@gajae-code/coding-agent 0.18.11 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/capability/rule.d.ts +7 -2
  7. package/dist/types/cli/accounts-cli.d.ts +2 -2
  8. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  9. package/dist/types/cli/args.d.ts +7 -2
  10. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  11. package/dist/types/cli/customize-doctor.d.ts +4 -3
  12. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  13. package/dist/types/cli/install-activation.d.ts +10 -4
  14. package/dist/types/cli/public-command-errors.d.ts +2 -0
  15. package/dist/types/cli/root-flags.d.ts +0 -12
  16. package/dist/types/cli/update-cli.d.ts +21 -3
  17. package/dist/types/cli-main.d.ts +0 -6
  18. package/dist/types/cli-ordinary.d.ts +2 -2
  19. package/dist/types/commands/accounts.d.ts +2 -1
  20. package/dist/types/commands/launch.d.ts +0 -6
  21. package/dist/types/commands/migrate.d.ts +1 -1
  22. package/dist/types/commands/sdk.d.ts +13 -0
  23. package/dist/types/commit/changelog/index.d.ts +1 -0
  24. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  25. package/dist/types/config/autorouting-tier-map.d.ts +53 -1
  26. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  27. package/dist/types/config/config-hot-reload.d.ts +44 -0
  28. package/dist/types/config/file-lock.d.ts +52 -3
  29. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  30. package/dist/types/config/model-profile-activation.d.ts +79 -9
  31. package/dist/types/config/model-registry.d.ts +72 -8
  32. package/dist/types/config/model-resolver.d.ts +7 -4
  33. package/dist/types/config/models-config-schema.d.ts +10 -0
  34. package/dist/types/config/settings-schema.d.ts +57 -4
  35. package/dist/types/config/settings.d.ts +33 -1
  36. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  37. package/dist/types/customization/import.d.ts +2 -0
  38. package/dist/types/customization/types.d.ts +1 -1
  39. package/dist/types/dap/config.d.ts +8 -4
  40. package/dist/types/dap/session.d.ts +11 -2
  41. package/dist/types/debug/system-info.d.ts +6 -1
  42. package/dist/types/discovery/helpers.d.ts +7 -2
  43. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  44. package/dist/types/edit/index.d.ts +5 -0
  45. package/dist/types/edit/modes/patch.d.ts +14 -0
  46. package/dist/types/edit/modes/replace.d.ts +3 -0
  47. package/dist/types/eval/py/runtime.d.ts +9 -0
  48. package/dist/types/exec/bash-executor.d.ts +12 -0
  49. package/dist/types/export/ttsr.d.ts +19 -0
  50. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  51. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  52. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  53. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  54. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  55. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  56. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  57. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  58. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  59. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  60. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  61. package/dist/types/hindsight/content.d.ts +5 -6
  62. package/dist/types/internal-urls/types.d.ts +2 -0
  63. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  64. package/dist/types/main.d.ts +23 -18
  65. package/dist/types/migrate/action-planner.d.ts +10 -1
  66. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  67. package/dist/types/migrate/adapters/index.d.ts +42 -1
  68. package/dist/types/migrate/executor.d.ts +3 -1
  69. package/dist/types/migrate/hook-module.d.ts +23 -0
  70. package/dist/types/migrate/report.d.ts +2 -1
  71. package/dist/types/migrate/types.d.ts +82 -3
  72. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  73. package/dist/types/modes/components/model-selector.d.ts +43 -0
  74. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  75. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  76. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  77. package/dist/types/modes/theme/theme.d.ts +1 -0
  78. package/dist/types/modes/utils/host-status.d.ts +15 -11
  79. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  80. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  81. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  82. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  83. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  84. package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
  85. package/dist/types/sdk/broker/broker.d.ts +29 -2
  86. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  87. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  88. package/dist/types/sdk/broker/ensure.d.ts +141 -9
  89. package/dist/types/sdk/broker/hop.d.ts +45 -0
  90. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  91. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  92. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  93. package/dist/types/sdk/broker/session-index.d.ts +6 -1
  94. package/dist/types/sdk/broker/transport.d.ts +2 -0
  95. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  96. package/dist/types/sdk/bus/index.d.ts +44 -0
  97. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  98. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
  99. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  100. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  101. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  102. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  103. package/dist/types/sdk/host/events.d.ts +4 -0
  104. package/dist/types/sdk/host/host.d.ts +2 -1
  105. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +40 -8
  107. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  109. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  111. package/dist/types/sdk/prompt-status.d.ts +11 -4
  112. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  113. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  114. package/dist/types/sdk/session-list.d.ts +1 -1
  115. package/dist/types/session/agent-session.d.ts +107 -9
  116. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  117. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  118. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  119. package/dist/types/session/messages.d.ts +5 -1
  120. package/dist/types/session/session-manager.d.ts +29 -3
  121. package/dist/types/session/startup-auth-config.d.ts +24 -2
  122. package/dist/types/session/streaming-output.d.ts +11 -0
  123. package/dist/types/session/terminal-abort.d.ts +3 -1
  124. package/dist/types/session-import/service.d.ts +1 -1
  125. package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
  126. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  127. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  128. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  129. package/dist/types/system-prompt.d.ts +3 -2
  130. package/dist/types/task/executor.d.ts +19 -2
  131. package/dist/types/task/role-body.d.ts +11 -0
  132. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  133. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  134. package/dist/types/tools/awaiting-input.d.ts +28 -0
  135. package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
  136. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  137. package/dist/types/tools/bash.d.ts +7 -0
  138. package/dist/types/tools/descriptors.d.ts +15 -2
  139. package/dist/types/tools/fetch.d.ts +7 -1
  140. package/dist/types/tools/index.d.ts +2 -0
  141. package/dist/types/tools/irc.d.ts +5 -3
  142. package/dist/types/tools/python.d.ts +1 -1
  143. package/dist/types/tools/report-issue.d.ts +41 -0
  144. package/dist/types/tools/subagent.d.ts +23 -0
  145. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  146. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  147. package/dist/types/tools/write.d.ts +2 -0
  148. package/dist/types/utils/file-mentions.d.ts +5 -0
  149. package/dist/types/utils/image-limits.d.ts +7 -0
  150. package/dist/types/utils/image-loading.d.ts +0 -1
  151. package/dist/types/utils/open.d.ts +0 -1
  152. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  153. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  154. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  155. package/dist/types/utils/title-generator.d.ts +13 -3
  156. package/dist/types/web/scrapers/types.d.ts +8 -1
  157. package/dist/types/web/search/providers/codex.d.ts +6 -7
  158. package/dist/types/web/search/providers/insane.d.ts +7 -1
  159. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  160. package/package.json +10 -9
  161. package/scripts/acp-conformance-agent.ts +12 -2
  162. package/scripts/build-binary.ts +2 -1
  163. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  164. package/scripts/generate-docs-index.ts +8 -1
  165. package/scripts/generate-sdk-operation-inventory.ts +38 -4
  166. package/src/async/job-manager.ts +356 -56
  167. package/src/async/result-formatting.ts +40 -0
  168. package/src/autoresearch/git.ts +9 -2
  169. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  170. package/src/capability/fs.ts +58 -22
  171. package/src/capability/index.ts +2 -2
  172. package/src/capability/rule.ts +11 -2
  173. package/src/cli/accounts-cli.ts +176 -8
  174. package/src/cli/append-system-prompt.ts +12 -0
  175. package/src/cli/args.ts +11 -7
  176. package/src/cli/auth-broker-cli.ts +21 -11
  177. package/src/cli/customize-doctor-precedence.ts +31 -0
  178. package/src/cli/customize-doctor.ts +49 -39
  179. package/src/cli/doctor/install-repairs.ts +35 -5
  180. package/src/cli/fast-help.ts +1 -3
  181. package/src/cli/install-activation.ts +47 -4
  182. package/src/cli/migrate-cli.ts +19 -6
  183. package/src/cli/public-command-errors.ts +6 -0
  184. package/src/cli/root-flags.ts +2 -4
  185. package/src/cli/stats-cli.ts +9 -5
  186. package/src/cli/update-cli.ts +471 -13
  187. package/src/cli-ordinary.ts +7 -2
  188. package/src/commands/accounts.ts +10 -5
  189. package/src/commands/harness.ts +25 -15
  190. package/src/commands/migrate.ts +3 -3
  191. package/src/commands/sdk.ts +277 -50
  192. package/src/commands/ultragoal.ts +16 -8
  193. package/src/commit/agentic/agent.ts +6 -6
  194. package/src/commit/analysis/conventional.ts +14 -5
  195. package/src/commit/changelog/index.ts +39 -1
  196. package/src/commit/map-reduce/index.ts +7 -7
  197. package/src/commit/message.ts +10 -3
  198. package/src/commit/pipeline.ts +12 -12
  199. package/src/config/autorouting-tier-map.ts +50 -1
  200. package/src/config/config-file.ts +106 -3
  201. package/src/config/config-hot-reload-trust.ts +148 -0
  202. package/src/config/config-hot-reload.ts +742 -0
  203. package/src/config/file-lock.ts +1615 -328
  204. package/src/config/keybindings.ts +154 -7
  205. package/src/config/model-bindings-applier.ts +85 -47
  206. package/src/config/model-discovery-manager.ts +10 -0
  207. package/src/config/model-profile-activation.ts +889 -148
  208. package/src/config/model-registry.ts +1706 -205
  209. package/src/config/model-resolver.ts +52 -22
  210. package/src/config/models-config-schema.ts +6 -0
  211. package/src/config/settings-schema.ts +73 -4
  212. package/src/config/settings.ts +460 -24
  213. package/src/coordinator-mcp/policy.ts +26 -1
  214. package/src/coordinator-mcp/server.ts +37 -2
  215. package/src/crash/sanitize.ts +7 -1
  216. package/src/customization/import.ts +279 -8
  217. package/src/customization/inventory.ts +24 -8
  218. package/src/customization/types.ts +1 -0
  219. package/src/dap/client.ts +3 -2
  220. package/src/dap/config.ts +31 -11
  221. package/src/dap/defaults.json +1 -1
  222. package/src/dap/session.ts +161 -36
  223. package/src/debug/crash-diagnostics.ts +89 -6
  224. package/src/debug/report-bundle.ts +90 -2
  225. package/src/debug/system-info.ts +8 -3
  226. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  227. package/src/discovery/agents-md.ts +1 -1
  228. package/src/discovery/builtin.ts +4 -3
  229. package/src/discovery/helpers.ts +25 -16
  230. package/src/discovery/mcp-compat.ts +120 -17
  231. package/src/edit/apply-patch/index.ts +12 -1
  232. package/src/edit/index.ts +204 -22
  233. package/src/edit/modes/patch.ts +214 -8
  234. package/src/edit/modes/replace.ts +136 -0
  235. package/src/eval/js/context-manager.ts +29 -3
  236. package/src/eval/js/worker-entry.ts +51 -18
  237. package/src/eval/py/executor.ts +14 -4
  238. package/src/eval/py/runner.py +144 -5
  239. package/src/eval/py/runtime.ts +44 -4
  240. package/src/exec/bash-executor.ts +81 -4
  241. package/src/exec/bash-shell-guardian.ts +4 -1
  242. package/src/exec/bash-shell-supervisor.ts +8 -4
  243. package/src/exec/isolated-shell.ts +5 -1
  244. package/src/exec/non-interactive-env.ts +0 -1
  245. package/src/export/html/index.ts +86 -6
  246. package/src/export/ttsr.ts +176 -1
  247. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  248. package/src/extensibility/extensions/loader.ts +10 -1
  249. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  250. package/src/extensibility/extensions/types.ts +5 -1
  251. package/src/extensibility/hooks/loader.ts +10 -2
  252. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  253. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  254. package/src/extensibility/slash-commands.ts +5 -0
  255. package/src/gjc-runtime/gc-runtime.ts +37 -7
  256. package/src/gjc-runtime/launch-tmux.ts +30 -15
  257. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  258. package/src/gjc-runtime/launch-worktree.ts +101 -14
  259. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  260. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  261. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  262. package/src/gjc-runtime/session-resolution.ts +38 -6
  263. package/src/gjc-runtime/session-state-lock.ts +59 -14
  264. package/src/gjc-runtime/state-runtime.ts +23 -19
  265. package/src/gjc-runtime/state-writer.ts +109 -5
  266. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  267. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  268. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  269. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  270. package/src/harness-control-plane/finalize.ts +11 -0
  271. package/src/harness-control-plane/receipt-spool.ts +105 -28
  272. package/src/harness-control-plane/storage.ts +124 -6
  273. package/src/hindsight/content.ts +116 -16
  274. package/src/hindsight/mental-models.ts +22 -10
  275. package/src/hindsight/state.ts +7 -4
  276. package/src/hooks/native-skill-hook.ts +17 -12
  277. package/src/internal-urls/artifact-protocol.ts +3 -1
  278. package/src/internal-urls/docs-index.generated.ts +23 -22
  279. package/src/internal-urls/local-protocol.ts +167 -8
  280. package/src/internal-urls/types.ts +2 -0
  281. package/src/lsp/client.ts +2 -1
  282. package/src/lsp/clients/biome-client.ts +2 -0
  283. package/src/lsp/clients/swiftlint-client.ts +2 -0
  284. package/src/lsp/index.ts +29 -0
  285. package/src/lsp/workspace-path.ts +23 -0
  286. package/src/main.ts +330 -92
  287. package/src/memories/index.ts +25 -2
  288. package/src/migrate/action-planner.ts +574 -2
  289. package/src/migrate/adapters/claude-code.ts +162 -5
  290. package/src/migrate/adapters/codex.ts +77 -7
  291. package/src/migrate/adapters/index.ts +258 -5
  292. package/src/migrate/adapters/opencode.ts +10 -1
  293. package/src/migrate/executor.ts +222 -7
  294. package/src/migrate/hook-module.ts +193 -0
  295. package/src/migrate/report.ts +63 -2
  296. package/src/migrate/types.ts +84 -4
  297. package/src/modes/DESIGN.md +51 -0
  298. package/src/modes/acp/acp-agent.ts +10 -1
  299. package/src/modes/acp/permission-mode.ts +18 -1
  300. package/src/modes/components/agent-dashboard.ts +20 -4
  301. package/src/modes/components/assistant-message.ts +55 -1
  302. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  303. package/src/modes/components/customization/import-wizard.ts +29 -3
  304. package/src/modes/components/model-selector.ts +157 -103
  305. package/src/modes/components/settings-selector.ts +103 -37
  306. package/src/modes/components/welcome.ts +3 -1
  307. package/src/modes/controllers/command-controller.ts +300 -9
  308. package/src/modes/controllers/event-controller.ts +1 -1
  309. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  310. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  311. package/src/modes/controllers/input-controller.ts +2 -0
  312. package/src/modes/controllers/plan-mode-controller.ts +66 -11
  313. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  314. package/src/modes/controllers/selector-controller.ts +76 -7
  315. package/src/modes/interactive-mode.ts +123 -4
  316. package/src/modes/prompt-suggestion-controller.ts +1 -1
  317. package/src/modes/runtime-init.ts +5 -1
  318. package/src/modes/theme/theme.ts +15 -0
  319. package/src/modes/utils/host-status.ts +21 -10
  320. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  321. package/src/prompts/system/irc-incoming.md +3 -1
  322. package/src/prompts/tools/debug.md +1 -1
  323. package/src/prompts/tools/irc.md +1 -1
  324. package/src/prompts/tools/read.md +1 -1
  325. package/src/prompts/tools/report-issue.md +13 -0
  326. package/src/prompts/tools/task.md +1 -1
  327. package/src/runtime-credential-selector.ts +2 -2
  328. package/src/runtime-mcp/content-limits.ts +7 -0
  329. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  330. package/src/runtime-mcp/manager.ts +40 -3
  331. package/src/runtime-mcp/oauth-flow.ts +32 -6
  332. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  333. package/src/runtime-mcp/project-host-values.ts +12 -8
  334. package/src/runtime-mcp/render.ts +16 -1
  335. package/src/runtime-mcp/tool-bridge.ts +194 -21
  336. package/src/runtime-mcp/transports/stdio.ts +2 -1
  337. package/src/sdk/broker/broker-exit.ts +10 -5
  338. package/src/sdk/broker/broker.ts +484 -85
  339. package/src/sdk/broker/daemon-entry.ts +338 -116
  340. package/src/sdk/broker/doctor-restart.ts +2 -1
  341. package/src/sdk/broker/ensure.ts +1214 -268
  342. package/src/sdk/broker/hop.ts +286 -0
  343. package/src/sdk/broker/lifecycle-ledger.ts +38 -11
  344. package/src/sdk/broker/lifecycle.ts +102 -6
  345. package/src/sdk/broker/lock-host-identity.ts +29 -0
  346. package/src/sdk/broker/session-index.ts +91 -16
  347. package/src/sdk/broker/transport.ts +17 -1
  348. package/src/sdk/bus/chat-command-policy.ts +16 -3
  349. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  350. package/src/sdk/bus/index.ts +460 -35
  351. package/src/sdk/bus/reconciliation-store.ts +201 -8
  352. package/src/sdk/bus/session-control.ts +2 -0
  353. package/src/sdk/bus/slack-daemon.ts +105 -9
  354. package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
  355. package/src/sdk/bus/telegram-daemon.ts +10 -2
  356. package/src/sdk/bus/threaded-render.ts +20 -1
  357. package/src/sdk/cli/session-cli.ts +23 -6
  358. package/src/sdk/host/control/dispatch.ts +13 -1
  359. package/src/sdk/host/control/operations.ts +10 -1
  360. package/src/sdk/host/events.ts +67 -4
  361. package/src/sdk/host/host.ts +8 -2
  362. package/src/sdk/host/prompt-image-upload.ts +438 -0
  363. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  364. package/src/sdk/host/session-runtime.test.ts +1737 -129
  365. package/src/sdk/host/session-runtime.ts +684 -83
  366. package/src/sdk/host/surface-policy.ts +4 -0
  367. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  368. package/src/sdk/host/websocket-transport.ts +19 -7
  369. package/src/sdk/lifecycle/service.ts +15 -1
  370. package/src/sdk/prompt-deadline-flush.ts +78 -5
  371. package/src/sdk/prompt-deadline-manager.ts +68 -14
  372. package/src/sdk/prompt-status.ts +6 -4
  373. package/src/sdk/protocol/adapter-validation.ts +81 -12
  374. package/src/sdk/protocol/operation-inventory.generated.json +261 -2
  375. package/src/sdk/protocol/operation-registry.ts +33 -3
  376. package/src/sdk/reconciliation-extensions.ts +1 -0
  377. package/src/sdk/session-list.ts +1 -0
  378. package/src/sdk/session.ts +237 -97
  379. package/src/session/agent-session.ts +2726 -479
  380. package/src/session/history-storage.ts +182 -12
  381. package/src/session/internal/managed-session-scope.ts +173 -57
  382. package/src/session/internal/managed-session-storage.ts +85 -12
  383. package/src/session/internal/native-publish-outcome.ts +5 -1
  384. package/src/session/messages.ts +47 -6
  385. package/src/session/session-manager.ts +326 -19
  386. package/src/session/startup-auth-config.ts +116 -8
  387. package/src/session/streaming-output.ts +262 -4
  388. package/src/session/terminal-abort.ts +14 -2
  389. package/src/session-import/redact.ts +53 -2
  390. package/src/session-import/service.ts +1 -0
  391. package/src/skill-state/workflow-mutation-guard.ts +2145 -206
  392. package/src/slash-commands/builtin-registry.ts +21 -6
  393. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  394. package/src/slash-commands/helpers/mcp.ts +11 -3
  395. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  396. package/src/ssh/ssh-executor.ts +9 -2
  397. package/src/stt/recorder.ts +26 -9
  398. package/src/system-prompt.ts +61 -16
  399. package/src/task/executor.ts +115 -20
  400. package/src/task/index.ts +389 -175
  401. package/src/task/role-body.ts +84 -0
  402. package/src/task/token-log-mode.test.ts +162 -0
  403. package/src/task/token-log.ts +91 -2
  404. package/src/tools/archive-reader.ts +650 -37
  405. package/src/tools/ask.ts +1 -0
  406. package/src/tools/ast-edit-write-path.ts +64 -0
  407. package/src/tools/ast-edit.ts +18 -1
  408. package/src/tools/ast-grep.ts +7 -2
  409. package/src/tools/atomic-file-write.ts +46 -15
  410. package/src/tools/auto-generated-guard.ts +15 -3
  411. package/src/tools/awaiting-input.ts +114 -0
  412. package/src/tools/bash-allowed-prefixes.ts +214 -6
  413. package/src/tools/bash-interactive.ts +14 -2
  414. package/src/tools/bash-skill-urls.ts +58 -5
  415. package/src/tools/bash.ts +113 -26
  416. package/src/tools/browser/tab-worker.ts +39 -13
  417. package/src/tools/browser.ts +17 -7
  418. package/src/tools/cron.ts +10 -1
  419. package/src/tools/debug.ts +16 -8
  420. package/src/tools/descriptors.test.ts +44 -0
  421. package/src/tools/descriptors.ts +50 -6
  422. package/src/tools/eval.ts +15 -0
  423. package/src/tools/fetch.ts +93 -4
  424. package/src/tools/gh.ts +27 -12
  425. package/src/tools/image-gen.ts +20 -6
  426. package/src/tools/index.ts +2 -0
  427. package/src/tools/irc.ts +5 -3
  428. package/src/tools/monitor.ts +9 -1
  429. package/src/tools/output-meta.ts +74 -5
  430. package/src/tools/python.ts +1 -1
  431. package/src/tools/read.ts +26 -37
  432. package/src/tools/recipe/runner.ts +23 -12
  433. package/src/tools/render-utils.ts +2 -2
  434. package/src/tools/report-issue.ts +184 -0
  435. package/src/tools/search.ts +3 -2
  436. package/src/tools/ssh.ts +274 -8
  437. package/src/tools/subagent-render.ts +114 -12
  438. package/src/tools/subagent.ts +118 -5
  439. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  440. package/src/tools/todo-write.ts +41 -3
  441. package/src/tools/tool-catalog.generated.ts +32 -4
  442. package/src/tools/ultragoal-ask-guard.ts +13 -1
  443. package/src/tools/write.ts +42 -3
  444. package/src/tui/tree-list.ts +15 -1
  445. package/src/utils/commit-message-generator.ts +12 -4
  446. package/src/utils/external-editor.ts +21 -7
  447. package/src/utils/file-mentions.ts +75 -7
  448. package/src/utils/git.ts +38 -7
  449. package/src/utils/image-limits.ts +8 -0
  450. package/src/utils/image-loading.ts +1 -1
  451. package/src/utils/open.ts +73 -4
  452. package/src/utils/pasted-image-loading.ts +9 -8
  453. package/src/utils/prompt-suggestion.ts +5 -4
  454. package/src/utils/shell-snapshot.ts +9 -1
  455. package/src/utils/title-generator.ts +72 -22
  456. package/src/web/scrapers/index.ts +3 -1
  457. package/src/web/scrapers/types.ts +8 -0
  458. package/src/web/scrapers/youtube.ts +3 -2
  459. package/src/web/search/index.ts +2 -1
  460. package/src/web/search/providers/codex.ts +29 -32
  461. package/src/web/search/providers/insane.ts +91 -28
  462. package/src/web/search/providers/searxng.ts +62 -10
