@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
@@ -31,6 +31,8 @@ export interface CronJobView {
31
31
  prompt: string;
32
32
  recurring: boolean;
33
33
  nextFireAt?: number;
34
+ /** A cron firing whose spawned job is currently running. */
35
+ firing?: boolean;
34
36
  createdAt: number;
35
37
  }
36
38
 
@@ -140,6 +142,7 @@ export class JobsObserver {
140
142
  recurring: snapshot.recurring,
141
143
  nextFireAt: snapshot.nextFireAt,
142
144
  createdAt: snapshot.createdAt,
145
+ firing: snapshot.firing,
143
146
  }))
144
147
  .sort((a, b) => b.createdAt - a.createdAt);
145
148
  const worstState: JobsWorstState = this.#failedUnacknowledged
@@ -66,6 +66,10 @@ export async function initializeExtensions(session: AgentSession, options: Initi
66
66
  },
67
67
  getActiveTools: () => session.getActiveToolNames(),
68
68
  getAllTools: () => session.getAllToolNames(),
69
+ resolveTool: name => {
70
+ const tool = session.getToolByName(name);
71
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
72
+ },
69
73
  setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames),
70
74
  getCommands: () => getSessionSlashCommands(session),
71
75
  setModel: model => runExtensionSetModel(session, model),
