@gajae-code/coding-agent 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/types/cli/config-cli.d.ts +11 -1
  3. package/dist/types/cli/launch-disposition.d.ts +23 -0
  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 +141 -11
  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-runtime.d.ts +55 -33
  54. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  55. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  56. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  57. package/dist/types/goals/runtime.d.ts +1 -0
  58. package/dist/types/goals/state.d.ts +8 -0
  59. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  60. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  61. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  62. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  63. package/dist/types/hindsight/state.d.ts +11 -4
  64. package/dist/types/lsp/lspmux.d.ts +5 -3
  65. package/dist/types/lsp/path-trust.d.ts +1 -0
  66. package/dist/types/main.d.ts +4 -2
  67. package/dist/types/memories/storage.d.ts +1 -0
  68. package/dist/types/modes/action-registry.d.ts +29 -0
  69. package/dist/types/modes/components/chrome.d.ts +14 -0
  70. package/dist/types/modes/components/command-palette.d.ts +27 -0
  71. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  72. package/dist/types/modes/components/index.d.ts +2 -1
  73. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  74. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  75. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  76. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  77. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  78. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  79. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  80. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  81. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  82. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  83. package/dist/types/modes/components/status-line.d.ts +1 -94
  84. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  85. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  86. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  87. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  88. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  89. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  90. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  91. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  92. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  93. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  94. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  95. package/dist/types/modes/interactive-mode.d.ts +19 -17
  96. package/dist/types/modes/jobs-observer.d.ts +2 -0
  97. package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
  98. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  99. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +20 -2
  100. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  101. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  102. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  103. package/dist/types/modes/types.d.ts +20 -13
  104. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  105. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  106. package/dist/types/sdk/broker/broker.d.ts +78 -12
  107. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  108. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  109. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  110. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  111. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  112. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  113. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  114. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  115. package/dist/types/sdk/bus/config.d.ts +11 -0
  116. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  117. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  118. package/dist/types/sdk/bus/index.d.ts +29 -0
  119. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  120. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  121. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  122. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  123. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  124. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  125. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  126. package/dist/types/sdk/bus/telegram-daemon.d.ts +46 -3
  127. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  128. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  129. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  130. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  131. package/dist/types/sdk/host/host.d.ts +2 -0
  132. package/dist/types/sdk/index.d.ts +1 -0
  133. package/dist/types/sdk/session-directory.d.ts +56 -0
  134. package/dist/types/sdk/session.d.ts +2 -0
  135. package/dist/types/session/agent-session.d.ts +82 -47
  136. package/dist/types/session/agent-storage.d.ts +7 -0
  137. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  138. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  139. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  140. package/dist/types/session/messages.d.ts +16 -0
  141. package/dist/types/session/session-manager-internal.d.ts +14 -0
  142. package/dist/types/session/session-manager.d.ts +82 -12
  143. package/dist/types/session/session-storage.d.ts +43 -3
  144. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  145. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  146. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  147. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  148. package/dist/types/system-prompt.d.ts +2 -11
  149. package/dist/types/task/executor.d.ts +3 -1
  150. package/dist/types/thinking.d.ts +7 -0
  151. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  152. package/dist/types/tools/ask.d.ts +64 -2
  153. package/dist/types/tools/bash.d.ts +3 -0
  154. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  155. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  156. package/dist/types/tools/cron.d.ts +2 -0
  157. package/dist/types/tools/fetch.d.ts +1 -0
  158. package/dist/types/tools/index.d.ts +30 -15
  159. package/dist/types/tools/output-meta.d.ts +1 -0
  160. package/dist/types/tools/read.d.ts +5 -0
  161. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  162. package/dist/types/tools/subagent.d.ts +1 -0
  163. package/dist/types/utils/event-bus.d.ts +1 -1
  164. package/dist/types/utils/file-mentions.d.ts +3 -3
  165. package/dist/types/web/search/index.d.ts +3 -1
  166. package/package.json +21 -9
  167. package/scripts/build-sdk-package-smoke.ts +34 -1
  168. package/scripts/generate-hotkeys-docs.ts +74 -0
  169. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  170. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  171. package/src/async/job-manager.ts +49 -6
  172. package/src/cli/config-cli.ts +75 -2
  173. package/src/cli/launch-disposition.ts +42 -0
  174. package/src/cli/notify-cli.ts +92 -26
  175. package/src/cli/session-picker.ts +20 -3
  176. package/src/cli/setup-cli.ts +1 -1
  177. package/src/cli.ts +1 -1
  178. package/src/commands/config.ts +1 -1
  179. package/src/commands/harness.ts +34 -106
  180. package/src/commands/launch.ts +6 -4
  181. package/src/commands/mcp-serve.ts +85 -8
  182. package/src/commands/mcp.ts +2 -2
  183. package/src/commands/notify.ts +8 -4
  184. package/src/commands/sdk.ts +61 -40
  185. package/src/commands/ultragoal.ts +4 -1
  186. package/src/config/atomic-yaml-patch.ts +80 -13
  187. package/src/config/file-lock.ts +105 -43
  188. package/src/config/keybindings.ts +64 -20
  189. package/src/config/model-auth.ts +9 -0
  190. package/src/config/model-bindings-applier.ts +139 -0
  191. package/src/config/model-discovery-manager.ts +224 -0
  192. package/src/config/model-equivalence.ts +66 -0
  193. package/src/config/model-registry.ts +169 -302
  194. package/src/config/model-resolver.ts +361 -369
  195. package/src/config/models-config-schema.ts +5 -4
  196. package/src/config/settings-schema.ts +214 -12
  197. package/src/config/settings.ts +458 -482
  198. package/src/coordinator-mcp/policy.ts +76 -11
  199. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  200. package/src/coordinator-mcp/question-state.ts +778 -0
  201. package/src/coordinator-mcp/server.ts +1702 -238
  202. package/src/debug/index.ts +1 -1
  203. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  204. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  205. package/src/discovery/helpers.ts +1 -50
  206. package/src/edit/index.ts +64 -0
  207. package/src/eval/py/env.ts +26 -0
  208. package/src/eval/py/kernel.ts +5 -3
  209. package/src/extensibility/extensions/loader.ts +9 -1
  210. package/src/extensibility/extensions/runner.ts +12 -3
  211. package/src/extensibility/extensions/types.ts +39 -1
  212. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  213. package/src/extensibility/hooks/runner.ts +2 -2
  214. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  215. package/src/extensibility/shared-events.ts +2 -0
  216. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  217. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  218. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  219. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  220. package/src/gjc-runtime/launch-tmux.ts +2 -5
  221. package/src/gjc-runtime/launch-worktree.ts +5 -5
  222. package/src/gjc-runtime/linux-proc.ts +71 -0
  223. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  224. package/src/gjc-runtime/session-resolution.ts +18 -3
  225. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  226. package/src/gjc-runtime/state-migrations.ts +1 -1
  227. package/src/gjc-runtime/state-runtime.ts +327 -249
  228. package/src/gjc-runtime/state-writer.ts +36 -40
  229. package/src/gjc-runtime/team-launch.ts +319 -0
  230. package/src/gjc-runtime/team-notify.ts +326 -0
  231. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  232. package/src/gjc-runtime/team-store.ts +828 -0
  233. package/src/gjc-runtime/team-workers.ts +962 -0
  234. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  235. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  236. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  237. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  238. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  239. package/src/gjc-runtime/ultragoal-guard.ts +29 -5
  240. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  241. package/src/gjc-runtime/ultragoal-runtime.ts +84 -1311
  242. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  243. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  244. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  245. package/src/goals/continuation-timeout-guard.ts +79 -0
  246. package/src/goals/runtime.ts +7 -5
  247. package/src/goals/state.ts +18 -0
  248. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  249. package/src/harness-control-plane/phase-rollup.ts +4 -7
  250. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  251. package/src/harness-control-plane/receipt-spool.ts +51 -7
  252. package/src/harness-control-plane/session-lease.ts +12 -130
  253. package/src/hindsight/backend.ts +26 -27
  254. package/src/hindsight/state.ts +101 -32
  255. package/src/hooks/skill-state.ts +17 -11
  256. package/src/internal-urls/docs-index.generated.ts +21 -18
  257. package/src/lsp/client.ts +1 -1
  258. package/src/lsp/config.ts +145 -26
  259. package/src/lsp/index.ts +1 -1
  260. package/src/lsp/lspmux.ts +39 -10
  261. package/src/lsp/path-trust.ts +81 -0
  262. package/src/main.ts +127 -30
  263. package/src/memories/index.ts +1 -0
  264. package/src/memories/storage.ts +6 -2
  265. package/src/modes/action-registry.ts +165 -0
  266. package/src/modes/components/chrome.ts +40 -0
  267. package/src/modes/components/command-palette.ts +168 -0
  268. package/src/modes/components/custom-editor.ts +1 -1
  269. package/src/modes/components/custom-provider-wizard.ts +51 -14
  270. package/src/modes/components/index.ts +2 -1
  271. package/src/modes/components/pet-selector.ts +11 -17
  272. package/src/modes/components/plan-preview-overlay.ts +295 -0
  273. package/src/modes/components/queue-mode-selector.ts +10 -27
  274. package/src/modes/components/queue-pane.ts +79 -0
  275. package/src/modes/components/read-tool-group.ts +13 -0
  276. package/src/modes/components/session-observer-overlay.ts +207 -772
  277. package/src/modes/components/sessions-dashboard.ts +181 -0
  278. package/src/modes/components/settings-selector.ts +1 -1
  279. package/src/modes/components/show-images-selector.ts +10 -24
  280. package/src/modes/components/status-line/types.ts +1 -1
  281. package/src/modes/components/status-line.ts +1 -969
  282. package/src/modes/components/tasks-pane.ts +73 -0
  283. package/src/modes/components/theme-selector.ts +11 -31
  284. package/src/modes/components/tool-execution.ts +22 -0
  285. package/src/modes/components/tool-status-header.ts +1056 -0
  286. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  287. package/src/modes/controllers/btw-controller.ts +1 -0
  288. package/src/modes/controllers/event-controller.ts +3 -2
  289. package/src/modes/controllers/extension-ui-controller.ts +24 -1
  290. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  291. package/src/modes/controllers/input-controller.ts +764 -229
  292. package/src/modes/controllers/mode-gate.ts +24 -0
  293. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  294. package/src/modes/controllers/selector-controller.ts +225 -10
  295. package/src/modes/controllers/stt-controller.ts +125 -0
  296. package/src/modes/interactive-mode.ts +289 -1150
  297. package/src/modes/jobs-observer.ts +3 -0
  298. package/src/modes/prompt-action-autocomplete.ts +7 -0
  299. package/src/modes/runtime-init.ts +8 -0
  300. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  301. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +71 -16
  302. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  303. package/src/modes/tasks-aggregator.ts +199 -0
  304. package/src/modes/transcript-item-registry.ts +229 -0
  305. package/src/modes/types.ts +45 -18
  306. package/src/modes/utils/ui-helpers.ts +17 -9
  307. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  308. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  309. package/src/prompts/agents/architect.md +2 -8
  310. package/src/prompts/agents/critic.md +2 -8
  311. package/src/prompts/agents/executor.md +3 -0
  312. package/src/prompts/agents/planner.md +4 -12
  313. package/src/prompts/system/custom-system-prompt.md +1 -23
  314. package/src/prompts/system/plan-mode-approved.md +8 -0
  315. package/src/prompts/system/system-prompt.md +17 -96
  316. package/src/prompts/tools/bash.md +4 -4
  317. package/src/prompts/tools/browser.md +15 -52
  318. package/src/prompts/tools/find.md +2 -2
  319. package/src/prompts/tools/hashline.md +13 -55
  320. package/src/prompts/tools/read.md +3 -6
  321. package/src/prompts/tools/search-tool-bm25.md +0 -2
  322. package/src/prompts/tools/search.md +2 -4
  323. package/src/prompts/tools/skill-discovery.md +1 -2
  324. package/src/prompts/tools/task.md +1 -1
  325. package/src/runtime-mcp/manager.ts +1 -1
  326. package/src/sdk/broker/broker.ts +206 -78
  327. package/src/sdk/broker/discovery.ts +1 -1
  328. package/src/sdk/broker/ensure.ts +3 -1
  329. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  330. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  331. package/src/sdk/broker/lifecycle.ts +1399 -142
  332. package/src/sdk/broker/process-incarnation.ts +5 -13
  333. package/src/sdk/broker/session-index.ts +145 -56
  334. package/src/sdk/broker/state-version.ts +21 -0
  335. package/src/sdk/broker/transport.ts +1 -1
  336. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  337. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  338. package/src/sdk/bus/chat-effect-journal.ts +10 -2
  339. package/src/sdk/bus/config-commands.ts +5 -4
  340. package/src/sdk/bus/config.ts +98 -0
  341. package/src/sdk/bus/conversation-store.ts +5 -29
  342. package/src/sdk/bus/daemon-paths.ts +2 -0
  343. package/src/sdk/bus/html-format.ts +20 -3
  344. package/src/sdk/bus/index.ts +782 -94
  345. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  346. package/src/sdk/bus/natives-augment.d.ts +26 -1
  347. package/src/sdk/bus/notification-service.ts +168 -20
  348. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  349. package/src/sdk/bus/recent-activity.ts +188 -74
  350. package/src/sdk/bus/rich-document.ts +258 -0
  351. package/src/sdk/bus/rich-render.ts +62 -70
  352. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  353. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  354. package/src/sdk/bus/telegram-daemon.ts +1389 -237
  355. package/src/sdk/bus/threaded-inbound.ts +8 -3
  356. package/src/sdk/bus/threaded-render.ts +57 -1
  357. package/src/sdk/bus/topic-registry.ts +120 -13
  358. package/src/sdk/host/control/dispatch.ts +14 -2
  359. package/src/sdk/host/control/operations.ts +6 -1
  360. package/src/sdk/host/host.ts +44 -2
  361. package/src/sdk/host/query/handlers.ts +20 -22
  362. package/src/sdk/host/query/revision-store.ts +80 -57
  363. package/src/sdk/index.ts +1 -0
  364. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  365. package/src/sdk/session-directory.ts +141 -0
  366. package/src/sdk/session.ts +462 -480
  367. package/src/session/agent-session.ts +1700 -583
  368. package/src/session/agent-storage.ts +18 -0
  369. package/src/session/cache-economics.ts +25 -8
  370. package/src/session/fallback-chain-controller.ts +53 -3
  371. package/src/session/internal/managed-session-scope.ts +2226 -0
  372. package/src/session/internal/managed-session-storage.ts +453 -0
  373. package/src/session/messages.ts +91 -2
  374. package/src/session/session-manager-internal.ts +33 -0
  375. package/src/session/session-manager.ts +1112 -333
  376. package/src/session/session-storage.ts +444 -39
  377. package/src/session/volatile-context-pruning.ts +78 -0
  378. package/src/setup/credential-auto-import.ts +2 -1
  379. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  380. package/src/setup/provider-onboarding.ts +36 -61
  381. package/src/setup/provider-presets.json +5 -0
  382. package/src/skill-state/active-state.ts +3 -13
  383. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  384. package/src/skill-state/workflow-state-contract.ts +6 -41
  385. package/src/slash-commands/builtin-registry.ts +46 -11
  386. package/src/system-prompt.ts +37 -44
  387. package/src/task/agents.ts +19 -1
  388. package/src/task/executor.ts +90 -6
  389. package/src/task/index.ts +36 -21
  390. package/src/thinking.ts +21 -6
  391. package/src/tool-discovery/tool-index.ts +2 -134
  392. package/src/tools/ask.ts +157 -21
  393. package/src/tools/ast-edit.ts +1 -1
  394. package/src/tools/bash.ts +38 -0
  395. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  396. package/src/tools/browser/tab-supervisor.ts +282 -36
  397. package/src/tools/computer.ts +10 -3
  398. package/src/tools/cron.ts +30 -16
  399. package/src/tools/eval.ts +1 -2
  400. package/src/tools/fetch.ts +11 -4
  401. package/src/tools/index.ts +76 -24
  402. package/src/tools/output-meta.ts +19 -7
  403. package/src/tools/read.ts +219 -64
  404. package/src/tools/resource-gc.ts +2 -2
  405. package/src/tools/search-tool-bm25.ts +11 -74
  406. package/src/tools/subagent.ts +43 -2
  407. package/src/utils/event-bus.ts +9 -4
  408. package/src/utils/file-mentions.ts +4 -4
  409. package/src/web/search/index.ts +3 -3
  410. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  411. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  412. package/src/discovery/plugin-dir-roots.ts +0 -28
  413. 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
