@orion-agents/orion-code 0.1.1 → 0.1.3

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 (643) hide show
  1. package/README.md +165 -68
  2. package/README.zh-CN.md +252 -212
  3. package/dist/cli.js +13 -17
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/index.d.ts.map +1 -1
  6. package/dist/commands/index.js +381 -168
  7. package/dist/commands/index.js.map +1 -1
  8. package/dist/commands/target-command.d.ts.map +1 -1
  9. package/dist/commands/target-command.js +42 -8
  10. package/dist/commands/target-command.js.map +1 -1
  11. package/dist/commands/types.d.ts +3 -1
  12. package/dist/commands/types.d.ts.map +1 -1
  13. package/dist/commands/types.js.map +1 -1
  14. package/dist/core/agent.d.ts.map +1 -1
  15. package/dist/core/agent.js +2 -2
  16. package/dist/core/agent.js.map +1 -1
  17. package/dist/framework/external-assertion.d.ts +65 -0
  18. package/dist/framework/external-assertion.d.ts.map +1 -0
  19. package/dist/framework/external-assertion.js +571 -0
  20. package/dist/framework/external-assertion.js.map +1 -0
  21. package/dist/framework/index.d.ts +6 -4
  22. package/dist/framework/index.d.ts.map +1 -1
  23. package/dist/framework/index.js +4 -1
  24. package/dist/framework/index.js.map +1 -1
  25. package/dist/framework/query.d.ts +18 -1
  26. package/dist/framework/query.d.ts.map +1 -1
  27. package/dist/framework/query.js +43 -23
  28. package/dist/framework/query.js.map +1 -1
  29. package/dist/framework/tool-scheduler.d.ts +63 -1
  30. package/dist/framework/tool-scheduler.d.ts.map +1 -1
  31. package/dist/framework/tool-scheduler.js +183 -51
  32. package/dist/framework/tool-scheduler.js.map +1 -1
  33. package/dist/framework/tool-serializer.d.ts.map +1 -1
  34. package/dist/framework/tool-serializer.js +20 -4
  35. package/dist/framework/tool-serializer.js.map +1 -1
  36. package/dist/framework/tool.d.ts +17 -5
  37. package/dist/framework/tool.d.ts.map +1 -1
  38. package/dist/framework/tool.js +2 -0
  39. package/dist/framework/tool.js.map +1 -1
  40. package/dist/harness/safety.d.ts.map +1 -1
  41. package/dist/harness/safety.js +19 -3
  42. package/dist/harness/safety.js.map +1 -1
  43. package/dist/init.d.ts +1 -1
  44. package/dist/init.d.ts.map +1 -1
  45. package/dist/init.js +115 -34
  46. package/dist/init.js.map +1 -1
  47. package/dist/ink-ui/screens/ReplScreen.d.ts +2 -1
  48. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  49. package/dist/ink-ui/screens/ReplScreen.js +65 -0
  50. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  51. package/dist/memory/storage.d.ts.map +1 -1
  52. package/dist/memory/storage.js +1 -0
  53. package/dist/memory/storage.js.map +1 -1
  54. package/dist/memory/store.d.ts.map +1 -1
  55. package/dist/memory/store.js +11 -5
  56. package/dist/memory/store.js.map +1 -1
  57. package/dist/print-ui/launch.d.ts +6 -1
  58. package/dist/print-ui/launch.d.ts.map +1 -1
  59. package/dist/print-ui/launch.js +109 -9
  60. package/dist/print-ui/launch.js.map +1 -1
  61. package/dist/product/identity.d.ts +2 -2
  62. package/dist/product/identity.d.ts.map +1 -1
  63. package/dist/product/identity.js +2 -2
  64. package/dist/product/identity.js.map +1 -1
  65. package/dist/product/index.d.ts +2 -1
  66. package/dist/product/index.d.ts.map +1 -1
  67. package/dist/product/index.js +5 -1
  68. package/dist/product/index.js.map +1 -1
  69. package/dist/product/version.d.ts +12 -0
  70. package/dist/product/version.d.ts.map +1 -0
  71. package/dist/product/version.js +31 -0
  72. package/dist/product/version.js.map +1 -0
  73. package/dist/runtime/agent-runtime-controller.d.ts +35 -0
  74. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  75. package/dist/runtime/agent-runtime-controller.js +975 -149
  76. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  77. package/dist/runtime/agent-runtime-protocol.d.ts +20 -3
  78. package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
  79. package/dist/runtime/agent-runtime-protocol.js +12 -0
  80. package/dist/runtime/agent-runtime-protocol.js.map +1 -1
  81. package/dist/runtime/chat-controller.d.ts +12 -0
  82. package/dist/runtime/chat-controller.d.ts.map +1 -1
  83. package/dist/runtime/chat-controller.js +213 -19
  84. package/dist/runtime/chat-controller.js.map +1 -1
  85. package/dist/runtime/goals/accounting.d.ts +28 -0
  86. package/dist/runtime/goals/accounting.d.ts.map +1 -1
  87. package/dist/runtime/goals/accounting.js +69 -0
  88. package/dist/runtime/goals/accounting.js.map +1 -1
  89. package/dist/runtime/goals/completion-audit.d.ts +13 -8
  90. package/dist/runtime/goals/completion-audit.d.ts.map +1 -1
  91. package/dist/runtime/goals/completion-audit.js +598 -16
  92. package/dist/runtime/goals/completion-audit.js.map +1 -1
  93. package/dist/runtime/goals/coordinator.d.ts +44 -5
  94. package/dist/runtime/goals/coordinator.d.ts.map +1 -1
  95. package/dist/runtime/goals/coordinator.js +1124 -58
  96. package/dist/runtime/goals/coordinator.js.map +1 -1
  97. package/dist/runtime/goals/evidence.d.ts +37 -0
  98. package/dist/runtime/goals/evidence.d.ts.map +1 -0
  99. package/dist/runtime/goals/evidence.js +300 -0
  100. package/dist/runtime/goals/evidence.js.map +1 -0
  101. package/dist/runtime/goals/presentation.d.ts +4 -0
  102. package/dist/runtime/goals/presentation.d.ts.map +1 -0
  103. package/dist/runtime/goals/presentation.js +50 -0
  104. package/dist/runtime/goals/presentation.js.map +1 -0
  105. package/dist/runtime/goals/prompt.d.ts.map +1 -1
  106. package/dist/runtime/goals/prompt.js +38 -8
  107. package/dist/runtime/goals/prompt.js.map +1 -1
  108. package/dist/runtime/goals/tools.d.ts +18 -1
  109. package/dist/runtime/goals/tools.d.ts.map +1 -1
  110. package/dist/runtime/goals/tools.js +310 -30
  111. package/dist/runtime/goals/tools.js.map +1 -1
  112. package/dist/runtime/goals/types.d.ts +247 -7
  113. package/dist/runtime/goals/types.d.ts.map +1 -1
  114. package/dist/runtime/goals/types.js +8 -2
  115. package/dist/runtime/goals/types.js.map +1 -1
  116. package/dist/runtime/subagents/child-executor-guard.d.ts +12 -4
  117. package/dist/runtime/subagents/child-executor-guard.d.ts.map +1 -1
  118. package/dist/runtime/subagents/child-executor-guard.js +397 -61
  119. package/dist/runtime/subagents/child-executor-guard.js.map +1 -1
  120. package/dist/runtime/subagents/presets.d.ts +4 -5
  121. package/dist/runtime/subagents/presets.d.ts.map +1 -1
  122. package/dist/runtime/subagents/presets.js +18 -33
  123. package/dist/runtime/subagents/presets.js.map +1 -1
  124. package/dist/runtime/subagents/production.d.ts +3 -1
  125. package/dist/runtime/subagents/production.d.ts.map +1 -1
  126. package/dist/runtime/subagents/production.js +37 -7
  127. package/dist/runtime/subagents/production.js.map +1 -1
  128. package/dist/runtime/subagents/runner.d.ts.map +1 -1
  129. package/dist/runtime/subagents/runner.js +24 -3
  130. package/dist/runtime/subagents/runner.js.map +1 -1
  131. package/dist/runtime/subagents/runtime-integration.d.ts +3 -0
  132. package/dist/runtime/subagents/runtime-integration.d.ts.map +1 -1
  133. package/dist/runtime/subagents/runtime-integration.js +18 -7
  134. package/dist/runtime/subagents/runtime-integration.js.map +1 -1
  135. package/dist/runtime/subagents/supervisor.d.ts +4 -4
  136. package/dist/runtime/subagents/supervisor.d.ts.map +1 -1
  137. package/dist/runtime/subagents/supervisor.js +39 -19
  138. package/dist/runtime/subagents/supervisor.js.map +1 -1
  139. package/dist/runtime/subagents/types.d.ts +7 -0
  140. package/dist/runtime/subagents/types.d.ts.map +1 -1
  141. package/dist/runtime/subagents/types.js +12 -1
  142. package/dist/runtime/subagents/types.js.map +1 -1
  143. package/dist/runtime/turn-controller.d.ts.map +1 -1
  144. package/dist/runtime/turn-controller.js +3 -1
  145. package/dist/runtime/turn-controller.js.map +1 -1
  146. package/dist/runtime/ui-events.d.ts +23 -0
  147. package/dist/runtime/ui-events.d.ts.map +1 -1
  148. package/dist/runtime/ui-events.js +5 -5
  149. package/dist/runtime/ui-events.js.map +1 -1
  150. package/dist/services/config.d.ts +1 -1
  151. package/dist/services/config.d.ts.map +1 -1
  152. package/dist/services/config.js +55 -49
  153. package/dist/services/config.js.map +1 -1
  154. package/dist/services/doctor.d.ts.map +1 -1
  155. package/dist/services/doctor.js +163 -17
  156. package/dist/services/doctor.js.map +1 -1
  157. package/dist/services/global-config.d.ts +31 -0
  158. package/dist/services/global-config.d.ts.map +1 -1
  159. package/dist/services/global-config.js.map +1 -1
  160. package/dist/services/goal-storage.d.ts +9 -4
  161. package/dist/services/goal-storage.d.ts.map +1 -1
  162. package/dist/services/goal-storage.js +1113 -75
  163. package/dist/services/goal-storage.js.map +1 -1
  164. package/dist/services/llm.d.ts +19 -0
  165. package/dist/services/llm.d.ts.map +1 -1
  166. package/dist/services/llm.js +153 -20
  167. package/dist/services/llm.js.map +1 -1
  168. package/dist/services/model-catalog.d.ts +2 -0
  169. package/dist/services/model-catalog.d.ts.map +1 -1
  170. package/dist/services/model-catalog.js +25 -15
  171. package/dist/services/model-catalog.js.map +1 -1
  172. package/dist/services/model-context.d.ts.map +1 -1
  173. package/dist/services/model-context.js +36 -0
  174. package/dist/services/model-context.js.map +1 -1
  175. package/dist/services/model-registry.d.ts +1 -1
  176. package/dist/services/model-registry.d.ts.map +1 -1
  177. package/dist/services/model-registry.js +101 -19
  178. package/dist/services/model-registry.js.map +1 -1
  179. package/dist/services/provider-resilience/coordinator.d.ts +2 -1
  180. package/dist/services/provider-resilience/coordinator.d.ts.map +1 -1
  181. package/dist/services/provider-resilience/coordinator.js +7 -6
  182. package/dist/services/provider-resilience/coordinator.js.map +1 -1
  183. package/dist/services/provider-resilience/error-classifier.d.ts.map +1 -1
  184. package/dist/services/provider-resilience/error-classifier.js +67 -17
  185. package/dist/services/provider-resilience/error-classifier.js.map +1 -1
  186. package/dist/services/provider-resilience/types.d.ts +1 -1
  187. package/dist/services/provider-resilience/types.d.ts.map +1 -1
  188. package/dist/services/provider-resilience/types.js.map +1 -1
  189. package/dist/services/redaction.d.ts.map +1 -1
  190. package/dist/services/redaction.js +9 -1
  191. package/dist/services/redaction.js.map +1 -1
  192. package/dist/services/session-storage.d.ts +12 -1
  193. package/dist/services/session-storage.d.ts.map +1 -1
  194. package/dist/services/session-storage.js +23 -7
  195. package/dist/services/session-storage.js.map +1 -1
  196. package/dist/services/storage-maintenance.d.ts +73 -3
  197. package/dist/services/storage-maintenance.d.ts.map +1 -1
  198. package/dist/services/storage-maintenance.js +771 -104
  199. package/dist/services/storage-maintenance.js.map +1 -1
  200. package/dist/services/task-manager.d.ts.map +1 -1
  201. package/dist/services/task-manager.js +2 -2
  202. package/dist/services/task-manager.js.map +1 -1
  203. package/dist/services/tool-allowlist.d.ts +100 -0
  204. package/dist/services/tool-allowlist.d.ts.map +1 -0
  205. package/dist/services/tool-allowlist.js +250 -0
  206. package/dist/services/tool-allowlist.js.map +1 -0
  207. package/dist/services/web-search-adapters.d.ts.map +1 -1
  208. package/dist/services/web-search-adapters.js +89 -49
  209. package/dist/services/web-search-adapters.js.map +1 -1
  210. package/dist/services/web-search-mcp.d.ts.map +1 -1
  211. package/dist/services/web-search-mcp.js +34 -37
  212. package/dist/services/web-search-mcp.js.map +1 -1
  213. package/dist/services/workspace-state.d.ts +4 -0
  214. package/dist/services/workspace-state.d.ts.map +1 -1
  215. package/dist/services/workspace-state.js +30 -9
  216. package/dist/services/workspace-state.js.map +1 -1
  217. package/dist/terminal-ui/launch.d.ts +24 -2
  218. package/dist/terminal-ui/launch.d.ts.map +1 -1
  219. package/dist/terminal-ui/launch.js +166 -8
  220. package/dist/terminal-ui/launch.js.map +1 -1
  221. package/dist/tools/git.d.ts +4 -0
  222. package/dist/tools/git.d.ts.map +1 -1
  223. package/dist/tools/git.js +585 -30
  224. package/dist/tools/git.js.map +1 -1
  225. package/dist/tools/index.d.ts.map +1 -1
  226. package/dist/tools/index.js +79 -11
  227. package/dist/tools/index.js.map +1 -1
  228. package/dist/tools/mcp.d.ts.map +1 -1
  229. package/dist/tools/mcp.js +20 -12
  230. package/dist/tools/mcp.js.map +1 -1
  231. package/dist/tools/sandbox.d.ts +139 -0
  232. package/dist/tools/sandbox.d.ts.map +1 -0
  233. package/dist/tools/sandbox.js +485 -0
  234. package/dist/tools/sandbox.js.map +1 -0
  235. package/dist/tools/web.d.ts.map +1 -1
  236. package/dist/tools/web.js +149 -54
  237. package/dist/tools/web.js.map +1 -1
  238. package/dist/tui-ui/launch.d.ts.map +1 -1
  239. package/dist/tui-ui/launch.js +46 -8
  240. package/dist/tui-ui/launch.js.map +1 -1
  241. package/dist/tui-ui/layout.d.ts.map +1 -1
  242. package/dist/tui-ui/layout.js +98 -23
  243. package/dist/tui-ui/layout.js.map +1 -1
  244. package/dist/tui-ui/pickers.d.ts.map +1 -1
  245. package/dist/tui-ui/pickers.js +4 -3
  246. package/dist/tui-ui/pickers.js.map +1 -1
  247. package/dist/tui-ui/runner.d.ts +7 -0
  248. package/dist/tui-ui/runner.d.ts.map +1 -1
  249. package/dist/tui-ui/runner.js +95 -1
  250. package/dist/tui-ui/runner.js.map +1 -1
  251. package/dist/tui-ui/state.d.ts +25 -1
  252. package/dist/tui-ui/state.d.ts.map +1 -1
  253. package/dist/tui-ui/state.js +95 -37
  254. package/dist/tui-ui/state.js.map +1 -1
  255. package/dist/tui-ui/terminal-image.d.ts +3 -13
  256. package/dist/tui-ui/terminal-image.d.ts.map +1 -1
  257. package/dist/tui-ui/terminal-image.js +5 -160
  258. package/dist/tui-ui/terminal-image.js.map +1 -1
  259. package/dist/utils/mask.d.ts +28 -0
  260. package/dist/utils/mask.d.ts.map +1 -0
  261. package/dist/utils/mask.js +54 -0
  262. package/dist/utils/mask.js.map +1 -0
  263. package/package.json +14 -9
  264. package/dist/core/state.d.ts +0 -94
  265. package/dist/core/state.d.ts.map +0 -1
  266. package/dist/core/state.js +0 -178
  267. package/dist/core/state.js.map +0 -1
  268. package/dist/ink/bidi.d.ts +0 -16
  269. package/dist/ink/bidi.d.ts.map +0 -1
  270. package/dist/ink/bidi.js +0 -124
  271. package/dist/ink/bidi.js.map +0 -1
  272. package/dist/ink/clearTerminal.d.ts +0 -14
  273. package/dist/ink/clearTerminal.d.ts.map +0 -1
  274. package/dist/ink/clearTerminal.js +0 -63
  275. package/dist/ink/clearTerminal.js.map +0 -1
  276. package/dist/ink/colorize.d.ts +0 -17
  277. package/dist/ink/colorize.d.ts.map +0 -1
  278. package/dist/ink/colorize.js +0 -208
  279. package/dist/ink/colorize.js.map +0 -1
  280. package/dist/ink/components/App.d.ts +0 -73
  281. package/dist/ink/components/App.d.ts.map +0 -1
  282. package/dist/ink/components/App.js +0 -646
  283. package/dist/ink/components/App.js.map +0 -1
  284. package/dist/ink/components/AppContext.d.ts +0 -12
  285. package/dist/ink/components/AppContext.d.ts.map +0 -1
  286. package/dist/ink/components/AppContext.js +0 -14
  287. package/dist/ink/components/AppContext.js.map +0 -1
  288. package/dist/ink/components/Box.d.ts +0 -44
  289. package/dist/ink/components/Box.d.ts.map +0 -1
  290. package/dist/ink/components/Box.js +0 -191
  291. package/dist/ink/components/Box.js.map +0 -1
  292. package/dist/ink/components/ClockContext.d.ts +0 -9
  293. package/dist/ink/components/ClockContext.d.ts.map +0 -1
  294. package/dist/ink/components/ClockContext.js +0 -146
  295. package/dist/ink/components/ClockContext.js.map +0 -1
  296. package/dist/ink/components/CursorDeclarationContext.d.ts +0 -23
  297. package/dist/ink/components/CursorDeclarationContext.d.ts.map +0 -1
  298. package/dist/ink/components/CursorDeclarationContext.js +0 -6
  299. package/dist/ink/components/CursorDeclarationContext.js.map +0 -1
  300. package/dist/ink/components/ErrorOverview.d.ts +0 -6
  301. package/dist/ink/components/ErrorOverview.d.ts.map +0 -1
  302. package/dist/ink/components/ErrorOverview.js +0 -27
  303. package/dist/ink/components/ErrorOverview.js.map +0 -1
  304. package/dist/ink/components/Newline.d.ts +0 -13
  305. package/dist/ink/components/Newline.d.ts.map +0 -1
  306. package/dist/ink/components/Newline.js +0 -36
  307. package/dist/ink/components/Newline.js.map +0 -1
  308. package/dist/ink/components/NoSelect.d.ts +0 -20
  309. package/dist/ink/components/NoSelect.d.ts.map +0 -1
  310. package/dist/ink/components/NoSelect.js +0 -63
  311. package/dist/ink/components/NoSelect.js.map +0 -1
  312. package/dist/ink/components/RawAnsi.d.ts +0 -17
  313. package/dist/ink/components/RawAnsi.d.ts.map +0 -1
  314. package/dist/ink/components/RawAnsi.js +0 -52
  315. package/dist/ink/components/RawAnsi.js.map +0 -1
  316. package/dist/ink/components/Spacer.d.ts +0 -6
  317. package/dist/ink/components/Spacer.d.ts.map +0 -1
  318. package/dist/ink/components/Spacer.js +0 -26
  319. package/dist/ink/components/Spacer.js.map +0 -1
  320. package/dist/ink/components/StdinContext.d.ts +0 -28
  321. package/dist/ink/components/StdinContext.d.ts.map +0 -1
  322. package/dist/ink/components/StdinContext.js +0 -19
  323. package/dist/ink/components/StdinContext.js.map +0 -1
  324. package/dist/ink/components/TerminalFocusContext.d.ts +0 -10
  325. package/dist/ink/components/TerminalFocusContext.d.ts.map +0 -1
  326. package/dist/ink/components/TerminalFocusContext.js +0 -81
  327. package/dist/ink/components/TerminalFocusContext.js.map +0 -1
  328. package/dist/ink/components/TerminalSizeContext.d.ts +0 -6
  329. package/dist/ink/components/TerminalSizeContext.d.ts.map +0 -1
  330. package/dist/ink/components/TerminalSizeContext.js +0 -6
  331. package/dist/ink/components/TerminalSizeContext.js.map +0 -1
  332. package/dist/ink/components/Text.d.ts +0 -56
  333. package/dist/ink/components/Text.d.ts.map +0 -1
  334. package/dist/ink/components/Text.js +0 -201
  335. package/dist/ink/components/Text.js.map +0 -1
  336. package/dist/ink/constants.d.ts +0 -2
  337. package/dist/ink/constants.d.ts.map +0 -1
  338. package/dist/ink/constants.js +0 -6
  339. package/dist/ink/constants.js.map +0 -1
  340. package/dist/ink/devtools.d.ts +0 -3
  341. package/dist/ink/devtools.d.ts.map +0 -1
  342. package/dist/ink/devtools.js +0 -5
  343. package/dist/ink/devtools.js.map +0 -1
  344. package/dist/ink/dom.d.ts +0 -77
  345. package/dist/ink/dom.d.ts.map +0 -1
  346. package/dist/ink/dom.js +0 -322
  347. package/dist/ink/dom.js.map +0 -1
  348. package/dist/ink/events/click-event.d.ts +0 -32
  349. package/dist/ink/events/click-event.d.ts.map +0 -1
  350. package/dist/ink/events/click-event.js +0 -30
  351. package/dist/ink/events/click-event.js.map +0 -1
  352. package/dist/ink/events/dispatcher.d.ts +0 -41
  353. package/dist/ink/events/dispatcher.d.ts.map +0 -1
  354. package/dist/ink/events/dispatcher.js +0 -181
  355. package/dist/ink/events/dispatcher.js.map +0 -1
  356. package/dist/ink/events/emitter.d.ts +0 -6
  357. package/dist/ink/events/emitter.d.ts.map +0 -1
  358. package/dist/ink/events/emitter.js +0 -36
  359. package/dist/ink/events/emitter.js.map +0 -1
  360. package/dist/ink/events/event-handlers.d.ts +0 -47
  361. package/dist/ink/events/event-handlers.d.ts.map +0 -1
  362. package/dist/ink/events/event-handlers.js +0 -34
  363. package/dist/ink/events/event-handlers.js.map +0 -1
  364. package/dist/ink/events/event.d.ts +0 -6
  365. package/dist/ink/events/event.d.ts.map +0 -1
  366. package/dist/ink/events/event.js +0 -16
  367. package/dist/ink/events/event.js.map +0 -1
  368. package/dist/ink/events/focus-event.d.ts +0 -14
  369. package/dist/ink/events/focus-event.d.ts.map +0 -1
  370. package/dist/ink/events/focus-event.js +0 -20
  371. package/dist/ink/events/focus-event.js.map +0 -1
  372. package/dist/ink/events/input-event.d.ts +0 -31
  373. package/dist/ink/events/input-event.d.ts.map +0 -1
  374. package/dist/ink/events/input-event.js +0 -163
  375. package/dist/ink/events/input-event.js.map +0 -1
  376. package/dist/ink/events/keyboard-event.d.ts +0 -20
  377. package/dist/ink/events/keyboard-event.d.ts.map +0 -1
  378. package/dist/ink/events/keyboard-event.js +0 -44
  379. package/dist/ink/events/keyboard-event.js.map +0 -1
  380. package/dist/ink/events/terminal-event.d.ts +0 -55
  381. package/dist/ink/events/terminal-event.d.ts.map +0 -1
  382. package/dist/ink/events/terminal-event.js +0 -79
  383. package/dist/ink/events/terminal-event.js.map +0 -1
  384. package/dist/ink/events/terminal-focus-event.d.ts +0 -14
  385. package/dist/ink/events/terminal-focus-event.d.ts.map +0 -1
  386. package/dist/ink/events/terminal-focus-event.js +0 -19
  387. package/dist/ink/events/terminal-focus-event.js.map +0 -1
  388. package/dist/ink/focus.d.ts +0 -44
  389. package/dist/ink/focus.d.ts.map +0 -1
  390. package/dist/ink/focus.js +0 -164
  391. package/dist/ink/focus.js.map +0 -1
  392. package/dist/ink/frame.d.ts +0 -95
  393. package/dist/ink/frame.d.ts.map +0 -1
  394. package/dist/ink/frame.js +0 -35
  395. package/dist/ink/frame.js.map +0 -1
  396. package/dist/ink/get-max-width.d.ts +0 -18
  397. package/dist/ink/get-max-width.d.ts.map +0 -1
  398. package/dist/ink/get-max-width.js +0 -26
  399. package/dist/ink/get-max-width.js.map +0 -1
  400. package/dist/ink/hit-test.d.ts +0 -34
  401. package/dist/ink/hit-test.d.ts.map +0 -1
  402. package/dist/ink/hit-test.js +0 -119
  403. package/dist/ink/hit-test.js.map +0 -1
  404. package/dist/ink/hooks/use-animation-frame.d.ts +0 -27
  405. package/dist/ink/hooks/use-animation-frame.d.ts.map +0 -1
  406. package/dist/ink/hooks/use-animation-frame.js +0 -52
  407. package/dist/ink/hooks/use-animation-frame.js.map +0 -1
  408. package/dist/ink/hooks/use-app.d.ts +0 -6
  409. package/dist/ink/hooks/use-app.d.ts.map +0 -1
  410. package/dist/ink/hooks/use-app.js +0 -13
  411. package/dist/ink/hooks/use-app.js.map +0 -1
  412. package/dist/ink/hooks/use-declared-cursor.d.ts +0 -27
  413. package/dist/ink/hooks/use-declared-cursor.d.ts.map +0 -1
  414. package/dist/ink/hooks/use-declared-cursor.js +0 -67
  415. package/dist/ink/hooks/use-declared-cursor.js.map +0 -1
  416. package/dist/ink/hooks/use-input.d.ts +0 -38
  417. package/dist/ink/hooks/use-input.d.ts.map +0 -1
  418. package/dist/ink/hooks/use-input.js +0 -86
  419. package/dist/ink/hooks/use-input.js.map +0 -1
  420. package/dist/ink/hooks/use-interval.d.ts +0 -19
  421. package/dist/ink/hooks/use-interval.d.ts.map +0 -1
  422. package/dist/ink/hooks/use-interval.js +0 -59
  423. package/dist/ink/hooks/use-interval.js.map +0 -1
  424. package/dist/ink/hooks/use-search-highlight.d.ts +0 -31
  425. package/dist/ink/hooks/use-search-highlight.d.ts.map +0 -1
  426. package/dist/ink/hooks/use-search-highlight.js +0 -39
  427. package/dist/ink/hooks/use-search-highlight.js.map +0 -1
  428. package/dist/ink/hooks/use-selection.d.ts +0 -40
  429. package/dist/ink/hooks/use-selection.d.ts.map +0 -1
  430. package/dist/ink/hooks/use-selection.js +0 -68
  431. package/dist/ink/hooks/use-selection.js.map +0 -1
  432. package/dist/ink/hooks/use-stdin.d.ts +0 -6
  433. package/dist/ink/hooks/use-stdin.d.ts.map +0 -1
  434. package/dist/ink/hooks/use-stdin.js +0 -13
  435. package/dist/ink/hooks/use-stdin.js.map +0 -1
  436. package/dist/ink/hooks/use-tab-status.d.ts +0 -14
  437. package/dist/ink/hooks/use-tab-status.d.ts.map +0 -1
  438. package/dist/ink/hooks/use-tab-status.js +0 -61
  439. package/dist/ink/hooks/use-tab-status.js.map +0 -1
  440. package/dist/ink/hooks/use-terminal-focus.d.ts +0 -11
  441. package/dist/ink/hooks/use-terminal-focus.d.ts.map +0 -1
  442. package/dist/ink/hooks/use-terminal-focus.js +0 -22
  443. package/dist/ink/hooks/use-terminal-focus.js.map +0 -1
  444. package/dist/ink/hooks/use-terminal-title.d.ts +0 -13
  445. package/dist/ink/hooks/use-terminal-title.d.ts.map +0 -1
  446. package/dist/ink/hooks/use-terminal-title.js +0 -36
  447. package/dist/ink/hooks/use-terminal-title.js.map +0 -1
  448. package/dist/ink/hooks/use-terminal-viewport.d.ts +0 -30
  449. package/dist/ink/hooks/use-terminal-viewport.d.ts.map +0 -1
  450. package/dist/ink/hooks/use-terminal-viewport.js +0 -81
  451. package/dist/ink/hooks/use-terminal-viewport.js.map +0 -1
  452. package/dist/ink/ink.d.ts +0 -371
  453. package/dist/ink/ink.d.ts.map +0 -1
  454. package/dist/ink/ink.js +0 -1686
  455. package/dist/ink/ink.js.map +0 -1
  456. package/dist/ink/instances.d.ts +0 -4
  457. package/dist/ink/instances.d.ts.map +0 -1
  458. package/dist/ink/instances.js +0 -10
  459. package/dist/ink/instances.js.map +0 -1
  460. package/dist/ink/line-width-cache.d.ts +0 -2
  461. package/dist/ink/line-width-cache.d.ts.map +0 -1
  462. package/dist/ink/line-width-cache.js +0 -23
  463. package/dist/ink/line-width-cache.js.map +0 -1
  464. package/dist/ink/log-update.d.ts +0 -18
  465. package/dist/ink/log-update.d.ts.map +0 -1
  466. package/dist/ink/log-update.js +0 -637
  467. package/dist/ink/log-update.js.map +0 -1
  468. package/dist/ink/measure-element.d.ts +0 -17
  469. package/dist/ink/measure-element.d.ts.map +0 -1
  470. package/dist/ink/measure-element.js +0 -11
  471. package/dist/ink/measure-element.js.map +0 -1
  472. package/dist/ink/measure-text.d.ts +0 -7
  473. package/dist/ink/measure-text.d.ts.map +0 -1
  474. package/dist/ink/measure-text.js +0 -38
  475. package/dist/ink/measure-text.js.map +0 -1
  476. package/dist/ink/node-cache.d.ts +0 -21
  477. package/dist/ink/node-cache.d.ts.map +0 -1
  478. package/dist/ink/node-cache.js +0 -36
  479. package/dist/ink/node-cache.js.map +0 -1
  480. package/dist/ink/optimizer.d.ts +0 -16
  481. package/dist/ink/optimizer.d.ts.map +0 -1
  482. package/dist/ink/optimizer.js +0 -85
  483. package/dist/ink/optimizer.js.map +0 -1
  484. package/dist/ink/output.d.ts +0 -120
  485. package/dist/ink/output.d.ts.map +0 -1
  486. package/dist/ink/output.js +0 -559
  487. package/dist/ink/output.js.map +0 -1
  488. package/dist/ink/parse-keypress.d.ts +0 -112
  489. package/dist/ink/parse-keypress.d.ts.map +0 -1
  490. package/dist/ink/parse-keypress.js +0 -719
  491. package/dist/ink/parse-keypress.js.map +0 -1
  492. package/dist/ink/parse-keypress.test.d.ts +0 -2
  493. package/dist/ink/parse-keypress.test.d.ts.map +0 -1
  494. package/dist/ink/parse-keypress.test.js +0 -37
  495. package/dist/ink/parse-keypress.test.js.map +0 -1
  496. package/dist/ink/reconciler.d.ts +0 -12
  497. package/dist/ink/reconciler.d.ts.map +0 -1
  498. package/dist/ink/reconciler.js +0 -456
  499. package/dist/ink/reconciler.js.map +0 -1
  500. package/dist/ink/render-border.d.ts +0 -25
  501. package/dist/ink/render-border.d.ts.map +0 -1
  502. package/dist/ink/render-border.js +0 -141
  503. package/dist/ink/render-border.js.map +0 -1
  504. package/dist/ink/render-node-to-output.d.ts +0 -46
  505. package/dist/ink/render-node-to-output.d.ts.map +0 -1
  506. package/dist/ink/render-node-to-output.js +0 -1257
  507. package/dist/ink/render-node-to-output.js.map +0 -1
  508. package/dist/ink/render-to-screen.d.ts +0 -47
  509. package/dist/ink/render-to-screen.d.ts.map +0 -1
  510. package/dist/ink/render-to-screen.js +0 -213
  511. package/dist/ink/render-to-screen.js.map +0 -1
  512. package/dist/ink/renderer.d.ts +0 -15
  513. package/dist/ink/renderer.d.ts.map +0 -1
  514. package/dist/ink/renderer.js +0 -169
  515. package/dist/ink/renderer.js.map +0 -1
  516. package/dist/ink/root.d.ts +0 -75
  517. package/dist/ink/root.d.ts.map +0 -1
  518. package/dist/ink/root.js +0 -89
  519. package/dist/ink/root.js.map +0 -1
  520. package/dist/ink/screen.d.ts +0 -270
  521. package/dist/ink/screen.d.ts.map +0 -1
  522. package/dist/ink/screen.js +0 -1194
  523. package/dist/ink/screen.js.map +0 -1
  524. package/dist/ink/searchHighlight.d.ts +0 -21
  525. package/dist/ink/searchHighlight.d.ts.map +0 -1
  526. package/dist/ink/searchHighlight.js +0 -82
  527. package/dist/ink/searchHighlight.js.map +0 -1
  528. package/dist/ink/selection.d.ts +0 -230
  529. package/dist/ink/selection.d.ts.map +0 -1
  530. package/dist/ink/selection.js +0 -814
  531. package/dist/ink/selection.js.map +0 -1
  532. package/dist/ink/squash-text-nodes.d.ts +0 -23
  533. package/dist/ink/squash-text-nodes.d.ts.map +0 -1
  534. package/dist/ink/squash-text-nodes.js +0 -60
  535. package/dist/ink/squash-text-nodes.js.map +0 -1
  536. package/dist/ink/stringWidth.d.ts +0 -2
  537. package/dist/ink/stringWidth.d.ts.map +0 -1
  538. package/dist/ink/stringWidth.js +0 -207
  539. package/dist/ink/stringWidth.js.map +0 -1
  540. package/dist/ink/styles.d.ts +0 -307
  541. package/dist/ink/styles.d.ts.map +0 -1
  542. package/dist/ink/styles.js +0 -302
  543. package/dist/ink/styles.js.map +0 -1
  544. package/dist/ink/supports-hyperlinks.d.ts +0 -14
  545. package/dist/ink/supports-hyperlinks.d.ts.map +0 -1
  546. package/dist/ink/supports-hyperlinks.js +0 -48
  547. package/dist/ink/supports-hyperlinks.js.map +0 -1
  548. package/dist/ink/tabstops.d.ts +0 -2
  549. package/dist/ink/tabstops.d.ts.map +0 -1
  550. package/dist/ink/tabstops.js +0 -43
  551. package/dist/ink/tabstops.js.map +0 -1
  552. package/dist/ink/terminal-focus-state.d.ts +0 -7
  553. package/dist/ink/terminal-focus-state.d.ts.map +0 -1
  554. package/dist/ink/terminal-focus-state.js +0 -43
  555. package/dist/ink/terminal-focus-state.js.map +0 -1
  556. package/dist/ink/terminal-querier.d.ts +0 -130
  557. package/dist/ink/terminal-querier.d.ts.map +0 -1
  558. package/dist/ink/terminal-querier.js +0 -184
  559. package/dist/ink/terminal-querier.js.map +0 -1
  560. package/dist/ink/terminal.d.ts +0 -56
  561. package/dist/ink/terminal.d.ts.map +0 -1
  562. package/dist/ink/terminal.js +0 -249
  563. package/dist/ink/terminal.js.map +0 -1
  564. package/dist/ink/termio/csi.d.ts +0 -166
  565. package/dist/ink/termio/csi.d.ts.map +0 -1
  566. package/dist/ink/termio/csi.js +0 -285
  567. package/dist/ink/termio/csi.js.map +0 -1
  568. package/dist/ink/termio/dec.d.ts +0 -38
  569. package/dist/ink/termio/dec.d.ts.map +0 -1
  570. package/dist/ink/termio/dec.js +0 -59
  571. package/dist/ink/termio/dec.js.map +0 -1
  572. package/dist/ink/termio.d.ts +0 -25
  573. package/dist/ink/termio.d.ts.map +0 -1
  574. package/dist/ink/termio.js +0 -32
  575. package/dist/ink/termio.js.map +0 -1
  576. package/dist/ink/useTerminalNotification.d.ts +0 -27
  577. package/dist/ink/useTerminalNotification.d.ts.map +0 -1
  578. package/dist/ink/useTerminalNotification.js +0 -62
  579. package/dist/ink/useTerminalNotification.js.map +0 -1
  580. package/dist/ink/warn.d.ts +0 -2
  581. package/dist/ink/warn.d.ts.map +0 -1
  582. package/dist/ink/warn.js +0 -14
  583. package/dist/ink/warn.js.map +0 -1
  584. package/dist/ink/widest-line.d.ts +0 -2
  585. package/dist/ink/widest-line.d.ts.map +0 -1
  586. package/dist/ink/widest-line.js +0 -18
  587. package/dist/ink/widest-line.js.map +0 -1
  588. package/dist/ink/wrap-text.d.ts +0 -3
  589. package/dist/ink/wrap-text.d.ts.map +0 -1
  590. package/dist/ink/wrap-text.js +0 -61
  591. package/dist/ink/wrap-text.js.map +0 -1
  592. package/dist/ink/wrapAnsi.d.ts +0 -8
  593. package/dist/ink/wrapAnsi.d.ts.map +0 -1
  594. package/dist/ink/wrapAnsi.js +0 -13
  595. package/dist/ink/wrapAnsi.js.map +0 -1
  596. package/dist/ink-ui/components/TerminalCursor.d.ts +0 -15
  597. package/dist/ink-ui/components/TerminalCursor.d.ts.map +0 -1
  598. package/dist/ink-ui/components/TerminalCursor.js +0 -49
  599. package/dist/ink-ui/components/TerminalCursor.js.map +0 -1
  600. package/dist/ink-ui/runtime/stdout.d.ts +0 -11
  601. package/dist/ink-ui/runtime/stdout.d.ts.map +0 -1
  602. package/dist/ink-ui/runtime/stdout.js +0 -82
  603. package/dist/ink-ui/runtime/stdout.js.map +0 -1
  604. package/dist/runtime/goal-coordinator.d.ts +0 -66
  605. package/dist/runtime/goal-coordinator.d.ts.map +0 -1
  606. package/dist/runtime/goal-coordinator.js +0 -200
  607. package/dist/runtime/goal-coordinator.js.map +0 -1
  608. package/dist/screens/REPL.d.ts +0 -21
  609. package/dist/screens/REPL.d.ts.map +0 -1
  610. package/dist/screens/REPL.js +0 -139
  611. package/dist/screens/REPL.js.map +0 -1
  612. package/dist/services/goal-sidecar.d.ts +0 -96
  613. package/dist/services/goal-sidecar.d.ts.map +0 -1
  614. package/dist/services/goal-sidecar.js +0 -145
  615. package/dist/services/goal-sidecar.js.map +0 -1
  616. package/dist/tools/goal.d.ts +0 -17
  617. package/dist/tools/goal.d.ts.map +0 -1
  618. package/dist/tools/goal.js +0 -191
  619. package/dist/tools/goal.js.map +0 -1
  620. package/dist/ui/ink/components/App.d.ts +0 -6
  621. package/dist/ui/ink/components/App.d.ts.map +0 -1
  622. package/dist/ui/ink/components/App.js +0 -38
  623. package/dist/ui/ink/components/App.js.map +0 -1
  624. package/dist/ui/ink/components/CommandPanel.d.ts +0 -13
  625. package/dist/ui/ink/components/CommandPanel.d.ts.map +0 -1
  626. package/dist/ui/ink/components/CommandPanel.js +0 -41
  627. package/dist/ui/ink/components/CommandPanel.js.map +0 -1
  628. package/dist/ui/ink/components/InputLine.d.ts +0 -8
  629. package/dist/ui/ink/components/InputLine.d.ts.map +0 -1
  630. package/dist/ui/ink/components/InputLine.js +0 -15
  631. package/dist/ui/ink/components/InputLine.js.map +0 -1
  632. package/dist/ui/ink/components/StatusBar.d.ts +0 -9
  633. package/dist/ui/ink/components/StatusBar.d.ts.map +0 -1
  634. package/dist/ui/ink/components/StatusBar.js +0 -18
  635. package/dist/ui/ink/components/StatusBar.js.map +0 -1
  636. package/dist/ui/ink/components/index.d.ts +0 -5
  637. package/dist/ui/ink/components/index.d.ts.map +0 -1
  638. package/dist/ui/ink/components/index.js +0 -5
  639. package/dist/ui/ink/components/index.js.map +0 -1
  640. package/dist/utils/chalk.d.ts +0 -9
  641. package/dist/utils/chalk.d.ts.map +0 -1
  642. package/dist/utils/chalk.js +0 -16
  643. package/dist/utils/chalk.js.map +0 -1
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AgentRuntimeController = void 0;
4
+ exports.goalProviderError = goalProviderError;
5
+ exports.goalTurnMadeProgress = goalTurnMadeProgress;
4
6
  const parser_1 = require("../commands/parser");
