@gajae-code/coding-agent 0.18.11 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/capability/rule.d.ts +7 -2
  7. package/dist/types/cli/accounts-cli.d.ts +2 -2
  8. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  9. package/dist/types/cli/args.d.ts +7 -2
  10. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  11. package/dist/types/cli/customize-doctor.d.ts +4 -3
  12. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  13. package/dist/types/cli/install-activation.d.ts +10 -4
  14. package/dist/types/cli/public-command-errors.d.ts +2 -0
  15. package/dist/types/cli/root-flags.d.ts +0 -12
  16. package/dist/types/cli/update-cli.d.ts +21 -3
  17. package/dist/types/cli-main.d.ts +0 -6
  18. package/dist/types/cli-ordinary.d.ts +2 -2
  19. package/dist/types/commands/accounts.d.ts +2 -1
  20. package/dist/types/commands/launch.d.ts +0 -6
  21. package/dist/types/commands/migrate.d.ts +1 -1
  22. package/dist/types/commands/sdk.d.ts +13 -0
  23. package/dist/types/commit/changelog/index.d.ts +1 -0
  24. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  25. package/dist/types/config/autorouting-tier-map.d.ts +53 -1
  26. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  27. package/dist/types/config/config-hot-reload.d.ts +44 -0
  28. package/dist/types/config/file-lock.d.ts +52 -3
  29. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  30. package/dist/types/config/model-profile-activation.d.ts +79 -9
  31. package/dist/types/config/model-registry.d.ts +72 -8
  32. package/dist/types/config/model-resolver.d.ts +7 -4
  33. package/dist/types/config/models-config-schema.d.ts +10 -0
  34. package/dist/types/config/settings-schema.d.ts +57 -4
  35. package/dist/types/config/settings.d.ts +33 -1
  36. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  37. package/dist/types/customization/import.d.ts +2 -0
  38. package/dist/types/customization/types.d.ts +1 -1
  39. package/dist/types/dap/config.d.ts +8 -4
  40. package/dist/types/dap/session.d.ts +11 -2
  41. package/dist/types/debug/system-info.d.ts +6 -1
  42. package/dist/types/discovery/helpers.d.ts +7 -2
  43. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  44. package/dist/types/edit/index.d.ts +5 -0
  45. package/dist/types/edit/modes/patch.d.ts +14 -0
  46. package/dist/types/edit/modes/replace.d.ts +3 -0
  47. package/dist/types/eval/py/runtime.d.ts +9 -0
  48. package/dist/types/exec/bash-executor.d.ts +12 -0
  49. package/dist/types/export/ttsr.d.ts +19 -0
  50. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  51. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  52. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  53. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  54. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  55. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  56. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  57. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  58. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  59. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  60. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  61. package/dist/types/hindsight/content.d.ts +5 -6
  62. package/dist/types/internal-urls/types.d.ts +2 -0
  63. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  64. package/dist/types/main.d.ts +23 -18
  65. package/dist/types/migrate/action-planner.d.ts +10 -1
  66. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  67. package/dist/types/migrate/adapters/index.d.ts +42 -1
  68. package/dist/types/migrate/executor.d.ts +3 -1
  69. package/dist/types/migrate/hook-module.d.ts +23 -0
  70. package/dist/types/migrate/report.d.ts +2 -1
  71. package/dist/types/migrate/types.d.ts +82 -3
  72. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  73. package/dist/types/modes/components/model-selector.d.ts +43 -0
  74. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  75. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  76. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  77. package/dist/types/modes/theme/theme.d.ts +1 -0
  78. package/dist/types/modes/utils/host-status.d.ts +15 -11
  79. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  80. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  81. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  82. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  83. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  84. package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
  85. package/dist/types/sdk/broker/broker.d.ts +29 -2
  86. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  87. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  88. package/dist/types/sdk/broker/ensure.d.ts +141 -9
  89. package/dist/types/sdk/broker/hop.d.ts +45 -0
  90. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  91. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  92. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  93. package/dist/types/sdk/broker/session-index.d.ts +6 -1
  94. package/dist/types/sdk/broker/transport.d.ts +2 -0
  95. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  96. package/dist/types/sdk/bus/index.d.ts +44 -0
  97. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  98. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
  99. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  100. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  101. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  102. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  103. package/dist/types/sdk/host/events.d.ts +4 -0
  104. package/dist/types/sdk/host/host.d.ts +2 -1
  105. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +40 -8
  107. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  109. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  111. package/dist/types/sdk/prompt-status.d.ts +11 -4
  112. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  113. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  114. package/dist/types/sdk/session-list.d.ts +1 -1
  115. package/dist/types/session/agent-session.d.ts +107 -9
  116. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  117. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  118. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  119. package/dist/types/session/messages.d.ts +5 -1
  120. package/dist/types/session/session-manager.d.ts +29 -3
  121. package/dist/types/session/startup-auth-config.d.ts +24 -2
  122. package/dist/types/session/streaming-output.d.ts +11 -0
  123. package/dist/types/session/terminal-abort.d.ts +3 -1
  124. package/dist/types/session-import/service.d.ts +1 -1
  125. package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
  126. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  127. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  128. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  129. package/dist/types/system-prompt.d.ts +3 -2
  130. package/dist/types/task/executor.d.ts +19 -2
  131. package/dist/types/task/role-body.d.ts +11 -0
  132. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  133. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  134. package/dist/types/tools/awaiting-input.d.ts +28 -0
  135. package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
  136. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  137. package/dist/types/tools/bash.d.ts +7 -0
  138. package/dist/types/tools/descriptors.d.ts +15 -2
  139. package/dist/types/tools/fetch.d.ts +7 -1
  140. package/dist/types/tools/index.d.ts +2 -0
  141. package/dist/types/tools/irc.d.ts +5 -3
  142. package/dist/types/tools/python.d.ts +1 -1
  143. package/dist/types/tools/report-issue.d.ts +41 -0
  144. package/dist/types/tools/subagent.d.ts +23 -0
  145. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  146. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  147. package/dist/types/tools/write.d.ts +2 -0
  148. package/dist/types/utils/file-mentions.d.ts +5 -0
  149. package/dist/types/utils/image-limits.d.ts +7 -0
  150. package/dist/types/utils/image-loading.d.ts +0 -1
  151. package/dist/types/utils/open.d.ts +0 -1
  152. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  153. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  154. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  155. package/dist/types/utils/title-generator.d.ts +13 -3
  156. package/dist/types/web/scrapers/types.d.ts +8 -1
  157. package/dist/types/web/search/providers/codex.d.ts +6 -7
  158. package/dist/types/web/search/providers/insane.d.ts +7 -1
  159. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  160. package/package.json +10 -9
  161. package/scripts/acp-conformance-agent.ts +12 -2
  162. package/scripts/build-binary.ts +2 -1
  163. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  164. package/scripts/generate-docs-index.ts +8 -1
  165. package/scripts/generate-sdk-operation-inventory.ts +38 -4
  166. package/src/async/job-manager.ts +356 -56
  167. package/src/async/result-formatting.ts +40 -0
  168. package/src/autoresearch/git.ts +9 -2
  169. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  170. package/src/capability/fs.ts +58 -22
  171. package/src/capability/index.ts +2 -2
  172. package/src/capability/rule.ts +11 -2
  173. package/src/cli/accounts-cli.ts +176 -8
  174. package/src/cli/append-system-prompt.ts +12 -0
  175. package/src/cli/args.ts +11 -7
  176. package/src/cli/auth-broker-cli.ts +21 -11
  177. package/src/cli/customize-doctor-precedence.ts +31 -0
  178. package/src/cli/customize-doctor.ts +49 -39
  179. package/src/cli/doctor/install-repairs.ts +35 -5
  180. package/src/cli/fast-help.ts +1 -3
  181. package/src/cli/install-activation.ts +47 -4
  182. package/src/cli/migrate-cli.ts +19 -6
  183. package/src/cli/public-command-errors.ts +6 -0
  184. package/src/cli/root-flags.ts +2 -4
  185. package/src/cli/stats-cli.ts +9 -5
  186. package/src/cli/update-cli.ts +471 -13
  187. package/src/cli-ordinary.ts +7 -2
  188. package/src/commands/accounts.ts +10 -5
  189. package/src/commands/harness.ts +25 -15
  190. package/src/commands/migrate.ts +3 -3
  191. package/src/commands/sdk.ts +277 -50
  192. package/src/commands/ultragoal.ts +16 -8
  193. package/src/commit/agentic/agent.ts +6 -6
  194. package/src/commit/analysis/conventional.ts +14 -5
  195. package/src/commit/changelog/index.ts +39 -1
  196. package/src/commit/map-reduce/index.ts +7 -7
  197. package/src/commit/message.ts +10 -3
  198. package/src/commit/pipeline.ts +12 -12
  199. package/src/config/autorouting-tier-map.ts +50 -1
  200. package/src/config/config-file.ts +106 -3
  201. package/src/config/config-hot-reload-trust.ts +148 -0
  202. package/src/config/config-hot-reload.ts +742 -0
  203. package/src/config/file-lock.ts +1615 -328
  204. package/src/config/keybindings.ts +154 -7
  205. package/src/config/model-bindings-applier.ts +85 -47
  206. package/src/config/model-discovery-manager.ts +10 -0
  207. package/src/config/model-profile-activation.ts +889 -148
  208. package/src/config/model-registry.ts +1706 -205
  209. package/src/config/model-resolver.ts +52 -22
  210. package/src/config/models-config-schema.ts +6 -0
  211. package/src/config/settings-schema.ts +73 -4
  212. package/src/config/settings.ts +460 -24
  213. package/src/coordinator-mcp/policy.ts +26 -1
  214. package/src/coordinator-mcp/server.ts +37 -2
  215. package/src/crash/sanitize.ts +7 -1
  216. package/src/customization/import.ts +279 -8
  217. package/src/customization/inventory.ts +24 -8
  218. package/src/customization/types.ts +1 -0
  219. package/src/dap/client.ts +3 -2
  220. package/src/dap/config.ts +31 -11
  221. package/src/dap/defaults.json +1 -1
  222. package/src/dap/session.ts +161 -36
  223. package/src/debug/crash-diagnostics.ts +89 -6
  224. package/src/debug/report-bundle.ts +90 -2
  225. package/src/debug/system-info.ts +8 -3
  226. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  227. package/src/discovery/agents-md.ts +1 -1
  228. package/src/discovery/builtin.ts +4 -3
  229. package/src/discovery/helpers.ts +25 -16
  230. package/src/discovery/mcp-compat.ts +120 -17
  231. package/src/edit/apply-patch/index.ts +12 -1
  232. package/src/edit/index.ts +204 -22
  233. package/src/edit/modes/patch.ts +214 -8
  234. package/src/edit/modes/replace.ts +136 -0
  235. package/src/eval/js/context-manager.ts +29 -3
  236. package/src/eval/js/worker-entry.ts +51 -18
  237. package/src/eval/py/executor.ts +14 -4
  238. package/src/eval/py/runner.py +144 -5
  239. package/src/eval/py/runtime.ts +44 -4
  240. package/src/exec/bash-executor.ts +81 -4
  241. package/src/exec/bash-shell-guardian.ts +4 -1
  242. package/src/exec/bash-shell-supervisor.ts +8 -4
  243. package/src/exec/isolated-shell.ts +5 -1
  244. package/src/exec/non-interactive-env.ts +0 -1
  245. package/src/export/html/index.ts +86 -6
  246. package/src/export/ttsr.ts +176 -1
  247. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  248. package/src/extensibility/extensions/loader.ts +10 -1
  249. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  250. package/src/extensibility/extensions/types.ts +5 -1
  251. package/src/extensibility/hooks/loader.ts +10 -2
  252. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  253. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  254. package/src/extensibility/slash-commands.ts +5 -0
  255. package/src/gjc-runtime/gc-runtime.ts +37 -7
  256. package/src/gjc-runtime/launch-tmux.ts +30 -15
  257. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  258. package/src/gjc-runtime/launch-worktree.ts +101 -14
  259. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  260. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  261. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  262. package/src/gjc-runtime/session-resolution.ts +38 -6
  263. package/src/gjc-runtime/session-state-lock.ts +59 -14
  264. package/src/gjc-runtime/state-runtime.ts +23 -19
  265. package/src/gjc-runtime/state-writer.ts +109 -5
  266. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  267. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  268. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  269. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  270. package/src/harness-control-plane/finalize.ts +11 -0
  271. package/src/harness-control-plane/receipt-spool.ts +105 -28
  272. package/src/harness-control-plane/storage.ts +124 -6
  273. package/src/hindsight/content.ts +116 -16
  274. package/src/hindsight/mental-models.ts +22 -10
  275. package/src/hindsight/state.ts +7 -4
  276. package/src/hooks/native-skill-hook.ts +17 -12
  277. package/src/internal-urls/artifact-protocol.ts +3 -1
  278. package/src/internal-urls/docs-index.generated.ts +23 -22
  279. package/src/internal-urls/local-protocol.ts +167 -8
  280. package/src/internal-urls/types.ts +2 -0
  281. package/src/lsp/client.ts +2 -1
  282. package/src/lsp/clients/biome-client.ts +2 -0
  283. package/src/lsp/clients/swiftlint-client.ts +2 -0
  284. package/src/lsp/index.ts +29 -0
  285. package/src/lsp/workspace-path.ts +23 -0
  286. package/src/main.ts +330 -92
  287. package/src/memories/index.ts +25 -2
  288. package/src/migrate/action-planner.ts +574 -2
  289. package/src/migrate/adapters/claude-code.ts +162 -5
  290. package/src/migrate/adapters/codex.ts +77 -7
  291. package/src/migrate/adapters/index.ts +258 -5
  292. package/src/migrate/adapters/opencode.ts +10 -1
  293. package/src/migrate/executor.ts +222 -7
  294. package/src/migrate/hook-module.ts +193 -0
  295. package/src/migrate/report.ts +63 -2
  296. package/src/migrate/types.ts +84 -4
  297. package/src/modes/DESIGN.md +51 -0
  298. package/src/modes/acp/acp-agent.ts +10 -1
  299. package/src/modes/acp/permission-mode.ts +18 -1
  300. package/src/modes/components/agent-dashboard.ts +20 -4
  301. package/src/modes/components/assistant-message.ts +55 -1
  302. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  303. package/src/modes/components/customization/import-wizard.ts +29 -3
  304. package/src/modes/components/model-selector.ts +157 -103
  305. package/src/modes/components/settings-selector.ts +103 -37
  306. package/src/modes/components/welcome.ts +3 -1
  307. package/src/modes/controllers/command-controller.ts +300 -9
  308. package/src/modes/controllers/event-controller.ts +1 -1
  309. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  310. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  311. package/src/modes/controllers/input-controller.ts +2 -0
  312. package/src/modes/controllers/plan-mode-controller.ts +66 -11
  313. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  314. package/src/modes/controllers/selector-controller.ts +76 -7
  315. package/src/modes/interactive-mode.ts +123 -4
  316. package/src/modes/prompt-suggestion-controller.ts +1 -1
  317. package/src/modes/runtime-init.ts +5 -1
  318. package/src/modes/theme/theme.ts +15 -0
  319. package/src/modes/utils/host-status.ts +21 -10
  320. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  321. package/src/prompts/system/irc-incoming.md +3 -1
  322. package/src/prompts/tools/debug.md +1 -1
  323. package/src/prompts/tools/irc.md +1 -1
  324. package/src/prompts/tools/read.md +1 -1
  325. package/src/prompts/tools/report-issue.md +13 -0
  326. package/src/prompts/tools/task.md +1 -1
  327. package/src/runtime-credential-selector.ts +2 -2
  328. package/src/runtime-mcp/content-limits.ts +7 -0
  329. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  330. package/src/runtime-mcp/manager.ts +40 -3
  331. package/src/runtime-mcp/oauth-flow.ts +32 -6
  332. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  333. package/src/runtime-mcp/project-host-values.ts +12 -8
  334. package/src/runtime-mcp/render.ts +16 -1
  335. package/src/runtime-mcp/tool-bridge.ts +194 -21
  336. package/src/runtime-mcp/transports/stdio.ts +2 -1
  337. package/src/sdk/broker/broker-exit.ts +10 -5
  338. package/src/sdk/broker/broker.ts +484 -85
  339. package/src/sdk/broker/daemon-entry.ts +338 -116
  340. package/src/sdk/broker/doctor-restart.ts +2 -1
  341. package/src/sdk/broker/ensure.ts +1214 -268
  342. package/src/sdk/broker/hop.ts +286 -0
  343. package/src/sdk/broker/lifecycle-ledger.ts +38 -11
  344. package/src/sdk/broker/lifecycle.ts +102 -6
  345. package/src/sdk/broker/lock-host-identity.ts +29 -0
  346. package/src/sdk/broker/session-index.ts +91 -16
  347. package/src/sdk/broker/transport.ts +17 -1
  348. package/src/sdk/bus/chat-command-policy.ts +16 -3
  349. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  350. package/src/sdk/bus/index.ts +460 -35
  351. package/src/sdk/bus/reconciliation-store.ts +201 -8
  352. package/src/sdk/bus/session-control.ts +2 -0
  353. package/src/sdk/bus/slack-daemon.ts +105 -9
  354. package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
  355. package/src/sdk/bus/telegram-daemon.ts +10 -2
  356. package/src/sdk/bus/threaded-render.ts +20 -1
  357. package/src/sdk/cli/session-cli.ts +23 -6
  358. package/src/sdk/host/control/dispatch.ts +13 -1
  359. package/src/sdk/host/control/operations.ts +10 -1
  360. package/src/sdk/host/events.ts +67 -4
  361. package/src/sdk/host/host.ts +8 -2
  362. package/src/sdk/host/prompt-image-upload.ts +438 -0
  363. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  364. package/src/sdk/host/session-runtime.test.ts +1737 -129
  365. package/src/sdk/host/session-runtime.ts +684 -83
  366. package/src/sdk/host/surface-policy.ts +4 -0
  367. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  368. package/src/sdk/host/websocket-transport.ts +19 -7
  369. package/src/sdk/lifecycle/service.ts +15 -1
  370. package/src/sdk/prompt-deadline-flush.ts +78 -5
  371. package/src/sdk/prompt-deadline-manager.ts +68 -14
  372. package/src/sdk/prompt-status.ts +6 -4
  373. package/src/sdk/protocol/adapter-validation.ts +81 -12
  374. package/src/sdk/protocol/operation-inventory.generated.json +261 -2
  375. package/src/sdk/protocol/operation-registry.ts +33 -3
  376. package/src/sdk/reconciliation-extensions.ts +1 -0
  377. package/src/sdk/session-list.ts +1 -0
  378. package/src/sdk/session.ts +237 -97
  379. package/src/session/agent-session.ts +2726 -479
  380. package/src/session/history-storage.ts +182 -12
  381. package/src/session/internal/managed-session-scope.ts +173 -57
  382. package/src/session/internal/managed-session-storage.ts +85 -12
  383. package/src/session/internal/native-publish-outcome.ts +5 -1
  384. package/src/session/messages.ts +47 -6
  385. package/src/session/session-manager.ts +326 -19
  386. package/src/session/startup-auth-config.ts +116 -8
  387. package/src/session/streaming-output.ts +262 -4
  388. package/src/session/terminal-abort.ts +14 -2
  389. package/src/session-import/redact.ts +53 -2
  390. package/src/session-import/service.ts +1 -0
  391. package/src/skill-state/workflow-mutation-guard.ts +2145 -206
  392. package/src/slash-commands/builtin-registry.ts +21 -6
  393. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  394. package/src/slash-commands/helpers/mcp.ts +11 -3
  395. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  396. package/src/ssh/ssh-executor.ts +9 -2
  397. package/src/stt/recorder.ts +26 -9
  398. package/src/system-prompt.ts +61 -16
  399. package/src/task/executor.ts +115 -20
  400. package/src/task/index.ts +389 -175
  401. package/src/task/role-body.ts +84 -0
  402. package/src/task/token-log-mode.test.ts +162 -0
  403. package/src/task/token-log.ts +91 -2
  404. package/src/tools/archive-reader.ts +650 -37
  405. package/src/tools/ask.ts +1 -0
  406. package/src/tools/ast-edit-write-path.ts +64 -0
  407. package/src/tools/ast-edit.ts +18 -1
  408. package/src/tools/ast-grep.ts +7 -2
  409. package/src/tools/atomic-file-write.ts +46 -15
  410. package/src/tools/auto-generated-guard.ts +15 -3
  411. package/src/tools/awaiting-input.ts +114 -0
  412. package/src/tools/bash-allowed-prefixes.ts +214 -6
  413. package/src/tools/bash-interactive.ts +14 -2
  414. package/src/tools/bash-skill-urls.ts +58 -5
  415. package/src/tools/bash.ts +113 -26
  416. package/src/tools/browser/tab-worker.ts +39 -13
  417. package/src/tools/browser.ts +17 -7
  418. package/src/tools/cron.ts +10 -1
  419. package/src/tools/debug.ts +16 -8
  420. package/src/tools/descriptors.test.ts +44 -0
  421. package/src/tools/descriptors.ts +50 -6
  422. package/src/tools/eval.ts +15 -0
  423. package/src/tools/fetch.ts +93 -4
  424. package/src/tools/gh.ts +27 -12
  425. package/src/tools/image-gen.ts +20 -6
  426. package/src/tools/index.ts +2 -0
  427. package/src/tools/irc.ts +5 -3
  428. package/src/tools/monitor.ts +9 -1
  429. package/src/tools/output-meta.ts +74 -5
  430. package/src/tools/python.ts +1 -1
  431. package/src/tools/read.ts +26 -37
  432. package/src/tools/recipe/runner.ts +23 -12
  433. package/src/tools/render-utils.ts +2 -2
  434. package/src/tools/report-issue.ts +184 -0
  435. package/src/tools/search.ts +3 -2
  436. package/src/tools/ssh.ts +274 -8
  437. package/src/tools/subagent-render.ts +114 -12
  438. package/src/tools/subagent.ts +118 -5
  439. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  440. package/src/tools/todo-write.ts +41 -3
  441. package/src/tools/tool-catalog.generated.ts +32 -4
  442. package/src/tools/ultragoal-ask-guard.ts +13 -1
  443. package/src/tools/write.ts +42 -3
  444. package/src/tui/tree-list.ts +15 -1
  445. package/src/utils/commit-message-generator.ts +12 -4
  446. package/src/utils/external-editor.ts +21 -7
  447. package/src/utils/file-mentions.ts +75 -7
  448. package/src/utils/git.ts +38 -7
  449. package/src/utils/image-limits.ts +8 -0
  450. package/src/utils/image-loading.ts +1 -1
  451. package/src/utils/open.ts +73 -4
  452. package/src/utils/pasted-image-loading.ts +9 -8
  453. package/src/utils/prompt-suggestion.ts +5 -4
  454. package/src/utils/shell-snapshot.ts +9 -1
  455. package/src/utils/title-generator.ts +72 -22
  456. package/src/web/scrapers/index.ts +3 -1
  457. package/src/web/scrapers/types.ts +8 -0
  458. package/src/web/scrapers/youtube.ts +3 -2
  459. package/src/web/search/index.ts +2 -1
  460. package/src/web/search/providers/codex.ts +29 -32
  461. package/src/web/search/providers/insane.ts +91 -28
  462. package/src/web/search/providers/searxng.ts +62 -10
