neoctl 0.2.40 → 0.2.42

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 (206) hide show
  1. package/README.md +43 -585
  2. package/dist/context/prompts.js +2 -1
  3. package/dist/context/prompts.js.map +1 -1
  4. package/dist/core/run-agent.js +2 -0
  5. package/dist/core/run-agent.js.map +1 -1
  6. package/dist/repl/index.js +13 -1
  7. package/dist/repl/index.js.map +1 -1
  8. package/dist/tools/builtins/image-capabilities.d.ts +21 -0
  9. package/dist/tools/builtins/image-capabilities.js +97 -0
  10. package/dist/tools/builtins/image-capabilities.js.map +1 -0
  11. package/dist/tools/builtins/image-generation-tool.d.ts +14 -7
  12. package/dist/tools/builtins/image-generation-tool.js +141 -143
  13. package/dist/tools/builtins/image-generation-tool.js.map +1 -1
  14. package/dist/tools/builtins/image-output-verification.d.ts +14 -0
  15. package/dist/tools/builtins/image-output-verification.js +183 -0
  16. package/dist/tools/builtins/image-output-verification.js.map +1 -0
  17. package/dist/tools/registry.d.ts +2 -0
  18. package/dist/tools/registry.js +6 -0
  19. package/dist/tools/registry.js.map +1 -1
  20. package/dist/tools/run-tool-use.js +5 -0
  21. package/dist/tools/run-tool-use.js.map +1 -1
  22. package/dist/web/image-result-metadata.d.ts +20 -0
  23. package/dist/web/image-result-metadata.js +20 -0
  24. package/dist/web/image-result-metadata.js.map +1 -0
  25. package/dist/web/index.d.ts +4 -1
  26. package/dist/web/index.js +20 -5
  27. package/dist/web/index.js.map +1 -1
  28. package/docs/prompt-config.md +1 -1
  29. package/package.json +22 -20
  30. package/dist/agents/agent-report-security.test.d.ts +0 -1
  31. package/dist/agents/agent-report-security.test.js +0 -301
  32. package/dist/agents/agent-report-security.test.js.map +0 -1
  33. package/dist/agents/agent-tool-persistence.test.d.ts +0 -1
  34. package/dist/agents/agent-tool-persistence.test.js +0 -319
  35. package/dist/agents/agent-tool-persistence.test.js.map +0 -1
  36. package/dist/agents/coordination-boundary.test.d.ts +0 -1
  37. package/dist/agents/coordination-boundary.test.js +0 -58
  38. package/dist/agents/coordination-boundary.test.js.map +0 -1
  39. package/dist/agents/live-preview-redaction-security.test.d.ts +0 -1
  40. package/dist/agents/live-preview-redaction-security.test.js +0 -286
  41. package/dist/agents/live-preview-redaction-security.test.js.map +0 -1
  42. package/dist/agents/no-nested-delegation.test.d.ts +0 -1
  43. package/dist/agents/no-nested-delegation.test.js +0 -38
  44. package/dist/agents/no-nested-delegation.test.js.map +0 -1
  45. package/dist/agents/obs07-11-run-facts.test.d.ts +0 -1
  46. package/dist/agents/obs07-11-run-facts.test.js +0 -459
  47. package/dist/agents/obs07-11-run-facts.test.js.map +0 -1
  48. package/dist/agents/smoke-agent-lifecycle.d.ts +0 -1
  49. package/dist/agents/smoke-agent-lifecycle.js +0 -192
  50. package/dist/agents/smoke-agent-lifecycle.js.map +0 -1
  51. package/dist/agents/smoke-agents.d.ts +0 -1
  52. package/dist/agents/smoke-agents.js +0 -362
  53. package/dist/agents/smoke-agents.js.map +0 -1
  54. package/dist/context/prompt-config.test.d.ts +0 -1
  55. package/dist/context/prompt-config.test.js +0 -128
  56. package/dist/context/prompt-config.test.js.map +0 -1
  57. package/dist/context/smoke-context.d.ts +0 -1
  58. package/dist/context/smoke-context.js +0 -502
  59. package/dist/context/smoke-context.js.map +0 -1
  60. package/dist/core/obs04-visible-data.test.d.ts +0 -1
  61. package/dist/core/obs04-visible-data.test.js +0 -147
  62. package/dist/core/obs04-visible-data.test.js.map +0 -1
  63. package/dist/core/query-session-settings.test.d.ts +0 -1
  64. package/dist/core/query-session-settings.test.js +0 -252
  65. package/dist/core/query-session-settings.test.js.map +0 -1
  66. package/dist/core/run-agent-pause.test.d.ts +0 -1
  67. package/dist/core/run-agent-pause.test.js +0 -52
  68. package/dist/core/run-agent-pause.test.js.map +0 -1
  69. package/dist/core/run-agent-persistence.test.d.ts +0 -1
  70. package/dist/core/run-agent-persistence.test.js +0 -222
  71. package/dist/core/run-agent-persistence.test.js.map +0 -1
  72. package/dist/core/session-prompt-inheritance.test.d.ts +0 -1
  73. package/dist/core/session-prompt-inheritance.test.js +0 -132
  74. package/dist/core/session-prompt-inheritance.test.js.map +0 -1
  75. package/dist/core/session-settings-prompt.test.d.ts +0 -1
  76. package/dist/core/session-settings-prompt.test.js +0 -266
  77. package/dist/core/session-settings-prompt.test.js.map +0 -1
  78. package/dist/core/smoke-core-loop.d.ts +0 -1
  79. package/dist/core/smoke-core-loop.js +0 -290
  80. package/dist/core/smoke-core-loop.js.map +0 -1
  81. package/dist/core/smoke-image-integrity.d.ts +0 -1
  82. package/dist/core/smoke-image-integrity.js +0 -67
  83. package/dist/core/smoke-image-integrity.js.map +0 -1
  84. package/dist/execution/docker.test.d.ts +0 -1
  85. package/dist/execution/docker.test.js +0 -57
  86. package/dist/execution/docker.test.js.map +0 -1
  87. package/dist/model/openai-native-image-guard.test.d.ts +0 -1
  88. package/dist/model/openai-native-image-guard.test.js +0 -101
  89. package/dist/model/openai-native-image-guard.test.js.map +0 -1
  90. package/dist/model/openai-responses-instructions.test.d.ts +0 -1
  91. package/dist/model/openai-responses-instructions.test.js +0 -64
  92. package/dist/model/openai-responses-instructions.test.js.map +0 -1
  93. package/dist/model/smoke-openai.d.ts +0 -1
  94. package/dist/model/smoke-openai.js +0 -44
  95. package/dist/model/smoke-openai.js.map +0 -1
  96. package/dist/model/smoke-responses-mapper.d.ts +0 -1
  97. package/dist/model/smoke-responses-mapper.js +0 -156
  98. package/dist/model/smoke-responses-mapper.js.map +0 -1
  99. package/dist/plugins/smoke-plugin-system.d.ts +0 -1
  100. package/dist/plugins/smoke-plugin-system.js +0 -63
  101. package/dist/plugins/smoke-plugin-system.js.map +0 -1
  102. package/dist/repl/smoke-run-command.d.ts +0 -1
  103. package/dist/repl/smoke-run-command.js +0 -43
  104. package/dist/repl/smoke-run-command.js.map +0 -1
  105. package/dist/secrets/smoke-secrets.d.ts +0 -1
  106. package/dist/secrets/smoke-secrets.js +0 -73
  107. package/dist/secrets/smoke-secrets.js.map +0 -1
  108. package/dist/session/session-store-safety.test.d.ts +0 -1
  109. package/dist/session/session-store-safety.test.js +0 -107
  110. package/dist/session/session-store-safety.test.js.map +0 -1
  111. package/dist/session/smoke-session.d.ts +0 -1
  112. package/dist/session/smoke-session.js +0 -233
  113. package/dist/session/smoke-session.js.map +0 -1
  114. package/dist/skills/smoke-skills.d.ts +0 -1
  115. package/dist/skills/smoke-skills.js +0 -109
  116. package/dist/skills/smoke-skills.js.map +0 -1
  117. package/dist/tasks/subagent-tools.test.d.ts +0 -1
  118. package/dist/tasks/subagent-tools.test.js +0 -159
  119. package/dist/tasks/subagent-tools.test.js.map +0 -1
  120. package/dist/tasks/task-ack-size.test.d.ts +0 -1
  121. package/dist/tasks/task-ack-size.test.js +0 -101
  122. package/dist/tasks/task-ack-size.test.js.map +0 -1
  123. package/dist/tasks/task-persistence.test.d.ts +0 -1
  124. package/dist/tasks/task-persistence.test.js +0 -331
  125. package/dist/tasks/task-persistence.test.js.map +0 -1
  126. package/dist/tools/smoke-exec-process.d.ts +0 -1
  127. package/dist/tools/smoke-exec-process.js +0 -127
  128. package/dist/tools/smoke-exec-process.js.map +0 -1
  129. package/dist/tools/smoke-terminal-output-chain.d.ts +0 -1
  130. package/dist/tools/smoke-terminal-output-chain.js +0 -245
  131. package/dist/tools/smoke-terminal-output-chain.js.map +0 -1
  132. package/dist/tools/smoke-tool-system.d.ts +0 -1
  133. package/dist/tools/smoke-tool-system.js +0 -412
  134. package/dist/tools/smoke-tool-system.js.map +0 -1
  135. package/dist/tools/terminal-background-history.test.d.ts +0 -1
  136. package/dist/tools/terminal-background-history.test.js +0 -77
  137. package/dist/tools/terminal-background-history.test.js.map +0 -1
  138. package/dist/tools/terminal-output-persistence.test.d.ts +0 -1
  139. package/dist/tools/terminal-output-persistence.test.js +0 -50
  140. package/dist/tools/terminal-output-persistence.test.js.map +0 -1
  141. package/dist/tools/terminal-output-store.test.d.ts +0 -1
  142. package/dist/tools/terminal-output-store.test.js +0 -364
  143. package/dist/tools/terminal-output-store.test.js.map +0 -1
  144. package/dist/web/agent-content-detail.test.d.ts +0 -1
  145. package/dist/web/agent-content-detail.test.js +0 -307
  146. package/dist/web/agent-content-detail.test.js.map +0 -1
  147. package/dist/web/agent-parent-messages.test.d.ts +0 -1
  148. package/dist/web/agent-parent-messages.test.js +0 -58
  149. package/dist/web/agent-parent-messages.test.js.map +0 -1
  150. package/dist/web/agent-tool-payload-security.test.d.ts +0 -1
  151. package/dist/web/agent-tool-payload-security.test.js +0 -120
  152. package/dist/web/agent-tool-payload-security.test.js.map +0 -1
  153. package/dist/web/prompt-config-protocol.test.d.ts +0 -1
  154. package/dist/web/prompt-config-protocol.test.js +0 -78
  155. package/dist/web/prompt-config-protocol.test.js.map +0 -1
  156. package/dist/web/session-prompt-integration.test.d.ts +0 -1
  157. package/dist/web/session-prompt-integration.test.js +0 -57
  158. package/dist/web/session-prompt-integration.test.js.map +0 -1
  159. package/dist/web/session-settings.test.d.ts +0 -1
  160. package/dist/web/session-settings.test.js +0 -254
  161. package/dist/web/session-settings.test.js.map +0 -1
  162. package/dist/web/smoke-agent-content-http.d.ts +0 -1
  163. package/dist/web/smoke-agent-content-http.js +0 -446
  164. package/dist/web/smoke-agent-content-http.js.map +0 -1
  165. package/dist/web/smoke-plan-payload.d.ts +0 -1
  166. package/dist/web/smoke-plan-payload.js +0 -30
  167. package/dist/web/smoke-plan-payload.js.map +0 -1
  168. package/dist/web/smoke-runtime-context-protocol.d.ts +0 -1
  169. package/dist/web/smoke-runtime-context-protocol.js +0 -42
  170. package/dist/web/smoke-runtime-context-protocol.js.map +0 -1
  171. package/dist/web/smoke-status-semantics.d.ts +0 -1
  172. package/dist/web/smoke-status-semantics.js +0 -90
  173. package/dist/web/smoke-status-semantics.js.map +0 -1
  174. package/dist/web/smoke-task-session-entrypoints.d.ts +0 -1
  175. package/dist/web/smoke-task-session-entrypoints.js +0 -96
  176. package/dist/web/smoke-task-session-entrypoints.js.map +0 -1
  177. package/dist/web/smoke-terminal-output-http.d.ts +0 -1
  178. package/dist/web/smoke-terminal-output-http.js +0 -377
  179. package/dist/web/smoke-terminal-output-http.js.map +0 -1
  180. package/dist/web/smoke-tool-call-detail.d.ts +0 -1
  181. package/dist/web/smoke-tool-call-detail.js +0 -106
  182. package/dist/web/smoke-tool-call-detail.js.map +0 -1
  183. package/dist/web/smoke-tool-call-http.d.ts +0 -1
  184. package/dist/web/smoke-tool-call-http.js +0 -100
  185. package/dist/web/smoke-tool-call-http.js.map +0 -1
  186. package/dist/web/smoke-tool-detail-fields-http.d.ts +0 -1
  187. package/dist/web/smoke-tool-detail-fields-http.js +0 -121
  188. package/dist/web/smoke-tool-detail-fields-http.js.map +0 -1
  189. package/dist/web/smoke-web-agent-tasks.d.ts +0 -1
  190. package/dist/web/smoke-web-agent-tasks.js +0 -50
  191. package/dist/web/smoke-web-agent-tasks.js.map +0 -1
  192. package/dist/web/smoke-web-history.d.ts +0 -1
  193. package/dist/web/smoke-web-history.js +0 -183
  194. package/dist/web/smoke-web-history.js.map +0 -1
  195. package/dist/web/smoke-web-queue.d.ts +0 -1
  196. package/dist/web/smoke-web-queue.js +0 -7
  197. package/dist/web/smoke-web-queue.js.map +0 -1
  198. package/dist/web/smoke-web-terminal-tasks.d.ts +0 -1
  199. package/dist/web/smoke-web-terminal-tasks.js +0 -238
  200. package/dist/web/smoke-web-terminal-tasks.js.map +0 -1
  201. package/dist/web/terminal-presentation.test.d.ts +0 -1
  202. package/dist/web/terminal-presentation.test.js +0 -33
  203. package/dist/web/terminal-presentation.test.js.map +0 -1
  204. package/dist/web/tool-detail-fields.test.d.ts +0 -1
  205. package/dist/web/tool-detail-fields.test.js +0 -121
  206. package/dist/web/tool-detail-fields.test.js.map +0 -1
