@gajae-code/coding-agent 0.18.7 → 0.18.9

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 (344) hide show
  1. package/CHANGELOG.md +377 -1
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/job-manager.d.ts +1 -1
  4. package/dist/types/async/support.d.ts +0 -14
  5. package/dist/types/capability/mcp.d.ts +2 -0
  6. package/dist/types/capability/skill.d.ts +2 -0
  7. package/dist/types/capability/types.d.ts +2 -0
  8. package/dist/types/cli/public-command-errors.d.ts +3 -1
  9. package/dist/types/cli/setup-cli.d.ts +1 -1
  10. package/dist/types/commands/deep-interview.d.ts +3 -0
  11. package/dist/types/commands/mcp-serve.d.ts +4 -0
  12. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  13. package/dist/types/config/file-lock.d.ts +1 -0
  14. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  15. package/dist/types/config/model-profile-activation.d.ts +11 -3
  16. package/dist/types/config/model-registry.d.ts +4 -9
  17. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  18. package/dist/types/cursor-delete-path.d.ts +1 -0
  19. package/dist/types/cursor-mcp-name.d.ts +8 -0
  20. package/dist/types/customization/inventory.d.ts +2 -0
  21. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  24. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  25. package/dist/types/discovery/helpers.d.ts +6 -1
  26. package/dist/types/discovery/mcp-json.d.ts +1 -0
  27. package/dist/types/eval/py/kernel.d.ts +18 -0
  28. package/dist/types/export/html/css-color.d.ts +2 -0
  29. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  32. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  33. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  34. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  35. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  36. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  37. package/dist/types/extensibility/skill-management.d.ts +4 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  40. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  41. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  42. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  44. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  45. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  46. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  47. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  48. package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
  49. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  50. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  51. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  52. package/dist/types/lsp/config.d.ts +8 -5
  53. package/dist/types/lsp/workspace-path.d.ts +1 -0
  54. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  55. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  56. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  57. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  58. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  59. package/dist/types/modes/interactive-mode.d.ts +1 -0
  60. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  61. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  62. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  63. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  64. package/dist/types/progress/progress-contract.d.ts +154 -0
  65. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  66. package/dist/types/progress/project-progress.d.ts +141 -0
  67. package/dist/types/progress/render-progress.d.ts +28 -0
  68. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  69. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  70. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
  71. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  72. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  73. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  74. package/dist/types/runtime-mcp/types.d.ts +6 -0
  75. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  76. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  77. package/dist/types/sdk/broker/broker.d.ts +24 -2
  78. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  79. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  80. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  81. package/dist/types/sdk/bus/index.d.ts +12 -1
  82. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  83. package/dist/types/sdk/client/client.d.ts +2 -0
  84. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  85. package/dist/types/sdk/index.d.ts +1 -0
  86. package/dist/types/sdk/router/session-router.d.ts +2 -0
  87. package/dist/types/sdk/session-list.d.ts +1 -1
  88. package/dist/types/sdk/session.d.ts +8 -0
  89. package/dist/types/session/agent-session.d.ts +38 -9
  90. package/dist/types/session/artifacts.d.ts +2 -2
  91. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  92. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  93. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  94. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  95. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  96. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  97. package/dist/types/session/session-manager.d.ts +31 -5
  98. package/dist/types/session/session-retirement.d.ts +31 -7
  99. package/dist/types/session/session-storage.d.ts +69 -2
  100. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  101. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  102. package/dist/types/setup/credential-import.d.ts +6 -4
  103. package/dist/types/skill-state/active-state.d.ts +38 -0
  104. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  105. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  106. package/dist/types/task/executor.d.ts +3 -0
  107. package/dist/types/task/isolation-dir.d.ts +26 -0
  108. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  109. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  110. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  111. package/dist/types/tools/github-side-effect.d.ts +22 -0
  112. package/dist/types/tools/index.d.ts +3 -0
  113. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  114. package/dist/types/tools/python.d.ts +9 -1
  115. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  116. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  117. package/dist/types/tools/todo-write.d.ts +6 -0
  118. package/dist/types/utils/tools-checksum.d.ts +7 -0
  119. package/package.json +7 -7
  120. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  121. package/scripts/verify-managed-empty-stop.ts +28 -0
  122. package/src/async/endpoint-id.ts +26 -0
  123. package/src/async/job-manager.ts +247 -132
  124. package/src/async/support.ts +0 -25
  125. package/src/capability/index.ts +15 -0
  126. package/src/capability/mcp.ts +2 -0
  127. package/src/capability/skill.ts +2 -0
  128. package/src/capability/types.ts +2 -0
  129. package/src/cli/auth-broker-cli.ts +1 -1
  130. package/src/cli/customize-doctor.ts +15 -2
  131. package/src/cli/doctor/repairs.ts +4 -1
  132. package/src/cli/doctor-supervisor.ts +3 -0
  133. package/src/cli/public-command-errors.ts +21 -1
  134. package/src/cli/public-command-registry.ts +28 -6
  135. package/src/cli/setup-cli.ts +4 -4
  136. package/src/cli/skills-cli.ts +5 -0
  137. package/src/commands/deep-interview.ts +47 -8
  138. package/src/commands/mcp-serve.ts +7 -0
  139. package/src/commands/sdk.ts +455 -397
  140. package/src/config/autorouting-tier-map.ts +117 -7
  141. package/src/config/file-lock.ts +316 -79
  142. package/src/config/mcp-schema.json +4 -0
  143. package/src/config/model-bindings-applier.ts +20 -0
  144. package/src/config/model-profile-activation.ts +240 -46
  145. package/src/config/model-registry.ts +47 -153
  146. package/src/config/skill-settings-defaults.ts +7 -0
  147. package/src/coordinator-mcp/policy.ts +3 -2
  148. package/src/coordinator-mcp/server.ts +2 -0
  149. package/src/cursor-delete-path.ts +10 -0
  150. package/src/cursor-mcp-name.ts +15 -0
  151. package/src/cursor.ts +13 -2
  152. package/src/customization/inventory.ts +4 -13
  153. package/src/dap/config.ts +2 -2
  154. package/src/debug/report-bundle-parent.ts +22 -0
  155. package/src/debug/report-bundle.ts +3 -0
  156. package/src/debug/system-info.ts +13 -1
  157. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  158. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  159. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  160. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  161. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  162. package/src/defaults/gjc-defaults.test.ts +18 -1
  163. package/src/defaults/gjc-defaults.ts +3 -0
  164. package/src/defaults/gjc-skills.generated.ts +58 -36
  165. package/src/discovery/agents.ts +6 -1
  166. package/src/discovery/builtin.ts +13 -1
  167. package/src/discovery/helpers.ts +171 -29
  168. package/src/discovery/mcp-json.ts +26 -12
  169. package/src/discovery/ssh.ts +1 -1
  170. package/src/discovery/substitute-plugin-root.ts +3 -1
  171. package/src/edit/file-read-cache.ts +1 -1
  172. package/src/eval/py/executor.ts +796 -154
  173. package/src/eval/py/kernel.ts +79 -13
  174. package/src/eval/py/tool-bridge.ts +4 -0
  175. package/src/export/html/css-color.ts +9 -0
  176. package/src/export/html/html-export-destination.ts +16 -0
  177. package/src/export/html/index.ts +7 -10
  178. package/src/export/html/template.generated.ts +1 -1
  179. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  180. package/src/extensibility/extensions/runner.ts +15 -1
  181. package/src/extensibility/extensions/types.ts +7 -0
  182. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  183. package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -8
  184. package/src/extensibility/plugins/manager.ts +3 -2
  185. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  186. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  187. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  188. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  189. package/src/extensibility/skill-management.ts +9 -2
  190. package/src/extensibility/skills.ts +6 -1
  191. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  192. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  193. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  194. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  195. package/src/gjc-runtime/gc-runtime.ts +206 -9
  196. package/src/gjc-runtime/launch-worktree.ts +70 -9
  197. package/src/gjc-runtime/psmux-detect.ts +28 -4
  198. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  199. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  200. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  201. package/src/gjc-runtime/state-argv.ts +2 -0
  202. package/src/gjc-runtime/state-migrations.ts +91 -29
  203. package/src/gjc-runtime/state-runtime.ts +3716 -135
  204. package/src/gjc-runtime/state-writer.ts +105 -16
  205. package/src/gjc-runtime/tmux-common.ts +8 -5
  206. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  207. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  208. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  209. package/src/harness-control-plane/operate.ts +12 -4
  210. package/src/harness-control-plane/owner.ts +5 -1
  211. package/src/hashline/parser.ts +48 -9
  212. package/src/hashline/recovery.ts +13 -4
  213. package/src/internal-urls/docs-index.generated.ts +20 -19
  214. package/src/internal-urls/skill-path.ts +19 -0
  215. package/src/internal-urls/skill-protocol.ts +2 -0
  216. package/src/lsp/config.ts +12 -63
  217. package/src/lsp/edits.ts +6 -0
  218. package/src/lsp/workspace-path.ts +51 -0
  219. package/src/main.ts +195 -31
  220. package/src/memories/index.ts +88 -7
  221. package/src/modes/acp/acp-agent.ts +88 -4
  222. package/src/modes/components/custom-editor.ts +14 -0
  223. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  224. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  225. package/src/modes/components/extensions/state-manager.ts +15 -5
  226. package/src/modes/components/hook-editor.ts +1 -1
  227. package/src/modes/components/hook-selector.ts +1 -1
  228. package/src/modes/components/model-selector.ts +1 -4
  229. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  230. package/src/modes/components/status-line/segments.ts +4 -0
  231. package/src/modes/controllers/event-controller.ts +11 -4
  232. package/src/modes/controllers/input-controller.ts +76 -31
  233. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
  234. package/src/modes/controllers/selector-controller.ts +24 -15
  235. package/src/modes/controllers/todo-command-controller.ts +1 -1
  236. package/src/modes/interactive-mode.ts +5 -1
  237. package/src/modes/runtime-init.ts +4 -0
  238. package/src/modes/utils/abort-message.ts +3 -1
  239. package/src/modes/utils/interrupt-ownership.ts +12 -0
  240. package/src/modes/utils/keybinding-matchers.ts +4 -0
  241. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  242. package/src/progress/collect-project-progress.ts +153 -0
  243. package/src/progress/progress-contract.ts +172 -0
  244. package/src/progress/progress-snapshot.ts +140 -0
  245. package/src/progress/project-progress.ts +446 -0
  246. package/src/progress/render-progress.ts +209 -0
  247. package/src/prompts/system/master-mode.md +1 -0
  248. package/src/prompts/tools/skill-discovery.md +1 -1
  249. package/src/runtime-mcp/client.ts +4 -2
  250. package/src/runtime-mcp/config-writer.ts +10 -6
  251. package/src/runtime-mcp/config.ts +1 -0
  252. package/src/runtime-mcp/json-rpc.ts +2 -1
  253. package/src/runtime-mcp/manager.ts +29 -11
  254. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  255. package/src/runtime-mcp/oauth-flow.ts +14 -2
  256. package/src/runtime-mcp/oauth-public-url.ts +14 -0
  257. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  258. package/src/runtime-mcp/pool.ts +6 -0
  259. package/src/runtime-mcp/project-host-values.ts +46 -0
  260. package/src/runtime-mcp/smithery-registry.ts +22 -3
  261. package/src/runtime-mcp/transports/http.ts +4 -3
  262. package/src/runtime-mcp/types.ts +6 -0
  263. package/src/sdk/acp/adapter.ts +95 -8
  264. package/src/sdk/broker/broker-exit.ts +52 -23
  265. package/src/sdk/broker/broker.ts +81 -6
  266. package/src/sdk/broker/doctor-restart.ts +4 -0
  267. package/src/sdk/broker/ensure.ts +31 -2
  268. package/src/sdk/broker/lifecycle.ts +711 -66
  269. package/src/sdk/broker/session-index.ts +191 -81
  270. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  271. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  272. package/src/sdk/bus/index.ts +238 -43
  273. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  274. package/src/sdk/cli/session-cli.ts +17 -3
  275. package/src/sdk/client/client.ts +28 -8
  276. package/src/sdk/host/query/handlers.ts +9 -1
  277. package/src/sdk/host/session-runtime.test.ts +2705 -303
  278. package/src/sdk/host/session-runtime.ts +490 -143
  279. package/src/sdk/host/surface-policy.ts +1 -0
  280. package/src/sdk/index.ts +8 -0
  281. package/src/sdk/lifecycle-session.ts +3 -1
  282. package/src/sdk/prompt-deadline-manager.ts +15 -0
  283. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  284. package/src/sdk/protocol/operation-registry.ts +9 -2
  285. package/src/sdk/router/session-router.ts +82 -18
  286. package/src/sdk/session-list.ts +1 -0
  287. package/src/sdk/session.ts +69 -45
  288. package/src/session/agent-session.ts +679 -238
  289. package/src/session/artifacts.ts +176 -71
  290. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  291. package/src/session/internal/managed-session-scope.ts +3779 -462
  292. package/src/session/internal/managed-session-storage.ts +501 -78
  293. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  294. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  295. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  296. package/src/session/session-manager.ts +951 -133
  297. package/src/session/session-retirement.ts +573 -14
  298. package/src/session/session-storage.ts +447 -15
  299. package/src/session/task-artifact-owner-codec.ts +71 -8
  300. package/src/session/task-artifact-owner-retirement.ts +21 -19
  301. package/src/setup/credential-auto-import.ts +12 -3
  302. package/src/setup/credential-import.ts +176 -5
  303. package/src/setup/paseo/announce.ts +6 -1
  304. package/src/skill-state/active-state.ts +145 -2
  305. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  306. package/src/slash-commands/builtin-registry.ts +39 -9
  307. package/src/slash-commands/helpers/mcp.ts +8 -4
  308. package/src/slash-commands/helpers/progress-report.ts +23 -0
  309. package/src/stt/setup.ts +3 -5
  310. package/src/task/commands.ts +3 -1
  311. package/src/task/executor.ts +4 -0
  312. package/src/task/index.ts +6 -0
  313. package/src/task/isolation-dir.ts +143 -0
  314. package/src/task/worktree.ts +16 -7
  315. package/src/tools/ask.ts +205 -6
  316. package/src/tools/ast-edit-write-path.ts +68 -0
  317. package/src/tools/ast-edit.ts +10 -1
  318. package/src/tools/bash-allowed-prefixes.ts +15 -0
  319. package/src/tools/bash-skill-urls.ts +56 -3
  320. package/src/tools/bash.ts +48 -79
  321. package/src/tools/browser/cdp-url.ts +18 -0
  322. package/src/tools/browser.ts +8 -1
  323. package/src/tools/descriptors.ts +5 -1
  324. package/src/tools/eval.ts +56 -36
  325. package/src/tools/gh.ts +10 -0
  326. package/src/tools/github-side-effect.ts +112 -0
  327. package/src/tools/image-gen.ts +1 -1
  328. package/src/tools/index.ts +3 -0
  329. package/src/tools/monitor-mutation.ts +27 -0
  330. package/src/tools/monitor.ts +7 -0
  331. package/src/tools/python.ts +188 -66
  332. package/src/tools/recipe/index.ts +9 -0
  333. package/src/tools/recipe/recipe-guard.ts +19 -0
  334. package/src/tools/restricted-cwd.ts +10 -0
  335. package/src/tools/skill-discovery.ts +5 -0
  336. package/src/tools/skill.ts +28 -1
  337. package/src/tools/todo-write.ts +10 -1
  338. package/src/tools/tool-catalog.generated.ts +1 -1
  339. package/src/tools/write.ts +1 -0
  340. package/src/utils/external-editor.ts +3 -2
  341. package/src/utils/tools-checksum.ts +34 -0
  342. package/src/utils/tools-manager.ts +24 -6
  343. package/src/web/insane/url-guard.ts +2 -1
  344. package/src/web/search/providers/codex.ts +3 -2
