@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.10

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 (396) hide show
  1. package/README.md +13 -10
  2. package/dist/agent/agent-definition-registry.d.ts +3 -0
  3. package/dist/agent/agent-definition-registry.js +3 -1
  4. package/dist/agent/agent-definition.d.ts +15 -0
  5. package/dist/agent/agent-definition.js +27 -3
  6. package/dist/arena/context/context-tools.js +47 -4
  7. package/dist/arena/ledger.js +9 -1
  8. package/dist/arena/strategies/utils.d.ts +1 -4
  9. package/dist/arena/strategies/utils.js +7 -84
  10. package/dist/automation/cron-expr.d.ts +10 -0
  11. package/dist/automation/cron-expr.js +23 -5
  12. package/dist/automation/runner.d.ts +21 -4
  13. package/dist/automation/runner.js +48 -12
  14. package/dist/automation/scheduler.d.ts +38 -0
  15. package/dist/automation/scheduler.js +89 -12
  16. package/dist/automation/store.js +5 -2
  17. package/dist/capability-control/disabled-lists.d.ts +25 -0
  18. package/dist/capability-control/disabled-lists.js +57 -0
  19. package/dist/capability-control/overlay.d.ts +15 -0
  20. package/dist/capability-control/overlay.js +27 -0
  21. package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
  22. package/dist/cc-orchestrator/agent-adapter.js +133 -0
  23. package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
  24. package/dist/cc-orchestrator/cc-capability.js +53 -0
  25. package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
  26. package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
  27. package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
  28. package/dist/cc-orchestrator/codex-session-history.js +187 -0
  29. package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
  30. package/dist/cc-orchestrator/external-agent-changes.js +214 -0
  31. package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
  32. package/dist/cc-orchestrator/external-agent-driver.js +69 -0
  33. package/dist/cc-orchestrator/index.d.ts +8 -0
  34. package/dist/cc-orchestrator/index.js +8 -0
  35. package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
  36. package/dist/cc-orchestrator/relevance-judge.js +29 -0
  37. package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
  38. package/dist/cc-orchestrator/session-discovery.js +125 -0
  39. package/dist/cc-orchestrator/session-history.d.ts +19 -0
  40. package/dist/cc-orchestrator/session-history.js +67 -0
  41. package/dist/cli/agent-server-stdio.d.ts +15 -1
  42. package/dist/cli/agent-server-stdio.js +115 -17
  43. package/dist/cli/agent-server-tcp.js +40 -26
  44. package/dist/context/compaction.d.ts +86 -0
  45. package/dist/context/compaction.js +279 -0
  46. package/dist/context/manager.d.ts +18 -0
  47. package/dist/context/manager.js +181 -48
  48. package/dist/context/token-counter.js +13 -0
  49. package/dist/cost-tracker.js +5 -61
  50. package/dist/credentials/cipher.d.ts +49 -0
  51. package/dist/credentials/cipher.js +45 -0
  52. package/dist/credentials/cookie-jar.d.ts +24 -0
  53. package/dist/credentials/cookie-jar.js +40 -0
  54. package/dist/credentials/index.d.ts +7 -0
  55. package/dist/credentials/index.js +5 -0
  56. package/dist/credentials/inject-credential-tool.d.ts +20 -0
  57. package/dist/credentials/inject-credential-tool.js +130 -0
  58. package/dist/credentials/store.d.ts +72 -0
  59. package/dist/credentials/store.js +184 -0
  60. package/dist/credentials/types.d.ts +56 -0
  61. package/dist/credentials/use-credential-tool.d.ts +29 -0
  62. package/dist/credentials/use-credential-tool.js +205 -0
  63. package/dist/credentials/use-gate.d.ts +56 -0
  64. package/dist/credentials/use-gate.js +52 -0
  65. package/dist/data/model-metadata.d.ts +77 -0
  66. package/dist/data/model-metadata.js +56 -0
  67. package/dist/data/model-metadata.json +216 -0
  68. package/dist/data/openrouter-models.d.ts +18 -7
  69. package/dist/data/openrouter-models.js +35 -8
  70. package/dist/engine/aux-key.d.ts +10 -0
  71. package/dist/engine/aux-key.js +11 -0
  72. package/dist/engine/dynamic-tool-defs.d.ts +19 -0
  73. package/dist/engine/dynamic-tool-defs.js +36 -0
  74. package/dist/engine/engine.d.ts +280 -141
  75. package/dist/engine/engine.js +1193 -245
  76. package/dist/engine/friendly-error.d.ts +18 -0
  77. package/dist/engine/friendly-error.js +63 -0
  78. package/dist/engine/goal.d.ts +145 -0
  79. package/dist/engine/goal.js +144 -0
  80. package/dist/engine/image-policy.d.ts +13 -0
  81. package/dist/engine/image-policy.js +24 -0
  82. package/dist/engine/model-connections-pool.d.ts +17 -0
  83. package/dist/engine/model-connections-pool.js +67 -0
  84. package/dist/engine/model-facade.d.ts +10 -0
  85. package/dist/engine/model-facade.js +15 -0
  86. package/dist/engine/patch-orphaned-tools.js +3 -0
  87. package/dist/engine/query.js +2 -0
  88. package/dist/engine/resolve-llm-config.d.ts +16 -0
  89. package/dist/engine/resolve-llm-config.js +44 -0
  90. package/dist/engine/runtime.d.ts +2 -0
  91. package/dist/engine/runtime.js +27 -1
  92. package/dist/engine/sandbox-cache-key.d.ts +10 -0
  93. package/dist/engine/sandbox-cache-key.js +9 -0
  94. package/dist/engine/sandbox-config.d.ts +31 -0
  95. package/dist/engine/sandbox-config.js +38 -0
  96. package/dist/engine/session-usage.d.ts +31 -0
  97. package/dist/engine/session-usage.js +81 -0
  98. package/dist/engine/steer-queue.d.ts +33 -0
  99. package/dist/engine/steer-queue.js +26 -0
  100. package/dist/engine/streaming-tool-queue.d.ts +12 -0
  101. package/dist/engine/streaming-tool-queue.js +50 -10
  102. package/dist/engine/turn-loop.d.ts +94 -4
  103. package/dist/engine/turn-loop.js +432 -54
  104. package/dist/engine/types.d.ts +175 -0
  105. package/dist/engine/types.js +13 -0
  106. package/dist/external-agents/config.d.ts +2 -0
  107. package/dist/external-agents/config.js +15 -0
  108. package/dist/external-agents/types.d.ts +31 -0
  109. package/dist/external-agents/types.js +1 -0
  110. package/dist/git/utils.d.ts +12 -0
  111. package/dist/git/utils.js +38 -6
  112. package/dist/git/worktree.d.ts +48 -0
  113. package/dist/git/worktree.js +86 -10
  114. package/dist/hooks/goal-stop-hook.d.ts +28 -0
  115. package/dist/hooks/goal-stop-hook.js +186 -9
  116. package/dist/hooks/registry.d.ts +8 -0
  117. package/dist/hooks/registry.js +19 -0
  118. package/dist/hooks/shell-runner.js +10 -21
  119. package/dist/index.d.ts +51 -7
  120. package/dist/index.js +62 -5
  121. package/dist/llm/capabilities/param-specs.d.ts +14 -0
  122. package/dist/llm/capabilities/param-specs.js +62 -0
  123. package/dist/llm/capabilities/rules.js +5 -1
  124. package/dist/llm/capabilities/types.d.ts +10 -0
  125. package/dist/llm/client-base.d.ts +28 -1
  126. package/dist/llm/client-base.js +119 -13
  127. package/dist/llm/model-cache.js +4 -2
  128. package/dist/llm/model-pool.d.ts +21 -0
  129. package/dist/llm/model-pool.js +21 -1
  130. package/dist/llm/provider-auth.d.ts +41 -0
  131. package/dist/llm/provider-auth.js +76 -0
  132. package/dist/llm/provider-catalog.d.ts +4 -0
  133. package/dist/llm/providers/anthropic.js +64 -14
  134. package/dist/llm/providers/openai.d.ts +36 -0
  135. package/dist/llm/providers/openai.js +257 -51
  136. package/dist/llm/reasoning-setting.d.ts +3 -3
  137. package/dist/llm/reasoning-setting.js +9 -1
  138. package/dist/llm/stream-watchdog.js +5 -1
  139. package/dist/llm/token-counter.js +9 -2
  140. package/dist/llm/types.d.ts +4 -0
  141. package/dist/logging/sanitize-messages.js +22 -0
  142. package/dist/lsp/manager.d.ts +1 -1
  143. package/dist/lsp/manager.js +40 -10
  144. package/dist/model-catalog/builtin.d.ts +12 -0
  145. package/dist/model-catalog/builtin.js +412 -0
  146. package/dist/model-catalog/gen-connections.d.ts +20 -0
  147. package/dist/model-catalog/gen-connections.js +28 -0
  148. package/dist/model-catalog/index.d.ts +41 -0
  149. package/dist/model-catalog/index.js +90 -0
  150. package/dist/model-catalog/params.d.ts +20 -0
  151. package/dist/model-catalog/params.js +45 -0
  152. package/dist/model-catalog/resolve.d.ts +48 -0
  153. package/dist/model-catalog/resolve.js +33 -0
  154. package/dist/model-catalog/save-entry.d.ts +32 -0
  155. package/dist/model-catalog/save-entry.js +104 -0
  156. package/dist/model-catalog/types.d.ts +561 -0
  157. package/dist/model-catalog/types.js +93 -0
  158. package/dist/model-catalog/upsert.d.ts +9 -0
  159. package/dist/model-catalog/upsert.js +8 -0
  160. package/dist/onboarding.d.ts +12 -82
  161. package/dist/onboarding.js +61 -326
  162. package/dist/plugins/gitOps.d.ts +19 -0
  163. package/dist/plugins/gitOps.js +73 -4
  164. package/dist/plugins/installer/checkUpdate.d.ts +16 -0
  165. package/dist/plugins/installer/checkUpdate.js +32 -0
  166. package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
  167. package/dist/plugins/installer/codex/convertCommands.js +46 -0
  168. package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
  169. package/dist/plugins/installer/codex/convertMcp.js +45 -5
  170. package/dist/plugins/installer/install.js +25 -1
  171. package/dist/plugins/installer/installFromArchive.d.ts +43 -0
  172. package/dist/plugins/installer/installFromArchive.js +134 -0
  173. package/dist/plugins/installer/installFromSource.js +8 -2
  174. package/dist/plugins/installer/loadPluginAgents.js +6 -2
  175. package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
  176. package/dist/plugins/installer/loadPluginMcp.js +35 -2
  177. package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
  178. package/dist/plugins/installer/pruneDisabled.js +73 -0
  179. package/dist/plugins/installer/types.d.ts +5 -2
  180. package/dist/plugins/installer/types.js +1 -0
  181. package/dist/plugins/installer/uninstall.js +4 -0
  182. package/dist/plugins/installer/unzip.d.ts +14 -0
  183. package/dist/plugins/installer/unzip.js +82 -0
  184. package/dist/plugins/installer/update.d.ts +14 -0
  185. package/dist/plugins/installer/update.js +53 -21
  186. package/dist/plugins/loadPluginHooks.d.ts +47 -1
  187. package/dist/plugins/loadPluginHooks.js +73 -1
  188. package/dist/plugins/marketplaceManager.d.ts +7 -0
  189. package/dist/plugins/marketplaceManager.js +20 -0
  190. package/dist/plugins/pluginCommandHook.js +4 -18
  191. package/dist/plugins/pluginContent.d.ts +30 -0
  192. package/dist/plugins/pluginContent.js +83 -0
  193. package/dist/plugins/pluginInstaller.d.ts +13 -0
  194. package/dist/plugins/pluginInstaller.js +42 -8
  195. package/dist/plugins/schemas.js +1 -0
  196. package/dist/plugins/types.d.ts +6 -0
  197. package/dist/preset/index.d.ts +11 -1
  198. package/dist/preset/index.js +115 -6
  199. package/dist/product/types.d.ts +1 -1
  200. package/dist/prompt/composer.d.ts +32 -0
  201. package/dist/prompt/composer.js +75 -21
  202. package/dist/prompt/instruction-scanner.js +5 -3
  203. package/dist/prompt/section-loader.js +1 -0
  204. package/dist/prompt/sections/base.md +2 -0
  205. package/dist/prompt/sections/browser.md +10 -0
  206. package/dist/prompt/sections/coding.md +4 -0
  207. package/dist/protocol/chat-session-manager.d.ts +9 -2
  208. package/dist/protocol/chat-session-manager.js +38 -0
  209. package/dist/protocol/chat-session.d.ts +69 -1
  210. package/dist/protocol/chat-session.js +102 -4
  211. package/dist/protocol/client.d.ts +31 -0
  212. package/dist/protocol/client.js +44 -0
  213. package/dist/protocol/server.d.ts +120 -7
  214. package/dist/protocol/server.js +753 -51
  215. package/dist/protocol/transport.js +3 -2
  216. package/dist/protocol/types.d.ts +60 -0
  217. package/dist/protocol/types.js +16 -0
  218. package/dist/quota/credentials.d.ts +3 -0
  219. package/dist/quota/credentials.js +80 -0
  220. package/dist/quota/index.d.ts +36 -0
  221. package/dist/quota/index.js +155 -0
  222. package/dist/quota/types.d.ts +48 -0
  223. package/dist/quota/types.js +13 -0
  224. package/dist/review/review-prompt.d.ts +28 -0
  225. package/dist/review/review-prompt.js +81 -0
  226. package/dist/run/FileRunStore.js +8 -3
  227. package/dist/run/RunApprovalBackend.js +25 -5
  228. package/dist/run/RunManager.d.ts +12 -0
  229. package/dist/run/RunManager.js +35 -0
  230. package/dist/run/factory.d.ts +1 -1
  231. package/dist/runtime/background-shell.d.ts +139 -0
  232. package/dist/runtime/background-shell.js +509 -0
  233. package/dist/runtime/output-clean.d.ts +24 -0
  234. package/dist/runtime/output-clean.js +41 -0
  235. package/dist/runtime/ring-file.d.ts +64 -0
  236. package/dist/runtime/ring-file.js +174 -0
  237. package/dist/runtime/safe-spawn.js +98 -39
  238. package/dist/runtime/spawn-common.d.ts +159 -0
  239. package/dist/runtime/spawn-common.js +404 -0
  240. package/dist/runtime/truncate-output.d.ts +22 -0
  241. package/dist/runtime/truncate-output.js +49 -0
  242. package/dist/runtime/utf8-cut.d.ts +11 -0
  243. package/dist/runtime/utf8-cut.js +23 -0
  244. package/dist/services/auto-dream.d.ts +4 -0
  245. package/dist/services/auto-dream.js +26 -26
  246. package/dist/services/diagnostics.d.ts +1 -2
  247. package/dist/services/diagnostics.js +12 -7
  248. package/dist/services/extract-memories.d.ts +14 -1
  249. package/dist/services/extract-memories.js +45 -6
  250. package/dist/services/memory-orchestrator.d.ts +21 -0
  251. package/dist/services/memory-orchestrator.js +125 -50
  252. package/dist/services/session-memory.js +24 -12
  253. package/dist/session/file-history.d.ts +124 -1
  254. package/dist/session/file-history.js +222 -6
  255. package/dist/session/memory.d.ts +116 -2
  256. package/dist/session/memory.js +250 -28
  257. package/dist/session/session-manager.d.ts +40 -0
  258. package/dist/session/session-manager.js +119 -3
  259. package/dist/session/simple-diff.d.ts +23 -0
  260. package/dist/session/simple-diff.js +84 -0
  261. package/dist/session/transcript.d.ts +29 -1
  262. package/dist/session/transcript.js +56 -2
  263. package/dist/session/undo-target.d.ts +67 -0
  264. package/dist/session/undo-target.js +144 -0
  265. package/dist/settings/disk-defaults.d.ts +9 -2
  266. package/dist/settings/disk-defaults.js +11 -2
  267. package/dist/settings/feature-flags.d.ts +64 -0
  268. package/dist/settings/feature-flags.js +61 -0
  269. package/dist/settings/manager.d.ts +68 -1
  270. package/dist/settings/manager.js +266 -29
  271. package/dist/settings/migrate-config.d.ts +45 -0
  272. package/dist/settings/migrate-config.js +125 -0
  273. package/dist/settings/schema-export.d.ts +25 -0
  274. package/dist/settings/schema-export.js +38 -0
  275. package/dist/settings/schema.d.ts +1186 -771
  276. package/dist/settings/schema.js +268 -97
  277. package/dist/skills/scanner.d.ts +9 -0
  278. package/dist/skills/scanner.js +30 -2
  279. package/dist/stt/resolve-transcribe.d.ts +31 -0
  280. package/dist/stt/resolve-transcribe.js +108 -0
  281. package/dist/stt/transcribe.d.ts +51 -0
  282. package/dist/stt/transcribe.js +65 -0
  283. package/dist/tool-system/browser-bridge.d.ts +226 -0
  284. package/dist/tool-system/browser-bridge.js +163 -0
  285. package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
  286. package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
  287. package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
  288. package/dist/tool-system/builtin/agent-notifications.js +9 -3
  289. package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
  290. package/dist/tool-system/builtin/agent-output-file.js +72 -0
  291. package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
  292. package/dist/tool-system/builtin/agent-registry.js +8 -0
  293. package/dist/tool-system/builtin/agent.d.ts +21 -1
  294. package/dist/tool-system/builtin/agent.js +489 -42
  295. package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
  296. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
  297. package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
  298. package/dist/tool-system/builtin/apply-patch/index.js +0 -15
  299. package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
  300. package/dist/tool-system/builtin/background-jobs.js +124 -0
  301. package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
  302. package/dist/tool-system/builtin/background-shell-tools.js +108 -0
  303. package/dist/tool-system/builtin/background-work.d.ts +67 -0
  304. package/dist/tool-system/builtin/background-work.js +86 -0
  305. package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
  306. package/dist/tool-system/builtin/bash-output-style.js +40 -0
  307. package/dist/tool-system/builtin/bash.d.ts +5 -2
  308. package/dist/tool-system/builtin/bash.js +101 -64
  309. package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
  310. package/dist/tool-system/builtin/browser-tools.js +312 -0
  311. package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
  312. package/dist/tool-system/builtin/cancel-goal.js +64 -0
  313. package/dist/tool-system/builtin/check-quota.d.ts +15 -0
  314. package/dist/tool-system/builtin/check-quota.js +34 -0
  315. package/dist/tool-system/builtin/config.js +7 -0
  316. package/dist/tool-system/builtin/cron.d.ts +7 -0
  317. package/dist/tool-system/builtin/cron.js +64 -4
  318. package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
  319. package/dist/tool-system/builtin/drive-claude-code.js +157 -0
  320. package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
  321. package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
  322. package/dist/tool-system/builtin/edit.js +20 -16
  323. package/dist/tool-system/builtin/eol.d.ts +29 -0
  324. package/dist/tool-system/builtin/eol.js +37 -0
  325. package/dist/tool-system/builtin/file-cache.d.ts +6 -0
  326. package/dist/tool-system/builtin/file-cache.js +8 -0
  327. package/dist/tool-system/builtin/generate-image.d.ts +35 -0
  328. package/dist/tool-system/builtin/generate-image.js +278 -50
  329. package/dist/tool-system/builtin/generate-video.d.ts +55 -0
  330. package/dist/tool-system/builtin/generate-video.js +364 -0
  331. package/dist/tool-system/builtin/glob.js +0 -7
  332. package/dist/tool-system/builtin/grep.d.ts +9 -0
  333. package/dist/tool-system/builtin/grep.js +106 -11
  334. package/dist/tool-system/builtin/image-providers.d.ts +86 -0
  335. package/dist/tool-system/builtin/image-providers.js +190 -0
  336. package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
  337. package/dist/tool-system/builtin/image-uploader.js +74 -0
  338. package/dist/tool-system/builtin/index.d.ts +10 -2
  339. package/dist/tool-system/builtin/index.js +251 -27
  340. package/dist/tool-system/builtin/mcp-tools.js +23 -3
  341. package/dist/tool-system/builtin/memory.js +45 -7
  342. package/dist/tool-system/builtin/notebook-edit.js +0 -7
  343. package/dist/tool-system/builtin/powershell.js +8 -2
  344. package/dist/tool-system/builtin/read.js +10 -10
  345. package/dist/tool-system/builtin/repl.js +4 -1
  346. package/dist/tool-system/builtin/skill.js +9 -0
  347. package/dist/tool-system/builtin/sleep.js +8 -2
  348. package/dist/tool-system/builtin/tool-search.js +25 -7
  349. package/dist/tool-system/builtin/video-providers.d.ts +154 -0
  350. package/dist/tool-system/builtin/video-providers.js +235 -0
  351. package/dist/tool-system/builtin/web-fetch.js +12 -2
  352. package/dist/tool-system/builtin/web-search.js +21 -7
  353. package/dist/tool-system/builtin/worktree.d.ts +2 -1
  354. package/dist/tool-system/builtin/worktree.js +31 -4
  355. package/dist/tool-system/builtin/write.js +0 -6
  356. package/dist/tool-system/context.d.ts +164 -6
  357. package/dist/tool-system/executor.d.ts +3 -1
  358. package/dist/tool-system/executor.js +197 -90
  359. package/dist/tool-system/investigation-guard.js +1 -1
  360. package/dist/tool-system/mcp-manager.d.ts +42 -4
  361. package/dist/tool-system/mcp-manager.js +203 -20
  362. package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
  363. package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
  364. package/dist/tool-system/path-policy.d.ts +5 -0
  365. package/dist/tool-system/path-policy.js +307 -8
  366. package/dist/tool-system/permission.d.ts +39 -2
  367. package/dist/tool-system/permission.js +283 -70
  368. package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
  369. package/dist/tool-system/plan-mode-allowlist.js +24 -2
  370. package/dist/tool-system/registry.d.ts +1 -0
  371. package/dist/tool-system/registry.js +16 -2
  372. package/dist/tool-system/sandbox/index.d.ts +8 -0
  373. package/dist/tool-system/sandbox/index.js +7 -2
  374. package/dist/tool-system/sandbox/off.js +7 -1
  375. package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
  376. package/dist/tool-system/validate-tool-metadata.js +63 -0
  377. package/dist/types.d.ts +252 -7
  378. package/dist/updater.js +20 -9
  379. package/dist/utils/envUtils.d.ts +0 -9
  380. package/dist/utils/envUtils.js +3 -28
  381. package/dist/utils/exec.d.ts +48 -0
  382. package/dist/utils/exec.js +154 -0
  383. package/dist/utils/json.d.ts +12 -0
  384. package/dist/utils/json.js +92 -0
  385. package/dist/utils/theme.d.ts +1 -1
  386. package/dist/utils/theme.js +1 -1
  387. package/dist/utils/toolDisplay.js +0 -1
  388. package/package.json +13 -7
  389. package/dist/agent/coordinator.d.ts +0 -49
  390. package/dist/agent/coordinator.js +0 -77
  391. package/dist/settings/manager.test.js +0 -73
  392. package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
  393. package/dist/tool-system/builtin/remote-trigger.js +0 -54
  394. package/dist/tool-system/builtin/send-message.d.ts +0 -6
  395. package/dist/tool-system/builtin/send-message.js +0 -47
  396. /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