package/README.md CHANGED
@@ -1,601 +1,59 @@
1
- # neoctl / neo
2
-
3
- neoctl 是一个用 TypeScript 编写的本地 AI 工程代理运行时。项目提供 `neo` 命令行 REPL,也导出核心运行时模块,围绕流式模型调用、工具执行、上下文管理、会话恢复和子代理任务编排构建。
4
-
5
- ## 特性亮点
6
-
7
- - **流式多轮 Agent Loop**:模型输出、thinking、工具调用、工具结果和终止状态都通过统一事件流传递。
8
- - **OpenAI 兼容模型网关**:支持 `/v1/responses` 与 `/v1/chat/completions`,`OPENAI_ENDPOINT=auto` 时会优先尝试 Responses API,并在兼容网关不支持时回退到 Chat Completions。
9
- - **内置工程工具集**:文件读写、文本替换、命令执行、目录列表、ripgrep 搜索、Web 搜索、计划展示、子代理和后台任务控制。
10
- - **上下文预算与手动压缩**:在每次模型调用前注入用户/系统上下文、估算上下文占用并预算大型工具结果;默认不自动压缩会话,用户可通过 `/compact` 主动压缩。
11
- - **会话持久化与恢复**:默认记录 JSONL transcript,大型工具结果落盘保存,支持最近/指定会话恢复和交互式会话浏览;Web 恢复快照只返回图片引用,图片通过独立接口按需加载,避免多图会话被 base64 阻塞。
12
- - **子代理与后台终端**:同一套 query loop 可运行同步子代理、后台子代理、fork 子代理,并可持续轮询和操作异步终端。
13
- - **TTY REPL 体验**:Ink UI、slash command 补全、Markdown 渲染、流式状态栏、token 使用统计、剪贴板文本/图片粘贴、会话标题和终端标题更新。
14
-
15
- ## 快速开始
16
-
17
- 要求 Node.js >= 20。
18
-
19
- ```bash
20
- npm install
21
- npm run build
22
- npm start
23
- ```
24
-
25
- 开发模式:
26
-
27
- ```bash
28
- npm run dev
29
- ```
30
-
31
- 构建当前平台的便携可执行分发目录:
32
-
33
- ```bash
34
- npm run standalone
35
- ```
36
-
37
- 产物输出到 `standalone/<platform>-<arch>/`,例如 Windows x64 为 `standalone/win32-x64/neo.exe`。该目录内包含内嵌 Node.js 的启动器、`dist/`、`node_modules/` 和当前平台的 `vendor/ripgrep/`,目标机器无需预装 Node.js;分发时请压缩并保留整个目录结构,不要只复制单个 `neo.exe`。
38
-
39
- 推送 `v*` tag 或手动触发 GitHub Actions 的 `Build standalone executables` workflow,会分别生成:
40
-
41
- - `neo-win32-x64.zip`
42
- - `neo-linux-x64.tar.gz`
43
- - `neo-darwin-x64.tar.gz`
44
- - `neo-darwin-arm64.tar.gz`
45
-
46
- 首次启动会创建用户级配置文件:
47
-
48
- - Windows:`%APPDATA%\neo\.env`
49
- - macOS/Linux:`~/.config/neo/.env`
50
-
51
- 可以运行 `/login` 交互式填写并保存,也可以手动编辑。OpenAI 的 key、base URL、model 写在 `OPENAI_*` 下;共享运行参数保留 `MODEL_*`。
52
-
53
- ```env
54
- # Active provider
55
- MODEL_PROVIDER=openai
56
-
57
- # OpenAI provider settings
58
- OPENAI_API_KEY=your-openai-api-key
59
- OPENAI_BASE_URL=https://api.openai.com
60
- OPENAI_MODEL=gpt-5.6
61
- OPENAI_ENDPOINT=auto
62
-
63
- # Shared model runtime settings
64
- MODEL_REASONING_EFFORT=high
65
- MODEL_REASONING_SUMMARY=auto
66
- # MODEL_MAX_OUTPUT_TOKENS=32768
67
- MODEL_TIMEOUT_MS=120000
68
- MODEL_STREAM_IDLE_TIMEOUT_MS=120000
69
- MODEL_MAX_RETRIES=2
70
- ```
71
-
72
- 也可以在当前工作目录放 `.env`,或通过 `NEO_ENV_FILE=/path/to/.env` 指定配置文件。加载顺序是:当前目录 `.env` → 用户级 `.env` → `NEO_ENV_FILE`,后者优先级最高。
73
-
74
- ## 常用命令
75
-
76
- ```bash
77
- npm run typecheck # TypeScript 类型检查
78
- npm run build # 编译到 dist,并复制模型元数据
79
- npm run vendor:rg # 显式补齐当前平台的 ripgrep 到 vendor/ripgrep
80
- npm run vendor:rg:all # 显式补齐全部支持平台的 ripgrep
81
- npm run verify:rg # 校验六个平台资源完整且已由 Git 跟踪
82
- npm run standalone # 构建当前平台的便携可执行分发目录
83
- npm run standalone:clean # 清理 standalone 构建产物
84
- npm run smoke:core # 核心 query loop 冒烟测试
85
- npm run smoke:tools # 工具体系冒烟测试
86
- npm run smoke:context # 上下文和压缩冒烟测试
87
- npm run smoke:session # 会话持久化冒烟测试
88
- npm run smoke:agents # 子代理/任务冒烟测试
89
- npm run smoke:skills # skill 模块冒烟测试
90
- npm run smoke:responses # OpenAI Responses mapper 冒烟测试
91
- npm run smoke:openai -- "Say pong"
92
- ```
93
-
94
- 六个支持平台的 `vendor/ripgrep/<platform>-<arch>/rg[.exe]`、manifest 和许可证文件都是不可缺失的发布资源,并全部由 Git 纳管。`postinstall` 会校验安装包内资源,`prepack` 还会校验 Git 跟踪状态;缺失、空文件、格式错误或未纳管都会直接导致安装/发布失败。`vendor:rg` 仅用于显式补齐资源,网络受限时可临时设置 HTTP/HTTPS 代理后执行。
95
-
96
- `postinstall` 还会运行 `scripts/patch-ink-clear-terminal.cjs`,对 Ink 的满屏重绘逻辑做一个本地兼容 patch:Ink 在动态输出高度达到终端高度时原本会调用 `ansiEscapes.clearTerminal`,该序列在现代终端中包含 `ESC[3J`,会清空 scrollback buffer,导致 TTY REPL 在长 Markdown 输出或子代理活动期间出现“滚动条冲顶/归零”的现象。patch 会把该分支替换为只清可见屏幕的 `ESC[2J ESC[H`,保留终端 scrollback。升级 Ink 后如果脚本提示找不到预期代码,需要重新检查 `node_modules/ink/build/ink.js` 的满屏渲染分支。
97
-
98
- ## REPL 用法
99
-
100
- 启动后直接输入自然语言任务即可。命令行参数也可用 `-`/`--` 形式调用同名 REPL slash command:
101
-
102
- ```bash
103
- neo -help
104
- neo run "总结当前仓库"
105
- echo "检查当前改动" | neo run --json
106
- neo -web
107
- neo -web --port 3001
108
- neo -model
109
- neo -model gpt-5.6 high
110
- neo -new
111
- ```
1
+ # Neo Engine
2
+
3
+ Neo 的 TypeScript 核心运行时,提供 `neo` 命令行、模型调用、工具执行、会话管理和子代理任务。Web 和 Desktop 共用这套核心。
112
4
 