@@ -22,6 +22,7 @@ export type BrokerFenceReason = "suspect-unpublished" | "observation-ambiguous"
22
22
  export type BrokerExitReason =
23
23
  | "ownership-fence-expired"
24
24
  | "publication-liveness-timeout"
25
+ | "heartbeat-renewal-blocked"
25
26
  | "restart-committed"
26
27
  | "shutdown-request"
27
28
  | "startup-failure"
@@ -39,6 +40,8 @@ export interface BrokerExitRecord {
39
40
  fenceReason: BrokerFenceReason | null;
40
41
  fencedForMs: number;
41
42
  uptimeMs: number;
43
+ /** For a blocked heartbeat renewal: retained removal transition path. */
44
+ blockingLockPath?: string;
42
45
  pid: number;
43
46
  signal: "SIGINT" | "SIGTERM" | null;
44
47
  writtenAt: number;
@@ -47,7 +50,7 @@ export interface BrokerExitRecord {
47
50
  export interface BrokerStartupExitRecord {
48
51
  version: 1;
49
52
  mode: "startup";
50
- reason: "startup-deadline" | "startup-signal";
53
+ reason: "startup-deadline" | "startup-signal" | "startup-lock-blocked";
51
54
  fenceReason: null;
52
55
  fencedForMs: 0;
53
56
  uptimeMs: number;
@@ -56,6 +59,8 @@ export interface BrokerStartupExitRecord {
56
59
  exitCode: 1 | 130 | 143;
57
60
  timeoutMs: number | null;
58
61
  writtenAt: number;
62
+ /** For lock-blocked reason: path to the retained removal transition. */
63
+ blockingLockPath?: string;
59
64
  }
60
65
 
61
66
  export type BrokerStartupExitWriteStatus =
@@ -71,6 +76,7 @@ const lastWriteGenerationByPath = new Map<string, number>();
71
76
  const EXIT_REASONS = new Set<BrokerExitReason>([
72
77
  "ownership-fence-expired",
73
78
  "publication-liveness-timeout",
79
+ "heartbeat-renewal-blocked",
74
80
  "restart-committed",
75
81
  "shutdown-request",
76
82
  "startup-failure",
@@ -94,11 +100,20 @@ function isBrokerExitRecord(value: unknown): value is BrokerExitRecord {
94
100
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
95
101
  const record = value as Partial<BrokerExitRecord>;
96
102
  const keys = Object.keys(record).sort();
103
+ const requiredKeys = [
104
+ "fenceReason",
105
+ "fencedForMs",
106
+ "mode",
107
+ "pid",
108
+ "reason",
109
+ "signal",
110
+ "uptimeMs",
111
+ "version",
112
+ "writtenAt",
113
+ ];
97
114
  return (
98
- keys.join(",") ===
99
- ["fenceReason", "fencedForMs", "mode", "pid", "reason", "signal", "uptimeMs", "version", "writtenAt"]
100
- .sort()
101
- .join(",") &&
115
+ keys.every(key => requiredKeys.includes(key) || key === "blockingLockPath") &&
116
+ requiredKeys.every(key => keys.includes(key)) &&
102
117
  record.version === 1 &&
103
118
  (record.mode === "owned-root" || record.mode === "lost-root") &&
104
119
  typeof record.reason === "string" &&
@@ -115,6 +130,14 @@ function isBrokerExitRecord(value: unknown): value is BrokerExitRecord {
115
130
  (record.pid as number) > 0 &&
116
131
  (record.signal === null || record.signal === "SIGINT" || record.signal === "SIGTERM") &&
117
132
  (record.reason === "signal") === (record.signal !== null) &&
133
+ (record.reason === "heartbeat-renewal-blocked"
134
+ ? record.mode === "owned-root" &&
135
+ record.fenceReason === null &&
136
+ record.fencedForMs === 0 &&
137
+ record.signal === null &&
138
+ typeof record.blockingLockPath === "string" &&
139
+ record.blockingLockPath.length > 0
140
+ : record.blockingLockPath === undefined) &&
118
141
  Number.isSafeInteger(record.writtenAt) &&
119
142
  (record.writtenAt as number) > 0
120
143
  );
@@ -125,26 +148,29 @@ function isBrokerStartupExitRecord(value: unknown): value is BrokerStartupExitRe
125
148
  const record = value as Partial<BrokerStartupExitRecord>;
126
149
  const signalExitCode = record.signal === "SIGINT" ? 130 : record.signal === "SIGTERM" ? 143 : null;
127
150
  const keys = Object.keys(record).sort();
151
+ const requiredKeys = [
152
+ "exitCode",
153
+ "fenceReason",
154
+ "fencedForMs",
155
+ "mode",
156
+ "pid",
157
+ "reason",
158
+ "signal",
159
+ "timeoutMs",
160
+ "uptimeMs",
161
+ "version",
162
+ "writtenAt",
163
+ ];
164
+ const isValidShape =
165
+ keys.every(k => requiredKeys.includes(k) || k === "blockingLockPath") &&
166
+ requiredKeys.every(k => keys.includes(k));
128
167
  return (
129
- keys.join(",") ===
130
- [
131
- "exitCode",
132
- "fenceReason",
133
- "fencedForMs",
134
- "mode",
135
- "pid",
136
- "reason",
137
- "signal",
138
- "timeoutMs",
139
- "uptimeMs",
140
- "version",
141
- "writtenAt",
142
- ]
143
- .sort()
144
- .join(",") &&
168
+ isValidShape &&
145
169
  record.version === 1 &&
146
170
  record.mode === "startup" &&
147
- (record.reason === "startup-deadline" || record.reason === "startup-signal") &&
171
+ (record.reason === "startup-deadline" ||
172
+ record.reason === "startup-signal" ||
173
+ record.reason === "startup-lock-blocked") &&
148
174
  record.fenceReason === null &&
149
175
  record.fencedForMs === 0 &&
150
176
  Number.isSafeInteger(record.uptimeMs) &&
@@ -154,7 +180,10 @@ function isBrokerStartupExitRecord(value: unknown): value is BrokerStartupExitRe
154
180
  (record.signal === null || record.signal === "SIGINT" || record.signal === "SIGTERM") &&
155
181
  (record.exitCode === 1 || record.exitCode === 130 || record.exitCode === 143) &&
156
182
  (record.timeoutMs === null || (Number.isSafeInteger(record.timeoutMs) && (record.timeoutMs as number) > 0)) &&
157
- (record.reason === "startup-deadline"
183
+ (record.reason === "startup-lock-blocked"
184
+ ? typeof record.blockingLockPath === "string" && record.blockingLockPath.length > 0
185
+ : record.blockingLockPath === undefined) &&
186
+ (record.reason === "startup-deadline" || record.reason === "startup-lock-blocked"
158
187
  ? record.exitCode === 1 && record.signal === null && record.timeoutMs !== null
159
188
  : record.exitCode === signalExitCode && signalExitCode !== null && record.timeoutMs === null) &&
160
189
  Number.isSafeInteger(record.writtenAt) &&
@@ -6,9 +6,14 @@ import path from "node:path";
6
6
  import type { NativeBrokerRestartIntent, NativeDirectoryTreeSnapshot } from "@gajae-code/natives";
7
7
  import { logger, resolveEquivalentPath } from "@gajae-code/utils";
8
8
  import packageJson from "../../../package.json" with { type: "json" };
9
+ import { FileLockAcquireError } from "../../config/file-lock";
9
10
  import type { ModelProfileErrorDetails } from "../../config/model-profile-contract";
10
11
  import { planLaunchWorktree } from "../../gjc-runtime/launch-worktree";
11
12
  import { readExistingStateForMutation, withWorkflowStateLock } from "../../gjc-runtime/state-writer";
13
+ import type {
14
+ TaskArtifactOwnerDeletionEvidence,
15
+ TaskArtifactOwnerRetirementContinuation,
16
+ } from "../../session/task-artifact-owner-codec";
12
17
  import { SdkClient, SdkClientError } from "../client";
13
18
  import {
14
19
  BROKER_RUNTIME_ABORT_CAPABILITY_FIELD,
@@ -127,6 +132,7 @@ import {
127
132
  type SpawnSubstrateProvider,
128
133
  } from "./spawn-authority";
129
134
  import { createSpawnSubstrateProvider } from "./spawn-substrate";
135
+ import type { BrokerTaskArtifactOwnerRetirementDisposition } from "./task-artifact-owner-validation";
130
136
  import { BrokerTransport } from "./transport";
131
137
 
132
138
  export interface BrokerSettings {
@@ -158,6 +164,8 @@ export interface BrokerSettings {
158
164
  restartRequestId?: string;
159
165
  /** Cancel bootstrap before retained publication when the owning CLI receives a signal. */
160
166
  startupAbortSignal?: AbortSignal;
167
+ /** Monotonic checkpoint deadline, leaving time for retained publication. */
168
+ startupCheckpointDeadline?: number;
161
169
  /** Called synchronously when retained publication establishes broker readiness. */
162
170
  onStartupReady?: () => void;
163
171
  /** Test-only delay after session checkpoint to verify unpublished discovery ownership. */
@@ -293,6 +301,19 @@ export type BrokerCleanupEvidence = {
293
301
  retainedTranscriptSuccessorPath?: string;
294
302
  retainedTranscriptPlaceholderPath?: string;
295
303
  retainedTranscriptUnknownPath?: string;
304
+ /** Immutable task-artifact owner authority captured before the first deletion effect. */
305
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
306
+ /** Latest exact native remnant; it never replaces the original deletion evidence. */
307
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
308
+ /** Strict owner-only wire disposition, decoded only with its separately stored evidence. */
309
+ taskArtifactOwnerRetirementOutcome?: BrokerTaskArtifactOwnerRetirementDisposition;
310
+ taskArtifactOwnerPayloadRetired?: true;
311
+ taskArtifactOwnerNamespaceRetained?: true;
312
+ taskArtifactOwnerRetired?: true;
313
+ /** Durable fact that transcript retirement completed while the owner namespace remains. */
314
+ taskArtifactOwnerTranscriptDeleted?: true;
315
+ /** Stable artifacts-phase owner refusal diagnostic. */
316
+ taskArtifactOwnerCleanupError?: string;
296
317
  /** Durable proof that artifact cleanup completed before transcript mutation. */
297
318
  artifactsRemoved?: boolean;
298
319
  artifactsAbsentAtAuthorization?: true;
@@ -756,6 +777,17 @@ function normalizeAliasedString(
756
777
  return { value: values[0] };
757
778
  }
758
779
 
780
+ const BROKER_TASK_ARTIFACT_OWNER_CLEANUP_FIELDS = [
781
+ "taskArtifactOwnerDeletionEvidence",
782
+ "taskArtifactOwnerRetirementContinuation",
783
+ "taskArtifactOwnerRetirementOutcome",
784
+ "taskArtifactOwnerPayloadRetired",
785
+ "taskArtifactOwnerNamespaceRetained",
786
+ "taskArtifactOwnerRetired",
787
+ "taskArtifactOwnerTranscriptDeleted",
788
+ "taskArtifactOwnerCleanupError",
789
+ ] as const;
790
+
759
791
  export function normalizeBrokerInput(operation: string, input: Record<string, unknown>): InputNormalization {
760
792
  const normalized: Record<string, unknown> = { ...input };
761
793
  const session = normalizeAliasedString(input, "sessionId", ["id"]);
@@ -834,6 +866,18 @@ export function normalizeBrokerInput(operation: string, input: Record<string, un
834
866
  if (cwd.value !== undefined) normalized.stateRoot = path.join(cwd.value, ".gjc", "state");
835
867
  else if (stateRoot.value !== undefined) return error("invalid_input", "stateRoot requires cwd.");
836
868
 
869
+ if (operation === "session.delete") {
870
+ const hasOwnerField = (value: unknown): boolean =>
871
+ typeof value === "object" &&
872
+ value !== null &&
873
+ !Array.isArray(value) &&
874
+ BROKER_TASK_ARTIFACT_OWNER_CLEANUP_FIELDS.some(key => Object.hasOwn(value, key));
875
+ if (hasOwnerField(input) || hasOwnerField(target) || hasOwnerField(input.cleanup))
876
+ return error(
877
+ "invalid_input",
878
+ "Task-artifact owner cleanup state is broker-managed and cannot be supplied by clients.",
879
+ );
880
+ }
837
881
  if (target) {
838
882
  const normalizedTarget = { ...target };
839
883
  delete normalizedTarget.path;
@@ -1530,12 +1574,14 @@ export class Broker {
1530
1574
  #transport: BrokerTransport | null = null;
1531
1575
  #heartbeatTimer: NodeJS.Timeout | null = null;
1532
1576
  #startupAbortSignal: AbortSignal | undefined;
1577
+ #startupCheckpointDeadline: number | undefined;
1533
1578
  #onStartupReady: (() => void) | undefined;
1534
1579
  #startupPrePublicationDelayMs: number;
1535
1580
  #startupPrePublicationTestHook: (() => Promise<void>) | undefined;
1536
1581
  #startupAfterDiscoveryWriteTestHook: (() => Promise<void>) | undefined;
1537
1582
  #startupPostPublicationDelayMs: number;
1538
1583
  #completionTask: Promise<void> | null = null;
1584
+ #terminalExit: { mode: BrokerExitMode; reason: BrokerExitReason } | null = null;
1539
1585
  #completion!: Promise<void>;
1540
1586
  #resolveCompletion!: () => void;
1541
1587
  #rejectCompletion!: (error: unknown) => void;
@@ -1569,6 +1615,7 @@ export class Broker {
1569
1615
  this.#ownsResolveModelPin = settings.resolveModelPin === undefined;
1570
1616
  this.#resolveModelPin = settings.resolveModelPin ?? createDefaultSdkHostModelResolver(this.settings.agentDir);
1571
1617
  this.#startupAbortSignal = settings.startupAbortSignal;
1618
+ this.#startupCheckpointDeadline = settings.startupCheckpointDeadline;
1572
1619
  this.#onStartupReady = settings.onStartupReady;
1573
1620
  this.#startupPrePublicationDelayMs =
1574
1621
  Number.isSafeInteger(settings.startupPrePublicationDelayMs) &&
@@ -3898,7 +3945,7 @@ export class Broker {
3898
3945
  // checkpoint settles. The bootstrap watchdog owns this pre-publication
3899
3946
  // interval; publishing first allowed it to kill an endpoint already handed
3900
3947
  // to callers when a legitimate index-lock wait outlived the fence.
3901
- await this.#checkpointSessionHeartbeats();
3948
+ await this.#checkpointSessionHeartbeats(this.#startupAbortSignal, this.#startupCheckpointDeadline);
3902
3949
  this.#throwIfStartupAborted();
3903
3950
  await this.#startupPrePublicationTestHook?.();
3904
3951
  if (this.#startupPrePublicationDelayMs > 0) await Bun.sleep(this.#startupPrePublicationDelayMs);
@@ -3973,6 +4020,10 @@ export class Broker {
3973
4020
  get completion(): Promise<void> {
3974
4021
  return this.#completion;
3975
4022
  }
4023
+ /** Why this broker completed, or null while it is still running. */
4024
+ get terminalExit(): { readonly mode: BrokerExitMode; readonly reason: BrokerExitReason } | null {
4025
+ return this.#terminalExit;
4026
+ }
3976
4027
  #restartIdentity(): BrokerRestartOwnerIdentity | undefined {
3977
4028
  const d = this.discovery;
3978
4029
  if (!d?.incarnation) return undefined;
@@ -4231,15 +4282,29 @@ export class Broker {
4231
4282
  if (publication) await this.#writeHeartbeat(publication);
4232
4283
  }
4233
4284
  /** Re-observes provably live session hosts and checkpoints their liveness. */
4234
- async heartbeatSessions(now = Date.now()): Promise<number> {
4235
- return await this.index.checkpointLiveHeartbeats(now);
4285
+ async heartbeatSessions(now = Date.now(), abortSignal?: AbortSignal, deadlineAt?: number): Promise<number> {
4286
+ return await this.index.checkpointLiveHeartbeats(now, abortSignal, deadlineAt);
4236
4287
  }
4237
- async #checkpointSessionHeartbeats(): Promise<void> {
4288
+ async #checkpointSessionHeartbeats(abortSignal?: AbortSignal, startupCheckpointDeadline?: number): Promise<void> {
4238
4289
  if (this.#checkpointInFlight || this.#stopping) return;
4239
4290
  this.#checkpointInFlight = true;
4240
4291
  try {
4241
- await this.heartbeatSessions();
4292
+ // The CLI reserves publication headroom in this startup deadline.
4293
+ // Periodic passes use their own budget, not a retired startup signal.
4294
+ await this.heartbeatSessions(Date.now(), abortSignal, startupCheckpointDeadline);
4242
4295
  } catch (error) {
4296
+ if (
4297
+ error instanceof FileLockAcquireError &&
4298
+ error.orphanPath &&
4299
+ this.#publication !== null &&
4300
+ this.#publicationState === "healthy-owned" &&
4301
+ this.#fenceReason === null &&
4302
+ this.#completionTask === null &&
4303
+ this.#provenOwnedRoot()
4304
+ ) {
4305
+ await this.#complete("owned-root", "heartbeat-renewal-blocked", null, error.orphanPath);
4306
+ return;
4307
+ }
4243
4308
  logger.warn(`sdk broker: session heartbeat checkpoint failed: ${String(error)}`);
4244
4309
  } finally {
4245
4310
  this.#checkpointInFlight = false;
@@ -4249,8 +4314,10 @@ export class Broker {
4249
4314
  mode: BrokerExitMode,
4250
4315
  reason: BrokerExitReason,
4251
4316
  signal: BrokerExitRecord["signal"] = null,
4317
+ blockingLockPath?: string,
4252
4318
  ): Promise<void> {
4253
4319
  if (this.#completionTask) return this.#completionTask;
4320
+ this.#terminalExit = { mode, reason };
4254
4321
  const now = process.hrtime.bigint();
4255
4322
  const fenceStartedAt = this.#lossAt ?? this.#ambiguousAt;
4256
4323
  const exitRecord: BrokerExitRecord & Record<string, unknown> = {
@@ -4260,11 +4327,19 @@ export class Broker {
4260
4327
  fenceReason: this.#fenceReason,
4261
4328
  fencedForMs: fenceStartedAt === null ? 0 : Math.max(0, Number((now - fenceStartedAt) / 1_000_000n)),
4262
4329
  uptimeMs: this.#startedAt === null ? 0 : Math.max(0, Number((now - this.#startedAt) / 1_000_000n)),
4330
+ ...(blockingLockPath === undefined ? {} : { blockingLockPath }),
4263
4331
  pid: process.pid,
4264
4332
  signal,
4265
4333
  writtenAt: Date.now(),
4266
4334
  };
4267
- (mode === "lost-root" ? logger.warn : logger.info)("sdk broker: exiting", exitRecord);
4335
+ const message =
4336
+ reason === "heartbeat-renewal-blocked" && blockingLockPath
4337
+ ? `blocked by retained removal transition ${blockingLockPath}`
4338
+ : undefined;
4339
+ (mode === "lost-root" || message !== undefined ? logger.warn : logger.info)("sdk broker: exiting", {
4340
+ ...exitRecord,
4341
+ ...(message === undefined ? {} : { message }),
4342
+ });
4268
4343
  this.#stopping = true;
4269
4344
  this.#checkpointInFlight = false;
4270
4345
  this.#publicationState = "stopping";
@@ -9,6 +9,7 @@ import type {
9
9
  } from "./broker";
10
10
  import { launchAuthorizedBrokerSuccessor, oldOwnerConfirmedExited } from "./daemon-entry";
11
11
  import { type BrokerDiscovery, readBrokerDiscovery } from "./discovery";
12
+ import { isBrokerAttachOnly } from "./ensure";
12
13
 
13
14
  export interface DoctorBrokerRestartOptions {
14
15
  agentDir: string;
@@ -33,6 +34,8 @@ export type DoctorBrokerRestartOutcome =
33
34
  adopted: boolean;
34
35
  }
35
36
  | { kind: "owner_unavailable"; reason: "no_discovery" | "owner_not_confirmed_live" }
37
+ /** Attach-only mode: the broker belongs to an external supervisor, so restart through it. */
38
+ | { kind: "attach_only_refused"; reason: "supervisor_owned" }
36
39
  | { kind: "prepare_refused"; code: string; message: string }
37
40
  | { kind: "commit_refused"; code: string; message: string }
38
41
  | { kind: "old_owner_exit_timeout"; requestId: string; oldOwner: BrokerRestartOwnerIdentity }
@@ -163,6 +166,7 @@ const OLD_OWNER_EXIT_POLL_MS = 50;
163
166
  * proven it owns the root -- never here, and never before that proof exists.
164
167
  */
165
168
  export async function restartBrokerForDoctor(options: DoctorBrokerRestartOptions): Promise<DoctorBrokerRestartOutcome> {
169
+ if (isBrokerAttachOnly()) return { kind: "attach_only_refused", reason: "supervisor_owned" };
166
170
  const old = await readBrokerDiscovery(options.agentDir);
167
171
  if (!old) return { kind: "owner_unavailable", reason: "no_discovery" };
168
172
  const oldOwner = identity(old);
@@ -6,7 +6,7 @@ import path from "node:path";
6
6
  import packageJson from "../../../package.json" with { type: "json" };
7
7
  import { acquireFileLock, type FileLockOptions, withFileLock } from "../../config/file-lock";
8
8
  import { loadInstallationHostId, loadLegacyInstallationHostId } from "../../config/machine-identity";
9
- import { SdkClient } from "../client/client";
9
+ import { SdkClient, SdkClientError } from "../client/client";
10
10
  import { type BrokerStartupExitRecord, clearBrokerStartupExitRecord, readBrokerStartupExitRecord } from "./broker-exit";
11
11
  import {
12
12
  type BrokerDiscovery,
@@ -41,6 +41,8 @@ function brokerStartupFailureReason(marker: BrokerStartupFailureMarker | undefin
41
41
 
42
42
  function brokerStartupExitReason(record: BrokerStartupExitRecord | undefined): string | undefined {
43
43
  if (!record) return undefined;
44
+ if (record.reason === "startup-lock-blocked")
45
+ return `SDK broker startup blocked by retained removal transition ${record.blockingLockPath}.`;
44
46
  if (record.reason === "startup-deadline")
45
47
  return `SDK broker startup exceeded its ${record.timeoutMs}ms fence deadline.`;
46
48
  return `SDK broker startup interrupted by ${record.signal} before readiness.`;
@@ -1001,9 +1003,36 @@ function startEnsure(settings: EnsureBrokerSettings, initiator: EnsureInitiator)
1001
1003
  return entry;
1002
1004
  }
1003
1005
 
1004
- /** Starts the detached broker entrypoint when discovery has no live owner. */
1006
+ /**
1007
+ * Attach-only client mode. When `GJC_SDK_BROKER_AUTOSTART=0`, `ensureBroker`
1008
+ * never spawns, retires or restarts a broker: it returns a live, reusable
1009
+ * discovery or fails with `broker_unavailable`. Hosts that supervise their own
1010
+ * broker (for example a system service) set it for every client they run. The
1011
+ * broker entrypoints never call `ensureBroker`, so they ignore the variable and
1012
+ * their descendants may inherit it.
1013
+ */
1014
+ export const SDK_BROKER_AUTOSTART_ENV = "GJC_SDK_BROKER_AUTOSTART";
1015
+
1016
+ export function isBrokerAttachOnly(env: NodeJS.ProcessEnv = process.env): boolean {
1017
+ return env[SDK_BROKER_AUTOSTART_ENV] === "0";
1018
+ }
1019
+
1020
+ async function attachExistingBroker(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
1021
+ const discovery = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
1022
+ if (discovery && (await isBrokerReusable(discovery))) return discovery;
1023
+ throw new SdkClientError(
1024
+ "broker_unavailable",
1025
+ `SDK broker is not running and ${SDK_BROKER_AUTOSTART_ENV}=0 forbids starting one.`,
1026
+ );
1027
+ }
1028
+
1029
+ /**
1030
+ * Starts the detached broker entrypoint when discovery has no live owner. In
1031
+ * attach-only mode (`SDK_BROKER_AUTOSTART_ENV`) it only attaches.
1032
+ */
1005
1033
  export function ensureBroker(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
1006
1034
  const resolvedSettings = { ...settings, agentDir: path.resolve(settings.agentDir) };
1035
+ if (isBrokerAttachOnly()) return attachExistingBroker(resolvedSettings);
1007
1036
  const inFlight = ensureInFlight.get(resolvedSettings.agentDir) ?? startEnsure(resolvedSettings, "discovery");
1008
1037
  return inFlight.discovery;
1009
1038
  }