@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
package/src/main.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * createAgentSession() options. The SDK does the heavy lifting.
6
6
  */
7
7
 
8
+ import { existsSync } from "node:fs";
8
9
  import * as fs from "node:fs/promises";
9
10
  import * as os from "node:os";
10
11
  import * as path from "node:path";
@@ -21,14 +22,16 @@ import {
21
22
  VERSION,
22
23
  } from "@gajae-code/utils";
23
24
  import chalk from "chalk";
24
- import type { Args } from "./cli/args";
25
+ import { selectAppendSystemPromptFile } from "./cli/append-system-prompt";
26
+ import { type Args, resolvePlanModelOverride } from "./cli/args";
25
27
  import { processFileArguments } from "./cli/file-processor";
26
28
  import { fetchGithubChannelRelease } from "./cli/github-release";
27
29
  import { buildInitialMessage } from "./cli/initial-message";
28
30
  import { resolveLaunchDisposition } from "./cli/launch-disposition";
29
31
  import { runListModelsCommand } from "./cli/list-models";
30
32
  import { selectSession } from "./cli/session-picker";
31
- import { findConfigFile } from "./config";
33
+ import { getConfigDirs } from "./config";
34
+ import { FileLockAcquireError, FileLockStagingCleanupError } from "./config/file-lock";
32
35
  import {
33
36
  activateModelProfile,
34
37
  applyModelProfileRuntimeBindings,
@@ -74,7 +77,12 @@ import {
74
77
  discoverAuthStorage,
75
78
  } from "./sdk";
76
79
  import { processIncarnation } from "./sdk/broker/process-incarnation";
77
- import { newMasterAttestationEpoch, resolveSessionLocator, SessionIndex } from "./sdk/broker/session-index";
80
+ import {
81
+ newMasterAttestationEpoch,
82
+ resolveSessionLocator,
83
+ SessionIndex,
84
+ type SessionLocatorV2,
85
+ } from "./sdk/broker/session-index";
78
86
  import type { AgentSession } from "./session/agent-session";
79
87
  import { ManagedAppendIdentityMismatchError } from "./session/internal/managed-session-storage";
80
88
  import { SessionMigrationBusyError } from "./session/internal/session-open-errors";
@@ -249,8 +257,6 @@ export function resolveAcpStartupOptions(
249
257
  | "resume"
250
258
  | "sessionDir"
251
259
  | "skills"
252
- | "slow"
253
- | "smol"
254
260
  | "plan"
255
261
  | "systemPrompt"
256
262
  | "thinking"
@@ -291,8 +297,6 @@ export function resolveAcpStartupOptions(
291
297
  ...(parsed.resume ? ["--resume"] : []),
292
298
  ...(parsed.sessionDir ? ["--session-dir"] : []),
293
299
  ...(parsed.skills !== undefined ? ["--skills"] : []),
294
- ...(parsed.slow ? ["--slow"] : []),
295
- ...(parsed.smol ? ["--smol"] : []),
296
300
  ...(parsed.plan ? ["--plan"] : []),
297
301
  ...(parsed.systemPrompt ? ["--system-prompt"] : []),
298
302
  ...(parsed.tmux ? ["--tmux"] : []),
@@ -1446,17 +1450,13 @@ async function maybeAutoChdir(parsed: Args): Promise<void> {
1446
1450
  }
1447
1451
  }
1448
1452
 
1449
- /** Discover APPEND_SYSTEM.md file if no CLI append system prompt was provided */
1453
+ /** User APPEND_SYSTEM.md only. Repository copies are not appended. */
1450
1454
  function discoverAppendSystemPromptFile(): string | undefined {
1451
- const projectPath = findConfigFile("APPEND_SYSTEM.md", { user: false });
1452
- if (projectPath) {
1453
- return projectPath;
1454
- }
1455
- const globalPath = findConfigFile("APPEND_SYSTEM.md", { user: true });
1456
- if (globalPath) {
1457
- return globalPath;
1458
- }
1459
- return undefined;
1455
+ const candidates = getConfigDirs("", { existingOnly: false }).flatMap(entry => {
1456
+ const filePath = path.join(entry.path, "APPEND_SYSTEM.md");
1457
+ return existsSync(filePath) ? [{ level: entry.level, path: filePath }] : [];
1458
+ });
1459
+ return selectAppendSystemPromptFile(candidates);
1460
1460
  }
1461
1461
 
1462
1462
  async function buildSessionOptions(
@@ -1617,6 +1617,7 @@ type RunPrintMode = (session: AgentSession, options: PrintModeOptions) => Promis
1617
1617
  export interface RunRootCommandDependencies {
1618
1618
  createAgentSession?: typeof createAgentSession;
1619
1619
  createSessionManager?: typeof createSessionManager;
1620
+ registerDirectSession?: typeof registerDirectSession;
1620
1621
  discoverAuthStorage?: typeof discoverAuthStorage;
1621
1622
  resolveStartupAuthConfig?: typeof resolveStartupAuthConfig;
1622
1623
  runAcpMode?: (options?: { agentDir?: string }) => Promise<void>;
@@ -1663,84 +1664,278 @@ interface DirectSessionRegistrationDependencies {
1663
1664
  masterRole?: { ownerSessionId: string; attestationEpoch: string };
1664
1665
  }
1665
1666
 
1667
+ /**
1668
+ * Handle for a direct CLI session's broker-index registration.
1669
+ *
1670
+ * A direct session registers once at startup, but `/new`, `/resume` and forks
1671
+ * replace the manager's session id inside the same process. The index row is
1672
+ * keyed on that id, so without a follow-up the successor is never indexed and the
1673
+ * predecessor stays live for as long as this pid is alive (the broker keeps
1674
+ * heartbeating a live pid). `syncIdentity` moves the registration to whichever
1675
+ * id the manager the session has adopted currently reports.
1676
+ */
1677
+ export interface DirectSessionRegistration {
1678
+ /**
1679
+ * Register the current manager's session id and release the one it replaced.
1680
+ *
1681
+ * `adoptedManager` rebinds the registration to the manager the session now
1682
+ * owns: a bounded cold-sidecar fork adopts its own forked manager before it
1683
+ * emits `session_switch`, so the wired handler must hand that manager over or
1684
+ * the sync keeps reading the startup manager's predecessor id.
1685
+ */
1686
+ syncIdentity(adoptedManager?: DirectSessionManager): Promise<void>;
1687
+ }
1688
+
1689
+ const NOOP_DIRECT_SESSION_REGISTRATION: DirectSessionRegistration = { syncIdentity: async () => {} };
1690
+
1691
+ /**
1692
+ * Appends allowed for one successor registration before the switch gives up and
1693
+ * keeps the predecessor registered. `SessionIndex.append` takes the shared index
1694
+ * lock independently per append, so a peer holding it is a transient failure.
1695
+ */
1696
+ const DIRECT_SESSION_REGISTRATION_ATTEMPTS = 3;
1697
+
1666
1698
  /** Register a durable direct CLI session while leaving ephemeral sessions lock-free. */
1667
1699
  export async function registerDirectSession(
1668
1700
  sessionManager: DirectSessionManager | undefined,
1669
1701
  agentDir: string,
1670
1702
  lifecycleRequestId: string | undefined,
1671
1703
  dependencies: DirectSessionRegistrationDependencies = {},
1672
- ): Promise<void> {
1673
- const directSessionId = directSessionRegistrationId(sessionManager, lifecycleRequestId);
1674
- if (!directSessionId || !sessionManager) return;
1704
+ ): Promise<DirectSessionRegistration> {
1705
+ const initialSessionId = directSessionRegistrationId(sessionManager, lifecycleRequestId);
1706
+ if (!initialSessionId || !sessionManager) return NOOP_DIRECT_SESSION_REGISTRATION;
1675
1707
  const sessionIndex = dependencies.createIndex?.(agentDir) ?? new SessionIndex(agentDir);
1676
- const locator = await resolveSessionLocator(sessionManager.getCwd(), agentDir);
1677
1708
  const directSessionIncarnation = (dependencies.processIncarnation ?? processIncarnation)(process.pid);
1678
- await sessionIndex.append({
1679
- type: "host_registered",
1680
- sessionId: directSessionId,
1681
- locator,
1682
- endpointGeneration: 0,
1683
- pid: process.pid,
1684
- ...(directSessionIncarnation ? { processIncarnation: directSessionIncarnation } : {}),
1685
- ...(dependencies.masterRole && directSessionIncarnation
1686
- ? {
1687
- masterRole: {
1688
- version: 2 as const,
1689
- ownerSessionId: dependencies.masterRole.ownerSessionId,
1690
- launchPid: process.pid,
1691
- launchProcessIncarnation: directSessionIncarnation,
1692
- role: "master" as const,
1693
- attestationEpoch: dependencies.masterRole.attestationEpoch,
1694
- },
1695
- }
1696
- : {}),
1697
- });
1698
- if (locator.worktreeRoot !== null) {
1709
+ // The session can adopt a different manager mid-process (bounded cold-sidecar
1710
+ // fork), so every read goes through the manager that is current now.
1711
+ let activeManager = sessionManager;
1712
+ let registeredLocator: SessionLocatorV2 | undefined;
1713
+ let registeredSessionId: string | undefined;
1714
+ // Predecessors whose `host_unregistered` append has not succeeded yet. Once the
1715
+ // successor is registered the old id is no longer `registeredSessionId`, so a
1716
+ // release that failed here would otherwise be lost and keep the predecessor live
1717
+ // in the index for the rest of the process. An entry is dropped as soon as its id
1718
+ // is current again: replaying it would retire a session this process reactivated.
1719
+ const pendingPredecessorReleases: Array<{ sessionId: string; locator: SessionLocatorV2 | undefined }> = [];
1720
+ const forgetPendingRelease = (sessionId: string): void => {
1721
+ for (let index = pendingPredecessorReleases.length - 1; index >= 0; index--) {
1722
+ if (pendingPredecessorReleases[index]?.sessionId === sessionId) pendingPredecessorReleases.splice(index, 1);
1723
+ }
1724
+ };
1725
+
1726
+ const register = async (sessionId: string): Promise<void> => {
1727
+ const locator = await resolveSessionLocator(activeManager.getCwd(), agentDir);
1699
1728
  try {
1700
- await releaseLaunchWorktreeReservationAfterRegistration(agentDir, locator.worktreeRoot);
1729
+ await sessionIndex.append({
1730
+ type: "host_registered",
1731
+ sessionId,
1732
+ locator,
1733
+ endpointGeneration: 0,
1734
+ pid: process.pid,
1735
+ ...(directSessionIncarnation ? { processIncarnation: directSessionIncarnation } : {}),
1736
+ ...(dependencies.masterRole && directSessionIncarnation
1737
+ ? {
1738
+ masterRole: {
1739
+ version: 2 as const,
1740
+ ownerSessionId: dependencies.masterRole.ownerSessionId,
1741
+ launchPid: process.pid,
1742
+ launchProcessIncarnation: directSessionIncarnation,
1743
+ role: "master" as const,
1744
+ attestationEpoch: dependencies.masterRole.attestationEpoch,
1745
+ },
1746
+ }
1747
+ : {}),
1748
+ });
1701
1749
  } catch (error) {
1702
- logger.warn("Failed to release launch worktree reservation after host registration", {
1750
+ // A lock-release refusal after the row is durable is a real startup failure:
1751
+ // the next process would observe a registered session whose lock generation
1752
+ // this process can no longer prove. Surface it instead of adopting the row.
1753
+ if (error instanceof Error && /^Failed to release file lock:/u.test(error.message)) throw error;
1754
+ // `SessionIndex.append` can reject after its row is already durable (awaited
1755
+ // post-append maintenance), so a rejection does not prove the registration is
1756
+ // missing. Adopt the id when this process already owns a live row for it, so a
1757
+ // later switch still releases it; otherwise the switch keeps failing and the
1758
+ // previous identity stays registered.
1759
+ if (!isRegisteredLive(sessionId)) throw error;
1760
+ logger.warn("Direct session registration append failed after its row was durable", {
1703
1761
  error: error instanceof Error ? error.message : String(error),
1704
1762
  });
1705
1763
  }
1706
- }
1707
- (dependencies.registerPostmortem ?? postmortem.register)("direct-session-index", async _reason => {
1764
+ // This id is current again: any queued release for it is stale.
1765
+ forgetPendingRelease(sessionId);
1766
+ registeredLocator = locator;
1767
+ registeredSessionId = sessionId;
1768
+ };
1769
+ const isRegisteredLive = (sessionId: string): boolean => {
1770
+ try {
1771
+ return sessionIndex
1772
+ .listSessions()
1773
+ .sessions.some(row => row.sessionId === sessionId && row.pid === process.pid && row.live === true);
1774
+ } catch {
1775
+ return false;
1776
+ }
1777
+ };
1778
+ const unregister = async (sessionId: string, locator: SessionLocatorV2 | undefined): Promise<void> => {
1779
+ if (!locator) return;
1708
1780
  await sessionIndex.append({
1709
1781
  type: "host_unregistered",
1710
- sessionId: directSessionId,
1782
+ sessionId,
1711
1783
  locator,
1712
1784
  endpointGeneration: 0,
1713
1785
  pid: process.pid,
1714
1786
  });
1787
+ };
1788
+
1789
+ /**
1790
+ * Release queued predecessors in order. A failed append keeps its entry, so the
1791
+ * next sync or the teardown handler retries it; the failure itself is a warning
1792
+ * rather than a failed identity switch, matching the best-effort release of the
1793
+ * launch worktree reservation below.
1794
+ */
1795
+ const releasePendingPredecessors = async (): Promise<void> => {
1796
+ while (pendingPredecessorReleases.length > 0) {
1797
+ const predecessor = pendingPredecessorReleases[0]!;
1798
+ // Defence in depth for the reactivation order: never retire the id this
1799
+ // process currently publishes as its own.
1800
+ if (predecessor.sessionId === registeredSessionId) {
1801
+ pendingPredecessorReleases.shift();
1802
+ continue;
1803
+ }
1804
+ try {
1805
+ await unregister(predecessor.sessionId, predecessor.locator);
1806
+ } catch (error) {
1807
+ logger.warn("Failed to release a superseded session registration", {
1808
+ error: error instanceof Error ? error.message : String(error),
1809
+ });
1810
+ return;
1811
+ }
1812
+ pendingPredecessorReleases.shift();
1813
+ }
1814
+ };
1815
+
1816
+ /**
1817
+ * Register `sessionId`, retrying a bounded number of times so a transient
1818
+ * index-lock loss self-heals instead of leaving the successor unregistered
1819
+ * until the next identity change.
1820
+ */
1821
+ const registerWithRetry = async (sessionId: string): Promise<void> => {
1822
+ for (let attempt = 1; ; attempt++) {
1823
+ try {
1824
+ await register(sessionId);
1825
+ return;
1826
+ } catch (error) {
1827
+ // Only lock contention self-heals. A refused release is an identity
1828
+ // verdict; retrying it would mask a committed registration whose lock
1829
+ // generation no longer matches.
1830
+ const code = (error as NodeJS.ErrnoException | undefined)?.code;
1831
+ const transient =
1832
+ code === "sharing_violation" ||
1833
+ code === "access_denied" ||
1834
+ code === "EBUSY" ||
1835
+ code === "EPERM" ||
1836
+ code === "EACCES" ||
1837
+ code === "ENOTEMPTY" ||
1838
+ code === "removal_record_busy";
1839
+ if (!transient || attempt >= DIRECT_SESSION_REGISTRATION_ATTEMPTS) throw error;
1840
+ await Bun.sleep(50 * attempt);
1841
+ }
1842
+ }
1843
+ };
1844
+
1845
+ /**
1846
+ * Move the registration to the id `activeManager` currently reports.
1847
+ *
1848
+ * A successor append that cannot land after its bounded retries keeps the
1849
+ * predecessor registered and the successor unregistered, so every later call
1850
+ * (any session event, or teardown) retries the successor instead of leaving it
1851
+ * unindexed for the life of the process.
1852
+ */
1853
+ const syncToActiveManager = async (): Promise<void> => {
1854
+ const currentId = activeManager.getSessionFile() === undefined ? undefined : activeManager.getSessionId();
1855
+ // A predecessor left over from a failed release is retried here even when
1856
+ // the identity itself did not change.
1857
+ if (!currentId || currentId === registeredSessionId) {
1858
+ await releasePendingPredecessors();
1859
+ return;
1860
+ }
1861
+ // The predecessor is released under the locator it was registered with, and
1862
+ // only after the successor's own append succeeded.
1863
+ const predecessorId = registeredSessionId;
1864
+ const predecessorLocator = registeredLocator;
1865
+ await registerWithRetry(currentId);
1866
+ if (predecessorId) {
1867
+ pendingPredecessorReleases.push({ sessionId: predecessorId, locator: predecessorLocator });
1868
+ await releasePendingPredecessors();
1869
+ }
1870
+ };
1871
+
1872
+ await register(initialSessionId);
1873
+ if (registeredLocator?.worktreeRoot != null) {
1874
+ try {
1875
+ await releaseLaunchWorktreeReservationAfterRegistration(agentDir, registeredLocator.worktreeRoot);
1876
+ } catch (error) {
1877
+ logger.warn("Failed to release launch worktree reservation after host registration", {
1878
+ error: error instanceof Error ? error.message : String(error),
1879
+ });
1880
+ }
1881
+ }
1882
+ // Teardown registers the id the session is on now — a post-switch append that
1883
+ // failed would otherwise leave the active transcript unindexed — then releases
1884
+ // everything this process still holds.
1885
+ (dependencies.registerPostmortem ?? postmortem.register)("direct-session-index", async _reason => {
1886
+ try {
1887
+ await syncToActiveManager();
1888
+ } catch (error) {
1889
+ logger.warn("Failed to register the active direct session during teardown", {
1890
+ error: error instanceof Error ? error.message : String(error),
1891
+ });
1892
+ }
1893
+ if (registeredSessionId) await unregister(registeredSessionId, registeredLocator);
1894
+ await releasePendingPredecessors();
1715
1895
  });
1716
- }
1717
1896
 
1718
- export interface ModelRoleOverrides {
1719
- smol?: string;
1720
- slow?: string;
1721
- plan?: string;
1897
+ let syncing: Promise<void> = Promise.resolve();
1898
+ return {
1899
+ syncIdentity: adoptedManager => {
1900
+ // Serialized: a rapid /new /new must register in order and never interleave.
1901
+ const run = syncing.then(async () => {
1902
+ // An ephemeral manager owns nothing the index could fence, so the tracked
1903
+ // manager survives an adoption that has no durable transcript.
1904
+ if (adoptedManager?.getSessionFile() !== undefined) activeManager = adoptedManager;
1905
+ await syncToActiveManager();
1906
+ });
1907
+ syncing = run.catch(() => {});
1908
+ return run;
1909
+ },
1910
+ };
1722
1911
  }
1723
1912
 
1724
- /**
1725
- * Resolve the ephemeral `smol`/`slow`/`plan` model-role overrides.
1726
- *
1727
- * Precedence per role is CLI flag > documented `GJC_*_MODEL` > legacy
1728
- * `PI_*_MODEL`, matching the repo-wide GJC-first/PI-fallback convention.
1729
- * Resolution reads the process environment via `$pickenv`, which trims values
1730
- * and treats empty/whitespace as unset; it is deliberately kept separate from
1731
- * credential env resolution (`$credentialEnv`/`$pickCredentialEnv`). The
1732
- * function is pure and stateless, so it reads fresh each call and a later
1733
- * invocation never inherits an earlier one's values.
1734
- */
1735
- export function resolveModelRoleOverrides(parsed: Pick<Args, "smol" | "slow" | "plan">): ModelRoleOverrides {
1736
- const overrides: ModelRoleOverrides = {};
1737
- const smol = parsed.smol ?? $pickenv("GJC_SMOL_MODEL", "PI_SMOL_MODEL");
1738
- const slow = parsed.slow ?? $pickenv("GJC_SLOW_MODEL", "PI_SLOW_MODEL");
1739
- const plan = parsed.plan ?? $pickenv("GJC_PLAN_MODEL", "PI_PLAN_MODEL");
1740
- if (smol) overrides.smol = smol;
1741
- if (slow) overrides.slow = slow;
1742
- if (plan) overrides.plan = plan;
1743
- return overrides;
1913
+ function directSessionIndexLockFailureMessage(error: unknown, agentDir: string): string | undefined {
1914
+ if (error instanceof FileLockStagingCleanupError) {
1915
+ return (
1916
+ "Session index lock staging cleanup failed; acquisition stopped instead of retrying with another staging directory.\n" +
1917
+ `Lock: ${error.lockPath}\nStaging: ${error.stagingPath}\nCode: ${error.code ?? "unknown"}\n${error.message}`
1918
+ );
1919
+ }
1920
+ if (error instanceof FileLockAcquireError) {
1921
+ const code = error.publicationFailureCode ?? error.code;
1922
+ if (error.publicationFailureCode === "sharing_violation" || error.holderIsLive)
1923
+ return (
1924
+ "Session index is busy (validated lock-publication contention). Retry in a few seconds.\n" +
1925
+ `Lock: ${error.lockPath}\nCode: ${code}\n${error.message}`
1926
+ );
1927
+ return (
1928
+ "Session index lock acquisition failed; no session was registered.\n" +
1929
+ `Lock: ${error.lockPath}\nCode: ${code}\nHolder: ${error.holder}`
1930
+ );
1931
+ }
1932
+ if (!(error instanceof Error) || !/^Failed to publish file lock:/u.test(error.message)) return undefined;
1933
+ const code =
1934
+ (error as NodeJS.ErrnoException).code ??
1935
+ error.message.match(/^Failed to publish file lock: ([a-z0-9_]+)/u)?.[1] ??
1936
+ "unknown";
1937
+ const lockPath = path.join(agentDir, "sdk", "sessions", "index.jsonl.lock");
1938
+ return `Session index lock publication failed; no session was registered.\nLock: ${lockPath}\nCode: ${code}\n${error.message}`;
1744
1939
  }
1745
1940
 
1746
1941
  /**
@@ -2002,19 +2197,12 @@ export async function runRootCommand(
2002
2197
  // Initialize discovery system with settings for provider persistence
2003
2198
  logger.time("initializeWithSettings", initializeWithSettings, settingsInstance);
2004
2199
 
2005
- // Apply model role overrides from CLI args or env vars (ephemeral, not persisted).
2006
- // Precedence per role: CLI flag > documented GJC_*_MODEL > legacy PI_*_MODEL.
2007
- const roleOverrides = resolveModelRoleOverrides(parsedArgs);
2008
- if (roleOverrides.smol || roleOverrides.slow || roleOverrides.plan) {
2009
- settingsInstance.overrideModelRoles({
2010
- ...(roleOverrides.smol ? { smol: roleOverrides.smol } : {}),
2011
- ...(roleOverrides.slow ? { slow: roleOverrides.slow } : {}),
2012
- ...(roleOverrides.plan ? { plan: roleOverrides.plan } : {}),
2013
- });
2014
- }
2200
+ // Plan mode switches to the `plan` model role; --plan / GJC_PLAN_MODEL override it
2201
+ // for this run only (ephemeral, not persisted).
2202
+ const planModelOverride = resolvePlanModelOverride(parsedArgs);
2203
+ if (planModelOverride) settingsInstance.overrideModelRoles({ plan: planModelOverride });
2015
2204
  // Apply --clipboard-transport / --clipboard-ssh-host as ephemeral runtime
2016
- // overrides (CLI > persisted config > "auto" default). Not persisted —
2017
- // mirrors the role-override precedence above.
2205
+ // overrides (CLI > persisted config > "auto" default). Not persisted.
2018
2206
  if (parsedArgs.clipboardTransport) {
2019
2207
  settingsInstance.override("clipboard.transport", parsedArgs.clipboardTransport);
2020
2208
  }
@@ -2302,15 +2490,64 @@ export async function runRootCommand(
2302
2490
  // Register a resumed direct session before constructing the agent: GC holds the
2303
2491
  // same index lock while deleting artifacts, so startup and deletion are fenced.
2304
2492
  // Ephemeral sessions have no durable artifacts or liveness authority to fence.
2305
- await registerDirectSession(sessionManager, settingsInstance.getAgentDir(), process.env.GJC_LIFECYCLE_REQUEST_ID, {
2306
- masterRole: masterModeContext
2307
- ? {
2308
- ownerSessionId: masterModeContext.ownerSessionId,
2309
- attestationEpoch: masterModeContext.attestationEpoch,
2493
+ const sessionAgentDir = settingsInstance.getAgentDir();
2494
+ let directSessionRegistration: DirectSessionRegistration;
2495
+ try {
2496
+ directSessionRegistration = await (deps.registerDirectSession ?? registerDirectSession)(
2497
+ sessionManager,
2498
+ sessionAgentDir,
2499
+ process.env.GJC_LIFECYCLE_REQUEST_ID,
2500
+ {
2501
+ masterRole: masterModeContext
2502
+ ? {
2503
+ ownerSessionId: masterModeContext.ownerSessionId,
2504
+ attestationEpoch: masterModeContext.attestationEpoch,
2505
+ }
2506
+ : undefined,
2507
+ },
2508
+ );
2509
+ } catch (error) {
2510
+ const message = directSessionIndexLockFailureMessage(error, sessionAgentDir);
2511
+ if (message === undefined) throw error;
2512
+ process.stderr.write(`${message}\n`);
2513
+ if (!deps.suppressProcessExit) process.exitCode = 1;
2514
+ await sessionManager?.close().catch(closeError => {
2515
+ logger.warn("Failed to close session manager after session-index registration failure", {
2516
+ error: closeError instanceof Error ? closeError.message : String(closeError),
2517
+ });
2518
+ });
2519
+ authStorage.close();
2520
+ stopThemeWatcher();
2521
+ await postmortem.cleanup();
2522
+ return;
2523
+ }
2524
+ // /new, /resume and forks replace the manager's session id in this same process.
2525
+ // Follow it, or the broker index keeps listing the predecessor as live forever.
2526
+ //
2527
+ // The extension context is not a usable source here: `ExtensionRunner` is
2528
+ // constructed with the startup manager and `createContext()` keeps handing that
2529
+ // facade back, while a bounded cold-sidecar fork replaces only the session's own
2530
+ // manager. Read the live session at dispatch time instead.
2531
+ let liveSession: AgentSession | undefined;
2532
+ sessionOptions.extensions = [
2533
+ ...(sessionOptions.extensions ?? []),
2534
+ pi => {
2535
+ const sync = async (): Promise<void> => {
2536
+ try {
2537
+ await directSessionRegistration.syncIdentity(liveSession?.sessionManager);
2538
+ } catch (error) {
2539
+ logger.warn("Failed to move direct session registration to the new session id", {
2540
+ error: error instanceof Error ? error.message : String(error),
2541
+ });
2310
2542
  }
2311
- : undefined,
2312
- });
2313
-
2543
+ };
2544
+ pi.on("session_switch", sync);
2545
+ pi.on("session_branch", sync);
2546
+ // A transition whose append failed is retried at the next turn boundary, not
2547
+ // only on another identity change.
2548
+ pi.on("agent_end", sync);
2549
+ },
2550
+ ];
2314
2551
  const createAgentSessionImpl = deps.createAgentSession ?? createAgentSession;
2315
2552
  const createSession: CreateSessionForMain = async (options, context): Promise<CreateAgentSessionResult> => {
2316
2553
  const result = await logger.time("createAgentSession", createAgentSessionImpl, options);
@@ -2346,6 +2583,7 @@ export async function runRootCommand(
2346
2583
  startDeferredMemoryBackend,
2347
2584
  eventBus,
2348
2585
  } = await createSession(sessionOptions, { skipPostCreateModelRefresh: hasRootStartupProfile });
2586
+ liveSession = session;
2349
2587
  applyCliRuntimeApiKeyOverride(authStorage, parsedArgs.apiKey, session.model);
2350
2588
  // Herdr integration: report gjc lifecycle state when running in a Herdr pane.
2351
2589
  installHerdrReporter(listener => session.subscribe(listener));
@@ -1041,6 +1041,29 @@ function redactSecrets(input: string): string {
1041
1041
  for (const pattern of patterns) {
1042
1042
  out = out.replace(pattern, "[REDACTED]");
1043
1043
  }
1044
+ return redactBearerAndBasicAssignments(out);
1045
+ }
1046
+
1047
+ /**
1048
+ * Bearer and Basic assignments have no vendor prefix, so the rules above miss
1049
+ * them. A following word with no digit, "+", "/", or "=" stays, which keeps
1050
+ * phrases such as "Basic authentication" and "the bearer of".
1051
+ */
1052
+ function bearerOrBasicValueIsCredential(value: string): boolean {
1053
+ if (value.length < 8) return false;
1054
+ if (value.includes("+") || value.includes("/") || value.includes("=")) return true;
1055
+ return value.length >= 12 && /[0-9]/.test(value);
1056
+ }
1057
+
1058
+ function redactBearerAndBasicAssignments(input: string): string {
1059
+ const labeled = /(["']?authorization["']?\s*[:=]\s*["']?)((?:bearer|basic)\s+["']?)([A-Za-z0-9._~+/=-]{8,})/gi;
1060
+ const bare = /\b((?:bearer|basic)\s+["']?)([A-Za-z0-9._~+/=-]{8,})/gi;
1061
+ let out = input.replace(labeled, (match, label: string, scheme: string, value: string) =>
1062
+ bearerOrBasicValueIsCredential(value) ? `${label}${scheme}[REDACTED]` : match,
1063
+ );
1064
+ out = out.replace(bare, (match, scheme: string, value: string) =>
1065
+ bearerOrBasicValueIsCredential(value) ? `${scheme}[REDACTED]` : match,
1066
+ );
1044
1067
  return out;
1045
1068
  }
1046
1069
 
@@ -1196,7 +1219,7 @@ async function resolveMemoryModel(options: {
1196
1219
  const { modelRegistry, session, fallbackRole } = options;
1197
1220
  const requestedModel = session.settings.getModelRole(fallbackRole) || session.settings.getModelRole("default");
1198
1221
  if (requestedModel) {
1199
- const resolved = resolveModelRoleValue(requestedModel, modelRegistry.getAll(), {
1222
+ const resolved = resolveModelRoleValue(requestedModel, modelRegistry.getAll(session.credentialSessionId), {
1200
1223
  settings: session.settings,
1201
1224
  matchPreferences: { usageOrder: session.settings.getStorage()?.getModelUsageOrder() },
1202
1225
  modelRegistry,
@@ -1210,7 +1233,7 @@ async function resolveMemoryModel(options: {
1210
1233
  // before lazy model resolution): prefer the most recently used model over
1211
1234
  // raw registry order. `getAll()[0]` can be a retired model the provider
1212
1235
  // now rejects with 404, which permanently fails consolidation jobs.
1213
- const allModels = modelRegistry.getAll();
1236
+ const allModels = modelRegistry.getAll(session.credentialSessionId);
1214
1237
  const usageOrder = session.settings.getStorage()?.getModelUsageOrder() ?? [];
1215
1238
  for (const usedKey of usageOrder) {
1216
1239
  const match = allModels.find(model => `${model.provider}/${model.id}` === usedKey);