113
- `neo run [prompt]` 会执行一次 Agent 请求并退出,适合 shell、脚本和 CI。省略 prompt 或传入 `-` 时从 stdin 读取;`--json` 输出逐行 JSON(NDJSON)事件;`--model` 和 `--reasoning` 可只覆盖本次运行的模型设置。成功退出码为 `0`,运行失败为 `1`,参数错误为 `2`,收到中断为 `130`。
5
+ 子代理调度及管理工具(`subagent_run/output/list/get/stop/message/resume`)默认关闭:未配置时不会向模型暴露,也不能通过工具别名调用。Web/Desktop 可在工具设置中显式开启,会话设置可覆盖全局设置;已有明确保存的设置保留。SDK 使用者可通过 `ToolRegistry.setEnabled(name, true)` 显式开启。子代理内部的 `subagent_report` 汇报通道不受此默认开关影响。
114
6
 
115
- `neo -web` / `neo --web` 会启动本地浏览器 UI,默认监听 `127.0.0.1:3000`,可通过 `--host`、`--port` 参数或 `NEO_WEB_HOST`、`NEO_WEB_PORT` 环境变量调整。
116
-
117
- 除 `-help` 会直接打印帮助并退出、`-web` 会启动 Web UI 外,其它命令会启动 REPL 并执行对应内部命令,例如 `neo -model` 等同于进入 REPL 后输入 `/model`。
118
-
119
- 常用 slash commands:
120
-
121
- | 命令 | 作用 |
122
- | --- | --- |
123
- | `/help` | 显示命令列表 |
124
- | `/model` | 查看当前模型和 reasoning 设置 |
125
- | `/model <model-id>` | 切换模型 |
126
- | `/model <model-id> <effort>` | 切换模型并设置 reasoning effort |
127
- | `/model <effort>` | 只切换 reasoning effort |
128
- | `/login` | 交互式选择供应者、编辑配置并保存到 env 文件 |
129
- | `/cost` | 查看当前 REPL 会话累计 token 使用量 |
130
- | `/compact` | 手动压缩早期上下文 |
131
- | `/pure` | 在风险/WAF 阻断后清理上下文但不重置会话 |
132
- | `/sessions` | 打开会话浏览器 |
133
- | `/state` | 查看 query engine 状态与通信日志状态 |
134
- | `/log <absolute-dir>` | 将模型通信日志写入指定绝对目录 |
135
- | `/log off` | 关闭模型通信日志 |
136
- | `/reset` | 清空当前历史,并在 transcript 中写入 reset marker |
137
- | `/exit` / `/quit` | 退出 |
138
-
139
- 交互细节:
140
-
141
- - `Tab` 可补全 slash command。
142
- - 上/下方向键可浏览输入历史,也可在补全面板中移动选择。
143
- - `/sessions` 中使用上/下选择,会话多页时左/右或 PageUp/PageDown 翻页,Enter 恢复,Esc 关闭,`d`/Delete/Backspace 删除选中的非活跃会话。
144
- - `Ctrl+V` / `Cmd+V` 或右键粘贴会读取系统剪贴板;长文本会以附件形式折叠,图片会作为 image block 发送给支持图片输入的模型。
145
- - 空输入时第一次 `Ctrl+C` 会尝试中断当前任务或提示再次退出,第二次退出;有输入内容时 `Ctrl+C` 清空输入。
146
-
147
- ## 架构概览
148
-
149
- ```text
150
- src/
151
- repl/ Ink 终端 UI、输入编辑、slash commands、剪贴板、会话浏览
152
- core/ QueryEngine、多轮 query loop、消息管线、事件流、子代理 runner
153
- model/ 模型网关、OpenAI adapter、HTTP/SSE、重试、错误归一化、模型元数据
154
- tools/ Tool 接口、注册表、schema 校验、执行编排、内置工具
155
- context/ system prompt、用户/系统上下文、上下文指标、压缩器
156
- session/ JSONL transcript、会话列表/恢复、大型工具结果落盘
157
- agents/ AgentTool、AgentDefinition、本地后台任务输出
158
- tasks/ TaskStore、TaskOutput/TaskList/TaskGet/TaskStop/TaskResume/SendMessage
159
- skills/ 可复用 prompt workflow 的 SkillTool 与内存 catalog
160
- app/ AppState port 和内存实现
161
- safety/ permission / sandbox / audit 的接口边界
162
- types/ message 与 event 类型
163
- ```
164
-
165
- ### 运行主线
166
-
167
- `QueryEngine` 是 REPL 与核心 loop 之间的状态封装:
168
-
169
- 1. 接收用户输入并追加到历史。
170
- 2. 记录 session transcript。
171
- 3. 生成一次 system init message,用于展示本轮可用工具、模型、命令等信息。
172
- 4. 调用 `query()` 进入流式多轮循环。
173
- 5. 将模型消息、工具结果、压缩边界和终止状态持续写回历史与 transcript。
174
-
175
- `query()` 的每轮流程:
176
-
177
- 1. 构建 runtime context:system prompt、user context、system context。
178
- 2. 根据 compact boundary 选择参与模型调用的消息。
179
- 3. 对大型工具结果做预算处理;启用 session 时会将超大结果写到 `.agent/sessions/<session>/tool-results/` 并用预览替换。
180
- 4. 修复缺失的 tool_use/tool_result 配对,避免模型 API 拒绝历史。
181
- 5. 估算 context metrics;默认不按预算自动压缩。
182
- 6. 流式调用模型网关。
183
- 7. 收集 assistant 文本、thinking、tool_use 和 usage。
184
- 8. 如有工具调用,按并发安全规则执行工具,把 tool_result 放入下一轮。
185
- 9. 无工具调用时结束;如果因输出 token 达限且没有工具调用,会尝试提高输出预算继续。
186
- 10. 遇到 context length 错误时直接返回模型错误,不自动改写历史或压缩后重试。
187
-
188
- 事件类型定义在 `src/types/events.ts`,包括 `state`、`context.metrics`、`assistant.delta`、`thinking.delta`、`tool.started`、`tool.finished`、`usage`、`terminal` 等。
189
-
190
- ## 模型层
191
-
192
- 模型访问通过 `ModelGateway` 抽象。当前内置 provider 为 OpenAI:
193
-
194
- - `openai-adapter.ts`:端点选择、认证、超时、重试、Responses→Chat fallback。
195
- - `openai-responses-mapper.ts`:Responses API 请求和流事件归一化。
196
- - `openai-chat-mapper.ts`:Chat Completions 请求和流事件归一化。
197
- - `http-transport.ts` / `sse-decoder.ts`:HTTP 请求与 SSE 流解析。
198
- - `errors.ts`:将 provider 错误归一为 `ModelAPIError` 分类。
199
- - `context-window.ts` + `model-metadata.json`:静态模型元数据,用于 context window、reasoning effort 和图片输入能力判断。
200
-
201
- 支持的配置变量:
202
-
203
- | 变量 | 说明 |
204
- | --- | --- |
205
- | `MODEL_PROVIDER` | `openai` |
206
- | `OPENAI_API_KEY` | OpenAI API Key |
207
- | `OPENAI_BASE_URL` | OpenAI 服务地址,默认 `https://api.openai.com` |
208
- | `OPENAI_MODEL` | 默认模型,默认为 `gpt-5.6` |
209
- | `OPENAI_ENDPOINT` | `responses`、`chat` 或 `auto` |
210
- | `MODEL_REASONING_EFFORT` | 共享运行设置:`none`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max` |
211
- | `MODEL_REASONING_SUMMARY` | `auto`、`concise`、`detailed` |
212
- | `MODEL_MAX_OUTPUT_TOKENS` | 可选的最大输出 token;未设置时采用 API/模型默认值 |
213
- | `MODEL_CONTEXT_WINDOW_TOKENS` | 覆盖模型上下文窗口估算 |
214
- | `MODEL_TIMEOUT_MS` | 请求超时 |
215
- | `MODEL_STREAM_IDLE_TIMEOUT_MS` | 流式响应空闲超时 |
216
- | `MODEL_MAX_RETRIES` | provider 重试次数 |
217
-
218
- ## 工具体系
219
-
220
- 工具实现统一遵循 `Tool<TInput>` 接口,包含:
221
-
222
- - 名称与 alias。
223
- - JSON Schema 输入定义。
224
- - 元数据:是否只读、是否可并发、是否可见、最大结果大小等。
225
- - 输入 normalize 与自定义校验。
226
- - 权限决策入口 `canUseTool`。
227
- - 执行函数 `call()` / `execute()`。
228
- - 结果映射、进度消息渲染、上下文修改器。
229
-
230
- `ToolRegistry` 负责注册和按 prompt cache 友好顺序输出工具定义;`runToolUse()` 负责 schema 校验、权限检查、进度事件、执行、结果映射和异常转 tool_result;`runTools()` 会把同一轮模型产生的工具调用按并发安全性分批执行。默认并发上限为 10,可用 `AGENT_MAX_TOOL_USE_CONCURRENCY` 调整。
231
-
232
- REPL 当前注册的内置工具:
233
-
234
- | 工具 | 作用 |
235
- | --- | --- |
236
- | `echo` | 返回输入文本,主要用于测试链路 |
237
- | `read` / `view` | 按行范围读取文本文件 |
238
- | `list` | 列目录,支持递归、隐藏文件、深度、排除项和数量限制 |
239
- | `grep` | 通过 bundled ripgrep 搜索工作区文本 |
240
- | `write` | 创建或覆盖文本文件 |
241
- | `edit` / `replace` | 基于唯一字符串替换修改文件,容忍 LF/CRLF 和直/弯引号差异 |
242
- | `exec_command` | 创建可持续交互的异步终端 |
243
- | `write_stdin` | 轮询终端增量输出、写入字符、中断或终止后台终端 |
244
- | `search` | 通过可插拔 provider 搜索 Web;OpenAI 模型提供者默认走 GPT web search,否则默认 Exa MCP;可显式切换 provider |
245
- | `image2` | 仅在 `MODEL_PROVIDER=openai` 时注册;通过 OpenAI Images API 生成图片并返回可展示的 data URL;非 OpenAI provider 不暴露绘图工具,系统提示会要求模型说明当前不具备绘图能力 |
246
- | `plan` | 输出和更新当前任务计划 |
247
- | `agent` | 启动同步/后台/fork 子代理 |
248
- | `TaskOutput` | 读取后台任务输出,可阻塞等待完成 |
249
- | `TaskList` | 列出后台任务 |
250
- | `TaskGet` | 查看单个后台任务详情 |
251
- | `TaskStop` | 停止后台任务 |
252
- | `TaskResume` | 以新指令恢复已结束/失败/停止的后台 agent 任务 |
253
- | `SendMessage` | 给命名后台 agent 排队消息 |
254
-
255
- ### 文件与搜索
256
-
257
- - `read` 对大文件使用 offset/limit 分段读取,避免一次性塞满上下文。
258
- - `list` 默认跳过 `.git`、`node_modules`、`dist`、`build`、`coverage` 等重目录。
259
- - `grep` 不依赖系统 PATH,会调用 `vendor/ripgrep` 中的平台二进制;支持 glob、大小写模式、fixed strings、隐藏文件、上下文行、结果数和列宽限制。
260
- - `search` 默认使用 OpenAI Responses API 的 `web_search`,URL、Key 和模型分别继承 `OPENAI_SEARCH_*`,未单独配置时继承 `OPENAI_*`。Base URL 会同时兼容带 `/v1` 和不带 `/v1` 的写法。只有 OpenAI 搜索不可用或对部分查询不可用时,模型才切换到 Exa 官方 `https://mcp.exa.ai/mcp` 的 `web_search_exa`。OpenAI 搜索可通过 `OPENAI_SEARCH_API_KEY`、`OPENAI_SEARCH_BASE_URL`、`OPENAI_SEARCH_MODEL`、`OPENAI_SEARCH_TOOL_TYPE`、`OPENAI_SEARCH_CONTEXT_SIZE` 配置;Exa 可通过 `EXA_MCP_URL`、`EXA_MCP_TOOL_NAME` 配置;两者超时可用 `SEARCH_TIMEOUT_MS` 配置。
261
- - `image2` 按 OpenAI 图片生成官方接口实现,底层请求 `POST /v1/images/generations`;底层 OpenAI 图片模型只允许 `gpt-image-2`,默认也是 `gpt-image-2`。可用 `OPENAI_IMAGE_API_KEY` / `OPENAI_API_KEY`、`OPENAI_IMAGE_BASE_URL` / `OPENAI_BASE_URL`、`OPENAI_IMAGE_MODEL`、`OPENAI_IMAGE_TIMEOUT_MS` 配置,其中 `OPENAI_IMAGE_MODEL` 若不是 `gpt-image-2` 会被 image2 校验拒绝。只有 `MODEL_PROVIDER=openai` 时 REPL/Web 运行时会注册该工具;切换到 Anthropic 等其他 provider 后会移除该工具,并在系统提示中要求模型告知用户当前模型/供应者不具备绘图工具。
262
-
263
- ### 命令执行
264
-
265
- `exec_command` 根据平台和 `shell` 参数选择 PowerShell、cmd、bash 或 sh。命令启动后持续收集输出;首次等待期内结束则直接返回,仍在运行则返回 `session_id`,进程不会依赖当前模型轮次存活。`timeout_ms` 约束命令进入后台前的前台阶段;一旦命令让出成为后台终端,超时计时器会被清除,任务将持续运行到自行退出、显式停止或宿主进程关闭。
7
+ ## 安装使用
266
8
 