@@ -100,6 +104,10 @@ export async function initializeExtensions(session: AgentSession, options: Initi
100
104
  getQueuedMessages: () => session.getQueuedMessageEntries(),
101
105
  getActiveTools: () => session.getActiveToolNames(),
102
106
  getAllTools: () => session.getAllToolNames(),
107
+ resolveTool: name => {
108
+ const tool = session.getToolByName(name);
109
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
110
+ },
103
111
  shutdown,
104
112
  getContextUsage: () => session.getContextUsage(),
105
113
  getSystemPrompt: () => session.systemPrompt,
@@ -11,19 +11,20 @@
11
11
  */
12
12
 
13
13
  import type { OpenGateInput } from "./workflow-gate-broker";
14
- import type { JsonSchema, WorkflowGateKind, WorkflowStage } from "./workflow-gate-types";
14
+ import {
15
+ type AskGateDeepInterviewState,
16
+ buildAskGateAnswerSchema,
17
+ buildAskGateStageState as buildCanonicalAskGateStageState,
18
+ type WorkflowGateKind,
19
+ type WorkflowStage,
20
+ } from "./workflow-gate-types";
15
21
 
16
- /** "Other (type your own)" sentinel, mirroring the interactive ask tool. */
17
- export const GATE_OTHER_OPTION = "Other (type your own)";
18
-
19
- /** Optional structured deep-interview round metadata supplied by the agent. */
20
- export interface AskGateDeepInterviewState {
21
- round_id?: string;
22
- round: number;
23
- component: string;
24
- dimension: string;
25
- ambiguity: number;
26
- }
22
+ export {
23
+ type AskGateDeepInterviewState,
24
+ buildAskGateAnswerSchema,
25
+ GATE_OTHER_OPTION,
26
+ validateAskGateStageState,
27
+ } from "./workflow-gate-types";
27
28
 
28
29
  export interface AskGateWorkflowGateMeta {
29
30
  stage: WorkflowStage;
@@ -95,7 +96,7 @@ function deepInterviewQuestionState(questionText: string): Record<string, unknow
95
96
  const state: Record<string, unknown> = {};
96
97
  if (roundMatch) {
97
98
  const round = Number(roundMatch[1]);
98
- if (Number.isFinite(round)) state.round = round;
99
+ if (Number.isSafeInteger(round) && round >= 0) state.round = round;
99
100
  const mode = roundMatch[2]?.trim();
100
101
  if (mode) {
101
102
  state.mode = mode;
@@ -103,8 +104,9 @@ function deepInterviewQuestionState(questionText: string): Record<string, unknow
103
104
  if (normalized.includes("topology")) state.topology_gate = true;
104
105
  if (/(contrarian|simplifier|ontologist)/u.test(normalized)) state.challenge_mode = normalized;
105
106
  }
106
- const ambiguity = roundMatch[3]?.trim();
107
- if (ambiguity) state.ambiguity = ambiguity;
107
+ const rawAmbiguity = roundMatch[3]?.trim();
108
+ const ambiguity = rawAmbiguity === "" || rawAmbiguity === undefined ? Number.NaN : Number(rawAmbiguity);
109
+ if (Number.isFinite(ambiguity) && ambiguity >= 0 && ambiguity <= 1) state.ambiguity = ambiguity;
108
110
  }
109
111
  if (/Round\s+0\s+\|\s+Topology confirmation/im.test(questionText)) {
110
112
  state.round = 0;
@@ -114,91 +116,25 @@ function deepInterviewQuestionState(questionText: string): Record<string, unknow
114
116
  return state;
115
117
  }
116
118
 
117
- function questionAnswerSchema(question: AskGateQuestion, labels: string[]): JsonSchema {
118
- const multi = question.multi ?? false;
119
- const selectedItems: JsonSchema = { type: "string", enum: labels };
120
- const selectedBase: JsonSchema = { type: "array", items: selectedItems, uniqueItems: true };
121
- const selectedOnly: JsonSchema = {
122
- ...selectedBase,
123
- minItems: question.allowEmpty ? 0 : 1,
124
- ...(multi ? {} : { maxItems: 1 }),
125
- };
126
- const selectedWithOther: JsonSchema = {
127
- ...selectedBase,
128
- ...(multi ? {} : { maxItems: 0 }),
129
- };
130
- return {
131
- type: "object",
132
- properties: {
133
- selected: selectedBase,
134
- other: { type: "boolean", description: "set true to provide a free-text answer in `custom`" },
135
- custom: {
136
- type: "string",
137
- minLength: 1,
138
- pattern: "\\S",
139
- description: "free-text answer; required when `other` is true",
140
- },
141
- action: {
142
- type: "string",
143
- enum: ["answer", "clarify"],
144
- description: "set to `clarify` to ask about the choices without answering the round",
145
- },
146
- question: {
147
- type: "string",
148
- minLength: 1,
149
- pattern: "\\S",
150
- description: "clarification question; required when action is `clarify`",
151
- },
119
+ /** Build and validate exact producer-compatible `stage_state` metadata for one ask question. */
120
+ export function buildAskGateStageState(question: AskGateQuestion, labels: string[]): Record<string, unknown> {
121
+ return buildCanonicalAskGateStageState(
122
+ {
123
+ id: question.id,
124
+ multi: question.multi,
125
+ allowEmpty: question.allowEmpty,
126
+ navigationLabel: question.navigationLabel,
127
+ deepInterview: question.deepInterview,
128
+ fallbackState: deepInterviewQuestionState(question.question),
152
129
  },
153
- additionalProperties: false,
154
- anyOf: [
155
- {
156
- type: "object",
157
- properties: { selected: selectedOnly, other: { const: false }, action: { const: "answer" } },
158
- required: ["selected"],
159
- additionalProperties: false,
160
- },
161
- {
162
- type: "object",
163
- properties: {
164
- selected: selectedWithOther,
165
- other: { const: true },
166
- custom: { type: "string", minLength: 1, pattern: "\\S" },
167
- action: { const: "answer" },
168
- },
169
- required: ["selected", "other", "custom"],
170
- additionalProperties: false,
171
- },
172
- {
173
- type: "object",
174
- properties: {
175
- action: { const: "clarify" },
176
- question: { type: "string", minLength: 1, pattern: "\\S" },
177
- },
178
- required: ["action", "question"],
179
- additionalProperties: false,
180
- },
181
- ],
182
- };
183
- }
184
-
185
- /** Build `stage_state` round metadata from the structured param (authoritative when present). */
186
- function structuredDeepInterviewState(meta: AskGateDeepInterviewState): Record<string, unknown> {
187
- const state: Record<string, unknown> = {
188
- deep_interview_metadata: true,
189
- round: meta.round,
190
- component: meta.component,
191
- dimension: meta.dimension,
192
- ambiguity: meta.ambiguity,
193
- };
194
- if (meta.round_id !== undefined) state.round_id = meta.round_id;
195
- return state;
130
+ labels,
131
+ );
196
132
  }
197
133
 
198
134
  /** Build the `workflow_gate` open-input for one ask question. */
199
135
  export function questionToGate(question: AskGateQuestion): OpenGateInput {
200
136
  const labels = question.options.map(o => o.label);
201
- const schema = questionAnswerSchema(question, labels);
137
+ const schema = buildAskGateAnswerSchema(question, labels);
202
138
  return {
203
139
  stage: question.workflowGate?.stage ?? "deep-interview",
204
140
  kind: question.workflowGate?.kind ?? "question",
@@ -211,18 +147,7 @@ export function questionToGate(question: AskGateQuestion): OpenGateInput {
211
147
  context: {
212
148
  title: question.question,
213
149
  prompt: question.question,
214
- stage_state: {
215
- question_id: question.id,
216
- multi: question.multi ?? false,
217
- options: labels,
218
- other_option: GATE_OTHER_OPTION,
219
- clarification_action: "clarify",
220
- allow_empty: question.allowEmpty === true,
221
- navigation_label: question.navigationLabel,
222
- ...(question.deepInterview
223
- ? structuredDeepInterviewState(question.deepInterview)
224
- : deepInterviewQuestionState(question.question)),
225
- },
150
+ stage_state: buildAskGateStageState(question, labels),
226
151
  },
227
152
  };
228
153
  }
@@ -28,7 +28,7 @@ import type {
28
28
  WorkflowGateValidationError,
29
29
  WorkflowStage,
30
30
  } from "./workflow-gate-types";
31
- import { RESERVED_WORKFLOW_STAGES } from "./workflow-gate-types";
31
+ import { RESERVED_WORKFLOW_STAGES, WORKFLOW_GATE_V1_STAGES } from "./workflow-gate-types";
32
32
 
33
33
  export type PersistedSemanticDisposition = "commit" | "resolve_without_commit";
34
34
  export type PersistedResolutionOrigin =
@@ -108,6 +108,10 @@ export interface WorkflowGateEmitter {
108
108
  setAckRecoveryParticipant?(participant: AskSelectedAckRecoveryParticipant | null): void;
109
109
  /** Explicit host hook for same-process accepted-but-unadvanced recovery. */
110
110
  recoverAcceptedGates?(): Promise<string[]>;
111
+ /** Reads a durable completed resolution before any presentation claim. */
112
+ lookupCompletedResolution?(response: WorkflowGateResponse): WorkflowGateCompletedResolutionLookup;
113
+ /** Supplies the active runtime turn captured immediately before a gate is opened. */
114
+ setRuntimeTurnProvider?(provider: (() => string | undefined) | null): void;
111
115
  /** Permanently revoke all in-process continuation authority and reject their waiters. */
112
116
  fence?(): void;
113
117
  /** Temporarily make this emitter unavailable while a session transition is reversible. */
@@ -139,6 +143,7 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
139
143
  readonly #runId: string;
140
144
  readonly #store: GateStore;
141
145
  readonly #emitterHooks: Pick<BrokerHooks, "advance">;
146
+ #runtimeTurnProvider: (() => string | undefined) | undefined;
142
147
 
143
148
  constructor(runId: string, store: GateStore, emitterHooks: Pick<BrokerHooks, "advance"> = {}) {
144
149
  this.#runId = runId;
@@ -158,20 +163,29 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
158
163
  isUnattended(): boolean {
159
164
  return true;
160
165
  }
166
+ setRuntimeTurnProvider(provider: (() => string | undefined) | null): void {
167
+ this.#runtimeTurnProvider = provider ?? undefined;
168
+ }
161
169
 
162
170
  emitGate(input: OpenGateInput): Promise<unknown> {
163
171
  if (this.#fenced || this.#suspended) return Promise.reject(new Error("workflow gate emitter is unavailable"));
164
172
  const waiter = Promise.withResolvers<unknown>();
165
- const gate = this.#broker.openGate(input, {
166
- activate: opened => this.#waiters.set(opened.gate_id, waiter),
167
- isLive: gateId => this.#waiters.has(gateId),
168
- release: gateId => {
169
- const activeWaiter = this.#waiters.get(gateId);
170
- if (!activeWaiter) return;
171
- this.#waiters.delete(gateId);
172
- activeWaiter.reject(new Error(`workflow gate ${gateId} continuation was fenced`));
173
+ const gate = this.#broker.openGate(
174
+ {
175
+ ...input,
176
+ ...(input.runtimeTurnId === undefined ? { runtimeTurnId: this.#runtimeTurnProvider?.() } : {}),
173
177
  },
174
- });
178
+ {
179
+ activate: opened => this.#waiters.set(opened.gate_id, waiter),
180
+ isLive: gateId => this.#waiters.has(gateId),
181
+ release: gateId => {
182
+ const activeWaiter = this.#waiters.get(gateId);
183
+ if (!activeWaiter) return;
184
+ this.#waiters.delete(gateId);
185
+ activeWaiter.reject(new Error(`workflow gate ${gateId} continuation was fenced`));
186
+ },
187
+ },
188
+ );
175
189
  // A listener may answer synchronously while the broker emits. The durable
176
190
  // record is the source of truth in that race; never strand its promise.
177
191
  const persisted = this.#store.get(gate.gate_id);
@@ -192,6 +206,10 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
192
206
  throw error;
193
207
  });
194
208
  }
209
+ lookupCompletedResolution(response: WorkflowGateResponse): WorkflowGateCompletedResolutionLookup {
210
+ return this.#broker.lookupCompletedResolution(response);
211
+ }
212
+
195
213
  prepareTerminalization(gateId: string, proof: WorkflowGateTerminalProof): boolean {
196
214
  return this.#broker.prepareTerminalization(gateId, proof);
197
215
  }
@@ -223,6 +241,7 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
223
241
  if (this.#fenced) return;
224
242
  this.#fenced = true;
225
243
  this.#suspended = false;
244
+ this.#runtimeTurnProvider = undefined;
226
245
  if (this.#recoveryTimer) clearTimeout(this.#recoveryTimer);
227
246
  this.#recoveryTimer = undefined;
228
247
  if (this.#recoveryGraceTimer) clearTimeout(this.#recoveryGraceTimer);
@@ -467,8 +486,6 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
467
486
  }
468
487
  }
469
488
 
470
- const V1_STAGES: readonly WorkflowStage[] = ["deep-interview", "ralplan", "ultragoal"];
471
-
472
489
  export interface PersistedGate {
473
490
  gate: WorkflowGate;
474
491
  status: "pending" | "accepted" | "quarantined";
@@ -760,7 +777,7 @@ function assertFileState(value: unknown, filePath: string): asserts value is Fil
760
777
  invalidFileState(filePath);
761
778
  for (const [stage, counter] of Object.entries(value.counters)) {
762
779
  if (
763
- !V1_STAGES.includes(stage as WorkflowStage) ||
780
+ !WORKFLOW_GATE_V1_STAGES.includes(stage as WorkflowStage) ||
764
781
  typeof counter !== "number" ||
765
782
  !Number.isSafeInteger(counter) ||
766
783
  counter < 0
@@ -805,7 +822,7 @@ function assertPersistedGate(
805
822
  if (
806
823
  gate.type !== "workflow_gate" ||
807
824
  !isNonEmptyString(gate.gate_id) ||
808
- !V1_STAGES.includes(stage) ||
825
+ !WORKFLOW_GATE_V1_STAGES.includes(stage) ||
809
826
  !["question", "approval", "execution"].includes(gate.kind as string) ||
810
827
  !isObject(gate.schema) ||
811
828
  !isNonEmptyString(gate.schema_hash) ||
@@ -814,6 +831,7 @@ function assertPersistedGate(
814
831
  gate.required !== true
815
832
  )
816
833
  invalidFileState(filePath);
834
+ if (gate.runtime_turn_id !== undefined && !isNonEmptyString(gate.runtime_turn_id)) invalidFileState(filePath);
817
835
  const id = new RegExp(`^wg_[A-Za-z0-9]+_${gate.stage}_(\\d{6,})$`).exec(gate.gate_id);
818
836
  if (
819
837
  !id ||
@@ -936,10 +954,14 @@ function assertPersistedGate(
936
954
  invalidFileState(filePath);
937
955
  }
938
956
 
939
- function isUnsupportedWindowsDirectorySyncError(error: unknown): boolean {
940
- if (process.platform !== "win32") return false;
957
+ export function isUnsupportedWindowsDirectorySyncError(
958
+ error: unknown,
959
+ platform: NodeJS.Platform = process.platform,
960
+ ): boolean {
961
+ if (platform !== "win32") return false;
941
962
  const code = (error as NodeJS.ErrnoException | undefined)?.code;
942
- return code === "EINVAL" || code === "ENOTSUP" || code === "EOPNOTSUPP";
963
+ // Bun reports EPERM when fsyncSync is applied to a Windows directory handle.
964
+ return code === "EINVAL" || code === "ENOTSUP" || code === "EOPNOTSUPP" || code === "EPERM";
943
965
  }
944
966
 
945
967
  /** A write may have reached `rename` but not the directory fsync durability barrier. */
@@ -1145,11 +1167,24 @@ export interface OpenGateInput {
1145
1167
  context?: WorkflowGateContext;
1146
1168
  /** Optional diagnostic gate replaced by this newly persisted gate. */
1147
1169
  supersedesGateId?: string;
1170
+ /** Optional immutable runtime turn captured by the emitter before broker persistence. */
1171
+ runtimeTurnId?: string;
1148
1172
  }
1149
1173
 
1174
+ /** Durable resolution state used to safely replay direct control after presentation retirement. */
1175
+ export type WorkflowGateCompletedResolutionLookup =
1176
+ | { kind: "none" }
1177
+ | { kind: "completed"; resolution: WorkflowGateResolution }
1178
+ | { kind: "accepted_incomplete" };
1179
+
1150
1180
  export class WorkflowGateBrokerError extends Error {
1151
1181
  constructor(
1152
- readonly code: "unknown_gate" | "already_resolved" | "idempotency_conflict" | "invalid_workflow_stage",
1182
+ readonly code:
1183
+ | "unknown_gate"
1184
+ | "already_resolved"
1185
+ | "idempotency_conflict"
1186
+ | "invalid_workflow_stage"
1187
+ | "invalid_runtime_turn",
1153
1188
  message: string,
1154
1189
  ) {
1155
1190
  super(message);
@@ -1208,6 +1243,27 @@ export class WorkflowGateBroker {
1208
1243
  this.store.put({ ...record, ackPolicy });
1209
1244
  }
1210
1245
 
1246
+ lookupCompletedResolution(response: WorkflowGateResponse): WorkflowGateCompletedResolutionLookup {
1247
+ const record = this.store.get(response.gate_id);
1248
+ if (!record) return { kind: "none" };
1249
+ const responseHash = answerHashOf({ gate_id: response.gate_id, answer: response.answer });
1250
+ if (record.status !== "accepted" && record.status !== "quarantined") return { kind: "none" };
1251
+ if (record.responseHash === undefined) return { kind: "none" };
1252
+ const sameBody = record.responseHash === responseHash;
1253
+ const sameKey = record.idempotencyKey === response.idempotency_key;
1254
+ if (response.idempotency_key !== undefined && sameKey && !sameBody)
1255
+ throw new WorkflowGateBrokerError(
1256
+ "idempotency_conflict",
1257
+ `idempotency_conflict: gate ${response.gate_id} resolved with a different body`,
1258
+ );
1259
+ if (response.idempotency_key !== undefined && sameKey && sameBody) {
1260
+ if (record.terminalized === true && record.advanced === true && record.resolution)
1261
+ return { kind: "completed", resolution: record.resolution };
1262
+ return { kind: "accepted_incomplete" };
1263
+ }
1264
+ throw new WorkflowGateBrokerError("already_resolved", `already_resolved: gate ${response.gate_id}`);
1265
+ }
1266
+
1211
1267
  async #terminalize(record: PersistedGate): Promise<void> {
1212
1268
  const proof = this.#terminalProofs.get(record.gate.gate_id) ?? (await this.hooks.terminalizeAccepted?.(record));
1213
1269
  this.#terminalProofs.delete(record.gate.gate_id);
@@ -1346,12 +1402,14 @@ export class WorkflowGateBroker {
1346
1402
 
1347
1403
  /** Open and emit a gate. The pending record is persisted BEFORE emission. */
1348
1404
  openGate(input: OpenGateInput, continuation?: GateContinuation): WorkflowGate {
1349
- if (RESERVED_WORKFLOW_STAGES.includes(input.stage) || !V1_STAGES.includes(input.stage)) {
1405
+ if (RESERVED_WORKFLOW_STAGES.includes(input.stage) || !WORKFLOW_GATE_V1_STAGES.includes(input.stage)) {
1350
1406
  throw new WorkflowGateBrokerError(
1351
1407
  "invalid_workflow_stage",
1352
1408
  `stage "${input.stage}" is not a v1 workflow stage`,
1353
1409
  );
1354
1410
  }
1411
+ if (input.runtimeTurnId !== undefined && !isNonEmptyString(input.runtimeTurnId))
1412
+ throw new WorkflowGateBrokerError("invalid_runtime_turn", "runtime turn id must be a nonempty string");
1355
1413
  // Asserts schema shape (throws WorkflowGateSchemaError on unsupported keywords).
1356
1414
  compileGateSchema(input.schema);
1357
1415
  const seq = this.store.nextSeq(input.stage).toString().padStart(6, "0");
@@ -1367,6 +1425,7 @@ export class WorkflowGateBroker {
1367
1425
  context: input.context ?? {},
1368
1426
  created_at: new Date().toISOString(),
1369
1427
  required: true,
1428
+ ...(input.runtimeTurnId === undefined ? {} : { runtime_turn_id: input.runtimeTurnId }),
1370
1429
  };
1371
1430
  if (!continuation) {
1372
1431
  this.store.put({
@@ -3,11 +3,186 @@
3
3
  /** Lifecycle stages that emit machine-addressable SDK workflow gates. */
4
4
  export type WorkflowStage = "deep-interview" | "ralplan" | "ultragoal";
5
5
 
6
+ /** Lifecycle stages included in the v1 workflow-gate contract. */
7
+ export const WORKFLOW_GATE_V1_STAGES: readonly WorkflowStage[] = ["deep-interview", "ralplan", "ultragoal"];
8
+
6
9
  /** Reserved stage names that are explicitly not part of the v1 contract. */
7
10
  export const RESERVED_WORKFLOW_STAGES: readonly string[] = ["team"];
8
11
 
9
12
  export type WorkflowGateKind = "question" | "approval" | "execution";
10
13
 
14
+ /** "Other (type your own)" sentinel, mirroring the interactive ask tool. */
15
+ export const GATE_OTHER_OPTION = "Other (type your own)";
16
+
17
+ export interface AskGateDeepInterviewState {
18
+ round_id?: string;
19
+ round: number;
20
+ component: string;
21
+ dimension: string;
22
+ ambiguity: number;
23
+ }
24
+
25
+ export interface AskGateStageStateInput {
26
+ id: string;
27
+ multi?: boolean;
28
+ allowEmpty?: boolean;
29
+ navigationLabel?: "Next" | "Done";
30
+ deepInterview?: AskGateDeepInterviewState;
31
+ fallbackState?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface AskGateSchemaInput {
35
+ multi?: boolean;
36
+ allowEmpty?: boolean;
37
+ }
38
+
39
+ function isBoundedAskGateString(value: unknown): value is string {
40
+ return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).byteLength <= 256;
41
+ }
42
+
43
+ /** Rejects stage state that is not exactly compatible with the generic ask-gate producer. */
44
+ export function validateAskGateStageState(value: unknown): asserts value is Record<string, unknown> {
45
+ if (typeof value !== "object" || value === null || Array.isArray(value))
46
+ throw new Error("ask gate stage_state must be an object");
47
+ const state = value as Record<string, unknown>;
48
+ const allowed = new Set([
49
+ "question_id",
50
+ "multi",
51
+ "allow_empty",
52
+ "options",
53
+ "other_option",
54
+ "clarification_action",
55
+ "navigation_label",
56
+ "deep_interview_metadata",
57
+ "round",
58
+ "round_id",
59
+ "component",
60
+ "dimension",
61
+ "mode",
62
+ "challenge_mode",
63
+ "ambiguity",
64
+ "topology_gate",
65
+ ]);
66
+ if (Object.keys(state).some(key => !allowed.has(key))) throw new Error("ask gate stage_state has an unknown field");
67
+ if (
68
+ !isBoundedAskGateString(state.question_id) ||
69
+ typeof state.multi !== "boolean" ||
70
+ typeof state.allow_empty !== "boolean" ||
71
+ !Array.isArray(state.options) ||
72
+ state.options.some(option => !isBoundedAskGateString(option)) ||
73
+ state.other_option !== GATE_OTHER_OPTION ||
74
+ state.clarification_action !== "clarify"
75
+ )
76
+ throw new Error("ask gate stage_state has invalid required fields");
77
+ if (state.navigation_label !== undefined && state.navigation_label !== "Next" && state.navigation_label !== "Done")
78
+ throw new Error("ask gate stage_state has an invalid navigation label");
79
+ for (const key of ["round_id", "component", "dimension", "mode", "challenge_mode"])
80
+ if (state[key] !== undefined && !isBoundedAskGateString(state[key]))
81
+ throw new Error(`ask gate stage_state has an invalid ${key}`);
82
+ if (state.deep_interview_metadata !== undefined && typeof state.deep_interview_metadata !== "boolean")
83
+ throw new Error("ask gate stage_state has invalid deep interview metadata");
84
+ if (state.round !== undefined && (!Number.isSafeInteger(state.round) || (state.round as number) < 0))
85
+ throw new Error("ask gate stage_state has an invalid round");
86
+ if (
87
+ state.ambiguity !== undefined &&
88
+ (typeof state.ambiguity !== "number" ||
89
+ !Number.isFinite(state.ambiguity) ||
90
+ state.ambiguity < 0 ||
91
+ state.ambiguity > 1)
92
+ )
93
+ throw new Error("ask gate stage_state has an invalid ambiguity");
94
+ if (state.topology_gate !== undefined && typeof state.topology_gate !== "boolean")
95
+ throw new Error("ask gate stage_state has an invalid topology flag");
96
+ }
97
+
98
+ /** Builds the canonical generic ask answer schema. */
99
+ export function buildAskGateAnswerSchema(question: AskGateSchemaInput, labels: string[]): JsonSchema {
100
+ const multi = question.multi ?? false;
101
+ const selectedItems: JsonSchema = { type: "string", enum: labels };
102
+ const selectedBase: JsonSchema = { type: "array", items: selectedItems, uniqueItems: true };
103
+ const selectedOnly: JsonSchema = {
104
+ ...selectedBase,
105
+ minItems: question.allowEmpty ? 0 : 1,
106
+ ...(multi ? {} : { maxItems: 1 }),
107
+ };
108
+ const selectedWithOther: JsonSchema = { ...selectedBase, ...(multi ? {} : { maxItems: 0 }) };
109
+ return {
110
+ type: "object",
111
+ properties: {
112
+ selected: selectedBase,
113
+ other: { type: "boolean", description: "set true to provide a free-text answer in `custom`" },
114
+ custom: {
115
+ type: "string",
116
+ minLength: 1,
117
+ pattern: "\\S",
118
+ description: "free-text answer; required when `other` is true",
119
+ },
120
+ action: {
121
+ type: "string",
122
+ enum: ["answer", "clarify"],
123
+ description: "set to `clarify` to ask about the choices without answering the round",
124
+ },
125
+ question: {
126
+ type: "string",
127
+ minLength: 1,
128
+ pattern: "\\S",
129
+ description: "clarification question; required when action is `clarify`",
130
+ },
131
+ },
132
+ additionalProperties: false,
133
+ anyOf: [
134
+ {
135
+ type: "object",
136
+ properties: { selected: selectedOnly, other: { const: false }, action: { const: "answer" } },
137
+ required: ["selected"],
138
+ additionalProperties: false,
139
+ },
140
+ {
141
+ type: "object",
142
+ properties: {
143
+ selected: selectedWithOther,
144
+ other: { const: true },
145
+ custom: { type: "string", minLength: 1, pattern: "\\S" },
146
+ action: { const: "answer" },
147
+ },
148
+ required: ["selected", "other", "custom"],
149
+ additionalProperties: false,
150
+ },
151
+ {
152
+ type: "object",
153
+ properties: { action: { const: "clarify" }, question: { type: "string", minLength: 1, pattern: "\\S" } },
154
+ required: ["action", "question"],
155
+ additionalProperties: false,
156
+ },
157
+ ],
158
+ };
159
+ }
160
+
161
+ /** Builds and validates exact generic ask stage-state metadata. */
162
+ export function buildAskGateStageState(input: AskGateStageStateInput, labels: string[]): Record<string, unknown> {
163
+ const state: Record<string, unknown> = {
164
+ question_id: input.id,
165
+ multi: input.multi ?? false,
166
+ options: labels,
167
+ other_option: GATE_OTHER_OPTION,
168
+ clarification_action: "clarify",
169
+ allow_empty: input.allowEmpty === true,
170
+ ...(input.navigationLabel === undefined ? {} : { navigation_label: input.navigationLabel }),
171
+ ...(input.deepInterview
172
+ ? {
173
+ deep_interview_metadata: true,
174
+ round: input.deepInterview.round,
175
+ component: input.deepInterview.component,
176
+ dimension: input.deepInterview.dimension,
177
+ ambiguity: input.deepInterview.ambiguity,
178
+ ...(input.deepInterview.round_id === undefined ? {} : { round_id: input.deepInterview.round_id }),
179
+ }
180
+ : (input.fallbackState ?? {})),
181
+ };
182
+ validateAskGateStageState(state);
183
+ return state;
184
+ }
185
+
11
186
  /** The documented JSON Schema 2020-12 subset supported by the gate validator. */
12
187
  export interface JsonSchema {
13
188
  type?: "string" | "number" | "integer" | "boolean" | "object" | "array" | "null";
@@ -60,6 +235,8 @@ export interface WorkflowGate {
60
235
  context: WorkflowGateContext;
61
236
  created_at: string;
62
237
  required: true;
238
+ /** Immutable runtime turn correlation captured before durable gate visibility. */
239
+ runtime_turn_id?: string;
63
240
  }
64
241
 
65
242
  /** Non-actionable durable-gate state retained for restart diagnostics. */