@gajae-code/coding-agent 0.11.0 → 0.11.2

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 (418) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/dist/types/async/job-manager.d.ts +46 -7
  3. package/dist/types/cli/config-cli.d.ts +11 -1
  4. package/dist/types/cli/notify-cli.d.ts +1 -0
  5. package/dist/types/cli/session-picker.d.ts +2 -1
  6. package/dist/types/cli/setup-cli.d.ts +1 -1
  7. package/dist/types/commands/mcp-serve.d.ts +33 -0
  8. package/dist/types/commands/mcp.d.ts +1 -1
  9. package/dist/types/commands/notify.d.ts +0 -2
  10. package/dist/types/commands/sdk.d.ts +1 -1
  11. package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
  12. package/dist/types/config/file-lock.d.ts +14 -4
  13. package/dist/types/config/keybindings.d.ts +42 -1
  14. package/dist/types/config/model-auth.d.ts +3 -0
  15. package/dist/types/config/model-bindings-applier.d.ts +13 -0
  16. package/dist/types/config/model-discovery-manager.d.ts +49 -0
  17. package/dist/types/config/model-equivalence.d.ts +20 -0
  18. package/dist/types/config/model-registry.d.ts +16 -15
  19. package/dist/types/config/model-resolver.d.ts +28 -18
  20. package/dist/types/config/models-config-schema.d.ts +11 -0
  21. package/dist/types/config/settings-schema.d.ts +56 -1
  22. package/dist/types/config/settings.d.ts +14 -11
  23. package/dist/types/coordinator-mcp/policy.d.ts +5 -0
  24. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
  26. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  27. package/dist/types/discovery/helpers.d.ts +0 -6
  28. package/dist/types/edit/index.d.ts +3 -0
  29. package/dist/types/eval/py/env.d.ts +9 -0
  30. package/dist/types/extensibility/extensions/loader.d.ts +2 -1
  31. package/dist/types/extensibility/extensions/runner.d.ts +2 -2
  32. package/dist/types/extensibility/extensions/types.d.ts +34 -3
  33. package/dist/types/extensibility/hooks/runner.d.ts +1 -1
  34. package/dist/types/extensibility/shared-events.d.ts +2 -0
  35. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  36. package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
  37. package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
  38. package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
  39. package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
  40. package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
  41. package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
  42. package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
  43. package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
  44. package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
  45. package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
  46. package/dist/types/gjc-runtime/team-store.d.ts +160 -0
  47. package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
  48. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
  49. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
  50. package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
  51. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
  52. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
  53. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
  54. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
  55. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  56. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  57. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  58. package/dist/types/goals/runtime.d.ts +1 -0
  59. package/dist/types/goals/state.d.ts +8 -0
  60. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  61. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  62. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  63. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  64. package/dist/types/hindsight/state.d.ts +11 -4
  65. package/dist/types/lsp/lspmux.d.ts +5 -3
  66. package/dist/types/lsp/path-trust.d.ts +1 -0
  67. package/dist/types/main.d.ts +3 -2
  68. package/dist/types/memories/storage.d.ts +1 -0
  69. package/dist/types/modes/action-registry.d.ts +29 -0
  70. package/dist/types/modes/components/chrome.d.ts +14 -0
  71. package/dist/types/modes/components/command-palette.d.ts +27 -0
  72. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  73. package/dist/types/modes/components/index.d.ts +2 -1
  74. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  75. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  76. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  77. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  78. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  79. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  80. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  81. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  82. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  83. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  84. package/dist/types/modes/components/status-line.d.ts +1 -94
  85. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  86. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  87. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  88. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  89. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  90. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  91. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
  92. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  93. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  94. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  95. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  96. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  97. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  98. package/dist/types/modes/interactive-mode.d.ts +21 -19
  99. package/dist/types/modes/jobs-observer.d.ts +2 -0
  100. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  101. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
  102. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  103. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  104. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  105. package/dist/types/modes/types.d.ts +22 -15
  106. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  107. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  108. package/dist/types/sdk/broker/broker.d.ts +78 -12
  109. package/dist/types/sdk/broker/ensure.d.ts +9 -0
  110. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  111. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  112. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  113. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  114. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  115. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  116. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  117. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  118. package/dist/types/sdk/bus/config.d.ts +11 -0
  119. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  120. package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
  121. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  122. package/dist/types/sdk/bus/index.d.ts +27 -0
  123. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  124. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  125. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  126. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  127. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  128. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  129. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  130. package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
  131. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  132. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  133. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  134. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  135. package/dist/types/sdk/host/host.d.ts +2 -0
  136. package/dist/types/sdk/index.d.ts +1 -0
  137. package/dist/types/sdk/session-directory.d.ts +56 -0
  138. package/dist/types/session/agent-session.d.ts +82 -47
  139. package/dist/types/session/agent-storage.d.ts +7 -0
  140. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  141. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  142. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  143. package/dist/types/session/messages.d.ts +16 -0
  144. package/dist/types/session/session-manager-internal.d.ts +14 -0
  145. package/dist/types/session/session-manager.d.ts +82 -12
  146. package/dist/types/session/session-storage.d.ts +43 -3
  147. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  148. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  149. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  150. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  151. package/dist/types/system-prompt.d.ts +2 -11
  152. package/dist/types/task/executor.d.ts +3 -1
  153. package/dist/types/thinking.d.ts +7 -0
  154. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  155. package/dist/types/tools/ask.d.ts +64 -2
  156. package/dist/types/tools/bash.d.ts +3 -0
  157. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  158. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  159. package/dist/types/tools/cron.d.ts +2 -0
  160. package/dist/types/tools/fetch.d.ts +1 -0
  161. package/dist/types/tools/index.d.ts +30 -15
  162. package/dist/types/tools/output-meta.d.ts +1 -0
  163. package/dist/types/tools/read.d.ts +3 -0
  164. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  165. package/dist/types/tools/subagent-render.d.ts +1 -0
  166. package/dist/types/tools/subagent.d.ts +6 -1
  167. package/dist/types/utils/event-bus.d.ts +1 -1
  168. package/dist/types/web/search/index.d.ts +3 -1
  169. package/package.json +21 -9
  170. package/scripts/build-sdk-package-smoke.ts +39 -1
  171. package/scripts/generate-hotkeys-docs.ts +74 -0
  172. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  173. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  174. package/src/async/job-manager.ts +455 -69
  175. package/src/cli/config-cli.ts +75 -2
  176. package/src/cli/notify-cli.ts +92 -26
  177. package/src/cli/session-picker.ts +20 -3
  178. package/src/cli/setup-cli.ts +1 -1
  179. package/src/cli.ts +1 -1
  180. package/src/commands/config.ts +1 -1
  181. package/src/commands/harness.ts +34 -106
  182. package/src/commands/launch.ts +6 -4
  183. package/src/commands/mcp-serve.ts +85 -8
  184. package/src/commands/mcp.ts +2 -2
  185. package/src/commands/notify.ts +8 -4
  186. package/src/commands/sdk.ts +61 -40
  187. package/src/commands/ultragoal.ts +4 -1
  188. package/src/config/atomic-yaml-patch.ts +80 -13
  189. package/src/config/file-lock.ts +105 -43
  190. package/src/config/keybindings.ts +64 -20
  191. package/src/config/model-auth.ts +9 -0
  192. package/src/config/model-bindings-applier.ts +139 -0
  193. package/src/config/model-discovery-manager.ts +224 -0
  194. package/src/config/model-equivalence.ts +66 -0
  195. package/src/config/model-registry.ts +169 -302
  196. package/src/config/model-resolver.ts +361 -369
  197. package/src/config/models-config-schema.ts +5 -4
  198. package/src/config/settings-schema.ts +159 -2
  199. package/src/config/settings.ts +458 -482
  200. package/src/coordinator-mcp/policy.ts +76 -11
  201. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  202. package/src/coordinator-mcp/question-state.ts +778 -0
  203. package/src/coordinator-mcp/server.ts +1702 -238
  204. package/src/debug/index.ts +1 -1
  205. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  206. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  207. package/src/discovery/helpers.ts +1 -50
  208. package/src/edit/index.ts +64 -0
  209. package/src/eval/py/env.ts +26 -0
  210. package/src/eval/py/kernel.ts +5 -3
  211. package/src/extensibility/extensions/loader.ts +9 -1
  212. package/src/extensibility/extensions/runner.ts +12 -3
  213. package/src/extensibility/extensions/types.ts +39 -1
  214. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  215. package/src/extensibility/hooks/runner.ts +2 -2
  216. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  217. package/src/extensibility/shared-events.ts +2 -0
  218. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  219. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  220. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  221. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  222. package/src/gjc-runtime/launch-tmux.ts +2 -5
  223. package/src/gjc-runtime/launch-worktree.ts +5 -5
  224. package/src/gjc-runtime/linux-proc.ts +71 -0
  225. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  226. package/src/gjc-runtime/session-resolution.ts +18 -3
  227. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  228. package/src/gjc-runtime/state-migrations.ts +1 -1
  229. package/src/gjc-runtime/state-runtime.ts +320 -249
  230. package/src/gjc-runtime/state-writer.ts +36 -40
  231. package/src/gjc-runtime/team-launch.ts +319 -0
  232. package/src/gjc-runtime/team-notify.ts +326 -0
  233. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  234. package/src/gjc-runtime/team-store.ts +828 -0
  235. package/src/gjc-runtime/team-workers.ts +962 -0
  236. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  237. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  238. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  239. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  240. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  241. package/src/gjc-runtime/ultragoal-guard.ts +83 -12
  242. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  243. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
  244. package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
  245. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  246. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  247. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  248. package/src/goals/continuation-timeout-guard.ts +79 -0
  249. package/src/goals/runtime.ts +7 -5
  250. package/src/goals/state.ts +18 -0
  251. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  252. package/src/harness-control-plane/phase-rollup.ts +4 -7
  253. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  254. package/src/harness-control-plane/receipt-spool.ts +51 -7
  255. package/src/harness-control-plane/session-lease.ts +12 -130
  256. package/src/hindsight/backend.ts +26 -27
  257. package/src/hindsight/state.ts +101 -32
  258. package/src/hooks/skill-state.ts +17 -11
  259. package/src/internal-urls/docs-index.generated.ts +21 -18
  260. package/src/lsp/client.ts +1 -1
  261. package/src/lsp/config.ts +145 -26
  262. package/src/lsp/index.ts +1 -1
  263. package/src/lsp/lspmux.ts +39 -10
  264. package/src/lsp/path-trust.ts +81 -0
  265. package/src/main.ts +28 -8
  266. package/src/memories/index.ts +1 -0
  267. package/src/memories/storage.ts +6 -2
  268. package/src/modes/action-registry.ts +165 -0
  269. package/src/modes/components/chrome.ts +40 -0
  270. package/src/modes/components/command-palette.ts +168 -0
  271. package/src/modes/components/custom-editor.ts +1 -1
  272. package/src/modes/components/custom-provider-wizard.ts +51 -14
  273. package/src/modes/components/index.ts +2 -1
  274. package/src/modes/components/pet-selector.ts +11 -17
  275. package/src/modes/components/plan-preview-overlay.ts +295 -0
  276. package/src/modes/components/queue-mode-selector.ts +10 -27
  277. package/src/modes/components/queue-pane.ts +79 -0
  278. package/src/modes/components/read-tool-group.ts +13 -0
  279. package/src/modes/components/session-observer-overlay.ts +207 -772
  280. package/src/modes/components/sessions-dashboard.ts +181 -0
  281. package/src/modes/components/settings-selector.ts +1 -1
  282. package/src/modes/components/show-images-selector.ts +10 -24
  283. package/src/modes/components/status-line/types.ts +1 -1
  284. package/src/modes/components/status-line.ts +1 -969
  285. package/src/modes/components/tasks-pane.ts +73 -0
  286. package/src/modes/components/theme-selector.ts +11 -31
  287. package/src/modes/components/tool-execution.ts +22 -0
  288. package/src/modes/components/tool-status-header.ts +1056 -0
  289. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  290. package/src/modes/controllers/btw-controller.ts +1 -0
  291. package/src/modes/controllers/command-controller.ts +9 -15
  292. package/src/modes/controllers/event-controller.ts +3 -2
  293. package/src/modes/controllers/extension-ui-controller.ts +72 -25
  294. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  295. package/src/modes/controllers/input-controller.ts +764 -229
  296. package/src/modes/controllers/mode-gate.ts +24 -0
  297. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  298. package/src/modes/controllers/selector-controller.ts +225 -10
  299. package/src/modes/controllers/stt-controller.ts +125 -0
  300. package/src/modes/interactive-mode.ts +302 -1151
  301. package/src/modes/jobs-observer.ts +3 -0
  302. package/src/modes/runtime-init.ts +8 -0
  303. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  304. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
  305. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  306. package/src/modes/tasks-aggregator.ts +199 -0
  307. package/src/modes/transcript-item-registry.ts +229 -0
  308. package/src/modes/types.ts +47 -20
  309. package/src/modes/utils/ui-helpers.ts +17 -9
  310. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  311. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  312. package/src/prompts/agents/architect.md +2 -8
  313. package/src/prompts/agents/critic.md +2 -8
  314. package/src/prompts/agents/executor.md +3 -0
  315. package/src/prompts/agents/planner.md +4 -12
  316. package/src/prompts/system/custom-system-prompt.md +1 -23
  317. package/src/prompts/system/plan-mode-approved.md +8 -0
  318. package/src/prompts/system/system-prompt.md +17 -96
  319. package/src/prompts/tools/bash.md +4 -4
  320. package/src/prompts/tools/browser.md +15 -52
  321. package/src/prompts/tools/find.md +2 -2
  322. package/src/prompts/tools/hashline.md +13 -55
  323. package/src/prompts/tools/read.md +3 -6
  324. package/src/prompts/tools/search-tool-bm25.md +0 -2
  325. package/src/prompts/tools/search.md +2 -4
  326. package/src/prompts/tools/skill-discovery.md +1 -2
  327. package/src/prompts/tools/task.md +1 -1
  328. package/src/runtime-mcp/manager.ts +1 -1
  329. package/src/sdk/broker/broker.ts +206 -78
  330. package/src/sdk/broker/discovery.ts +21 -2
  331. package/src/sdk/broker/ensure.ts +85 -25
  332. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  333. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  334. package/src/sdk/broker/lifecycle.ts +1399 -142
  335. package/src/sdk/broker/process-incarnation.ts +5 -13
  336. package/src/sdk/broker/session-index.ts +145 -56
  337. package/src/sdk/broker/state-version.ts +21 -0
  338. package/src/sdk/broker/transport.ts +1 -1
  339. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  340. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  341. package/src/sdk/bus/chat-effect-journal.ts +14 -4
  342. package/src/sdk/bus/config-commands.ts +5 -4
  343. package/src/sdk/bus/config.ts +98 -0
  344. package/src/sdk/bus/conversation-store.ts +5 -29
  345. package/src/sdk/bus/daemon-paths.ts +2 -0
  346. package/src/sdk/bus/discord-daemon.ts +25 -8
  347. package/src/sdk/bus/html-format.ts +20 -3
  348. package/src/sdk/bus/index.ts +834 -113
  349. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  350. package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
  351. package/src/sdk/bus/natives-augment.d.ts +26 -1
  352. package/src/sdk/bus/notification-service.ts +168 -20
  353. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  354. package/src/sdk/bus/recent-activity.ts +188 -74
  355. package/src/sdk/bus/rich-document.ts +258 -0
  356. package/src/sdk/bus/rich-render.ts +62 -70
  357. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  358. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  359. package/src/sdk/bus/telegram-daemon.ts +1491 -247
  360. package/src/sdk/bus/threaded-inbound.ts +8 -3
  361. package/src/sdk/bus/threaded-render.ts +57 -1
  362. package/src/sdk/bus/topic-registry.ts +120 -13
  363. package/src/sdk/host/control/dispatch.ts +14 -2
  364. package/src/sdk/host/control/operations.ts +6 -1
  365. package/src/sdk/host/host.ts +31 -1
  366. package/src/sdk/host/query/handlers.ts +20 -22
  367. package/src/sdk/host/query/revision-store.ts +80 -57
  368. package/src/sdk/index.ts +1 -0
  369. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  370. package/src/sdk/session-directory.ts +141 -0
  371. package/src/sdk/session.ts +458 -479
  372. package/src/session/agent-session.ts +1901 -637
  373. package/src/session/agent-storage.ts +18 -0
  374. package/src/session/cache-economics.ts +25 -8
  375. package/src/session/fallback-chain-controller.ts +53 -3
  376. package/src/session/internal/managed-session-scope.ts +2226 -0
  377. package/src/session/internal/managed-session-storage.ts +453 -0
  378. package/src/session/messages.ts +91 -2
  379. package/src/session/session-manager-internal.ts +33 -0
  380. package/src/session/session-manager.ts +1112 -333
  381. package/src/session/session-storage.ts +444 -39
  382. package/src/session/volatile-context-pruning.ts +78 -0
  383. package/src/setup/credential-auto-import.ts +2 -1
  384. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  385. package/src/setup/provider-onboarding.ts +36 -61
  386. package/src/setup/provider-presets.json +5 -0
  387. package/src/skill-state/active-state.ts +3 -13
  388. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  389. package/src/skill-state/workflow-state-contract.ts +6 -41
  390. package/src/slash-commands/builtin-registry.ts +46 -11
  391. package/src/system-prompt.ts +37 -44
  392. package/src/task/agents.ts +19 -1
  393. package/src/task/executor.ts +90 -6
  394. package/src/task/index.ts +62 -37
  395. package/src/thinking.ts +21 -6
  396. package/src/tool-discovery/tool-index.ts +2 -134
  397. package/src/tools/ask.ts +157 -21
  398. package/src/tools/ast-edit.ts +1 -1
  399. package/src/tools/bash.ts +38 -0
  400. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  401. package/src/tools/browser/tab-supervisor.ts +282 -36
  402. package/src/tools/computer.ts +10 -3
  403. package/src/tools/cron.ts +30 -16
  404. package/src/tools/eval.ts +1 -2
  405. package/src/tools/fetch.ts +11 -4
  406. package/src/tools/index.ts +76 -24
  407. package/src/tools/output-meta.ts +13 -5
  408. package/src/tools/read.ts +37 -1
  409. package/src/tools/resource-gc.ts +2 -2
  410. package/src/tools/search-tool-bm25.ts +11 -74
  411. package/src/tools/subagent-render.ts +19 -8
  412. package/src/tools/subagent.ts +84 -32
  413. package/src/utils/event-bus.ts +9 -4
  414. package/src/web/search/index.ts +3 -3
  415. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  416. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  417. package/src/discovery/plugin-dir-roots.ts +0 -28
  418. package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