267
- - `workdir`:命令工作目录。
268
- - `timeout_ms`:命令进入后台前的前台阶段超时上限;后台任务不设自动超时。
269
- - `yield_time_ms`:首次等待时间;到期后将仍在运行的命令交还为后台终端。
270
- - `max_output_chars`:分别限制每次待读取的 stdout/stderr,超限时保留开头和结尾。
271
- - `tty=true`:通过 PTY/ConPTY 运行需要真实终端语义的交互程序。
9
+ 需要 Node.js 20+。
272
10
 
273
- `stdout`、`stderr`、`output_chars` 和 `omitted_chars` 都是本次调用新消费的增量;调用方应追加保存,重复查询终态不会返回已经读取的输出。`duration_ms` 是进程实际运行时间,进入终态后保持不变。每个终态都通过 `termination_reason` 区分正常完成、非零退出、用户中断、用户终止、强制停止、超时、外部信号和启动错误。Windows TTY 优先返回终端进程 PID;底层无法提供时返回 PTY 宿主 PID,不返回 `0`。TTY 输出可能包含 ANSI/VT 控制序列,后端会原样保留供终端界面解释。
11
+ ```sh
12
+ npm install -g neoctl
13
+ neo
14
+ ```
274
15
 
275
- `write_stdin` 使用 `session_id` 续接后台终端:空字符用于等待并读取新增输出,非空字符原样写入终端,也可发送 interrupt、terminate 或 kill。不同终端可并行执行,同一终端的交互按调用顺序串行处理。并行工具批次采用聚合返回语义:单个调用的 `yield_time_ms` 只约束该调用自身,不约束整批结果的交付时间;需要观察精确状态时序时应串行调用 `exec_command` 与 `write_stdin`。
276
-
277
- ## 上下文与压缩
278
-
279
- `DefaultContextManager` 每轮构建两类上下文:
280
-
281
- - **User context**:当前日期,以及项目记忆文件内容。默认读取 `AGENTS.md`、`CLAUDE.md`、`.agent/memory.md`、`.codex/memory.md`、`.github/copilot-instructions.md`。
282
- - **System context**:cwd、platform、git branch、recent commit、status。
283
-
284
- `prompts.ts` 将 system prompt 分为可缓存稳定段和动态段,中间使用 `__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__` 标记。`message-pipeline.ts` 在模型调用前把 user context 作为用户消息 prepend,并把 system context append 到 system prompt。
285
-
286
- 压缩实现位于 `src/context/compaction.ts`:
287
-
288
- - `DeterministicCompactor` 提供可预测的 snip、microcompact、summary fallback。
289
- - 默认运行时使用 `ModelDrivenCompactor`:达到上下文窗口阈值时主动压缩,模型返回上下文超限错误时执行响应式压缩并重试;如需仅允许手动压缩,可显式注入 `ManualOnlyCompactor`。
290
- - 当工具结果过大时,session 模式下 `FileToolResultMemory` 会把完整结果写入文件,仅把预览和路径留在上下文中。
291
-
292
- ## 会话持久化
293
-
294
- 默认启用 transcript,位置为:
295
-
296
- ```text
297
- .agent/sessions/<session_id>/transcript.jsonl
298
- .agent/sessions/<session_id>/tool-results/*
299
- ```
300
-
301
- 会话记录包括用户/助手/工具消息、内容替换记录、title、compact marker 和 reset marker。`/reset` 不删除文件,而是写入 reset marker,使未来 resume 从 reset 后继续。
302
-
303
- 相关环境变量:
304
-
305
- | 变量 | 作用 |
306
- | --- | --- |
307
- | `AGENT_SESSION_TRANSCRIPT=0` | 禁用 transcript |
308
- | `AGENT_SESSION_DIR=<dir>` | 修改 session 根目录 |
309
- | `AGENT_SESSION_RESUME=1` | 启动时恢复最近会话 |
310
- | `AGENT_SESSION_ID=<id>` | 指定 session id;配合 resume 恢复指定会话 |
311
- | `AGENT_SESSION_TITLE_DELAY_MS` | 会话标题生成延迟,默认 5000ms |
312
- | `AGENT_TOOL_RESULT_THRESHOLD_CHARS` | 大型工具结果落盘阈值 |
313
-
314
- 每次用户输入后,`QueryEngine` 会延迟启动一个无工具的标题子代理:先生成初始短标题,后续在已有标题基础上进行一次 refinement。标题用于 `/sessions` 列表和终端标题。
315
-
316
- ## 子代理与任务
317
-
318
- `agent` 工具通过 `runAgent()` 复用主 query loop,但使用独立的消息、上下文和工具池。子代理定义支持工具 allow/deny、模型覆盖、最大轮数、背景运行、隔离类型和自定义 system prompt。
319
-
320
- 调用模式:
321
-
322
- - **同步子代理**:默认模式;当前工具调用等待子代理完成后返回最终文本、耗时、token 和工具调用数。
323
- - **后台子代理**:`run_in_background=true` 或 `mode=background`;立即返回 `task_id` 和 output file。
324
- - **fork 子代理**:`mode=fork`;继承父上下文,但追加反递归和作用域约束。
325
- - **并行同步子代理**:同一模型轮次中多个 `agent` 调用设置 `parallel=true` 后可被并发批处理。
326
-
327
- 后台任务由 `TaskStore` 管理,完成、失败或停止后会写入:
328
-
329
- ```text
330
- .agent-tasks/<task_id>.txt
331
- ```
332
-
333
- 控制工具:
334
-
335
- - `TaskList()`:列任务。
336
- - `TaskGet({ task_id })`:查详情。
337
- - `TaskOutput({ task_id, block, timeout_ms })`:读输出,可等待完成。
338
- - `TaskStop({ task_id })`:停止任务。
339
- - `TaskResume({ task_id, directive })`:带新指令恢复任务。
340
- - `SendMessage({ target, message })`:向命名或指定 agent id 的后台任务追加待处理消息。
341
-
342
- 子代理相关限制:
343
-
344
- - `AGENT_SUBAGENT_MAX_TURNS` 可覆盖子代理最大轮数。
345
- - `AGENT_SUBAGENT_WALL_TIMEOUT_MS` 可设置子代理墙钟超时。
346
- - fork 子代理不能继续生成更多子代理,避免递归失控。
347
-
348
- ## Skill 模块
349
-
350
- `src/skills` 提供可复用 prompt workflow 与插件化 catalog。设计参考通用的 `SKILL.md` + frontmatter 目录形态、OpenAI Agents SDK 的 tools / agents-as-tools / guardrails 组合方式,以及 OpenClaw 的多目录、插件目录和 skill gating 思路。默认 REPL 运行时当前未注册 skill catalog,嵌入方可按需装配。
351
-
352
- 核心能力:
353
-
354
- - `SkillDescriptor` 支持 `version`、`tags`、`inputSchema`、`outputSchema`、`permissions`、`examples`、`trustLevel`、`source` 等插件元数据。
355
- - `InMemorySkillCatalog` 适合测试和静态注入。
356
- - `FileSystemSkillCatalog` 支持 `.neo/skills/<skill-name>/SKILL.md` 风格目录,也可合并 workspace、user、plugin、remote mirror 等多个 root。
357
- - `CompositeSkillCatalog` 可按优先级合并多个 catalog。
358
- - `createSkillTool()` 会创建 `skill` 调用工具。
359
- - inline skill 会向下一轮模型注入 meta user message,并可修改主循环模型/effort,同时记录 `activeSkill`。
360
- - `createSkillAwareCanUseTool()` 可基于 active skill 的 `allowedTools` 做运行期工具 gating。
361
- - fork skill 会返回 `fork_required`,需要调用方用 AgentTool / 子 agent 编排承接。
362
- - `createSkillManagementTools()` 提供 `skill_list`、`skill_read`、`skill_validate`、`skill_create`、`skill_update`、`skill_delete`,方便父项目实现 agent 自动生成 skill。
363
-
364
- `SKILL.md` 示例:
365
-
366
- ```md
367
- ---
368
- name: review-code
369
- description: Review code changes for correctness and risk.
370
- version: 1.0.0
371
- execution: inline
372
- allowed-tools:
373
- - read
374
- - grep
375
- tags:
376
- - code-review
377
- trust-level: workspace
378
- ---
379
-
380
- Review the provided changes. Focus on correctness, security, tests, and migration risk.
381
- Return concise findings with file references when available.
382
- ```
383
-
384
- 父项目装配示例:
385
-
386
- ```ts
387
- import {
388
- FileSystemSkillCatalog,
389
- createSkillTool,
390
- createSkillManagementTools,
391
- createSkillAwareCanUseTool,
392
- } from "neoctl";
393
-
394
- const skills = new FileSystemSkillCatalog({
395
- roots: [
396
- { root: ".neo/skills", kind: "workspace" },
397
- { root: ".neo/plugins/acme/skills", kind: "plugin", plugin: "acme", readonly: true },
398
- ],
399
- });
400
-
401
- tools.register(createSkillTool(skills));
402
- for (const tool of createSkillManagementTools(skills, { requireApproval: true })) tools.register(tool);
403
-
404
- const canUseTool = createSkillAwareCanUseTool(skills, parentCanUseTool);
405
- ```
406
-
407
- 建议:开放 `skill_create`/`skill_update` 给模型时保持 approval;对 remote/plugin skill 使用只读 root;生产环境使用 `createSkillAwareCanUseTool()` 或父级权限系统强制 `allowedTools`。
408
-
409
- ## 插件协议
16
+ 首次使用输入 `/login` 配置模型。常用命令:
410
17
 