5
7
  const target_command_1 = require("../commands/target-command");
6
8
  const agent_runtime_protocol_1 = require("./agent-runtime-protocol");
@@ -8,10 +10,47 @@ const agent_status_1 = require("./agent-status");
8
10
  const chat_controller_1 = require("./chat-controller");
9
11
  const ui_events_1 = require("./ui-events");
10
12
  const turn_controller_1 = require("./turn-controller");
13
+ const tools_1 = require("./goals/tools");
14
+ const accounting_1 = require("./goals/accounting");
15
+ const crypto_1 = require("crypto");
16
+ const workspace_state_1 = require("../services/workspace-state");
17
+ const redaction_1 = require("../services/redaction");
18
+ const evidence_1 = require("./goals/evidence");
19
+ const session_storage_1 = require("../services/session-storage");
20
+ const external_assertion_1 = require("../framework/external-assertion");
21
+ const global_config_1 = require("../services/global-config");
11
22
  function isExitInput(input) {
12
23
  const parsed = (0, parser_1.parseInput)(input.trim());
13
24
  return parsed.isCommand && ['exit', 'quit', 'q'].includes(parsed.name);
14
25
  }
26
+ function goalProviderError(finishReason, errorType) {
27
+ if (finishReason !== 'failed' || !errorType)
28
+ return undefined;
29
+ switch (errorType) {
30
+ case 'quota_or_credit_exhausted':
31
+ return { kind: 'usage_limit', retryable: false };
32
+ case 'rate_limit':
33
+ return { kind: 'rate_limit', retryable: true };
34
+ case 'provider_busy':
35
+ return { kind: 'provider_busy', retryable: true };
36
+ case 'auth_failed':
37
+ return { kind: 'auth', retryable: false };
38
+ case 'connect_timeout':
39
+ case 'read_timeout':
40
+ case 'connection_reset':
41
+ case 'network_error':
42
+ return { kind: 'network', retryable: true };
43
+ default:
44
+ return { kind: 'unknown', retryable: false };
45
+ }
46
+ }
47
+ function goalTurnMadeProgress(input) {
48
+ const passedEvidence = input.evidenceRecords?.some(record => record.result === 'passed') ?? false;
49
+ const workspaceChanged = Boolean(input.workspaceFingerprintBefore &&
50
+ input.workspaceFingerprintAfter &&
51
+ input.workspaceFingerprintBefore !== input.workspaceFingerprintAfter);
52
+ return passedEvidence || workspaceChanged;
53
+ }
15
54
  function submittedEntry(input) {
16
55
  const parsed = (0, parser_1.parseInput)(input.trim());
17
56
  return {
@@ -41,10 +80,7 @@ class AgentRuntimeController {
41
80
  /** v0.2.24: set the goal coordinator for /target mode. */
42
81
  setGoalCoordinator(coord) {
43
82
  this.goalCoordinator = coord;
44
- this.goalCoordinatorSessionId = '*';
45
- // Wire goal tools to the coordinator so model can call get_goal/create_goal/update_goal.
46
- const { setGoalToolCoordinator } = require('./goals/tools');
47
- setGoalToolCoordinator(coord);
83
+ this.goalCoordinatorSessionId = coord.boundSessionId;
48
84
  // Wire goal prompt injection into the chat controller.
49
85
  if ('setGoalCoordinator' in this.runner) {
50
86
  this.runner.setGoalCoordinator(coord);
@@ -55,6 +91,9 @@ class AgentRuntimeController {
55
91
  this.pendingPermissions = new Map();
56
92
  this.activeRun = null;
57
93
  this.stopping = false;
94
+ this.continuationScheduleEpoch = 0;
95
+ /** Conservative reservations for every provider attempt in the current root turn. */
96
+ this.goalProviderReservedTokens = 0;
58
97
  this.nextPermissionRequestId = 1;
59
98
  /** v0.2.24: optional goal coordinator for /target mode. */
60
99
  this.goalCoordinator = null;
@@ -63,9 +102,20 @@ class AgentRuntimeController {
63
102
  throw new Error('AgentRuntimeController requires either events or eventSink');
64
103
  }
65
104
  this.turnController = new turn_controller_1.TurnController(options);
66
- this.eventSink = options.eventSink ?? (0, agent_runtime_protocol_1.createAgentRuntimeEventSinkFromUiEvents)(options.events);
67
- const events = options.events ?? (0, agent_runtime_protocol_1.createUiEventSinkFromAgentRuntimeEvents)(this.eventSink);
68
- this.runner = options.runner ?? new chat_controller_1.AgentChatController(options.runtime, events, this.createChatOptions());
105
+ const downstream = options.eventSink ?? (0, agent_runtime_protocol_1.createAgentRuntimeEventSinkFromUiEvents)(options.events);
106
+ this.eventSink = {
107
+ emit: event => {
108
+ this.captureGoalEvidence(event);
109
+ const result = downstream.emit(event);
110
+ if (event.type === 'session_restored') {
111
+ this.restoreGoalForSession(event.event.sessionId, event.event.projectPath);
112
+ }
113
+ return result;
114
+ },
115
+ };
116
+ const events = (0, agent_runtime_protocol_1.createUiEventSinkFromAgentRuntimeEvents)(this.eventSink);
117
+ this.runner =
118
+ options.runner ?? new chat_controller_1.AgentChatController(options.runtime, events, this.createChatOptions());
69
119
  }
70
120
  hasActiveTurn() {
71
121
  return this.turnController.hasActiveTurn();
@@ -80,86 +130,162 @@ class AgentRuntimeController {
80
130
  content: parsed.error,
81
131
  errorLayer: 'runtime',
82
132
  });
83
- return { handled: true, statusText: parsed.error, runtimeResult: { type: 'command_handled' } };
133
+ return {
134
+ handled: true,
135
+ statusText: parsed.error,
136
+ runtimeResult: { type: 'command_handled' },
137
+ };
84
138
  }
85
139
  const input = parsed.input;
86
140
  const coord = this.ensureGoalCoordinator(input.action !== 'show');
87
141
  if (!coord) {
88
142
  const statusText = 'Target unavailable: no active session.';
89
- this.emitAppend({ role: 'error', title: 'target', content: statusText, errorLayer: 'session' });
143
+ this.emitAppend({
144
+ role: 'error',
145
+ title: 'target',
146
+ content: statusText,
147
+ errorLayer: 'session',
148
+ });
90
149
  return { handled: true, statusText, runtimeResult: { type: 'command_handled' } };
91
150
  }
92
- if (this.turnController.hasActiveTurn()
93
- && !['pause', 'show', 'clear'].includes(input.action)) {
151
+ if (this.turnController.hasActiveTurn() && !['pause', 'show', 'clear'].includes(input.action)) {
94
152
  const statusText = 'Target command ignored while the agent is running. Use /target pause or interrupt first.';
95
153
  this.emitStatus(statusText);
96
154
  return { handled: true, statusText, runtimeResult: { type: 'command_ignored' } };
97
155
  }
98
156
  let success = true;
99
157
  let error;
100
- switch (input.action) {
101
- case 'show':
102
- break;
103
- case 'create': {
104
- const result = coord.create(input.payload?.objective ?? '');
105
- success = result.ok;
106
- if (!result.ok)
107
- error = result.error;
108
- break;
109
- }
110
- case 'pause':
111
- success = coord.pause();
112
- if (success && this.turnController.hasActiveTurn()) {
113
- this.turnController.interruptActiveTurn();
114
- }
115
- if (!success)
116
- error = 'Target is not active.';
117
- break;
118
- case 'resume':
119
- success = coord.resume();
120
- if (!success)
121
- error = 'Target cannot be resumed from its current state.';
122
- break;
123
- case 'edit':
124
- success = coord.edit(input.payload?.objective ?? '');
125
- if (!success)
126
- error = 'Target objective could not be updated.';
127
- break;
128
- case 'replace':
129
- success = coord.replace(input.payload?.objective ?? '');
130
- if (!success)
131
- error = 'Target could not be replaced.';
132
- break;
133
- case 'set_budget':
134
- success = coord.setBudget(input.payload?.tokenBudget ?? null);
135
- if (!success)
136
- error = 'Create or resume a target before setting a budget.';
137
- break;
138
- case 'clear':
139
- if (!input.payload?.confirmed) {
140
- success = false;
141
- error = 'Clearing a target requires confirmation: /target clear --yes';
158
+ let executionRevoked = false;
159
+ const previousGoalId = coord.goal?.goalId;
160
+ try {
161
+ switch (input.action) {
162
+ case 'show':
163
+ break;
164
+ case 'create': {
165
+ const result = coord.create(input.payload?.objective ?? '');
166
+ success = result.ok;
167
+ if (!result.ok)
168
+ error = result.error;
169
+ break;
142
170
  }
143
- else {
144
- success = coord.clear();
171
+ case 'pause':
172
+ if (coord.isActive) {
173
+ this.abortGoalOwnedExecution();
174
+ executionRevoked = true;
175
+ }
176
+ success = coord.pause();
145
177
  if (!success)
146
- error = 'No target exists to clear.';
147
- }
148
- break;
178
+ error = 'Target is not active.';
179
+ break;
180
+ case 'resume':
181
+ success = coord.resume({
182
+ confirmBoundary: true,
183
+ expectedGoalId: coord.goal?.goalId,
184
+ expectedRevision: coord.goal?.revision,
185
+ });
186
+ if (!success)
187
+ error = 'Target cannot be resumed from its current state.';
188
+ break;
189
+ case 'confirm':
190
+ success = coord.confirmCriterion(input.payload?.criterionId ?? '');
191
+ if (!success) {
192
+ error = 'Criterion cannot be confirmed. It must exist and require user evidence.';
193
+ }
194
+ break;
195
+ case 'edit':
196
+ success = coord.edit(input.payload?.objective ?? '');
197
+ if (!success)
198
+ error = 'Target objective could not be updated.';
199
+ break;
200
+ case 'replace':
201
+ success = coord.replace(input.payload?.objective ?? '');
202
+ if (!success) {
203
+ error = 'Target could not be replaced.';
204
+ if (input.payload?.objective?.trim()) {
205
+ error = this.failClosedGoalMutation(coord, input.action, new Error(error), executionRevoked, previousGoalId);
206
+ executionRevoked = true;
207
+ }
208
+ }
209
+ break;
210
+ case 'set_budget':
211
+ success = coord.setBudget(input.payload?.tokenBudget ?? null);
212
+ if (!success)
213
+ error = 'Create or resume a target before setting a budget.';
214
+ break;
215
+ case 'clear':
216
+ if (!input.payload?.confirmed) {
217
+ success = false;
218
+ error = 'Clearing a target requires confirmation: /target clear --yes';
219
+ }
220
+ else {
221
+ const hadGoal = coord.goal !== null;
222
+ if (hadGoal) {
223
+ this.abortGoalOwnedExecution();
224
+ executionRevoked = true;
225
+ }
226
+ success = coord.clear();
227
+ if (hadGoal && !success) {
228
+ error = this.failClosedGoalMutation(coord, input.action, new Error('Target clear did not remove the active Goal.'), executionRevoked, previousGoalId);
229
+ }
230
+ if (!success && !hadGoal)
231
+ error = 'No target exists to clear.';
232
+ }
233
+ break;
234
+ }
235
+ }
236
+ catch (cause) {
237
+ success = false;
238
+ error =
239
+ input.action === 'show'
240
+ ? cause instanceof Error
241
+ ? cause.message
242
+ : String(cause)
243
+ : this.failClosedGoalMutation(coord, input.action, cause, executionRevoked, previousGoalId);
149
244
  }
150
245
  if (!success && error) {
151
246
  this.emitAppend({ role: 'error', title: 'target', content: error, errorLayer: 'runtime' });
152
247
  }
248
+ if (success &&
249
+ input.action === 'resume' &&
250
+ coord.goal?.status === 'active' &&
251
+ coord.goal.tokenBudget !== undefined) {
252
+ const resumedBudget = (0, accounting_1.budgetPreflight)(coord.goal.tokensUsed, coord.goal.tokenBudget, 0);
253
+ if (!resumedBudget.available) {
254
+ try {
255
+ coord.limitBudget(resumedBudget.reason ?? 'Token budget unavailable after resuming the target.');
256
+ }
257
+ catch (cause) {
258
+ success = false;
259
+ error = this.failClosedGoalMutation(coord, 'resume_budget_stop', cause, executionRevoked, previousGoalId);
260
+ this.emitGoalMutationError(error);
261
+ }
262
+ }
263
+ }
264
+ if (success) {
265
+ const { updateSessionGoalBinding } = require('../services/session-storage');
266
+ updateSessionGoalBinding(coord.boundSessionId, coord.goal);
267
+ if (input.action === 'clear' && previousGoalId) {
268
+ this.emitGoalEvent({ type: 'goal_cleared', goalId: previousGoalId, reason: 'user_clear' });
269
+ }
270
+ else {
271
+ const snapshot = coord.snapshot();
272
+ if (snapshot) {
273
+ this.emitGoalEvent({
274
+ type: 'goal_updated',
275
+ goal: snapshot,
276
+ reason: `target_${input.action}`,
277
+ });
278
+ }
279
+ }
280
+ }
153
281
  const statusText = this.formatTargetStatus(coord);
154
282
  this.emitAppend({ role: 'system', title: 'target', content: statusText });
155
- if (success
156
- && (input.action === 'create' || input.action === 'resume' || input.action === 'replace')
157
- && coord.isActive) {
283
+ if (success &&
284
+ (input.action === 'create' || input.action === 'resume' || input.action === 'replace') &&
285
+ coord.isActive) {
158
286
  const req = coord.buildContinuationRequest();
159
287
  if (req) {
160
- const runtimeResult = this.submit(req.goal?.continuationIndex
161
- ? `[goal continuation #${req.goal.continuationIndex}]`
162
- : 'Continue pursuing the goal.');
288
+ const runtimeResult = this.submitGoalContinuation(req, `target_${input.action}`, true);
163
289
  return { handled: true, statusText, runtimeResult };
164
290
  }
165
291
  }
@@ -184,6 +310,7 @@ class AgentRuntimeController {
184
310
  }
185
311
  /** v0.1.1: emit shutdown_requested event through the renderer protocol. */
186
312
  emitShutdownRequested(reason) {
313
+ this.deferGoalContinuation(reason ?? 'shutdown requested', true);
187
314
  this.eventSink.emit({ type: 'shutdown_requested', reason });
188
315
  }
189
316
  setVerificationState(state) {
@@ -210,6 +337,8 @@ class AgentRuntimeController {
210
337
  return { type: 'exit_intent_cleared' };
211
338
  case 'goal_control':
212
339
  return this.handleGoalControl(input);
340
+ case 'permission_mode_change':
341
+ return this.applyPermissionModeChange(input.value);
213
342
  }
214
343
  }
215
344
  submit(input) {
@@ -232,22 +361,64 @@ class AgentRuntimeController {
232
361
  if (this.turnController.hasActiveTurn()) {
233
362
  const parsed = (0, parser_1.parseInput)(submitted);
234
363
  if (parsed.isCommand) {
235
- this.emitStatus(this.options.commandWhileRunningStatus ?? 'Command ignored while agent is running. Press Ctrl+C to interrupt first.');
364
+ this.emitStatus(this.options.commandWhileRunningStatus ??
365
+ 'Command ignored while agent is running. Press Ctrl+C to interrupt first.');
236
366
  return { type: 'command_ignored' };
237
367
  }
238
368
  // v0.2.26: user steering input during active goal — update constraints
239
369
  // without replacing the root objective.
240
370
  const gc = this.goalCoordinator;
241
371
  if (gc?.goal && !(0, target_command_1.isTargetCommand)(submitted)) {
242
- // Record the steering as a constraint update without changing objective.
243
- gc.edit(gc.goal.objective); // no-op edit to bump revision, steering is via prompt
372
+ const steeringGoalId = gc.goal.goalId;
373
+ try {
374
+ gc.addConstraint(submitted);
375
+ }
376
+ catch (cause) {
377
+ const message = this.failClosedGoalMutation(gc, 'steering', cause, false, steeringGoalId);
378
+ this.emitAppend({
379
+ role: 'error',
380
+ title: 'target steering',
381
+ content: message,
382
+ errorLayer: 'runtime',
383
+ });
384
+ this.emitStatus(message);
385
+ return { type: 'command_ignored' };
386
+ }
244
387
  }
245
388
  this.turnController.clearExitIntent();
246
389
  this.turnController.requestRevision(submitted);
247
- this.emitStatus(this.options.revisionStatus ?? 'Revision received. Interrupting current response...');
390
+ // v0.1.3 (G1): echo the incremental input to the transcript immediately,
391
+ // so the user sees their correction without waiting for the turn to abort.
392
+ this.emitAppend(submittedEntry(submitted));
393
+ this.emitStatus(this.options.revisionStatus ?? '已接收补充,正在中断当前轮…');
248
394
  return { type: 'revision_requested' };
249
395
  }
250
- this.activeRun = this.runTurn(submitted)
396
+ const session = this.options.runtime.getSession() ?? this.options.runtime.ensureSession();
397
+ const coord = this.ensureGoalCoordinator(false);
398
+ const goal = coord?.goal?.status === 'active' ? coord.goal : undefined;
399
+ const request = {
400
+ inputKind: 'user',
401
+ text: submitted,
402
+ sessionId: session?.id ?? 'pending-session',
403
+ goal: goal
404
+ ? {
405
+ goalId: goal.goalId,
406
+ revision: goal.revision,
407
+ continuationIndex: goal.continuationCount,
408
+ }
409
+ : undefined,
410
+ persistAsUserMessage: true,
411
+ echoToTranscript: true,
412
+ generation: coord?.generation ?? 0,
413
+ };
414
+ return this.submitTurnRequest(request);
415
+ }
416
+ submitTurnRequest(request) {
417
+ if (this.turnController.hasActiveTurn())
418
+ return { type: 'command_ignored' };
419
+ if (!this.requestIsCurrent(request))
420
+ return { type: 'command_ignored' };
421
+ this.activeRun = this.runTurn(request)
251
422
  .catch(error => {
252
423
  this.handleRunLoopError(error);
253
424
  })
@@ -258,58 +429,326 @@ class AgentRuntimeController {
258
429
  });
259
430
  return { type: 'started' };
260
431
  }
432
+ submitGoalContinuation(request, reason, emitScheduled) {
433
+ const goalId = request.goal?.goalId;
434
+ if (emitScheduled && goalId) {
435
+ this.emitGoalEvent({ type: 'goal_continuation', goalId, phase: 'scheduled', reason });
436
+ }
437
+ const result = this.submitTurnRequest(request);
438
+ if (goalId) {
439
+ this.emitGoalEvent({
440
+ type: 'goal_continuation',
441
+ goalId,
442
+ phase: result.type === 'started' ? 'started' : 'deferred',
443
+ reason: result.type === 'started' ? reason : `request rejected: ${reason}`,
444
+ });
445
+ }
446
+ return result;
447
+ }
261
448
  // --- v0.2.26: goal continuation scheduling ---
262
449
  scheduleGoalContinuation() {
263
450
  const coord = this.goalCoordinator;
264
451
  if (!coord || !coord.isActive || coord.goal?.status !== 'active')
265
452
  return;
266
- if (!coord.canContinue)
267
- return; // don't continue if deferred, budget exceeded, etc.
453
+ if (!coord.canContinue) {
454
+ this.emitGoalEvent({
455
+ type: 'goal_continuation',
456
+ goalId: coord.goal.goalId,
457
+ phase: 'deferred',
458
+ reason: 'coordinator is not eligible to continue',
459
+ });
460
+ return;
461
+ }
462
+ const scheduleEpoch = ++this.continuationScheduleEpoch;
463
+ const scheduledGoalId = coord.goal.goalId;
268
464
  // Defer to next tick to let the current turn's events settle.
269
465
  setImmediate(() => {
270
- if (this.stopping || this.turnController.hasActiveTurn())
466
+ if (scheduleEpoch !== this.continuationScheduleEpoch) {
467
+ this.emitGoalEvent({
468
+ type: 'goal_continuation',
469
+ goalId: scheduledGoalId,
470
+ phase: 'deferred',
471
+ reason: 'scheduled continuation was invalidated',
472
+ });
271
473
  return;
272
- if (!coord.isActive || coord.goal?.status !== 'active')
273
- return; // re-check after tick
474
+ }
475
+ if (this.stopping ||
476
+ this.turnController.hasActiveTurn() ||
477
+ this.pendingPermissions.size > 0) {
478
+ this.emitGoalEvent({
479
+ type: 'goal_continuation',
480
+ goalId: scheduledGoalId,
481
+ phase: 'deferred',
482
+ reason: this.stopping
483
+ ? 'runtime is stopping'
484
+ : this.pendingPermissions.size > 0
485
+ ? 'tool permission is pending'
486
+ : 'another turn is active',
487
+ });
488
+ return;
489
+ }
490
+ if (!coord.isActive || coord.goal?.status !== 'active') {
491
+ this.emitGoalEvent({
492
+ type: 'goal_continuation',
493
+ goalId: scheduledGoalId,
494
+ phase: 'deferred',
495
+ reason: 'goal is no longer active',
496
+ });
497
+ return;
498
+ }
274
499
  const req = coord.buildContinuationRequest();
275
500
  if (req) {
276
- this.handle({
277
- type: 'submit',
278
- text: `[goal continuation #${coord.goal.continuationCount + 1}]`,
279
- source: 'programmatic',
501
+ const projected = coord.goal?.lastTurn?.totalTokens ?? 0;
502
+ const preflight = (0, accounting_1.budgetPreflight)(coord.goal?.tokensUsed ?? 0, coord.goal?.tokenBudget, projected);
503
+ if (!preflight.available) {
504
+ try {
505
+ coord.limitBudget(preflight.reason ?? 'Token budget unavailable for another turn.');
506
+ }
507
+ catch (cause) {
508
+ const message = this.failClosedGoalMutation(coord, 'budget_stop', cause, false, scheduledGoalId);
509
+ this.emitGoalMutationError(message);
510
+ return;
511
+ }
512
+ this.emitGoalEvent({
513
+ type: 'goal_continuation',
514
+ goalId: coord.goal.goalId,
515
+ phase: 'stopped',
516
+ reason: preflight.reason ?? 'budget preflight failed',
517
+ });
518
+ return;
519
+ }
520
+ this.submitGoalContinuation(req, `continuation ${req.goal.continuationIndex}`, true);
521
+ }
522
+ else {
523
+ this.emitGoalEvent({
524
+ type: 'goal_continuation',
525
+ goalId: scheduledGoalId,
526
+ phase: 'deferred',
527
+ reason: 'coordinator did not produce a continuation request',
280
528
  });
281
529
  }
282
530
  });
283
531
  }
284
532
  /** v0.2.26: finalize goal turn with usage data from the last loop. */
285
- finalizeGoalTurn(turnId) {
533
+ finalizeGoalTurn(turnId, request, toolContext, startedAt, workspaceFingerprintBefore, turnAborted) {
286
534
  const coord = this.goalCoordinator;
287
- if (!coord?.goal || coord.goal.status !== 'active')
535
+ const requestGoal = request.goal;
536
+ const currentSessionId = this.options.runtime.getSession()?.id;
537
+ if (!coord?.goal ||
538
+ !requestGoal ||
539
+ currentSessionId !== request.sessionId ||
540
+ coord.boundSessionId !== request.sessionId ||
541
+ coord.generation !== request.generation ||
542
+ coord.goal.goalId !== requestGoal.goalId) {
543
+ if (request.inputKind === 'goal_continuation' && requestGoal) {
544
+ this.emitGoalEvent({
545
+ type: 'goal_continuation',
546
+ goalId: requestGoal.goalId,
547
+ phase: 'deferred',
548
+ reason: 'turn outcome rejected because its session, goal, revision, or generation is stale',
549
+ });
550
+ }
288
551
  return;
552
+ }
289
553
  const usage = this.options.runtime.store.getSnapshot().tokenUsage;
290
554
  const loopStats = this.options.runtime.store.getSnapshot().lastLoopStats;
555
+ const subagentPromptTokens = loopStats?.subagentPromptTokens ?? 0;
556
+ const subagentCompletionTokens = loopStats?.subagentCompletionTokens ?? 0;
557
+ const subagentTokens = loopStats?.subagentTotalTokens ?? 0;
558
+ const workspaceFingerprintAfter = (0, workspace_state_1.captureWorkspaceFingerprint)(this.activeProjectPath());
559
+ const workspaceChanged = Boolean(workspaceFingerprintBefore &&
560
+ workspaceFingerprintAfter &&
561
+ workspaceFingerprintBefore !== workspaceFingerprintAfter);
562
+ const usageAccountingComplete = loopStats
563
+ ?.usageAccountingComplete !== false;
291
564
  const outcome = {
292
565
  turnId: String(turnId),
293
- sessionId: this.options.runtime.getSession()?.id ?? '',
294
- goalId: coord.goal.goalId,
295
- goalRevision: coord.goal.revision,
296
- startedAt: coord.goal.updatedAt,
566
+ sessionId: request.sessionId,
567
+ goalId: requestGoal.goalId,
568
+ goalRevision: requestGoal.revision,
569
+ goalGeneration: request.generation,
570
+ startedAt,
297
571
  endedAt: Date.now(),
298
572
  finishReason: loopStats?.finishReason ?? 'unknown',
299
573
  usage: {
300
- promptTokens: usage?.promptTokens ?? 0,
301
- completionTokens: usage?.completionTokens ?? 0,
302
- subagentTokens: 0,
303
- totalTokens: (usage?.promptTokens ?? 0) + (usage?.completionTokens ?? 0),
574
+ promptTokens: Math.max(0, (usage?.promptTokens ?? 0) - subagentPromptTokens),
575
+ completionTokens: Math.max(0, (usage?.completionTokens ?? 0) - subagentCompletionTokens),
576
+ subagentTokens,
577
+ totalTokens: usage !== null
578
+ ? (usage?.promptTokens ?? 0) + (usage?.completionTokens ?? 0)
579
+ : subagentTokens,
304
580
  },
305
- madeProgress: loopStats ? loopStats.toolCalls > 0 : true,
581
+ madeProgress: goalTurnMadeProgress({
582
+ evidenceRecords: toolContext?.evidenceRecords,
583
+ pendingPlanUpdate: toolContext?.pendingPlanUpdate,
584
+ workspaceFingerprintBefore,
585
+ workspaceFingerprintAfter,
586
+ }),
587
+ workspaceChanged,
588
+ providerError: goalProviderError(loopStats?.finishReason, loopStats?.providerLastRetryErrorType),
589
+ blocker: toolContext?.pendingBlocker,
590
+ pendingTerminalRequest: toolContext?.pendingTerminalRequest,
591
+ pendingPlanUpdate: toolContext?.pendingPlanUpdate,
592
+ evidenceRecords: toolContext?.evidenceRecords,
593
+ workspaceFingerprint: workspaceFingerprintAfter,
594
+ evidenceRefs: toolContext?.evidenceRecords.map(record => record.id),
595
+ verificationSummary: this.verificationSummary(loopStats),
596
+ usageComplete: usageAccountingComplete &&
597
+ usage !== null &&
598
+ loopStats?.finishReason !== 'failed' &&
599
+ (loopStats?.providerRetryCount ?? 0) === 0 &&
600
+ (loopStats?.providerFallbackCount ?? 0) === 0,
306
601
  };
307
- coord.finalizeTurn(outcome);
602
+ const semanticOutcomeIsCurrent = coord.goal.status === 'active' &&
603
+ !turnAborted &&
604
+ coord.goal.revision === requestGoal.revision;
605
+ if (!semanticOutcomeIsCurrent) {
606
+ const accountingOnlyEligible = turnAborted || coord.goal.status === 'paused' || coord.goal.revision > requestGoal.revision;
607
+ if (!accountingOnlyEligible) {
608
+ if (request.inputKind === 'goal_continuation') {
609
+ this.emitGoalEvent({
610
+ type: 'goal_continuation',
611
+ goalId: requestGoal.goalId,
612
+ phase: 'deferred',
613
+ reason: 'turn outcome rejected because the Goal lifecycle no longer accepts this turn',
614
+ });
615
+ }
616
+ return;
617
+ }
618
+ // accountStaleTurn is the coordinator's accounting-only, idempotent
619
+ // entry point. An interrupt can leave the Goal revision unchanged, so
620
+ // freeze this outcome immediately before the current revision without
621
+ // admitting any semantic payload from the cancelled turn.
622
+ const accountingOutcome = {
623
+ ...outcome,
624
+ goalRevision: Math.min(outcome.goalRevision ?? requestGoal.revision, coord.goal.revision - 1),
625
+ };
626
+ let accounted = false;
627
+ try {
628
+ const alreadyAccounted = coord.goal.lastTurn?.turnId === String(turnId);
629
+ accounted = alreadyAccounted ? false : coord.accountStaleTurn(accountingOutcome);
630
+ // Missing/partial provider usage is fail-closed. Known lower-bound
631
+ // tokens have already been retained by accountStaleTurn.
632
+ if (outcome.usageComplete === false && coord.goal?.status === 'active') {
633
+ coord.pause();
634
+ }
635
+ }
636
+ catch (error) {
637
+ this.failClosedGoalPersistence(coord, requestGoal.goalId, error, 'stale_accounting');
638
+ return;
639
+ }
640
+ const snapshot = coord.snapshot();
641
+ if (accounted && snapshot) {
642
+ this.emitGoalEvent({
643
+ type: 'goal_updated',
644
+ goal: snapshot,
645
+ reason: 'interrupted or stale turn usage accounted without semantic finalization',
646
+ });
647
+ this.recordGoalTraceState(turnId, request, snapshot);
648
+ }
649
+ if (request.inputKind === 'goal_continuation') {
650
+ this.emitGoalEvent({
651
+ type: 'goal_continuation',
652
+ goalId: requestGoal.goalId,
653
+ phase: 'deferred',
654
+ reason: 'turn semantic outcome rejected; incurred usage was accounted without continuation',
655
+ });
656
+ }
657
+ return;
658
+ }
659
+ // Live steering intentionally bumps the Goal revision before the aborted
660
+ // provider turn settles. The semantic payload is stale, but the provider
661
+ // usage and elapsed time were still incurred and must be counted once.
662
+ const planRevisionBefore = coord.goal.contract?.planSnapshot?.revision;
663
+ try {
664
+ coord.finalizeTurn(outcome);
665
+ }
666
+ catch (error) {
667
+ this.failClosedGoalPersistence(coord, requestGoal.goalId, error, 'turn_finalize');
668
+ return;
669
+ }
670
+ const snapshot = coord.snapshot();
671
+ if (snapshot) {
672
+ this.recordGoalTraceState(turnId, request, snapshot);
673
+ if (coord.goal?.contract?.planSnapshot &&
674
+ coord.goal.contract.planSnapshot.revision !== planRevisionBefore) {
675
+ const plan = coord.goal.contract.planSnapshot;
676
+ this.emitGoalEvent({
677
+ type: 'goal_plan_updated',
678
+ goalId: snapshot.goalId,
679
+ planRevision: plan.revision,
680
+ phase: plan.phase,
681
+ nextAction: plan.nextAction,
682
+ });
683
+ }
684
+ let terminalEvent;
685
+ if (snapshot.status === 'complete' && coord.goal?.completionAudit) {
686
+ terminalEvent = {
687
+ type: 'goal_completed',
688
+ goal: snapshot,
689
+ audit: coord.goal.completionAudit,
690
+ };
691
+ }
692
+ else if (coord.goal?.completionAudit && !coord.goal.completionAudit.passed) {
693
+ terminalEvent = {
694
+ type: 'goal_audit_failed',
695
+ goalId: snapshot.goalId,
696
+ audit: 'completion',
697
+ summary: coord.goal.completionAudit.remainingRequirements.join(' '),
698
+ };
699
+ }
700
+ else if (toolContext?.pendingTerminalRequest?.requestedStatus === 'blocked' &&
701
+ snapshot.status !== 'blocked') {
702
+ const blocker = coord.goal?.blocker;
703
+ terminalEvent = {
704
+ type: 'goal_audit_failed',
705
+ goalId: snapshot.goalId,
706
+ audit: 'blocked',
707
+ summary: blocker
708
+ ? `Blocker is not terminal: ${blocker.consecutiveTurns}/3 consecutive turns; no-progress ${coord.goal?.noProgressCount ?? 0}/3.`
709
+ : 'Blocked request did not include a valid non-retryable user, permission, or external-state blocker.',
710
+ };
711
+ }
712
+ // Project the fresh snapshot first, then the specific audit/completion
713
+ // event. This keeps renderer state current without a generic update
714
+ // overwriting the user-facing terminal result.
715
+ this.emitGoalEvent({ type: 'goal_updated', goal: snapshot, reason: 'turn_finalized' });
716
+ if (terminalEvent)
717
+ this.emitGoalEvent(terminalEvent);
718
+ }
719
+ }
720
+ failClosedGoalPersistence(coord, goalId, error, operation) {
721
+ const message = this.failClosedGoalMutation(coord, operation, error, false, goalId);
722
+ this.emitAppend({
723
+ role: 'error',
724
+ title: 'goal persistence',
725
+ content: message,
726
+ errorLayer: 'session',
727
+ });
728
+ this.emitStatus(message);
729
+ }
730
+ recordGoalTraceState(turnId, request, snapshot) {
731
+ if (!request.goal)
732
+ return;
733
+ const trace = (0, session_storage_1.appendSessionTraceEvent)(request.sessionId, {
734
+ turnId: String(turnId),
735
+ type: 'goal_state',
736
+ goalId: request.goal.goalId,
737
+ goalRevision: request.goal.revision,
738
+ goalInputKind: request.inputKind,
739
+ goalStopReason: snapshot.stopReason ?? (snapshot.status === 'complete' ? 'completed' : 'continue'),
740
+ note: `status=${snapshot.status}; snapshotRevision=${snapshot.revision}`,
741
+ });
742
+ if (trace)
743
+ this.eventSink.emit({ type: 'trace_event_recorded', event: trace });
308
744
  }
309
745
  interrupt() {
310
- const shouldExit = this.turnController.registerExitIntent();
311
746
  // v0.2.26: pause active goal on interrupt to prevent immediate restart.
312
- this.goalCoordinator?.deferContinuation();
747
+ this.deferGoalContinuation('user interrupt', true);
748
+ // Register after any synchronous persistence recovery. A lock wait can be
749
+ // longer than the confirmation window, but must not consume the user's
750
+ // first Ctrl+C before this call even returns.
751
+ const shouldExit = this.turnController.registerExitIntent();
313
752
  if (this.turnController.hasActiveTurn()) {
314
753
  this.turnController.interruptActiveTurn();
315
754
  if (shouldExit)
@@ -324,6 +763,7 @@ class AgentRuntimeController {
324
763
  }
325
764
  async stopActiveTurn() {
326
765
  this.stopping = true;
766
+ this.deferGoalContinuation('runtime stopping');
327
767
  this.turnController.interruptActiveTurn();
328
768
  this.rejectPendingPermissions();
329
769
  if (this.activeRun) {
@@ -332,24 +772,136 @@ class AgentRuntimeController {
332
772
  // Reset stopping flag so subsequent turns can execute.
333
773
  this.stopping = false;
334
774
  }
775
+ deferGoalContinuation(reason, preserveExitIntent = false) {
776
+ this.continuationScheduleEpoch += 1;
777
+ const coord = this.goalCoordinator;
778
+ const goalId = coord?.goal?.status === 'active' ? coord.goal.goalId : undefined;
779
+ if (!coord || !goalId)
780
+ return;
781
+ try {
782
+ coord.deferContinuation();
783
+ }
784
+ catch (cause) {
785
+ const message = this.failClosedGoalMutation(coord, 'pause', cause, false, goalId, preserveExitIntent);
786
+ this.emitGoalMutationError(message);
787
+ return;
788
+ }
789
+ this.emitGoalEvent({
790
+ type: 'goal_continuation',
791
+ goalId,
792
+ phase: 'deferred',
793
+ reason,
794
+ });
795
+ }
796
+ /** Revoke all Goal-owned execution before mutating a lifecycle boundary. */
797
+ abortGoalOwnedExecution(preserveExitIntent = false) {
798
+ this.continuationScheduleEpoch += 1;
799
+ if (!preserveExitIntent)
800
+ this.turnController.clearExitIntent();
801
+ this.turnController.interruptActiveTurn();
802
+ this.rejectPendingPermissions();
803
+ }
804
+ /**
805
+ * Contain a failed Goal mutation without writing the restored disk state back.
806
+ * The coordinator advances its generation so every in-flight request becomes
807
+ * stale, then preserves complete or deleted disk authority as-is.
808
+ */
809
+ failClosedGoalMutation(coord, operation, cause, executionAlreadyRevoked = false, goalIdBeforeMutation, preserveExitIntent = false) {
810
+ if (!executionAlreadyRevoked)
811
+ this.abortGoalOwnedExecution(preserveExitIntent);
812
+ const detail = (0, redaction_1.redactTraceText)(cause instanceof Error ? cause.message : String(cause)).slice(0, 600);
813
+ const operationSubject = operation === 'steering'
814
+ ? 'Steering'
815
+ : operation === 'turn_finalize'
816
+ ? 'Goal turn finalization'
817
+ : operation === 'stale_accounting'
818
+ ? 'Stale Goal accounting'
819
+ : `Target ${operation}`;
820
+ const goalIdBeforeRecovery = coord.goal?.goalId ?? goalIdBeforeMutation;
821
+ const failureReason = `${operationSubject} was not saved; Goal-owned execution was revoked fail-closed. ${detail}`;
822
+ coord.failClosedAfterPersistenceError(failureReason);
823
+ const snapshot = coord.snapshot();
824
+ const message = !snapshot
825
+ ? `${operationSubject} was not saved; disk authority reports that the Goal was deleted. No continuation was started. ${detail}`
826
+ : coord.goal?.status === 'complete'
827
+ ? `${operationSubject} was not saved; the restored completed Goal remains terminal. No continuation was started. ${detail}`
828
+ : `${operationSubject} was not saved; execution was paused fail-closed. Resolve the storage error, then use /target resume. ${detail}`;
829
+ if (snapshot) {
830
+ this.emitGoalEvent({
831
+ type: 'goal_updated',
832
+ goal: snapshot,
833
+ reason: coord.goal?.status === 'complete'
834
+ ? `target_${operation} persistence failed; completed disk authority preserved`
835
+ : `target_${operation} persistence failed; continuation paused fail-closed`,
836
+ });
837
+ }
838
+ else if (goalIdBeforeRecovery) {
839
+ this.emitGoalEvent({
840
+ type: 'goal_cleared',
841
+ goalId: goalIdBeforeRecovery,
842
+ reason: `target_${operation} failed after the Goal was deleted`,
843
+ });
844
+ }
845
+ const deferredGoalId = snapshot?.goalId ?? goalIdBeforeRecovery;
846
+ if (deferredGoalId) {
847
+ this.emitGoalEvent({
848
+ type: 'goal_continuation',
849
+ goalId: deferredGoalId,
850
+ phase: 'deferred',
851
+ reason: `target_${operation} persistence failed`,
852
+ });
853
+ }
854
+ return message;
855
+ }
335
856
  waitForIdle() {
336
857
  return this.activeRun ?? Promise.resolve();
337
858
  }
338
- async runTurn(firstInput) {
339
- let nextInput = firstInput;
340
- while (nextInput?.trim() && !this.stopping) {
341
- if (this.options.echoSubmittedInput ?? true) {
859
+ async runTurn(firstRequest) {
860
+ let nextRequest = firstRequest;
861
+ while (nextRequest && !this.stopping) {
862
+ this.goalProviderReservedTokens = 0;
863
+ if (!this.requestIsCurrent(nextRequest))
864
+ break;
865
+ const request = nextRequest;
866
+ const nextInput = request.text?.trim() ||
867
+ 'Continue pursuing the active goal from its persisted plan and evidence.';
868
+ if ((this.options.echoSubmittedInput ?? true) && request.echoToTranscript && !request.alreadyEchoed) {
342
869
  this.emitAppend(submittedEntry(nextInput));
343
870
  }
344
871
  this.options.beforeTurn?.(nextInput);
345
872
  const turn = this.turnController.beginTurn(nextInput);
873
+ const startedAt = Date.now();
874
+ const workspaceFingerprintBefore = (0, workspace_state_1.captureWorkspaceFingerprint)(this.activeProjectPath());
875
+ const coord = this.ensureGoalCoordinator(false);
876
+ const toolContext = coord
877
+ ? { coordinator: coord, request, turnId: String(turn.id), evidenceRecords: [] }
878
+ : undefined;
346
879
  this.options.runtime.store.setProcessing(true);
347
880
  this.emitProcessing(true);
348
881
  const runningStatus = statusText(this.options.runningStatus, nextInput);
349
882
  if (runningStatus)
350
883
  this.emitStatus(runningStatus);
351
884
  try {
352
- await this.runner.runInput(nextInput, { abortSignal: turn.abortSignal, turnId: turn.id });
885
+ const execute = () => {
886
+ if (this.runner.runRequest) {
887
+ return this.runner.runRequest(request, {
888
+ abortSignal: turn.abortSignal,
889
+ turnId: turn.id,
890
+ });
891
+ }
892
+ return this.runner.runInput(nextInput, {
893
+ abortSignal: turn.abortSignal,
894
+ turnId: turn.id,
895
+ persistAsUserMessage: request.persistAsUserMessage,
896
+ inputKind: request.inputKind,
897
+ });
898
+ };
899
+ if (toolContext) {
900
+ await (0, tools_1.runWithGoalToolContext)(toolContext, execute);
901
+ }
902
+ else {
903
+ await execute();
904
+ }
353
905
  }
354
906
  catch (error) {
355
907
  if (!turn.abortSignal.aborted) {
@@ -365,13 +917,37 @@ class AgentRuntimeController {
365
917
  finally {
366
918
  const revision = this.turnController.finishTurn(turn.id);
367
919
  // v0.2.26: finalize goal turn with usage data.
368
- this.finalizeGoalTurn(turn.id);
920
+ this.finalizeGoalTurn(turn.id, request, toolContext, startedAt, workspaceFingerprintBefore, turn.abortSignal.aborted);
369
921
  if (revision?.trim()) {
370
- this.emitStatus(this.options.restartingStatus ?? 'Restarting with latest instruction...');
371
- nextInput = revision;
922
+ const currentCoord = this.ensureGoalCoordinator(false);
923
+ const currentGoal = currentCoord?.goal?.status === 'active' ? currentCoord.goal : undefined;
924
+ if (request.goal && currentCoord?.goal && !currentGoal) {
925
+ this.emitStatus('Latest instruction was saved, but the Goal is paused because usage accounting is incomplete. Use /target resume after reviewing the budget.');
926
+ nextRequest = undefined;
927
+ }
928
+ else {
929
+ this.emitStatus(this.options.restartingStatus ?? '根据补充调整方向中…');
930
+ nextRequest = {
931
+ inputKind: 'revision',
932
+ text: revision,
933
+ sessionId: this.options.runtime.getSession()?.id ?? request.sessionId,
934
+ goal: currentGoal
935
+ ? {
936
+ goalId: currentGoal.goalId,
937
+ revision: currentGoal.revision,
938
+ continuationIndex: currentGoal.continuationCount,
939
+ }
940
+ : undefined,
941
+ persistAsUserMessage: true,
942
+ echoToTranscript: true,
943
+ // v0.1.3 (G1): the revision was already echoed at submission time.
944
+ alreadyEchoed: true,
945
+ generation: currentCoord?.generation ?? 0,
946
+ };
947
+ }
372
948
  }
373
949
  else {
374
- nextInput = undefined;
950
+ nextRequest = undefined;
375
951
  }
376
952
  }
377
953
  }
@@ -392,7 +968,11 @@ class AgentRuntimeController {
392
968
  }
393
969
  else {
394
970
  const message = error instanceof Error ? error.message : String(error);
395
- this.emitAppend({ role: 'error', content: `[RUNTIME] Error: ${message}`, errorLayer: 'runtime' });
971
+ this.emitAppend({
972
+ role: 'error',
973
+ content: `[RUNTIME] Error: ${message}`,
974
+ errorLayer: 'runtime',
975
+ });
396
976
  }
397
977
  this.options.runtime.store.setProcessing(false);
398
978
  this.emitProcessing(false);
@@ -412,6 +992,131 @@ class AgentRuntimeController {
412
992
  emitProcessing(processing) {
413
993
  this.eventSink.emit({ type: 'processing_changed', processing });
414
994
  }
995
+ emitGoalEvent(event) {
996
+ this.eventSink.emit({ type: 'goal_event', event });
997
+ }
998
+ requestIsCurrent(request) {
999
+ const activeSessionId = this.options.runtime.getSession()?.id;
1000
+ if (activeSessionId && request.sessionId !== activeSessionId)
1001
+ return false;
1002
+ if (!request.goal)
1003
+ return true;
1004
+ const coord = this.goalCoordinator;
1005
+ const goal = coord?.goal;
1006
+ return Boolean(coord &&
1007
+ goal &&
1008
+ coord.boundSessionId === request.sessionId &&
1009
+ coord.generation === request.generation &&
1010
+ goal.goalId === request.goal.goalId &&
1011
+ goal.revision === request.goal.revision &&
1012
+ goal.status === 'active');
1013
+ }
1014
+ verificationSummary(loopStats) {
1015
+ if (!loopStats)
1016
+ return 'No loop statistics were recorded.';
1017
+ const passed = loopStats.verificationPassedCommands ?? [];
1018
+ const failed = loopStats.verificationFailedCommands ?? [];
1019
+ const missing = loopStats.verificationMissingCommands ?? [];
1020
+ return [
1021
+ `finish=${loopStats.finishReason}`,
1022
+ passed.length ? `passed=${passed.join(', ')}` : '',
1023
+ failed.length ? `failed=${failed.join(', ')}` : '',
1024
+ missing.length ? `missing=${missing.join(', ')}` : '',
1025
+ ]
1026
+ .filter(Boolean)
1027
+ .join('; ');
1028
+ }
1029
+ captureGoalEvidence(event) {
1030
+ if (event.type !== 'tool_finished')
1031
+ return;
1032
+ const context = (0, tools_1.currentGoalToolContext)();
1033
+ const goal = context?.coordinator.goal;
1034
+ if (!context || !goal || goal.status !== 'active')
1035
+ return;
1036
+ const capturedAt = Date.now();
1037
+ const claimedAssertion = event.event.externalAssertion;
1038
+ const assertion = claimedAssertion &&
1039
+ (0, external_assertion_1.externalAssertionMatchesInvocation)({
1040
+ assertion: claimedAssertion,
1041
+ name: event.event.name,
1042
+ args: event.event.args,
1043
+ success: event.event.success,
1044
+ skipped: event.event.skipped,
1045
+ now: capturedAt,
1046
+ })
1047
+ ? claimedAssertion
1048
+ : undefined;
1049
+ const kind = claimedAssertion
1050
+ ? 'external'
1051
+ : (0, evidence_1.classifyGoalEvidenceKind)(event.event.name, event.event.args);
1052
+ if (!kind)
1053
+ return;
1054
+ const classifiedResult = (0, evidence_1.classifyGoalEvidenceResult)({
1055
+ kind,
1056
+ success: event.event.success,
1057
+ skipped: event.event.skipped,
1058
+ summary: event.event.summary,
1059
+ error: event.event.error,
1060
+ });
1061
+ // A successful transport plus display text is never sufficient proof of
1062
+ // an external mutation/state. Only runtime-derived typed assertions may
1063
+ // promote successful external tool output to passed evidence. Explicit
1064
+ // tool failures remain failed so the ledger still records negative proof.
1065
+ const result = assertion
1066
+ ? assertion.status
1067
+ : kind === 'external' && classifiedResult === 'passed'
1068
+ ? 'inconclusive'
1069
+ : classifiedResult;
1070
+ const assertionSubject = assertion
1071
+ ? [
1072
+ `external ${assertion.action} ${assertion.status}`,
1073
+ `provider=${assertion.provider}`,
1074
+ `target=${assertion.target}`,
1075
+ assertion.observedValue ? `observed=${assertion.observedValue}` : '',
1076
+ ]
1077
+ .filter(Boolean)
1078
+ .join(' ')
1079
+ : undefined;
1080
+ const record = {
1081
+ id: `evidence:${(0, crypto_1.randomUUID)()}`,
1082
+ goalId: goal.goalId,
1083
+ goalRevision: goal.revision,
1084
+ objectiveRevision: goal.contract?.objectiveRevision ?? 0,
1085
+ turnId: context.turnId,
1086
+ kind,
1087
+ subject: (0, redaction_1.redactTraceText)(assertionSubject || event.event.summary || event.event.error || event.event.name).slice(0, 512),
1088
+ result,
1089
+ sourceRef: `tool:${event.event.callId}:${event.event.name}`,
1090
+ capturedAt,
1091
+ workspaceFingerprint: (0, workspace_state_1.captureWorkspaceFingerprint)(this.activeProjectPath()),
1092
+ expiresAt: kind === 'external' ? capturedAt + 5 * 60000 : undefined,
1093
+ ...(assertion ? { externalAssertion: assertion } : {}),
1094
+ redacted: true,
1095
+ };
1096
+ context.evidenceRecords.push(record);
1097
+ this.emitGoalEvent({
1098
+ type: 'goal_evidence_recorded',
1099
+ goalId: goal.goalId,
1100
+ evidence: {
1101
+ id: record.id,
1102
+ kind: record.kind,
1103
+ result: record.result,
1104
+ subject: record.subject,
1105
+ },
1106
+ });
1107
+ }
1108
+ restoreGoalForSession(sessionId, projectPath) {
1109
+ // A continuation queued by the previously active session must not survive a
1110
+ // session switch. It captured the old coordinator and would otherwise be
1111
+ // able to start after /resume has rebound the runtime.
1112
+ this.continuationScheduleEpoch += 1;
1113
+ this.goalCoordinator = null;
1114
+ this.goalCoordinatorSessionId = null;
1115
+ const coord = this.bindGoalCoordinator(sessionId, true, projectPath);
1116
+ const snapshot = coord?.snapshot();
1117
+ if (snapshot)
1118
+ this.emitGoalEvent({ type: 'goal_restored', goal: snapshot });
1119
+ }
415
1120
  async requestToolPermission(request) {
416
1121
  if (request.abortSignal?.aborted || this.stopping)
417
1122
  return false;
@@ -441,72 +1146,112 @@ class AgentRuntimeController {
441
1146
  const coord = this.goalCoordinator;
442
1147
  if (!coord)
443
1148
  return { type: 'empty' };
444
- switch (input.action) {
445
- case 'show':
446
- // Just shows status — already handled by the renderer format function.
447
- return { type: 'empty' };
448
- case 'create': {
449
- const obj = input.payload?.objective;
450
- if (!obj)
1149
+ let executionRevoked = false;
1150
+ const goalIdBeforeMutation = coord.goal?.goalId;
1151
+ try {
1152
+ switch (input.action) {
1153
+ case 'show':
1154
+ // Just shows status — already handled by the renderer format function.
451
1155
  return { type: 'empty' };
452
- const result = coord.create(obj);
453
- return { type: result.ok ? 'interrupted' : 'empty' };
454
- }
455
- case 'pause':
456
- coord.pause();
457
- return { type: 'interrupted' };
458
- case 'resume':
459
- coord.resume();
460
- return { type: 'interrupted' };
461
- case 'edit': {
462
- const obj = input.payload?.objective;
463
- if (obj)
464
- coord.edit(obj);
465
- return { type: 'interrupted' };
466
- }
467
- case 'replace': {
468
- const obj = input.payload?.objective;
469
- if (obj)
470
- coord.replace(obj);
471
- return { type: 'interrupted' };
472
- }
473
- case 'set_budget':
474
- coord.setBudget(input.payload?.tokenBudget ?? null);
475
- return { type: 'interrupted' };
476
- case 'clear':
477
- if (input.payload?.confirmed) {
478
- coord.clear();
1156
+ case 'create': {
1157
+ const obj = input.payload?.objective;
1158
+ if (!obj)
1159
+ return { type: 'empty' };
1160
+ const result = coord.create(obj);
1161
+ return { type: result.ok ? 'interrupted' : 'empty' };
1162
+ }
1163
+ case 'pause':
1164
+ if (coord.isActive) {
1165
+ this.abortGoalOwnedExecution();
1166
+ executionRevoked = true;
1167
+ }
1168
+ return { type: coord.pause() ? 'interrupted' : 'empty' };
1169
+ case 'resume':
1170
+ return { type: coord.resume() ? 'interrupted' : 'empty' };
1171
+ case 'confirm':
1172
+ coord.confirmCriterion(input.payload?.criterionId ?? '');
1173
+ return { type: 'interrupted' };
1174
+ case 'edit': {
1175
+ const obj = input.payload?.objective;
1176
+ if (obj)
1177
+ coord.edit(obj);
1178
+ return { type: 'interrupted' };
1179
+ }
1180
+ case 'replace': {
1181
+ const obj = input.payload?.objective;
1182
+ if (obj && !coord.replace(obj)) {
1183
+ const message = this.failClosedGoalMutation(coord, input.action, new Error('Target could not be replaced.'), executionRevoked, goalIdBeforeMutation);
1184
+ this.emitGoalMutationError(message);
1185
+ }
1186
+ return { type: 'interrupted' };
479
1187
  }
480
- return { type: 'interrupted' };
481
- default:
482
- return { type: 'empty' };
1188
+ case 'set_budget':
1189
+ coord.setBudget(input.payload?.tokenBudget ?? null);
1190
+ return { type: 'interrupted' };
1191
+ case 'clear':
1192
+ if (input.payload?.confirmed && coord.goal !== null) {
1193
+ this.abortGoalOwnedExecution();
1194
+ executionRevoked = true;
1195
+ if (!coord.clear()) {
1196
+ const message = this.failClosedGoalMutation(coord, input.action, new Error('Target clear did not remove the active Goal.'), executionRevoked, goalIdBeforeMutation);
1197
+ this.emitGoalMutationError(message);
1198
+ }
1199
+ }
1200
+ return { type: 'interrupted' };
1201
+ default:
1202
+ return { type: 'empty' };
1203
+ }
1204
+ }
1205
+ catch (cause) {
1206
+ const message = this.failClosedGoalMutation(coord, input.action, cause, executionRevoked, goalIdBeforeMutation);
1207
+ this.emitGoalMutationError(message);
1208
+ return { type: 'interrupted' };
483
1209
  }
484
1210
  }
1211
+ emitGoalMutationError(message) {
1212
+ this.emitAppend({
1213
+ role: 'error',
1214
+ title: 'target',
1215
+ content: message,
1216
+ errorLayer: 'runtime',
1217
+ });
1218
+ this.emitStatus(message);
1219
+ }
485
1220
  ensureGoalCoordinator(ensureSession) {
486
- if (this.goalCoordinator && this.goalCoordinatorSessionId === '*') {
487
- return this.goalCoordinator;
488
- }
489
1221
  if (ensureSession) {
490
1222
  this.options.runtime.ensureSession();
491
1223
  }
492
- const sessionId = this.options.runtime.getSession()?.id;
1224
+ const session = this.options.runtime.getSession();
1225
+ const sessionId = session?.id;
493
1226
  if (!sessionId)
494
1227
  return this.goalCoordinator;
495
1228
  if (this.goalCoordinator && this.goalCoordinatorSessionId === sessionId) {
496
1229
  return this.goalCoordinator;
497
1230
  }
1231
+ return this.bindGoalCoordinator(sessionId, true, session.projectPath || this.options.runtime.cwd);
1232
+ }
1233
+ bindGoalCoordinator(sessionId, recoverActive, projectPath) {
498
1234
  const { GoalCoordinator } = require('./goals/coordinator');
499
- const coord = new GoalCoordinator(this.options.runtime.cwd, sessionId);
500
- coord.load();
1235
+ const coord = new GoalCoordinator(projectPath, sessionId);
1236
+ coord.load(recoverActive);
1237
+ if (coord.lastLoadIssue) {
1238
+ this.emitAppend({
1239
+ role: 'error',
1240
+ title: 'target recovery',
1241
+ content: `${coord.lastLoadIssue.code}: ${coord.lastLoadIssue.message}. Run orion doctor for recovery guidance.`,
1242
+ errorLayer: 'runtime',
1243
+ });
1244
+ }
501
1245
  this.goalCoordinator = coord;
502
1246
  this.goalCoordinatorSessionId = sessionId;
503
- const { setGoalToolCoordinator } = require('./goals/tools');
504
- setGoalToolCoordinator(coord);
505
1247
  if ('setGoalCoordinator' in this.runner) {
506
1248
  this.runner.setGoalCoordinator(coord);
507
1249
  }
508
1250
  return coord;
509
1251
  }
1252
+ activeProjectPath() {
1253
+ return this.options.runtime.getSession()?.projectPath || this.options.runtime.cwd;
1254
+ }
510
1255
  formatTargetStatus(coord) {
511
1256
  const goal = coord.goal;
512
1257
  if (!goal) {
@@ -514,7 +1259,33 @@ class AgentRuntimeController {
514
1259
  }
515
1260
  const objective = goal.objective.length > 60 ? `${goal.objective.slice(0, 57)}...` : goal.objective;
516
1261
  const tokens = goal.tokensUsed >= 1000 ? `${(goal.tokensUsed / 1000).toFixed(1)}K` : String(goal.tokensUsed);
517
- return `Target: [${goal.status}] ${objective} | ${goal.continuationCount} turns | ${tokens} tokens`;
1262
+ const lines = [
1263
+ `Target: [${goal.status}] ${objective} | ${goal.continuationCount} turns | ${tokens} tokens${goal.tokenBudget ? ` | budget ${tokens}/${goal.tokenBudget}` : ''}`,
1264
+ ];
1265
+ const contract = goal.contract;
1266
+ const plan = contract?.planSnapshot;
1267
+ if (plan) {
1268
+ lines.push(`Plan: r${plan.revision} ${plan.phase}${plan.nextAction ? ` | next: ${plan.nextAction}` : ''}`);
1269
+ }
1270
+ if (contract?.successCriteria.length) {
1271
+ const passed = contract.successCriteria.filter(criterion => criterion.status === 'passed').length;
1272
+ const failed = contract.successCriteria.filter(criterion => criterion.status === 'failed').length;
1273
+ const stale = contract.successCriteria.filter(criterion => criterion.status === 'stale').length;
1274
+ const pending = contract.successCriteria.length - passed - failed - stale;
1275
+ lines.push(`Criteria: ${pending} pending | ${passed}/${contract.successCriteria.length} passed | ${failed} failed | ${stale} stale`);
1276
+ }
1277
+ if (goal.completionAudit?.remainingRequirements.length) {
1278
+ lines.push(`Audit: ${goal.completionAudit.remainingRequirements.slice(0, 3).join(' | ')}`);
1279
+ }
1280
+ if (goal.stopReason)
1281
+ lines.push(`Resume: ${goal.stopReason.message}`);
1282
+ const recentEvidence = (goal.evidenceLedger ?? []).slice(-3);
1283
+ if (recentEvidence.length > 0) {
1284
+ lines.push(`Evidence: ${recentEvidence
1285
+ .map(item => `${item.id} ${item.kind}/${item.result} ${item.subject}`)
1286
+ .join(' | ')}`);
1287
+ }
1288
+ return lines.join('\n');
518
1289
  }
519
1290
  recordPermissionDecision(requestId, approved) {
520
1291
  const resolve = this.pendingPermissions.get(requestId);
@@ -523,6 +1294,19 @@ class AgentRuntimeController {
523
1294
  resolve(approved);
524
1295
  return { type: 'permission_decision_recorded' };
525
1296
  }
1297
+ applyPermissionModeChange(value) {
1298
+ const allowed = ['allow', 'ask', 'deny'];
1299
+ if (!allowed.includes(value))
1300
+ return { type: 'permission_mode_invalid' };
1301
+ // Persist to disk so the change survives restart...
1302
+ (0, global_config_1.updateGlobalConfig)({ toolConfirmation: value });
1303
+ // ...and mutate the live runtime config so the very next tool call uses it
1304
+ // immediately (chat-controller passes this.runtime.config.toolConfirmation into
1305
+ // the scheduler on every turn).
1306
+ this.options.runtime.config.toolConfirmation = value;
1307
+ this.emitStatus(`Tool confirmation → ${value}`);
1308
+ return { type: 'permission_mode_changed' };
1309
+ }
526
1310
  rejectPendingPermissions() {
527
1311
  const pending = [...this.pendingPermissions.values()];
528
1312
  this.pendingPermissions.clear();
@@ -545,7 +1329,9 @@ class AgentRuntimeController {
545
1329
  };
546
1330
  chatOptions.uiCapabilities = uiCapabilities;
547
1331
  chatOptions.uiRenderer = chatOptions.uiRenderer ?? resolvedRenderer;
548
- chatOptions.onVerificationStateChange = chatOptions.onVerificationStateChange ?? (state => this.turnController.setVerificationState(state));
1332
+ chatOptions.onVerificationStateChange =
1333
+ chatOptions.onVerificationStateChange ??
1334
+ (state => this.turnController.setVerificationState(state));
549
1335
  if (this.options.useRuntimeToolPermissions && !chatOptions.confirmToolUse) {
550
1336
  chatOptions.confirmToolUse = request => this.requestToolPermission(request);
551
1337
  }
@@ -572,6 +1358,46 @@ class AgentRuntimeController {
572
1358
  });
573
1359
  };
574
1360
  }
1361
+ const configuredPreflight = chatOptions.beforeProviderRequest;
1362
+ chatOptions.beforeProviderRequest = async (context) => {
1363
+ if (configuredPreflight) {
1364
+ const configuredDecision = await configuredPreflight(context);
1365
+ if (!configuredDecision.available)
1366
+ return configuredDecision;
1367
+ }
1368
+ const coord = this.goalCoordinator;
1369
+ const goal = coord?.goal;
1370
+ if (!coord || !goal || goal.status !== 'active' || !goal.tokenBudget) {
1371
+ return { available: true };
1372
+ }
1373
+ const projectedTokens = Math.max(1, context.estimatedPromptTokens, goal.lastTurn?.totalTokens ?? 0);
1374
+ const decision = (0, accounting_1.budgetPreflight)(goal.tokensUsed + this.goalProviderReservedTokens, goal.tokenBudget, projectedTokens);
1375
+ if (!decision.available) {
1376
+ try {
1377
+ coord.limitBudget(decision.reason ?? 'Token budget unavailable for provider request.');
1378
+ }
1379
+ catch (cause) {
1380
+ const message = this.failClosedGoalMutation(coord, 'provider_budget_stop', cause, false, goal.goalId);
1381
+ this.emitGoalMutationError(message);
1382
+ return { available: false, reason: message };
1383
+ }
1384
+ const snapshot = coord.snapshot();
1385
+ if (snapshot) {
1386
+ this.emitGoalEvent({
1387
+ type: 'goal_updated',
1388
+ goal: snapshot,
1389
+ reason: `provider request ${context.operation} attempt ${context.attempt} rejected by token budget`,
1390
+ });
1391
+ }
1392
+ return decision;
1393
+ }
1394
+ // Reserve the projected cost before the network call. Failed retries and
1395
+ // fallbacks keep their reservation because providers may charge attempts
1396
+ // that fail before returning usage. Actual successful usage is persisted
1397
+ // at turn finalization; reservations reset only when the next root turn starts.
1398
+ this.goalProviderReservedTokens += projectedTokens;
1399
+ return decision;
1400
+ };
575
1401
  return Object.keys(chatOptions).length > 0 ? chatOptions : undefined;
576
1402
  }
577
1403
  }