@@ -47,7 +47,11 @@ import {
47
47
  } from "../harness-control-plane/lifecycle-reconciliation";
48
48
  import { type ResolvedOwner, RuntimeOwner, resolveOwner, resolveOwnerLive } from "../harness-control-plane/owner";
49
49
  import { preserveDirtyWorktree } from "../harness-control-plane/preserve";
50
- import { RECEIPT_SPOOL_DIR_ENV } from "../harness-control-plane/receipt-spool";
50
+ import {
51
+ RECEIPT_SPOOL_DIR_ENV,
52
+ resolveReceiptSpoolDir,
53
+ trustedReceiptSpoolDir,
54
+ } from "../harness-control-plane/receipt-spool";
51
55
  import { buildReceipt, requiresVanishBeforeAction, type VanishEvidence } from "../harness-control-plane/receipts";
52
56
  import { createSdkSessionTransport, spawnNormalHarnessSession } from "../harness-control-plane/sdk-transport";
53
57
  import { classifyLeaseStatus, readLease } from "../harness-control-plane/session-lease";
@@ -1004,8 +1008,9 @@ export default class Harness extends Command {
1004
1008
  `${GJC_TMUX_OWNER_STATE_DIR_ENV}=${shellQuote(ownerStateDir)}`,
1005
1009
  `${GJC_TMUX_OWNER_SERVER_KEY_ENV}=${shellQuote(socketKey)}`,
1006
1010
  ];