@@ -0,0 +1,44 @@
1
+ import { getMergedCatalog } from "../model-catalog/index.js";
2
+ import { modelEntriesFromConnections } from "./model-connections-pool.js";
3
+ import { ModelPool } from "../llm/model-pool.js";
4
+ export function resolveLLMConfigForTag(settings, tag, preferredInstanceId) {
5
+ const connections = settings.modelConnections;
6
+ if (!Array.isArray(connections) || connections.length === 0)
7
+ return null;
8
+ const credentials = settings.credentials;
9
+ const catalog = getMergedCatalog();
10
+ const entries = modelEntriesFromConnections(connections, (Array.isArray(credentials) ? credentials : []), catalog);
11
+ if (entries.length === 0)
12
+ return null;
13
+ // entry.key === connection.id(见 modelEntriesFromConnections)。
14
+ const defaultId = settings.defaults?.text;
15
+ const pick = (preferredInstanceId && entries.find((e) => e.key === preferredInstanceId)) ||
16
+ (defaultId && entries.find((e) => e.key === defaultId)) ||
17
+ entries[0];
18
+ if (!pick)
19
+ return null;
20
+ // 用户显式选了 default/preferred 但它解析不到(连接的 catalogId 被删 →
21
+ // modelEntriesFromConnections 静默过滤掉)→ 这里 find 落空、悄悄回退到 entries[0]
22
+ // 是「换了个模型还不告诉你」。不改回退行为(有可用连接就用),但出一条 warn 让
23
+ // 静默替换可见(否则用户以为还在用选的那个)。
24
+ const wanted = preferredInstanceId ?? defaultId;
25
+ if (wanted && pick.key !== wanted) {
26
+ console.warn(`[resolveLLMConfigForTag] 偏好/默认连接 "${wanted}" 解析不到(catalogId 可能已删)— 回退到 "${pick.key}"。`);
27
+ }
28
+ // The picked connection's catalog template needs a key but none resolved
29
+ // (credentialId missing/points at a deleted credential). Returning null here
30
+ // routes to the caller's existing clean "没有可用的文本模型连接 … 请在「连接」
31
+ // 页添加并填写凭证" message + exit, instead of silently building a config with
32
+ // apiKey:undefined that only fails later as a cryptic provider 401. In
33
+ // CONTRACT: this fn's null already means "no USABLE connection for this tag".
34
+ // resolveInstance's no-crash (apiKey:undefined) behavior is untouched — this
35
+ // gate is one layer up where usability is decided.
36
+ if (pick.needsKey !== false && !pick.apiKey) {
37
+ console.warn(`[resolveLLMConfigForTag] 连接 "${pick.key}" 需要密钥但未解析到凭证(credentialId 缺失或指向已删凭证)— 视为该 tag 无可用连接。`);
38
+ return null;
39
+ }
40
+ // 临时 pool 复用 toLLMConfig 的全部映射逻辑(reasoning/headers/providerKind 等)。
41
+ const pool = new ModelPool([]);
42
+ pool.register(pick);
43
+ return pool.toLLMConfig(pick);
44
+ }
@@ -23,6 +23,8 @@ export declare class EngineRuntime {
23
23
  readonly costTracker: CostTracker;
24
24
  private sandboxCache;
25
25
  constructor(opts: EngineRuntimeOptions);
26
+ clearModels(): void;
27
+ reloadModelsFromSettings(): void;
26
28
  /**
27
29
  * Lazily resolve and cache a sandbox backend for `(mode, cwd)`.
28
30
  *
@@ -1,4 +1,8 @@
1
+ import { getMergedCatalog } from "../model-catalog/index.js";
2
+ import { modelEntriesFromConnections } from "./model-connections-pool.js";
3
+ import { defaultCacheDir } from "../llm/model-cache.js";
1
4
  import { resolveSandboxBackend, } from "../tool-system/sandbox/index.js";
5
+ import { sandboxCacheKey } from "./sandbox-cache-key.js";
2
6
  /**
3
7
  * Shared read-only resources used by all Engine instances in a worker.
4
8
  * Mutable per-session state stays on Engine itself.
@@ -21,6 +25,28 @@ export class EngineRuntime {
21
25
  this.mcpPool = opts.mcpPool;
22
26
  this.costTracker = opts.costTracker;
23
27
  }
28
+ clearModels() {
29
+ this.modelPool.clear();
30
+ }
31
+ reloadModelsFromSettings() {
32
+ const settings = this.settings.load();
33
+ const connections = settings.modelConnections ?? [];
34
+ const credentials = settings.credentials ?? [];
35
+ const catalog = getMergedCatalog();
36
+ const entries = modelEntriesFromConnections(connections, credentials, catalog);
37
+ this.modelPool.clear();
38
+ if (entries.length === 0)
39
+ return;
40
+ for (const entry of entries)
41
+ this.modelPool.register(entry);
42
+ if (entries.length > 0) {
43
+ const defaultText = settings.defaults?.text;
44
+ const key = defaultText && entries.some((e) => e.key === defaultText) ? defaultText : entries[0].key;
45
+ this.modelPool.switch(key);
46
+ this.modelPool.setCacheDir(defaultCacheDir());
47
+ this.modelPool.reloadCachedContextWindows();
48
+ }
49
+ }
24
50
  /**
25
51
  * Lazily resolve and cache a sandbox backend for `(mode, cwd)`.
26
52
  *
@@ -31,7 +57,7 @@ export class EngineRuntime {
31
57
  * violate standard §S4 fail-closed rule.
32
58
  */
33
59
  resolveSandbox(config, cwd) {
34
- const key = `${config.mode}:${cwd}`;
60
+ const key = sandboxCacheKey(config, cwd);
35
61
  let cached = this.sandboxCache.get(key);
36
62
  if (!cached) {
37
63
  cached = resolveSandboxBackend(config, cwd);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * sandboxCacheKey — cache key for a resolved sandbox backend. Includes every
3
+ * field that affects the backend (mode, network, writableRoots, deniedReads)
4
+ * plus cwd. The key was previously `${mode}:${cwd}`, which ignored network /
5
+ * roots / reads — so editing those in 设置页 hit the stale cached backend and
6
+ * didn't take effect until app restart. Stable JSON shape so equal configs map
7
+ * to equal keys.
8
+ */
9
+ import type { SandboxConfig } from "../tool-system/sandbox/index.js";
10
+ export declare function sandboxCacheKey(config: SandboxConfig, cwd: string): string;
@@ -0,0 +1,9 @@
1
+ export function sandboxCacheKey(config, cwd) {
2
+ return JSON.stringify({
3
+ mode: config.mode,
4
+ network: config.network,
5
+ writableRoots: config.writableRoots,
6
+ deniedReads: config.deniedReads,
7
+ cwd,
8
+ });
9
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * resolveSandboxConfig — pick the sandbox config for a run, reading the
3
+ * 设置页's settings.sandbox so a project-scoped sandbox setting actually takes
4
+ * effect. Priority: explicit config.sandbox → settings.sandbox.mode → per-run
5
+ * default (headless=auto, REPL/desktop=off).
6
+ *
7
+ * Background: the engine previously only honored config.sandbox, which the
8
+ * desktop host never passes — so a user enabling sandbox in 设置页 had no
9
+ * effect. This bridges settings → run config.
10
+ * See memory project_10_review_bug_list #9.
11
+ */
12
+ import { type SandboxConfig, type SandboxMode } from "../tool-system/sandbox/index.js";
13
+ /** The shape of settings.sandbox (all fields optional). */
14
+ export interface SettingsSandbox {
15
+ mode?: SandboxMode;
16
+ network?: "allow" | "deny";
17
+ writableRoots?: string[];
18
+ deniedReads?: string[];
19
+ }
20
+ /**
21
+ * Three-layer resolve. Priority:
22
+ * 1. config.sandbox — explicit host-passed.
23
+ * 2. project settings.sandbox (has mode) — overrides global.
24
+ * 3. global settings.sandbox (has mode) — what a project "follows".
25
+ * 4. per-run default: headless → auto, desktop/REPL → off.
26
+ *
27
+ * A layer without `mode` is "unset / 跟随上一层" — pass unmerged per-scope
28
+ * values so a project that wrote nothing genuinely follows global (a merged
29
+ * value would make every project look like it set global's mode).
30
+ */
31
+ export declare function resolveSandboxConfig(configSandbox: SandboxConfig | undefined, projectSandbox: SettingsSandbox | undefined, globalSandbox: SettingsSandbox | undefined, headless: boolean): SandboxConfig;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * resolveSandboxConfig — pick the sandbox config for a run, reading the
3
+ * 设置页's settings.sandbox so a project-scoped sandbox setting actually takes
4
+ * effect. Priority: explicit config.sandbox → settings.sandbox.mode → per-run
5
+ * default (headless=auto, REPL/desktop=off).
6
+ *
7
+ * Background: the engine previously only honored config.sandbox, which the
8
+ * desktop host never passes — so a user enabling sandbox in 设置页 had no
9
+ * effect. This bridges settings → run config.
10
+ * See memory project_10_review_bug_list #9.
11
+ */
12
+ import { defaultSandboxConfig } from "../tool-system/sandbox/index.js";
13
+ /**
14
+ * Three-layer resolve. Priority:
15
+ * 1. config.sandbox — explicit host-passed.
16
+ * 2. project settings.sandbox (has mode) — overrides global.
17
+ * 3. global settings.sandbox (has mode) — what a project "follows".
18
+ * 4. per-run default: headless → auto, desktop/REPL → off.
19
+ *
20
+ * A layer without `mode` is "unset / 跟随上一层" — pass unmerged per-scope
21
+ * values so a project that wrote nothing genuinely follows global (a merged
22
+ * value would make every project look like it set global's mode).
23
+ */
24
+ export function resolveSandboxConfig(configSandbox, projectSandbox, globalSandbox, headless) {
25
+ if (configSandbox)
26
+ return configSandbox;
27
+ const layer = projectSandbox?.mode ? projectSandbox : globalSandbox?.mode ? globalSandbox : undefined;
28
+ if (layer?.mode) {
29
+ const base = defaultSandboxConfig(layer.mode);
30
+ return {
31
+ mode: layer.mode,
32
+ network: layer.network ?? base.network,
33
+ writableRoots: layer.writableRoots ?? base.writableRoots,
34
+ deniedReads: layer.deniedReads ?? base.deniedReads,
35
+ };
36
+ }
37
+ return defaultSandboxConfig(headless ? "auto" : "off");
38
+ }
@@ -0,0 +1,31 @@
1
+ import type { TokenUsage } from "../types.js";
2
+ import type { LLMUsageTracker } from "../llm/types.js";
3
+ export interface CumulativeUsageCounters {
4
+ cumulativePromptTokens: number;
5
+ cumulativeCacheReadTokens: number;
6
+ cumulativeCacheCreationTokens: number;
7
+ }
8
+ export declare function emptyCumulativeUsageCounters(): CumulativeUsageCounters;
9
+ export declare function normalizeCumulativeUsageCounters(counters: Partial<CumulativeUsageCounters> | undefined, legacyUsage?: TokenUsage): CumulativeUsageCounters;
10
+ export declare function addCumulativeUsage(counters: Partial<CumulativeUsageCounters> | undefined, usage: TokenUsage): CumulativeUsageCounters;
11
+ export declare function addTokenUsage(left: TokenUsage, right: TokenUsage): TokenUsage;
12
+ export declare function cacheHitRateFromTokens(promptTokens: number, cacheReadTokens: number | undefined, cacheCreationTokens: number | undefined): number | undefined;
13
+ export declare function cacheHitRateFromUsage(usage: TokenUsage | undefined): number | undefined;
14
+ export declare function cumulativeCacheHitRate(counters: CumulativeUsageCounters): number | undefined;
15
+ /**
16
+ * Fold one run's cumulative usage onto a session baseline, producing the new
17
+ * session-cumulative TokenUsage.
18
+ *
19
+ * The LLM client is recreated per `engine.run()`, so its `getUsage()` totals
20
+ * count only the current run and start from zero each run. To get a true
21
+ * session cumulative we add the run's running total onto the baseline captured
22
+ * at run start (the value persisted from prior runs).
23
+ *
24
+ * Crucially this is `baseline + current`, NOT `baseline += current`: the
25
+ * turn-boundary heartbeat fires many times within one run, each reading the
26
+ * run's growing total. Recomputing `baseline + current` every time is
27
+ * idempotent — it never double-counts — whereas a `+=` would add the running
28
+ * total repeatedly. Feed the folded result back as the baseline only across
29
+ * run boundaries (new client, fresh run total).
30
+ */
31
+ export declare function foldRunUsage(baseline: TokenUsage, runUsage: LLMUsageTracker): TokenUsage;
@@ -0,0 +1,81 @@
1
+ export function emptyCumulativeUsageCounters() {
2
+ return {
3
+ cumulativePromptTokens: 0,
4
+ cumulativeCacheReadTokens: 0,
5
+ cumulativeCacheCreationTokens: 0,
6
+ };
7
+ }
8
+ export function normalizeCumulativeUsageCounters(counters, legacyUsage) {
9
+ return {
10
+ cumulativePromptTokens: typeof counters?.cumulativePromptTokens === "number"
11
+ ? counters.cumulativePromptTokens
12
+ : (legacyUsage?.promptTokens ?? 0),
13
+ cumulativeCacheReadTokens: typeof counters?.cumulativeCacheReadTokens === "number"
14
+ ? counters.cumulativeCacheReadTokens
15
+ : (legacyUsage?.cacheReadTokens ?? 0),
16
+ cumulativeCacheCreationTokens: typeof counters?.cumulativeCacheCreationTokens === "number"
17
+ ? counters.cumulativeCacheCreationTokens
18
+ : (legacyUsage?.cacheCreationTokens ?? 0),
19
+ };
20
+ }
21
+ export function addCumulativeUsage(counters, usage) {
22
+ const current = normalizeCumulativeUsageCounters(counters);
23
+ return {
24
+ cumulativePromptTokens: current.cumulativePromptTokens + (usage.promptTokens ?? 0),
25
+ cumulativeCacheReadTokens: current.cumulativeCacheReadTokens + (usage.cacheReadTokens ?? 0),
26
+ cumulativeCacheCreationTokens: current.cumulativeCacheCreationTokens + (usage.cacheCreationTokens ?? 0),
27
+ };
28
+ }
29
+ export function addTokenUsage(left, right) {
30
+ return {
31
+ promptTokens: left.promptTokens + (right.promptTokens ?? 0),
32
+ completionTokens: left.completionTokens + (right.completionTokens ?? 0),
33
+ totalTokens: left.totalTokens + (right.totalTokens ?? 0),
34
+ cacheReadTokens: (left.cacheReadTokens ?? 0) + (right.cacheReadTokens ?? 0),
35
+ cacheCreationTokens: (left.cacheCreationTokens ?? 0) + (right.cacheCreationTokens ?? 0),
36
+ };
37
+ }
38
+ export function cacheHitRateFromTokens(promptTokens, cacheReadTokens, cacheCreationTokens) {
39
+ const read = cacheReadTokens ?? 0;
40
+ const creation = cacheCreationTokens ?? 0;
41
+ if (read === 0 && creation === 0)
42
+ return undefined;
43
+ const uncached = Math.max(0, promptTokens - read - creation);
44
+ const denom = read + creation + uncached;
45
+ if (denom === 0)
46
+ return undefined;
47
+ return read / denom;
48
+ }
49
+ export function cacheHitRateFromUsage(usage) {
50
+ if (!usage)
51
+ return undefined;
52
+ return cacheHitRateFromTokens(usage.promptTokens ?? 0, usage.cacheReadTokens, usage.cacheCreationTokens);
53
+ }
54
+ export function cumulativeCacheHitRate(counters) {
55
+ return cacheHitRateFromTokens(counters.cumulativePromptTokens, counters.cumulativeCacheReadTokens, counters.cumulativeCacheCreationTokens);
56
+ }
57
+ /**
58
+ * Fold one run's cumulative usage onto a session baseline, producing the new
59
+ * session-cumulative TokenUsage.
60
+ *
61
+ * The LLM client is recreated per `engine.run()`, so its `getUsage()` totals
62
+ * count only the current run and start from zero each run. To get a true
63
+ * session cumulative we add the run's running total onto the baseline captured
64
+ * at run start (the value persisted from prior runs).
65
+ *
66
+ * Crucially this is `baseline + current`, NOT `baseline += current`: the
67
+ * turn-boundary heartbeat fires many times within one run, each reading the
68
+ * run's growing total. Recomputing `baseline + current` every time is
69
+ * idempotent — it never double-counts — whereas a `+=` would add the running
70
+ * total repeatedly. Feed the folded result back as the baseline only across
71
+ * run boundaries (new client, fresh run total).
72
+ */
73
+ export function foldRunUsage(baseline, runUsage) {
74
+ return {
75
+ promptTokens: baseline.promptTokens + runUsage.totalPromptTokens,
76
+ completionTokens: baseline.completionTokens + runUsage.totalCompletionTokens,
77
+ totalTokens: baseline.totalTokens + runUsage.totalTokens,
78
+ cacheReadTokens: (baseline.cacheReadTokens ?? 0) + runUsage.totalCacheReadTokens,
79
+ cacheCreationTokens: (baseline.cacheCreationTokens ?? 0) + runUsage.totalCacheCreationTokens,
80
+ };
81
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Step-gap steering queue — pure helpers over a per-session list of pending
3
+ * user messages. Each entry carries a stable `id` so the host can (a) match the
4
+ * later `steer_injected` event back to the queued draft it showed in the UI and
5
+ * (b) revoke a still-pending entry via `unsteer` before the turn loop consumes
6
+ * it. Pure + side-effect-free so it unit-tests without the Engine/TurnLoop
7
+ * harness (same rationale as the renderer's queuedInput.ts).
8
+ */
9
+ export interface SteerItem {
10
+ id: string;
11
+ text: string;
12
+ clientMessageId?: string;
13
+ }
14
+ /** Append a steer entry. Blank text is dropped (returns the list unchanged). */
15
+ export declare function enqueueSteerItem(list: SteerItem[], id: string, text: string, clientMessageId?: string): SteerItem[];
16
+ /**
17
+ * Take everything currently queued and clear the list. Returns the drained
18
+ * entries (in order) and the now-empty remainder. The turn loop calls this at
19
+ * each step boundary.
20
+ */
21
+ export declare function consumeSteerItems(list: SteerItem[]): {
22
+ drained: SteerItem[];
23
+ rest: SteerItem[];
24
+ };
25
+ /**
26
+ * Remove a still-pending entry by id (the 撤回 path). Returns the new list and
27
+ * whether anything was removed — `false` means it was already consumed (the
28
+ * host can't take it back; it has already been spliced into the run).
29
+ */
30
+ export declare function removeSteerItem(list: SteerItem[], id: string): {
31
+ list: SteerItem[];
32
+ removed: boolean;
33
+ };
@@ -0,0 +1,26 @@
1
+ /** Append a steer entry. Blank text is dropped (returns the list unchanged). */
2
+ export function enqueueSteerItem(list, id, text, clientMessageId) {
3
+ const t = text?.trim();
4
+ if (!id || !t)
5
+ return list;
6
+ return [...list, { id, text: t, ...(clientMessageId ? { clientMessageId } : {}) }];
7
+ }
8
+ /**
9
+ * Take everything currently queued and clear the list. Returns the drained
10
+ * entries (in order) and the now-empty remainder. The turn loop calls this at
11
+ * each step boundary.
12
+ */
13
+ export function consumeSteerItems(list) {
14
+ if (list.length === 0)
15
+ return { drained: [], rest: [] };
16
+ return { drained: list, rest: [] };
17
+ }
18
+ /**
19
+ * Remove a still-pending entry by id (the 撤回 path). Returns the new list and
20
+ * whether anything was removed — `false` means it was already consumed (the
21
+ * host can't take it back; it has already been spliced into the run).
22
+ */
23
+ export function removeSteerItem(list, id) {
24
+ const next = list.filter((item) => item.id !== id);
25
+ return { list: next, removed: next.length !== list.length };
26
+ }
@@ -17,6 +17,7 @@ export declare class StreamingToolQueue {
17
17
  private readonly pending;
18
18
  private readonly unsafeQueue;
19
19
  private readonly callOrder;
20
+ private readonly toolNameById;
20
21
  private draining;
21
22
  constructor(executor: ToolExecutor);
22
23
  /**
@@ -27,8 +28,19 @@ export declare class StreamingToolQueue {
27
28
  /**
28
29
  * Execute all remaining unsafe tools sequentially, then await all results.
29
30
  * Returns results in original enqueue order.
31
+ *
32
+ * A tool's promise can REJECT — not just resolve to an error ToolResult —
33
+ * because permission.handleAsk (and pre_tool_use "ask") throw outside
34
+ * executeSingle's try/catch. We must never let one rejection (a) abort the
35
+ * drain and lose the other tools' results, or (b) leave a hole that surfaces
36
+ * as `undefined` downstream (toolResultToBlock would crash on it). So every
37
+ * await is funneled through toResult(), which converts a thrown value into a
38
+ * synthetic error ToolResult, and any id still missing at the end is filled
39
+ * with one too.
30
40
  */
31
41
  drain(): Promise<ToolResult[]>;
42
+ /** Await a tool promise, converting a rejection into an error ToolResult. */
43
+ private toResult;
32
44
  /** Number of tools currently enqueued or executing. */
33
45
  get size(): number;
34
46
  }
@@ -15,6 +15,7 @@ export class StreamingToolQueue {
15
15
  pending = new Map();
16
16
  unsafeQueue = [];
17
17
  callOrder = [];
18
+ toolNameById = new Map();
18
19
  draining = false;
19
20
  constructor(executor) {
20
21
  this.executor = executor;
@@ -25,9 +26,14 @@ export class StreamingToolQueue {
25
26
  */
26
27
  enqueue(call) {
27
28
  this.callOrder.push(call.id);
29
+ this.toolNameById.set(call.id, call.toolName);
28
30
  if (this.executor.isConcurrencySafe(call.toolName)) {
29
- // Start immediately
30
- this.pending.set(call.id, this.executor.executeSingle(call));
31
+ // Start immediately. Attach a no-op catch so a synchronous/early rejection
32
+ // doesn't surface as an unhandledRejection before drain() awaits it; the
33
+ // real handling (→ error ToolResult) happens in drain()/toResult().
34
+ const p = this.executor.executeSingle(call);
35
+ p.catch(() => { });
36
+ this.pending.set(call.id, p);
31
37
  }
32
38
  else {
33
39
  this.unsafeQueue.push(call);
@@ -36,23 +42,57 @@ export class StreamingToolQueue {
36
42
  /**
37
43
  * Execute all remaining unsafe tools sequentially, then await all results.
38
44
  * Returns results in original enqueue order.
45
+ *
46
+ * A tool's promise can REJECT — not just resolve to an error ToolResult —
47
+ * because permission.handleAsk (and pre_tool_use "ask") throw outside
48
+ * executeSingle's try/catch. We must never let one rejection (a) abort the
49
+ * drain and lose the other tools' results, or (b) leave a hole that surfaces
50
+ * as `undefined` downstream (toolResultToBlock would crash on it). So every
51
+ * await is funneled through toResult(), which converts a thrown value into a
52
+ * synthetic error ToolResult, and any id still missing at the end is filled
53
+ * with one too.
39
54
  */
40
55
  async drain() {
41
56
  if (this.draining)
42
57
  throw new Error("StreamingToolQueue already draining");
43
58
  this.draining = true;
44
- // Execute unsafe tools sequentially
59
+ const resultMap = new Map();
60
+ // Execute unsafe tools sequentially. A rejection here must not stop the
61
+ // remaining unsafe tools from running.
45
62
  for (const call of this.unsafeQueue) {
46
- this.pending.set(call.id, this.executor.executeSingle(call));
47
- await this.pending.get(call.id);
63
+ const p = this.executor.executeSingle(call);
64
+ this.pending.set(call.id, p);
65
+ resultMap.set(call.id, await this.toResult(call.id, call.toolName, p));
48
66
  }
49
- // Await all results
50
- const resultMap = new Map();
67
+ // Await all results (concurrency-safe tools started at enqueue time).
51
68
  for (const [id, promise] of this.pending) {
52
- resultMap.set(id, await promise);
69
+ if (resultMap.has(id))
70
+ continue; // already drained above
71
+ const toolName = this.toolNameById.get(id) ?? "unknown";
72
+ resultMap.set(id, await this.toResult(id, toolName, promise));
73
+ }
74
+ // Return in original order, with a synthetic error for any id that somehow
75
+ // never produced a result (defensive — should not happen now).
76
+ return this.callOrder.map((id) => resultMap.get(id) ?? {
77
+ id,
78
+ toolName: this.toolNameById.get(id) ?? "unknown",
79
+ error: "Tool execution produced no result.",
80
+ isError: true,
81
+ });
82
+ }
83
+ /** Await a tool promise, converting a rejection into an error ToolResult. */
84
+ async toResult(id, toolName, p) {
85
+ try {
86
+ return await p;
87
+ }
88
+ catch (err) {
89
+ return {
90
+ id,
91
+ toolName,
92
+ error: err instanceof Error ? err.message : String(err),
93
+ isError: true,
94
+ };
53
95
  }
54
- // Return in original order
55
- return this.callOrder.map((id) => resultMap.get(id));
56
96
  }
57
97
  /** Number of tools currently enqueued or executing. */
58
98
  get size() {
@@ -4,19 +4,27 @@
4
4
  * Following Claude Code's po_() pattern:
5
5
  * pre_check → model_call → post_check → tool_exec → context_mgmt → hook_notify → next turn
6
6
  */
7
- import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult } from "../types.js";
7
+ import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult, TokenUsage } from "../types.js";
8
+ import type { SteerItem } from "./steer-queue.js";
8
9
  import { ModelFacade } from "./model-facade.js";
9
10
  import { ToolExecutor } from "../tool-system/executor.js";
10
11
  import { ContextManager } from "../context/manager.js";
11
12
  import { HookRegistry } from "../hooks/registry.js";
12
13
  import { Transcript } from "../session/transcript.js";
13
- import { type GoalConfig } from "./goal.js";
14
+ import { type CumulativeUsageCounters } from "./session-usage.js";
15
+ import { type GoalConfig, type GoalExtension } from "./goal.js";
14
16
  export interface TurnLoopConfig {
15
17
  maxTurns: number;
16
18
  maxToolCallsPerTurn: number;
17
19
  tokenBudget?: number;
18
20
  onStream?: StreamCallback;
19
21
  signal?: AbortSignal;
22
+ /**
23
+ * Image-bearing messages added for the current request and not yet consumed
24
+ * by the model. TurnLoop preserves their base64 for one successful model
25
+ * response, then downgrades them to placeholders in the working history.
26
+ */
27
+ freshImageMessages?: Iterable<Message>;
20
28
  /**
21
29
  * Fired after each turn boundary is recorded. Lets the engine flush an
22
30
  * up-to-date snapshot to state.json mid-run, so a long run doesn't leave
@@ -35,7 +43,8 @@ export interface TurnLoopConfig {
35
43
  * Max consecutive times an on_stop handler may block termination before
36
44
  * the loop forces a stop, mirroring Claude Code's stop-hook block cap.
37
45
  * Resets to 0 whenever a turn completes without being blocked. Defaults
38
- * to 8. Independent of maxTurns, which still bounds total turns.
46
+ * to GOAL_DEFAULT_MAX_STOP_BLOCKS. Independent of maxTurns, which still
47
+ * bounds total turns.
39
48
  */
40
49
  maxStopBlocks?: number;
41
50
  }
@@ -74,6 +83,35 @@ export interface TurnLoopDeps {
74
83
  before: number;
75
84
  after: number;
76
85
  } | null;
86
+ /**
87
+ * Records one LLM response into the session-monotonic cumulative counters.
88
+ * Returns the updated counters so usage_update can carry both metric scopes.
89
+ */
90
+ recordCumulativeUsage?: (usage: TokenUsage) => CumulativeUsageCounters;
91
+ /**
92
+ * Reads/clears any user messages queued for THIS session via the steering
93
+ * channel (Engine.enqueueSteer) while a run is in flight. Consumed at the top
94
+ * of each turn so the messages join the next LLM request WITHOUT aborting the
95
+ * current run — the "不打断、步间隙注入" path (vs the relay abort+resend).
96
+ * Returns [] when nothing is queued. Wired by Engine; absent in standalone
97
+ * tests (turn loop tolerates undefined).
98
+ */
99
+ consumeSteer?: (source?: "normal_step" | "finalize_backfill") => SteerItem[];
100
+ /**
101
+ * Execution-level idempotency guard for host-supplied user/steer intents.
102
+ * Returns false only when a present clientMessageId has already entered this
103
+ * run or the persisted transcript. Messages without an id bypass this guard.
104
+ */
105
+ claimClientMessageId?: (clientMessageId: string, source: "steer") => boolean;
106
+ /**
107
+ * Clear this session's PERSISTED goal (state.activeGoal) and drop the
108
+ * in-flight goal-stop hook, so a user-initiated cancel_goal both stops the
109
+ * current run AND prevents the goal from re-inheriting on the next bare send.
110
+ * Wired by Engine (delegates to Engine.clearGoal for the running session);
111
+ * absent in standalone tests (turn loop tolerates undefined — the local
112
+ * goalTracker short-circuit still stops the run).
113
+ */
114
+ clearPersistedGoal?: () => void;
77
115
  }
78
116
  export interface TurnLoopResult {
79
117
  text: string;
@@ -88,7 +126,7 @@ export interface TurnLoopResult {
88
126
  export declare function toolResultToBlock(result: ToolResult): ContentBlock;
89
127
  export declare class TurnLoop {
90
128
  private readonly deps;
91
- private readonly config;
129
+ private config;
92
130
  private turnCount;
93
131
  /** Tool IDs already emitted as tool_use_start during streaming (to avoid duplicates). */
94
132
  private streamedToolIds;
@@ -101,12 +139,49 @@ export declare class TurnLoop {
101
139
  private currentTurnLog;
102
140
  /** Last emitted ctx token estimate; used to skip no-op usage_update events. */
103
141
  private lastCtxEmit;
142
+ private currentTurnUsage;
143
+ private currentCumulativeUsage;
144
+ private readonly pendingImageMessages;
104
145
  /**
105
146
  * Consecutive on_stop blocks (Goal mode kept the agent going). Reset to 0
106
147
  * on any unblocked completion. When it reaches config.maxStopBlocks the
107
148
  * loop forces a stop so a stuck goal can't loop forever.
108
149
  */
109
150
  private stopBlockCount;
151
+ /**
152
+ * Run-scoped goal budget tracker (Goal mode). Hoisted to an instance field
153
+ * (not a run() local) so extend() can bump its budgets mid-run. Null when no
154
+ * goal or between runs.
155
+ */
156
+ private goalTracker;
157
+ /**
158
+ * Whether an approaching_limit marker was already emitted for the current
159
+ * ceiling. Reset when the run advances past the moment (a fresh extension, or
160
+ * the goal completing) so the next approach re-announces. Prevents the marker
161
+ * being re-emitted every turn while still within the approach threshold.
162
+ */
163
+ private approachAnnounced;
164
+ /**
165
+ * Extend the in-flight run's limits (TODO 3.1 — 运行中续轮/加预算). Mutates
166
+ * the maxTurns ceiling, the maxStopBlocks cap, and/or the live goal budgets;
167
+ * the loop re-reads all of them each turn so the change takes effect on the
168
+ * next iteration. No-op for fields not supplied. Returns the resulting
169
+ * effective limits.
170
+ */
171
+ extend(opts: GoalExtension): {
172
+ maxTurns: number;
173
+ tokenBudget?: number;
174
+ timeBudgetMs?: number;
175
+ maxStopBlocks: number;
176
+ };
177
+ /**
178
+ * Goal mode only: if the run is nearing EITHER stop ceiling (maxTurns or
179
+ * maxStopBlocks) and we haven't announced it yet, emit one approaching_limit
180
+ * marker so the UI can offer a "再续" button while the run is still live.
181
+ * Watches both limits because a re-blocked goal hits the stop-block cap long
182
+ * before maxTurns. Idempotent within an approach window via approachAnnounced.
183
+ */
184
+ private maybeAnnounceApproachingLimit;
110
185
  constructor(deps: TurnLoopDeps, config: TurnLoopConfig);
111
186
  /**
112
187
  * Emit a lifecycle hook with isSubAgent + sessionId auto-merged into data.
@@ -114,6 +189,19 @@ export declare class TurnLoop {
114
189
  * `decision` / `stop`. Use this instead of `deps.hooks.emit` directly so
115
190
  * every emit carries the same context envelope.
116
191
  */
192
+ /**
193
+ * Persist a turn_stopped marker on a user Stop so a resume can rebuild the
194
+ * renderer's "你在 Ns 后停止了" line (the in-memory turn_end is lost on reload,
195
+ * which makes the interrupted turn fold behind the process-card header).
196
+ * Skipped for sub-agents — their signal.aborted is usually a parent abort,
197
+ * not a user Stop, and sub-agent turns aren't shown as interruptible. The
198
+ * underlying append is idempotent, so calling this at several abort returns
199
+ * for one Stop still writes a single marker.
200
+ */
201
+ private markStopped;
202
+ private prepareMessagesForModel;
203
+ private markPendingImagesConsumed;
204
+ private trackFreshImageMessage;
117
205
  private emitHook;
118
206
  /**
119
207
  * Emit a usage_update so the UI ctx bar reflects current message-array
@@ -123,6 +211,7 @@ export declare class TurnLoop {
123
211
  * promptTokens to override our estimate).
124
212
  */
125
213
  private emitCtxFromMessages;
214
+ private recordResponseUsage;
126
215
  private emitCtxFromUsage;
127
216
  /**
128
217
  * Run the multi-turn agent loop until completion.
@@ -134,6 +223,7 @@ export declare class TurnLoop {
134
223
  */
135
224
  private callModelWithFallback;
136
225
  get currentTurn(): number;
226
+ private consumeQueuedSteer;
137
227
  /**
138
228
  * Generate synthetic error tool_results for any dangling tool_use blocks
139
229
  * that never received results (e.g. because the API call failed).