@cjhyy/code-shell-core 0.7.1 → 0.8.0

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 (594) hide show
  1. package/README.md +36 -23
  2. package/dist/agent/agent-definition-registry.d.ts +2 -2
  3. package/dist/agent/agent-definition-registry.js +47 -10
  4. package/dist/agent/agent-definition.d.ts +5 -0
  5. package/dist/agent/agent-definition.js +14 -0
  6. package/dist/automation/index.d.ts +7 -4
  7. package/dist/automation/index.js +4 -2
  8. package/dist/automation/runner.js +3 -1
  9. package/dist/automation/scheduler.d.ts +57 -1
  10. package/dist/automation/scheduler.js +123 -23
  11. package/dist/automation/store.d.ts +6 -2
  12. package/dist/automation/store.js +7 -3
  13. package/dist/capabilities/index.d.ts +109 -0
  14. package/dist/capabilities/index.js +80 -0
  15. package/dist/capability-control/disabled-lists.d.ts +2 -1
  16. package/dist/capability-control/disabled-lists.js +3 -5
  17. package/dist/capability-control/overlay.d.ts +14 -1
  18. package/dist/capability-control/overlay.js +40 -0
  19. package/dist/capability-control/service.js +6 -4
  20. package/dist/cli/agent-server-stdio.d.ts +1 -1
  21. package/dist/cli/agent-server-stdio.js +66 -6
  22. package/dist/cli/agent-server-tcp.js +7 -3
  23. package/dist/context/compaction.d.ts +39 -3
  24. package/dist/context/compaction.js +151 -39
  25. package/dist/context/manager.d.ts +17 -1
  26. package/dist/context/manager.js +36 -1
  27. package/dist/credentials/access.d.ts +3 -1
  28. package/dist/credentials/access.js +24 -3
  29. package/dist/credentials/index.d.ts +1 -1
  30. package/dist/credentials/index.js +1 -1
  31. package/dist/credentials/oauth.d.ts +4 -1
  32. package/dist/credentials/oauth.js +14 -0
  33. package/dist/credentials/store.d.ts +15 -1
  34. package/dist/credentials/store.js +17 -4
  35. package/dist/credentials/types.d.ts +24 -0
  36. package/dist/credentials/use-credential-tool.js +15 -1
  37. package/dist/engine/auxiliary-pipeline.d.ts +40 -0
  38. package/dist/engine/auxiliary-pipeline.js +181 -0
  39. package/dist/{settings → engine}/disk-defaults.d.ts +2 -2
  40. package/dist/{settings → engine}/disk-defaults.js +5 -2
  41. package/dist/engine/dynamic-tool-defs.d.ts +4 -0
  42. package/dist/engine/dynamic-tool-defs.js +4 -0
  43. package/dist/engine/engine.d.ts +254 -195
  44. package/dist/engine/engine.js +1812 -2041
  45. package/dist/engine/file-history-hook.d.ts +11 -0
  46. package/dist/engine/file-history-hook.js +31 -0
  47. package/dist/engine/goal-judge-context.d.ts +9 -0
  48. package/dist/engine/goal-judge-context.js +234 -0
  49. package/dist/engine/injected-context-cache.d.ts +6 -0
  50. package/dist/engine/injected-context-cache.js +13 -0
  51. package/dist/engine/input-attachments.d.ts +1 -1
  52. package/dist/engine/model-facade.d.ts +3 -3
  53. package/dist/engine/model-facade.js +15 -14
  54. package/dist/engine/permission-controller.d.ts +55 -0
  55. package/dist/engine/permission-controller.js +148 -0
  56. package/dist/engine/prompt-cache-diagnostics.d.ts +58 -0
  57. package/dist/engine/prompt-cache-diagnostics.js +178 -0
  58. package/dist/engine/run-accounting.d.ts +41 -0
  59. package/dist/engine/run-accounting.js +126 -0
  60. package/dist/engine/run-context.d.ts +44 -0
  61. package/dist/engine/run-context.js +88 -0
  62. package/dist/engine/run-environment.d.ts +30 -0
  63. package/dist/engine/run-environment.js +77 -0
  64. package/dist/engine/run-finalize.d.ts +58 -0
  65. package/dist/engine/run-finalize.js +245 -0
  66. package/dist/engine/run-goal.d.ts +58 -0
  67. package/dist/engine/run-goal.js +188 -0
  68. package/dist/engine/run-image-input.d.ts +1 -1
  69. package/dist/engine/run-image-input.js +5 -3
  70. package/dist/engine/run-session-open.d.ts +41 -0
  71. package/dist/engine/run-session-open.js +186 -0
  72. package/dist/engine/run-setup.d.ts +42 -0
  73. package/dist/engine/run-setup.js +58 -0
  74. package/dist/engine/run-tooling.d.ts +125 -0
  75. package/dist/engine/run-tooling.js +283 -0
  76. package/dist/engine/run-types.d.ts +139 -0
  77. package/dist/engine/run-types.js +12 -0
  78. package/dist/engine/run-workspace.d.ts +50 -0
  79. package/dist/engine/run-workspace.js +114 -0
  80. package/dist/engine/steer-queue.d.ts +1 -1
  81. package/dist/engine/subagent-spawner.d.ts +32 -0
  82. package/dist/engine/subagent-spawner.js +287 -0
  83. package/dist/engine/tool-summary.d.ts +2 -2
  84. package/dist/engine/tool-summary.js +2 -2
  85. package/dist/engine/turn-loop.d.ts +49 -10
  86. package/dist/engine/turn-loop.js +370 -104
  87. package/dist/engine/types.d.ts +30 -3
  88. package/dist/engine/types.js +1 -1
  89. package/dist/{engine/goal.d.ts → goal/lifecycle.d.ts} +76 -4
  90. package/dist/goal/lifecycle.js +482 -0
  91. package/dist/hooks/events.d.ts +24 -10
  92. package/dist/hooks/goal-stop-hook.d.ts +17 -28
  93. package/dist/hooks/goal-stop-hook.js +110 -465
  94. package/dist/hooks/registry.js +1 -1
  95. package/dist/index.d.ts +48 -108
  96. package/dist/index.extension.d.ts +51 -0
  97. package/dist/index.extension.js +39 -0
  98. package/dist/index.internal.d.ts +86 -0
  99. package/dist/index.internal.js +92 -0
  100. package/dist/index.js +34 -123
  101. package/dist/links/cli.d.ts +48 -0
  102. package/dist/links/cli.js +617 -0
  103. package/dist/links/http.d.ts +40 -0
  104. package/dist/links/http.js +166 -0
  105. package/dist/links/index.d.ts +4 -0
  106. package/dist/links/index.js +3 -0
  107. package/dist/links/link-action-tool.d.ts +5 -0
  108. package/dist/links/link-action-tool.js +241 -0
  109. package/dist/links/providers.d.ts +8 -0
  110. package/dist/links/providers.js +665 -0
  111. package/dist/links/types.d.ts +44 -0
  112. package/dist/llm/client-base.d.ts +7 -0
  113. package/dist/llm/client-base.js +60 -0
  114. package/dist/llm/model-pool.d.ts +1 -1
  115. package/dist/llm/model-pool.js +1 -1
  116. package/dist/llm/providers/anthropic.d.ts +1 -0
  117. package/dist/llm/providers/anthropic.js +9 -0
  118. package/dist/llm/providers/openai.d.ts +1 -0
  119. package/dist/llm/providers/openai.js +17 -0
  120. package/dist/model-catalog/index.js +15 -2
  121. package/dist/model-catalog/resolve.d.ts +13 -1
  122. package/dist/model-catalog/resolve.js +37 -2
  123. package/dist/model-catalog/types.d.ts +74 -48
  124. package/dist/model-catalog/types.js +16 -5
  125. package/dist/model-catalog/upsert.d.ts +3 -1
  126. package/dist/model-catalog/upsert.js +9 -0
  127. package/dist/onboarding.d.ts +0 -1
  128. package/dist/onboarding.js +1 -22
  129. package/dist/panel-apps/bindings.d.ts +24 -0
  130. package/dist/panel-apps/bindings.js +69 -0
  131. package/dist/panel-apps/github-archive.d.ts +21 -0
  132. package/dist/panel-apps/github-archive.js +98 -0
  133. package/dist/panel-apps/index.d.ts +4 -0
  134. package/dist/panel-apps/index.js +4 -0
  135. package/dist/panel-apps/installer.d.ts +68 -0
  136. package/dist/panel-apps/installer.js +622 -0
  137. package/dist/panel-apps/manifest.d.ts +364 -0
  138. package/dist/panel-apps/manifest.js +189 -0
  139. package/dist/panel-apps/paths.d.ts +13 -0
  140. package/dist/panel-apps/paths.js +38 -0
  141. package/dist/panel-apps/registry.d.ts +51 -0
  142. package/dist/panel-apps/registry.js +99 -0
  143. package/dist/panel-apps/runtime.d.ts +80 -0
  144. package/dist/panel-apps/runtime.js +154 -0
  145. package/dist/plugins/gitOps.js +8 -1
  146. package/dist/plugins/installedPlugins.d.ts +2 -1
  147. package/dist/plugins/installedPlugins.js +146 -8
  148. package/dist/plugins/installer/codex/convertAgents.d.ts +4 -1
  149. package/dist/plugins/installer/codex/convertAgents.js +62 -8
  150. package/dist/plugins/installer/codex/convertCommands.d.ts +5 -5
  151. package/dist/plugins/installer/codex/convertCommands.js +35 -12
  152. package/dist/plugins/installer/codex/convertHooks.d.ts +10 -0
  153. package/dist/plugins/installer/codex/convertHooks.js +82 -0
  154. package/dist/plugins/installer/codex/convertSkills.js +41 -6
  155. package/dist/plugins/installer/install.d.ts +7 -1
  156. package/dist/plugins/installer/install.js +21 -87
  157. package/dist/plugins/installer/installFromArchive.d.ts +26 -4
  158. package/dist/plugins/installer/installFromArchive.js +44 -27
  159. package/dist/plugins/installer/installFromNpm.d.ts +31 -0
  160. package/dist/plugins/installer/installFromNpm.js +273 -0
  161. package/dist/plugins/installer/loadPluginAgents.js +17 -4
  162. package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
  163. package/dist/plugins/installer/loadPluginMcp.js +245 -35
  164. package/dist/plugins/installer/normalizeManifest.d.ts +13 -0
  165. package/dist/plugins/installer/normalizeManifest.js +306 -0
  166. package/dist/plugins/installer/npmTar.d.ts +13 -0
  167. package/dist/plugins/installer/npmTar.js +321 -0
  168. package/dist/plugins/installer/parseSource.d.ts +11 -0
  169. package/dist/plugins/installer/parseSource.js +48 -0
  170. package/dist/plugins/installer/preview.d.ts +101 -0
  171. package/dist/plugins/installer/preview.js +336 -0
  172. package/dist/plugins/installer/projectPluginSource.d.ts +14 -0
  173. package/dist/plugins/installer/projectPluginSource.js +140 -0
  174. package/dist/plugins/installer/types.d.ts +775 -0
  175. package/dist/plugins/installer/types.js +100 -0
  176. package/dist/plugins/installer/unzip.d.ts +11 -0
  177. package/dist/plugins/installer/unzip.js +123 -9
  178. package/dist/plugins/installer/update.d.ts +2 -2
  179. package/dist/plugins/installer/update.js +43 -4
  180. package/dist/plugins/loadPluginHooks.d.ts +19 -2
  181. package/dist/plugins/loadPluginHooks.js +97 -86
  182. package/dist/plugins/pluginAutomationTemplates.d.ts +18 -0
  183. package/dist/plugins/pluginAutomationTemplates.js +50 -0
  184. package/dist/plugins/pluginCatalog.d.ts +45 -0
  185. package/dist/plugins/pluginCatalog.js +87 -0
  186. package/dist/plugins/pluginCommandHook.d.ts +6 -0
  187. package/dist/plugins/pluginCommandHook.js +25 -8
  188. package/dist/plugins/pluginCommandsLoader.d.ts +19 -0
  189. package/dist/plugins/pluginCommandsLoader.js +181 -7
  190. package/dist/plugins/pluginContent.d.ts +10 -1
  191. package/dist/plugins/pluginContent.js +87 -30
  192. package/dist/plugins/pluginHookApproval.d.ts +30 -0
  193. package/dist/plugins/pluginHookApproval.js +160 -0
  194. package/dist/plugins/pluginHookIntegrity.d.ts +69 -0
  195. package/dist/plugins/pluginHookIntegrity.js +361 -0
  196. package/dist/plugins/pluginInstaller.js +71 -12
  197. package/dist/plugins/pluginMcpApproval.d.ts +19 -0
  198. package/dist/plugins/pluginMcpApproval.js +110 -0
  199. package/dist/plugins/pluginMcpIntegrity.d.ts +21 -0
  200. package/dist/plugins/pluginMcpIntegrity.js +74 -0
  201. package/dist/plugins/types.d.ts +30 -0
  202. package/dist/plugins/varRewrite.d.ts +9 -5
  203. package/dist/plugins/varRewrite.js +26 -17
  204. package/dist/preset/index.d.ts +11 -8
  205. package/dist/preset/index.js +62 -203
  206. package/dist/product/define.js +9 -3
  207. package/dist/product/types.d.ts +0 -2
  208. package/dist/profile/activation.d.ts +36 -0
  209. package/dist/profile/activation.js +57 -0
  210. package/dist/profile/catalog-store.d.ts +77 -0
  211. package/dist/profile/catalog-store.js +346 -0
  212. package/dist/profile/catalog.d.ts +54 -0
  213. package/dist/profile/catalog.js +152 -0
  214. package/dist/profile/index.d.ts +7 -0
  215. package/dist/profile/index.js +7 -0
  216. package/dist/profile/requirements.d.ts +76 -0
  217. package/dist/profile/requirements.js +117 -0
  218. package/dist/profile/resolve.d.ts +11 -0
  219. package/dist/profile/resolve.js +41 -0
  220. package/dist/profile/store.d.ts +17 -0
  221. package/dist/profile/store.js +167 -0
  222. package/dist/profile/types.d.ts +321 -0
  223. package/dist/profile/types.js +104 -0
  224. package/dist/prompt/composer.d.ts +46 -7
  225. package/dist/prompt/composer.js +78 -48
  226. package/dist/prompt/instruction-scanner.d.ts +6 -5
  227. package/dist/prompt/instruction-scanner.js +8 -23
  228. package/dist/prompt/section-loader.d.ts +2 -2
  229. package/dist/prompt/section-loader.js +7 -4
  230. package/dist/prompt/sections/browser.md +10 -9
  231. package/dist/prompt/sections/harness-base.md +9 -0
  232. package/dist/protocol/chat-session-manager.d.ts +64 -1
  233. package/dist/protocol/chat-session-manager.js +104 -0
  234. package/dist/protocol/chat-session.d.ts +61 -10
  235. package/dist/protocol/chat-session.js +135 -25
  236. package/dist/protocol/client.d.ts +33 -3
  237. package/dist/protocol/client.js +96 -5
  238. package/dist/protocol/index.d.ts +2 -2
  239. package/dist/protocol/index.js +2 -2
  240. package/dist/protocol/mobile-remote-types.d.ts +406 -0
  241. package/dist/protocol/mobile-remote-types.js +1 -0
  242. package/dist/protocol/server.d.ts +127 -3
  243. package/dist/protocol/server.js +1309 -182
  244. package/dist/protocol/types.d.ts +152 -34
  245. package/dist/protocol/types.js +16 -0
  246. package/dist/run/ArtifactTracker.d.ts +6 -1
  247. package/dist/run/ArtifactTracker.js +29 -21
  248. package/dist/run/EngineRunner.d.ts +2 -0
  249. package/dist/run/EngineRunner.js +1 -0
  250. package/dist/run/RunManager.d.ts +26 -0
  251. package/dist/run/RunManager.js +82 -11
  252. package/dist/run/factory.d.ts +3 -0
  253. package/dist/run/factory.js +2 -0
  254. package/dist/runtime/safe-spawn.d.ts +6 -0
  255. package/dist/runtime/safe-spawn.js +8 -13
  256. package/dist/services/auto-dream.d.ts +13 -1
  257. package/dist/services/auto-dream.js +56 -8
  258. package/dist/services/dream-consolidation.js +10 -1
  259. package/dist/services/memory-orchestrator.js +6 -2
  260. package/dist/services/oauth.js +4 -1
  261. package/dist/services/session-memory.d.ts +4 -4
  262. package/dist/services/session-memory.js +13 -10
  263. package/dist/session/memory.d.ts +26 -7
  264. package/dist/session/memory.js +79 -21
  265. package/dist/session/session-manager.d.ts +182 -28
  266. package/dist/session/session-manager.js +910 -130
  267. package/dist/session/session-message.d.ts +22 -0
  268. package/dist/session/session-message.js +1 -0
  269. package/dist/session/transcript.d.ts +46 -5
  270. package/dist/session/transcript.js +159 -3
  271. package/dist/settings/feature-flags.d.ts +40 -0
  272. package/dist/settings/feature-flags.js +40 -0
  273. package/dist/settings/manager.d.ts +39 -1
  274. package/dist/settings/manager.js +102 -41
  275. package/dist/settings/migrate-config.js +4 -2
  276. package/dist/settings/schema.d.ts +511 -177
  277. package/dist/settings/schema.js +98 -25
  278. package/dist/skills/scanner.d.ts +6 -2
  279. package/dist/skills/scanner.js +160 -9
  280. package/dist/sources/adapter.d.ts +14 -0
  281. package/dist/sources/adapter.js +7 -0
  282. package/dist/sources/adapters/local-files.d.ts +10 -0
  283. package/dist/sources/adapters/local-files.js +127 -0
  284. package/dist/sources/adapters/mcp-resource.d.ts +17 -0
  285. package/dist/sources/adapters/mcp-resource.js +41 -0
  286. package/dist/sources/adapters/mock.d.ts +3 -0
  287. package/dist/sources/adapters/mock.js +29 -0
  288. package/dist/sources/binding.d.ts +6 -0
  289. package/dist/sources/binding.js +24 -0
  290. package/dist/sources/catalog.d.ts +6 -0
  291. package/dist/sources/catalog.js +64 -0
  292. package/dist/sources/context-summary.d.ts +8 -0
  293. package/dist/sources/context-summary.js +13 -0
  294. package/dist/sources/credential-status.d.ts +2 -0
  295. package/dist/sources/credential-status.js +13 -0
  296. package/dist/sources/index.d.ts +10 -0
  297. package/dist/sources/index.js +10 -0
  298. package/dist/sources/resolve.d.ts +28 -0
  299. package/dist/sources/resolve.js +47 -0
  300. package/dist/sources/truncate-utf8.d.ts +7 -0
  301. package/dist/sources/truncate-utf8.js +18 -0
  302. package/dist/sources/types.d.ts +71 -0
  303. package/dist/sources/types.js +26 -0
  304. package/dist/testing/fetch-stub.d.ts +32 -0
  305. package/dist/testing/fetch-stub.js +24 -0
  306. package/dist/themes/image.d.ts +18 -0
  307. package/dist/themes/image.js +117 -0
  308. package/dist/themes/index.d.ts +4 -0
  309. package/dist/themes/index.js +4 -0
  310. package/dist/themes/installer.d.ts +50 -0
  311. package/dist/themes/installer.js +290 -0
  312. package/dist/themes/manifest.d.ts +102 -0
  313. package/dist/themes/manifest.js +86 -0
  314. package/dist/themes/paths.d.ts +14 -0
  315. package/dist/themes/paths.js +33 -0
  316. package/dist/tool-system/browser-bridge.d.ts +53 -11
  317. package/dist/tool-system/browser-bridge.js +8 -9
  318. package/dist/tool-system/builtin/agent-heartbeat.d.ts +2 -2
  319. package/dist/tool-system/builtin/agent-heartbeat.js +20 -6
  320. package/dist/tool-system/builtin/agent-notifications.d.ts +127 -72
  321. package/dist/tool-system/builtin/agent-notifications.js +291 -101
  322. package/dist/tool-system/builtin/agent-output-file.js +1 -3
  323. package/dist/tool-system/builtin/agent-progress.d.ts +6 -0
  324. package/dist/tool-system/builtin/agent-progress.js +83 -0
  325. package/dist/tool-system/builtin/agent-registry.d.ts +38 -2
  326. package/dist/tool-system/builtin/agent-registry.js +178 -9
  327. package/dist/tool-system/builtin/agent-transcript-translator.js +1 -1
  328. package/dist/tool-system/builtin/agent.d.ts +2 -0
  329. package/dist/tool-system/builtin/agent.js +274 -33
  330. package/dist/tool-system/builtin/background-jobs.d.ts +55 -2
  331. package/dist/tool-system/builtin/background-jobs.js +96 -4
  332. package/dist/tool-system/builtin/background-work.d.ts +6 -0
  333. package/dist/tool-system/builtin/background-work.js +8 -1
  334. package/dist/tool-system/builtin/bash.js +5 -2
  335. package/dist/tool-system/builtin/browser-tools.d.ts +6 -3
  336. package/dist/tool-system/builtin/browser-tools.js +89 -23
  337. package/dist/tool-system/builtin/edit-model-catalog.d.ts +6 -0
  338. package/dist/tool-system/builtin/edit-model-catalog.js +172 -12
  339. package/dist/tool-system/builtin/edit.js +2 -6
  340. package/dist/tool-system/builtin/file-cache.d.ts +2 -0
  341. package/dist/tool-system/builtin/file-cache.js +4 -0
  342. package/dist/tool-system/builtin/generate-image.js +17 -10
  343. package/dist/tool-system/builtin/generate-video.js +26 -7
  344. package/dist/tool-system/builtin/grep.js +2 -2
  345. package/dist/tool-system/builtin/image-uploader.js +7 -1
  346. package/dist/tool-system/builtin/index.d.ts +32 -7
  347. package/dist/tool-system/builtin/index.js +237 -167
  348. package/dist/tool-system/builtin/memory.d.ts +6 -0
  349. package/dist/tool-system/builtin/memory.js +36 -15
  350. package/dist/tool-system/builtin/panel.d.ts +9 -0
  351. package/dist/tool-system/builtin/panel.js +314 -0
  352. package/dist/tool-system/builtin/read.js +5 -1
  353. package/dist/tool-system/builtin/send-message-to-session.d.ts +6 -0
  354. package/dist/tool-system/builtin/send-message-to-session.js +67 -0
  355. package/dist/tool-system/builtin/sources.d.ts +8 -0
  356. package/dist/tool-system/builtin/sources.js +137 -0
  357. package/dist/tool-system/builtin/tool-search.js +7 -2
  358. package/dist/tool-system/builtin/update-automation-memory.js +2 -0
  359. package/dist/tool-system/builtin/web-fetch.js +17 -13
  360. package/dist/tool-system/builtin/web-search.js +1 -3
  361. package/dist/tool-system/capability-module.d.ts +93 -0
  362. package/dist/tool-system/capability-module.js +53 -0
  363. package/dist/tool-system/context.d.ts +77 -19
  364. package/dist/tool-system/executor.js +75 -50
  365. package/dist/tool-system/external-tool-exposure.d.ts +60 -0
  366. package/dist/tool-system/external-tool-exposure.js +304 -0
  367. package/dist/tool-system/mcp-manager.d.ts +11 -1
  368. package/dist/tool-system/mcp-manager.js +19 -4
  369. package/dist/tool-system/mcp-tool-policy.d.ts +9 -0
  370. package/dist/tool-system/mcp-tool-policy.js +34 -0
  371. package/dist/tool-system/panel-bridge.d.ts +53 -0
  372. package/dist/tool-system/panel-bridge.js +1 -0
  373. package/dist/tool-system/permission.d.ts +26 -0
  374. package/dist/tool-system/permission.js +46 -0
  375. package/dist/tool-system/registry.d.ts +12 -1
  376. package/dist/tool-system/registry.js +31 -7
  377. package/dist/tool-system/sandbox/index.js +3 -8
  378. package/dist/tool-system/sandbox/seatbelt.js +2 -6
  379. package/dist/tool-system/session-tool-host.d.ts +139 -0
  380. package/dist/tool-system/session-tool-host.js +167 -0
  381. package/dist/tool-system/testing/tool-registry-harness.d.ts +31 -0
  382. package/dist/tool-system/testing/tool-registry-harness.js +51 -0
  383. package/dist/tool-system/validation.d.ts +14 -7
  384. package/dist/tool-system/validation.js +701 -7
  385. package/dist/types.d.ts +186 -20
  386. package/dist/{cc-orchestrator → utils}/cwd-normalize.d.ts +1 -0
  387. package/dist/{cc-orchestrator → utils}/cwd-normalize.js +1 -0
  388. package/dist/utils/file-mutex.d.ts +65 -0
  389. package/dist/utils/file-mutex.js +145 -0
  390. package/dist/utils/json.d.ts +2 -4
  391. package/dist/utils/json.js +2 -4
  392. package/dist/utils/lockfile.d.ts +30 -1
  393. package/dist/utils/lockfile.js +2 -2
  394. package/dist/utils/secret-scrubber.d.ts +8 -0
  395. package/dist/utils/secret-scrubber.js +227 -0
  396. package/dist/utils/toolDisplay.js +1 -1
  397. package/package.json +20 -5
  398. package/THIRD_PARTY_NOTICES.md +0 -206
  399. package/dist/arena/arena.d.ts +0 -45
  400. package/dist/arena/arena.js +0 -362
  401. package/dist/arena/context/context-tools.d.ts +0 -16
  402. package/dist/arena/context/context-tools.js +0 -272
  403. package/dist/arena/context/within-root.d.ts +0 -7
  404. package/dist/arena/context/within-root.js +0 -15
  405. package/dist/arena/detect-mode.d.ts +0 -20
  406. package/dist/arena/detect-mode.js +0 -78
  407. package/dist/arena/digest-builder.d.ts +0 -25
  408. package/dist/arena/digest-builder.js +0 -120
  409. package/dist/arena/index.d.ts +0 -29
  410. package/dist/arena/index.js +0 -29
  411. package/dist/arena/iterate/convergence.d.ts +0 -25
  412. package/dist/arena/iterate/convergence.js +0 -103
  413. package/dist/arena/iterate/formats/index.d.ts +0 -22
  414. package/dist/arena/iterate/formats/index.js +0 -283
  415. package/dist/arena/iterate/index.d.ts +0 -11
  416. package/dist/arena/iterate/index.js +0 -9
  417. package/dist/arena/iterate/iterative-arena.d.ts +0 -23
  418. package/dist/arena/iterate/iterative-arena.js +0 -237
  419. package/dist/arena/iterate/parse.d.ts +0 -42
  420. package/dist/arena/iterate/parse.js +0 -123
  421. package/dist/arena/iterate/phases/argue.d.ts +0 -22
  422. package/dist/arena/iterate/phases/argue.js +0 -165
  423. package/dist/arena/iterate/phases/revise.d.ts +0 -16
  424. package/dist/arena/iterate/phases/revise.js +0 -62
  425. package/dist/arena/iterate/phases/tournament.d.ts +0 -34
  426. package/dist/arena/iterate/phases/tournament.js +0 -113
  427. package/dist/arena/iterate/tools/web-tools.d.ts +0 -13
  428. package/dist/arena/iterate/tools/web-tools.js +0 -54
  429. package/dist/arena/iterate/types.d.ts +0 -152
  430. package/dist/arena/iterate/types.js +0 -8
  431. package/dist/arena/ledger.d.ts +0 -47
  432. package/dist/arena/ledger.js +0 -159
  433. package/dist/arena/lenses/architecture.d.ts +0 -5
  434. package/dist/arena/lenses/architecture.js +0 -22
  435. package/dist/arena/lenses/engineering.d.ts +0 -5
  436. package/dist/arena/lenses/engineering.js +0 -22
  437. package/dist/arena/lenses/general.d.ts +0 -5
  438. package/dist/arena/lenses/general.js +0 -20
  439. package/dist/arena/lenses/index.d.ts +0 -16
  440. package/dist/arena/lenses/index.js +0 -47
  441. package/dist/arena/lenses/product.d.ts +0 -5
  442. package/dist/arena/lenses/product.js +0 -22
  443. package/dist/arena/model-presets.d.ts +0 -23
  444. package/dist/arena/model-presets.js +0 -44
  445. package/dist/arena/phases/adjudication.d.ts +0 -25
  446. package/dist/arena/phases/adjudication.js +0 -142
  447. package/dist/arena/phases/build-consensus.d.ts +0 -30
  448. package/dist/arena/phases/build-consensus.js +0 -85
  449. package/dist/arena/phases/claim-registry.d.ts +0 -26
  450. package/dist/arena/phases/claim-registry.js +0 -60
  451. package/dist/arena/phases/cross-review.d.ts +0 -47
  452. package/dist/arena/phases/cross-review.js +0 -224
  453. package/dist/arena/phases/debate-rounds.d.ts +0 -28
  454. package/dist/arena/phases/debate-rounds.js +0 -161
  455. package/dist/arena/phases/participant-research.d.ts +0 -39
  456. package/dist/arena/phases/participant-research.js +0 -316
  457. package/dist/arena/phases/planning-detail-expansion.d.ts +0 -39
  458. package/dist/arena/phases/planning-detail-expansion.js +0 -120
  459. package/dist/arena/planner.d.ts +0 -28
  460. package/dist/arena/planner.js +0 -312
  461. package/dist/arena/providers/docs.d.ts +0 -7
  462. package/dist/arena/providers/docs.js +0 -111
  463. package/dist/arena/providers/git.d.ts +0 -8
  464. package/dist/arena/providers/git.js +0 -174
  465. package/dist/arena/providers/index.d.ts +0 -32
  466. package/dist/arena/providers/index.js +0 -132
  467. package/dist/arena/providers/none.d.ts +0 -7
  468. package/dist/arena/providers/none.js +0 -11
  469. package/dist/arena/providers/repo.d.ts +0 -7
  470. package/dist/arena/providers/repo.js +0 -258
  471. package/dist/arena/render/session.d.ts +0 -17
  472. package/dist/arena/render/session.js +0 -190
  473. package/dist/arena/render/terminal.d.ts +0 -34
  474. package/dist/arena/render/terminal.js +0 -286
  475. package/dist/arena/strategies/discussion.d.ts +0 -25
  476. package/dist/arena/strategies/discussion.js +0 -143
  477. package/dist/arena/strategies/index.d.ts +0 -15
  478. package/dist/arena/strategies/index.js +0 -28
  479. package/dist/arena/strategies/language-wrapper.d.ts +0 -17
  480. package/dist/arena/strategies/language-wrapper.js +0 -102
  481. package/dist/arena/strategies/lens-wrapper.d.ts +0 -16
  482. package/dist/arena/strategies/lens-wrapper.js +0 -236
  483. package/dist/arena/strategies/planning.d.ts +0 -30
  484. package/dist/arena/strategies/planning.js +0 -225
  485. package/dist/arena/strategies/review.d.ts +0 -26
  486. package/dist/arena/strategies/review.js +0 -168
  487. package/dist/arena/strategies/utils.d.ts +0 -36
  488. package/dist/arena/strategies/utils.js +0 -603
  489. package/dist/arena/tools/selector.d.ts +0 -17
  490. package/dist/arena/tools/selector.js +0 -61
  491. package/dist/arena/transitions.d.ts +0 -53
  492. package/dist/arena/transitions.js +0 -97
  493. package/dist/arena/types.d.ts +0 -518
  494. package/dist/arena/types.js +0 -27
  495. package/dist/cc-orchestrator/agent-adapter.d.ts +0 -54
  496. package/dist/cc-orchestrator/agent-adapter.js +0 -143
  497. package/dist/cc-orchestrator/cc-capability.d.ts +0 -19
  498. package/dist/cc-orchestrator/cc-capability.js +0 -53
  499. package/dist/cc-orchestrator/codex-session-discovery.d.ts +0 -24
  500. package/dist/cc-orchestrator/codex-session-discovery.js +0 -191
  501. package/dist/cc-orchestrator/codex-session-history.d.ts +0 -38
  502. package/dist/cc-orchestrator/codex-session-history.js +0 -247
  503. package/dist/cc-orchestrator/external-agent-bindings.d.ts +0 -27
  504. package/dist/cc-orchestrator/external-agent-bindings.js +0 -150
  505. package/dist/cc-orchestrator/external-agent-changes.d.ts +0 -19
  506. package/dist/cc-orchestrator/external-agent-changes.js +0 -231
  507. package/dist/cc-orchestrator/external-agent-driver.d.ts +0 -19
  508. package/dist/cc-orchestrator/external-agent-driver.js +0 -284
  509. package/dist/cc-orchestrator/external-agent-session-store.d.ts +0 -23
  510. package/dist/cc-orchestrator/external-agent-session-store.js +0 -146
  511. package/dist/cc-orchestrator/index.d.ts +0 -8
  512. package/dist/cc-orchestrator/index.js +0 -8
  513. package/dist/cc-orchestrator/relevance-judge.d.ts +0 -15
  514. package/dist/cc-orchestrator/relevance-judge.js +0 -29
  515. package/dist/cc-orchestrator/session-discovery.d.ts +0 -46
  516. package/dist/cc-orchestrator/session-discovery.js +0 -125
  517. package/dist/cc-orchestrator/session-history.d.ts +0 -54
  518. package/dist/cc-orchestrator/session-history.js +0 -150
  519. package/dist/cron/cron-runtime.d.ts +0 -2
  520. package/dist/cron/cron-runtime.js +0 -2
  521. package/dist/cron/cron-store.d.ts +0 -2
  522. package/dist/cron/cron-store.js +0 -2
  523. package/dist/cron/scheduler.d.ts +0 -7
  524. package/dist/cron/scheduler.js +0 -7
  525. package/dist/engine/goal.js +0 -206
  526. package/dist/external-agents/config.d.ts +0 -2
  527. package/dist/external-agents/config.js +0 -15
  528. package/dist/external-agents/types.d.ts +0 -31
  529. package/dist/git/parse-log.d.ts +0 -13
  530. package/dist/git/parse-log.js +0 -21
  531. package/dist/git/utils.d.ts +0 -49
  532. package/dist/git/utils.js +0 -161
  533. package/dist/git/worktree/crud.d.ts +0 -72
  534. package/dist/git/worktree/crud.js +0 -235
  535. package/dist/git/worktree/diff.d.ts +0 -14
  536. package/dist/git/worktree/diff.js +0 -82
  537. package/dist/git/worktree/git-exec.d.ts +0 -7
  538. package/dist/git/worktree/git-exec.js +0 -87
  539. package/dist/git/worktree/index.d.ts +0 -5
  540. package/dist/git/worktree/index.js +0 -5
  541. package/dist/git/worktree/query.d.ts +0 -43
  542. package/dist/git/worktree/query.js +0 -128
  543. package/dist/git/worktree/slug.d.ts +0 -11
  544. package/dist/git/worktree/slug.js +0 -58
  545. package/dist/git/worktree.d.ts +0 -1
  546. package/dist/git/worktree.js +0 -5
  547. package/dist/lsp/client.d.ts +0 -41
  548. package/dist/lsp/client.js +0 -186
  549. package/dist/lsp/manager.d.ts +0 -40
  550. package/dist/lsp/manager.js +0 -155
  551. package/dist/lsp/root-path.d.ts +0 -9
  552. package/dist/lsp/root-path.js +0 -12
  553. package/dist/lsp/servers.d.ts +0 -16
  554. package/dist/lsp/servers.js +0 -60
  555. package/dist/prompt/sections/coding.md +0 -35
  556. package/dist/quota/credentials.d.ts +0 -3
  557. package/dist/quota/credentials.js +0 -80
  558. package/dist/quota/index.d.ts +0 -36
  559. package/dist/quota/index.js +0 -155
  560. package/dist/quota/types.d.ts +0 -48
  561. package/dist/quota/types.js +0 -13
  562. package/dist/review/review-prompt.d.ts +0 -28
  563. package/dist/review/review-prompt.js +0 -81
  564. package/dist/state.d.ts +0 -160
  565. package/dist/state.js +0 -267
  566. package/dist/tool-system/builtin/apply-patch/applier.d.ts +0 -26
  567. package/dist/tool-system/builtin/apply-patch/applier.js +0 -308
  568. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +0 -10
  569. package/dist/tool-system/builtin/apply-patch/backup-targets.js +0 -30
  570. package/dist/tool-system/builtin/apply-patch/index.d.ts +0 -20
  571. package/dist/tool-system/builtin/apply-patch/index.js +0 -105
  572. package/dist/tool-system/builtin/apply-patch/parser.d.ts +0 -17
  573. package/dist/tool-system/builtin/apply-patch/parser.js +0 -212
  574. package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +0 -18
  575. package/dist/tool-system/builtin/apply-patch/seek-sequence.js +0 -123
  576. package/dist/tool-system/builtin/apply-patch/types.d.ts +0 -49
  577. package/dist/tool-system/builtin/apply-patch/types.js +0 -13
  578. package/dist/tool-system/builtin/arena.d.ts +0 -31
  579. package/dist/tool-system/builtin/arena.js +0 -416
  580. package/dist/tool-system/builtin/brief.d.ts +0 -6
  581. package/dist/tool-system/builtin/brief.js +0 -41
  582. package/dist/tool-system/builtin/check-quota.d.ts +0 -15
  583. package/dist/tool-system/builtin/check-quota.js +0 -34
  584. package/dist/tool-system/builtin/drive-claude-code.d.ts +0 -51
  585. package/dist/tool-system/builtin/drive-claude-code.js +0 -698
  586. package/dist/tool-system/builtin/lsp.d.ts +0 -7
  587. package/dist/tool-system/builtin/lsp.js +0 -144
  588. package/dist/tool-system/builtin/notebook-edit.d.ts +0 -7
  589. package/dist/tool-system/builtin/notebook-edit.js +0 -127
  590. package/dist/tool-system/builtin/worktree.d.ts +0 -11
  591. package/dist/tool-system/builtin/worktree.js +0 -363
  592. /package/dist/{external-agents → links}/types.js +0 -0
  593. /package/dist/{engine/session-usage.d.ts → session/usage.d.ts} +0 -0
  594. /package/dist/{engine/session-usage.js → session/usage.js} +0 -0