411
- Core 提供 `neo-plugin/v1` 目录资源协议和 `loadNeoPlugins()` 动态加载器。Core 只定义协议、校验并加载资源,不内置任何外部插件实现。
18
+ ```sh
19
+ neo -help # 查看帮助
20
+ neo run "总结当前仓库" # 执行一次任务
21
+ neo -web # 打开核心自带的 Web 界面
22
+ ```
412
23
 
413
- 一个插件根目录可以包含多个直接子目录;每个插件目录必须包含 `neo-plugin.json`:
24
+ 对话中可用 `/new` 新建会话、`/sessions` 查看历史、`/compact` 压缩上下文。
414
25
 
415
- ```json
416
- {
417
- "protocol": "neo-plugin/v1",
418
- "id": "example",
419
- "name": "Example",
420
- "version": "1.0.0",
421
- "entry": "index.mjs",
422
- "defaultEnabled": true
423
- }
26
+ ## 模型配置
27
+
28
+ 支持 OpenAI 兼容的 Responses 和 Chat Completions 接口。除交互配置外,也可在工作目录的 `.env` 中填写:
29
+
30
+ ```env
31
+ MODEL_PROVIDER=openai
32
+ OPENAI_API_KEY=your-api-key
33
+ OPENAI_BASE_URL=https://api.openai.com
34
+ OPENAI_MODEL=your-model-name
35
+ OPENAI_ENDPOINT=auto
424
36
  ```
425
37
 
426
- 入口模块导出 `createPlugin(context)` 或默认工厂函数,返回以下能力之一或多项:
38
+ 将密钥和模型名替换为实际值。`NEO_ENV_FILE` 可指定其他配置文件。
427
39
 
428
- - `tools`: 符合 Core `Tool` 协议的工具数组。
429
- - `promptSections`: 符合 `PromptSection` 协议的系统提示词段。
430
- - `route(req, res, url, helpers)`: 由嵌入后台托管的 HTTP 路由处理器。
40
+ ## 源码开发
431
41
 
432
- ```ts
433
- import { loadNeoPlugins } from "neoctl";
42
+ 在 `engine/` 目录执行:
434
43
 