@@ -248,6 +248,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
248
248
 
249
249
  return this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol);
250
250
  }
251
+ getForceFileSuggestions(
252
+ lines: string[],
253
+ cursorLine: number,
254
+ cursorCol: number,
255
+ ): Promise<{ items: AutocompleteItem[]; prefix: string } | null> {
256
+ return this.#baseProvider.getForceFileSuggestions(lines, cursorLine, cursorCol);
257
+ }
251
258
 
252
259
  applyCompletion(
253
260
  lines: string[],
@@ -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 ||
@@ -1149,11 +1167,24 @@ export interface OpenGateInput {
1149
1167
  context?: WorkflowGateContext;
1150
1168
  /** Optional diagnostic gate replaced by this newly persisted gate. */
1151
1169
  supersedesGateId?: string;
1170
+ /** Optional immutable runtime turn captured by the emitter before broker persistence. */
1171
+ runtimeTurnId?: string;
1152
1172
  }
1153
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
+
1154
1180
  export class WorkflowGateBrokerError extends Error {
1155
1181
  constructor(
1156
- 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",
1157
1188
  message: string,
1158
1189
  ) {
1159
1190
  super(message);
@@ -1212,6 +1243,27 @@ export class WorkflowGateBroker {
1212
1243
  this.store.put({ ...record, ackPolicy });
1213
1244
  }
1214
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
+
1215
1267
  async #terminalize(record: PersistedGate): Promise<void> {
1216
1268
  const proof = this.#terminalProofs.get(record.gate.gate_id) ?? (await this.hooks.terminalizeAccepted?.(record));
1217
1269
  this.#terminalProofs.delete(record.gate.gate_id);
@@ -1350,12 +1402,14 @@ export class WorkflowGateBroker {
1350
1402
 
1351
1403
  /** Open and emit a gate. The pending record is persisted BEFORE emission. */
1352
1404
  openGate(input: OpenGateInput, continuation?: GateContinuation): WorkflowGate {
1353
- 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)) {
1354
1406
  throw new WorkflowGateBrokerError(
1355
1407
  "invalid_workflow_stage",
1356
1408
  `stage "${input.stage}" is not a v1 workflow stage`,
1357
1409
  );
1358
1410
  }
1411
+ if (input.runtimeTurnId !== undefined && !isNonEmptyString(input.runtimeTurnId))
1412
+ throw new WorkflowGateBrokerError("invalid_runtime_turn", "runtime turn id must be a nonempty string");
1359
1413
  // Asserts schema shape (throws WorkflowGateSchemaError on unsupported keywords).
1360
1414
  compileGateSchema(input.schema);
1361
1415
  const seq = this.store.nextSeq(input.stage).toString().padStart(6, "0");
@@ -1371,6 +1425,7 @@ export class WorkflowGateBroker {
1371
1425
  context: input.context ?? {},
1372
1426
  created_at: new Date().toISOString(),
1373
1427
  required: true,
1428
+ ...(input.runtimeTurnId === undefined ? {} : { runtime_turn_id: input.runtimeTurnId }),
1374
1429
  };
1375
1430
  if (!continuation) {
1376
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. */