@@ -4,9 +4,11 @@
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 { initialTurnState, newTurnId } from "./turn-state.js";
7
+ import { randomUUID } from "node:crypto";
8
+ import { buildAgentDirectionMessage } from "../tool-system/builtin/agent-notifications.js";
9
+ import { newTurnId } from "./turn-state.js";
8
10
  import { formatFriendlyError } from "./friendly-error.js";
9
- import { projectGoalJudgeToolResult, } from "../hooks/goal-stop-hook.js";
11
+ import { buildGoalJudgeRuntimeContext } from "./goal-judge-context.js";
10
12
  import { wrapHookMessages } from "../hooks/inject.js";
11
13
  import { ContextLimitError } from "../exceptions.js";
12
14
  import { logger } from "../logging/logger.js";
@@ -18,9 +20,9 @@ import { isAbortError } from "../llm/client-base.js";
18
20
  import { crossedReactiveThreshold } from "./reactive-threshold.js";
19
21
  import { COMPLETE_GOAL_TOOL_NAME } from "../tool-system/builtin/complete-goal.js";
20
22
  import { CANCEL_GOAL_TOOL_NAME } from "../tool-system/builtin/cancel-goal.js";
21
- import { redactSensitiveToolResultsInMessages, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
22
- import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "./session-usage.js";
23
- import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "./goal.js";
23
+ import { redactSensitiveToolResultsInMessages, SENSITIVE_TOOL_RESULT_PLACEHOLDER, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
24
+ import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "../session/usage.js";
25
+ import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, isSameGoalVersion, normalizeGoal, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "../goal/lifecycle.js";
24
26
  /**
25
27
  * 把一个 ToolResult 映射成发给 LLM 的 tool_result ContentBlock。
26
28
  * 有 contentBlocks(view_image 的图片块)就原样用作 content;否则
@@ -87,6 +89,57 @@ export class TurnLoop {
87
89
  * being re-emitted every turn while still within the approach threshold.
88
90
  */
89
91
  approachAnnounced = false;
92
+ /** Latest coalesced Goal edit/resume reminder, consumed at a safe point. */
93
+ pendingGoalControlMessage = null;
94
+ /** Pause/delete requested while a Goal run is in flight. The current
95
+ * model/tool step is allowed to settle, then the loop stops before starting
96
+ * another model request. */
97
+ goalControlStopRequested = false;
98
+ /** Auxiliary tool-summary calls started by this loop and revoked by Goal pause/delete. */
99
+ activeToolSummaryControllers = new Set();
100
+ /**
101
+ * Apply a user goal edit/pause/resume to the live loop. Passing undefined
102
+ * pauses goal-driving at the next step boundary; a goal value resumes it.
103
+ * Usage accumulated before an edit is retained, while a pause/resume starts
104
+ * a fresh run-scoped budget window (matching a later persisted resume).
105
+ */
106
+ updateGoal(goal, controlMessage, limits) {
107
+ const normalized = normalizeGoal(goal);
108
+ if (!normalized) {
109
+ this.config = { ...this.config, goal: undefined };
110
+ this.goalTracker = null;
111
+ this.pendingGoalControlMessage = null;
112
+ this.goalControlStopRequested = true;
113
+ for (const controller of this.activeToolSummaryControllers)
114
+ controller.abort();
115
+ this.activeToolSummaryControllers.clear();
116
+ this.stopBlockCount = 0;
117
+ this.approachAnnounced = false;
118
+ return;
119
+ }
120
+ const next = { ...normalized, goalId: normalized.goalId ?? randomUUID() };
121
+ const wasPaused = !this.config.goal;
122
+ this.config = {
123
+ ...this.config,
124
+ goal: next,
125
+ ...(typeof limits?.maxTurns === "number" && limits.maxTurns > 0
126
+ ? { maxTurns: Math.max(this.config.maxTurns, Math.floor(limits.maxTurns)) }
127
+ : {}),
128
+ ...(typeof limits?.maxStopBlocks === "number" && limits.maxStopBlocks > 0
129
+ ? { maxStopBlocks: Math.floor(limits.maxStopBlocks) }
130
+ : {}),
131
+ };
132
+ this.goalControlStopRequested = false;
133
+ this.pendingGoalControlMessage = controlMessage?.trim()
134
+ ? { goalId: next.goalId, revision: next.revision, text: controlMessage.trim() }
135
+ : null;
136
+ if (this.goalTracker && !wasPaused)
137
+ this.goalTracker.goal = next;
138
+ else
139
+ this.goalTracker = createGoalBudgetTracker(next, Date.now());
140
+ this.stopBlockCount = 0;
141
+ this.approachAnnounced = false;
142
+ }
90
143
  /**
91
144
  * Extend the in-flight run's limits (TODO 3.1 — 运行中续轮/加预算). Mutates
92
145
  * the maxTurns ceiling, the maxStopBlocks cap, and/or the live goal budgets;
@@ -163,6 +216,8 @@ export class TurnLoop {
163
216
  this.approachAnnounced = true;
164
217
  this.config.onStream?.({
165
218
  type: "goal_progress",
219
+ goalId: this.config.goal.goalId,
220
+ revision: this.config.goal.revision,
166
221
  status: "approaching_limit",
167
222
  round: this.stopBlockCount,
168
223
  turnsRemaining: prox.turnsRemaining,
@@ -177,6 +232,16 @@ export class TurnLoop {
177
232
  config) {
178
233
  this.deps = deps;
179
234
  this.config = config;
235
+ const normalizedGoal = normalizeGoal(this.config.goal);
236
+ if (normalizedGoal) {
237
+ this.config = {
238
+ ...this.config,
239
+ goal: { ...normalizedGoal, goalId: normalizedGoal.goalId ?? randomUUID() },
240
+ };
241
+ }
242
+ else if (this.config.goal !== undefined) {
243
+ this.config = { ...this.config, goal: undefined };
244
+ }
180
245
  for (const msg of this.config.freshImageMessages ?? []) {
181
246
  this.pendingImageMessages.add(msg);
182
247
  }
@@ -359,10 +424,17 @@ export class TurnLoop {
359
424
  promptTokensConfidence: overhead > 0 ? "medium" : "low",
360
425
  });
361
426
  }
362
- recordResponseUsage(usage) {
427
+ recordResponseUsage(usage, requestKind = "primary", recordCacheDiagnostics = true) {
363
428
  this.currentTurnUsage = addTokenUsage(this.currentTurnUsage, usage);
429
+ this.deps.onAgentUsage?.(usage);
364
430
  this.currentCumulativeUsage = this.deps.recordCumulativeUsage?.(usage);
365
- this.deps.recordCacheReadDiagnostics?.(usage);
431
+ if (recordCacheDiagnostics && this.deps.recordCacheReadDiagnostics) {
432
+ this.deps.recordCacheReadDiagnostics({
433
+ usage,
434
+ requestKind,
435
+ fingerprint: this.deps.model.getPromptPrefixFingerprint(this.deps.systemPrompt, this.deps.tools),
436
+ });
437
+ }
366
438
  }
367
439
  emitCtxFromUsage(usage, messages) {
368
440
  if (!this.config.onStream)
@@ -429,16 +501,18 @@ export class TurnLoop {
429
501
  */
430
502
  async run(initialMessages) {
431
503
  const result = await this.runUnredacted(initialMessages);
432
- return { ...result, messages: this.redactConsumedSensitiveToolResults(result.messages) };
504
+ return {
505
+ ...result,
506
+ ...(result.reason === "completed" && this.goalControlStopRequested
507
+ ? { completionKind: "goal_control_stop" }
508
+ : {}),
509
+ messages: this.redactConsumedSensitiveToolResults(result.messages),
510
+ };
433
511
  }
434
512
  async runUnredacted(initialMessages) {
435
513
  let messages = [...initialMessages];
436
514
  let finalText = "";
437
515
  const budgetTracker = createBudgetTracker();
438
- // Recent execution evidence for the built-in Goal judge. Values enter this
439
- // window only after irreversible sensitive-data removal, non-text omission,
440
- // and per-item truncation at the current-round ToolResult boundary.
441
- const goalToolResults = [];
442
516
  const sensitiveGoalResultTools = this.config.goal
443
517
  ? new Set(this.deps.tools.filter((tool) => tool.sensitiveResult).map((tool) => tool.name))
444
518
  : undefined;
@@ -449,7 +523,6 @@ export class TurnLoop {
449
523
  if (!this.goalTracker && this.config.goal) {
450
524
  this.goalTracker = createGoalBudgetTracker(this.config.goal, Date.now());
451
525
  }
452
- const goalTracker = this.goalTracker;
453
526
  // Fresh run: re-arm the approaching-limit announcement.
454
527
  this.approachAnnounced = false;
455
528
  // run() must never reject: the engine's post-run bookkeeping (saveState
@@ -461,8 +534,12 @@ export class TurnLoop {
461
534
  // hook emits, guards) and surfaces them as a model_error result.
462
535
  try {
463
536
  while (this.turnCount < this.config.maxTurns) {
464
- const preTurnGoalTermination = goalTracker
465
- ? goalBudgetTerminationReason(goalTracker, Date.now())
537
+ if (this.goalControlStopRequested) {
538
+ messages = this.redactConsumedSensitiveToolResults(messages);
539
+ return { text: finalText, reason: "completed", messages };
540
+ }
541
+ const preTurnGoalTermination = this.goalTracker
542
+ ? goalBudgetTerminationReason(this.goalTracker, Date.now())
466
543
  : undefined;
467
544
  if (preTurnGoalTermination) {
468
545
  this.config.onStream?.({
@@ -478,6 +555,7 @@ export class TurnLoop {
478
555
  reason: "goal_budget_exhausted",
479
556
  messages,
480
557
  goalTermination: preTurnGoalTermination,
558
+ goalTerminationRound: this.stopBlockCount,
481
559
  };
482
560
  }
483
561
  this.turnCount++;
@@ -507,8 +585,18 @@ export class TurnLoop {
507
585
  // they join THIS step's request — no abort, no lost in-flight work. Same
508
586
  // loop-top user-push pattern as turnStartInjection / turn-limit warnings
509
587
  // below. Push to transcript too so they persist + survive resume.
588
+ // Bind self-reported Goal terminal tools to the revision that was
589
+ // actually visible when this model step began. An edit/pause/delete can
590
+ // arrive while context management, the model request, or its tool batch
591
+ // is in flight; a late complete_goal/cancel_goal from that old prompt
592
+ // must never terminate the newer revision.
593
+ const requestedGoalVersion = this.config.goal ? { ...this.config.goal } : undefined;
510
594
  await this.consumeQueuedSteer(messages, "normal_step");
511
- const state = initialTurnState(this.turnCount);
595
+ if (this.goalControlStopRequested) {
596
+ messages = this.redactConsumedSensitiveToolResults(messages);
597
+ return { text: finalText, reason: "completed", messages };
598
+ }
599
+ this.deps.onAgentControlState?.("model");
512
600
  // Per-turn correlation ID. Every log written through `tlog` (or any
513
601
  // child derived from it) is stamped with `turn` + `turnId`, so
514
602
  // `jq 'select(.turnId == "...")'` reconstructs one turn's timeline.
@@ -523,11 +611,6 @@ export class TurnLoop {
523
611
  tlog.info("turn.start", { cat: "turn", messageCount: messages.length });
524
612
  // Tag downstream tool-exec / permission lines with this turn's IDs.
525
613
  this.deps.toolExecutor.setLogger(tlog);
526
- this.config.onStream?.({
527
- type: "stream_request_start",
528
- turnNumber: this.turnCount,
529
- messageId: assistantMessageId,
530
- });
531
614
  const turnStartHook = await this.emitHook("on_turn_start", {
532
615
  turnNumber: this.turnCount,
533
616
  });
@@ -535,6 +618,10 @@ export class TurnLoop {
535
618
  if (turnStartInjection) {
536
619
  messages.push(turnStartInjection);
537
620
  }
621
+ if (this.goalControlStopRequested) {
622
+ messages = this.redactConsumedSensitiveToolResults(messages);
623
+ return { text: finalText, reason: "completed", messages };
624
+ }
538
625
  // Approaching max turns: inject a warning so the model can wrap up.
539
626
  // (Model-facing only — the user-facing "再续" marker is handled by
540
627
  // maybeAnnounceApproachingLimit below, which also watches the stop-block
@@ -587,6 +674,10 @@ export class TurnLoop {
587
674
  this.markStopped();
588
675
  return { text: finalText, reason: "aborted_streaming", messages };
589
676
  }
677
+ if (this.goalControlStopRequested) {
678
+ messages = this.redactConsumedSensitiveToolResults(messages);
679
+ return { text: finalText, reason: "completed", messages };
680
+ }
590
681
  }
591
682
  // No pre-llm ctx emit here: the messages-only estimate would be ~16k
592
683
  // smaller than the real prompt (system + tools not included), making
@@ -614,8 +705,17 @@ export class TurnLoop {
614
705
  messages.push(compactInjection);
615
706
  }
616
707
  }
708
+ if (this.goalControlStopRequested) {
709
+ messages = this.redactConsumedSensitiveToolResults(messages);
710
+ return { text: finalText, reason: "completed", messages };
711
+ }
617
712
  messages = this.appendVolatileContextMessages(messages);
618
713
  // Model call (with streaming fallback and max_output_tokens continuation)
714
+ this.config.onStream?.({
715
+ type: "stream_request_start",
716
+ turnNumber: this.turnCount,
717
+ messageId: assistantMessageId,
718
+ });
619
719
  // Track tool IDs streamed during this turn to avoid duplicate UI events
620
720
  this.streamedToolIds.clear();
621
721
  // Tool queue is created before the call, but enqueue happens only after
@@ -627,10 +727,18 @@ export class TurnLoop {
627
727
  }
628
728
  catch (err) {
629
729
  if (err instanceof ContextLimitError) {
730
+ if (this.goalControlStopRequested) {
731
+ messages = this.redactConsumedSensitiveToolResults(messages);
732
+ return { text: finalText, reason: "completed", messages };
733
+ }
630
734
  // Progressive recovery: drop oldest API rounds, up to 3 retries
631
735
  const { dropOldestRounds } = await import("../context/compaction.js");
632
736
  let recovered = false;
633
737
  for (let retry = 1; retry <= 3; retry++) {
738
+ if (this.goalControlStopRequested) {
739
+ messages = this.redactConsumedSensitiveToolResults(messages);
740
+ return { text: finalText, reason: "completed", messages };
741
+ }
634
742
  tlog.warn("turn.ptl_recovery", { cat: "turn", retry, roundsToDrop: retry });
635
743
  messages = dropOldestRounds(messages, retry);
636
744
  try {
@@ -685,12 +793,12 @@ export class TurnLoop {
685
793
  }
686
794
  // Charge every provider response to the run-scoped Goal budget before
687
795
  // any truncation path can retry or continue the outer turn loop.
688
- if (goalTracker && response.usage) {
796
+ if (this.goalTracker && response.usage) {
689
797
  const used = (response.usage.promptTokens ?? 0) + (response.usage.completionTokens ?? 0);
690
- recordGoalUsage(goalTracker, used);
798
+ recordGoalUsage(this.goalTracker, used);
691
799
  }
692
- let budgetTermination = goalTracker
693
- ? goalBudgetTerminationReason(goalTracker, Date.now())
800
+ let budgetTermination = this.goalTracker
801
+ ? goalBudgetTerminationReason(this.goalTracker, Date.now())
694
802
  : undefined;
695
803
  // Feed actual token usage back to the context manager so subsequent
696
804
  // compaction decisions use hybrid (actual + delta) estimation rather than
@@ -748,10 +856,10 @@ export class TurnLoop {
748
856
  // Don't fire another continuation call if the user cancelled — without
749
857
  // this an abort during a truncated response could still issue up to 3
750
858
  // more model calls, emitting text after Stop.
751
- if (this.config.signal?.aborted)
859
+ if (this.config.signal?.aborted || this.goalControlStopRequested)
752
860
  break;
753
- budgetTermination = goalTracker
754
- ? goalBudgetTerminationReason(goalTracker, Date.now())
861
+ budgetTermination = this.goalTracker
862
+ ? goalBudgetTerminationReason(this.goalTracker, Date.now())
755
863
  : undefined;
756
864
  if (budgetTermination)
757
865
  break;
@@ -772,16 +880,16 @@ export class TurnLoop {
772
880
  this.stopBlockCount = 0;
773
881
  }
774
882
  if (contResponse.usage?.promptTokens !== undefined) {
775
- this.recordResponseUsage(contResponse.usage);
883
+ this.recordResponseUsage(contResponse.usage, "continuation");
776
884
  continuedResponse = true;
777
885
  }
778
- if (goalTracker && contResponse.usage) {
886
+ if (this.goalTracker && contResponse.usage) {
779
887
  const used = (contResponse.usage.promptTokens ?? 0) +
780
888
  (contResponse.usage.completionTokens ?? 0);
781
- recordGoalUsage(goalTracker, used);
889
+ recordGoalUsage(this.goalTracker, used);
782
890
  }
783
- budgetTermination = goalTracker
784
- ? goalBudgetTerminationReason(goalTracker, Date.now())
891
+ budgetTermination = this.goalTracker
892
+ ? goalBudgetTerminationReason(this.goalTracker, Date.now())
785
893
  : undefined;
786
894
  combinedText += contResponse.text;
787
895
  if (budgetTermination ||
@@ -821,13 +929,13 @@ export class TurnLoop {
821
929
  // is force-stopped regardless of what the model wants to do next (stop OR
822
930
  // continue with tool calls). This is the unattended-safety backstop, so
823
931
  // it sits BEFORE the "tool calls?" branch — both paths pass the gate.
824
- budgetTermination = goalTracker
825
- ? (budgetTermination ?? goalBudgetTerminationReason(goalTracker, Date.now()))
932
+ budgetTermination = this.goalTracker
933
+ ? (budgetTermination ?? goalBudgetTerminationReason(this.goalTracker, Date.now()))
826
934
  : undefined;
827
- if (goalTracker && budgetTermination) {
935
+ if (this.goalTracker && budgetTermination) {
828
936
  tlog.info("turn.goal_budget_exhausted", {
829
937
  cat: "goal",
830
- tokensUsed: goalTracker.tokensUsed,
938
+ tokensUsed: this.goalTracker.tokensUsed,
831
939
  tokenBudget: this.config.goal?.tokenBudget,
832
940
  timeBudgetMs: this.config.goal?.timeBudgetMs,
833
941
  });
@@ -845,6 +953,7 @@ export class TurnLoop {
845
953
  reason: "goal_budget_exhausted",
846
954
  messages,
847
955
  goalTermination: budgetTermination,
956
+ goalTerminationRound: this.stopBlockCount,
848
957
  };
849
958
  }
850
959
  // Post-check: tool calls?
@@ -864,28 +973,31 @@ export class TurnLoop {
864
973
  if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
865
974
  continue;
866
975
  }
976
+ // A steer accepted while an async tool was launching must be
977
+ // answered before parking the run. Keep the tool's yield request
978
+ // pending across that extra model round, then park once the model
979
+ // has replied and there is still no background result to consume.
980
+ if (this.deps.consumeToolRunYield?.() === "background_notification") {
981
+ tlog.info("turn.background_notification_wait_after_steer", { cat: "turn" });
982
+ messages = this.redactConsumedSensitiveToolResults(messages);
983
+ return {
984
+ text: finalText,
985
+ reason: "completed",
986
+ messages,
987
+ completionKind: "background_wait",
988
+ };
989
+ }
867
990
  // on_stop seam: the model wants to stop. Give handlers (Goal mode)
868
991
  // a chance to BLOCK termination and keep the agent working. A
869
992
  // handler returning continueSession=true injects its messages and
870
993
  // we run another turn instead of returning. Bounded by
871
994
  // maxStopBlocks (consecutive) and the outer maxTurns ceiling.
872
995
  const maxStopBlocks = this.config.maxStopBlocks ?? GOAL_DEFAULT_MAX_STOP_BLOCKS;
873
- if (goalTracker) {
874
- this.deps.updateGoalJudgeContext?.({
875
- toolResults: goalToolResults.map((item) => ({
876
- ...item,
877
- })),
878
- progress: {
879
- turnCount: this.turnCount,
880
- stopRound: this.stopBlockCount + 1,
881
- elapsedMs: Math.max(0, Date.now() - goalTracker.startedAtMs),
882
- tokensUsed: goalTracker.tokensUsed,
883
- tokenBudget: goalTracker.goal.tokenBudget,
884
- timeBudgetMs: goalTracker.goal.timeBudgetMs,
885
- maxTurns: this.config.maxTurns,
886
- maxStopBlocks,
887
- },
888
- });
996
+ if (this.goalTracker) {
997
+ const judgeMessages = this.stripVolatileContextMessages(messages);
998
+ this.deps.publishGoalJudgeContext?.(buildGoalJudgeRuntimeContext(judgeMessages, {
999
+ sensitiveToolResultRedactions: this.sensitiveToolResultRedactions,
1000
+ }));
889
1001
  }
890
1002
  const stopHook = await this.emitHook("on_stop", {
891
1003
  goal: this.config.goal,
@@ -900,16 +1012,16 @@ export class TurnLoop {
900
1012
  hookGoalTermination === "time_budget_exhausted"
901
1013
  ? hookGoalTermination
902
1014
  : undefined;
903
- const postJudgeBudgetTermination = goalTracker
904
- ? (hookBudgetTermination ?? goalBudgetTerminationReason(goalTracker, Date.now()))
1015
+ const postJudgeBudgetTermination = this.goalTracker
1016
+ ? (hookBudgetTermination ?? goalBudgetTerminationReason(this.goalTracker, Date.now()))
905
1017
  : undefined;
906
- if (goalTracker && postJudgeBudgetTermination) {
1018
+ if (this.goalTracker && postJudgeBudgetTermination) {
907
1019
  tlog.info("turn.goal_budget_exhausted_after_judge", {
908
1020
  cat: "goal",
909
1021
  reason: postJudgeBudgetTermination,
910
- tokensUsed: goalTracker.tokensUsed,
911
- tokenBudget: goalTracker.goal.tokenBudget,
912
- timeBudgetMs: goalTracker.goal.timeBudgetMs,
1022
+ tokensUsed: this.goalTracker.tokensUsed,
1023
+ tokenBudget: this.goalTracker.goal.tokenBudget,
1024
+ timeBudgetMs: this.goalTracker.goal.timeBudgetMs,
913
1025
  });
914
1026
  this.config.onStream?.({
915
1027
  type: "assistant_message",
@@ -924,9 +1036,10 @@ export class TurnLoop {
924
1036
  reason: "goal_budget_exhausted",
925
1037
  messages,
926
1038
  goalTermination: postJudgeBudgetTermination,
1039
+ goalTerminationRound: this.stopBlockCount,
927
1040
  };
928
1041
  }
929
- if (goalTracker && hookGoalTermination === "judge_prompt_too_large") {
1042
+ if (this.goalTracker && hookGoalTermination === "judge_prompt_too_large") {
930
1043
  tlog.warn("turn.goal_judge_prompt_too_large", { cat: "goal" });
931
1044
  this.config.onStream?.({
932
1045
  type: "assistant_message",
@@ -953,6 +1066,8 @@ export class TurnLoop {
953
1066
  // up with stopBlockCount so the UI can show "第 N 轮".
954
1067
  this.config.onStream?.({
955
1068
  type: "goal_progress",
1069
+ goalId: this.config.goal?.goalId,
1070
+ revision: this.config.goal?.revision,
956
1071
  status: "not_met",
957
1072
  round: this.stopBlockCount,
958
1073
  gaps: goalVerdict?.gaps || undefined,
@@ -991,11 +1106,6 @@ export class TurnLoop {
991
1106
  stopBlockCount: this.stopBlockCount,
992
1107
  maxStopBlocks,
993
1108
  });
994
- this.config.onStream?.({
995
- type: "goal_progress",
996
- status: "exhausted",
997
- round: this.stopBlockCount,
998
- });
999
1109
  goalTermination = "stop_blocks_exhausted";
1000
1110
  this.config.onStream?.({
1001
1111
  type: "assistant_message",
@@ -1011,18 +1121,28 @@ export class TurnLoop {
1011
1121
  // rounds = prior blocks + this accepted final round.
1012
1122
  this.config.onStream?.({
1013
1123
  type: "goal_progress",
1124
+ goalId: this.config.goal.goalId,
1125
+ revision: this.config.goal.revision,
1014
1126
  status: "met",
1015
1127
  round: this.stopBlockCount + 1,
1016
1128
  });
1017
1129
  }
1130
+ const goalTerminationRound = goalTermination ? this.stopBlockCount : undefined;
1018
1131
  this.stopBlockCount = 0;
1019
1132
  if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1020
1133
  continue;
1021
1134
  }
1022
1135
  messages = this.redactConsumedSensitiveToolResults(messages);
1023
- return { text: finalText, reason: "completed", messages, goalTermination };
1136
+ return {
1137
+ text: finalText,
1138
+ reason: "completed",
1139
+ messages,
1140
+ goalTermination,
1141
+ goalTerminationRound,
1142
+ };
1024
1143
  }
1025
1144
  // Tool execution phase
1145
+ this.deps.onAgentControlState?.("tool-batch");
1026
1146
  tlog.info("turn.tool_use", {
1027
1147
  cat: "turn",
1028
1148
  tools: response.toolCalls.map((t) => t.toolName),
@@ -1063,20 +1183,13 @@ export class TurnLoop {
1063
1183
  // Record results in transcript and stream
1064
1184
  const resultBlocks = [];
1065
1185
  for (const result of results) {
1066
- if (goalTracker) {
1067
- goalToolResults.push(projectGoalJudgeToolResult(result, this.turnCount, sensitiveGoalResultTools?.has(result.toolName) === true));
1068
- // Never evict the front of one legal batch: the default executor may
1069
- // return 25 results in a turn. Keep at least maxToolCallsPerTurn so
1070
- // the judge renderer can preserve metadata for the entire newest batch.
1071
- const residencyLimit = Math.max(20, this.config.maxToolCallsPerTurn);
1072
- if (goalToolResults.length > residencyLimit) {
1073
- goalToolResults.splice(0, goalToolResults.length - residencyLimit);
1074
- }
1075
- }
1076
1186
  resultBlocks.push(toolResultToBlock(result));
1077
1187
  const streamResult = toolResultForDisplay(result);
1078
- const transcriptResult = toolResultTranscriptText(result);
1079
- if (result.sensitive && transcriptResult !== undefined) {
1188
+ const sensitiveByMetadata = sensitiveGoalResultTools?.has(result.toolName) === true;
1189
+ const transcriptResult = sensitiveByMetadata
1190
+ ? SENSITIVE_TOOL_RESULT_PLACEHOLDER
1191
+ : toolResultTranscriptText(result);
1192
+ if ((result.sensitive || sensitiveByMetadata) && transcriptResult !== undefined) {
1080
1193
  this.sensitiveToolResultRedactions.set(result.id, transcriptResult);
1081
1194
  }
1082
1195
  this.deps.transcript.appendToolResult(result.id, result.toolName, transcriptResult, result.error, result.sensitive ? undefined : result.contentBlocks);
@@ -1086,13 +1199,15 @@ export class TurnLoop {
1086
1199
  // best-effort observability — a thrown onStream handler, a failed dynamic
1087
1200
  // import, or a rejecting summarize must never surface as an unhandled
1088
1201
  // rejection (Node can treat those as fatal). Swallow at the tail. (B-2)
1089
- if (this.config.onStream) {
1202
+ if (this.config.onStream && this.deps.model.summarize && !this.goalControlStopRequested) {
1203
+ const summaryController = new AbortController();
1204
+ this.activeToolSummaryControllers.add(summaryController);
1090
1205
  void import("./tool-summary.js")
1091
1206
  .then(({ generateToolUseSummary }) => {
1092
- if (!this.deps.model.summarize)
1207
+ if (this.goalControlStopRequested || summaryController.signal.aborted)
1093
1208
  return;
1094
- return generateToolUseSummary(toolCalls, toolResultsForDisplay(results), this.deps.model.summarize).then((summary) => {
1095
- if (summary) {
1209
+ return generateToolUseSummary(toolCalls, toolResultsForDisplay(results), this.deps.model.summarize, summaryController.signal).then((summary) => {
1210
+ if (summary && !summaryController.signal.aborted) {
1096
1211
  this.config.onStream?.({
1097
1212
  type: "tool_summary",
1098
1213
  summary,
@@ -1106,6 +1221,9 @@ export class TurnLoop {
1106
1221
  cat: "turn",
1107
1222
  error: err.message,
1108
1223
  });
1224
+ })
1225
+ .finally(() => {
1226
+ this.activeToolSummaryControllers.delete(summaryController);
1109
1227
  });
1110
1228
  }
1111
1229
  const toolResultMessage = { role: "user", content: resultBlocks };
@@ -1136,6 +1254,31 @@ export class TurnLoop {
1136
1254
  // Tool results just pushed; recompute ctx so the bar updates *before*
1137
1255
  // the next model round-trip — large tool outputs can move it sharply.
1138
1256
  this.emitCtxFromMessages(messages);
1257
+ // A trusted tool launched asynchronous work whose completion is routed
1258
+ // back into this Session. End this run at the tool boundary instead of
1259
+ // asking the model for another step with no new evidence; the queued
1260
+ // completion notification will wake the Session and continue normally.
1261
+ // This precedes complete_goal so a single batch cannot launch unfinished
1262
+ // background work and simultaneously claim the enclosing Goal is done.
1263
+ if (this.deps.peekToolRunYield?.() === "background_notification") {
1264
+ // Close the current model turn before a queued steer re-drives it;
1265
+ // interrupt-and-redrive swaps the turn signal at this boundary.
1266
+ this.finalizeModelTurn();
1267
+ if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1268
+ continue;
1269
+ }
1270
+ }
1271
+ if (this.deps.peekToolRunYield?.() === "background_notification" &&
1272
+ this.deps.consumeToolRunYield?.() === "background_notification") {
1273
+ tlog.info("turn.background_notification_wait", { cat: "turn" });
1274
+ messages = this.redactConsumedSensitiveToolResults(messages);
1275
+ return {
1276
+ text: finalText,
1277
+ reason: "completed",
1278
+ messages,
1279
+ completionKind: "background_wait",
1280
+ };
1281
+ }
1139
1282
  // Goal mode P0: explicit completion. If the model called complete_goal,
1140
1283
  // it has DECLARED the goal done — short-circuit to "completed" WITHOUT
1141
1284
  // running the judge hook. The tool's result is already in `messages`
@@ -1145,16 +1288,33 @@ export class TurnLoop {
1145
1288
  // the model just declared finished (the judge's onMet does this when IT
1146
1289
  // decides; a self-report must do the same, else the goal outlives its
1147
1290
  // completion and re-arms every follow-up turn).
1148
- if (goalTracker && toolCalls.some((tc) => tc.toolName === COMPLETE_GOAL_TOOL_NAME)) {
1149
- tlog.info("turn.goal_self_reported_complete", { cat: "goal" });
1150
- this.stopBlockCount = 0;
1151
- this.deps.clearPersistedGoal?.();
1152
- this.finalizeModelTurn();
1153
- if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1154
- continue;
1291
+ const completeGoalRequested = !!this.goalTracker &&
1292
+ isSameGoalVersion(requestedGoalVersion, this.config.goal) &&
1293
+ toolCalls.some((tc) => tc.toolName === COMPLETE_GOAL_TOOL_NAME);
1294
+ if (completeGoalRequested) {
1295
+ const persisted = this.deps.clearPersistedGoal?.("completed") !== false;
1296
+ if (persisted) {
1297
+ tlog.info("turn.goal_self_reported_complete", { cat: "goal" });
1298
+ this.config.onStream?.({
1299
+ type: "goal_progress",
1300
+ goalId: this.config.goal?.goalId,
1301
+ revision: this.config.goal?.revision,
1302
+ status: "met",
1303
+ round: this.stopBlockCount + 1,
1304
+ });
1305
+ this.stopBlockCount = 0;
1306
+ this.finalizeModelTurn();
1307
+ if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1308
+ continue;
1309
+ }
1310
+ messages = this.redactConsumedSensitiveToolResults(messages);
1311
+ return { text: finalText, reason: "completed", messages };
1155
1312
  }
1156
- messages = this.redactConsumedSensitiveToolResults(messages);
1157
- return { text: finalText, reason: "completed", messages };
1313
+ tlog.warn("turn.goal_self_reported_complete_persist_failed", { cat: "goal" });
1314
+ messages.push({
1315
+ role: "user",
1316
+ content: "<system-reminder>complete_goal 未能持久化完成状态;目标仍处于活动状态。请不要宣称已经结束,并在会话状态恢复可写后重试。</system-reminder>",
1317
+ });
1158
1318
  }
1159
1319
  // Goal mode: user-initiated cancellation. cancel_goal is the "strong
1160
1320
  // intent" escape hatch — honor it ONLY when confirm===true (the guard the
@@ -1162,15 +1322,35 @@ export class TurnLoop {
1162
1322
  // persisted goal so it never re-arms; an unconfirmed call is a no-op here
1163
1323
  // (the tool's own result string already told the model it was ignored).
1164
1324
  const confirmedCancel = toolCalls.some((tc) => tc.toolName === CANCEL_GOAL_TOOL_NAME && tc.args?.confirm === true);
1165
- if (goalTracker && confirmedCancel) {
1166
- tlog.info("turn.goal_user_cancelled", { cat: "goal" });
1167
- this.stopBlockCount = 0;
1168
- this.deps.clearPersistedGoal?.();
1169
- this.finalizeModelTurn();
1170
- if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1171
- continue;
1325
+ if (this.goalTracker &&
1326
+ isSameGoalVersion(requestedGoalVersion, this.config.goal) &&
1327
+ confirmedCancel &&
1328
+ !completeGoalRequested) {
1329
+ const persisted = this.deps.clearPersistedGoal?.("cancelled") !== false;
1330
+ if (persisted) {
1331
+ tlog.info("turn.goal_user_cancelled", { cat: "goal" });
1332
+ this.stopBlockCount = 0;
1333
+ this.config.onStream?.({
1334
+ type: "goal_cleared",
1335
+ goalId: this.config.goal?.goalId,
1336
+ revision: this.config.goal?.revision,
1337
+ });
1338
+ this.finalizeModelTurn();
1339
+ if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1340
+ continue;
1341
+ }
1342
+ return {
1343
+ text: finalText,
1344
+ reason: "completed",
1345
+ messages,
1346
+ completionKind: "goal_control_stop",
1347
+ };
1172
1348
  }
1173
- return { text: finalText, reason: "completed", messages };
1349
+ tlog.warn("turn.goal_user_cancelled_persist_failed", { cat: "goal" });
1350
+ messages.push({
1351
+ role: "user",
1352
+ content: "<system-reminder>cancel_goal 未能持久化删除状态;目标仍处于活动状态。请在会话状态恢复可写后重试。</system-reminder>",
1353
+ });
1174
1354
  }
1175
1355
  // Token budget check
1176
1356
  const totalOutputTokens = this.deps.model.getOutputTokens?.() ?? 0;
@@ -1192,7 +1372,12 @@ export class TurnLoop {
1192
1372
  continue;
1193
1373
  }
1194
1374
  messages = this.redactConsumedSensitiveToolResults(messages);
1195
- return { text: finalText, reason: "completed", messages };
1375
+ return {
1376
+ text: finalText,
1377
+ reason: "completed",
1378
+ messages,
1379
+ completionKind: "limit_stop",
1380
+ };
1196
1381
  }
1197
1382
  if (budgetDecision === "nudge") {
1198
1383
  messages.push({
@@ -1265,6 +1450,12 @@ export class TurnLoop {
1265
1450
  messages = this.redactConsumedSensitiveToolResults(messages);
1266
1451
  return { text: finalText, reason: "model_error", messages };
1267
1452
  }
1453
+ // Pause/delete may have landed while the final allowed tool batch was
1454
+ // draining. Do not cross that safe boundary into an extra summary request.
1455
+ if (this.goalControlStopRequested) {
1456
+ messages = this.redactConsumedSensitiveToolResults(messages);
1457
+ return { text: finalText, reason: "completed", messages };
1458
+ }
1268
1459
  // Max turns reached — do one final summarization call (no tools)
1269
1460
  logger.warn("turn.max_turns_reached", {
1270
1461
  cat: "turn",
@@ -1272,6 +1463,11 @@ export class TurnLoop {
1272
1463
  turnCount: this.turnCount,
1273
1464
  });
1274
1465
  await this.consumeQueuedSteer(messages, "finalize_backfill");
1466
+ if (this.goalControlStopRequested) {
1467
+ messages = this.redactConsumedSensitiveToolResults(messages);
1468
+ return { text: finalText, reason: "completed", messages };
1469
+ }
1470
+ this.deps.onAgentControlState?.("model");
1275
1471
  const hasPendingSensitiveToolResults = this.sensitiveToolResultRedactions.size > 0;
1276
1472
  messages = this.prepareMessagesForModel(messages);
1277
1473
  if (hasPendingSensitiveToolResults) {
@@ -1284,19 +1480,27 @@ export class TurnLoop {
1284
1480
  else {
1285
1481
  messages = this.deps.contextManager.manage(messages);
1286
1482
  }
1483
+ if (this.goalControlStopRequested) {
1484
+ messages = this.redactConsumedSensitiveToolResults(messages);
1485
+ return { text: finalText, reason: "completed", messages };
1486
+ }
1287
1487
  messages.push({
1288
1488
  role: "user",
1289
1489
  content: "<system-reminder>Turn limit reached. Provide a final summary of what you accomplished and what remains to be done. Do NOT call any tools.</system-reminder>",
1290
1490
  });
1291
1491
  this.emitCtxFromMessages(messages);
1292
1492
  try {
1493
+ if (this.goalControlStopRequested) {
1494
+ messages = this.redactConsumedSensitiveToolResults(messages);
1495
+ return { text: finalText, reason: "completed", messages };
1496
+ }
1293
1497
  const summaryResponse = await this.deps.model.call(this.deps.systemPrompt, this.prepareMessagesForModel(messages), [], // No tools available for summary turn
1294
1498
  this.config.onStream, this.config.signal, this.modelCallRecordingOptions());
1295
1499
  if (summaryResponse.usage?.promptTokens !== undefined) {
1296
- this.recordResponseUsage(summaryResponse.usage);
1500
+ this.recordResponseUsage(summaryResponse.usage, "primary", false);
1297
1501
  }
1298
- if (goalTracker && summaryResponse.usage) {
1299
- recordGoalUsage(goalTracker, (summaryResponse.usage.promptTokens ?? 0) + (summaryResponse.usage.completionTokens ?? 0));
1502
+ if (this.goalTracker && summaryResponse.usage) {
1503
+ recordGoalUsage(this.goalTracker, (summaryResponse.usage.promptTokens ?? 0) + (summaryResponse.usage.completionTokens ?? 0));
1300
1504
  }
1301
1505
  if (this.config.signal?.aborted) {
1302
1506
  this.markStopped();
@@ -1330,6 +1534,7 @@ export class TurnLoop {
1330
1534
  reason: "max_turns",
1331
1535
  messages,
1332
1536
  ...(this.config.goal ? { goalTermination: "max_turns_exhausted" } : {}),
1537
+ ...(this.config.goal ? { goalTerminationRound: this.stopBlockCount } : {}),
1333
1538
  };
1334
1539
  }
1335
1540
  /**
@@ -1388,6 +1593,14 @@ export class TurnLoop {
1388
1593
  // really just a cancel. The signal is the authoritative cancel source.
1389
1594
  if (isAbortError(err) || this.config.signal?.aborted)
1390
1595
  throw err;
1596
+ // Pause/delete arrived while the streaming attempt was in flight. A
1597
+ // non-streaming fallback would be a brand-new provider request after the
1598
+ // promised safe boundary, so revoke any partial stream and synthesize an
1599
+ // empty settled step instead.
1600
+ if (this.goalControlStopRequested) {
1601
+ this.config.onStream?.({ type: "tombstone", messageId: assistantMessageId });
1602
+ return { text: "", toolCalls: [], stopReason: "stop" };
1603
+ }
1391
1604
  // Sensitive tool results are model-facing exactly once. A streaming
1392
1605
  // fallback would re-send the same pending plaintext in a second request,
1393
1606
  // so fail the turn and let the unified exit redact returned history.
@@ -1413,8 +1626,25 @@ export class TurnLoop {
1413
1626
  return this.turnCount;
1414
1627
  }
1415
1628
  async consumeQueuedSteer(messages, source) {
1416
- const steered = this.deps.consumeSteer?.(source) ?? [];
1417
1629
  let consumed = false;
1630
+ const goalControl = this.pendingGoalControlMessage;
1631
+ this.pendingGoalControlMessage = null;
1632
+ if (goalControl &&
1633
+ this.config.goal?.paused !== true &&
1634
+ this.config.goal?.goalId === goalControl.goalId &&
1635
+ (this.config.goal?.revision ?? 1) === (goalControl.revision ?? 1)) {
1636
+ const content = `<system-reminder>\n${goalControl.text}\n</system-reminder>`;
1637
+ const message = { role: "user", content };
1638
+ messages.push(message);
1639
+ this.deps.transcript.appendMessage("user", content, {
1640
+ injected: true,
1641
+ source: "goal-control",
1642
+ });
1643
+ consumed = true;
1644
+ }
1645
+ const directionConsumed = await this.consumeQueuedAgentDirections(messages, source);
1646
+ const steered = this.deps.consumeSteer?.(source) ?? [];
1647
+ consumed ||= directionConsumed;
1418
1648
  for (let index = 0; index < steered.length; index++) {
1419
1649
  const item = steered[index];
1420
1650
  const { id, text, clientMessageId } = item;
@@ -1460,6 +1690,42 @@ export class TurnLoop {
1460
1690
  }
1461
1691
  return consumed;
1462
1692
  }
1693
+ async consumeQueuedAgentDirections(messages, source) {
1694
+ if (!this.deps.consumeAgentDirections)
1695
+ return false;
1696
+ this.deps.onAgentControlState?.("safe-point");
1697
+ const directions = this.deps.consumeAgentDirections();
1698
+ if (directions.length === 0)
1699
+ return false;
1700
+ directions.sort((left, right) => left.sequence - right.sequence);
1701
+ const content = buildAgentDirectionMessage(directions);
1702
+ const envelopeIds = directions.map((item) => item.id);
1703
+ const correlationIds = directions
1704
+ .map((item) => item.correlationId)
1705
+ .filter((value) => value !== undefined);
1706
+ const hook = await this.emitHook("agent_direction_submit", {
1707
+ source: "agent-direction",
1708
+ authority: "agent",
1709
+ prompt: directions.map((item) => item.payload.prompt).join("\n"),
1710
+ envelopeIds,
1711
+ correlationIds,
1712
+ safePoint: source,
1713
+ });
1714
+ const message = { role: "user", content };
1715
+ messages.push(message);
1716
+ this.deps.transcript.appendMessage("user", content, {
1717
+ injected: true,
1718
+ authority: "agent",
1719
+ source: "agent-direction",
1720
+ envelopeIds,
1721
+ correlationIds,
1722
+ });
1723
+ this.deps.onAgentDirectionsDelivered?.(envelopeIds);
1724
+ const hookInjection = wrapHookMessages(hook.messages);
1725
+ if (hookInjection)
1726
+ messages.push(hookInjection);
1727
+ return true;
1728
+ }
1463
1729
  /**
1464
1730
  * Generate synthetic error tool_results for any dangling tool_use blocks
1465
1731
  * that never received results (e.g. because the API call failed).