435
- const plugins = await loadNeoPlugins({
436
- directories: "/absolute/path/to/plugins",
437
- appDataDir: "/absolute/path/to/app-data",
438
- });
44
+ ```sh
45
+ npm ci
46
+ npm run dev
439
47
  ```
440
48
 
441
- 加载器会校验清单版本、插件 id、入口边界、工具/提示词结构、插件 id 冲突和工具名冲突;不存在的插件根目录视为空目录。
49
+ | 命令 | 用途 |
50
+ | --- | --- |
51
+ | `npm run build` | 编译源码到 `dist/` |
52
+ | `npm start` | 运行已构建的 CLI |
53
+ | `npm run typecheck` | 检查源码和测试类型 |
54
+ | `npm test` | 运行单元测试 |
55
+ | `npm run standalone` | 构建当前平台的便携分发目录 |
56
+
57
+ 源码位于 `src/`,测试位于 `tests/`。便携构建输出到 `standalone/<平台>-<架构>/`。
442
58
 
443
- ## 作为库使用
444
-
445
- 包入口会导出核心 agent 编排运行时、模型网关、上下文管理、任务/子代理、工具系统、session 与 safety 边界:
446
-
447
- ```ts
448
- import {
449
- QueryEngine,
450
- ToolRegistry,
451
- createModelGatewayFromEnv,
452
- readFileTool,
453
- listDirectoryTool,
454
- grepTool,
455
- createExecTools,
456
- planTool,
457
- } from "neoctl";
458
-
459
- const tools = new ToolRegistry();
460
- tools.register(readFileTool);
461
- tools.register(listDirectoryTool);
462
- tools.register(grepTool);
463
- for (const tool of createExecTools()) tools.register(tool);
464
- tools.register(planTool);
465
-
466
- const engine = new QueryEngine({
467
- agentId: "main",
468
- modelGateway: createModelGatewayFromEnv(),
469
- tools,
470
- });
471
-
472
- for await (const event of engine.sendUserText("Summarize this repository")) {
473
- console.log(event);
474
- }
475
- ```
476
-
477
- Vue 等前端项目如果通过 API 消费消息,可使用展示层投影工具把内部 `Message` 转为可直接渲染的 DTO。`image2` 生成结果会被写入 `image` block;`imageMode: "data-url"` 时图片块会提供可直接赋给 `<img :src>` 的 `thumbnail.src` / `original.src`:
478
-
479
- ```ts
480
- import { extractDisplayImages, toDisplayAgentEvent, toDisplayMessages } from "neoctl";
481
-
482
- const displayMessages = toDisplayMessages(engine.getHistoryMessages(), {
483
- imageMode: "data-url",
484
- includeThinking: false,
485
- includeToolUse: false,
486
- });
487
-
488
- // 如果只想拿图片列表:
489
- const images = extractDisplayImages(displayMessages);
490
- // images[0]?.src 可直接返回给 Vue 的 <img :src>
491
- ```
492
-
493
- ```vue
494
- <template v-for="message in displayMessages" :key="message.id">
495
- <template v-for="(block, index) in message.blocks" :key="index">
496
- <img
497
- v-if="block.type === 'image' && block.thumbnail"
498
- :src="block.thumbnail.src"
499
- :alt="block.label || 'generated image'"
500
- class="message-image-thumb"
501
- />
502
- </template>
503
- </template>
504
- ```
505
-
506
- SSE/WebSocket 流式推送事件时,可以在后端把单个 `AgentEvent` 投影为 `DisplayAgentEvent`,这样 Vue 收到 `event.type === "message"` 时同样能直接渲染图片:
507
-
508
- ```ts
509
- for await (const event of engine.sendUserText("画一张小猫")) {
510
- sendSse(toDisplayAgentEvent(event, { imageMode: "data-url" }));
511
- }
512
- ```
513
-
514
- 也可以用 `imageMode: "metadata-only"` 只返回图片标签、MIME 与大小信息,避免在列表接口中内联 base64。
515
-
516
- ### 简单多会话 / Vue 后端集成
517
-
518
- 如果 Vue 侧只需要“每个用户使用自己的会话”或“一个用户操作,其他用户旁观”,可以使用轻量的 `SimpleSessionRuntime`。它不会改变底层 `QueryEngine` / `SessionStore` 行为,只是在库侧封装:
519
-
520
- - 每个 `sessionId` 一个活动 `QueryEngine`。
521
- - 同一 session 默认只允许一个发送任务运行,避免并发写历史。
522
- - 支持读取 Vue 展示 DTO。
523
- - 支持 `abort()` 中断当前 session。
524
- - 支持 `onEvent()` 监听事件,便于服务端广播给 SSE/WebSocket 客户端。
525
- - 可通过 `sessionRootDir` 做简单用户隔离。
526
-
527
- ```ts
528
- import { SimpleSessionRuntime, createModelGatewayFromEnv, ToolRegistry } from "neoctl";
529
-
530
- const tools = new ToolRegistry();
531
-
532
- const runtime = new SimpleSessionRuntime({
533
- agentId: "main",
534
- modelGateway: createModelGatewayFromEnv(),
535
- tools,
536
- // 简单多用户推荐:后端根据登录态为每个用户分配独立 session 目录。
537
- sessionRootDir: `.agent/users/${userId}/sessions`,
538
- });
539
-
540
- runtime.onDisplayEvent((event, { sessionId }) => {
541
- // 可在这里把已投影的 DisplayAgentEvent 广播给正在观看该 session 的 SSE/WebSocket 客户端。
542
- // image2 图片会以 message.blocks[].type === "image" 且 block.thumbnail.src 可直接渲染的形式出现。
543
- broadcast(sessionId, event);
544
- }, { imageMode: "data-url", includeThinking: false, includeToolUse: false });
545
-
546
- for await (const event of runtime.sendUserText(sessionId, "Summarize this repository")) {
547
- console.log(event);
548
- }
549
-
550
- const displayMessages = await runtime.getDisplayMessages(sessionId, {
551
- imageMode: "data-url", // Vue <img :src> 用这个;列表页可改为 metadata-only
552
- includeThinking: false,
553
- includeToolUse: false,
554
- });
555
- const images = await runtime.getDisplayImages(sessionId, { imageMode: "data-url" });
556
- ```
557
-
558
- 常用方法:
559
-
560
- ```ts
561
- await runtime.newSession();
562
- await runtime.resumeSession(sessionId);
563
- await runtime.listSessions(20);
564
- await runtime.getMessages(sessionId);
565
- await runtime.getDisplayMessages(sessionId, { imageMode: "metadata-only" });
566
- await runtime.getDisplayImages(sessionId, { imageMode: "data-url" });
567
- runtime.isBusy(sessionId);
568
- runtime.abort(sessionId);
569
- runtime.release(sessionId);
570
- ```
571
-
572
- 如果同一 session 正在运行,再次发送默认会抛出 `session is busy`。需要新请求打断旧请求时,可以使用:
573
-
574
- ```ts
575
- runtime.sendUserText(sessionId, text, { busyBehavior: "interrupt" });
576
- ```
577
-
578
- 除根入口外,发布包还通过 package `exports` 暴露 `dist` 下的编译后子路径,便于依赖方按需导入较底层模块:
579
-
580
- ```ts
581
- import { HttpTransport } from "neoctl/model/http-transport";
582
- import { createAgentTool } from "neoctl/agents/agent-tool";
583
- ```
584
-
585
- 如果直接从源码运行,请使用 `.ts` 源文件路径或 `tsx`;发布包会通过 `dist` 导出编译后的 `.js` 模块与 `.d.ts` 类型声明。
586
-
587
- ## 运行数据目录
588
-
589
- | 路径 | 内容 |
590
- | --- | --- |
591
- | `.agent/sessions/` | 默认会话 transcript 和大型工具结果 |
592
- | `.agent-tasks/` | 后台 agent 任务最终输出 |
593
- | `vendor/ripgrep/` | 当前平台 ripgrep 二进制和 manifest |
594
- | `dist/` | `npm run build` 生成的编译产物 |
595
-
596
- ## 当前边界
597
-
598
- - 模型 provider 配置类型目前内置 OpenAI 与 Anthropic provider。
599
- - `src/safety` 是 permission、sandbox、audit 的接口边界;默认 REPL 没有强制沙箱策略。
600
- - `src/skills` 已实现工具与 catalog,但默认 REPL 未装配 skill catalog。
601
- - `isolation=worktree/remote` 在 AgentTool schema 中保留为接口形态,当前本地实现主要通过 `cwd` 和独立消息上下文隔离。
59
+ 更多测试命令见 [tests/README.md](tests/README.md)。
@@ -1,3 +1,4 @@
1
+ import { IMAGE_SELECTION_GUIDE } from "../tools/builtins/image-capabilities.js";
1
2
  import { readBundledSystemPrompt } from "./prompt-config.js";