1007
- if (process.env[RECEIPT_SPOOL_DIR_ENV])
1008
- envAssignments.push(`${RECEIPT_SPOOL_DIR_ENV}=${shellQuote(process.env[RECEIPT_SPOOL_DIR_ENV])}`);
1011
+ const trustedSpoolDir = resolveReceiptSpoolDir();
1012
+ if (trustedSpoolDir) envAssignments.push(`${RECEIPT_SPOOL_DIR_ENV}=${shellQuote(trustedSpoolDir)}`);
1013
+ else envAssignments.unshift(`-u ${RECEIPT_SPOOL_DIR_ENV}`);
1009
1014
  if (process.env.GJC_HARNESS_TEST_NODE_MODULES)
1010
1015
  envAssignments.push(`GJC_HARNESS_TEST_NODE_MODULES=${shellQuote(process.env.GJC_HARNESS_TEST_NODE_MODULES)}`);
1011
1016
  if (process.env.GJC_SDK_DISABLE)
@@ -1203,19 +1208,20 @@ export default class Harness extends Command {
1203
1208
  }
1204
1209
  const fallbackReason = tmux.reason;
1205
1210
  const cmd = this.#buildOwnerCommand(sessionId);
1211
+ const trustedSpoolDir = resolveReceiptSpoolDir();
1212
+ const childEnv: NodeJS.ProcessEnv = {
1213
+ ...process.env,
1214
+ GJC_HARNESS_STATE_ROOT: root,
1215
+ ...(process.env.GJC_HARNESS_TEST_NODE_MODULES
1216
+ ? { GJC_HARNESS_TEST_NODE_MODULES: process.env.GJC_HARNESS_TEST_NODE_MODULES }
1217
+ : {}),
1218
+ ...(process.env.GJC_SDK_DISABLE ? { GJC_SDK_DISABLE: process.env.GJC_SDK_DISABLE } : {}),
1219
+ };
1220
+ if (trustedSpoolDir) childEnv[RECEIPT_SPOOL_DIR_ENV] = trustedSpoolDir;
1221
+ else delete childEnv[RECEIPT_SPOOL_DIR_ENV];
1206
1222
  const child = Bun.spawn(cmd, {
1207
1223
  cwd,
1208
- env: {
1209
- ...process.env,
1210
- GJC_HARNESS_STATE_ROOT: root,
1211
- ...(process.env[RECEIPT_SPOOL_DIR_ENV]
1212
- ? { [RECEIPT_SPOOL_DIR_ENV]: process.env[RECEIPT_SPOOL_DIR_ENV] }
1213
- : {}),
1214
- ...(process.env.GJC_HARNESS_TEST_NODE_MODULES
1215
- ? { GJC_HARNESS_TEST_NODE_MODULES: process.env.GJC_HARNESS_TEST_NODE_MODULES }
1216
- : {}),
1217
- ...(process.env.GJC_SDK_DISABLE ? { GJC_SDK_DISABLE: process.env.GJC_SDK_DISABLE } : {}),
1218
- },
1224
+ env: childEnv,
1219
1225
  stdout: "ignore",
1220
1226
  stderr: "ignore",
1221
1227
  stdin: "ignore",
@@ -1374,7 +1380,11 @@ export default class Harness extends Command {
1374
1380
  if (!owner.live || !owner.socketPath) return false;
1375
1381
  const priorSpoolDir = input[RECEIPT_SPOOL_DIR_ENV];
1376
1382
  try {
1377
- if (process.env[RECEIPT_SPOOL_DIR_ENV]) input[RECEIPT_SPOOL_DIR_ENV] = process.env[RECEIPT_SPOOL_DIR_ENV];
1383
+ const fromEnv = resolveReceiptSpoolDir();
1384
+ const fromInput = trustedReceiptSpoolDir(typeof priorSpoolDir === "string" ? priorSpoolDir : undefined);
1385
+ const trustedSpoolDir = fromEnv ?? fromInput;
1386
+ if (trustedSpoolDir) input[RECEIPT_SPOOL_DIR_ENV] = trustedSpoolDir;
1387
+ else delete input[RECEIPT_SPOOL_DIR_ENV];
1378
1388
  const res = (await callEndpoint(owner.socketPath, { verb, input })) as { ok?: boolean };
1379
1389
  writeJson(res);
1380
1390
  if (res?.ok === false) process.exitCode = 1;
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Import MCP servers and skills from other coding agents into GJC.
2
+ * Import MCP servers, skills, slash commands, and instructions from other coding agents into GJC.
3
3
  */
4
4
  import { Command, Flags } from "@gajae-code/utils/cli";
5
5
  import { type MigrateCommandArgs, runMigrateCommand } from "../cli/migrate-cli";
6
6
 
7
7
  export default class Migrate extends Command {
8
- static description = "Import MCP servers and skills from Claude Code, Codex, or OpenCode";
8
+ static description = "Import MCP servers, skills, slash commands, and instructions from other coding agents";
9
9
 
10
10
  static examples = [
11
11
  "gjc migrate --from claude-code",
@@ -25,7 +25,7 @@ export default class Migrate extends Command {
25
25
  default: false,
26
26
  }),
27
27
  force: Flags.boolean({
28
- description: "Overwrite existing skills/MCP servers instead of skipping them",
28
+ description: "Overwrite existing skills, commands, MCP servers, or instruction files instead of skipping them",
29
29
  default: false,
30
30
  }),
31
31
  "dry-run": Flags.boolean({ description: "Preview the migration without writing anything", default: false }),
@@ -12,12 +12,15 @@ import {
12
12
  } from "@gajae-code/natives";
13
13
  import { getAgentDir, logger, postmortem } from "@gajae-code/utils";
14
14
  import { CliParseError, Command } from "@gajae-code/utils/cli";
15
+ import { startupProjectEnvSnapshot } from "@gajae-code/utils/env";
16
+ import { type ProjectEnvSnapshot, projectEnvSnapshot } from "@gajae-code/utils/env-file";
15
17
  import type { Args as ParsedArgs } from "../cli/args";
16
18
  import { isSafeSdkInternalAgentDir, scanPublicCommand } from "../cli/public-command-entry";
17
19
  import { PublicCommandFailure } from "../cli/public-command-errors";
18
- import { FileLockAcquireError } from "../config/file-lock";
20
+ import { FileLockAcquireError, isFileLockAcquireTimeout } from "../config/file-lock";
19
21
  import { parseModelString } from "../config/model-resolver";
20
22
  import { Settings } from "../config/settings";
23
+ import { trustedCoordinatorEnv } from "../gjc-runtime/sidecar-bootstrap-env";
21
24
  import { usePostmortemSignalExitAuthority } from "../lsp/client";
22
25
  import { applyStartupModelProfiles, createSessionManager } from "../main";
23
26
  import { initializeExtensions } from "../modes/runtime-init";
@@ -29,15 +32,21 @@ import {
29
32
  type BrokerStartupExitWriteStatus,
30
33
  writeBrokerStartupExitRecordBounded,
31
34
  } from "../sdk/broker/broker-exit";
32
- import { readBrokerDiscovery } from "../sdk/broker/discovery";
35
+ import {
36
+ type BrokerRestartIntentWithIdentity,
37
+ readBrokerDiscovery,
38
+ readBrokerRestartIntent,
39
+ } from "../sdk/broker/discovery";
33
40
  import { type EndpointFileRead, readEndpointFile } from "../sdk/broker/endpoint-authority";
34
41
  import {
35
42
  BROKER_DISCOVERY_BUDGET,
36
43
  emitBrokerStartupTestSignal,
44
+ reapBrokerTrampolineChild,
37
45
  reconcileBrokerGenerationForStartup,
38
46
  SDK_BROKER_AUTOSTART_ENV,
39
47
  withBrokerStartupLock,
40
48
  } from "../sdk/broker/ensure";
49
+ import { waitForBrokerHandoffAcknowledgement, waitForBrokerHandoffDecision } from "../sdk/broker/hop";
41
50
  import {
42
51
  LifecycleFailurePublicationCleanupError,
43
52
  LifecycleReadinessCleanupError,
@@ -88,11 +97,17 @@ export async function lifecycleArgs(
88
97
  if (targetScope.kind !== "resolved") throw new Error(`Lifecycle session scope is invalid: ${targetScope.message}`);
89
98
  const forkSessionDir =
90
99
  request.operation === "session.fork" ? SessionManager.getDefaultSessionDir(cwd, agentDir) : undefined;
100
+ // Bun has already copied the project dotenv into process.env. A matching or
101
+ // dynamic declaration must not select an in-memory session. The exact "1"
102
+ // check stays; an operator value the project does not declare is still honored.
103
+ const inMemorySession =
104
+ process.env.GJC_SDK_TEST_IN_MEMORY_SESSION === "1" &&
105
+ trustedCoordinatorEnv("GJC_SDK_TEST_IN_MEMORY_SESSION", process.env, projectEnvSnapshot(cwd)) === "1";
91
106
  return {
92
107
  messages: [],
93
108
  fileArgs: [],
94
109
  unknownFlags: new Map(),
95
- ...(process.env.GJC_SDK_TEST_IN_MEMORY_SESSION === "1" ? { noSession: true } : {}),
110
+ ...(inMemorySession ? { noSession: true } : {}),
96
111
  ...(request.operation === "session.resume" ? { resume: request.sessionPath } : {}),
97
112
  ...(request.modelPreset ? { mpreset: request.modelPreset } : {}),
98
113
  // Explicit model pin (#4707): coordinator-resolved `provider/model`
@@ -547,6 +562,65 @@ export function startMemoryBackendAfterReadiness(start: () => Promise<void>): vo
547
562
  });
548
563
  }
549
564
 
565
+ const SDK_CWD_EQUALITY_TEST_SEAMS = [
566
+ "GJC_SDK_TEST_HANG_MODEL_PROFILE",
567
+ "GJC_SDK_TEST_FAIL_AFTER_REGISTRATION",
568
+ "GJC_SDK_TEST_EXIT_AFTER_READY",
569
+ "GJC_SDK_TEST_REJECT_AFTER_READY",
570
+ ] as const;
571
+
572
+ type SdkCwdEqualityTestSeam = (typeof SDK_CWD_EQUALITY_TEST_SEAMS)[number];
573
+
574
+ function isSdkCwdEqualityTestSeam(name: string): name is SdkCwdEqualityTestSeam {
575
+ return (SDK_CWD_EQUALITY_TEST_SEAMS as readonly string[]).includes(name);
576
+ }
577
+
578
+ function sdkCwdEqualitySnapshots(cwd: string): ProjectEnvSnapshot[] {
579
+ // The startup snapshot keeps a declaration Bun loaded before a later chdir.
580
+ return [projectEnvSnapshot(cwd), startupProjectEnvSnapshot()];
581
+ }
582
+
583
+ /**
584
+ * Cwd-equality test seams accept only an operator value. A project dotenv line,
585
+ * including one Bun expands from `$PWD`, never arms the seam.
586
+ */
587
+ export function sdkCwdEqualityTestSeamActive(
588
+ name: string,
589
+ cwd: string,
590
+ env: NodeJS.ProcessEnv = process.env,
591
+ snapshots: readonly ProjectEnvSnapshot[] = sdkCwdEqualitySnapshots(cwd),
592
+ ): boolean {
593
+ if (!isSdkCwdEqualityTestSeam(name) || env[name] !== cwd) return false;
594
+ // Provenance only. trustedCoordinatorEnv trims, so a second compare to cwd
595
+ // would drop a trusted path that still matches exactly, including trailing space.
596
+ return snapshots.every(snapshot => trustedCoordinatorEnv(name, env, snapshot) !== undefined);
597
+ }
598
+
599
+ /** Copy `env` without project-dotenv values for the four cwd-equality seams. */
600
+ export function omitUntrustedSdkCwdEqualitySeams(
601
+ env: NodeJS.ProcessEnv,
602
+ snapshots: readonly ProjectEnvSnapshot[],
603
+ ): NodeJS.ProcessEnv {
604
+ const denied: string[] = [];
605
+ for (const name of SDK_CWD_EQUALITY_TEST_SEAMS) {
606
+ const raw = env[name];
607
+ if (typeof raw !== "string" || raw.length === 0) continue;
608
+ if (snapshots.some(snapshot => trustedCoordinatorEnv(name, env, snapshot) === undefined)) denied.push(name);
609
+ }
610
+ if (denied.length === 0) return env;
611
+ const next: NodeJS.ProcessEnv = { ...env };
612
+ for (const name of denied) delete next[name];
613
+ return next;
614
+ }
615
+
616
+ function dropUntrustedSdkCwdEqualitySeams(env: NodeJS.ProcessEnv, snapshots: readonly ProjectEnvSnapshot[]): void {
617
+ const next = omitUntrustedSdkCwdEqualitySeams(env, snapshots);
618
+ if (next === env) return;
619
+ for (const name of SDK_CWD_EQUALITY_TEST_SEAMS) {
620
+ if (next[name] === undefined) delete env[name];
621
+ }
622
+ }
623
+
550
624
  /** Runs the same persisted AgentSession bootstrap used by the production CLI. */
551
625
  export async function runSessionHost(
552
626
  timing: {
@@ -578,6 +652,11 @@ export async function runSessionHost(
578
652
  const agentDir = process.env.GJC_AGENT_DIR;
579
653
  if (!agentDir) throw new Error("GJC_AGENT_DIR is required for sdk session-host-internal.");
580
654
  const cwd = timing.cwd ?? process.cwd();
655
+ const cwdSeamSnapshots = sdkCwdEqualitySnapshots(cwd);
656
+ // Drop rejected copies before startup work can spawn a child that inherits them.
657
+ dropUntrustedSdkCwdEqualitySeams(process.env, cwdSeamSnapshots);
658
+ const cwdEqualitySeam = (name: SdkCwdEqualityTestSeam): boolean =>
659
+ sdkCwdEqualityTestSeamActive(name, cwd, process.env, cwdSeamSnapshots);
581
660
  if ((await fs.realpath(request.cwd)) !== (await fs.realpath(cwd)))
582
661
  throw new Error(`Lifecycle worktree mismatch: expected ${request.cwd}, got ${cwd}.`);
583
662
  if (
@@ -1409,7 +1488,7 @@ export async function runSessionHost(
1409
1488
  try {
1410
1489
  const startupThinkingLevel = request.modelId ? parseModelString(request.modelId)?.thinkingLevel : undefined;
1411
1490
  await beforeCutoff(() =>
1412
- process.env.GJC_SDK_TEST_HANG_MODEL_PROFILE === cwd
1491
+ cwdEqualitySeam("GJC_SDK_TEST_HANG_MODEL_PROFILE")
1413
1492
  ? new Promise<void>(() => {})
1414
1493
  : applyModelProfiles({
1415
1494
  session,
@@ -1436,7 +1515,7 @@ export async function runSessionHost(
1436
1515
  const startup = await beforeCutoff<SdkStartupResult>(() => capability.promise);
1437
1516
  if (startup.status !== "started") throw startup.failure;
1438
1517
  throwIfStartupInterrupted();
1439
- if (process.env.GJC_SDK_TEST_FAIL_AFTER_REGISTRATION === cwd)
1518
+ if (cwdEqualitySeam("GJC_SDK_TEST_FAIL_AFTER_REGISTRATION"))
1440
1519
  throw new Error("Lifecycle test failure after SDK host registration.");
1441
1520
 
1442
1521
  await beforeCutoff(() => session.sessionManager.ensureOnDisk());
@@ -1505,11 +1584,11 @@ export async function runSessionHost(
1505
1584
  // Readiness is published; the session is live. Memory startup issues one LLM
1506
1585
  // request per queued rollout, so it must run outside the readiness window and
1507
1586
  // its failure must never take the session down.
1508
- if (process.env.GJC_SDK_TEST_EXIT_AFTER_READY === cwd) {
1587
+ if (cwdEqualitySeam("GJC_SDK_TEST_EXIT_AFTER_READY")) {
1509
1588
  process.stderr.write("GJC_SDK_TEST_EXIT_AFTER_READY\n");
1510
1589
  process.exit(7);
1511
1590
  }
1512
- if (process.env.GJC_SDK_TEST_REJECT_AFTER_READY === cwd) {
1591
+ if (cwdEqualitySeam("GJC_SDK_TEST_REJECT_AFTER_READY")) {
1513
1592
  // Simulates the post-readiness liveness watcher rejecting. With the
1514
1593
  // post-ready startup receipt removed, this unhandled rejection kills the
1515
1594
  // host without writing any startup receipt; the broker must classify the
@@ -1713,6 +1792,34 @@ export default class Sdk extends Command {
1713
1792
  }
1714
1793
  }
1715
1794
 
1795
+ /** Captures and removes one-shot restart credentials before the broker forks descendants. */
1796
+ export function consumeBrokerRestartEnvironment(environment: NodeJS.ProcessEnv): {
1797
+ requestIdEnv: string | undefined;
1798
+ restartDeadlineAt: number | undefined;
1799
+ } {
1800
+ const restartRequestEnv = environment.GJC_BROKER_RESTART_REQUEST;
1801
+ const restartDeadlineEnv = environment.GJC_BROKER_RESTART_DEADLINE;
1802
+ delete environment.GJC_BROKER_RESTART_REQUEST;
1803
+ delete environment.GJC_BROKER_RESTART_DEADLINE;
1804
+ const requestIdEnv =
1805
+ typeof restartRequestEnv === "string" && /^[A-Za-z0-9_-]{1,256}$/.test(restartRequestEnv)
1806
+ ? restartRequestEnv
1807
+ : undefined;
1808
+ let restartDeadlineAt: number | undefined;
1809
+ if (restartDeadlineEnv !== undefined) {
1810
+ const parsedDeadlineAt = Number(restartDeadlineEnv);
1811
+ if (
1812
+ requestIdEnv === undefined ||
1813
+ !/^[0-9]{1,16}$/.test(restartDeadlineEnv) ||
1814
+ !Number.isSafeInteger(parsedDeadlineAt) ||
1815
+ parsedDeadlineAt <= Date.now()
1816
+ )
1817
+ throw new Error("Invalid SDK broker restart deadline; refusing successor startup.");
1818
+ restartDeadlineAt = parsedDeadlineAt;
1819
+ }
1820
+ return { requestIdEnv, restartDeadlineAt };
1821
+ }
1822
+
1716
1823
  /**
1717
1824
  * Runs one parsed SDK internal entry point. `publicBrokerRun` is set only by
1718
1825
  * the stable `gjc sdk broker run` contract, which must report a lost ownership
@@ -1722,13 +1829,17 @@ export default class Sdk extends Command {
1722
1829
  async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false): Promise<void> {
1723
1830
  if (internal.action === "broker-trampoline-internal") {
1724
1831
  if (process.platform === "win32") throw new CliParseError("Broker trampoline is unavailable on Windows.");
1832
+ const retainChildUntilPublication = process.env.GJC_BROKER_TRAMPOLINE_RETAIN_UNTIL_PUBLICATION === "1";
1725
1833
  const command = resolveSdkInternalSpawnCommand("broker-internal");
1834
+ const brokerEnvironment = { ...command.env };
1835
+ delete brokerEnvironment.GJC_BROKER_TRAMPOLINE_RETAIN_UNTIL_PUBLICATION;
1836
+ dropUntrustedSdkCwdEqualitySeams(brokerEnvironment, sdkCwdEqualitySnapshots(process.cwd()));
1726
1837
  let child: ChildProcess;
1727
1838
  try {
1728
1839
  child = spawn(command.file, [...command.args, "--agent-dir", internal.agentDir], {
1729
1840
  detached: true,
1730
1841
  stdio: ["ignore", "ignore", "inherit"],
1731
- env: command.env,
1842
+ env: brokerEnvironment,
1732
1843
  ...(command.kind === "bun-source" ? { cwd: command.cwd } : {}),
1733
1844
  });
1734
1845
  } catch (error) {
@@ -1741,11 +1852,7 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1741
1852
  if (child.pid === undefined) throw new Error("Broker trampoline could not start broker.");
1742
1853
  const incarnation = processIncarnation(child.pid);
1743
1854
  if (!incarnation) {
1744
- try {
1745
- child.kill("SIGKILL");
1746
- } catch {
1747
- // The child may already have exited; either way identity was not established.
1748
- }
1855
+ await reapBrokerTrampolineChild(child, child.pid);
1749
1856
  throw new Error("Broker trampoline could not observe broker identity.");
1750
1857
  }
1751
1858
  if (spawnError) throw new Error("Broker trampoline could not start broker.", { cause: spawnError });
@@ -1757,13 +1864,19 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1757
1864
  try {
1758
1865
  await stdoutWrite.promise;
1759
1866
  } catch (error) {
1760
- try {
1761
- child.kill("SIGKILL");
1762
- } catch {
1763
- // The child may already have exited; reporting failure remains authoritative.
1764
- }
1867
+ await reapBrokerTrampolineChild(child, child.pid, incarnation);
1765
1868
  throw error;
1766
1869
  }
1870
+ if (retainChildUntilPublication) {
1871
+ const decision = await waitForBrokerHandoffDecision(process.stdin);
1872
+ if (decision === "abort") {
1873
+ await reapBrokerTrampolineChild(child, child.pid, incarnation);
1874
+ return;
1875
+ }
1876
+ } else if (!(await waitForBrokerHandoffAcknowledgement(process.stdin))) {
1877
+ await reapBrokerTrampolineChild(child, child.pid, incarnation);
1878
+ throw new Error("Broker trampoline handoff was not acknowledged; its direct child was reaped.");
1879
+ }
1767
1880
  child.unref();
1768
1881
  return;
1769
1882
  }
@@ -1846,7 +1959,13 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1846
1959
  if (startupExitLogged) return;
1847
1960
  startupExitLogged = true;
1848
1961
  const level = record.exitCode === 1 ? "error" : "warn";
1849
- if (record.exitCode === 1) logger.error("sdk broker: startup watchdog forcing exit", record);
1962
+ if (record.exitCode === 1)
1963
+ logger.error(
1964
+ record.reason === "startup-deadline"
1965
+ ? "sdk broker: startup watchdog forcing exit"
1966
+ : "sdk broker: startup refused",
1967
+ record,
1968
+ );
1850
1969
  else logger.warn("sdk broker: startup interrupted", record);
1851
1970
  process.stderr.write(`${JSON.stringify({ level, message, ...record })}\n`);
1852
1971
  };
@@ -1866,9 +1985,11 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1866
1985
  const message =
1867
1986
  reason === "startup-lock-blocked" && blockingLockPath
1868
1987
  ? `SDK broker startup blocked by retained removal transition ${blockingLockPath}.`
1869
- : reason === "startup-deadline"
1870
- ? `SDK broker startup exceeded its ${timeoutMs}ms fence deadline.`
1871
- : `SDK broker startup interrupted by ${signal} before readiness.`;
1988
+ : reason === "startup-lock-unavailable"
1989
+ ? `SDK broker startup could not acquire ${blockingLockPath ?? "a required lock"}.`
1990
+ : reason === "startup-deadline"
1991
+ ? `SDK broker startup exceeded its ${timeoutMs}ms fence deadline.`
1992
+ : `SDK broker startup interrupted by ${signal} before readiness.`;
1872
1993
  writeStartupExitLog(exitRecord, message);
1873
1994
  startupExitTask = (async () => {
1874
1995
  const exitRecordWrite = await beginStartupExitRecordWrite(exitRecord);
@@ -1922,33 +2043,124 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1922
2043
  await exitDuringStartup("startup-signal", startupSignalExitCode(signal), signal);
1923
2044
  return true;
1924
2045
  };
2046
+ let startupFenceAcquired = false;
1925
2047
  try {
2048
+ const { requestIdEnv, restartDeadlineAt } = consumeBrokerRestartEnvironment(process.env);
2049
+ const MAX_SESSION_INDEX_TIMEOUT_RETRIES = 2;
2050
+ let sessionIndexTimeoutRetries = 0;
2051
+ let activeStartupDeadlineAt = 0;
1926
2052
  const startupOperation = async (deadline: number): Promise<Broker | undefined> => {
2053
+ for (;;) {
2054
+ activeStartupDeadlineAt = deadline;
2055
+ try {
2056
+ return await singleStartupAttempt(deadline);
2057
+ } catch (error) {
2058
+ if (
2059
+ !isFileLockAcquireTimeout(error) ||
2060
+ !startupFenceAcquired ||
2061
+ path.resolve(error.filePath) !==
2062
+ path.resolve(path.join(agentDir, "sdk", "sessions", "index.jsonl")) ||
2063
+ sessionIndexTimeoutRetries >= MAX_SESSION_INDEX_TIMEOUT_RETRIES
2064
+ )
2065
+ throw error;
2066
+ sessionIndexTimeoutRetries++;
2067
+ const backoffMs = 100 * 2 ** (sessionIndexTimeoutRetries - 1);
2068
+ if (Date.now() + backoffMs >= activeStartupDeadlineAt) throw error;
2069
+ const failedBroker = broker;
2070
+ if (failedBroker) {
2071
+ try {
2072
+ await failedBroker.stop({ kind: "startup-failure" });
2073
+ } catch (cleanupError) {
2074
+ throw new AggregateError(
2075
+ [error, cleanupError],
2076
+ "Session-index startup retry could not clean up the failed broker candidate.",
2077
+ );
2078
+ }
2079
+ if (broker === failedBroker) broker = undefined;
2080
+ if (runningBroker === failedBroker) runningBroker = undefined;
2081
+ }
2082
+ candidateStartTask = undefined;
2083
+ logger.debug("sdk broker: session-index lock timeout, retrying startup", {
2084
+ reason: "session-index-timeout-retry",
2085
+ attempt: sessionIndexTimeoutRetries,
2086
+ backoffMs,
2087
+ });
2088
+ await Bun.sleep(backoffMs);
2089
+ }
2090
+ }
2091
+ };
2092
+ const singleStartupAttempt = async (deadline: number): Promise<Broker | undefined> => {
1927
2093
  const remainingMs = Math.max(1, deadline - Date.now());
1928
2094
  const testWatchdogMs = Number(process.env.GJC_SDK_TEST_BROKER_STARTUP_WATCHDOG_MS ?? 0);
1929
- const watchdogMs =
2095
+ const ordinaryWatchdogMs =
1930
2096
  Number.isSafeInteger(testWatchdogMs) && testWatchdogMs > 0 && testWatchdogMs <= remainingMs
1931
2097
  ? testWatchdogMs
1932
2098
  : remainingMs;
1933
- const startupCheckpointDeadline = performance.now() + watchdogMs - 1_000;
1934
- const startupWatchdog = setTimeout(() => {
1935
- startupAbortController.abort();
1936
- void exitDuringStartup("startup-deadline", 1, null, watchdogMs).then(async started => {
1937
- if (!started) {
1938
- // A prior startup signal owns the exit cause; its postmortem callback
1939
- // returns after bounded cleanup and preserves the signal status.
1940
- return;
1941
- }
1942
- await waitForStartupAbortCleanup();
1943
- process.exit(1);
1944
- });
1945
- }, watchdogMs);
2099
+ let restartRequestId: string | undefined;
2100
+ let startupDeadlineAt = deadline;
2101
+ let effectiveWatchdogMs = ordinaryWatchdogMs;
2102
+ let startupCheckpointDeadline = performance.now() + Math.max(1, effectiveWatchdogMs - 1_000);
2103
+ const armStartupWatchdog = (): NodeJS.Timeout =>
2104
+ setTimeout(() => {
2105
+ startupAbortController.abort();
2106
+ void exitDuringStartup("startup-deadline", 1, null, effectiveWatchdogMs).then(async started => {
2107
+ if (!started) {
2108
+ // A prior startup signal owns the exit cause; its postmortem callback
2109
+ // returns after bounded cleanup and preserves the signal status.
2110
+ return;
2111
+ }
2112
+ await waitForStartupAbortCleanup();
2113
+ process.exit(1);
2114
+ });
2115
+ }, effectiveWatchdogMs);
2116
+ let startupWatchdog = armStartupWatchdog();
1946
2117
  try {
2118
+ let restartIntent: BrokerRestartIntentWithIdentity | null;
2119
+ try {
2120
+ restartIntent = await readBrokerRestartIntent(agentDir);
2121
+ } catch (error) {
2122
+ throw new Error("Could not validate SDK broker restart intent; refusing untagged startup.", {
2123
+ cause: error,
2124
+ });
2125
+ }
2126
+ // A committed request survives the prior owner's exit. Ordinary or
2127
+ // mismatched starts must not publish ahead of its exact successor.
2128
+ if (
2129
+ restartIntent?.phase === "committed" &&
2130
+ restartIntent.expiresAt > Date.now() &&
2131
+ restartIntent.requestId !== requestIdEnv
2132
+ )
2133
+ throw new Error("A committed SDK broker restart is active; startup requires its exact request ID.");
2134
+ if (
2135
+ restartDeadlineAt !== undefined &&
2136
+ (restartIntent?.phase !== "committed" ||
2137
+ restartIntent.requestId !== requestIdEnv ||
2138
+ restartIntent.expiresAt !== restartDeadlineAt)
2139
+ )
2140
+ throw new Error("Could not validate SDK broker restart deadline; refusing successor startup.");
2141
+ if (requestIdEnv !== undefined && restartIntent?.requestId === requestIdEnv) {
2142
+ if (restartIntent.phase !== "committed")
2143
+ throw new Error("SDK broker restart intent is not committed; refusing successor startup.");
2144
+ const remainingSuccessorMs = restartIntent.expiresAt - Date.now();
2145
+ if (remainingSuccessorMs <= 0)
2146
+ throw new Error("SDK broker restart intent expired; refusing untagged startup.");
2147
+ restartRequestId = requestIdEnv;
2148
+ startupDeadlineAt = restartDeadlineAt ?? restartIntent.expiresAt;
2149
+ activeStartupDeadlineAt = startupDeadlineAt;
2150
+ effectiveWatchdogMs =
2151
+ Number.isSafeInteger(testWatchdogMs) && testWatchdogMs > 0 && testWatchdogMs <= remainingSuccessorMs
2152
+ ? testWatchdogMs
2153
+ : remainingSuccessorMs;
2154
+ clearTimeout(startupWatchdog);
2155
+ startupCheckpointDeadline = performance.now() + Math.max(1, effectiveWatchdogMs - 1_000);
2156
+ startupWatchdog = armStartupWatchdog();
2157
+ }
2158
+ if (startupAbortController.signal.aborted) return undefined;
1947
2159
  // `gjc sdk broker run` never retires another owner, even one this generation cannot
1948
2160
  // reuse: any live incumbent is a refusal. Autostart repairs unusable generations.
1949
2161
  const existing = publicBrokerRun
1950
2162
  ? ((await readBrokerDiscovery(agentDir)) ?? undefined)
1951
- : await reconcileBrokerGenerationForStartup({ agentDir }, deadline);
2163
+ : await reconcileBrokerGenerationForStartup({ agentDir }, startupDeadlineAt);
1952
2164
  if (startupAbortController.signal.aborted) return undefined;
1953
2165
  if (existing) {
1954
2166
  logger.info("sdk broker: startup reused an existing owner", {
@@ -1985,11 +2197,9 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
1985
2197
  // launcher-supplied environment variable; it is validated here and handed
1986
2198
  // to Broker as a typed setting, never read a second time inside broker.ts
1987
2199
  // from process.env directly.
1988
- const restartRequestEnv = process.env.GJC_BROKER_RESTART_REQUEST;
1989
- const restartRequestId =
1990
- typeof restartRequestEnv === "string" && /^[A-Za-z0-9_-]{1,256}$/.test(restartRequestEnv)
1991
- ? restartRequestEnv
1992
- : undefined;
2200
+ // Restart authorization is revalidated before constructing the broker; the
2201
+ // watchdog was already armed before any intent-file I/O.
2202
+ const effectiveCheckpointDeadline = startupCheckpointDeadline;
1993
2203
  const testPostPublicationDelayMs = Number(process.env.GJC_SDK_TEST_BROKER_POST_PUBLICATION_DELAY_MS ?? 0);
1994
2204
  const startupPostPublicationDelayMs =
1995
2205
  Number.isSafeInteger(testPostPublicationDelayMs) &&
@@ -2027,14 +2237,19 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
2027
2237
  }
2028
2238
  }
2029
2239
  : undefined;
2240
+ const brokerSettings = await Settings.loadForScope({ cwd: process.cwd(), agentDir });
2030
2241
  const candidate = new Broker({
2031
2242
  agentDir,
2032
2243
  startupAbortSignal: startupAbortController.signal,
2033
- startupCheckpointDeadline,
2244
+ startupCheckpointDeadline: effectiveCheckpointDeadline,
2034
2245
  onStartupReady: () => clearTimeout(startupWatchdog),
2035
- masterOrphanGraceMs: (await Settings.loadForScope({ cwd: process.cwd(), agentDir })).get(
2036
- "sdk.masterOrphanGraceMs",
2037
- ),
2246
+ masterOrphanGraceMs: brokerSettings.get("sdk.masterOrphanGraceMs"),
2247
+ abandonedGraceMs: brokerSettings.get("sdk.brokerAbandonedGraceMs"),
2248
+ // Only the detached autostart entry retires itself when nobody is left to
2249
+ // use it. A supervised `gjc sdk broker run` service is owned by its
2250
+ // operator's process manager, which reads an owned-root completion as a
2251
+ // clean exit and would not restart it.
2252
+ reapWhenAbandoned: !publicBrokerRun,
2038
2253
  resolveDirectoryMigration: async cwd => {
2039
2254
  const settings = await Settings.loadForScope({ cwd, agentDir });
2040
2255
  try {
@@ -2066,7 +2281,11 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
2066
2281
  }
2067
2282
  };
2068
2283
  broker = await withBrokerStartupLock(agentDir, startupOperation, {
2069
- onAcquired: () => void emitBrokerStartupTestSignal("fence-acquired"),
2284
+ ...(restartDeadlineAt === undefined ? {} : { deadlineAt: restartDeadlineAt }),
2285
+ onAcquired: () => {
2286
+ startupFenceAcquired = true;
2287
+ void emitBrokerStartupTestSignal("fence-acquired");
2288
+ },
2070
2289
  onContended: () => void emitBrokerStartupTestSignal("fence-contended"),
2071
2290
  });
2072
2291
  } catch (error) {
@@ -2074,9 +2293,14 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
2074
2293
  await finishPendingStartupSignal();
2075
2294
  return;
2076
2295
  }
2077
- // A retained removal transition is an explicit acquire failure, not only
2078
- // an exhausted ordinary contention timeout.
2079
- if (error instanceof FileLockAcquireError && error.orphanPath) {
2296
+ // Only a retained transition on the outer startup fence gets its own
2297
+ // blocked reason. Nested resource-lock failures are retryable unavailability.
2298
+ if (
2299
+ error instanceof FileLockAcquireError &&
2300
+ !startupFenceAcquired &&
2301
+ error.code === "orphan_transition" &&
2302
+ error.orphanPath
2303
+ ) {
2080
2304
  await exitDuringStartup(
2081
2305
  "startup-lock-blocked",
2082
2306
  1,
@@ -2084,6 +2308,9 @@ async function runSdkInternal(internal: SdkInternalArgv, publicBrokerRun = false
2084
2308
  BROKER_DISCOVERY_BUDGET.startupLockWaitMs,
2085
2309
  error.orphanPath,
2086
2310
  );
2311
+ } else if (error instanceof FileLockAcquireError) {
2312
+ // Nested lock failures or non-orphan-transition errors
2313
+ await exitDuringStartup("startup-lock-unavailable", 1, null, undefined, error.orphanPath ?? error.lockPath);
2087
2314
  }
2088
2315
  if (broker) {
2089
2316
  try {
@@ -8,6 +8,7 @@ import {
8
8
  writeCurrentSessionGoalModeState,
9
9
  writePendingGoalModeRequest,
10
10
  } from "../gjc-runtime/goal-mode-request";
11
+ import { trustedCoordinatorEnv } from "../gjc-runtime/sidecar-bootstrap-env";
11
12
  import { isUltragoalHelpInvocation, runNativeUltragoalCommand } from "../gjc-runtime/ultragoal-runtime";
12
13
 
13
14
  export default class Ultragoal extends Command {
@@ -44,16 +45,23 @@ export default class Ultragoal extends Command {
44
45
  const { objective, goalsPath, provenance } = await readUltragoalGjcObjective(cwd);
45
46
 
46
47
  await writeCurrentSessionGoalModeState({
47
- sessionFile: process.env[GJC_SESSION_FILE_ENV],
48
+ // Bun has already copied the project dotenv into the environment. A
49
+ // value that matches that snapshot must not select the transcript.
50
+ sessionFile: trustedCoordinatorEnv(GJC_SESSION_FILE_ENV),
48
51
  objective,
49
52
  provenance,
50
53
  });
51
- await writePendingGoalModeRequest({
52
- cwd,
53
- objective,
54
- goalsPath,
55
- provenance,
56
- sessionId: process.env[GJC_SESSION_ID_ENV],
57
- });
54
+ // A matching or dynamic project declaration must not select the session.
55
+ // Skip the write in that case: the helper falls back to the same env value.
56
+ const sessionId = trustedCoordinatorEnv(GJC_SESSION_ID_ENV);
57
+ if (sessionId !== undefined || !process.env[GJC_SESSION_ID_ENV]?.trim()) {
58
+ await writePendingGoalModeRequest({
59
+ cwd,
60
+ objective,
61
+ goalsPath,
62
+ provenance,
63
+ sessionId,
64
+ });
65
+ }
58
66
  }
59
67
  }