@gajae-code/coding-agent 0.13.3 → 0.14.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 (599) hide show
  1. package/CHANGELOG.md +184 -2
  2. package/README.md +2 -0
  3. package/bin/gajaessi.js +4 -0
  4. package/dist/types/async/job-manager.d.ts +55 -2
  5. package/dist/types/async/support.d.ts +14 -0
  6. package/dist/types/capability/mcp.d.ts +6 -0
  7. package/dist/types/capability/types.d.ts +6 -0
  8. package/dist/types/cli/accounts-cli.d.ts +19 -0
  9. package/dist/types/cli/args.d.ts +3 -0
  10. package/dist/types/cli/config-cli.d.ts +6 -0
  11. package/dist/types/cli/crash-cli.d.ts +10 -0
  12. package/dist/types/cli/customize-doctor.d.ts +145 -0
  13. package/dist/types/cli/daemon-cli.d.ts +3 -1
  14. package/dist/types/cli/mcp-cli.d.ts +11 -0
  15. package/dist/types/cli/notify-cli.d.ts +1 -1
  16. package/dist/types/cli/root-flags.d.ts +12 -0
  17. package/dist/types/cli/setup-cli.d.ts +3 -1
  18. package/dist/types/cli/skills-cli.d.ts +2 -1
  19. package/dist/types/cli/update-cli.d.ts +19 -1
  20. package/dist/types/cli.d.ts +6 -0
  21. package/dist/types/commands/accounts.d.ts +40 -0
  22. package/dist/types/commands/crash.d.ts +24 -0
  23. package/dist/types/commands/customize.d.ts +22 -0
  24. package/dist/types/commands/daemon.d.ts +1 -1
  25. package/dist/types/commands/launch.d.ts +6 -0
  26. package/dist/types/commands/setup.d.ts +6 -0
  27. package/dist/types/commands/skills.d.ts +6 -1
  28. package/dist/types/commands/update.d.ts +7 -0
  29. package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
  30. package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
  31. package/dist/types/config/config-schema-version.d.ts +10 -0
  32. package/dist/types/config/file-lock.d.ts +18 -0
  33. package/dist/types/config/model-profile-activation.d.ts +3 -1
  34. package/dist/types/config/model-registry.d.ts +9 -1
  35. package/dist/types/config/models-config-schema.d.ts +2 -0
  36. package/dist/types/config/settings-schema.d.ts +70 -6
  37. package/dist/types/config/settings.d.ts +63 -0
  38. package/dist/types/config/skill-settings-defaults.d.ts +26 -0
  39. package/dist/types/config/task-delegation.d.ts +39 -0
  40. package/dist/types/config/update-channel.d.ts +10 -0
  41. package/dist/types/coordinator-mcp/server.d.ts +5 -1
  42. package/dist/types/crash/index-store.d.ts +87 -0
  43. package/dist/types/crash/nudge.d.ts +50 -0
  44. package/dist/types/crash/record-loader.d.ts +17 -0
  45. package/dist/types/crash/report.d.ts +134 -0
  46. package/dist/types/crash/sanitize.d.ts +25 -0
  47. package/dist/types/cursor.d.ts +13 -2
  48. package/dist/types/customization/import.d.ts +37 -0
  49. package/dist/types/customization/index.d.ts +4 -0
  50. package/dist/types/customization/inventory.d.ts +25 -0
  51. package/dist/types/customization/mutations.d.ts +32 -0
  52. package/dist/types/customization/types.d.ts +115 -0
  53. package/dist/types/daemon/control-types.d.ts +8 -1
  54. package/dist/types/daemon/operator-contract.d.ts +4 -4
  55. package/dist/types/discovery/claude.d.ts +39 -1
  56. package/dist/types/discovery/codex.d.ts +39 -1
  57. package/dist/types/discovery/index.d.ts +2 -0
  58. package/dist/types/discovery/mcp-compat.d.ts +30 -0
  59. package/dist/types/edit/renderer.d.ts +23 -0
  60. package/dist/types/extensibility/extensions/runner.d.ts +3 -0
  61. package/dist/types/extensibility/extensions/types.d.ts +21 -4
  62. package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
  63. package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
  64. package/dist/types/extensibility/hooks/loader.d.ts +19 -5
  65. package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
  66. package/dist/types/extensibility/skill-management.d.ts +107 -0
  67. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
  68. package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
  69. package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
  70. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
  71. package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
  72. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
  73. package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
  74. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
  75. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
  76. package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
  77. package/dist/types/hooks/events.d.ts +60 -0
  78. package/dist/types/hooks/normalize.d.ts +83 -0
  79. package/dist/types/hooks/skill-state.d.ts +9 -2
  80. package/dist/types/main.d.ts +1 -1
  81. package/dist/types/modes/acp/abort-scope.d.ts +13 -0
  82. package/dist/types/modes/acp/acp-agent.d.ts +7 -4
  83. package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
  84. package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
  85. package/dist/types/modes/components/customization/index.d.ts +1 -0
  86. package/dist/types/modes/components/gajae-pet-widget.d.ts +8 -30
  87. package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
  88. package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
  89. package/dist/types/modes/components/oauth-selector.d.ts +10 -4
  90. package/dist/types/modes/components/pet-capability.d.ts +11 -25
  91. package/dist/types/modes/components/status-line/gh.d.ts +6 -2
  92. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  93. package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
  94. package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
  95. package/dist/types/modes/interactive-mode.d.ts +1 -0
  96. package/dist/types/modes/types.d.ts +1 -0
  97. package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
  98. package/dist/types/runtime-mcp/config.d.ts +9 -0
  99. package/dist/types/runtime-mcp/manager.d.ts +26 -1
  100. package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
  101. package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
  102. package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
  103. package/dist/types/runtime-mcp/protocol.d.ts +278 -0
  104. package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
  105. package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
  106. package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
  107. package/dist/types/runtime-mcp/types.d.ts +102 -10
  108. package/dist/types/sdk/acp/adapter.d.ts +26 -5
  109. package/dist/types/sdk/broker/broker.d.ts +3 -55
  110. package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
  111. package/dist/types/sdk/broker/session-index.d.ts +14 -5
  112. package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
  113. package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
  114. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
  115. package/dist/types/sdk/bus/config.d.ts +11 -0
  116. package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
  117. package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
  118. package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
  119. package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
  120. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
  121. package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
  122. package/dist/types/sdk/bus/index.d.ts +85 -99
  123. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
  124. package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
  125. package/dist/types/sdk/bus/notification-service.d.ts +52 -0
  126. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
  127. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
  128. package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
  129. package/dist/types/sdk/bus/session-control.d.ts +25 -1
  130. package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
  131. package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
  132. package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
  133. package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
  134. package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
  135. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
  136. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
  137. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
  138. package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
  139. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
  140. package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
  141. package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
  142. package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
  143. package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
  144. package/dist/types/sdk/cli/rows.d.ts +2 -11
  145. package/dist/types/sdk/cli/session-cli.d.ts +15 -25
  146. package/dist/types/sdk/client/client.d.ts +97 -1
  147. package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
  148. package/dist/types/sdk/host/control/index.d.ts +1 -1
  149. package/dist/types/sdk/host/control/operations.d.ts +18 -5
  150. package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
  151. package/dist/types/sdk/host/host.d.ts +15 -1
  152. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  153. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  154. package/dist/types/sdk/host/session-runtime.d.ts +127 -7
  155. package/dist/types/sdk/host/types.d.ts +1 -1
  156. package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
  157. package/dist/types/sdk/index.d.ts +2 -1
  158. package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
  159. package/dist/types/sdk/lifecycle/client.d.ts +59 -0
  160. package/dist/types/sdk/lifecycle/index.d.ts +3 -0
  161. package/dist/types/sdk/lifecycle/service.d.ts +232 -0
  162. package/dist/types/sdk/mcp/server.d.ts +9 -6
  163. package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
  164. package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
  165. package/dist/types/sdk/prompt-status.d.ts +10 -3
  166. package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
  167. package/dist/types/sdk/receipt-state.d.ts +17 -0
  168. package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
  169. package/dist/types/sdk/router/index.d.ts +1 -0
  170. package/dist/types/sdk/router/session-router.d.ts +152 -0
  171. package/dist/types/sdk/session-list.d.ts +22 -0
  172. package/dist/types/sdk/session-reconnect.d.ts +43 -1
  173. package/dist/types/sdk/session.d.ts +34 -1
  174. package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
  175. package/dist/types/session/account-inventory.d.ts +86 -0
  176. package/dist/types/session/agent-session.d.ts +145 -6
  177. package/dist/types/session/agent-storage.d.ts +10 -0
  178. package/dist/types/session/coordinator-tool-label.d.ts +28 -0
  179. package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
  180. package/dist/types/session/messages.d.ts +1 -1
  181. package/dist/types/session/secure-token-file.d.ts +3 -0
  182. package/dist/types/session/session-manager.d.ts +40 -1
  183. package/dist/types/session/startup-auth-config.d.ts +44 -0
  184. package/dist/types/session/terminal-abort.d.ts +378 -0
  185. package/dist/types/session/yield-queue.d.ts +7 -0
  186. package/dist/types/session-import/claude.d.ts +26 -0
  187. package/dist/types/session-import/codex.d.ts +88 -0
  188. package/dist/types/session-import/command.d.ts +35 -0
  189. package/dist/types/session-import/detect.d.ts +22 -0
  190. package/dist/types/session-import/index.d.ts +7 -0
  191. package/dist/types/session-import/provider-codex.d.ts +22 -0
  192. package/dist/types/session-import/provider-service.d.ts +38 -0
  193. package/dist/types/session-import/redact.d.ts +23 -0
  194. package/dist/types/session-import/service.d.ts +47 -0
  195. package/dist/types/session-import/types.d.ts +120 -0
  196. package/dist/types/setup/paseo/check.d.ts +16 -0
  197. package/dist/types/setup/paseo/install-saga.d.ts +115 -0
  198. package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
  199. package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
  200. package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
  201. package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
  202. package/dist/types/setup/paseo/provider-config.d.ts +35 -0
  203. package/dist/types/setup/paseo/remove.d.ts +14 -0
  204. package/dist/types/setup/paseo/result-types.d.ts +59 -0
  205. package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
  206. package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
  207. package/dist/types/skill-state/active-state.d.ts +5 -1
  208. package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
  209. package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
  210. package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
  211. package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
  212. package/dist/types/slash-commands/types.d.ts +7 -1
  213. package/dist/types/task/executor.d.ts +10 -0
  214. package/dist/types/task/index.d.ts +3 -1
  215. package/dist/types/tools/ask-answer-registry.d.ts +6 -3
  216. package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
  217. package/dist/types/tools/bash.d.ts +2 -6
  218. package/dist/types/tools/browser/attach.d.ts +11 -0
  219. package/dist/types/tools/browser/launch.d.ts +20 -0
  220. package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
  221. package/dist/types/tools/browser/render.d.ts +1 -0
  222. package/dist/types/tools/browser.d.ts +3 -1
  223. package/dist/types/tools/image-gen.d.ts +9 -2
  224. package/dist/types/tools/index.d.ts +8 -0
  225. package/dist/types/tools/monitor.d.ts +1 -1
  226. package/dist/types/tools/skill-discovery.d.ts +8 -1
  227. package/dist/types/tools/subagent.d.ts +2 -0
  228. package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
  229. package/dist/types/utils/herdr-pane.d.ts +76 -0
  230. package/package.json +19 -9
  231. package/scripts/build-binary.ts +2 -1
  232. package/scripts/build-sdk-package-smoke.ts +2 -13
  233. package/scripts/capture-sticky-viewport-showcase.ts +0 -1
  234. package/scripts/compile-args.ts +2 -0
  235. package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
  236. package/scripts/generate-sdk-operation-inventory.ts +28 -0
  237. package/scripts/generate-telegram-baseline-manifest.ts +1 -0
  238. package/scripts/generate-tool-catalog.ts +6 -4
  239. package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
  240. package/src/async/job-manager.ts +352 -17
  241. package/src/async/support.ts +25 -0
  242. package/src/autoresearch/dashboard.ts +28 -13
  243. package/src/capability/index.ts +3 -1
  244. package/src/capability/mcp.ts +6 -0
  245. package/src/capability/types.ts +6 -0
  246. package/src/cli/accounts-cli.ts +395 -0
  247. package/src/cli/args.ts +23 -0
  248. package/src/cli/auth-broker-cli.ts +136 -66
  249. package/src/cli/auth-gateway-cli.ts +94 -70
  250. package/src/cli/config-cli.ts +27 -2
  251. package/src/cli/crash-cli.ts +78 -0
  252. package/src/cli/customize-doctor.ts +1605 -0
  253. package/src/cli/daemon-cli.ts +12 -3
  254. package/src/cli/fast-help.ts +1 -0
  255. package/src/cli/mcp-cli.ts +128 -60
  256. package/src/cli/notify-cli.ts +8 -14
  257. package/src/cli/root-flags.ts +8 -0
  258. package/src/cli/setup-cli.ts +145 -0
  259. package/src/cli/skills-cli.ts +38 -2
  260. package/src/cli/update-cli.ts +167 -12
  261. package/src/cli.ts +4 -0
  262. package/src/commands/accounts.ts +71 -0
  263. package/src/commands/crash.ts +35 -0
  264. package/src/commands/customize.ts +84 -0
  265. package/src/commands/daemon.ts +2 -2
  266. package/src/commands/deep-interview.ts +2 -0
  267. package/src/commands/launch.ts +1 -0
  268. package/src/commands/mcp.ts +12 -7
  269. package/src/commands/ralplan.ts +2 -0
  270. package/src/commands/sdk.ts +65 -32
  271. package/src/commands/setup.ts +5 -0
  272. package/src/commands/skills.ts +14 -4
  273. package/src/commands/ultragoal.ts +10 -1
  274. package/src/commands/update.ts +26 -15
  275. package/src/config/atomic-yaml-patch-worker.ts +57 -0
  276. package/src/config/atomic-yaml-patch.ts +769 -29
  277. package/src/config/config-schema-version.ts +10 -0
  278. package/src/config/file-lock.ts +70 -1
  279. package/src/config/mcp-schema.json +52 -14
  280. package/src/config/model-profile-activation.ts +20 -1
  281. package/src/config/model-profiles.ts +29 -19
  282. package/src/config/model-registry.ts +132 -21
  283. package/src/config/models-config-schema.ts +1 -1
  284. package/src/config/settings-schema.ts +76 -6
  285. package/src/config/settings.ts +4038 -199
  286. package/src/config/skill-settings-defaults.ts +41 -3
  287. package/src/config/task-delegation.ts +87 -0
  288. package/src/config/update-channel.ts +23 -0
  289. package/src/coordinator-mcp/server.ts +377 -308
  290. package/src/crash/index-store.ts +723 -0
  291. package/src/crash/nudge.ts +89 -0
  292. package/src/crash/record-loader.ts +149 -0
  293. package/src/crash/report.ts +551 -0
  294. package/src/crash/sanitize.ts +129 -0
  295. package/src/cursor.ts +36 -10
  296. package/src/customization/import.ts +946 -0
  297. package/src/customization/index.ts +4 -0
  298. package/src/customization/inventory.ts +335 -0
  299. package/src/customization/mutations.ts +141 -0
  300. package/src/customization/types.ts +223 -0
  301. package/src/daemon/control-types.ts +8 -1
  302. package/src/daemon/operator-contract.ts +6 -6
  303. package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
  304. package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
  305. package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
  306. package/src/discovery/claude-hooks.ts +38 -0
  307. package/src/discovery/claude.ts +142 -209
  308. package/src/discovery/codex-hooks.ts +38 -0
  309. package/src/discovery/codex.ts +132 -198
  310. package/src/discovery/helpers.ts +8 -1
  311. package/src/discovery/index.ts +2 -0
  312. package/src/discovery/mcp-compat.ts +234 -0
  313. package/src/discovery/mcp-json.ts +6 -0
  314. package/src/edit/renderer.ts +40 -0
  315. package/src/edit/streaming.ts +4 -2
  316. package/src/exec/bash-executor.ts +24 -20
  317. package/src/export/html/index.ts +51 -2
  318. package/src/extensibility/extensions/loader.ts +9 -1
  319. package/src/extensibility/extensions/runner.ts +17 -4
  320. package/src/extensibility/extensions/types.ts +31 -6
  321. package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
  322. package/src/extensibility/gjc-plugins/installer.ts +9 -5
  323. package/src/extensibility/gjc-plugins/observability.ts +8 -2
  324. package/src/extensibility/gjc-plugins/registry.ts +13 -2
  325. package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
  326. package/src/extensibility/gjc-plugins/schema.ts +222 -39
  327. package/src/extensibility/hooks/loader.ts +154 -10
  328. package/src/extensibility/runtime-skill-discovery.ts +257 -60
  329. package/src/extensibility/skill-management.ts +368 -0
  330. package/src/extensibility/skills.ts +52 -16
  331. package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
  332. package/src/gjc-runtime/launch-worktree.ts +56 -2
  333. package/src/gjc-runtime/linux-proc.ts +7 -1
  334. package/src/gjc-runtime/ralplan-runtime.ts +100 -164
  335. package/src/gjc-runtime/session-state-lock.ts +992 -0
  336. package/src/gjc-runtime/session-state-sidecar.ts +644 -142
  337. package/src/gjc-runtime/state-writer.ts +39 -14
  338. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
  339. package/src/gjc-runtime/ultragoal-guard.ts +11 -4
  340. package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
  341. package/src/gjc-runtime/workflow-settings.ts +596 -0
  342. package/src/goals/tools/goal-tool.ts +6 -1
  343. package/src/hooks/events.ts +291 -0
  344. package/src/hooks/native-skill-hook.ts +4 -0
  345. package/src/hooks/normalize.ts +369 -0
  346. package/src/hooks/skill-state.ts +165 -46
  347. package/src/internal-urls/docs-index.generated.ts +40 -32
  348. package/src/main.ts +32 -5
  349. package/src/migrate/mcp-mapper.ts +25 -2
  350. package/src/modes/acp/abort-scope.ts +29 -0
  351. package/src/modes/acp/acp-agent.ts +423 -98
  352. package/src/modes/components/assistant-message.ts +6 -1
  353. package/src/modes/components/customization/customization-dashboard.ts +443 -0
  354. package/src/modes/components/customization/import-wizard.ts +372 -0
  355. package/src/modes/components/customization/index.ts +1 -0
  356. package/src/modes/components/gajae-pet-widget.ts +539 -213
  357. package/src/modes/components/irc-sidebar.ts +11 -0
  358. package/src/modes/components/iterm-pet-transport.ts +585 -0
  359. package/src/modes/components/oauth-selector.ts +277 -86
  360. package/src/modes/components/pet-capability.ts +39 -40
  361. package/src/modes/components/settings-selector.ts +9 -0
  362. package/src/modes/components/status-line/gh.ts +45 -1
  363. package/src/modes/components/status-line/segments.ts +16 -1
  364. package/src/modes/components/tool-status-header.ts +13 -3
  365. package/src/modes/controllers/command-controller.ts +20 -8
  366. package/src/modes/controllers/extension-ui-controller.ts +56 -257
  367. package/src/modes/controllers/input-controller.ts +10 -0
  368. package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
  369. package/src/modes/controllers/selector-controller.ts +177 -72
  370. package/src/modes/controllers/stt-controller.ts +3 -3
  371. package/src/modes/interactive-mode.ts +88 -16
  372. package/src/modes/print-mode.ts +35 -6
  373. package/src/modes/runtime-init.ts +21 -68
  374. package/src/modes/types.ts +1 -0
  375. package/src/modes/utils/injected-user-submission.ts +25 -0
  376. package/src/modes/utils/terminal-bell.ts +15 -1
  377. package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
  378. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  379. package/src/prompts/agents/critic.md +1 -1
  380. package/src/prompts/system/rlm-research.md +1 -1
  381. package/src/prompts/system/system-prompt.md +0 -2
  382. package/src/prompts/tools/bash.md +7 -1
  383. package/src/prompts/tools/browser.md +1 -1
  384. package/src/prompts/tools/cron.md +6 -6
  385. package/src/prompts/tools/read.md +0 -2
  386. package/src/prompts/tools/skill-discovery.md +4 -2
  387. package/src/runtime-mcp/client.ts +427 -23
  388. package/src/runtime-mcp/config.ts +37 -5
  389. package/src/runtime-mcp/manager-pool.test.ts +56 -0
  390. package/src/runtime-mcp/manager.ts +52 -2
  391. package/src/runtime-mcp/oauth-discovery.ts +13 -0
  392. package/src/runtime-mcp/oauth-flow.ts +46 -1
  393. package/src/runtime-mcp/pool-key.ts +5 -1
  394. package/src/runtime-mcp/pool.test.ts +9 -0
  395. package/src/runtime-mcp/protocol.test.ts +353 -0
  396. package/src/runtime-mcp/protocol.ts +624 -0
  397. package/src/runtime-mcp/scope-config.ts +78 -0
  398. package/src/runtime-mcp/tool-bridge.ts +44 -10
  399. package/src/runtime-mcp/transports/http.ts +84 -13
  400. package/src/runtime-mcp/types.ts +123 -12
  401. package/src/sdk/acp/adapter.ts +328 -97
  402. package/src/sdk/broker/broker.ts +160 -741
  403. package/src/sdk/broker/ensure.ts +9 -1
  404. package/src/sdk/broker/lifecycle.ts +510 -116
  405. package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
  406. package/src/sdk/broker/process-incarnation.ts +19 -0
  407. package/src/sdk/broker/session-index.ts +516 -117
  408. package/src/sdk/broker/startup-failure.ts +28 -5
  409. package/src/sdk/broker/transport.ts +1 -13
  410. package/src/sdk/bus/chat-daemon-cli.ts +4 -2
  411. package/src/sdk/bus/chat-daemon-control.ts +67 -6
  412. package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
  413. package/src/sdk/bus/config.ts +13 -0
  414. package/src/sdk/bus/conversation-store.ts +59 -171
  415. package/src/sdk/bus/daemon-paths.ts +20 -0
  416. package/src/sdk/bus/discord-conversation.ts +2 -0
  417. package/src/sdk/bus/discord-daemon.ts +524 -126
  418. package/src/sdk/bus/discord-live-provider.ts +206 -79
  419. package/src/sdk/bus/existing-thread-readiness.ts +4 -97
  420. package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
  421. package/src/sdk/bus/index.ts +2480 -456
  422. package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
  423. package/src/sdk/bus/lifecycle-commands.ts +2 -2
  424. package/src/sdk/bus/managed-daemon.ts +2 -145
  425. package/src/sdk/bus/notification-service.ts +196 -0
  426. package/src/sdk/bus/operator-runtime.ts +1 -1
  427. package/src/sdk/bus/prompt-reconciliation.ts +24 -15
  428. package/src/sdk/bus/reconciliation-store.ts +553 -24
  429. package/src/sdk/bus/session-control.ts +49 -3
  430. package/src/sdk/bus/slack-conversation.ts +4 -0
  431. package/src/sdk/bus/slack-daemon.ts +733 -134
  432. package/src/sdk/bus/slack-live-provider.ts +65 -15
  433. package/src/sdk/bus/slack-provider.ts +4 -0
  434. package/src/sdk/bus/slack-thread-binding.ts +9 -104
  435. package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
  436. package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
  437. package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
  438. package/src/sdk/bus/telegram-daemon-control.ts +3 -13
  439. package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
  440. package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
  441. package/src/sdk/bus/telegram-daemon.ts +2552 -2280
  442. package/src/sdk/bus/telegram-reference.ts +1 -226
  443. package/src/sdk/bus/telegram-setup.ts +2 -2
  444. package/src/sdk/bus/threaded-render.ts +1 -0
  445. package/src/sdk/bus/topic-registry.ts +279 -146
  446. package/src/sdk/cli/rows.ts +2 -35
  447. package/src/sdk/cli/session-cli.ts +776 -700
  448. package/src/sdk/client/client.ts +892 -1
  449. package/src/sdk/host/control/dispatch.ts +93 -58
  450. package/src/sdk/host/control/index.ts +1 -1
  451. package/src/sdk/host/control/operations.ts +18 -10
  452. package/src/sdk/host/control/runtime-gate.ts +40 -0
  453. package/src/sdk/host/host.ts +63 -12
  454. package/src/sdk/host/query/handlers.ts +66 -7
  455. package/src/sdk/host/reverse-leases.ts +2 -2
  456. package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
  457. package/src/sdk/host/session-runtime.test.ts +2787 -34
  458. package/src/sdk/host/session-runtime.ts +1489 -101
  459. package/src/sdk/host/surface-policy.ts +12 -8
  460. package/src/sdk/host/types.ts +1 -1
  461. package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
  462. package/src/sdk/host/websocket-transport.ts +85 -50
  463. package/src/sdk/index.ts +2 -1
  464. package/src/sdk/lifecycle/broker-client.ts +53 -0
  465. package/src/sdk/lifecycle/client.ts +234 -0
  466. package/src/sdk/lifecycle/index.ts +3 -0
  467. package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
  468. package/src/sdk/lifecycle/service.ts +720 -0
  469. package/src/sdk/mcp/server.ts +226 -188
  470. package/src/sdk/prompt-deadline-lease.ts +55 -0
  471. package/src/sdk/prompt-deadline-manager.ts +148 -0
  472. package/src/sdk/prompt-status.ts +11 -3
  473. package/src/sdk/protocol/operation-inventory.generated.json +272 -129
  474. package/src/sdk/protocol/operation-registry.ts +71 -8
  475. package/src/sdk/receipt-state.ts +43 -0
  476. package/src/sdk/reconciliation-extensions.ts +14 -0
  477. package/src/sdk/router/index.ts +1 -0
  478. package/src/sdk/router/session-router.ts +1658 -0
  479. package/src/sdk/session-list.ts +93 -0
  480. package/src/sdk/session-reconnect.ts +45 -1
  481. package/src/sdk/session.ts +716 -120
  482. package/src/sdk/transport/serve-cli.ts +18 -34
  483. package/src/session/account-inventory.ts +603 -0
  484. package/src/session/agent-session.ts +2857 -582
  485. package/src/session/agent-storage.ts +35 -0
  486. package/src/session/blob-store.ts +32 -1
  487. package/src/session/coordinator-tool-label.ts +40 -0
  488. package/src/session/internal/managed-session-scope.ts +4 -0
  489. package/src/session/internal/managed-session-storage.ts +173 -12
  490. package/src/session/messages.ts +1 -1
  491. package/src/session/secure-token-file.ts +80 -0
  492. package/src/session/session-manager.ts +730 -58
  493. package/src/session/session-storage.ts +25 -16
  494. package/src/session/startup-auth-config.ts +297 -0
  495. package/src/session/terminal-abort.ts +1253 -0
  496. package/src/session/yield-queue.ts +57 -15
  497. package/src/session-import/claude.ts +334 -0
  498. package/src/session-import/codex.ts +1033 -0
  499. package/src/session-import/command.ts +77 -0
  500. package/src/session-import/detect.ts +133 -0
  501. package/src/session-import/index.ts +7 -0
  502. package/src/session-import/provider-codex.ts +327 -0
  503. package/src/session-import/provider-service.ts +457 -0
  504. package/src/session-import/redact.ts +128 -0
  505. package/src/session-import/service.ts +1264 -0
  506. package/src/session-import/types.ts +148 -0
  507. package/src/setup/paseo/check.ts +213 -0
  508. package/src/setup/paseo/install-saga.ts +293 -0
  509. package/src/setup/paseo/json-publisher.ts +263 -0
  510. package/src/setup/paseo/orchestration-preferences.ts +86 -0
  511. package/src/setup/paseo/paseo-ownership.ts +224 -0
  512. package/src/setup/paseo/paseo-setup.ts +253 -0
  513. package/src/setup/paseo/provider-config.ts +139 -0
  514. package/src/setup/paseo/remove.ts +183 -0
  515. package/src/setup/paseo/result-types.ts +79 -0
  516. package/src/setup/paseo/setup-deps.ts +175 -0
  517. package/src/setup/paseo/skills-bridge.ts +260 -0
  518. package/src/skill-state/active-state.ts +21 -11
  519. package/src/slash-commands/acp-builtins.ts +9 -1
  520. package/src/slash-commands/builtin-registry.ts +98 -16
  521. package/src/slash-commands/helpers/credential-switch.ts +143 -0
  522. package/src/slash-commands/helpers/mcp.ts +9 -3
  523. package/src/slash-commands/helpers/usage-report.ts +61 -56
  524. package/src/slash-commands/types.ts +7 -2
  525. package/src/task/executor.ts +30 -11
  526. package/src/task/index.ts +66 -7
  527. package/src/task/render.ts +23 -9
  528. package/src/tools/ask-answer-registry.ts +23 -7
  529. package/src/tools/ask.ts +8 -4
  530. package/src/tools/bash-sleep-advisory.ts +115 -0
  531. package/src/tools/bash.ts +98 -18
  532. package/src/tools/browser/attach.ts +71 -3
  533. package/src/tools/browser/launch.ts +86 -13
  534. package/src/tools/browser/profile-discovery.ts +57 -7
  535. package/src/tools/browser/render.ts +8 -1
  536. package/src/tools/browser.ts +130 -21
  537. package/src/tools/computer.enforcement.test.ts +1 -1
  538. package/src/tools/computer.ts +3 -2
  539. package/src/tools/cron.ts +12 -1
  540. package/src/tools/image-gen.ts +27 -16
  541. package/src/tools/index.ts +8 -0
  542. package/src/tools/job.ts +33 -6
  543. package/src/tools/monitor.ts +41 -2
  544. package/src/tools/skill-discovery.ts +21 -29
  545. package/src/tools/subagent.ts +142 -24
  546. package/src/tools/tool-catalog.generated.ts +6 -6
  547. package/src/tools/ultragoal-ask-guard.ts +10 -3
  548. package/src/utils/herdr-pane.ts +345 -0
  549. package/src/utils/title-generator.ts +2 -0
  550. package/src/web/scrapers/index.ts +6 -4
  551. package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
  552. package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
  553. package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
  554. package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
  555. package/dist/types/runtime/lazy-service.test.d.ts +0 -1
  556. package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
  557. package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
  558. package/dist/types/runtime-mcp/config.test.d.ts +0 -1
  559. package/dist/types/runtime-mcp/http.test.d.ts +0 -1
  560. package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
  561. package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
  562. package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
  563. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
  564. package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
  565. package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
  566. package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
  567. package/dist/types/sdk/elevation/audit.d.ts +0 -93
  568. package/dist/types/sdk/elevation/capability.d.ts +0 -3
  569. package/dist/types/sdk/elevation/digest.d.ts +0 -72
  570. package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
  571. package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
  572. package/dist/types/sdk/elevation/owner.d.ts +0 -48
  573. package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
  574. package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
  575. package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
  576. package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
  577. package/dist/types/session/auth-broker-config.d.ts +0 -13
  578. package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
  579. package/dist/types/skills/skills.test.d.ts +0 -1
  580. package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
  581. package/dist/types/task/token-log.test.d.ts +0 -1
  582. package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
  583. package/dist/types/tools/descriptors.test.d.ts +0 -1
  584. package/scripts/g005-daemon-smoke.ts +0 -180
  585. package/scripts/g011-daemon-path-smoke.ts +0 -153
  586. package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
  587. package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
  588. package/src/sdk/bus/telegram-cli.ts +0 -212
  589. package/src/sdk/elevation/allowlist.ts +0 -54
  590. package/src/sdk/elevation/audit.ts +0 -208
  591. package/src/sdk/elevation/capability.ts +0 -41
  592. package/src/sdk/elevation/digest.ts +0 -129
  593. package/src/sdk/elevation/dispatch-receipt.ts +0 -184
  594. package/src/sdk/elevation/grant-ledger.ts +0 -1142
  595. package/src/sdk/elevation/owner.ts +0 -109
  596. package/src/session/auth-broker-config.ts +0 -109
  597. /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
  598. /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
  599. /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
