chivgent 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +115 -131
  2. package/README.zh-CN.md +103 -817
  3. package/dist/agent.d.ts +16 -1
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +95 -43
  6. package/dist/agent.js.map +1 -1
  7. package/dist/auth/oauth.d.ts +51 -0
  8. package/dist/auth/oauth.d.ts.map +1 -0
  9. package/dist/auth/oauth.js +444 -0
  10. package/dist/auth/oauth.js.map +1 -0
  11. package/dist/cli-options.d.ts +11 -6
  12. package/dist/cli-options.d.ts.map +1 -1
  13. package/dist/cli-options.js +46 -4
  14. package/dist/cli-options.js.map +1 -1
  15. package/dist/cli-runtime.d.ts +9 -0
  16. package/dist/cli-runtime.d.ts.map +1 -1
  17. package/dist/cli-runtime.js +32 -6
  18. package/dist/cli-runtime.js.map +1 -1
  19. package/dist/cli.d.ts +1 -1
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +129 -34
  22. package/dist/cli.js.map +1 -1
  23. package/dist/context/context-manager.d.ts +21 -0
  24. package/dist/context/context-manager.d.ts.map +1 -1
  25. package/dist/context/context-manager.js +199 -35
  26. package/dist/context/context-manager.js.map +1 -1
  27. package/dist/evals/tools.d.ts.map +1 -1
  28. package/dist/evals/tools.js +4 -2
  29. package/dist/evals/tools.js.map +1 -1
  30. package/dist/events.d.ts +5 -0
  31. package/dist/events.d.ts.map +1 -1
  32. package/dist/events.js.map +1 -1
  33. package/dist/permissions.d.ts +45 -0
  34. package/dist/permissions.d.ts.map +1 -0
  35. package/dist/permissions.js +97 -0
  36. package/dist/permissions.js.map +1 -0
  37. package/dist/prompts.d.ts +1 -1
  38. package/dist/prompts.d.ts.map +1 -1
  39. package/dist/prompts.js +1 -0
  40. package/dist/prompts.js.map +1 -1
  41. package/dist/providers/client.d.ts +1 -1
  42. package/dist/providers/client.d.ts.map +1 -1
  43. package/dist/providers/client.js +19 -1
  44. package/dist/providers/client.js.map +1 -1
  45. package/dist/providers/definitions.d.ts +2 -0
  46. package/dist/providers/definitions.d.ts.map +1 -1
  47. package/dist/providers/definitions.js +3 -1
  48. package/dist/providers/definitions.js.map +1 -1
  49. package/dist/providers/interactive.d.ts +4 -0
  50. package/dist/providers/interactive.d.ts.map +1 -1
  51. package/dist/providers/interactive.js +86 -2
  52. package/dist/providers/interactive.js.map +1 -1
  53. package/dist/providers/oauth-client.d.ts +14 -0
  54. package/dist/providers/oauth-client.d.ts.map +1 -0
  55. package/dist/providers/oauth-client.js +52 -0
  56. package/dist/providers/oauth-client.js.map +1 -0
  57. package/dist/providers/openai-compatible-chat.d.ts +1 -0
  58. package/dist/providers/openai-compatible-chat.d.ts.map +1 -1
  59. package/dist/providers/openai-compatible-chat.js +1 -0
  60. package/dist/providers/openai-compatible-chat.js.map +1 -1
  61. package/dist/providers/openai.d.ts +3 -0
  62. package/dist/providers/openai.d.ts.map +1 -1
  63. package/dist/providers/openai.js +72 -10
  64. package/dist/providers/openai.js.map +1 -1
  65. package/dist/remote/repl.d.ts.map +1 -1
  66. package/dist/remote/repl.js +9 -0
  67. package/dist/remote/repl.js.map +1 -1
  68. package/dist/repl.d.ts +43 -1
  69. package/dist/repl.d.ts.map +1 -1
  70. package/dist/repl.js +303 -21
  71. package/dist/repl.js.map +1 -1
  72. package/dist/runtime/desktop-runtime.d.ts +23 -0
  73. package/dist/runtime/desktop-runtime.d.ts.map +1 -0
  74. package/dist/runtime/desktop-runtime.js +63 -0
  75. package/dist/runtime/desktop-runtime.js.map +1 -0
  76. package/dist/session-store.d.ts +22 -1
  77. package/dist/session-store.d.ts.map +1 -1
  78. package/dist/session-store.js +80 -1
  79. package/dist/session-store.js.map +1 -1
  80. package/dist/session.d.ts +21 -0
  81. package/dist/session.d.ts.map +1 -1
  82. package/dist/session.js +110 -4
  83. package/dist/session.js.map +1 -1
  84. package/dist/shell/approval.d.ts +2 -2
  85. package/dist/shell/approval.d.ts.map +1 -1
  86. package/dist/shell/approval.js +2 -2
  87. package/dist/shell/approval.js.map +1 -1
  88. package/dist/shell/docker.d.ts +2 -0
  89. package/dist/shell/docker.d.ts.map +1 -1
  90. package/dist/shell/docker.js +17 -3
  91. package/dist/shell/docker.js.map +1 -1
  92. package/dist/shell/factory.d.ts +9 -0
  93. package/dist/shell/factory.d.ts.map +1 -0
  94. package/dist/shell/factory.js +13 -0
  95. package/dist/shell/factory.js.map +1 -0
  96. package/dist/shell/local.d.ts +4 -0
  97. package/dist/shell/local.d.ts.map +1 -1
  98. package/dist/shell/local.js +16 -1
  99. package/dist/shell/local.js.map +1 -1
  100. package/dist/shell/native.d.ts +15 -0
  101. package/dist/shell/native.d.ts.map +1 -0
  102. package/dist/shell/native.js +99 -0
  103. package/dist/shell/native.js.map +1 -0
  104. package/dist/shell/types.d.ts +1 -0
  105. package/dist/shell/types.d.ts.map +1 -1
  106. package/dist/tools/bash.d.ts +19 -0
  107. package/dist/tools/bash.d.ts.map +1 -1
  108. package/dist/tools/bash.js +30 -11
  109. package/dist/tools/bash.js.map +1 -1
  110. package/dist/tools/edit-file.d.ts.map +1 -1
  111. package/dist/tools/edit-file.js +1 -1
  112. package/dist/tools/edit-file.js.map +1 -1
  113. package/dist/tools/parallel.d.ts +2 -0
  114. package/dist/tools/parallel.d.ts.map +1 -1
  115. package/dist/tools/parallel.js +8 -0
  116. package/dist/tools/parallel.js.map +1 -1
  117. package/dist/tools/write-file.d.ts.map +1 -1
  118. package/dist/tools/write-file.js +1 -1
  119. package/dist/tools/write-file.js.map +1 -1
  120. package/dist/transcript-hash.d.ts +4 -0
  121. package/dist/transcript-hash.d.ts.map +1 -0
  122. package/dist/transcript-hash.js +6 -0
  123. package/dist/transcript-hash.js.map +1 -0
  124. package/dist/tui/activity.d.ts +1 -1
  125. package/dist/tui/activity.d.ts.map +1 -1
  126. package/dist/tui/activity.js +2 -2
  127. package/dist/tui/activity.js.map +1 -1
  128. package/dist/tui/editor-input.d.ts +8 -0
  129. package/dist/tui/editor-input.d.ts.map +1 -1
  130. package/dist/tui/editor-input.js +122 -18
  131. package/dist/tui/editor-input.js.map +1 -1
  132. package/dist/tui/editor.d.ts +11 -1
  133. package/dist/tui/editor.d.ts.map +1 -1
  134. package/dist/tui/editor.js +77 -29
  135. package/dist/tui/editor.js.map +1 -1
  136. package/dist/tui/live.d.ts +2 -0
  137. package/dist/tui/live.d.ts.map +1 -1
  138. package/dist/tui/live.js +5 -0
  139. package/dist/tui/live.js.map +1 -1
  140. package/dist/tui/selection.d.ts +24 -0
  141. package/dist/tui/selection.d.ts.map +1 -0
  142. package/dist/tui/selection.js +19 -0
  143. package/dist/tui/selection.js.map +1 -0
  144. package/dist/tui/slash.js +1 -1
  145. package/dist/tui/slash.js.map +1 -1
  146. package/dist/tui/welcome.d.ts +2 -2
  147. package/dist/tui/welcome.d.ts.map +1 -1
  148. package/dist/tui/welcome.js +14 -23
  149. package/dist/tui/welcome.js.map +1 -1
  150. package/dist/workspace/paths.d.ts.map +1 -1
  151. package/dist/workspace/paths.js +4 -0
  152. package/dist/workspace/paths.js.map +1 -1
  153. package/dist/workspace/types.d.ts +2 -2
  154. package/dist/workspace/types.d.ts.map +1 -1
  155. package/dist/workspace/types.js.map +1 -1
  156. package/dist/workspace.d.ts +2 -2
  157. package/dist/workspace.d.ts.map +1 -1
  158. package/dist/workspace.js +2 -2
  159. package/dist/workspace.js.map +1 -1
  160. package/package.json +6 -2
package/README.zh-CN.md CHANGED
@@ -1,887 +1,173 @@
1
- # chivgent
1
+ <p align="center">
2
+ <img src="docs/assets/chivgent-leaf-mark.png" width="88" alt="">
3
+ </p>
2
4
 