2
3
  import { DEFAULT_TOOL_RESULT_BUDGET_CHARS, MAX_TOOL_RESULT_BUDGET_CHARS } from "../session/tool-result-memory.js";
3
4
  export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = "__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__";
@@ -25,7 +26,7 @@ export function buildDefaultSystemPromptSections(enabledTools = [], basePrompt =
25
26
  ? "When you need to inspect, describe, OCR, or answer questions about a historical image that is no longer directly present in the active prompt, use the image_inspect tool with its image id (e.g. img_1) or label. The image registry in compact boundary messages lists all available historical images; compacted images are not text-summarized into visual facts, so load the pixels when visual details matter."
26
27
  : "This runtime has no image loading tool. Do not pretend to visually inspect stored image paths; ask the user to enable image inspection or select a compatible runtime if visual analysis is required.",
27
28
  hasImageGenerationTool
28
- ? "When the user asks for drawing/image generation or image editing/modification, use the image_create tool. It is backed by OpenAI's Images API, defaults to OpenAI model gpt-image-2, and supports mode=generate for new images and mode=edit for modifying existing images. If image_create validation fails, tell the user the model and exact parameter reason from the tool result."
29
+ ? `When the user asks for drawing/image generation or image editing/modification, use the image_create tool. ${IMAGE_SELECTION_GUIDE} It supports mode=generate and mode=edit. If image_create validation fails, report the model and exact parameter reason. Successful results may carry warnings: distinguish requested, upstream-reported and byte-verified properties, and clearly disclose material output mismatches.`
29
30
  : "This runtime has no drawing/image generation/editing tool. If the user asks you to draw, create, render, generate, or edit an image, say that image generation is unavailable in the current runtime configuration instead of pretending to generate one.",
30
31
  hasSecretTools
31
32
  ? `Secrets: you may inspect secret keys, statuses, and value lengths, but secret values are never shown to you. ${secretRequestInstruction} Do not ask users to paste secret values into the conversation; pass secret keys to enabled tools that accept secret references.`