@@ -71,7 +71,7 @@ import {
71
71
  type WorkflowGateTerminalController,
72
72
  type WorkflowGateTerminalProof,
73
73
  } from "../../modes/shared/agent-wire/workflow-gate-broker";
74
- import type { AgentSessionEvent } from "../../session/agent-session";
74
+ import type { AgentSessionEvent, QueuedRunStartFailure } from "../../session/agent-session";
75
75
  import type { ClientBridge } from "../../session/client-bridge";
76
76
  import {
77
77
  createSessionWorkLease,
@@ -102,6 +102,7 @@ import {
102
102
  registerAskAnswerSource,
103
103
  registerWorkflowGateEmitterListener,
104
104
  } from "../../tools/ask-answer-registry";
105
+ import type { AwaitingInputKind } from "../../tools/awaiting-input";
105
106
  import { acpFinalTextFromMessage } from "../acp/final-text";
106
107
  import { ensureBroker } from "../broker/ensure";
107
108
  import { publishSessionHostRuntimeEvidence, type SessionHostRuntimePublication } from "../broker/lifecycle";
@@ -117,11 +118,13 @@ import {
117
118
  type SessionSdkHost,
118
119
  SessionSdkSessionRuntime,
119
120
  shouldHostSdk,
121
+ TURN_STREAM_CAPABILITY,
120
122
  verifyMasterCapabilityFrame,
121
123
  } from "../host";
122
124
  import { type AbortScope, type ControlSurface, dispatchControl, TypedControlError } from "../host/control";
123
125
  import { BROKER_RUNTIME_CLOSE_CAPABILITY_FIELD } from "../host/control/runtime-gate";
124
126
  import { isAutoroutingInactive, markAutoroutingInactive } from "../host/internal-autorouting-state";
127
+ import { PromptImageUploadStore, retirePromptImageReservation } from "../host/prompt-image-upload";
125
128
  import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "../host/query";
126
129
  import { RESPONSE_CEILING_BYTES } from "../host/query/handlers";
127
130
  import type { SdkFrame } from "../host/types";
@@ -132,7 +135,7 @@ import {
132
135
  syntheticModelInputError,
133
136
  syntheticNamespaceCollision,
134
137
  } from "../model-profile-model";
135
- import { flushWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
138
+ import { autosaveWorktreeOnPromptDeadline, deadlineFlushSuppressed } from "../prompt-deadline-flush";
136
139
  import {
137
140
  createPromptDeadlineLease,
138
141
  isAttributableProgressEventType,
@@ -1235,6 +1238,10 @@ export class PresentationArbiter {
1235
1238
  interface SessionRuntime {
1236
1239
  server: NotificationServer;
1237
1240
  host: SessionSdkHost;
1241
+ imageUploads: PromptImageUploadStore | undefined;
1242
+ releaseAcceptedImage: (correlation: { commandId: string; turnId: string }) => void;
1243
+ releaseAcceptedImagesForRun: (owner: AgentTerminalOwnerContext) => void;
1244
+ releaseAcceptedImages: () => void;
1238
1245
  getJoinedPromptCorrelations: (owner: AgentTerminalOwnerContext) => Array<{ commandId: string; turnId: string }>;
1239
1246
  isPromptRunOwner: (correlation: { commandId: string; turnId: string }, owner: AgentTerminalOwnerContext) => boolean;
1240
1247
  retireJoinedPromptOwners: () => void;
@@ -1421,6 +1428,12 @@ interface SessionRuntime {
1421
1428
  string,
1422
1429
  { toolName: string; args?: unknown; pendingPhase?: "completed" | "failed" | "cancelled" }
1423
1430
  >;
1431
+ /**
1432
+ * Running tool calls that reported an awaiting-input signal, keyed by tool call
1433
+ * id. An entry stays undelivered while notifications are inactive or policy is
1434
+ * provisional, and is dropped when the call ends; one notification per call.
1435
+ */
1436
+ awaitingInput: Map<string, { toolName: string; kind: AwaitingInputKind; delivered: boolean }>;
1424
1437
  /** Cancels the postmortem cleanup that emits `session_closed` on process teardown. */
1425
1438
  cancelPostmortemCleanup: () => void;
1426
1439
  /** Disposes side-turn resources when their owning logical session becomes unavailable. */
@@ -1431,6 +1444,48 @@ const SENSITIVE_MODEL_LABEL =
1431
1444
  /(?:\b(?:https?|wss?):\/\/|\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b|\b(?:api[-_ ]?key|access[-_ ]?token|bearer|secret|password|account(?:\s*id)?|email|exception|stack trace)\b|\b(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}\b)/i;
1432
1445
  const TOOL_SUMMARY_MAX = 280;
1433
1446
 
1447
+ /** The `awaitingInput` signal a running bash update carries, validated at the extension boundary. */
1448
+ function readAwaitingInput(partialResult: unknown): { kind: AwaitingInputKind } | undefined {
1449
+ if (!partialResult || typeof partialResult !== "object") return undefined;
1450
+ const details = (partialResult as { details?: unknown }).details;
1451
+ if (!details || typeof details !== "object") return undefined;
1452
+ const signal = (details as { awaitingInput?: unknown }).awaitingInput;
1453
+ if (!signal || typeof signal !== "object") return undefined;
1454
+ const kind = (signal as { kind?: unknown }).kind;
1455
+ return kind === "browser_auth" || kind === "device_code" ? { kind } : undefined;
1456
+ }
1457
+
1458
+ /**
1459
+ * Deliver every undelivered awaiting-input notice once stable policy admits it.
1460
+ * The notice describes the present ("a command is waiting now"), so it goes out
1461
+ * on the live native leg only and never enters the replay ring: a client that
1462
+ * reconnects after the command exited must not receive it. The native server
1463
+ * still withholds it from connections without `awaiting_input_v1`.
1464
+ */
1465
+ function deliverAwaitingInput(
1466
+ runtime: Pick<SessionRuntime, "server" | "awaitingInput" | "notificationsActive" | "policySuspended">,
1467
+ sessionId: string,
1468
+ ): void {
1469
+ if (!runtime.notificationsActive || runtime.policySuspended) return;
1470
+ for (const [toolCallId, entry] of runtime.awaitingInput) {
1471
+ if (entry.delivered) continue;
1472
+ entry.delivered = true;
1473
+ try {
1474
+ runtime.server.pushFrame(
1475
+ JSON.stringify({
1476
+ type: "awaiting_input",
1477
+ sessionId,
1478
+ toolCallId,
1479
+ toolName: entry.toolName,
1480
+ kind: entry.kind,
1481
+ }),
1482
+ );
1483
+ } catch (e) {
1484
+ logger.warn(`notifications: awaiting_input failed: ${String(e)}`);
1485
+ }
1486
+ }
1487
+ }
1488
+
1434
1489
  /** Stable projection of the tool-owned safe-display seam (never the full Tool surface). */
1435
1490
  type SafeSummaryTool = Pick<Tool, "safeSummary" | "safeSummaryFields">;
1436
1491
 
@@ -2459,11 +2514,13 @@ function sdkQuerySurface(
2459
2514
  }),
2460
2515
  getRuntimeHost: () => SessionSdkHost | undefined = () => undefined,
2461
2516
  primaryControlSurface: "cli" | "sdk" = "sdk",
2517
+ imageUploadsAvailable = false,
2462
2518
  ): SessionSurface {
2463
2519
  return createSdkSurfaceFactory({
2464
2520
  ctx,
2465
2521
  id,
2466
2522
  api,
2523
+ imageUploadsAvailable,
2467
2524
  getInstalledDefinitions,
2468
2525
  getLiveState,
2469
2526
  configOverrides,
@@ -2612,6 +2669,19 @@ function sdkControlSurface(
2612
2669
  pendingInteractive: Map<string, PendingInteractiveAsk>,
2613
2670
  gatePresentations: PresentationArbiter | undefined,
2614
2671
  api: ExtensionAPI,
2672
+ imageUploads: PromptImageUploadStore | undefined,
2673
+ imageUploadsAvailable: boolean,
2674
+ retainAcceptedImage: (
2675
+ correlation: { commandId: string; turnId: string },
2676
+ release: () => void,
2677
+ preflightSignal: AbortSignal,
2678
+ submissionSettled: Promise<void>,
2679
+ ) => {
2680
+ accepted: () => void;
2681
+ promoted: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
2682
+ refused: () => boolean;
2683
+ },
2684
+ releaseAcceptedImage: (correlation: { commandId: string; turnId: string }) => void,
2615
2685
  isBusy: () => boolean,
2616
2686
  onPromptAccepted: (
2617
2687
  correlation: { commandId: string; turnId: string },
@@ -2626,6 +2696,12 @@ function sdkControlSurface(
2626
2696
  correlation: { commandId: string; turnId: string },
2627
2697
  error: unknown,
2628
2698
  ) => void | Promise<void> = () => {},
2699
+ onQueuedRunStartFailed: (
2700
+ correlation: { commandId: string; turnId: string },
2701
+ failure: QueuedRunStartFailure,
2702
+ sdkRunToken: string | undefined,
2703
+ refuseImage: (() => boolean) | undefined,
2704
+ ) => boolean,
2629
2705
  onPromptAcceptFailed: (correlation: { commandId: string; turnId: string }) => void = () => {},
2630
2706
  onPromptDiverted: (correlation: { commandId: string; turnId: string }) => void,
2631
2707
  onPromptPromoted: (
@@ -2659,6 +2735,10 @@ function sdkControlSurface(
2659
2735
  // the session-wide preflight seam must not be invoked while another
2660
2736
  // connection's active preflight could be cancelled by it (review P1).
2661
2737
  noOtherConnectionPreflights?: () => boolean,
2738
+ // True only for an operator terminal abort that the broker control route already
2739
+ // authorized (`TerminalAbortInput.operator`, which a public SDK caller cannot grant):
2740
+ // it addresses the session's active turn, not the requesting connection's own prompt.
2741
+ operatorAuthorized?: boolean,
2662
2742
  ) => Promise<
2663
2743
  | {
2664
2744
  ok: true;
@@ -2723,6 +2803,7 @@ function sdkControlSurface(
2723
2803
  ctx,
2724
2804
  id: ctx.sessionManager.getSessionId(),
2725
2805
  api,
2806
+ imageUploadsAvailable,
2726
2807
  }).policy;
2727
2808
  const missingExpectedSessionAudits = new Set<"workflow.gate_answer" | "workflow.plan_approve">();
2728
2809
  const auditMissingExpectedSessionId = (operation: "workflow.gate_answer" | "workflow.plan_approve") => {
@@ -2908,6 +2989,18 @@ function sdkControlSurface(
2908
2989
  requesterConnectionId?: string,
2909
2990
  clientRef?: string,
2910
2991
  trackReconciliation = false,
2992
+ onCorrelation?: (
2993
+ correlation: { commandId: string; turnId: string },
2994
+ preflightSignal: AbortSignal,
2995
+ submissionSettled: Promise<void>,
2996
+ ) =>
2997
+ | {
2998
+ accepted: () => void;
2999
+ promoted: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
3000
+ refused: () => boolean;
3001
+ }
3002
+ | undefined,
3003
+ resolveImages?: () => unknown,
2911
3004
  ) => {
2912
3005
  const trimmedClientRef = typeof clientRef === "string" ? clientRef.trim() : undefined;
2913
3006
  if (clientRef !== undefined && (!trimmedClientRef || trimmedClientRef.length > PROMPT_CLIENT_REF_MAX_LENGTH))
@@ -2953,6 +3046,8 @@ function sdkControlSurface(
2953
3046
  code: "busy",
2954
3047
  },
2955
3048
  );
3049
+ // Staged bytes are one-shot: reserve reconciliation before consuming them.
3050
+ if (resolveImages) images = resolveImages();
2956
3051
  } catch (error) {
2957
3052
  admission.abandon();
2958
3053
  if (trackReconciliation) releasePromptAdmission(trimmedClientRef);
@@ -2984,6 +3079,8 @@ function sdkControlSurface(
2984
3079
  const submissionSettled = Promise.withResolvers<void>();
2985
3080
  let cancellation: Promise<void> | undefined;
2986
3081
  const correlation = { commandId, turnId };
3082
+ let queuedRunRefused = false;
3083
+ const imageLifecycle = onCorrelation?.(correlation, preflightController.signal, submissionSettled.promise);
2987
3084
  const key = preflightKey(requesterConnectionId, correlation);
2988
3085
  const settlePreflight = (result: PreflightTerminalResult) => {
2989
3086
  if (preflightSettled) return;
@@ -3034,6 +3131,8 @@ function sdkControlSurface(
3034
3131
  admission.accepted();
3035
3132
  accepting = false;
3036
3133
  accepted = true;
3134
+ // A late durable acceptance after direct abort cannot end image retention.
3135
+ if (!preflightController.signal.aborted) imageLifecycle?.accepted();
3037
3136
  if (effectiveDeliverAs !== undefined) onPromptDiverted(correlation);
3038
3137
  // #4743: an accepted run owns a durable terminal publication when it
3039
3138
  // settles; teardown joins it via this latch (never-accepted preflights
@@ -3061,13 +3160,22 @@ function sdkControlSurface(
3061
3160
  onPreflightAcceptCommit,
3062
3161
  onPreflightAccepted,
3063
3162
  onQueuedPromoted: promotion => {
3163
+ if (queuedRunRefused) return;
3164
+ if (promotion.startsOwnRun === true) imageLifecycle?.promoted(promotion);
3064
3165
  admission.hooks.onQueuedPromoted(promotion);
3065
3166
  onPromptPromoted(correlation, promotion, ctx.getActivePromptHandle());
3167
+ if (promotion.startsOwnRun !== true) imageLifecycle?.promoted(promotion);
3168
+ if (promotion.removed) releaseAcceptedImage(correlation);
3066
3169
  },
3067
3170
  onDispatchDisposition: disposition => {
3171
+ if (queuedRunRefused) return;
3068
3172
  admission.hooks.onDispatchDisposition(disposition);
3069
3173
  if (!disposition.startsOwnRun) onPromptDiverted(correlation);
3070
3174
  },
3175
+ onQueuedRunStartFailed: failure => {
3176
+ if (queuedRunRefused) return;
3177
+ queuedRunRefused = onQueuedRunStartFailed(correlation, failure, sdkRunToken, imageLifecycle?.refused);
3178
+ },
3071
3179
  preflightSignal: preflightController.signal,
3072
3180
  ...(sdkRunToken ? { sdkRunToken } : {}),
3073
3181
  }),
@@ -3075,7 +3183,7 @@ function sdkControlSurface(
3075
3183
  } catch (error) {
3076
3184
  submissionSettled.resolve();
3077
3185
  admission.abandon();
3078
- if (accepted && !preflightController.signal.aborted)
3186
+ if (accepted && !queuedRunRefused && !preflightController.signal.aborted)
3079
3187
  trackReconciliationProducer?.(Promise.resolve(onPromptFailed(correlation, error)));
3080
3188
  else settlePreflight({ status: "rejected", error });
3081
3189
  }
@@ -3097,7 +3205,7 @@ function sdkControlSurface(
3097
3205
  },
3098
3206
  error => {
3099
3207
  admission.abandon();
3100
- if (accepted && !preflightController.signal.aborted)
3208
+ if (accepted && !queuedRunRefused && !preflightController.signal.aborted)
3101
3209
  trackReconciliationProducer?.(Promise.resolve(onPromptFailed(correlation, error)));
3102
3210
  else settlePreflight({ status: "rejected", error });
3103
3211
  submissionSettled.resolve();
@@ -3120,8 +3228,52 @@ function sdkControlSurface(
3120
3228
  cancelPendingPreflights(): Promise<void>;
3121
3229
  cancelPendingPreflightsForConnection(connectionId: string): Promise<void>;
3122
3230
  } = {
3123
- prompt: (text, images, clientRef) =>
3124
- submitPrompt(text, images, false, undefined, true, controlRequesterContext.getStore(), clientRef, true),
3231
+ prompt: async (text, images, clientRef, stagedImages) => {
3232
+ if (stagedImages !== undefined && images !== undefined)
3233
+ throw Object.assign(new Error("Direct and staged images cannot be mixed."), { code: "invalid_input" });
3234
+ const requesterConnectionId = controlRequesterContext.getStore();
3235
+ let reservation: { images: ImageContent[]; release: () => void } | undefined;
3236
+ let correlation: { commandId: string; turnId: string } | undefined;
3237
+ try {
3238
+ return await submitPrompt(
3239
+ text,
3240
+ images,
3241
+ false,
3242
+ undefined,
3243
+ true,
3244
+ requesterConnectionId,
3245
+ clientRef,
3246
+ true,
3247
+ (current, preflightSignal, submissionSettled) => {
3248
+ correlation = current;
3249
+ if (reservation)
3250
+ return retainAcceptedImage(current, reservation.release, preflightSignal, submissionSettled);
3251
+ },
3252
+ stagedImages === undefined
3253
+ ? undefined
3254
+ : () => {
3255
+ if (!imageUploads)
3256
+ throw Object.assign(new Error("Staged images are unavailable."), {
3257
+ code: "image_upload_unavailable",
3258
+ });
3259
+ reservation = imageUploads.redeem(requesterConnectionId, stagedImages);
3260
+ return reservation.images;
3261
+ },
3262
+ );
3263
+ } catch (error) {
3264
+ if (correlation) releaseAcceptedImage(correlation);
3265
+ else reservation?.release();
3266
+ throw error;
3267
+ }
3268
+ },
3269
+ ...(imageUploads
3270
+ ? {
3271
+ imageBegin: (input: unknown) => imageUploads.begin(controlRequesterContext.getStore(), input),
3272
+ imageAppend: (input: unknown) => imageUploads.append(controlRequesterContext.getStore(), input),
3273
+ imageFinish: (input: unknown) => imageUploads.finish(controlRequesterContext.getStore(), input),
3274
+ imageDiscard: (input: unknown) => imageUploads.discard(controlRequesterContext.getStore(), input),
3275
+ }
3276
+ : {}),
3125
3277
  steer: (text, clientRef) => sendSteer(text, clientRef),
3126
3278
  followUp: text => submitPrompt(text, undefined, false, "followUp", false, controlRequesterContext.getStore()),
3127
3279
  abort: async () => {
@@ -3178,6 +3330,9 @@ function sdkControlSurface(
3178
3330
  // invoking it (review thread P1).
3179
3331
  () =>
3180
3332
  ![...pendingPreflightCancellations.values()].some(entry => entry.connectionId !== requesterConnectionId),
3333
+ // Broker-authorized operator authority is the one case where the abort
3334
+ // addresses a turn another connection submitted (see the parameter note).
3335
+ input.operator === true,
3181
3336
  );
3182
3337
  // Preflight cancellation happens ONLY for a NEWLY ADMITTED abort,
3183
3338
  // after the durable admission/replay decision inside
@@ -4281,6 +4436,34 @@ export function boundedCorrelatedAgentEndFrame(frame: Record<string, unknown>):
4281
4436
  };
4282
4437
  }
4283
4438
 
4439
+ /** Connection-scoped identity of a queued prompt submission an abort may address. */
4440
+ export interface TerminalAbortSubmissionScope {
4441
+ connectionId?: string;
4442
+ terminal: boolean;
4443
+ }
4444
+
4445
+ /**
4446
+ * Whether a terminal abort addresses this prompt submission.
4447
+ *
4448
+ * An ordinary abort is scoped to the requesting connection's own prompt: `connectionId`
4449
+ * identifies the connection that submitted it, and a submission that already reached a
4450
+ * terminal state is never addressed. Broker-authorized operator authority
4451
+ * (`TerminalAbortInput.operator`, granted only through the broker control route) is the
4452
+ * documented exception — "operator authority changes only connection ownership, never
4453
+ * those settlement proofs" — so it addresses the session's submission even when another
4454
+ * connection submitted it. Without that override every operator abort issued over a
4455
+ * second connection (the SDK client case, where each request is its own connection)
4456
+ * reported no_active_turn while the turn was still running.
4457
+ */
4458
+ export function terminalAbortAddressesSubmission(input: {
4459
+ connectionId: string | undefined;
4460
+ operatorAuthorized: boolean;
4461
+ submission: TerminalAbortSubmissionScope;
4462
+ }): boolean {
4463
+ if (input.submission.terminal) return false;
4464
+ return input.operatorAuthorized || input.submission.connectionId === input.connectionId;
4465
+ }
4466
+
4284
4467
  export function createNotificationsExtension(
4285
4468
  api: ExtensionAPI,
4286
4469
  options: {
@@ -4313,6 +4496,8 @@ export function createNotificationsExtension(
4313
4496
  * preflight outside the durable admission path (review thread P2).
4314
4497
  */
4315
4498
  terminalAbortSeams?: {
4499
+ /** Definitive event-driven observation; never mutates the owning run. */
4500
+ waitForRunSettlement?: (handle: string) => Promise<RunSettlementProof>;
4316
4501
  getTerminalRunOwnerForEvent?: (event: object) => AgentTerminalOwnerContext | undefined;
4317
4502
  getRunOwnerDomain?: (handle: string) => RunCancellationDomain | undefined;
4318
4503
  getTerminalTurnEpoch: () => number | undefined;
@@ -4602,6 +4787,7 @@ export function createNotificationsExtension(
4602
4787
  if (reason === "session" && requestedRuntime) {
4603
4788
  requestedRuntime.inboundFenced = true;
4604
4789
  requestedRuntime.stopping = true;
4790
+ requestedRuntime.imageUploads?.close();
4605
4791
  requestedRuntime.retireJoinedPromptOwners();
4606
4792
  requestedRuntime.abortEphemeralTurns();
4607
4793
  }
@@ -4780,6 +4966,7 @@ export function createNotificationsExtension(
4780
4966
  cleanupRetries.set(id, rt);
4781
4967
  throw new AggregateError(ownerReleaseFailures, `SDK notification runtime ${id} owner release failed.`);
4782
4968
  }
4969
+ rt.releaseAcceptedImages();
4783
4970
  if (cleanupRetries.get(id) === rt) cleanupRetries.delete(id);
4784
4971
  return true;
4785
4972
  }
@@ -5054,7 +5241,11 @@ export function createNotificationsExtension(
5054
5241
  };
5055
5242
 
5056
5243
  type HostConnectionIncarnation = { generation: number; closed: boolean };
5057
- type HostCapabilityCacheEntry = { generation: number; capabilities: ReadonlySet<string> };
5244
+ type HostCapabilityCacheEntry = {
5245
+ generation: number;
5246
+ capabilities: ReadonlySet<string>;
5247
+ nativeConfirmed: ReadonlySet<string>;
5248
+ };
5058
5249
  const hostCapCache = new Map<string, HostCapabilityCacheEntry>();
5059
5250
  const hostConnectionIncarnations = new Map<string, HostConnectionIncarnation>();
5060
5251
  const hostAttachedConnections = new Set<string>();
@@ -5068,12 +5259,28 @@ export function createNotificationsExtension(
5068
5259
  hostConnectionIncarnations.set(connectionId, incarnation);
5069
5260
  return incarnation;
5070
5261
  };
5071
- const rememberHostCapabilities = (connectionId: string, capabilities: readonly string[]): void => {
5262
+ const rememberHostCapabilities = (
5263
+ connectionId: string,
5264
+ capabilities: readonly string[],
5265
+ source: "replay" | "native",
5266
+ ): void => {
5072
5267
  const incarnation = liveHostConnection(connectionId);
5073
5268
  if (!incarnation) return;
5269
+ const previous = hostCapCache.get(connectionId);
5270
+ const merged =
5271
+ previous?.generation === incarnation.generation ? new Set(previous.capabilities) : new Set<string>();
5272
+ const nativeConfirmed =
5273
+ previous?.generation === incarnation.generation ? new Set(previous.nativeConfirmed) : new Set<string>();
5274
+ for (const capability of capabilities) merged.add(capability);
5275
+ // Replay claims enable stream routing immediately, but only native snapshots
5276
+ // can remove a socket from host demand. Merge delayed snapshots monotonically.
5277
+ if (source === "native") {
5278
+ for (const capability of capabilities) nativeConfirmed.add(capability);
5279
+ }
5074
5280
  hostCapCache.set(connectionId, {
5075
5281
  generation: incarnation.generation,
5076
- capabilities: new Set(capabilities),
5282
+ capabilities: merged,
5283
+ nativeConfirmed,
5077
5284
  });
5078
5285
  };
5079
5286
  const liveHostCapabilities = (connectionId: string): ReadonlySet<string> | undefined => {
@@ -5095,6 +5302,17 @@ export function createNotificationsExtension(
5095
5302
  hostCapCache.delete(connectionId);
5096
5303
  hostAttachedConnections.delete(connectionId);
5097
5304
  };
5305
+ const imageUploadsAvailable =
5306
+ typeof terminalAbortSeams?.waitForRunSettlement === "function" &&
5307
+ typeof terminalAbortSeams.getRunOwnerDomain === "function" &&
5308
+ typeof terminalAbortSeams.getTerminalRunOwnerForEvent === "function" &&
5309
+ typeof ctx.getActivePromptHandle === "function";
5310
+ const imageUploads = imageUploadsAvailable
5311
+ ? new PromptImageUploadStore(connectionId => {
5312
+ const incarnation = hostConnectionIncarnations.get(connectionId);
5313
+ return incarnation !== undefined && !incarnation.closed;
5314
+ })
5315
+ : undefined;
5098
5316
 
5099
5317
  const configOverrides = new Map<string, unknown>();
5100
5318
  const configRevision = { current: 0 };
@@ -5110,6 +5328,115 @@ export function createNotificationsExtension(
5110
5328
  const OWNED_SETTLEMENT_GRACE_MS = 500;
5111
5329
  const promptSubmissionKey = (correlation: { commandId: string; turnId: string }) =>
5112
5330
  `${correlation.commandId}:${correlation.turnId}`;
5331
+ type AcceptedImageReservation = {
5332
+ release: () => void;
5333
+ preflightSignal: AbortSignal;
5334
+ /** The producer can still retain ImageContent until settlement or exact run adoption. */
5335
+ submissionPending: boolean;
5336
+ /** A promoted queue entry needs its own run's proof before release authority transfers. */
5337
+ awaitingAdoption: boolean;
5338
+ accepted: boolean;
5339
+ releaseRequested: boolean;
5340
+ };
5341
+ const acceptedImages = new Map<string, AcceptedImageReservation>();
5342
+ // Absence means never consumed; a present undefined owner means execution
5343
+ // started without exact ownership evidence and must remain charged.
5344
+ const acceptedImageRunOwners = new Map<string, AgentTerminalOwnerContext | undefined>();
5345
+ const retiringImages = new Set<string>();
5346
+ const retireAcceptedImage = (key: string): void => {
5347
+ const reservation = acceptedImages.get(key);
5348
+ if (!reservation) return;
5349
+ if (reservation.submissionPending || reservation.awaitingAdoption) {
5350
+ reservation.releaseRequested = true;
5351
+ return;
5352
+ }
5353
+ if (retiringImages.has(key)) return;
5354
+ const discard = (): void => {
5355
+ if (acceptedImages.get(key) !== reservation) return;
5356
+ acceptedImages.delete(key);
5357
+ acceptedImageRunOwners.delete(key);
5358
+ retiringImages.delete(key);
5359
+ reservation.release();
5360
+ };
5361
+ const owner = acceptedImageRunOwners.get(key);
5362
+ if (!owner) {
5363
+ if (acceptedImageRunOwners.has(key)) {
5364
+ retiringImages.add(key);
5365
+ logger.warn("SDK image capacity retained for execution without an exact resource owner");
5366
+ return;
5367
+ }
5368
+ discard();
5369
+ return;
5370
+ }
5371
+ const waitForSettlement = terminalAbortSeams?.waitForRunSettlement;
5372
+ if (!waitForSettlement) return;
5373
+ retiringImages.add(key);
5374
+ retirePromptImageReservation(
5375
+ owner.resourceRunId,
5376
+ waitForSettlement,
5377
+ () => acceptedImages.get(key) === reservation,
5378
+ discard,
5379
+ );
5380
+ };
5381
+ const settleAcceptedImageSubmission = (key: string, reservation: AcceptedImageReservation): void => {
5382
+ if (acceptedImages.get(key) !== reservation || !reservation.submissionPending) return;
5383
+ reservation.submissionPending = false;
5384
+ if (reservation.releaseRequested) retireAcceptedImage(key);
5385
+ };
5386
+ const retainAcceptedImage = (
5387
+ correlation: { commandId: string; turnId: string },
5388
+ release: () => void,
5389
+ preflightSignal: AbortSignal,
5390
+ submissionSettled: Promise<void>,
5391
+ ): {
5392
+ accepted: () => void;
5393
+ promoted: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
5394
+ refused: () => boolean;
5395
+ } => {
5396
+ const key = promptSubmissionKey(correlation);
5397
+ const reservation: AcceptedImageReservation = {
5398
+ release,
5399
+ preflightSignal,
5400
+ submissionPending: true,
5401
+ awaitingAdoption: false,
5402
+ accepted: false,
5403
+ releaseRequested: false,
5404
+ };
5405
+ acceptedImages.set(key, reservation);
5406
+ void submissionSettled.then(() => settleAcceptedImageSubmission(key, reservation));
5407
+ return {
5408
+ accepted: () => {
5409
+ reservation.accepted = true;
5410
+ if (reservation.preflightSignal.aborted) return;
5411
+ if (acceptedImageRunOwners.get(key) && !reservation.awaitingAdoption) {
5412
+ settleAcceptedImageSubmission(key, reservation);
5413
+ if (reservation.releaseRequested) retireAcceptedImage(key);
5414
+ }
5415
+ },
5416
+ promoted: promotion => {
5417
+ reservation.awaitingAdoption = promotion.startsOwnRun === true && promotion.removed !== true;
5418
+ if (!reservation.awaitingAdoption && reservation.releaseRequested) retireAcceptedImage(key);
5419
+ },
5420
+ refused: () => {
5421
+ if (acceptedImages.get(key) !== reservation || acceptedImageRunOwners.has(key)) return false;
5422
+ reservation.awaitingAdoption = false;
5423
+ retireAcceptedImage(key);
5424
+ return true;
5425
+ },
5426
+ };
5427
+ };
5428
+ const releaseAcceptedImage = (correlation: { commandId: string; turnId: string }): void =>
5429
+ retireAcceptedImage(promptSubmissionKey(correlation));
5430
+ const releaseAcceptedImagesForRun = (owner: AgentTerminalOwnerContext): void => {
5431
+ for (const [key, runOwner] of acceptedImageRunOwners) {
5432
+ if (!runOwner || runOwner.resourceRunId !== owner.resourceRunId || runOwner.domain !== owner.domain)
5433
+ continue;
5434
+ retireAcceptedImage(key);
5435
+ }
5436
+ };
5437
+ const releaseAcceptedImages = (): void => {
5438
+ for (const key of acceptedImages.keys()) retireAcceptedImage(key);
5439
+ };
5113
5440
  type PromptLifecycleFrame =
5114
5441
  | {
5115
5442
  type: "agent_start" | "agent_end";
@@ -5815,7 +6142,7 @@ export function createNotificationsExtension(
5815
6142
  );
5816
6143
  };
5817
6144
  const emitPromptEvent = (event: AgentSessionEvent) => {
5818
- if (!runtime) return;
6145
+ if (!runtime || runtime.stopping || runtime.serverStopped || runtimes.get(id) !== runtime) return;
5819
6146
  cleanupPromptRecords();
5820
6147
  const handle = ctx.getActivePromptHandle();
5821
6148
  const domain = handle ? terminalAbortSeams?.getRunOwnerDomain?.(handle) : undefined;
@@ -5828,12 +6155,14 @@ export function createNotificationsExtension(
5828
6155
  )
5829
6156
  )
5830
6157
  correlations.unshift(root);
6158
+ const owners = new Set<string>();
5831
6159
  for (const correlation of correlations) {
5832
6160
  // Renewal is attributed exactly like the correlated delivery below.
5833
6161
  renewPromptDeadline(correlation, event);
5834
6162
  noteToolDispatchBoundary(correlation, event);
5835
6163
  const submission = promptSubmissions.get(promptSubmissionKey(correlation));
5836
6164
  if (!submission || submission.abandoned) continue;
6165
+ owners.add(submission.connectionId);
5837
6166
  const frame = {
5838
6167
  type: "event",
5839
6168
  kind: event.type,
@@ -5846,6 +6175,25 @@ export function createNotificationsExtension(
5846
6175
  }
5847
6176
  deliverCorrelatedFrame(submission, frame);
5848
6177
  }
6178
+ // Broker-created hosts use this bus, not the SDK-only extension. Observers
6179
+ // must opt into BOTH capabilities and receive no other client's correlation.
6180
+ // Keep cumulative partialResult snapshots and authoritative final result
6181
+ // intact, and off the durable lifecycle replay ring, like owner content.
6182
+ for (const connectionId of sdkRuntime?.connectionIdsWithCapabilities([
6183
+ TURN_STREAM_CAPABILITY,
6184
+ SESSION_HOST_OBSERVER_CAPABILITY,
6185
+ ]) ?? []) {
6186
+ if (owners.has(connectionId)) continue;
6187
+ try {
6188
+ sdkRuntime?.sendFrameTo(connectionId, {
6189
+ type: "event",
6190
+ kind: event.type,
6191
+ payload: toAgentWireEventPayload(event),
6192
+ });
6193
+ } catch {
6194
+ // Content delivery is best-effort and must not interrupt execution.
6195
+ }
6196
+ }
5849
6197
  };
5850
6198
  /**
5851
6199
  * Status of a registered deadline expiry attempt after an awaited
@@ -6165,6 +6513,7 @@ export function createNotificationsExtension(
6165
6513
  logger.warn(`sdk: failed to cancel fenced preflight: ${String(error)}`),
6166
6514
  );
6167
6515
  fencedConnections.add(submission.connectionId);
6516
+ imageUploads?.disconnect(submission.connectionId);
6168
6517
  host?.handleDisconnect(submission.connectionId);
6169
6518
  for (const [key, owned] of promptSubmissions)
6170
6519
  if (owned.connectionId === submission.connectionId) {
@@ -6197,6 +6546,21 @@ export function createNotificationsExtension(
6197
6546
  submission.executionHandle = handle;
6198
6547
  submission.preflightAbort = undefined;
6199
6548
  }
6549
+ if (acceptedImages.has(key) && !acceptedImageRunOwners.has(key)) {
6550
+ acceptedImageRunOwners.set(key, undefined);
6551
+ const domain = handle ? terminalAbortSeams?.getRunOwnerDomain?.(handle) : undefined;
6552
+ if (handle && domain) acceptedImageRunOwners.set(key, { resourceRunId: handle, domain });
6553
+ }
6554
+ const imageReservation = acceptedImages.get(key);
6555
+ if (
6556
+ imageReservation?.accepted &&
6557
+ !imageReservation.preflightSignal.aborted &&
6558
+ acceptedImageRunOwners.get(key)
6559
+ ) {
6560
+ imageReservation.awaitingAdoption = false;
6561
+ settleAcceptedImageSubmission(key, imageReservation);
6562
+ if (imageReservation.releaseRequested) retireAcceptedImage(key);
6563
+ }
6200
6564
  };
6201
6565
  const onPromptDiverted = (correlation: { commandId: string; turnId: string }) => {
6202
6566
  const submission = promptSubmissions.get(promptSubmissionKey(correlation));
@@ -6384,6 +6748,7 @@ export function createNotificationsExtension(
6384
6748
  return;
6385
6749
  }
6386
6750
  if (capture) capture.proof = proof;
6751
+ releaseAcceptedImage(correlation);
6387
6752
  }
6388
6753
  if (deadlineAttempt) {
6389
6754
  // Durability BEFORE the durable terminal (#5583, #5623 review round 4).
@@ -6408,21 +6773,12 @@ export function createNotificationsExtension(
6408
6773
  if (
6409
6774
  winner.kind === "failed" &&
6410
6775
  winner.code === "prompt_deadline_exceeded" &&
6411
- settings?.get("sdk.flushWorktreeOnDeadline") !== false
6776
+ !deadlineFlushSuppressed(settings)
6412
6777
  ) {
6413
- // `has` is true only for a value the user actually wrote, so this
6414
- // separates an explicit opt-in from the schema default. The flush only
6415
- // honours the default inside a linked worktree the session owns.
6416
- const configured = settings?.get("sdk.flushWorktreeOnDeadline");
6417
- const hasExplicitSetting =
6418
- typeof settings?.has === "function"
6419
- ? settings.has("sdk.flushWorktreeOnDeadline")
6420
- : configured === true;
6421
- const explicitOptIn = hasExplicitSetting === true && configured === true;
6778
+ // Same opt-in as the session runtime: user/global config or a runtime
6779
+ // override, never a project `.gjc` file or the schema default.
6422
6780
  await runBoundedDeadlineFlush(signal =>
6423
- flushWorktreeOnPromptDeadline(ctx.cwd, {
6424
- agentDir: settings?.getAgentDir(),
6425
- explicitOptIn,
6781
+ autosaveWorktreeOnPromptDeadline(ctx.cwd, settings, {
6426
6782
  isCurrent: () =>
6427
6783
  deadlineAttemptStatus(promptSubmissionKey(correlation), submission, deadlineAttempt) ===
6428
6784
  "current",
@@ -6611,7 +6967,8 @@ export function createNotificationsExtension(
6611
6967
  };
6612
6968
  const recordPromptFailure = async (correlation: { commandId: string; turnId: string }, error: unknown) => {
6613
6969
  const submission = promptSubmissions.get(promptSubmissionKey(correlation));
6614
- if (!submission) return;
6970
+ if (!submission || submission.terminal || submission.failed) return;
6971
+ releaseAcceptedImage(correlation);
6615
6972
  submission.failed = true;
6616
6973
  removePendingPromptCorrelation(correlation);
6617
6974
  if (
@@ -6621,6 +6978,29 @@ export function createNotificationsExtension(
6621
6978
  runtime.activePromptCorrelation = undefined;
6622
6979
  await emitPromptFailure(correlation, error);
6623
6980
  };
6981
+ const recordQueuedRunStartFailure = (
6982
+ correlation: { commandId: string; turnId: string },
6983
+ failure: QueuedRunStartFailure,
6984
+ sdkRunToken: string | undefined,
6985
+ refuseImage: (() => boolean) | undefined,
6986
+ ): boolean => {
6987
+ const key = promptSubmissionKey(correlation);
6988
+ const submission = promptSubmissions.get(key);
6989
+ // A present undefined owner is already-started execution, not no-start evidence.
6990
+ if (
6991
+ failure.disposition !== "not-started" ||
6992
+ (submission && submission.sdkRunToken !== sdkRunToken) ||
6993
+ submission?.executionHandle !== undefined ||
6994
+ acceptedImageRunOwners.has(key)
6995
+ )
6996
+ return false;
6997
+ // Reservation proof outlives the mutable receipt. The captured lifecycle
6998
+ // checks reservation identity and keeps the pending producer fenced.
6999
+ if (refuseImage ? !refuseImage() : !submission) return false;
7000
+ if (submission && !submission.terminal && !submission.failed)
7001
+ trackReconciliationProducer(recordPromptFailure(correlation, failure.error));
7002
+ return true;
7003
+ };
6624
7004
  const acknowledgePrompt = (connectionId: string, correlation: { commandId: string; turnId: string }) => {
6625
7005
  const key = promptSubmissionKey(correlation);
6626
7006
  const submission = promptSubmissions.get(key);
@@ -6653,6 +7033,7 @@ export function createNotificationsExtension(
6653
7033
  selector => kindReconciliation.lookupSteer(selector),
6654
7034
  () => host,
6655
7035
  options.primaryControlSurface ?? "sdk",
7036
+ imageUploadsAvailable,
6656
7037
  ),
6657
7038
  id,
6658
7039
  revisions,
@@ -6663,12 +7044,17 @@ export function createNotificationsExtension(
6663
7044
  pendingInteractive,
6664
7045
  gatePresentations,
6665
7046
  api,
7047
+ imageUploads,
7048
+ imageUploadsAvailable,
7049
+ retainAcceptedImage,
7050
+ releaseAcceptedImage,
6666
7051
  () =>
6667
7052
  runtime?.busy === true ||
6668
7053
  pendingPromptCorrelations.length > 0 ||
6669
7054
  pendingPromptCorrelationsBySdkRunToken.size > 0,
6670
7055
  recordPromptAccepted,
6671
7056
  recordPromptFailure,
7057
+ recordQueuedRunStartFailure,
6672
7058
  discardPromptAcceptance,
6673
7059
  onPromptDiverted,
6674
7060
  onPromptPromoted,
@@ -6732,11 +7118,19 @@ export function createNotificationsExtension(
6732
7118
  await terminalizePrompt({ commandId, turnId }, cancellationOutcome, {
6733
7119
  fence: hasExecutionHandle || !submission.preflightAbort,
6734
7120
  });
7121
+ if (!hasExecutionHandle && submission.preflightAbort) releaseAcceptedImage({ commandId, turnId });
6735
7122
  }
6736
7123
  }
6737
7124
  return { aborted: true, disposition: "cancelled" as const };
6738
7125
  },
6739
- async (connectionId, scope, idempotencyKey, preflightCancel, noOtherConnectionPreflights) => {
7126
+ async (
7127
+ connectionId,
7128
+ scope,
7129
+ idempotencyKey,
7130
+ preflightCancel,
7131
+ noOtherConnectionPreflights,
7132
+ operatorAuthorized = false,
7133
+ ) => {
6740
7134
  // Capture the steering snapshot at ADMISSION (before the durable
6741
7135
  // marker transaction): client steering admitted while the abort
6742
7136
  // is in flight classifies as post-snapshot and is preserved at
@@ -7098,9 +7492,9 @@ export function createNotificationsExtension(
7098
7492
  return "failed";
7099
7493
  }
7100
7494
  };
7101
- let active = [...promptSubmissions.entries()].find(
7102
- ([, submission]) => submission.connectionId === connectionId && !submission.terminal,
7103
- );
7495
+ const addresses = (submission: TerminalAbortSubmissionScope): boolean =>
7496
+ terminalAbortAddressesSubmission({ connectionId, operatorAuthorized, submission });
7497
+ let active = [...promptSubmissions.entries()].find(([, submission]) => addresses(submission));
7104
7498
  if (!active) {
7105
7499
  // DURABLY reserve the key even for a no-active-turn abort: the
7106
7500
  // generic idempotency cache is in-memory only, so after restart
@@ -7130,9 +7524,7 @@ export function createNotificationsExtension(
7130
7524
  // and the durable no-effect row would block a same-key retry from
7131
7525
  // stopping it — fall through to the pre-run / active fencing path
7132
7526
  // when a submission now exists (review thread P1).
7133
- active = [...promptSubmissions.entries()].find(
7134
- ([, submission]) => submission.connectionId === connectionId && !submission.terminal,
7135
- );
7527
+ active = [...promptSubmissions.entries()].find(([, submission]) => addresses(submission));
7136
7528
  if (!active) {
7137
7529
  // Close the remaining acceptance race: cancel the requester's
7138
7530
  // preflights HERE, in the same synchronous region as the
@@ -8157,6 +8549,10 @@ export function createNotificationsExtension(
8157
8549
  runtime = {
8158
8550
  server,
8159
8551
  host,
8552
+ imageUploads,
8553
+ releaseAcceptedImage,
8554
+ releaseAcceptedImagesForRun,
8555
+ releaseAcceptedImages,
8160
8556
  getJoinedPromptCorrelations,
8161
8557
  isPromptRunOwner: (correlation, owner) => {
8162
8558
  const handle = promptSubmissions.get(promptSubmissionKey(correlation))?.executionHandle;
@@ -8296,6 +8692,7 @@ export function createNotificationsExtension(
8296
8692
  string,
8297
8693
  { toolName: string; args?: unknown; pendingPhase?: "completed" | "failed" | "cancelled" }
8298
8694
  >(),
8695
+ awaitingInput: new Map(),
8299
8696
  deferredInboundControls: [],
8300
8697
  };
8301
8698
  const initializedRuntime = runtime;
@@ -8415,7 +8812,18 @@ export function createNotificationsExtension(
8415
8812
  sendEndpointStale(inbound.connectionId, typedFrame);
8416
8813
  return;
8417
8814
  }
8815
+ if (typedFrame.type === "control_request" && !liveHostConnection(inbound.connectionId)) {
8816
+ sendEndpointStale(inbound.connectionId, typedFrame);
8817
+ return;
8818
+ }
8418
8819
  if (typedFrame.type === "event_replay") {
8820
+ const capabilities = typedFrame.capabilities;
8821
+ if (
8822
+ Array.isArray(capabilities) &&
8823
+ capabilities.every((capability: unknown) => typeof capability === "string")
8824
+ ) {
8825
+ rememberHostCapabilities(inbound.connectionId, capabilities as string[], "replay");
8826
+ }
8419
8827
  if (liveHostConnection(inbound.connectionId)) hostAttachedConnections.add(inbound.connectionId);
8420
8828
  }
8421
8829
  if (typedFrame.type === "ephemeral_turn" || typedFrame.type === "ephemeral_turn_cancel") return;
@@ -8460,7 +8868,7 @@ export function createNotificationsExtension(
8460
8868
  const negotiated = tuple?.[1] ?? capabilities;
8461
8869
  if (typeof id !== "string" || !Array.isArray(negotiated)) return;
8462
8870
  const normalized = negotiated.filter((capability): capability is string => typeof capability === "string");
8463
- rememberHostCapabilities(id, normalized);
8871
+ rememberHostCapabilities(id, normalized, "native");
8464
8872
  if (liveHostCapabilities(id) === undefined) return;
8465
8873
  hostAttachedConnections.add(id);
8466
8874
  negotiatedCapabilitiesHandler?.(id, normalized);
@@ -8468,6 +8876,7 @@ export function createNotificationsExtension(
8468
8876
  server.onConnectionClose((_err, connectionId) => {
8469
8877
  if (!connectionId) return;
8470
8878
  closeHostConnection(connectionId);
8879
+ imageUploads?.disconnect(connectionId);
8471
8880
  connectionCloseHandler?.(connectionId);
8472
8881
  void controlSurface
8473
8882
  .cancelPendingPreflightsForConnection(connectionId)
@@ -9003,8 +9412,10 @@ export function createNotificationsExtension(
9003
9412
  attachedClients: () => server.clientCount(),
9004
9413
  observerClients: () => {
9005
9414
  if (workLease.isHeld()) return 0;
9006
- const observers = [...hostCapCache.keys()].filter(connectionId =>
9007
- liveHostCapabilities(connectionId)?.has(SESSION_HOST_OBSERVER_CAPABILITY),
9415
+ const observers = [...hostCapCache.entries()].filter(
9416
+ ([connectionId, entry]) =>
9417
+ liveHostCapabilities(connectionId)?.has(SESSION_HOST_OBSERVER_CAPABILITY) &&
9418
+ entry.nativeConfirmed.has(SESSION_HOST_OBSERVER_CAPABILITY),
9008
9419
  ).length;
9009
9420
  return Math.min(server.clientCount(), observers);
9010
9421
  },
@@ -9459,6 +9870,7 @@ export function createNotificationsExtension(
9459
9870
  runtime.gatePresentations?.activateDeferred(runtime.workflowGatePublicationEpoch);
9460
9871
  flushPendingFinal(runtime, runtime.id);
9461
9872
  flushPendingSettled(runtime, runtime.id);
9873
+ deliverAwaitingInput(runtime, runtime.id);
9462
9874
  for (const processControl of runtime.deferredInboundControls.splice(0)) processControl();
9463
9875
  },
9464
9876
  reproveOwnership: async binding => {
@@ -10069,6 +10481,7 @@ export function createNotificationsExtension(
10069
10481
  // The Agent's terminal owner is independent of delivery correlation. Never
10070
10482
  // borrow a successor's run handle to settle a predecessor's SDK prompts.
10071
10483
  const terminalOwner = terminalAbortSeams?.getTerminalRunOwnerForEvent?.(event);
10484
+ if (terminalOwner) rt.releaseAcceptedImagesForRun(terminalOwner);
10072
10485
  const rootCorrelation = rt.activePromptCorrelation;
10073
10486
  const correlations = terminalOwner ? rt.getJoinedPromptCorrelations(terminalOwner) : [];
10074
10487
  if (
@@ -10085,6 +10498,7 @@ export function createNotificationsExtension(
10085
10498
  // subsequent durable terminal claim fails. A fatal transport closure
10086
10499
  // clears attribution, so an unrelated later agent_end cannot settle a
10087
10500
  // predecessor's prompt correlation.
10501
+ if (terminalOwner) rt.releaseAcceptedImage(correlation);
10088
10502
  const assistants = (Array.isArray(event.messages) ? [...event.messages].reverse() : []).filter(
10089
10503
  message => message && typeof message === "object" && (message as { role?: unknown }).role === "assistant",
10090
10504
  ) as Array<{ stopReason?: unknown; errorKind?: unknown; errorCode?: unknown }>;
@@ -10360,8 +10774,18 @@ export function createNotificationsExtension(
10360
10774
  });
10361
10775
 
10362
10776
  api.on("tool_execution_update", (event, ctx) => {
10363
- const rt = runtimes.get(sessionId(ctx));
10777
+ const id = sessionId(ctx);
10778
+ const rt = runtimes.get(id);
10364
10779
  rt?.emitPromptEvent(event);
10780
+ if (!rt) return;
10781
+ const awaitingInput = readAwaitingInput(event.partialResult);
10782
+ if (!awaitingInput || rt.awaitingInput.has(event.toolCallId)) return;
10783
+ // Notify-only and content-free (prompt kind only, never the URL or code), so
10784
+ // it is delivered under redaction exactly like the idle marker. While policy
10785
+ // is provisional it stays pending; activation delivers it if the call is
10786
+ // still running.
10787
+ rt.awaitingInput.set(event.toolCallId, { toolName: event.toolName, kind: awaitingInput.kind, delivered: false });
10788
+ deliverAwaitingInput(rt, id);
10365
10789
  });
10366
10790
 
10367
10791
  api.on("tool_execution_end", (event, ctx) => {
@@ -10369,6 +10793,7 @@ export function createNotificationsExtension(
10369
10793
  const rt = runtimes.get(id);
10370
10794
  if (!rt) return;
10371
10795
  rt.emitPromptEvent(event);
10796
+ rt.awaitingInput.delete(event.toolCallId);
10372
10797
  const inFlight = rt.inFlightTools.get(event.toolCallId);
10373
10798
  if (!inFlight) return;
10374
10799
  if (!rt.notificationsActive) {