3
- [English](README.md) | [简体中文](README.zh-CN.md)
4
-
5
- > ⚠️ **本中文版已过时,请以 [英文 README](README.md) 为准。** 尤其是安全说明:`--allow-shell` 现在在离线 Docker 容器中运行命令,每条命令需逐条确认,且不向容器传入 API key(详见 [shell sandbox](docs/shell-sandbox.md))。下文中与此冲突的说法均已失效。
6
-
7
- > 一款易读的终端 Coding Agent,提供流式 TUI 与可切换的 AI 供应商。
8
-
9
- ![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22-339933?logo=nodedotjs&logoColor=white)
10
- ![TypeScript](https://img.shields.io/badge/TypeScript-ESM-3178C6?logo=typescript&logoColor=white)
11
- [![CI](https://github.com/chivopic/chivgent/actions/workflows/ci.yml/badge.svg)](https://github.com/chivopic/chivgent/actions/workflows/ci.yml)
12
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
13
- ![Version](https://img.shields.io/badge/version-1.0.0-blue)
14
-
15
- `chivgent` 将 Provider 无关的 Agent Loop 与 LLM API、工具和工作区边界连接起来。
16
- 它可以发现文件、搜索源码、分段读取内容,并可按需编辑文件或运行命令;
17
- 支持 OpenAI、DeepSeek 和兼容 Chat Completions 的 API。
18
-
19
- 这个项目刻意保持精简:先让 Tool Calling、Conversation State、Provider Adapter
20
- 和循环终止条件容易理解,再逐步加入成熟 Agent Harness 所需的工程复杂度。
21
-
22
- ## 功能
23
-
24
- - 完整的多轮 Agent Loop:模型 -> Tool Call -> Tool Result -> 模型。
25
- - Provider 无关的运行时消息和工具契约。
26
- - Provider 注册表:新增 Provider 只是一条声明,不需要改 CLI。
27
- - API Key 依次从 `--api-key`、环境变量、可选文件解析。
28
- - 通过 Responses API 支持 OpenAI。
29
- - 通过通用 OpenAI-compatible Chat Completions 客户端支持 DeepSeek。
30
- - 可通过 TUI、CLI 参数或环境变量配置自定义 OpenAI-compatible API。
31
- - 滚动式 TUI:输入斜杠显示命令建议,可在菜单中选择供应商。
32
- - 既可以进入带斜杠命令的交互式会话,也可以单次提问后退出。
33
- - Session 以 JSON Lines 持久化,可以在之后的进程中恢复。
34
- - 提供 `--json` 事件流,便于脚本和其他前端消费。
35
- - 基于类型化运行时事件流的流式输出。
36
- - 上下文管理器:压缩较早的轮次以待在窗口内。
37
- - 可中断的运行:Ctrl+C 停止当前回答;空闲时退出,不丢失会话记录。
38
- - Provider 调用具备单次超时和有上限的指数退避重试。
39
- - 通过 `list_files` 和字面量 `search_text` 确定性地发现项目内容。
40
- - 支持带续读提示的分段 `read_file`,所有工具结果都有容量上限。
41
- - 默认只读;`--allow-writes` 才会启用 `write_file` 和 `edit_file`。
42
- - 通过 `--allow-shell` 选择性开启的 `bash` 工具,输出边跑边显示。
43
- - 采集 Provider 报告的 token 用量:按轮、按次运行、按 eval 任务分别统计。
44
- - Eval 套件:衡量 Agent 能不能把活干成,而不只是能不能跑起来。
45
- - 远程会话:一个进程持有会话,其他进程通过本地 socket 接上来。
46
- - 多个客户端可以同时观察同一个会话,其中任何一个都能中断当前运行。
47
- - 扩展系统:可以添加工具、斜杠命令、事件订阅和 system prompt 片段。
48
- - 项目扩展受按目录记录的信任决定门控;判定发生在任何模块被 import 之前,
49
- 没有终端可问时一律拒绝。
50
- - 命令运行在独立进程组中,取消一次运行会杀掉整棵进程树。
51
- - 命令输出从尾部截断,完整内容写入临时文件供按需读取。
52
- - 精确匹配的 `edit_file`,命中缺失或不唯一时拒绝执行。
53
- - 编辑会保留文件原有的 BOM 和 CRLF 换行符。
54
- - 原子写入:写入中途崩溃不会损坏原文件。
55
- - 安全的工作区访问,阻止路径穿越和符号链接逃逸。
56
- - 支持根 `.gitignore`、生成目录和敏感路径过滤。
57
- - 工具参数验证、明确的工具错误和有上限的轮次限制。
58
- - 可打包安装的 Node.js CLI,不依赖 Agent 框架。
59
- - 默认测试不调用真实 API,不消耗模型额度。
5
+ <div align="center">
60
6
 
61
- ## 快速开始
7
+ # chivgent
62
8
 
63
- ### 环境要求
9
+ ### 终端里的 coding agent。
64
10
 
65
- - Node.js 22 或更高版本
66
- - npm
67
- - OpenAI、DeepSeek 或其他兼容供应商的 API Key
11
+ 和 Claude Code、Codex 同一类。模型你自己带。
68
12
 
69
- ### 从 npm 安装
13
+ [![npm](https://img.shields.io/npm/v/chivgent?color=111111)](https://www.npmjs.com/package/chivgent)
14
+ [![CI](https://img.shields.io/github/actions/workflow/status/chivopic/chivgent/ci.yml?label=CI&color=111111)](https://github.com/chivopic/chivgent/actions/workflows/ci.yml)
15
+ ![Node.js >=22](https://img.shields.io/badge/Node.js-%3E%3D22-111111)
16
+ [![License](https://img.shields.io/badge/license-MIT-111111)](LICENSE)
70
17
 
71
- ```bash
72
- npm install -g chivgent
73
- chivgent --version
74
- ```
18
+ **[官网 ↗](https://chivgent-web.vercel.app/)** · [架构](docs/architecture.md) · [Shell](docs/shell-sandbox.md) · [登录](docs/subscription-oauth.md) · [English](README.md) · **简体中文**
75
19
 
76
- 通过 npm 全局安装后,可以直接更新:
20
+ </div>
77
21
 
78
- ```bash
79
- chivgent update --check # 只检查版本
80
- chivgent update # 安装更新,完成后重新启动 chivgent
81
- ```
82
-
83
- 旧版本若还没有这个命令,先执行一次 `npm install -g chivgent@latest`。
84
- 仅在主动运行命令时检查更新;源码、本地项目依赖和临时 `npx` 运行会显示对应的更新指引。
85
- 详见[更新说明](docs/updating.zh-CN.md)。
86
-
87
- 如果希望安装当前源码版本:
88
-
89
- ```bash
90
- git clone https://github.com/chivopic/chivgent.git
91
- cd chivgent
92
- npm install
93
- npm run build
94
- npm install -g .
95
- ```
22
+ chivgent 是跑在你打开的目录里的本地 agent。它要做的,是 [Claude Code](https://code.claude.com) 和 [Codex](https://github.com/openai/codex) 那一类事:读这个项目,改它,跑它,会话还能回来。模型是你的。一把 API key,或者 ChatGPT、xAI 订阅。
96
23
 
97
- ### 分析一个项目
24
+ 这一类现在的默认是 auto mode。工作区内的读取、编辑,以及沙箱里的命令,直接进行。动作离开工作区,或者命令要用宿主机,就停。这里这个策略叫 `workspace`。它不是另一个模型在逐条审查,也不是跳过所有边界。本地交互式 rich TUI 在没有指定权限模式或旧权限开关时默认使用 Ask for me:文件修改和命令都要确认;在空闲输入框按 Shift+Tab 可在 Plan、Auto mode 和 Ask for me 之间循环。headless、simple 和远程启动保留原有默认值。
98
25
 
99
- 进入你希望 Agent 分析的项目目录,然后运行 `chivgent`。
26
+ ## 快速开始
100
27
 
101
- 使用 OpenAI:
28
+ 需要 Node.js 22 或更高版本。
102
29
 
103
30
  ```bash
104
- export OPENAI_API_KEY="your-api-key"
105
- chivgent "src/agent.ts 是做什么的?"
31
+ npm install -g chivgent
106
32
  ```
107
33
 
108
- 使用 DeepSeek:
34
+ macOS/Linux 用户也可以通过[官网一键安装脚本](https://chivgent-web.vercel.app/install.sh)安装。仍需提前装好 Node.js 22+ 和 npm;脚本安装的是同一个 npm 包,不会自动调用 `sudo` 或修改 Shell 配置。
109
35
 
110
36
  ```bash
111
- export DEEPSEEK_API_KEY="your-api-key"
112
- chivgent --provider deepseek "解释 src/ 目录的架构"
37
+ curl -fsSL https://chivgent-web.vercel.app/install.sh | bash
113
38
  ```
114
39
 
115
- 使用任意 OpenAI-compatible Chat Completions API:
40
+ 这会直接执行从网上下载的脚本。更稳妥的做法是先运行 `curl -fsSLo install.sh https://chivgent-web.vercel.app/install.sh`,阅读内容后再运行 `bash install.sh`。如果 npm 全局命令目录不在 PATH 里,脚本会提示需要添加的目录。
116
41
 
117
- ```bash
118
- export OPENAI_API_KEY="your-provider-api-key"
119
- export OPENAI_BASE_URL="https://api.vendor.example/v1"
120
- export OPENAI_MODEL="vendor-model"
121
42
 
122
- chivgent --provider openai-compatible "解释 src/ 目录的架构"
123
- ```
43
+ 已经安装过?先 `chivgent update --check`,再 `chivgent update`。
124
44
 
125
- 不带问题直接运行 `chivgent` 会进入交互式会话:
45
+ 在项目目录里启动。
126
46
 
127
47
  ```bash
48
+ cd /path/to/project
128
49
  chivgent
129
- › src/agent.ts 在做什么?
130
- › 这个循环在哪里测试?
131
- › /exit
132
- ```
133
-
134
- 会话会跨多轮保留上下文,因此追问不需要重复之前的信息。之后可以用
135
- `chivgent --continue`(或 `chivgent --resume <id>`)恢复;`chivgent --sessions`
136
- 可以列出已记录的会话。
137
-
138
- 答案会随模型生成实时写入 stdout,因此 stdout 仍然可以直接管道使用。工具活动、
139
- 重试和运行状态写入 stderr;Provider 错误会返回非零退出码。使用 `--no-stream`
140
- 可改为一次性输出完整答案,`--quiet` 可隐藏工具活动。
141
-
142
- ## CLI 参考
143
-
144
- ```text
145
- chivgent [选项] "问题" 回答一次后退出
146
- chivgent [选项] 进入交互式会话
147
-
148
- 选项:
149
- --provider NAME openai、deepseek、openai-compatible、openrouter、groq、xai、
150
- moonshot(默认:openai)
151
- --model MODEL 覆盖 Provider 模型
152
- --max-turns N 工具调用轮次上限(默认:8,加 --allow-writes 或 --allow-shell
153
- 时为 16)
154
- --no-stream 关闭流式输出,等待完整答案
155
- -q, --quiet 不在 stderr 打印工具活动
156
- --json 以 JSON Lines 输出整次运行,而不是渲染文本
157
- -c, --continue 恢复当前工作区最近的一次 Session
158
- --resume ID 恢复指定 Session
159
- --api-key KEY 本次运行使用的 API Key;更推荐用环境变量
160
- --sessions 列出已记录的 Session 并退出
161
- --allow-writes 允许 Agent 创建和修改文件(默认只读)
162
- --allow-shell 允许 Agent 执行 Shell 命令。它蕴含写权限:Shell 不受工作区
163
- 边界约束。仅支持 Unix。
164
- --serve 把当前会话暴露在本地 socket 上并常驻
165
- --connect TARGET 接上一个已服务的会话,可用 id 或 socket 路径
166
- --servers 列出仍在响应的服务端后退出
167
- --no-extensions 不加载任何扩展,也不询问信任
168
- --extensions 列出已加载的扩展及其注册内容后退出
169
- --forget-trust 忘记覆盖当前工作区的信任决定后退出
170
- --context-window N 上下文的 Token 预算(默认:128000)
171
- --no-compaction 发送完整 transcript,不压缩较早的轮次
172
- --no-session 不记录本次运行
173
- -h, --help 显示帮助
174
- -v, --version 显示版本
175
- ```
176
-
177
- 交互式会话中 `/help` 会列出全部斜杠命令,包括 `/provider`、`/model`、`/endpoint`、`/login`、
178
- `/session`、`/tools`、`/clear` 和 `/exit`。TUI 输入 `/` 会在当前输入行下方打开命令菜单;
179
- 输入 `/provider` 会打开供应商菜单,可用上下键选择并按 Enter 切换。
180
- 选中没有 Key 的供应商后会直接进入隐藏的 Key 输入;已有 Key 时直接可用。
181
- `/login` 保留用于更换 Key。
182
- 运行中按 Ctrl+C 中断当前回答,不会退出会话;空闲时按 Ctrl+C 则退出(退出码 130)。
183
- Ctrl+D 在空行时也可离开。当前为单行输入,一个输入批次只提交首行,
184
- 后续内容被丢弃;请等提示符重新出现后再输入下一条问题。
185
-
186
- 退出码:`0` 正常回答,`1` 配置或 Provider 失败,`2` 达到轮次上限,`130` 被
187
- Ctrl+C 中断。
188
-
189
- ### 登录
190
-
191
- 最省事的方式是直接启动 chivgent,让它来问:
192
-
193
- ```text
194
- $ chivgent --tui
195
- v1.0.0
196
- openai / gpt-5.6
197
- Start with /provider → choose a Provider
198
- › /provider deepseek
199
- Selected deepseek / deepseek-v4-flash. API key needed for deepseek.
200
- API key for deepseek (hidden, saved locally; Enter skips):
201
- Stored the key for deepseek. Ready for your next prompt.
202
- ›
203
- ```
204
-
205
- **没有 Key 时交互式会话照样能启动**。打开 `/provider`、选中供应商后,
206
- TUI 会直接询问它的 Key;已有 Key 就跳过。Key 保存后立即生效,
207
- 无需再输入 `/login`。需要替换 Key 时仍可运行 `/login`。
208
-
209
- 输入的 Key 不会回显,`auth.json` 以仅所有者可读的权限写入。存的时候**不会**去
210
- Provider 那里验证;你之后的第一次提问才是真正的检验。
211
-
212
- 非交互式运行在没有 Key 时依然立刻失败——管道里没有人可问。
213
-
214
- 在会话中换供应商或模型:
215
-
216
- ```text
217
- › /provider # 打开供应商菜单,上下键选择
218
- › /provider deepseek # 选用默认模型;缺 Key 时直接输入
219
- › /model deepseek-v4-pro # 改用另一模型
220
- › /provider openrouter # 先切换供应商
221
- › /model anthropic/claude-sonnet-4 # 再设置该供应商的模型
222
- › /provider openai-compatible
223
- › /model vendor-model
224
- › /endpoint https://api.vendor.example/v1
225
- ```
226
-
227
- 切换后保留会话历史。`--api-key` 仅用于启动时选择的供应商,不会被带到另一家。
228
- 从 OpenAI 在会话内切换到自定义兼容端点时,也不会复用 `OPENAI_API_KEY`;
229
- TUI 会单独询问这个端点的 Key。
230
-
231
- ### Provider 配置
232
-
233
- | Provider | API Key | 模型环境变量 | 默认模型 | API 形式 |
234
- | --- | --- | --- | --- | --- |
235
- | OpenAI | `OPENAI_API_KEY` | `OPENAI_MODEL` | `gpt-5.6` | Responses API |
236
- | DeepSeek | `DEEPSEEK_API_KEY` | `DEEPSEEK_MODEL` | `deepseek-v4-flash` | OpenAI-compatible Chat Completions |
237
- | 自定义兼容供应商 | `OPENAI_API_KEY` | `OPENAI_MODEL` | 必填 | OpenAI-compatible Chat Completions |
238
- | OpenRouter | `OPENROUTER_API_KEY` | `OPENROUTER_MODEL` | 必填 | OpenAI-compatible Chat Completions |
239
- | Groq | `GROQ_API_KEY` | `GROQ_MODEL` | 必填 | OpenAI-compatible Chat Completions |
240
- | xAI | `XAI_API_KEY` | `XAI_MODEL` | 必填 | OpenAI-compatible Chat Completions |
241
- | Moonshot | `MOONSHOT_API_KEY` | `MOONSHOT_MODEL` | 必填 | OpenAI-compatible Chat Completions |
242
-
243
- 显式传入的 `--model` 优先于 Provider 对应的模型环境变量。自定义兼容供应商还必须
244
- 配置 `OPENAI_BASE_URL`。Session 记录在 `CHIVGENT_HOME`(默认 `~/.chivgent`)下。
245
-
246
- Provider 通过注册表声明,而不是在 CLI 里分支判断,因此 `--help` 的内容、
247
- `--provider` 的校验和实际创建 Client 用的是同一份清单。
248
-
249
- #### API Key 解析顺序
250
-
251
- 按以下顺序解析,命中即停:
252
-
253
- 1. `--api-key`(仅本次运行)
254
- 2. Provider 对应的环境变量
255
- 3. `<CHIVGENT_HOME>/auth.json`
256
-
257
- 环境变量刻意排在文件之前,与其他 CLI 的惯例一致,这样已存储的 Key 可以被
258
- 一次性的环境变量临时覆盖,而不必改文件。
259
-
260
- `auth.json` 是可选的,只接受字面量 Key:
261
-
262
- ```json
263
- {
264
- "openai": { "type": "api_key", "key": "sk-..." },
265
- "deepseek": "sk-..."
266
- }
267
- ```
268
-
269
- 不支持 `$VAR` 展开,也不支持 `!command` 替换:让配置文件能够启动进程,
270
- 是用很小的便利换很大的攻击面。当该文件可被其他用户读取时 chivgent 会告警,
271
- 请保持 `chmod 600`。
272
-
273
- ```bash
274
- chivgent --provider openai --model gpt-5.6 "解释 package.json"
275
- chivgent --provider deepseek --model deepseek-v4-pro "解释 package.json"
276
- chivgent --provider openai-compatible --model vendor-model "解释 package.json"
277
- ```
278
-
279
- ## 架构
280
-
281
- ```text
282
- +-> OpenAI Responses API
283
- 用户 -> CLI -> Agent -> LLMClient |
284
- | +-> OpenAI-compatible Chat -> DeepSeek / 自定义
285
- |
286
- +-> Tool Registry -> list_files / search_text / read_file -> Workspace
287
- write_file / edit_file(--allow-writes)
288
- | bash(--allow-shell)-> ShellOperations
289
- ```
290
-
291
- Agent Runtime 拥有自己的消息模型。Provider 特有的数据结构只在 `LLMClient` 边界
292
- 进行转换:
293
-
294
- ```text
295
- Agent Message[] -> Provider Adapter -> Provider Request
296
- <- Provider Response
297
- AssistantMessage <- Normalized Result
298
- ```
299
-
300
- 因此 Agent、工具和 CLI 不会依赖任何单一供应商的消息格式。
301
-
302
- ### 上下文管理
303
-
304
- Session 记录发生过的一切;Context Manager 决定这一次请求值得发送什么。把两者
305
- 分开,才能让长会话待在固定的上下文窗口内。
306
-
307
- ```text
308
- 完整 transcript ──────────────→ Session Store(发生了什么)
309
- │
310
- ↓
311
- ContextManager
312
- │ token 估算 vs. contextWindow - reserveTokens
313
- ↓
314
- 摘要 + 最近消息 ──────────────→ Provider(模型看到什么)
315
- ```
316
-
317
- 当估算超出预算时,较早的消息会被压缩成一条摘要消息,最近若干轮原样保留。
318
- 有三个细节值得注意:
319
-
320
- - **文件清单从工具调用推导,而不是从摘要里提取。** 摘要可能遗漏或者编造路径。
321
- 对 Coding Agent 来说,「我读过和改过哪些文件」是最需要完整存活的信息,因此
322
- 它直接从 `read_file`、`write_file`、`edit_file` 的调用参数收集。
323
- - **工具调用永远不会和它的结果被切开。** 会把 tool result 孤立的切点会向前
324
- 移动,越过整组消息。
325
- - **压缩会丢弃 Provider continuation。** 在服务端串联历史的 Provider 会重放
326
- 它自己那份历史、忽略请求里带的消息,保留 continuation 等于把刚压缩掉的历史
327
- 又送了回去。
328
-
329
- Token 数量按字符长度估算,而不是用真正的 tokenizer:后者与模型强相关,为一个
330
- 只用来决定「何时压缩」的数字引入大依赖并不划算,估算误差由 reserve 预算吸收。
331
-
332
- 单条超过整个预算的工具结果无法被压缩掉,这种情况应该去限制工具输出本身。
333
- 用 `--no-compaction` 可以关闭压缩。
334
-
335
- ### 运行时事件
336
-
337
- Agent Loop 自己不打印任何内容,而是通过类型化的事件流对外汇报。一次运行会产生:
338
-
339
- ```text
340
- agent_start
341
- turn_start -> message_start -> message_update* -> message_end
342
- tool_execution_start -> tool_execution_end (每个 Tool Call 一次)
343
- turn_end
344
- ...
345
- agent_end (completed | max_turns | aborted | error)
346
50
  ```
347
51
 
348
- `message_update` 只携带增量,不携带累计快照,因此事件流的体积与答案长度保持线性
349
- 关系。事件都是可结构化克隆的,且每个监听者拿到的是副本,渲染层无法修改
350
- transcript。`src/render.ts` 中的 CLI 渲染器只是其中一个消费者,日志、JSON 流或
351
- TUI 同样可以消费。
352
-
353
- `LLMClient.stream` 是可选的。Provider 未实现时,Agent 会退回 `complete`,事件序列
354
- 不变,只是没有增量事件。
355
-
356
- ### OpenAI-compatible 供应商
357
-
358
- 兼容供应商可以通过修改 `baseURL`、凭据和模型名称,复用官方 `openai` npm 包。
359
- CLI 用户无需修改代码:
52
+ 没有 key 也能打开会话。输入 `/provider`,按提示填入,或先导出一把。key 只给当前用户读,保存在 `~/.chivgent/auth.json`。
360
53
 
361
54
  ```bash
362
- export OPENAI_API_KEY="your-provider-api-key"
363
- export OPENAI_BASE_URL="https://api.vendor.example/v1"
364
- export OPENAI_MODEL="vendor-model"
365
-
366
- chivgent --provider openai-compatible "src/agent.ts 是做什么的?"
367
- ```
368
-
369
- `OPENAI_BASE_URL` 必须指向供应商的 OpenAI-compatible API 根地址。供应商至少需要
370
- 实现 `POST /chat/completions` 和 Function Tool Calling。
371
-
372
- 如果要在源码中增加一个具名 Provider,可以复用相同的 Adapter:
373
-
374
- ```ts
375
- const client = new OpenAICompatibleChatClient({
376
- apiKey: process.env.VENDOR_API_KEY!,
377
- baseURL: "https://api.vendor.example/v1",
378
- model: "vendor-model",
379
- continuationTag: "vendor-chat",
380
- });
381
- ```
382
-
383
- `DeepSeekChatClient` 就是共享客户端之上的轻量配置包装器。兼容层还会把 DeepSeek
384
- 的 `reasoning_content` 等 Provider 私有字段保存在不透明的 continuation state 中。
385
-
386
- 修改 `baseURL` 不代表所有能力都能完全兼容。不同供应商的模型名称、鉴权方式、
387
- 工具 Schema、Strict Mode、推理字段、流式事件和错误结构都可能不同。供应商差异
388
- 应保留在轻量 Provider Adapter 内,而不是泄漏到 Agent Loop。
389
-
390
- ## 项目结构
391
-
392
- ```text
393
- src/
394
- cli.ts CLI 入口与进程边界
395
- cli-options.ts 参数和 Provider 配置
396
- auth/
397
- credentials.ts 凭据契约与解析顺序
398
- runtime-credentials.ts --api-key 覆盖层
399
- env-credentials.ts 环境变量查找
400
- file-credentials.ts auth.json 存储的读与写
401
- agent.ts Agent Loop 与运行状态
402
- events.ts 运行时事件模型
403
- render.ts 运行时事件的终端渲染
404
- llm.ts Provider 无关的 LLM 契约
405
- retry.ts Provider 超时与重试装饰器
406
- messages.ts 运行时消息模型
407
- session.ts 会话状态与事件分发
408
- session-store.ts JSONL 会话日志与恢复
409
- repl.ts 交互式输入与斜杠命令
410
- context/
411
- context-manager.ts 构造单次请求的消息
412
- compaction.ts 压缩历史并跟踪文件
413
- token-estimator.ts 基于字符的 Token 估算
414
- workspace.ts 工作区配置与只读默认值
415
- workspace/
416
- types.ts 上限、错误类型与 Workspace 契约
417
- paths.ts 路径归一化与逃逸防护
418
- ignore.ts .gitignore 与生成目录过滤
419
- text.ts UTF-8 解码、行切分与预览
420
- read.ts 分段读取
421
- list.ts 目录遍历
422
- search.ts 字面量文本搜索
423
- write.ts 原子整文件写入与精确编辑
424
- providers/
425
- registry.ts Provider 注册表
426
- usage.ts Provider token 用量的解析与累加
427
- deferred-client.ts Provider 可以稍后到位的客户端
428
- definitions.ts 内置 Provider 声明
429
- client.ts 凭据解析并构造 LLM Client
430
- openai.ts OpenAI Responses Adapter
431
- openai-compatible-chat.ts 通用 Chat Completions Adapter
432
- deepseek.ts DeepSeek 配置包装器
433
- tools/
434
- tool.ts Tool 契约
435
- output.ts 共享的 64 KiB 工具输出边界
436
- list-files.ts 确定性的项目树发现工具
437
- search-text.ts 有界的源码字面量搜索工具
438
- read-file.ts 分段文本读取工具
439
- write-file.ts 整文件创建与替换
440
- edit-file.ts 精确唯一匹配编辑
441
- bash.ts Shell 命令执行
442
- prompts.ts Agent 运行时使用的提示词
443
- evals/
444
- task.ts 任务定义与校验
445
- fixture.ts 每次尝试一份全新的工作区副本
446
- graders.ts 确定性判定器
447
- runner.ts 跑 N 次并聚合
448
- report.ts 表格与 JSON 输出
449
- parse-args.ts Eval CLI 参数
450
- cli.ts npm run eval
451
- remote/
452
- protocol.ts 消息形状与版本协商
453
- framing.ts 有界缓冲的 JSON Lines 分帧
454
- server.ts 在 Unix socket 上服务一个会话
455
- client.ts 接入一个已服务的会话
456
- repl.ts 接入端的交互循环
457
- socket-path.ts socket 路径、陈旧检测与发现
458
- extensions/
459
- trust.ts 按目录记录的信任存储
460
- decide-trust.ts 询问流程,以及无人可问时的拒绝
461
- discover.ts 扩展的位置与文件识别规则
462
- api.ts 扩展可以注册什么
463
- registry.ts 收集注册项并拒绝冲突
464
- loader.ts import 模块并隔离其失败
465
- shell/
466
- types.ts 执行契约与 Shell 错误类型
467
- config.ts Shell 解析与 Windows 拦截
468
- local.ts 本地 spawn 后端
469
- process.ts 进程组终止与退出处理
470
- output.ts 有界的流式输出累积器
471
- truncate.ts 命令输出的尾部截断
472
- sanitize.ts 控制字符过滤
473
- tests/ Provider、Agent Loop 和 Workspace 测试
474
- evals/ Eval 任务与其 fixture
475
- docs/ 架构与学习文档
55
+ export OPENAI_API_KEY="your-api-key"
476
56
  ```
477
57
 
478
- ## 开发
58
+ 在交互会话里,`/login oauth openai` 或 `/login oauth xai` 用订阅登录。平常启动时,key 仍然优先。[订阅登录](docs/subscription-oauth.md)。
479
59
 
480
60
  ```bash
481
- npm install
482
- npm run check
483
- npm test
484
- npm run build
61
+ chivgent "入口在哪?" # 读完再答
62
+ chivgent --permission-mode workspace "跑测试并修掉"
63
+ chivgent --permission-mode ask-first "审这个项目"
64
+ chivgent --allow-writes "把 sayHi 改成 greet" # 旧开关,不能和模式一起用
65
+ chivgent --allow-shell "跑测试" # 先确认,再进 Seatbelt 或 Docker
66
+ chivgent --provider deepseek "讲讲 src/"
67
+ chivgent # 本地交互终端默认使用 rich TUI
68
+ chivgent --simple # 使用追加式界面
69
+ chivgent --tui # 显式请求 rich TUI
70
+ chivgent --continue # 这个目录里的上一次会话
485
71
  ```
486
72
 
487
- 运行完整的发布检查(包含 npm tarball dry run):
73
+ ## 模式
488
74
 
489
- ```bash
490
- npm run release:check
491
- ```
75
+ | 模式 | 能直接进行的 | 这一版 |
76
+ | --- | --- | --- |
77
+ | `workspace` | 工作区内的修改和沙箱命令。网络关着。命令要用宿主机时,只为这一条问一次。 | TUI 循环中的 Auto mode,或 `--permission-mode workspace`。 |
78
+ | `ask-first` | 读取。每次改文件、每条命令都要确认。没有答复就没有动作。 | 普通本地 rich TUI 启动,或 `--permission-mode ask-first`。 |
79
+ | `unrestricted` | shell 命令带着宽宿主机文件访问和网络运行,仍在操作系统沙箱里。文件工具不出工作区。 | 显式选择。 |
80
+ | 无 | 读取。写入和 shell 等开关。需要交互式确认或显式全批准。 | `--simple`、headless 和远程启动,除非指定模式或旧权限开关。 |
492
81
 
493
- 构建一个可以在本地安装的 tarball:
82
+ `--permission-mode` 在这个进程里代替那些开关。不能和 `--allow-writes`、`--allow-shell`、`--approve-all-shell`、`--shell-network` 一起用,也不能和 `--serve`、`--connect` 一起用。`/permissions` 查看或更改模式,但只有启动时选了模式的会话才行。这个选择不写进会话记录。恢复会话时用的是启动时选的模式,不是上次更宽的那个。
494
83
 
495
- ```bash
496
- npm install -g "$(npm pack --silent)"
497
- ```
84
+ `/plan` 是另一件事。它让 agent 停在只读工具和一份计划上。即使模型要求,写入、shell 和扩展工具也会被挡住。
498
85
 
499
- 测试使用脚本化或 Mock LLM Client。真实 API Smoke Test 需要手工执行,因此默认
500
- 测试不会消耗 API 额度。
86
+ ## 它怎么接在一起
501
87
 
502
- ## Evals
88
+ 你在终端里打字。chivgent 调用你选的模型。文件工具不出当前目录。shell 在沙箱里运行,拿不到你的模型 key。默认启动时,shell 要打开才出现。
503
89
 
504
- **Agent 任务计划:** 复杂任务可通过参考 Codex 的 `update_plan` 更新结构化任务清单,TUI 显示进度,且不会授予额外文件或 Shell 权限。详见 [Agent 计划与进度](docs/agent-plan-progress.md)。
90
+ <picture>
91
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/fit-zh-dark.svg">
92
+ <img src="docs/assets/fit-zh-light.svg" width="920" alt="默认启动:你、chivgent、你选的模型、当前目录,以及只有打开 shell 才出现的沙箱。">
93
+ </picture>
505
94
 
506
- **TUI V5:** 默认输入框现支持斜杠命令补全、鼠标定位与拖选,以及需逐次确认的只读 `/gitdiff [--staged]` 工作区审查。详见 [TUI V5 指南](docs/tui-v5.md)。
95
+ ## 它能碰到什么
507
96
 
508
- **TUI V4:** `--tui` 默认使用可自由编辑的多行输入框:Enter 发送、Ctrl+O 换行、Ctrl+Z/Y 撤销重做、Ctrl+R 搜索历史。详见 [TUI V4 指南](docs/tui-v4.md)。
97
+ 下面是这一版,不是上面那个 auto 式默认。
509
98
 
510
- **TUI V3:** `/editor` 支持光标移动的真正多行输入(Ctrl+S 发送),`/review` 支持交互式翻页审查补丁,代码块具备基础语法高亮。详见 [TUI V3 指南](docs/tui-v3.md)。
99
+ | | 怎么开 | 能碰到什么 |
100
+ | --- | --- | --- |
101
+ | 读这个项目 | 开着 | 当前目录。被忽略的、生成的和凭据路径会跳过。 |
102
+ | 改文件 | `--allow-writes` | 工作区内的文件。修改必须精确匹配,写入是原子的。 |
103
+ | 跑命令 | `--allow-shell` | macOS 用 Seatbelt,其他系统用 Docker。默认没有网络。每条命令都要确认。 |
104
+ | 选一种策略 | `--permission-mode` | `ask-first`、`workspace` 或 `unrestricted`。在这个进程里代替上面的开关。 |
511
105
 
512
- **TUI V2:** 支持 `/compose` 多行输入、`/diff [页码]` 查看完整补丁、Markdown/代码块显示以及单键 Shell 审批。详见 [TUI V2 指南](docs/tui-v2.md)。
106
+ 费用付给你选的供应商,按他们的价格。chivgent 显示 token 数,不折算成费用。
513
107
 
514
- **并行只读工具:** 内置文件读取、目录列举与搜索最多同时执行 4 个;写入、Shell 与第三方扩展保持独占,模型接收的工具结果顺序不变。详见 [工具调度说明](docs/parallel-read-tools.md)。
108
+ ## 模型
515
109
 
516
- **项目规则:** 支持分目录加载 `AGENTS.md` 与 `AGENTS.override.md`,详细作用域与上下文压缩说明见 [项目指令文档](docs/scoped-agents-md.md)。
110
+ | 供应商 | `--provider` | Key | 模型 |
111
+ | --- | --- | --- | --- |
112
+ | OpenAI,Responses API | `openai`(默认) | `OPENAI_API_KEY` | `gpt-5.6` |
113
+ | DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-v4-flash` |
114
+ | 任何兼容端点 | `openai-compatible` | `OPENAI_API_KEY` 和 `OPENAI_BASE_URL` | 设置 `OPENAI_MODEL` |
115
+ | OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | 设置 `OPENROUTER_MODEL` |
116
+ | Groq | `groq` | `GROQ_API_KEY` | 设置 `GROQ_MODEL` |
117
+ | xAI | `xai` | `XAI_API_KEY` | 设置 `XAI_MODEL` |
118
+ | Moonshot | `moonshot` | `MOONSHOT_API_KEY` | 设置 `MOONSHOT_MODEL` |
517
119
 
518
- **批量补丁:** `--allow-writes` 现在可以使用 `apply_patch` 一次修改多个文件,采用严格上下文校验;参见 [补丁格式和限制](docs/apply-patch.md)。
120
+ `--model` 覆盖模型变量。key 按这个顺序找:`--api-key`,然后环境变量,然后 `~/.chivgent/auth.json`。自定义的 base URL 会收到你的 key 和提示词。只把它指向你信任的端点。
519
121
 
520
- **Shell 安全提示:** `--allow-shell` 现在通过无网络的 Docker 容器执行命令,交互模式会逐条请求确认;非交互模式默认拒绝,除非显式使用 `--approve-all-shell`。限制与风险详见 [Shell 沙箱说明](docs/shell-sandbox.md)。
122
+ 兼容端点必须实现支持工具调用的 `POST /chat/completions`。只换 base URL,并不保证完全兼容。[架构](docs/architecture.md#openai-compatible-providers)。
521
123
 
124
+ ## 会话还在
522
125
 
523
- 评测前请先运行 `npm run eval -- --dry-run --allow-writes --allow-shell`,在不调用模型的情况下校验任务和生成题目指纹。具体方法见 [基线评测指南](docs/eval-baseline-guide.md)。Token 用量缺失会标为未知,而不是 0。
126
+ 不带问题运行 `chivgent`,就是一次交互会话。会话保存在 `~/.chivgent/sessions`。`--continue` 恢复这个目录里最近的一次。`/resume` 列出其余的,并且只打开这里的会话。
524
127
 
128
+ `/compact` 压缩较早的轮次,完整记录仍留着,方便恢复。`/status` 显示模型、权限和 token 用量。其余的,`/help`。
525
129
 
526
- 三百多个单元测试验证的是"代码是否按我写的那样运行",它们对"这是不是一个好用的 Agent"
527
- 一无所知。`npm run eval` 回答后一个问题:
130
+ 本地交互终端默认使用 rich TUI。`--simple` 选择追加式界面;`--tui` 仍可显式请求 rich TUI,包括将 stdout 重定向时。管道输入或输出、JSON、单次提问、`--serve` 和 `--connect` 保持原有简单输出。`TERM=dumb` 自动选择简单界面;设置 `NO_COLOR` 时仍使用 rich TUI,但不输出颜色。没有旧权限开关或显式权限模式时,rich TUI 默认使用 Ask for me。空闲输入框中的 Shift+Tab 可循环切换 Plan → Auto mode → Ask for me,页脚显示当前模式。简洁欢迎信息不显示 session ID;用 `/session` 或 `/status` 查看完整信息,`/help` 查看命令和快捷键。显式 `--permission-mode` 和旧权限开关继续按原有方式工作。[TUI](docs/tui.md)。长会话会压缩较早的轮次,并留下读过和改过的文件列表,从而留在上下文窗口里。[上下文](docs/architecture.md#context-management)。
528
131
 
529
- ```bash
530
- npm run eval # 跑 evals/ 下全部任务
531
- npm run eval -- --task rename-symbol --attempts 10
532
- npm run eval -- --allow-writes --allow-shell --json report.json
533
- DEEPSEEK_API_KEY=sk-... npm run eval -- --provider deepseek
534
- ```
132
+ 在内联编辑器中输入 `/` 可浏览已注册命令,继续输入可筛选命令。用 ↑/↓ 选择命令,再按 Tab 或 Enter 将其补全到草稿;之后按 Enter 提交。鼠标单击可补全可见命令,滚轮可浏览菜单。Esc 关闭菜单并保留草稿。完整输入的命令仍可直接按一次 Enter 执行。
535
133
 
536
- `--api-key` 也能用,但这里更推荐环境变量:npm 会在执行前把展开后的完整命令回显出来,
537
- 写在命令行上的 key 会明文出现在任何被采集的日志里。
134
+ ## 边界
538
135
 
539
- ```text
540
- task pass turns tokens tools p50
541
- find-auth-logic 4/5 2.0 3.4k list_files,search_text,... 6.1s
542
- rename-symbol 3/5 5.4 12.1k read_file,edit_file 9.8s
136
+ - 工作区以外的路径会被拒绝,包括 `..` 和符号链接逃逸。常见凭据路径会被拒绝。工具输出有上限。
137
+ - 没有选模式或旧权限开关时,`--simple`、headless 和远程启动仍为只读。本地 rich TUI 启动会为当前进程选择 `ask-first`,除非指定了旧权限开关。`--allow-shell` 需要交互式确认;非交互式命令除非指定 `--approve-all-shell`,否则会被拒绝。`--serve` 会话除非同时加上 `--approve-all-shell`,否则拒绝命令。沙箱起不来,命令不会改到无沙箱运行。
138
+ - `workspace` 自动执行工作区内的修改和沙箱命令,命令要访问宿主机文件和网络时问一次。`unrestricted` 是显式选择的宽宿主机访问和网络。这些都不随会话保存。[Shell 安全](docs/shell-sandbox.md)。
139
+ - macOS 沙箱仍然能读当前用户能读的文件。它限制的是写入和网络,不是机密隔离。
140
+ - 还没有按命令的允许列表,也没有原生 Windows shell。`workspace` 不是这一版的启动模式。
141
+ - 扩展是跑在 chivgent 进程里的 JavaScript。信任一个项目,就是允许它的作者以你的身份执行代码。每个目录问一次;没有终端可问时,不加载。
142
+ - key、订阅凭据和会话日志只对文件所有者可读,而且是明文。日志里有提示词、回答和文件摘录。
143
+ - `--serve` 把会话放在仅所有者可进的目录里的 Unix socket 上。谁能打开这个 socket,谁就拿到服务器启动时的全部能力。没有 TCP 监听。
543
144
 
544
- overall 7/10 (70%) 58.3k tokens
145
+ 在已提交的代码上工作。不信任的仓库,用一份副本。
545
146
 
546
- rename-symbol attempt 2,5: not-used-tool name="write_file" — called write_file 1 time(s)
547
- ```
548
-
549
- `tokens` 那一列是 Provider 自己报的数字,于是一次改动可以同时看两个维度:
550
- 一个把通过率从 70% 提到 75%、token 却翻三倍的 prompt 改动通常是笔坏买卖,
551
- 而少了第二个数字它看起来是纯赚。
552
-
553
- **Eval 不是测试。** 模型是不确定的,所以一个任务跑一次就是抛硬币:通过与失败不是这次
554
- 运行的属性,而是"模型 + harness"这个组合的属性。因此 eval 的基本单位是 **N 次尝试的
555
- 通过率**;也因此 **evals 绝不进 CI 门禁**——让 CI 花钱、并且以六成概率变红,两周内就会
556
- 训练所有人忽略 CI。`npm test` 依然免费、离线、确定。
557
-
558
- 一个任务就是 `evals/` 下的一个目录:一份 `task.json` 加一棵 `fixture/` 文件树,
559
- 每次尝试都复制到一个全新的临时工作区。
560
-
561
- ```jsonc
562
- {
563
- "prompt": "把 sayHi 重命名成 greet,包括所有调用处。",
564
- "capabilities": ["writes"],
565
- "attempts": 5,
566
- "graders": [
567
- { "type": "file-contains", "path": "src/greet.ts", "text": "export function greet(" },
568
- { "type": "file-excludes", "path": "src/greet.ts", "text": "sayHi" },
569
- { "type": "used-tool", "name": "edit_file" },
570
- { "type": "not-used-tool", "name": "write_file" }
571
- ]
572
- }
573
- ```
147
+ 这个仓库还包含 Electron 桌面应用。它通过原生目录选择器打开项目,并用同一 Agent 和只读项目工具运行已配置的 Provider。桌面端仅从环境变量或 CLI `auth.json` 读取 API Key,不保存凭证;它不会读取 `oauth.json` 或刷新订阅 OAuth 凭证。仅通过 `/login oauth` 登录的用户,使用桌面端前还需配置 API Key。选择兼容 Provider 时,请先核对界面显示的请求端点,再发送项目内容。目前仅有一个内存会话,不提供历史列表或重启恢复。设置 `CHIVGENT_DESKTOP_DEMO=1` 可显式启用离线演示。使用 `npm run desktop:dev` 开发;离线真实 adapter 验收运行 `npm run desktop:build && node apps/desktop/scripts/verify-readonly.mjs`。[桌面行为与限制](docs/desktop-architecture.md)。
574
148
 
575
- 判定器全部是确定性的——文件内容、工具调用、回答匹配、轮数。**没有 LLM judge**:
576
- 判官本身就是一个不确定的测量仪器,用它去测量不确定的对象,两个误差会叠加,
577
- 最后你无法分辨"Agent 变差了"和"判官今天心情不同"。
578
-
579
- 最后两条判定才是重点。整文件重写同样能得到正确的内容,一个只看结果的 eval 会给它满分。
580
- 对编码 Agent 来说,**工具误用是比答错更常见、也更值得测的失败**,所以任务不仅判定
581
- "做对了没有",还判定"是怎么做的"。
582
-
583
- **一条判定如果点名了这个任务拿不到的工具,加载任务时就会报错。** 只读任务上的
584
- `not-used-tool: write_file` 永远不可能失败——它断言模型没调用一个它从没拿到过的工具;
585
- 反过来 `used-tool` 一个拿不到的工具则永远不可能通过。两者都是任务定义写错了,都在花第一
586
- 分钱之前拦下来。这类判定曾经在仓库里躺了两个版本。
587
-
588
- 有三条判定的存在,是因为"用过哪些工具"这个集合回答不了它们回答的问题。
589
- `tool-never-failed` 抓瞎猜路径——猜错的读必然报错,而去重后的名字集合看不见这件事。
590
- 它只在"失败即出错"的工具上成立:`read_file` 成立,`bash` 不成立——后者对任何非零退出码
591
- 都报错,而在"先跑一次让它挂"的任务上,失败正是对的。`max-tool-calls` 卡的是调用次数
592
- 而不是轮数,因为一轮里可以发起任意多次调用:"到底是顺着 import 找过去,还是把整个项目
593
- 读了一遍",这是个次数问题;
594
- `file-unchanged` 拿 fixture 原件做字节比较,把"别动没让你动的东西"从愿望变成判定,
595
- 而且不需要在 `task.json` 里再抄一份文件内容(抄一份就会有忘记同步的那天)。
596
- JSON 报告里多了 `toolCalls`:按调用顺序记录每一次调用和它成功与否,和表格用的
597
- `toolsUsed` 集合并存。
598
-
599
- 九个任务分成"找到了什么"和"改了什么"两类。其中四个的存在理由是:第一次真实基线
600
- 30 次里过了 29 次,也就是什么都没测出来——`needle-in-many-files`(40 个文件名毫无
601
- 信息量,`list_files` 给不出线索)、`trace-the-default`(一个值被覆盖两次,停在第一跳
602
- 会得到一个看起来很合理的错数字)、`decoy-config`(两个几乎一样的模块,其中一个是死代码)、
603
- `wrong-test`(挂掉的测试,其期望与源码里写明的契约矛盾,改源码去迎合它就是陷阱)。
604
-
605
- **一个任务如果在不同模型上都是 0/N 或都是 N/N,它就没在测东西**,应该改难、改对,
606
- 或者删掉。全都过不是质量的证据。
607
-
608
- ## Token 用量
609
-
610
- Provider 每次调用都会报告代价,chivgent 现在会把它留下来:每轮在 `message_end` 上、
611
- 整次运行在 `agent_end` 和运行结果上、每次 eval 尝试在报告里、以及 `/session` 的累计。
612
-
613
- ```text
614
- › /session
615
- id: 2026-09-07T...
616
- workspace: /home/me/project
617
- prompts: 4
618
- messages: 18
619
- tokens: 38.2k total, 35.9k in, 2.3k out, 12.0k cached
620
- ```
621
-
622
- **只报 token,不报钱。** 换算成金额需要一张"模型 → 单价"的表,而那张表会在 Provider
623
- 调价时**悄悄过期**,产出一个看起来精确、实际错误的金额——那比没有数字更糟,因为没人
624
- 会去质疑一个带小数点的数。token 数是 Provider 自己报的事实,不会过期。
625
-
626
- 某次调用没报用量时,合计会被**标记为不完整**,而不是当成零:缺失的数字要看得见地
627
- 缺着,而不是悄悄把合计压小。压缩自己那次摘要调用**计入**合计——它拿"现在多花一次
628
- 调用"换"后续输入更小",少算一半就没法判断这笔交易划不划算。
629
-
630
- 流式的 Chat Completions 默认不报用量,必须显式索取,所以 chivgent 会发送
631
- `stream_options: { include_usage: true }`。某些较早的自建 OpenAI 兼容端点可能不认识
632
- 这个字段而拒绝请求,此时 `--no-stream` 是退路。
633
-
634
- ## 活的区域
635
-
636
- `--tui` 会在提示行上方画一块状态区域:第几轮、模型在想还是在跑工具、哪几个工具在跑、
637
- 各自针对什么、最新一行输出、已经跑了多久、累计花了多少 token。
638
-
639
- ```text
640
- Let me look at how sessions are stored.
641
- read_file src/session.ts (2s)
642
- search_text (1s) src/store.ts:41: export class SessionStore
643
- running tools · turn 2/8 · 11s · 4.2k tokens · ctrl+c to stop
644
- ```
645
-
646
- 它是事件流的第一个**反向**消费者。其他所有消费者——流式输出、会话日志、扩展、远程
647
- 客户端——都是追加式的:事件来了就打印,打印完就忘掉。这一个必须从同一条流里**重建出
648
- "现在是什么状态"**,并随着新事件到达保持它正确。这件事拆成三段:
649
-
650
- ```text
651
- reduce(state, event, now) -> ViewState 纯函数
652
- view(state, {width, now}) -> string[] 纯函数
653
- paint(previous, next) 唯一碰终端的地方
654
- ```
655
-
656
- 两个纯函数不需要终端就能测,真正的保证在那里。让状态模型成为承重结构的,是事件词汇表
657
- 里的一处不对称:`message_update` 带的是**增量**,`tool_execution_update` 带的是被截断过
658
- 的**快照**。对快照做追加,会堆出从未产生过的输出——那个窗口的头部早就被丢掉了;对增量
659
- 做替换,则只剩最后一个 token,整个回答就没了。追加式渲染器**永远不需要知道这个区别**,
660
- 因为它只管把收到的东西打出去。有两条测试,写反哪一边红哪一边。
661
-
662
- 它**不接管备用屏**。切过去滚动历史就没了,要补回来就得重新实现滚动、搜索和选中——
663
- 几千行,而且教的是终端图形,不是 Agent 架构。已完成的一轮会被刷进终端自己的滚动历史
664
- 并从状态里丢掉,所以再长的会话也不会把状态撑大。输入继续交给 readline 也是同一个理由:
665
- 行编辑器不是这里值得解决的问题。
666
-
667
- 绘制只重写内容变了的行,所以一个跳动的计时器只花一行,而不是重画整块。改变窗口大小会
668
- 让这个基准失效——终端已经把屏幕上的内容重新折行了——那是唯一一个"整块重画才是对的"
669
- 时刻。
670
-
671
- `--tui` 需要 stdin 和 stderr 都是终端,且是交互式会话;不满足时**直接报错而不是静默
672
- 退回**,因为一个安静地什么都不做的开关,会让打错的命令看起来像成功了。目前**不是默认**。
673
- 简短的启动提示显示供应商、模型、工作目录和下一步操作;用 `/session` 查看会话 ID。
674
- 输入 `/` 即在当前输入行下方显示斜杠命令(包括扩展命令),可用上下键选择、Tab 补全;
675
- 上下键浏览输入历史。运行中普通输入会被忽略,Ctrl+C 仍可立即取消;取消后恢复提示符,
676
- 已收到的部分回答保留在滚动历史中。运行结束会显示结果、耗时和已报告的 token 用量。
677
-
678
- 中文、组合字符和 Emoji 按终端列宽截断;长的单行回答保留最新文字,长路径保留文件名和
679
- 工具进度,窄窗口优先显示状态与停止快捷键。较矮的窗口优先保留状态行,缩放会重绘当前
680
- 区域。提示符及界面走 stderr,最终回答走 stdout,可用 `chivgent --tui > answers.txt`
681
- 保存回答,同时继续在终端交互。
682
-
683
- ```sh
684
- npm run build
685
- node dist/cli.js --tui
686
- npm run demo:tui # 离线交互演示,无需 API Key 或网络
687
- ```
688
-
689
- 演示使用模拟供应商与工具进度,不修改工作区。操作说明和验证方式见 [TUI 指南](docs/tui.md)。
690
-
691
- ## 远程会话
692
-
693
- 会话通常和启动它的那个终端同生共死。`--serve` 让它留在一个进程里,其他进程可以接上来:
694
-
695
- ```bash
696
- # 终端 A
697
- chivgent --serve --allow-writes
698
- # chivgent 0.12.0 serving session 2026-09-06T...
699
- # socket: ~/.chivgent/sockets/2026-09-06T....sock
700
- # capabilities: --allow-writes
701
-
702
- # 终端 B
703
- chivgent --servers # 列出正在运行的服务端
704
- chivgent --connect <id> # 交互式接入
705
- chivgent --connect <id> "问题" # 问一次就走
706
- ```
707
-
708
- 客户端不持有任何状态:它只负责发 prompt、渲染服务端广播的事件流。可以同时接入多个
709
- 客户端——多出来的那些实时观察同一次运行,并且**任何一个都能中断它**,因为运行属于会话,
710
- 不属于"谁发起的"。运行期间再来一个 prompt 会被拒绝而不是排队;客户端中途断开也不会
711
- 取消正在跑的运行。
712
-
713
- 接入时**不重放历史**——那是会话日志的职责。协议是 Unix socket 上的 JSON Lines,
714
- 一行一个对象,和 `--json` 输出同一种形状。
715
-
716
- **能连上这个 socket 的人,拥有服务端启动时的全部能力。** 如果它是带 `--allow-shell`
717
- 起来的,那么任何能连上的人都能让模型执行命令——这正是服务端启动时会打印本次能力的原因:
718
- 接入的人看不到你敲了什么参数。边界是文件系统权限(socket 位于 `CHIVGENT_HOME` 下仅
719
- 所有者可访问的目录),并且**不监听 TCP**。要跨机器访问请用 SSH 转发,让认证由 SSH 负责。
720
-
721
- ## 扩展系统
722
-
723
- 一个扩展就是默认导出一个函数的 ES 模块。启动时它会被调用一次,拿到的 API 可以注册
724
- 工具、注册斜杠命令、订阅运行时事件、追加 system prompt。
725
-
726
- ```js
727
- // .chivgent/extensions/word-count.js
728
- export default function (api) {
729
- api.registerTool({
730
- name: "word_count",
731
- description: "统计工作区某个文件的词数。",
732
- inputSchema: {
733
- type: "object",
734
- properties: { path: { type: "string" } },
735
- required: ["path"],
736
- additionalProperties: false,
737
- },
738
- async execute(args, context) {
739
- const file = await context.workspace.readTextFile(args.path);
740
- return {
741
- content: `${file.content.split(/\s+/).filter(Boolean).length} words`,
742
- isError: false,
743
- };
744
- },
745
- });
746
-
747
- api.contributeSystemPrompt("需要数词数时用 word_count,不要整篇读文件。");
748
- }
749
- ```
750
-
751
- 扩展从两个位置加载:
149
+ ## 文档
752
150
 
753
- | 位置 | 何时加载 |
151
+ | | |
754
152
  | --- | --- |
755
- | `<CHIVGENT_HOME>/extensions/` | 始终加载——那是你自己机器上的配置 |
756
- | `<工作区>/.chivgent/extensions/` | 只有在你信任该项目之后 |
757
-
758
- 两处都接受 `name.js` 和 `name/index.js`,不再往下递归。**只支持纯 JavaScript**:
759
- 用 TypeScript 写的话自己编译成 `.js` 即可,为此让 chivgent 背上一个 TS 运行时加载器
760
- 不划算。与内置工具或命令重名的注册会被拒绝,所以扩展无法顶替 `read_file` 或
761
- `/clear`;某个扩展坏掉只会被报告并跳过,不会把 chivgent 一起带走。
762
-
763
- `chivgent --extensions` 可以列出加载了哪些扩展、各自注册了什么。
764
-
765
- ### Project Trust
766
-
767
- 第一次在带扩展的项目里运行 chivgent 时,它会说明这些扩展是什么并询问一次。答案记在
768
- `<CHIVGENT_HOME>/trust.json` 里,键是解析后的目录路径,**按最近祖先匹配**——信任
769
- `~/work` 就覆盖它下面所有仓库。`--forget-trust` 可以删掉覆盖当前工作区的那条决定。
770
-
771
- **信任一个项目,等于允许这个仓库的作者以你的身份执行代码。** 扩展在 chivgent 进程内
772
- 运行:不受工作区边界约束,不需要 `--allow-shell` 就能执行命令,也能读到你环境变量里的
773
- API Key。这正是询问必须发生在 import 之前的原因,也是"没人可问时一律拒绝"的原因——
774
- CI 任务或管道运行永远不会自作主张去执行一个 clone 里的代码。
775
-
776
- ## 安全模型
777
-
778
- - API Key 依次从 `--api-key`、环境变量、可选的 `auth.json` 解析,绝不能提交到仓库。
779
- - `auth.json` 以明文保存 Key,因此它是可选的;当文件权限允许其他用户读取时会告警。
780
- - `/login` 会以仅所有者可读的权限写入,输入也不回显,但 Key 依然是明文存储的——
781
- 它是一个便利设施,不是密钥管理器。
782
- - 该文件只接受字面量 Key,无法展开环境变量或执行 Shell 命令。
783
- - 自定义 `OPENAI_BASE_URL` 会收到配置的 API Key 和提示词,只能使用可信端点。
784
- - 不传 `--allow-writes` 时工作区工具全部只读,`write_file` 和 `edit_file` 根本
785
- 不会被注册。
786
- - `bash` 工具需要 `--allow-shell`,它与 `--allow-writes` 相互独立,永远不会被后者
787
- 顺带打开。授予它意味着授予大得多的权限:Shell 能改动或删除运行 chivgent 的用户
788
- 能碰的任何东西,无论是否在工作区内,下面这些工作区限制对它一律不适用。
789
- - 命令运行在独立进程组中并按组杀死,取消一次运行不会留下孤儿后代进程。
790
- - 命令会继承 chivgent 的环境变量,其中包含它正在使用的 API Key。开了
791
- `--allow-shell` 的模型可以读到这个 Key 以及环境里的其他内容。请只带上这次任务
792
- 真正需要的环境变量。
793
- - 被截断的命令输出会写入临时文件供模型按需读取。该文件仅所有者可读,但运行结束后
794
- **不会自动删除**,其中可能包含命令打印的任何内容。处理敏感数据后请清理临时目录。
795
- - 开启 `--allow-shell` 后,会话日志除文件片段外还会记录命令输出。
796
- - 项目扩展只有在存在明确的、已记录的信任决定之后才会加载,且没有终端可问时绝不加载。
797
- 扩展以你的权限在进程内运行,上面那些工作区限制对它一概不适用——因此"信任一个项目"
798
- 与 `--allow-shell` 是同一量级的授权,区别只在于前者由 clone 一个仓库触发,
799
- 后者要你亲手敲一个参数。
800
- - `<CHIVGENT_HOME>/extensions/` 下的用户级扩展始终加载,那是你自己的配置。
801
- - 扩展无法占用内置工具或内置命令的名字。
802
- - `trust.json` 与会话日志、认证文件一样,以仅所有者可读的权限写入。
803
- - 被服务的会话对任何能打开其 socket 的人可达,且他们获得服务端启动时的全部能力。
804
- socket 位于仅所有者可访问的目录中;**真正起作用的是目录权限**,因为 socket 文件在
805
- 能被收紧权限之前会短暂地以默认权限存在。
806
- - chivgent 从不监听 TCP。跨机器访问请使用 SSH 转发。
807
- - 写入会解析到最深层已存在的祖先目录,路径上任何一段是符号链接都会被拒绝,
808
- 因此预先植入的链接无法把写入重定向到工作区之外。
809
- - 写入先落到同目录的临时文件再 rename 就位,中断的写入不会截断已有文件。
810
- - `edit_file` 在 `old_text` 找不到或命中多处时拒绝执行,宁可失败也不改错行。
811
- - 文件路径必须位于当前工作区内。
812
- - Real Path 检查会阻止 `..` 路径穿越和符号链接逃逸。
813
- - 文件大小和二进制内容检查会限制不安全的读取。
814
- - 自动发现遵循根 `.gitignore` 和固定的生成目录忽略规则。
815
- - 所有工具统一禁止常见凭据、私钥和敏感配置路径。
816
- - 工具结果限制为 64 KiB,读取、扫描、深度和结果数量都有硬上限。
817
- - 工具输入是不可信数据,执行前必须验证。
818
- - Agent 会在有限的模型轮数后终止。
819
- - `~/.chivgent/sessions` 下的会话日志包含提问、回答和工具结果(含文件片段)。
820
- 在敏感项目中请使用 `--no-session`,并像对待项目本身一样对待该目录。
821
- - Session id 在拼接成文件路径前会先做校验。
822
-
823
- 这是一个用于学习的 MVP,并不是经过加固的 Sandbox。它**没有权限系统**:能力开关
824
- 都是会话级的粗粒度开关,`--allow-writes` 和 `--allow-shell` 都不会对每次操作逐一
825
- 确认。这是有意为之:有了 Shell 之后,命令白名单能被一行 `sh -c` 绕开,而逐次弹确认
826
- 只会训练用户无脑确认,所以 chivgent 选择把边界说清楚,而不是假装能拦住什么。
827
-
828
- 请在已提交的代码上使用这些开关。如果需要真正的边界,请把整个进程放进容器,并只给
829
- 容器这次任务真正需要的东西。在允许它访问敏感项目之前,请先审查代码和威胁模型。
830
-
831
- ## 路线图
832
-
833
- - [x] 最小 Tool Calling Agent Loop
834
- - [x] 安全的 `read_file` 工具
835
- - [x] OpenAI 和 DeepSeek Provider
836
- - [x] 通用 OpenAI-compatible Chat Completions Adapter
837
- - [x] 自定义 OpenAI-compatible CLI Provider
838
- - [x] 项目发现工具:`list_files`、`search_text` 和分段 `read_file`
839
- - [x] 流式输出和运行时事件
840
- - [x] 持久化多轮 Session
841
- - [x] Context Window 管理和压缩
842
- - [x] 通过 `--allow-writes` 选择性开启的 `write_file` 和 `edit_file`
843
- - [x] Provider Registry 与凭据解析链
844
- - [x] 通过 `--allow-shell` 开启的 `bash` 工具,带流式输出
845
- - [x] 扩展系统与 Project Trust
846
- - [x] 基于本地 socket 的远程会话
847
- - [x] 带确定性判定器和通过率的 Eval 套件
848
- - [x] Provider Token 用量采集与聚合
849
- - [x] 由事件流驱动的活状态区域(`--tui`)
850
-
851
- 逐条命令确认和命令白名单是**主动放弃**的方向。有了 Shell 工具之后,`bash` 能做的
852
- 事是 `write_file` 的超集,任何白名单都能被一行 `sh -c` 绕开,而逐条弹确认只会训练
853
- 用户无脑确认。能力开关一律是会话级的粗粒度开关,真正的边界是容器。
854
-
855
- ## 文档
153
+ | [架构](docs/architecture.md) | 循环、供应商、上下文,以及源码在哪 |
154
+ | [Shell 安全](docs/shell-sandbox.md) | Seatbelt、Docker,以及一条命令仍能读到什么 |
155
+ | [订阅登录](docs/subscription-oauth.md) | ChatGPT 和 xAI:存了什么,key 仍然做什么 |
156
+ | [TUI](docs/tui.md) | 实时状态区、编辑器、diff 审阅 |
157
+ | [Evals](docs/evals.md) | agent 能不能把活干完,和单元测试分开 |
158
+ | [扩展与远程会话](docs/extensions-and-remote.md) | 你加的工具,以及另一个终端怎么接上来 |
856
159
 
857
- - [Stage 1:Minimal Agent 设计](docs/stage-1-minimal-agent.md)
858
- - [DeepSeek Provider 设计](docs/deepseek-provider.md)
859
- - [Stage 2:Project Discovery 实现设计](docs/stage-2-project-discovery.md)
860
- - [Stage 3:Runtime Events 与流式输出设计](docs/stage-3-runtime-events.md)
861
- - [Stage 4:Session 与交互模式设计](docs/stage-4-sessions.md)
862
- - [Stage 5:写入工具与 Workspace 拆分](docs/stage-5-write-tools.md)
863
- - [Stage 6:Provider Registry 与凭证解析链](docs/stage-6-provider-registry.md)
864
- - [Stage 7:上下文预算与压缩](docs/stage-7-context-management.md)
865
- - [Stage 8:Shell 工具与流式子进程](docs/stage-8-shell-tool.md)
866
- - [Stage 9:扩展系统与 Project Trust](docs/stage-9-extensions.md)
867
- - [Stage 10:远程会话](docs/stage-10-remote-sessions.md)
868
- - [Stage 11:Evals](docs/stage-11-evals.md)
869
- - [Stage 12:Token 用量](docs/stage-12-usage.md)
870
- - [发布流程](docs/releasing.md)
871
-
872
- ## 参与贡献
873
-
874
- 欢迎提交 Issue 和范围明确的 Pull Request。提交修改前请运行:
160
+ ## 开发
875
161
 
876
162
  ```bash
877
- npm run check
163
+ git clone https://github.com/chivopic/chivgent.git
164
+ cd chivgent
165
+ npm install
878
166
  npm test
879
- npm run build
880
167
  ```
881
168
 
882
- 请将 Provider 特有的类型保留在 `src/providers/` 中,并确保核心 Agent Runtime
883
- 不依赖供应商 SDK 的数据结构。
169
+ 测试用脚本化的模型客户端,不花 API 额度。欢迎 issue,也欢迎范围清楚的 pull request。供应商专有的类型留在 `src/providers/`,这样 agent 不依赖某一家的 SDK。
884
170
 
885
171
  ## 许可证
886
172
 
887
- 本项目使用 [MIT License](LICENSE)。
173
+ [MIT](LICENSE)