@@ -1,4 +1,11 @@
1
1
  import { logger } from "@gajae-code/utils";
2
+ import {
3
+ lookupOwnedRegistration,
4
+ registerOwnedRegistration,
5
+ resolveToolLineage,
6
+ retireOwnedRegistrationForDeadLetter,
7
+ unregisterOwnedRegistration,
8
+ } from "../session/terminal-abort";
2
9
  import type { AgentProgress, AgentSource } from "../task/types";
3
10
 
4
11
  const DELIVERY_RETRY_BASE_MS = 500;
@@ -160,7 +167,13 @@ export interface SubagentRecord {
160
167
  * file, followed by a separately available runner (`no_runner` otherwise).
161
168
  */
162
169
  resumable: boolean;
163
- queued?: { ownerId?: string; seq: number; message?: string; createdAt: number };
170
+ queued?: { ownerId?: string; seq: number; message?: string; resumeToolCallId?: string; createdAt: number };
171
+ /** Last queued-resume seq for a CANCELLED queued resume (rec.queued is
172
+ * cleared on cancel): retained on the record so owned settlement's second
173
+ * proof can still see the generation as provably cancelled, without a
174
+ * separate FIFO-capped evidence set that could evict an in-flight
175
+ * settlement's generation (review thread P2). */
176
+ terminalQueuedSeq?: number;
164
177
  /** Resolved model the subagent was asked to use, e.g. "openai-codex/gpt-5.5". */
165
178
  requestedModel?: string;
166
179
  /** Model actually used after auth fallback (#985); equals requestedModel when no fallback. */
@@ -187,7 +200,12 @@ export interface ResumeDescriptor {
187
200
  * In-memory resume runner bound to the session that originally launched a
188
201
  * subagent. Never serialized: process restart drops it so resume fails closed.
189
202
  */
190
- export type ResumeRunner = (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined;
203
+ export type ResumeRunner = (
204
+ subagentId: string,
205
+ message?: string,
206
+ descriptor?: ResumeDescriptor,
207
+ resumeToolCallId?: string,
208
+ ) => string | undefined;
191
209
 
192
210
  function sessionFileFromResumeDescriptorData(data: unknown): string | null {
193
211
  if (typeof data !== "object" || data === null) return null;
@@ -326,6 +344,10 @@ export interface AsyncJobWaitHandle {
326
344
  close(): void;
327
345
  }
328
346
 
347
+ export interface AsyncJobWatchHandle {
348
+ close(): number;
349
+ }
350
+
329
351
  interface TerminalEvent {
330
352
  generation: string;
331
353
  jobId: string | null;
@@ -429,6 +451,82 @@ export class AsyncJobManager {
429
451
  return AsyncJobManager.#instance;
430
452
  }
431
453
 
454
+ static readonly #byEndpoint = new Map<string, AsyncJobManager>();
455
+
456
+ /** Register a manager for a top-level session endpoint so concurrent
457
+ * sessions' owned work can be settled in the ABORTING endpoint's manager
458
+ * instead of the last-created process-global instance (review thread P1).
459
+ * Returns TRUE when the mapping was installed (or already held by this
460
+ * manager); returns FALSE when the endpoint id is already held by a
461
+ * FOREIGN live manager — a second top-level session constructed or
462
+ * resumed under that endpoint must then fail construction instead of
463
+ * silently replacing the first manager, which would make the first
464
+ * session's tools resolve the second manager and let same-id jobs be
465
+ * queried, registered, or cancelled across sessions (review thread P1). */
466
+ static registerForEndpoint(endpointId: string, manager: AsyncJobManager): boolean {
467
+ const holder = AsyncJobManager.#byEndpoint.get(endpointId);
468
+ if (holder !== undefined && holder !== manager) return false;
469
+ AsyncJobManager.#byEndpoint.set(endpointId, manager);
470
+ return true;
471
+ }
472
+
473
+ static forEndpoint(endpointId: string | undefined): AsyncJobManager | undefined {
474
+ if (endpointId === undefined) return undefined;
475
+ return AsyncJobManager.#byEndpoint.get(endpointId);
476
+ }
477
+
478
+ static unregisterForEndpoint(endpointId: string): void {
479
+ AsyncJobManager.#byEndpoint.delete(endpointId);
480
+ }
481
+
482
+ /** Reverse lookup: the endpoint this manager is registered under, if any
483
+ * (review thread P1 — endpoint-scoped lineage resolution). */
484
+ static endpointIdOf(manager: AsyncJobManager | undefined): string | undefined {
485
+ if (!manager) return undefined;
486
+ for (const [endpointId, candidate] of AsyncJobManager.#byEndpoint) {
487
+ if (candidate === manager) return endpointId;
488
+ }
489
+ return undefined;
490
+ }
491
+
492
+ /** Re-register a manager under a successor endpoint id after a committed
493
+ * session-identity transition (newSession / switchSession / handoff).
494
+ * Lineage bindings made after the transition use the successor id, so the
495
+ * process-global endpoint registry must follow — otherwise
496
+ * `endpointIdOf()` keeps resolving the predecessor and a queued subagent
497
+ * resume can neither resolve its lineage nor register its owned tuple
498
+ * (review thread P1). Returns TRUE when the mapping was moved (or no move
499
+ * was needed); returns FALSE when the successor endpoint is owned by a
500
+ * FOREIGN live manager — the transition must then abort or roll back
501
+ * BEFORE retiring predecessor state, because leaving this manager under
502
+ * the predecessor while tools resolve the successor to the foreign
503
+ * manager sends jobs to the wrong session and owned aborts lose their
504
+ * causal set (review thread P1). */
505
+ static rekeyForEndpoint(
506
+ predecessorEndpointId: string,
507
+ successorEndpointId: string,
508
+ manager: AsyncJobManager | undefined,
509
+ ): boolean {
510
+ if (!manager || predecessorEndpointId === successorEndpointId) return true;
511
+ if (AsyncJobManager.#byEndpoint.get(predecessorEndpointId) !== manager) return true;
512
+ const successorOwner = AsyncJobManager.#byEndpoint.get(successorEndpointId);
513
+ if (successorOwner !== undefined && successorOwner !== manager) return false;
514
+ AsyncJobManager.#byEndpoint.delete(predecessorEndpointId);
515
+ AsyncJobManager.#byEndpoint.set(successorEndpointId, manager);
516
+ return true;
517
+ }
518
+
519
+ /** Remove every endpoint registration owned by the manager. Disposal-safe:
520
+ * covers registrations whose key drifted from the session's current id
521
+ * (provider session ids, mid-transition teardown) and stale predecessor
522
+ * keys, so teardown cannot leave a dead manager behind. */
523
+ static unregisterManager(manager: AsyncJobManager | undefined): void {
524
+ if (!manager) return;
525
+ for (const [endpointId, candidate] of [...AsyncJobManager.#byEndpoint]) {
526
+ if (candidate === manager) AsyncJobManager.#byEndpoint.delete(endpointId);
527
+ }
528
+ }
529
+
432
530
  /** Install or clear the process-global instance. */
433
531
  static setInstance(value: AsyncJobManager | undefined): void {
434
532
  AsyncJobManager.#instance = value;
@@ -437,6 +535,7 @@ export class AsyncJobManager {
437
535
  /** Reset the process-global instance. Test-only. */
438
536
  static resetForTests(): void {
439
537
  AsyncJobManager.#instance = undefined;
538
+ AsyncJobManager.#byEndpoint.clear();
440
539
  }
441
540
 
442
541
  readonly #jobs = new Map<string, AsyncJob>();
@@ -444,7 +543,8 @@ export class AsyncJobManager {
444
543
  readonly #inFlightDeliveries: AsyncJobDelivery[] = [];
445
544
  readonly #suppressedDeliveries = new Set<string>();
446
545
  readonly #deliveryAckOwners = new Map<string, string>();
447
- readonly #watchedJobs = new Set<string>();
546
+ readonly #watchedJobs = new Map<string, number>();
547
+ readonly #watchedGenerations = new Map<string, number>();
448
548
  readonly #evictionTimers = new Map<string, NodeJS.Timeout>();
449
549
  readonly #outputState = new Map<string, AsyncJobOutputState>();
450
550
  readonly #ownerCleanups = new Map<string, Set<() => void>>();
@@ -863,6 +963,11 @@ export class AsyncJobManager {
863
963
  this.#runLifecycle(id, "terminal", job);
864
964
  this.#scheduleEviction(id);
865
965
  this.#drainResumeQueue();
966
+ // The run has now actually unwound: retire the owned tuple so a
967
+ // later scope:"owned" abort of the same turn does not report
968
+ // stopped/stopped_owned while this job is still executing
969
+ // (review thread P1).
970
+ this.#retireCancelledJobOwned(id, job.generation);
866
971
  return;
867
972
  }
868
973
  if (outcome.kind === "paused") {
@@ -908,6 +1013,7 @@ export class AsyncJobManager {
908
1013
  this.#runLifecycle(id, "terminal", job);
909
1014
  this.#scheduleEviction(id);
910
1015
  this.#drainResumeQueue();
1016
+ this.#retireCancelledJobOwned(id, job.generation);
911
1017
  return;
912
1018
  }
913
1019
  const errorText = error instanceof Error ? error.message : String(error);
@@ -936,6 +1042,17 @@ export class AsyncJobManager {
936
1042
  * so cross-agent cancellation is rejected at the manager level.
937
1043
  */
938
1044
  cancel(id: string, filter?: AsyncJobFilter): boolean {
1045
+ if (id.startsWith("queued:")) {
1046
+ // A queued resume (no real job yet): owned settlement cancels it by
1047
+ // removing the queued subagent record and publishing the terminal
1048
+ // (review thread P1).
1049
+ const colon = id.lastIndexOf(":");
1050
+ const subagentId = colon > "queued:".length ? id.slice("queued:".length, colon) : undefined;
1051
+ if (!subagentId) return false;
1052
+ const rec = this.getSubagentRecord(subagentId);
1053
+ if (rec?.status !== "queued") return false;
1054
+ return this.cancelSubagent(subagentId, filter);
1055
+ }
939
1056
  const job = this.#jobs.get(id);
940
1057
  if (!job) return false;
941
1058
  if (filter?.ownerId && job.ownerId !== filter.ownerId) return false;
@@ -949,6 +1066,10 @@ export class AsyncJobManager {
949
1066
  this.#runLifecycle(id, "cancel");
950
1067
  this.#scheduleEviction(id);
951
1068
  this.#drainResumeQueue();
1069
+ // A PAUSED job's run has already unwound (it returned "paused"), so
1070
+ // its owned tuple is retired here; a RUNNING job's tuple is retired
1071
+ // from the settlement path once the run actually unwinds below.
1072
+ this.#retireCancelledJobOwned(id, job.generation);
952
1073
  return true;
953
1074
  }
954
1075
  if (job.status !== "running") return false;
@@ -1463,6 +1584,7 @@ export class AsyncJobManager {
1463
1584
  subagentId: string,
1464
1585
  filter?: AsyncJobFilter,
1465
1586
  message?: string,
1587
+ resumeToolCallId?: string,
1466
1588
  ): { ok: boolean; status?: SubagentLifecycle; jobId?: string; queued?: boolean; reason?: string } {
1467
1589
  const rec = this.getSubagentRecord(subagentId, filter);
1468
1590
  if (!rec) return { ok: false, reason: "not_found" };
@@ -1487,24 +1609,81 @@ export class AsyncJobManager {
1487
1609
  if (this.getRunningJobs().length >= this.#maxRunningJobs) {
1488
1610
  const seq = ++this.#resumeSeq;
1489
1611
  rec.status = "queued";
1490
- rec.queued = { ownerId: rec.ownerId, seq, message, createdAt: Date.now() };
1612
+ rec.queued = {
1613
+ ownerId: rec.ownerId,
1614
+ seq,
1615
+ message,
1616
+ ...(resumeToolCallId ? { resumeToolCallId } : {}),
1617
+ createdAt: Date.now(),
1618
+ };
1491
1619
  this.#resumeQueue.push({
1492
1620
  subagentId: rec.subagentId,
1493
1621
  ownerId: rec.ownerId,
1494
1622
  seq,
1495
1623
  message,
1624
+ ...(resumeToolCallId ? { resumeToolCallId } : {}),
1496
1625
  createdAt: rec.queued.createdAt,
1497
1626
  });
1627
+ // Register the QUEUED generation as owned work of the resume request's
1628
+ // turn: no real job exists until #startResume drains it, so without a
1629
+ // registration a scope:"owned" abort of the requesting turn would see
1630
+ // an empty causal set, report stopped_owned, and then the queue would
1631
+ // drain and launch the resumed subagent anyway (review thread P1).
1632
+ if (resumeToolCallId) {
1633
+ const lineage = resolveToolLineage(resumeToolCallId, AsyncJobManager.endpointIdOf(this));
1634
+ if (lineage) {
1635
+ const queuedGeneration = `queued:${rec.subagentId}:${seq}`;
1636
+ registerOwnedRegistration({
1637
+ ...(lineage.endpointId ? { endpointId: lineage.endpointId } : {}),
1638
+ lineageIdHash: lineage.lineageIdHash,
1639
+ promptAttemptEpoch: lineage.promptAttemptEpoch,
1640
+ endpointGeneration: lineage.endpointGeneration,
1641
+ jobId: queuedGeneration,
1642
+ jobGeneration: queuedGeneration,
1643
+ });
1644
+ }
1645
+ }
1498
1646
  this.#notifyChange();
1499
1647
  return { ok: true, queued: true, status: "queued" };
1500
1648
  }
1501
- return this.#startResume(rec, message, descriptor);
1649
+ return this.#startResume(rec, message, descriptor, resumeToolCallId);
1650
+ }
1651
+
1652
+ /** Retire the owned registration of a job that settles WITHOUT a delivery
1653
+ * (explicit cancellation): the cancelled-job completion path deliberately
1654
+ * enqueues no delivery, so the tuple would otherwise persist until
1655
+ * eviction — an owned abort of the same turn then reports owned_unsettled
1656
+ * with no work remaining, and repeated cancellations exhaust the bounded
1657
+ * ownership registries (review thread P2). */
1658
+ #retireCancelledJobOwned(jobId: string, jobGeneration: string): void {
1659
+ const registration = lookupOwnedRegistration(jobId, jobGeneration, AsyncJobManager.endpointIdOf(this));
1660
+ if (registration) unregisterOwnedRegistration(registration);
1661
+ }
1662
+
1663
+ #retireQueuedOwned(rec: SubagentRecord): void {
1664
+ const seq = rec.queued?.seq ?? rec.terminalQueuedSeq;
1665
+ if (seq === undefined) return;
1666
+ const queuedGeneration = `queued:${rec.subagentId}:${seq}`;
1667
+ // Resolve the registration with the resume lineage's ENDPOINT identity:
1668
+ // task ids are session-scoped and each manager's resume sequence starts
1669
+ // locally, so concurrent sessions can both register an identical
1670
+ // queued:<subagent>:<seq> generation — an endpoint-less lookup could
1671
+ // retrieve and unregister the OTHER session's tuple (review thread P1).
1672
+ const endpointId = rec.queued?.resumeToolCallId
1673
+ ? (resolveToolLineage(rec.queued.resumeToolCallId, AsyncJobManager.endpointIdOf(this))?.endpointId ??
1674
+ // Fall back to the manager's own registered endpoint when the
1675
+ // binding itself predates endpoint keying or is not found.
1676
+ AsyncJobManager.endpointIdOf(this))
1677
+ : AsyncJobManager.endpointIdOf(this);
1678
+ const registration = lookupOwnedRegistration(queuedGeneration, queuedGeneration, endpointId);
1679
+ if (registration) unregisterOwnedRegistration(registration);
1502
1680
  }
1503
1681
 
1504
1682
  #startResume(
1505
1683
  rec: SubagentRecord,
1506
1684
  message: string | undefined,
1507
1685
  descriptor: ResumeDescriptor | undefined,
1686
+ resumeToolCallId?: string,
1508
1687
  ): { ok: boolean; status?: SubagentLifecycle; jobId?: string; reason?: string } {
1509
1688
  if (this.#isOwnerSubagentShutdownFenced(rec.ownerId)) {
1510
1689
  return { ok: false, status: rec.status, reason: "owner_shutdown_in_progress" };
@@ -1515,16 +1694,40 @@ export class AsyncJobManager {
1515
1694
  // never renders the prior run's tool/output as live before it emits again.
1516
1695
  this.#subagentProgress.delete(rec.subagentId);
1517
1696
  const runner = this.#resolveResumeRunner(rec, descriptor);
1518
- const newJobId = runner?.(rec.subagentId, message, descriptor);
1519
- if (!newJobId) return { ok: false, reason: "resume_failed" };
1697
+ const newJobId = runner?.(rec.subagentId, message, descriptor, resumeToolCallId);
1698
+ if (!newJobId) {
1699
+ // The queued resume FAILED to start: retire its owned registration
1700
+ // so the tuple does not accumulate indefinitely (review thread P2).
1701
+ this.#retireQueuedOwned(rec);
1702
+ return { ok: false, reason: "resume_failed" };
1703
+ }
1520
1704
  if (prevJobId && prevJobId !== newJobId) rec.historicalJobIds.push(prevJobId);
1521
1705
  rec.terminalGeneration = undefined;
1522
1706
  rec.currentJobId = newJobId;
1523
1707
  rec.currentJobGeneration = this.#jobs.get(newJobId)?.generation;
1524
1708
  rec.status = this.#jobs.get(newJobId)?.status ?? "running";
1525
1709
  rec.queued = undefined;
1526
- if (queuedGeneration)
1710
+ if (queuedGeneration) {
1527
1711
  this.#waitGenerationAliases.set(queuedGeneration, this.#jobs.get(newJobId)?.generation ?? newJobId);
1712
+ // The queued resume's owned registration is superseded by the real
1713
+ // job's registration (bound by the task tool): remove it so the
1714
+ // causal set has exactly one tuple per job. The EXACT stored tuple
1715
+ // is looked up first — unregisterOwnedRegistration now verifies the
1716
+ // full five-tuple before deleting (review thread P1). The lookup is
1717
+ // ENDPOINT-qualified via the resume lineage: concurrent sessions
1718
+ // mint the same queued:<subagent>:<seq> generation, and the
1719
+ // remaining endpoint-less fallback scan could retrieve and
1720
+ // unregister the OTHER session's tuple (review thread P1).
1721
+ const resumeEndpoint = resumeToolCallId
1722
+ ? (resolveToolLineage(resumeToolCallId, AsyncJobManager.endpointIdOf(this))?.endpointId ??
1723
+ // The binding may have been evicted (8192-cap FIFO): fall back
1724
+ // to the manager's own endpoint so the lookup never degrades
1725
+ // into the cross-endpoint scan (review thread P2).
1726
+ AsyncJobManager.endpointIdOf(this))
1727
+ : undefined;
1728
+ const queuedReg = lookupOwnedRegistration(queuedGeneration, queuedGeneration, resumeEndpoint);
1729
+ if (queuedReg) unregisterOwnedRegistration(queuedReg);
1730
+ }
1528
1731
  this.#notifyChange();
1529
1732
  return { ok: true, status: rec.status, jobId: newJobId };
1530
1733
  }
@@ -1546,15 +1749,22 @@ export class AsyncJobManager {
1546
1749
  continue;
1547
1750
  }
1548
1751
  try {
1549
- const result = this.#startResume(rec, entry.message, this.#descriptorForRecord(rec));
1752
+ const result = this.#startResume(
1753
+ rec,
1754
+ entry.message,
1755
+ this.#descriptorForRecord(rec),
1756
+ rec.queued?.resumeToolCallId,
1757
+ );
1550
1758
  if (!result.ok) {
1551
1759
  if (result.reason === "owner_shutdown_in_progress") {
1552
1760
  index += 1;
1553
1761
  continue;
1554
1762
  }
1555
1763
  const queuedSeq = rec.queued?.seq;
1556
- if (queuedSeq !== undefined)
1764
+ if (queuedSeq !== undefined) {
1765
+ this.#retireQueuedOwned(rec);
1557
1766
  this.#publishQueuedTerminal(rec.subagentId, `queued:${rec.subagentId}:${queuedSeq}`, "failed");
1767
+ }
1558
1768
  }
1559
1769
  this.#resumeQueue.splice(index, 1);
1560
1770
  } catch (error) {
@@ -1563,8 +1773,10 @@ export class AsyncJobManager {
1563
1773
  continue;
1564
1774
  }
1565
1775
  const queuedSeq = rec.queued?.seq;
1566
- if (queuedSeq !== undefined)
1776
+ if (queuedSeq !== undefined) {
1777
+ this.#retireQueuedOwned(rec);
1567
1778
  this.#publishQueuedTerminal(rec.subagentId, `queued:${rec.subagentId}:${queuedSeq}`, "failed");
1779
+ }
1568
1780
  this.#resumeQueue.splice(index, 1);
1569
1781
  }
1570
1782
  }
@@ -1587,6 +1799,13 @@ export class AsyncJobManager {
1587
1799
  if (shouldScheduleEviction && currentJobId) this.#scheduleEviction(currentJobId);
1588
1800
  else this.#notifyChange();
1589
1801
  this.#drainResumeQueue();
1802
+ // A paused run has already unwound (it returned "paused") and this
1803
+ // direct branch never reaches cancel(), so retire the owned tuple
1804
+ // here — the cancellation emits no completion delivery, and without
1805
+ // this the tuple survives job eviction and a later scope:"owned"
1806
+ // abort of the attempt reports owned_unsettled with no work left
1807
+ // (review thread P2).
1808
+ if (shouldScheduleEviction && currentJobId && job) this.#retireCancelledJobOwned(currentJobId, job.generation);
1590
1809
  return true;
1591
1810
  }
1592
1811
  if (rec.status === "queued") {
@@ -1594,8 +1813,16 @@ export class AsyncJobManager {
1594
1813
  const queuedSeq = rec.queued?.seq;
1595
1814
  if (idx !== -1) this.#resumeQueue.splice(idx, 1);
1596
1815
  rec.status = "cancelled";
1597
- if (queuedSeq !== undefined)
1816
+ if (queuedSeq !== undefined) {
1598
1817
  this.#publishQueuedTerminal(rec.subagentId, `queued:${rec.subagentId}:${queuedSeq}`, "cancelled");
1818
+ // rec.queued is cleared below; retain terminal evidence ON THE
1819
+ // RECORD so owned settlement can prove the queued generation was
1820
+ // cancelled even when an in-flight settlement outlives any
1821
+ // bounded evidence set (review thread P2). The owned tuple is
1822
+ // retired at this terminal state too.
1823
+ rec.terminalQueuedSeq = queuedSeq;
1824
+ this.#retireQueuedOwned(rec);
1825
+ }
1599
1826
  rec.queued = undefined;
1600
1827
  this.#subagentProgress.delete(rec.subagentId);
1601
1828
  this.#notifyChange();
@@ -1610,6 +1837,13 @@ export class AsyncJobManager {
1610
1837
  }
1611
1838
  for (const [sid, rec] of this.#subagentRecords) {
1612
1839
  if (!ownerId || rec.ownerId === ownerId) {
1840
+ // Retire the queued resume's owned registration BEFORE deleting the
1841
+ // record: the purge removes the resume-queue entry and record with
1842
+ // no start, cancellation, or delivery boundary, so the registration
1843
+ // would otherwise leak into the global ownership registries and
1844
+ // eventually make later owned aborts fail closed (review thread
1845
+ // P2). #retireQueuedOwned is a no-op for non-queued records.
1846
+ this.#retireQueuedOwned(rec);
1613
1847
  this.#liveHandles.delete(sid);
1614
1848
  this.#resumeDescriptors.delete(sid);
1615
1849
  this.#descriptorResumeRunners.delete(sid);
@@ -1620,9 +1854,69 @@ export class AsyncJobManager {
1620
1854
  }
1621
1855
 
1622
1856
  getJob(id: string): AsyncJob | undefined {
1857
+ if (id.startsWith("queued:")) {
1858
+ const colon = id.lastIndexOf(":");
1859
+ const subagentId = colon > "queued:".length ? id.slice("queued:".length, colon) : undefined;
1860
+ if (!subagentId) return undefined;
1861
+ const rec = this.getSubagentRecord(subagentId);
1862
+ if (!rec) return undefined;
1863
+ const liveSeq = rec.queued?.seq;
1864
+ const terminalSeq = rec.terminalQueuedSeq;
1865
+ // A failed queued resume publishes its terminalGeneration (the
1866
+ // queued id) via #publishQueuedTerminal — that is retained
1867
+ // failed-generation evidence for the settlement proof.
1868
+ const terminalQueuedId = terminalSeq !== undefined ? `queued:${subagentId}:${terminalSeq}` : undefined;
1869
+ const publishedQueuedId = rec.terminalGeneration?.startsWith("queued:") ? rec.terminalGeneration : undefined;
1870
+ if (liveSeq === undefined && terminalQueuedId === undefined && publishedQueuedId === undefined)
1871
+ return undefined;
1872
+ const resolvedQueuedId =
1873
+ liveSeq !== undefined ? `queued:${subagentId}:${liveSeq}` : (terminalQueuedId ?? publishedQueuedId);
1874
+ if (resolvedQueuedId === undefined || id !== resolvedQueuedId) return undefined;
1875
+ if (liveSeq === undefined) {
1876
+ // The queued resume reached a TERMINAL state (cancelled or
1877
+ // failed, rec.queued cleared): the generation is provably
1878
+ // terminal so owned settlement's second proof succeeds
1879
+ // (review thread P2).
1880
+ return {
1881
+ id,
1882
+ generation: id,
1883
+ type: "task",
1884
+ status: rec.status === "failed" ? "failed" : "cancelled",
1885
+ startTime: Date.now(),
1886
+ label: `terminal queued resume ${subagentId}`,
1887
+ abortController: new AbortController(),
1888
+ promise: Promise.resolve(),
1889
+ };
1890
+ }
1891
+ if (id !== `queued:${subagentId}:${liveSeq}`) return undefined;
1892
+ return {
1893
+ id,
1894
+ generation: id,
1895
+ type: "task",
1896
+ // A still-queued resume is NOT quiescent ("paused" fails the owned
1897
+ // settlement's second proof); once cancelled it is terminal.
1898
+ status: rec.status === "cancelled" ? "cancelled" : "paused",
1899
+ startTime: rec.queued?.createdAt ?? Date.now(),
1900
+ label: `queued resume ${subagentId}`,
1901
+ abortController: new AbortController(),
1902
+ promise: Promise.resolve(),
1903
+ };
1904
+ }
1623
1905
  return this.#jobs.get(id);
1624
1906
  }
1625
1907
 
1908
+ /**
1909
+ * The EXECUTION promise of the job record whose generation matches the
1910
+ * captured generation, or undefined when the record is gone or rebound.
1911
+ * The promise settles only when the job's function actually unwinds — the
1912
+ * eagerly-updated cancel status is not proof of quiescence (review P1).
1913
+ */
1914
+ getJobPromise(id: string, generation: string): Promise<void> | undefined {
1915
+ const job = this.#jobs.get(id);
1916
+ if (!job || job.generation !== generation) return undefined;
1917
+ return job.promise;
1918
+ }
1919
+
1626
1920
  getRunningJobs(filter?: AsyncJobFilter): AsyncJob[] {
1627
1921
  return this.#filterJobs(this.#jobs.values(), filter).filter(job => job.status === "running");
1628
1922
  }
@@ -1805,10 +2099,38 @@ export class AsyncJobManager {
1805
2099
  return this.getDeliveryState(filter).queued > 0;
1806
2100
  }
1807
2101
 
2102
+ watchJobGenerations(jobIds: string[]): AsyncJobWatchHandle {
2103
+ const uniqueJobIds = Array.from(new Set(jobIds.map(id => id.trim()).filter(id => id.length > 0)));
2104
+ const generations: string[] = [];
2105
+ for (const jobId of uniqueJobIds) {
2106
+ const generation = this.#jobs.get(jobId)?.generation;
2107
+ if (!generation) continue;
2108
+ generations.push(generation);
2109
+ this.#watchedGenerations.set(generation, (this.#watchedGenerations.get(generation) ?? 0) + 1);
2110
+ }
2111
+ let closed = false;
2112
+ return {
2113
+ close: () => {
2114
+ if (closed) return 0;
2115
+ closed = true;
2116
+ let removed = 0;
2117
+ for (const generation of generations) {
2118
+ const watchers = this.#watchedGenerations.get(generation) ?? 0;
2119
+ if (watchers === 1) {
2120
+ this.#watchedGenerations.delete(generation);
2121
+ removed += 1;
2122
+ } else if (watchers > 1) this.#watchedGenerations.set(generation, watchers - 1);
2123
+ }
2124
+ if (removed > 0) this.#ensureDeliveryLoop();
2125
+ return removed;
2126
+ },
2127
+ };
2128
+ }
2129
+
1808
2130
  watchJobs(jobIds: string[]): number {
1809
2131
  const uniqueJobIds = Array.from(new Set(jobIds.map(id => id.trim()).filter(id => id.length > 0)));
1810
2132
  for (const jobId of uniqueJobIds) {
1811
- this.#watchedJobs.add(jobId);
2133
+ this.#watchedJobs.set(jobId, (this.#watchedJobs.get(jobId) ?? 0) + 1);
1812
2134
  }
1813
2135
  return uniqueJobIds.length;
1814
2136
  }
@@ -1817,9 +2139,11 @@ export class AsyncJobManager {
1817
2139
  const uniqueJobIds = Array.from(new Set(jobIds.map(id => id.trim()).filter(id => id.length > 0)));
1818
2140
  let removed = 0;
1819
2141
  for (const jobId of uniqueJobIds) {
1820
- if (this.#watchedJobs.delete(jobId)) {
2142
+ const watchers = this.#watchedJobs.get(jobId) ?? 0;
2143
+ if (watchers === 1) {
2144
+ this.#watchedJobs.delete(jobId);
1821
2145
  removed += 1;
1822
- }
2146
+ } else if (watchers > 1) this.#watchedJobs.set(jobId, watchers - 1);
1823
2147
  }
1824
2148
  if (removed > 0) this.#ensureDeliveryLoop();
1825
2149
  return removed;
@@ -2004,6 +2328,7 @@ export class AsyncJobManager {
2004
2328
  this.#deliveryAckOwners.clear();
2005
2329
  this.#waitGenerationAliases.clear();
2006
2330
  this.#watchedJobs.clear();
2331
+ this.#watchedGenerations.clear();
2007
2332
  this.#outputState.clear();
2008
2333
  this.#ownerCleanups.clear();
2009
2334
  this.#subagentRecords.clear();
@@ -2077,7 +2402,6 @@ export class AsyncJobManager {
2077
2402
  this.#deadLetteredDeliveryOwners.delete(jobId);
2078
2403
  this.#suppressedDeliveries.delete(jobId);
2079
2404
  if (job) this.#publishedTerminalGenerations.delete(job.generation);
2080
- this.#watchedJobs.delete(jobId);
2081
2405
  this.#outputState.delete(jobId);
2082
2406
  }
2083
2407
 
@@ -2161,7 +2485,12 @@ export class AsyncJobManager {
2161
2485
  }
2162
2486
 
2163
2487
  isDeliverySuppressed(jobId: string, generation?: string): boolean {
2164
- return this.#isDeliveryAcknowledged(jobId, generation) || this.#watchedJobs.has(jobId);
2488
+ const watchedGeneration = generation ?? this.#jobs.get(jobId)?.generation;
2489
+ return (
2490
+ this.#isDeliveryAcknowledged(jobId, generation) ||
2491
+ (this.#watchedJobs.get(jobId) ?? 0) > 0 ||
2492
+ (watchedGeneration !== undefined && (this.#watchedGenerations.get(watchedGeneration) ?? 0) > 0)
2493
+ );
2165
2494
  }
2166
2495
 
2167
2496
  #pruneEvictedDeadLetters(): void {
@@ -2185,6 +2514,12 @@ export class AsyncJobManager {
2185
2514
  lastError: delivery.lastError,
2186
2515
  });
2187
2516
  this.#deadLetteredDeliveryOwners.set(delivery.jobId, delivery.ownerId);
2517
+ // The dead-lettered delivery never injects a message and has no later
2518
+ // consumption boundary: retire the exact owned registration so the
2519
+ // terminal tuple does not occupy the global registries until a future
2520
+ // job-record eviction makes it invisible to settlement scans (review
2521
+ // thread P2).
2522
+ retireOwnedRegistrationForDeadLetter(AsyncJobManager.endpointIdOf(this), delivery.jobId, delivery.generation);
2188
2523
  while (this.#deadLetteredDeliveries.size > MAX_DEAD_LETTERED_DELIVERIES) {
2189
2524
  const oldestJobId = this.#deadLetteredDeliveries.keys().next().value;
2190
2525
  if (oldestJobId === undefined) return;
@@ -1,6 +1,31 @@
1
+ import * as path from "node:path";
2
+ import { resolveEquivalentPath } from "@gajae-code/utils";
1
3
  import type { Settings } from "../config/settings";
2
4
 
3
5
  export function isBackgroundJobSupportEnabled(settings: Pick<Settings, "get">): boolean {
4
6
  void settings;
5
7
  return true;
6
8
  }
9
+
10
+ /**
11
+ * Single source of truth for the AsyncJobManager endpoint key.
12
+ *
13
+ * A session with an explicit provider session id is keyed by (provider id,
14
+ * transcript path); everything else is keyed by its logical session id. The
15
+ * transcript component is canonicalized so a path alias — symlink, `..`
16
+ * segment, or unresolved relative path — cannot register the manager under one
17
+ * key at construction and look it up under another across a session-identity
18
+ * transition, stranding ownership.
19
+ *
20
+ * Both the SDK constructor and AgentSession's transition rekeying must derive
21
+ * the key here; duplicating the shape lets the two drift apart silently.
22
+ */
23
+ export function asyncJobEndpointId(
24
+ providerSessionId: string | undefined,
25
+ sessionId: string,
26
+ sessionFile: string | undefined,
27
+ ): string {
28
+ return providerSessionId !== undefined && sessionFile !== undefined
29
+ ? JSON.stringify(["async-job-endpoint", providerSessionId, resolveEquivalentPath(path.resolve(sessionFile))])
30
+ : sessionId;
31
+ }
@@ -5,20 +5,39 @@ import { currentResults, findBaselineMetric, findBaselineRunNumber, findBaseline
5
5
  import type { AutoresearchRuntime, DashboardController, ExperimentResult, ExperimentState } from "./types";
6
6
 
7
7
  export function createDashboardController(): DashboardController {
8
- let overlayTui: { requestRender(): void } | null = null;
8
+ let overlayTui: { requestLayoutRender(source?: string): void } | null = null;
9
9
  let spinnerTimer: NodeJS.Timeout | undefined;
10
10
  let spinnerFrame = 0;
11
11
 
12
12
  const requestRender = (): void => {
13
- overlayTui?.requestRender();
13
+ overlayTui?.requestLayoutRender("autoresearch-overlay");
14
+ };
15
+
16
+ const stopSpinner = (): void => {
17
+ if (!spinnerTimer) return;
18
+ clearInterval(spinnerTimer);
19
+ spinnerTimer = undefined;
20
+ };
21
+
22
+ const syncSpinner = (runtime: AutoresearchRuntime): void => {
23
+ if (!overlayTui || !runtime.runningExperiment) {
24
+ stopSpinner();
25
+ return;
26
+ }
27
+ if (spinnerTimer) return;
28
+ spinnerTimer = setInterval(() => {
29
+ if (!runtime.runningExperiment) {
30
+ stopSpinner();
31
+ return;
32
+ }
33
+ spinnerFrame += 1;
34
+ requestRender();
35
+ }, 80);
14
36
  };
15
37
 
16
38
  const clear = (): void => {
17
39
  overlayTui = null;
18
- if (spinnerTimer) {
19
- clearInterval(spinnerTimer);
20
- spinnerTimer = undefined;
21
- }
40
+ stopSpinner();
22
41
  };
23
42
 
24
43
  return {
@@ -31,6 +50,7 @@ export function createDashboardController(): DashboardController {
31
50
  requestRender,
32
51
  updateWidget(ctx, runtime): void {
33
52
  if (!ctx.hasUI) return;
53
+ syncSpinner(runtime);
34
54
  const state = runtime.state;
35
55
  if (!shouldShowDashboard(runtime, state)) {
36
56
  ctx.ui.setWidget("autoresearch", undefined);
@@ -57,12 +77,7 @@ export function createDashboardController(): DashboardController {
57
77
  await ctx.ui.custom<void>(
58
78
  (tui, theme, _keybindings, done) => {
59
79
  overlayTui = tui;
60
- if (!spinnerTimer) {
61
- spinnerTimer = setInterval(() => {
62
- spinnerFrame += 1;
63
- requestRender();
64
- }, 80);
65
- }
80
+ syncSpinner(runtime);
66
81
 
67
82
  let scrollOffset = 0;
68
83
  return {
@@ -108,7 +123,7 @@ export function createDashboardController(): DashboardController {
108
123
  } else if (data === "G") {
109
124
  scrollOffset = maxScroll;
110
125
  }
111
- tui.requestRender();
126
+ tui.requestLayoutRender("autoresearch-overlay-input");
112
127
  },
113
128
  invalidate(): void {},
114
129
  dispose(): void {