⚠️ 学习声明 :本文档基于 Claude Code 2.1.88 源码分析整理,仅供个人学习研究使用,不做任何商业用途。
本文追踪一条用户消息从键盘输入到最终回答输出的完整生命周期 每个阶段都标注数据的变换形态,让你”看见”数据在每一步变成了什么样子
总览流水线 ┌──────────────────────────────────────────────────────────────────────────────┐ │ 用户在终端输入: "帮我重构 src/utils.ts" │ └──────────────────────┬───────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 1 ─────────────────────────────────────────────────────────────────────┐ │ React Ink UI 层 → TextInput 捕获输入 → handlePromptSubmit() │ │ 数据形态: 原始字符串 "帮我重构 src/utils.ts" │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 2 ─────────────────────────────────────────────────────────────────────┐ │ processUserInput() → 斜杠命令解析 / 图片处理 / 文本规范化 │ │ 数据形态: { messages: [UserMessage], shouldQuery: true, allowedTools: [...] } │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 3 ─────────────────────────────────────────────────────────────────────┐ │ Attachments 注入 → CLAUDE.md / Memory / IDE选择 / 条件上下文 │ │ 数据形态: messages[] 尾部追加多条 AttachmentMessage │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 4 ─────────────────────────────────────────────────────────────────────┐ │ System Prompt 组装 → 身份 + 工具描述 + 用户上下文 + 系统上下文 │ │ 数据形态: systemPrompt: string[], userContext: {}, systemContext: {} │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 5 ─────────────────────────────────────────────────────────────────────┐ │ query() 进入 Agentic Loop → 上下文压缩 / Snip / Microcompact / AutoCompact │ │ 数据形态: messagesForQuery[] (压缩后消息数组) │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 6 ─────────────────────────────────────────────────────────────────────┐ │ normalizeMessagesForAPI() → 过滤/重排/合并为 API 兼容格式 │ │ 数据形态: (UserMessage|AssistantMessage)[] — 仅 user/assistant 交替 │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 7 ─────────────────────────────────────────────────────────────────────┐ │ queryModelWithStreaming() → @anthropic-ai/sdk → HTTP POST /v1/messages │ │ 数据形态: BetaMessageStreamParams JSON → Server-Sent Events 流 │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 8 ─────────────────────────────────────────────────────────────────────┐ │ Stream 解析 → AssistantMessage 构建 → thinking / text / tool_use 提取 │ │ 数据形态: StreamEvent[] → AssistantMessage { content: ContentBlock[] } │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ▼ ┌─ STAGE 9 ─────────────────────────────────────────────────────────────────────┐ │ Tool Execution → 权限检查 → 并行/串行执行 → tool_result 注入 │ │ 数据形态: ToolUseBlock[] → 执行 → UserMessage[tool_result] 追加到 messages │ └──────────────────────┬────────────────────────────────────────────────────────┘ │ ┌────┴────┐ │ 有tool_use│──→ 回到 STAGE 5(继续循环) │ stop_reason│ │ ="end_turn"│──→ 进入 STAGE 10 └─────────┘ │ ▼ ┌─ STAGE 10 ────────────────────────────────────────────────────────────────────┐ │ 结果输出 → 成本统计 → 会话持久化 → UI渲染 │ │ 数据形态: 最终文本 + 成本报告 + 写入 transcript │ └───────────────────────────────────────────────────────────────────────────────┘
STAGE 1: UI层 — 用户输入捕获 源文件 : src/screens/Repl.tsx, src/hooks/useHandlePromptSubmit.ts
数据变换 用户键入: "帮我重构 src/utils.ts" │ ▼ React Ink TextInput组件捕获 onSubmit 回调 │ ▼ handlePromptSubmit({ input, pastedContents, ideSelection }) │ ▼ 传入数据: { input: "帮我重构 src/utils.ts", // 原始字符串 pastedContents: {}, // 粘贴的图片/文本 (此例为空) ideSelection: undefined, // IDE选中内容 (此例无) }
关键操作 :
如果用户有粘贴的图片/文本,pastedContents 会包含 { id: number, type: 'text'|'image', content: string }
如果连接了 IDE(如 VSCode),ideSelection 会包含当前选中的代码片段
输入队列 (messageQueueManager) 支持排队:用户可以在模型还在回答时继续输入
源文件 : src/utils/processUserInput/processUserInput.ts (606行)
输入到输出的变换 输入: "帮我重构 src/utils.ts" │ ├── 1. 斜杠命令检测: 是否以 / 开头? │ ├── "/compact" → 本地执行compact, shouldQuery=false │ ├── "/model opus" → 切换模型, shouldQuery=false │ └── 不是斜杠命令 → 继续 │ ├── 2. 特殊关键词检测 │ └── 包含 "ultraplan"? → 替换为ultraplan流程 │ ├── 3. 图片处理 │ └── pastedContents中的图片 → base64编码 → ImageBlockParam │ ├── 4. processTextPrompt() │ └── 纯文本 → 创建 UserMessage 对象 │ └── 5. UserPromptSubmit Hooks 执行 └── 可能被hook阻断(如安全策略拦截) 输出: { messages: [ { type: "user", uuid: "a1b2c3d4-...", timestamp: "2026-04-08T...", message: { role: "user", content: "帮我重构 src/utils.ts" // ← 用户原文 }, isMeta: false, isVisibleInTranscriptOnly: false, } ], shouldQuery: true, // 需要调用API allowedTools: undefined, // 无特殊工具限制 model: undefined, // 使用默认模型 }
斜杠命令的分叉路径 如果用户输入是 /compact:
输出: { messages: [SystemMessage("已执行compact...")], shouldQuery: false, // ← 不需要API调用,本地处理完毕 resultText: "Compacted conversation...", }
STAGE 3: 上下文附件注入 — Attachments 源文件 : src/utils/attachments.ts (3998行), src/query.ts
消息数组的丰富化 在用户消息被追加到 messages[] 后,进入 query() 循环之前和每次迭代开始时 ,系统自动注入上下文附件:
messages[] 当前状态: [ ...历史消息..., UserMessage("帮我重构 src/utils.ts"), // ← STAGE 2 创建的 ] │ ▼ getAttachmentMessages() 注入 messages[] 变为: [ ...历史消息..., UserMessage("帮我重构 src/utils.ts"), // ↓ 自动注入的附件消息 ↓ AttachmentMessage({ // CLAUDE.md 项目指令 type: "attachment", origin: "memory", content: "# Project Rules\n- Use TypeScript strict mode\n..." }), AttachmentMessage({ // 自动记忆 (MEMORY.md) type: "attachment", origin: "auto_memory", content: "## Context\n- 上次讨论了性能优化\n..." }), AttachmentMessage({ // 相关记忆 (sideQuery发现的) type: "attachment", origin: "relevant_memory", content: "## 相关: src/utils.ts 的历史修改记录\n..." }), AttachmentMessage({ // TODO列表状态 type: "attachment", origin: "todo", content: "<todos>[{\"id\":1,\"content\":\"重构utils\",\"status\":\"in_progress\"}]</todos>" }), AttachmentMessage({ // 条件规则 (如果匹配) type: "attachment", origin: "conditional_rule", content: "当处理 TypeScript 文件时: 确保运行 tsc --noEmit 检查类型" }), ]
附件来源和注入时机
附件类型
来源
注入条件
memory
.claude/CLAUDE.md + 各级目录
每次查询,首次注入
auto_memory
~/.claude/MEMORY.md
每次查询
relevant_memory
sideQuery 异步预取
首次有意义的查询
todo
内存中的 TODO 状态
TODO列表非空时
conditional_rule
CLAUDE.md 中的条件规则
路径/内容模式匹配时
ide_selection
VSCode/JetBrains
IDE有选中内容时
diagnostic
LSP 诊断
编辑相关文件时
skill_listing
可用技能列表
首次查询
plan
计划文件内容
有活跃计划时
task_context
后台任务状态
多Agent模式
STAGE 4: System Prompt 组装 源文件 : src/utils/queryContext.ts, src/constants/prompts.ts, src/context.ts
三层提示词组装 fetchSystemPromptParts() 并行获取: ├── getSystemPrompt(tools, model, dirs, mcpClients) ├── getUserContext() └── getSystemContext()
最终 System Prompt 的结构 :
systemPrompt = [ // ─── Part 1: 核心身份提示 (getSystemPrompt) ─── "You are Claude, an AI assistant made by Anthropic...", // ─── Part 2: 工具描述 ─── "You have access to the following tools:\n" + "- BashTool: Execute shell commands...\n" + "- FileReadTool: Read file contents...\n" + "- FileEditTool: Edit files with search/replace...\n" + "- GrepTool: Search with ripgrep...\n" + "- GlobTool: Find files by pattern...\n" + ... // 所有已注册工具的描述 // ─── Part 3: Memory Mechanics (如果有) ─── "(可选) How to use MEMORY.md...", // ─── Part 4: 追加系统提示 (appendSystemPrompt) ─── "(可选) 用户或SDK提供的额外指令", ] // 缓存键前缀部分: userContext = { "Current directory": "/home/user/project", "Date": "2026-04-08", "Platform": "Linux x86_64", "Shell": "/bin/bash", "Model": "claude-sonnet-4-20250514", "Editor": "VSCode", ... } systemContext = { "(内部指令,不对用户可见)": "...", ... }
完成后的数据形态 :
{ systemPrompt : SystemPrompt , userContext : { [k : string ]: string }, systemContext : { [k : string ]: string }, }
STAGE 5: Agentic Loop — 上下文压缩管道 源文件 : src/query.ts (1730行) — queryLoop() 函数
每次循环迭代的压缩流水线 进入 while(true) 循环后,消息在送往 API 前经过 4级压缩 :
messages[] (完整历史) │ ├── Step 5.1: getMessagesAfterCompactBoundary() │ └── 如果有 compact_boundary → 只取之后的消息 │ 数据变化: [100条] → [12条](丢弃已压缩的历史) │ ├── Step 5.2: applyToolResultBudget() │ └── 超长的 tool_result 被截断/替换为 "[content replaced, id=xxx]" │ 数据变化: 某个2MB的文件读取结果 → 替换为占位符 │ ├── Step 5.3: snipCompactIfNeeded() [HISTORY_SNIP feature] │ └── 老旧的中间对话段被"剪掉",保留头尾 │ 数据变化: [12条] → [8条](中间4条被snip) │ ├── Step 5.4: microcompact() │ └── 同一文件的多次Read/Edit → 只保留最新结果 │ 数据变化: 3次对utils.ts的Read结果 → 只保留最后1次 │ ├── Step 5.5: applyCollapsesIfNeeded() [CONTEXT_COLLAPSE feature] │ └── 已完成的工具调用链 → 收缩为摘要 │ 数据变化: [Read→Edit→Read→Edit] → [Summary: "编辑了utils.ts两次"] │ └── Step 5.6: autocompact (如果上下文过大) └── tokenCountWithEstimation() > threshold → 调用API压缩 数据变化: [180K tokens] → compact summary [20K tokens] messagesForQuery[] (压缩后,准备送往API)
压缩决策的Token计算 const currentTokens = lastAPIResponse.usage .input_tokens + lastAPIResponse.usage .output_tokens + lastAPIResponse.usage .cache_ *_tokens + roughEstimate (newMessagesSinceLastAPI) if (currentTokens > contextWindow * 0.9 ) { }
STAGE 6: 消息规范化 — normalizeMessagesForAPI() 源文件 : src/utils/messages.ts (5513行中的一部分)
内部消息 → API消息的变换 Claude Code 内部有 7+ 种消息类型,但 API 只接受 user 和 assistant 交替的序列:
内部 messages[]: [ { type: "user", message: {role:"user", content:"帮我重构..."} }, { type: "attachment", origin: "memory", content: "CLAUDE.md..." }, { type: "attachment", origin: "todo", content: "<todos>..." }, { type: "assistant", message: {role:"assistant", content:[...]} }, { type: "user", toolUseResult: {...} }, // tool_result { type: "progress", content: "..." }, // 进度消息 { type: "system", subtype: "local_command", content: "..." }, ] │ normalizeMessagesForAPI() ▼ API messages[]: [ { role: "user", content: [ { type: "text", text: "帮我重构 src/utils.ts" }, // attachment被合并进前一个user消息 { type: "text", text: "[CLAUDE.md内容]" }, { type: "text", text: "[TODO内容]" }, ] }, { role: "assistant", content: [ { type: "thinking", thinking: "..." }, { type: "text", text: "我来看一下..." }, { type: "tool_use", id: "tu_1", name: "FileReadTool", input: {...} }, ] }, { role: "user", content: [ { type: "tool_result", tool_use_id: "tu_1", content: "文件内容..." }, ] }, ]
关键变换规则 :
attachment 消息 → 合并到前一个 user 消息的 content 数组中
progress 消息 → 完全丢弃 (仅UI用)
system 消息 → 完全丢弃 (API不接受)
isVirtual 消息 → 过滤掉 (仅UI显示用)
连续相同角色的消息 → 合并 (API要求交替)
不可用工具的引用 → 过滤掉 (防止API报错)
STAGE 7: API调用 — queryModelWithStreaming() 源文件 : src/services/api/claude.ts (3420行)
HTTP请求的构建 queryModelWithStreaming() └── queryModel() └── withRetry() // 重试逻辑 └── anthropic.beta.messages.create({...})
实际发送到 Anthropic API 的 JSON :
{ "model" : "claude-sonnet-4-20250514" , "max_tokens" : 8000 , "system" : [ { "type" : "text" , "text" : "You are Claude, an AI assistant..." , "cache_control" : { "type" : "ephemeral" } } ] , "messages" : [ { "role" : "user" , "content" : [ { "type" : "text" , "text" : "帮我重构 src/utils.ts" } , { "type" : "text" , "text" : "# Project Rules\n..." } ] } ] , "tools" : [ { "name" : "BashTool" , "description" : "Execute shell commands..." , "input_schema" : { "type" : "object" , "properties" : { ...} } } , ... ] , "stream" : true , "metadata" : { "user_id" : "user_xxx" } , "betas" : [ "interleaved-thinking-2025-05-14" , "..." ] }
cache_control 分布 :
System Prompt ← cache_control: ephemeral (第1个断点) User Context ← cache_control: ephemeral (第2个断点) Tool Definitions ← cache_control: ephemeral (第3个断点) 最后2条user消息 ← cache_control: ephemeral (第4个断点)
这确保 system prompt + tool 定义在多轮对话中被缓存(cache_read 只要普通价格的 10%)。
重试与降级 请求失败? ├── 429 (Rate Limit) → 指数退避重试,最多4次 ├── 529 (Overloaded) → 更长退避,最多6次 ├── 401 (Auth Error) → OAuth token刷新后重试 ├── 网络超时 → 非流式降级 (executeNonStreamingRequest) ├── prompt_too_long → 触发 reactiveCompact(紧急压缩后重试) └── max_output_tokens → 自动扩容到 64k 重试(最多3次)
STAGE 8: 流式响应解析 源文件 : src/services/api/claude.ts — handleMessageFromStream
SSE事件流 → 结构化消息 API返回的 Server-Sent Events 流: event: message_start data: {"type":"message_start","message":{"id":"msg_01...","model":"claude-sonnet-4-...",...}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"让我分析..."}} ... (多个delta) event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"我来看看"}} event: content_block_start data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"tu_abc","name":"FileReadTool","input":{}}} event: content_block_delta data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"file_path\":\""}} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"tool_use"},"usage":{"output_tokens":150}} event: message_stop data: {"type":"message_stop"}
解析后得到 :
AssistantMessage = { type : "assistant" , uuid : "e5f6g7h8-..." , timestamp : "2026-04-08T..." , message : { id : "msg_01..." , role : "assistant" , model : "claude-sonnet-4-20250514" , content : [ { type : "thinking" , thinking : "让我分析一下用户的请求。他们想重构src/utils.ts..." }, { type : "text" , text : "我来看看 src/utils.ts 的内容:" }, { type : "tool_use" , id : "tu_abc" , name : "FileReadTool" , input : { "file_path" : "src/utils.ts" } } ], stop_reason : "tool_use" , usage : { input_tokens : 15234 , output_tokens : 150 , cache_creation_input_tokens : 3200 , cache_read_input_tokens : 12000 , } } }
同步操作(流式接收期间) :
每个 text_delta → 实时渲染到终端 (React Ink <Markdown>)
每个 thinking_delta → 渲染到思考展示区(如果启用)
每个 input_json_delta → StreamingToolExecutor 实时解析JSON
message_start → 重置当前消息usage计数器
message_delta → accumulateUsage() 累加token使用量
STAGE 9: 工具执行 源文件 : src/services/tools/toolOrchestration.ts, src/services/tools/toolExecution.ts
工具调用分类与执行 从 AssistantMessage.content 提取 tool_use blocks: [ { type: "tool_use", id: "tu_abc", name: "FileReadTool", input: { file_path: "src/utils.ts" } } ] │ ▼ partitionToolCalls() 将工具调用分为: ├── 只读批次 (isConcurrencySafe=true): FileReadTool, GrepTool, GlobTool │ └── 并行执行,最大并发度 10 └── 写入批次 (isConcurrencySafe=false): FileEditTool, BashTool └── 严格串行执行 │ ▼ 对每个工具调用: ┌─ 权限检查 ────────────────────────────────────────────┐ │ canUseTool(FileReadTool, {file_path:"src/utils.ts"}) │ │ │ │ 检查流程: │ │ 1. 是否在 allowedTools 中? │ │ 2. settings.json 的权限规则匹配? │ │ 3. 权限模式: │ │ ├── plan模式 → 自动拒绝写操作 │ │ ├── auto模式 → yoloClassifier判断 → 低风险自动允许 │ │ └── default模式 → 弹出UI询问用户 │ │ │ │ 结果: { behavior: "allow" | "deny", ... } │ └──────────────────────┬─────────────────────────────────┘ │ allow ▼ ┌─ 工具执行 ────────────────────────────────────────────┐ │ FileReadTool.call({file_path: "src/utils.ts"}) │ │ │ │ 内部流程: │ │ 1. expandPath("src/utils.ts") → "/home/user/.../src/utils.ts" │ 2. 检查文件大小 (≤ 256KB) │ │ 3. readFileSyncCached(path) → 文件内容 │ │ 4. addLineNumbers({content, startLine: 1}) │ │ 5. 返回: " 1→import { foo } from...\n 2→..." │ └──────────────────────┬─────────────────────────────────┘ │ ▼ 工具结果注入为 UserMessage (tool_result): { type: "user", message: { role: "user", content: [ { type: "tool_result", tool_use_id: "tu_abc", content: " 1→import { foo } from 'bar'\n 2→...(文件内容)...", is_error: false, } ] }, toolUseResult: "...(同上)...", sourceToolAssistantUUID: "e5f6g7h8-...", }
循环继续判断 stop_reason 判断: ├── "tool_use" → messages中追加tool_result → 回到 STAGE 5 继续循环 ├── "end_turn" → 模型完成回答 → 进入 STAGE 10 ├── "max_tokens" → 输出被截断 → 尝试扩容重试(最多3次) └── "stop_sequence" → 自定义停止序列 → 进入 STAGE 10
多轮工具调用示例 一个典型的重构请求可能产生 4-8 轮循环:
轮次1: Read src/utils.ts → tool_use → 继续 轮次2: Read tests/utils.test.ts → tool_use → 继续 轮次3: Edit src/utils.ts (重构代码) → tool_use → 继续 轮次4: Bash("npm test") → tool_use → 继续 轮次5: 发现测试失败, Edit tests/utils.test.ts → tool_use → 继续 轮次6: Bash("npm test") → tool_use → 继续 轮次7: 测试通过, 文本回复"重构完成" → end_turn → 结束
STAGE 10: 结果输出与持久化 源文件 : src/QueryEngine.ts, src/cost-tracker.ts, src/costHook.ts
最终处理流水线 模型回答 stop_reason="end_turn" │ ├── 1. 成本计算 │ calculateUSDCost("claude-sonnet-4-...", usage) │ → $0.08 (假设值) │ ├── 2. 累加到会话成本 │ addToTotalSessionCost(0.08, 1200, {"claude-sonnet-4-...": usage}) │ → OpenTelemetry 上报 │ ├── 3. Stop Hooks 执行 │ └── executeStopFailureHooks() → 检查模型是否遵循了指令 │ ├── 4. 会话持久化 │ recordTranscript(messages) │ → ~/.claude/projects/<hash>/sessions/<id>.jsonl │ ├── 5. 生成SDK结果消息 │ yield { │ type: "result", │ subtype: "success", │ result: "重构已完成。我对 src/utils.ts 做了以下修改...", │ duration_ms: 45000, │ duration_api_ms: 12000, │ total_cost_usd: 0.08, │ usage: { input_tokens: 45000, output_tokens: 1200, ... }, │ num_turns: 7, │ } │ └── 6. UI渲染最终回答 React Ink <Markdown> 组件渲染模型的文本回复 终端显示: "重构已完成。我对 src/utils.ts 做了以下修改..."
进程退出时(useCostSummary hook) process.exit 触发: ├── formatTotalCost() → "Total cost: $0.08\n Duration: 12s API, 45s wall..." ├── saveCurrentSessionCosts() │ ├── git diff --stat → +45 -20 lines │ └── setProjectConfig(sessionKey, { cost, duration, linesAdded, ... }) └── 输出到 stderr (灰色文本)
附录: 完整数据形态变换总结
阶段
数据形态
大小参考
STAGE 1
原始字符串 "帮我重构 src/utils.ts"
~20 bytes
STAGE 2
{ messages: [UserMessage], shouldQuery: true }
~200 bytes
STAGE 3
messages[] + 多条 AttachmentMessage
~5-50 KB
STAGE 4
systemPrompt + userContext + systemContext
~10-30 KB
STAGE 5
messagesForQuery[] (压缩后)
~20-200K tokens
STAGE 6
(User|Assistant)[] API格式
~同上
STAGE 7
HTTP JSON body → SSE stream
~根据上下文大小
STAGE 8
AssistantMessage (结构化)
~0.5-4K tokens output
STAGE 9
tool_result 注入 → 回到 STAGE 5
~根据工具输出
STAGE 10
最终文本 + 成本报告 + 持久化
渲染到终端
附录: 关键源文件索引
阶段
核心文件
行数
STAGE 1
src/screens/Repl.tsx
UI入口
STAGE 2
src/utils/processUserInput/processUserInput.ts
606
STAGE 3
src/utils/attachments.ts
3998
STAGE 4
src/utils/queryContext.ts + src/constants/prompts.ts + src/context.ts
180+大
STAGE 5
src/query.ts
1730
STAGE 6
src/utils/messages.ts (normalizeMessagesForAPI)
5513中
STAGE 7
src/services/api/claude.ts
3420
STAGE 8
src/services/api/claude.ts (streaming部分)
同上
STAGE 9
src/services/tools/toolOrchestration.ts + toolExecution.ts
189+
STAGE 10
src/QueryEngine.ts + src/cost-tracker.ts
1296+324
涉及源文件
src/constants/prompts.ts
src/context.ts
src/cost-tracker.ts
src/costHook.ts
src/hooks/useHandlePromptSubmit.ts
src/query.ts
src/QueryEngine.ts
src/screens/Repl.tsx
src/services/api/claude.ts
src/services/tools/toolExecution.ts
src/services/tools/toolOrchestration.ts
src/utils.ts
src/utils/attachments.ts
src/utils/messages.ts
src/utils/processUserInput/processUserInput.ts
src/utils/queryContext.ts