@@ -1,7 +1,11 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
3
3
  import { resolveGjcSessionForRead, SessionResolutionError } from "./session-resolution";
4
- import { validateDeferredMemberReceiptFresh, validateReceiptFreshBase } from "./ultragoal-receipt-freshness";
4
+ import {
5
+ validateDeferredMemberReceiptFresh,
6
+ validateReceiptFreshBase,
7
+ validateSupersededFinalAggregateReceipt,
8
+ } from "./ultragoal-receipt-freshness";
5
9
  import {
6
10
  getUltragoalPaths,
7
11
  getUltragoalRunCompletionState,
@@ -49,10 +53,18 @@ export interface UltragoalAskBlockDiagnostic {
49
53
  export interface CurrentGoalLike {
50
54
  objective: string;
51
55
  status?: string;
56
+ provenance?: { source: "ultragoal"; runId: string; goalId: string } | { source: "user" };
52
57
  }
53
58
 
54
- function objectiveMatches(currentObjective: string, plan: UltragoalPlan): boolean {
55
- const normalized = currentObjective.trim();
59
+ function objectiveMatches(currentGoal: CurrentGoalLike, plan: UltragoalPlan, sessionId?: string | null): boolean {
60
+ const provenance = currentGoal.provenance;
61
+ if (provenance?.source === "ultragoal") {
62
+ return (
63
+ provenance.runId === sessionId &&
64
+ (provenance.goalId === "aggregate" || plan.goals.some(goal => goal.id === provenance.goalId))
65
+ );
66
+ }
67
+ const normalized = currentGoal.objective.trim();
56
68
  if (!normalized) return false;
57
69
  if (normalized === plan.gjcObjective || normalized === DEFAULT_ULTRAGOAL_OBJECTIVE) return true;
58
70
  if (plan.gjcObjectiveAliases?.some(alias => alias === normalized)) return true;
@@ -156,8 +168,33 @@ function requiredGoals(plan: UltragoalPlan): UltragoalGoal[] {
156
168
  return plan.goals.filter(goal => goal.status !== "superseded");
157
169
  }
158
170
 
171
+ /**
172
+ * Select the goal whose final-aggregate receipt should represent the run.
173
+ * Prefer a receipt that still validates fresh; several goals can hold
174
+ * final-aggregate receipts once plan growth (e.g. `steer add_subgoal`) stales
175
+ * an earlier one and a later checkpoint re-mints. Fall back to the newest
176
+ * holder (array-last) purely for diagnostics when none validates.
177
+ */
178
+ function findFinalAggregateReceiptGoal(
179
+ plan: UltragoalPlan,
180
+ ledger: readonly UltragoalLedgerEvent[],
181
+ ): UltragoalGoal | null {
182
+ const candidates = [...requiredGoals(plan)]
183
+ .reverse()
184
+ .filter(goal => goal.completionVerification?.receiptKind === "final-aggregate");
185
+ if (candidates.length === 0) return null;
186
+ return (
187
+ candidates.find(
188
+ goal =>
189
+ validateCompletionReceipt({ plan, ledger, goal, receiptKind: "final-aggregate" }).state ===
190
+ "active_verified_complete",
191
+ ) ?? candidates[0]!
192
+ );
193
+ }
194
+
159
195
  function findReceiptGoal(
160
196
  plan: UltragoalPlan,
197
+ ledger: readonly UltragoalLedgerEvent[],
161
198
  currentObjective: string,
162
199
  ): { goal: UltragoalGoal; receiptKind: UltragoalReceiptKind } | null {
163
200
  if (
@@ -165,9 +202,7 @@ function findReceiptGoal(
165
202
  currentObjective === DEFAULT_ULTRAGOAL_OBJECTIVE ||
166
203
  plan.gjcObjectiveAliases?.some(alias => alias === currentObjective)
167
204
  ) {
168
- const finalGoal = [...requiredGoals(plan)]
169
- .reverse()
170
- .find(goal => goal.completionVerification?.receiptKind === "final-aggregate");
205
+ const finalGoal = findFinalAggregateReceiptGoal(plan, ledger);
171
206
  return finalGoal ? { goal: finalGoal, receiptKind: "final-aggregate" } : null;
172
207
  }
173
208
  const storyGoal = plan.goals.find(goal => goal.objective === currentObjective);
@@ -300,6 +335,28 @@ export function validateCompletionReceipt(input: {
300
335
  goalId: input.goal.id,
301
336
  };
302
337
  }
338
+ if (
339
+ priorGoal.completionVerification.validationBatch?.role !== "deferred-member" &&
340
+ priorGoal.completionVerification.receiptKind === "final-aggregate"
341
+ ) {
342
+ // A prior goal may hold the run's previous final-aggregate receipt
343
+ // when plan growth staled it and a later checkpoint re-minted the
344
+ // aggregate receipt. Accept it as historical evidence for its own
345
+ // goal instead of demanding an impossible per-goal receipt.
346
+ const supersededDiagnostic = validateSupersededFinalAggregateReceipt({
347
+ ledger: input.ledger,
348
+ goal: priorGoal,
349
+ receipt: priorGoal.completionVerification,
350
+ });
351
+ if (supersededDiagnostic) {
352
+ return {
353
+ state: supersededDiagnostic.state,
354
+ message: `Ultragoal final receipt requires valid historical evidence for ${priorGoal.id}: ${supersededDiagnostic.message}`,
355
+ goalId: input.goal.id,
356
+ };
357
+ }
358
+ continue;
359
+ }
303
360
  const priorDiagnostic =
304
361
  priorGoal.completionVerification.validationBatch?.role === "deferred-member"
305
362
  ? validateDeferredMemberReceiptFresh({
@@ -341,6 +398,8 @@ export async function readUltragoalVerificationState(input: {
341
398
  currentGoal?: CurrentGoalLike | null;
342
399
  sessionId?: string | null;
343
400
  }): Promise<UltragoalGuardDiagnostic> {
401
+ const currentGoal = input.currentGoal;
402
+
344
403
  const currentObjective = input.currentGoal?.objective?.trim() ?? "";
345
404
  if (!currentObjective) return { state: "inactive", message: "No current goal objective is active." };
346
405
  let plan: UltragoalPlan | null;
@@ -366,7 +425,7 @@ export async function readUltragoalVerificationState(input: {
366
425
  }
367
426
  return { state: "inactive", message: "No Ultragoal plan exists." };
368
427
  }
369
- if (!objectiveMatches(currentObjective, plan))
428
+ if (!currentGoal || !objectiveMatches(currentGoal, plan, input.sessionId))
370
429
  return { state: "unrelated_goal", message: "Current goal is not an active Ultragoal objective." };
371
430
  if (plan.goals.some(goal => goal.status === "review_blocked")) {
372
431
  return {
@@ -381,7 +440,21 @@ export async function readUltragoalVerificationState(input: {
381
440
  message: "Ultragoal has blocked or failed goals; record blockers or rerun verification.",
382
441
  };
383
442
  }
384
- const receiptTarget = findReceiptGoal(plan, currentObjective);
443
+ const provenance = currentGoal.provenance;
444
+
445
+ const receiptTarget =
446
+ provenance?.source === "ultragoal"
447
+ ? provenance.goalId === "aggregate" || plan.gjcGoalMode === "aggregate"
448
+ ? (() => {
449
+ const goal = findFinalAggregateReceiptGoal(plan, ledger);
450
+ return goal ? { goal, receiptKind: "final-aggregate" as const } : null;
451
+ })()
452
+ : (() => {
453
+ const goal = plan.goals.find(item => item.id === provenance.goalId);
454
+ return goal ? { goal, receiptKind: "per-goal" as const } : null;
455
+ })()
456
+ : findReceiptGoal(plan, ledger, currentObjective);
457
+
385
458
  if (!receiptTarget) {
386
459
  // When earlier required goals are already complete but later ones remain, name the
387
460
  // specific blocking goals (a final-aggregate receipt cannot exist yet anyway). Only
@@ -623,9 +696,7 @@ export async function isUltragoalAskBlocked(
623
696
  });
624
697
  }
625
698
 
626
- const finalReceiptGoal = [...requiredGoals(plan)]
627
- .reverse()
628
- .find(goal => goal.completionVerification?.receiptKind === "final-aggregate");
699
+ const finalReceiptGoal = findFinalAggregateReceiptGoal(plan, ledger);
629
700
  if (!finalReceiptGoal) {
630
701
  return activeAskDiagnostic({
631
702
  reason: "Ultragoal aggregate completion is missing a final aggregate receipt.",
@@ -886,7 +957,7 @@ export async function assertUltragoalDropAllowed(input: {
886
957
  if (!input.currentGoal) return;
887
958
  if (input.currentGoal.status !== "active") return;
888
959
  // Unrelated active goal: not this aggregate run.
889
- if (!objectiveMatches(input.currentGoal.objective, plan)) return;
960
+ if (!objectiveMatches(input.currentGoal, plan, sessionId)) return;
890
961
  // All required stories complete: a legitimate reset, not a give-up.
891
962
  if (getUltragoalRunCompletionState(plan).allComplete) return;
892
963
  const nudge = await consumeUltragoalNudge({
@@ -0,0 +1,306 @@
1
+ import * as crypto from "node:crypto";
2
+ import {
3
+ appendLedger,
4
+ collectPipelineBlockerFootprints,
5
+ currentUltragoalSessionId,
6
+ getUltragoalPaths,
7
+ handleIdsFromValue,
8
+ hashPipelineMetadata,
9
+ isSubstantiveEvidence,
10
+ type JsonObject,
11
+ nonEmptyString,
12
+ openPipelineOverlap,
13
+ pipelinePeer,
14
+ readUltragoalLedger,
15
+ readUltragoalPlan,
16
+ requireCoveredHandles,
17
+ requireFreshPipelineMetadata,
18
+ requireJsonObjectOrArrayValue,
19
+ requireJsonObjectValue,
20
+ requireNonEmptyPipelineTargets,
21
+ resultHandleIds,
22
+ stringArray,
23
+ targetsAreDisjoint,
24
+ targetsOverlap,
25
+ type UltragoalGoal,
26
+ type UltragoalLedgerEvent,
27
+ type UltragoalPipelineLedgerEventName,
28
+ type UltragoalPipelineOverlapReceipt,
29
+ type UltragoalPipelineOverlapState,
30
+ type UltragoalPlan,
31
+ validateValidationBatchPipelineExclusion,
32
+ writePlan,
33
+ } from "./ultragoal-runtime";
34
+
35
+ export function requirePipelineStartable(plan: UltragoalPlan, prior: UltragoalGoal, next: UltragoalGoal): void {
36
+ if (plan.gjcGoalMode !== "aggregate")
37
+ throw new Error("pipeline overlap is supported only for aggregate ultragoal mode");
38
+ if (openPipelineOverlap(plan))
39
+ throw new Error("Cannot start pipeline overlap because another overlap is already open");
40
+ if (prior.status !== "active")
41
+ throw new Error(`Prior goal ${prior.id} must be active before pipeline overlap starts`);
42
+ if (next.status !== "pending" && next.status !== "failed")
43
+ throw new Error(`Next goal ${next.id} must be pending or retryable failed`);
44
+ validateValidationBatchPipelineExclusion(prior);
45
+ validateValidationBatchPipelineExclusion(next);
46
+ if (prior.validationBatch || next.validationBatch)
47
+ throw new Error("pipeline overlap cannot start for validation batch goals");
48
+ const priorMetadata = requireFreshPipelineMetadata(prior);
49
+ const nextMetadata = requireFreshPipelineMetadata(next);
50
+ if (!priorMetadata.eligible || !nextMetadata.eligible)
51
+ throw new Error("pipeline overlap requires eligible original-plan metadata on both goals");
52
+ if (!priorMetadata.independentOf.includes(next.id) || !nextMetadata.independentOf.includes(prior.id)) {
53
+ throw new Error("pipeline overlap requires symmetric original independence");
54
+ }
55
+ if (!targetsAreDisjoint(priorMetadata.targets, nextMetadata.targets))
56
+ throw new Error("pipeline overlap requires disjoint files and surfaces");
57
+ }
58
+
59
+ function pipelineEventRefs(prior: UltragoalGoal, next: UltragoalGoal): JsonObject {
60
+ return {
61
+ priorMetadataHash: prior.pipelineMetadata?.metadataHash,
62
+ nextMetadataHash: next.pipelineMetadata?.metadataHash,
63
+ priorTargets: prior.pipelineMetadata?.targets,
64
+ nextTargets: next.pipelineMetadata?.targets,
65
+ };
66
+ }
67
+
68
+ function pipelineReceipt(
69
+ cwd: string,
70
+ event: string,
71
+ overlapId: string,
72
+ prior: UltragoalGoal,
73
+ next: UltragoalGoal,
74
+ ): UltragoalPipelineOverlapReceipt {
75
+ const paths = getUltragoalPaths(cwd, currentUltragoalSessionId(cwd));
76
+ return {
77
+ ok: true,
78
+ event,
79
+ overlap_id: overlapId,
80
+ prior_goal_id: prior.id,
81
+ next_goal_id: next.id,
82
+ status: next.pipelineMetadata?.overlap,
83
+ next_goal_status: next.status,
84
+ goals_path: paths.goalsPath,
85
+ ledger_path: paths.ledgerPath,
86
+ };
87
+ }
88
+
89
+ export async function startUltragoalPipelineOverlap(input: {
90
+ cwd: string;
91
+ priorGoalId: string;
92
+ nextGoalId: string;
93
+ reviewHandles: JsonObject | JsonObject[];
94
+ qaHandles: JsonObject | JsonObject[];
95
+ implementationHandle: JsonObject;
96
+ }): Promise<UltragoalPipelineOverlapReceipt> {
97
+ const plan = await readUltragoalPlan(input.cwd);
98
+ if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
99
+ const prior = plan.goals.find(goal => goal.id === input.priorGoalId);
100
+ const next = plan.goals.find(goal => goal.id === input.nextGoalId);
101
+ if (!prior || !next) throw new Error("start-pipeline-overlap requires existing prior and next goal ids");
102
+ const reviewHandles = requireJsonObjectOrArrayValue(input.reviewHandles, "review handles");
103
+ const qaHandles = requireJsonObjectOrArrayValue(input.qaHandles, "QA handles");
104
+ requireJsonObjectValue(input.implementationHandle, "implementation handle");
105
+ requirePipelineStartable(plan, prior, next);
106
+ const now = new Date().toISOString();
107
+ const overlapId = `pipeline-${crypto.randomUUID()}`;
108
+ const priorMetadata = requireFreshPipelineMetadata(prior);
109
+ const nextMetadata = requireFreshPipelineMetadata(next);
110
+ const priorOpenMetadata = {
111
+ ...priorMetadata,
112
+ overlap: "open" as const,
113
+ overlapId,
114
+ priorGoalId: prior.id,
115
+ nextGoalId: next.id,
116
+ };
117
+ const nextOpenMetadata = {
118
+ ...nextMetadata,
119
+ overlap: "open" as const,
120
+ overlapId,
121
+ priorGoalId: prior.id,
122
+ nextGoalId: next.id,
123
+ };
124
+ prior.pipelineMetadata = { ...priorOpenMetadata, metadataHash: hashPipelineMetadata(priorOpenMetadata) };
125
+ next.pipelineMetadata = { ...nextOpenMetadata, metadataHash: hashPipelineMetadata(nextOpenMetadata) };
126
+ prior.updatedAt = now;
127
+ next.updatedAt = now;
128
+ next.status = "active";
129
+ next.startedAt = next.startedAt ?? now;
130
+ plan.updatedAt = now;
131
+ await writePlan(input.cwd, plan);
132
+ const refs = pipelineEventRefs(prior, next);
133
+ const expectedReviewHandleIds = handleIdsFromValue(reviewHandles, "review");
134
+ const expectedQaHandleIds = handleIdsFromValue(qaHandles, "QA");
135
+ await appendLedger(input.cwd, {
136
+ event: "pipeline_overlap_started",
137
+ eventId: crypto.randomUUID(),
138
+ timestamp: now,
139
+ schemaVersion: 1,
140
+ overlapId,
141
+ priorGoalId: prior.id,
142
+ nextGoalId: next.id,
143
+ reviewHandles,
144
+ reviewHandleIds: expectedReviewHandleIds,
145
+ qaHandles,
146
+ qaHandleIds: expectedQaHandleIds,
147
+ implementationHandle: input.implementationHandle,
148
+ ...refs,
149
+ });
150
+ await appendLedger(input.cwd, { event: "goal_started", goalId: next.id, pipelineOverlapId: overlapId });
151
+ return pipelineReceipt(input.cwd, "pipeline_overlap_started", overlapId, prior, next);
152
+ }
153
+
154
+ function resultStatus(value: JsonObject, fieldName: string): string {
155
+ const status = nonEmptyString(value.status) ?? nonEmptyString(value.verdict) ?? nonEmptyString(value.result);
156
+ if (!status) throw new Error(`${fieldName} requires status, verdict, or result`);
157
+ return status.toLowerCase();
158
+ }
159
+
160
+ function requireCleanPipelineResult(result: JsonObject, expectedHandleIds: readonly string[], fieldName: string): void {
161
+ const status = resultStatus(result, fieldName);
162
+ if (!["passed", "pass", "approved", "clear"].includes(status)) throw new Error(`${fieldName} did not pass`);
163
+ const evidence = nonEmptyString(result.evidence);
164
+ if (!evidence || !isSubstantiveEvidence(evidence)) throw new Error(`${fieldName} requires substantive evidence`);
165
+ requireCoveredHandles(expectedHandleIds, resultHandleIds(result, fieldName), fieldName);
166
+ if (collectPipelineBlockerFootprints(result, fieldName).length > 0)
167
+ throw new Error(`${fieldName} cannot clean-join with blockers`);
168
+ }
169
+
170
+ function pipelineStartEventHandleIds(
171
+ ledger: UltragoalLedgerEvent[],
172
+ overlapId: string,
173
+ ): { review: string[]; qa: string[] } {
174
+ const event = ledger.find(row => row.event === "pipeline_overlap_started" && row.overlapId === overlapId) as
175
+ | JsonObject
176
+ | undefined;
177
+ if (!event) throw new Error(`No pipeline_overlap_started event found for ${overlapId}`);
178
+ const review =
179
+ stringArray(event.reviewHandleIds) ??
180
+ handleIdsFromValue(requireJsonObjectOrArrayValue(event.reviewHandles, "review handles"), "review");
181
+ const qa =
182
+ stringArray(event.qaHandleIds) ??
183
+ handleIdsFromValue(requireJsonObjectOrArrayValue(event.qaHandles, "QA handles"), "QA");
184
+ return { review, qa };
185
+ }
186
+
187
+ export async function joinUltragoalPipelineOverlap(input: {
188
+ cwd: string;
189
+ overlapId: string;
190
+ reviewResult: JsonObject;
191
+ qaResult: JsonObject;
192
+ }): Promise<UltragoalPipelineOverlapReceipt> {
193
+ const plan = await readUltragoalPlan(input.cwd);
194
+ if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
195
+ const overlap = openPipelineOverlap(plan);
196
+ if (!overlap || overlap.overlapId !== input.overlapId)
197
+ throw new Error(`No open pipeline overlap found for ${input.overlapId}`);
198
+ const { prior, next, overlapId } = overlap;
199
+ const nextMetadata = requireFreshPipelineMetadata(next);
200
+ const ledger = await readUltragoalLedger(input.cwd);
201
+ const expectedHandles = pipelineStartEventHandleIds(ledger, overlapId);
202
+ const reviewBlockers = collectPipelineBlockerFootprints(input.reviewResult, "review result");
203
+ const qaBlockers = collectPipelineBlockerFootprints(input.qaResult, "QA result");
204
+ const blockerFootprints = [...reviewBlockers, ...qaBlockers];
205
+ let state: UltragoalPipelineOverlapState;
206
+ let event: UltragoalPipelineLedgerEventName;
207
+ if (blockerFootprints.length === 0) {
208
+ try {
209
+ requireCleanPipelineResult(input.reviewResult, expectedHandles.review, "review result");
210
+ requireCleanPipelineResult(input.qaResult, expectedHandles.qa, "QA result");
211
+ state = "joined_clean";
212
+ event = "pipeline_overlap_joined";
213
+ } catch {
214
+ state = "quarantine_required";
215
+ event = "pipeline_overlap_quarantined";
216
+ }
217
+ } else if (blockerFootprints.every(footprint => !targetsOverlap(footprint, nextMetadata.targets))) {
218
+ state = "blocked_disjoint_continue";
219
+ event = "pipeline_overlap_joined";
220
+ } else {
221
+ state = "quarantine_required";
222
+ event = "pipeline_overlap_quarantined";
223
+ }
224
+ const now = new Date().toISOString();
225
+ for (const goal of [prior, next]) {
226
+ const metadata = requireFreshPipelineMetadata(goal);
227
+ const joinedMetadata = { ...metadata, overlap: state, blockerFootprints };
228
+ goal.pipelineMetadata = { ...joinedMetadata, metadataHash: hashPipelineMetadata(joinedMetadata) };
229
+ goal.updatedAt = now;
230
+ }
231
+ if (state === "quarantine_required") next.status = "blocked";
232
+ plan.updatedAt = now;
233
+ await writePlan(input.cwd, plan);
234
+ await appendLedger(input.cwd, {
235
+ event,
236
+ eventId: crypto.randomUUID(),
237
+ timestamp: now,
238
+ schemaVersion: 1,
239
+ overlapId,
240
+ priorGoalId: prior.id,
241
+ nextGoalId: next.id,
242
+ status: state,
243
+ reviewResult: input.reviewResult,
244
+ qaResult: input.qaResult,
245
+ blockerFootprints,
246
+ ...pipelineEventRefs(prior, next),
247
+ });
248
+ return pipelineReceipt(input.cwd, event, overlapId, prior, next);
249
+ }
250
+
251
+ export async function rebaselineUltragoalPipelineOverlap(input: {
252
+ cwd: string;
253
+ overlapId: string;
254
+ goalId: string;
255
+ evidence: string;
256
+ targetState: JsonObject;
257
+ }): Promise<UltragoalPipelineOverlapReceipt> {
258
+ const plan = await readUltragoalPlan(input.cwd);
259
+ if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
260
+ const goal = plan.goals.find(item => item.id === input.goalId);
261
+ if (!goal) throw new Error(`No ultragoal goal found for ${input.goalId}.`);
262
+ const evidence = input.evidence.trim();
263
+ if (!isSubstantiveEvidence(evidence)) throw new Error("rebaseline-pipeline-overlap requires substantive evidence");
264
+ const targetState = requireNonEmptyPipelineTargets(input.targetState, "target state");
265
+ const metadata = requireFreshPipelineMetadata(goal);
266
+ if (metadata.overlap !== "quarantine_required" || metadata.overlapId !== input.overlapId) {
267
+ throw new Error(`Goal ${goal.id} is not quarantined for overlap ${input.overlapId}`);
268
+ }
269
+ for (const footprint of metadata.blockerFootprints ?? []) {
270
+ if (targetsOverlap(footprint, targetState))
271
+ throw new Error("rebaseline-pipeline-overlap target state overlaps unresolved blocker footprints");
272
+ }
273
+ const now = new Date().toISOString();
274
+ const rebaselinedMetadata = { ...metadata, overlap: "rebaseline_complete" as const, targets: targetState };
275
+ goal.pipelineMetadata = { ...rebaselinedMetadata, metadataHash: hashPipelineMetadata(rebaselinedMetadata) };
276
+ goal.status = "active";
277
+ goal.evidence = evidence;
278
+ goal.updatedAt = now;
279
+ plan.updatedAt = now;
280
+ await writePlan(input.cwd, plan);
281
+ const peer = pipelinePeer(plan, metadata);
282
+ await appendLedger(input.cwd, {
283
+ event: "pipeline_overlap_rebaselined",
284
+ eventId: crypto.randomUUID(),
285
+ timestamp: now,
286
+ schemaVersion: 1,
287
+ overlapId: input.overlapId,
288
+ priorGoalId: metadata.priorGoalId ?? peer?.id ?? "",
289
+ nextGoalId: metadata.nextGoalId ?? goal.id,
290
+ goalId: goal.id,
291
+ evidence,
292
+ targetState,
293
+ metadataHash: goal.pipelineMetadata.metadataHash,
294
+ });
295
+ const paths = getUltragoalPaths(input.cwd, currentUltragoalSessionId(input.cwd));
296
+ return {
297
+ ok: true,
298
+ event: "pipeline_overlap_rebaselined",
299
+ overlap_id: input.overlapId,
300
+ prior_goal_id: metadata.priorGoalId ?? peer?.id ?? "",
301
+ goal_id: goal.id,
302
+ status: goal.pipelineMetadata.overlap,
303
+ goals_path: paths.goalsPath,
304
+ ledger_path: paths.ledgerPath,
305
+ };
306
+ }
@@ -244,6 +244,93 @@ export function validateReceiptFreshBase(input: {
244
244
  return null;
245
245
  }
246
246
 
247
+ /**
248
+ * Validate a receipt whose plan-generation freshness was legitimately staled
249
+ * by later plan growth as a historical attestation for its own goal:
250
+ * internally consistent, anchored to a ledger checkpoint event that carries a
251
+ * byte-identical receipt, quality gate untampered, and the goal row untouched
252
+ * since verification. Plan-generation freshness against the CURRENT plan is
253
+ * intentionally not required; that is exactly what later plan growth
254
+ * invalidates by design. Returns null when the receipt stands as valid
255
+ * historical evidence.
256
+ */
257
+ function validateLedgerAnchoredHistoricalReceipt(input: {
258
+ ledger: readonly UltragoalLedgerEvent[];
259
+ goal: UltragoalGoal;
260
+ receipt: UltragoalCompletionVerification;
261
+ label: string;
262
+ }): UltragoalReceiptFreshnessDiagnostic | null {
263
+ if (
264
+ input.receipt.schemaVersion !== 1 ||
265
+ input.receipt.goalId !== input.goal.id ||
266
+ !input.receipt.planGeneration ||
267
+ !input.receipt.checkpointLedgerEventId ||
268
+ hashStructuredValue(input.receipt.basis) !== input.receipt.planGeneration
269
+ ) {
270
+ return {
271
+ state: "active_stale_receipt",
272
+ message: `Ultragoal ${input.goal.id} ${input.label} is malformed.`,
273
+ goalId: input.goal.id,
274
+ };
275
+ }
276
+ const event = findLedgerReceiptEvent(input.ledger, input.receipt);
277
+ if (!event)
278
+ return {
279
+ state: "active_stale_receipt",
280
+ message: `Ultragoal ${input.goal.id} ${input.label} ledger event is missing.`,
281
+ goalId: input.goal.id,
282
+ };
283
+ // Bind the goals.json receipt to the FULL receipt recorded on the ledger
284
+ // event. Field-selective matching would accept a coordinated edit of the
285
+ // goals-row basis/generation paired with only the event generation.
286
+ const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
287
+ if (!eventReceipt || hashStructuredValue(eventReceipt) !== hashStructuredValue(input.receipt))
288
+ return {
289
+ state: "active_stale_receipt",
290
+ message: `Ultragoal ${input.goal.id} ${input.label} does not match its ledger event receipt.`,
291
+ goalId: input.goal.id,
292
+ };
293
+ if (hashStructuredValue(event.qualityGateJson) !== input.receipt.qualityGateHash)
294
+ return {
295
+ state: "active_dirty_quality_gate",
296
+ message: `Ultragoal ${input.goal.id} ${input.label} quality-gate hash does not match ledger.`,
297
+ goalId: input.goal.id,
298
+ };
299
+ if (input.goal.updatedAt !== input.receipt.verifiedAt)
300
+ return {
301
+ state: "active_stale_receipt",
302
+ message: `Ultragoal ${input.goal.id} changed after its ${input.label} was verified.`,
303
+ goalId: input.goal.id,
304
+ };
305
+ return null;
306
+ }
307
+
308
+ /**
309
+ * Validate a final-aggregate receipt that was legitimately superseded — its
310
+ * aggregate claim staled by later plan growth (e.g. `steer add_subgoal`
311
+ * appending goals after a terminal run) — as historical evidence for its own
312
+ * goal. Returns null when the receipt stands.
313
+ */
314
+ export function validateSupersededFinalAggregateReceipt(input: {
315
+ ledger: readonly UltragoalLedgerEvent[];
316
+ goal: UltragoalGoal;
317
+ receipt: UltragoalCompletionVerification;
318
+ }): UltragoalReceiptFreshnessDiagnostic | null {
319
+ if (input.receipt.receiptKind !== "final-aggregate") {
320
+ return {
321
+ state: "active_stale_receipt",
322
+ message: `Ultragoal ${input.goal.id} superseded final-aggregate receipt is malformed.`,
323
+ goalId: input.goal.id,
324
+ };
325
+ }
326
+ return validateLedgerAnchoredHistoricalReceipt({
327
+ ledger: input.ledger,
328
+ goal: input.goal,
329
+ receipt: input.receipt,
330
+ label: "superseded final-aggregate receipt",
331
+ });
332
+ }
333
+
247
334
  export function findFreshBatchCloseReceipt(input: {
248
335
  plan: UltragoalPlan;
249
336
  ledger: readonly UltragoalLedgerEvent[];
@@ -267,7 +354,21 @@ export function findFreshBatchCloseReceipt(input: {
267
354
  receipt: finalReceipt,
268
355
  receiptKind: finalReceipt.receiptKind,
269
356
  });
270
- return diagnostic ? null : finalReceipt;
357
+ if (!diagnostic) return finalReceipt;
358
+ // A batch close staled only by later plan growth (e.g. `steer add_subgoal`
359
+ // after the batch completed) still proves the batch was validly closed:
360
+ // the member receipts are freshness-checked separately, the close is
361
+ // anchored to its ledger event, and the final goal's row is untouched.
362
+ // Without this, completing a goal appended after a closed batch can never
363
+ // validate, because the old close can never be fresh against the grown
364
+ // plan and identical-evidence replays of fresh receipts are no-ops.
365
+ const historicalDiagnostic = validateLedgerAnchoredHistoricalReceipt({
366
+ ledger: input.ledger,
367
+ goal: finalGoal,
368
+ receipt: finalReceipt,
369
+ label: "superseded batch-close receipt",
370
+ });
371
+ return historicalDiagnostic ? null : finalReceipt;
271
372
  }
272
373
 
273
374
  export function validateDeferredMemberReceiptFresh(input: {