@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
@@ -5,7 +5,8 @@ import { CompactionCancelledError, type CompactionOutcome } from "@gajae-code/ag
5
5
  import { getEnvApiKey, type ToolCall, type UsageLimit, type UsageReport } from "@gajae-code/ai/core";
6
6
  import type { ProviderDetails } from "@gajae-code/ai/provider-details";
7
7
  import { type Keybinding, Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@gajae-code/tui";
8
- import { formatBytes, formatDuration, Snowflake, setProjectDir } from "@gajae-code/utils";
8
+ import { formatBytes, formatDuration, setProjectDir } from "@gajae-code/utils";
9
+ import { canonicalEnvKey, projectEnvSnapshot } from "@gajae-code/utils/env-file";
9
10
  import { resolveAppendOnlyMode } from "../../append-only-mode";
10
11
  import { jobElapsedMs } from "../../async";
11
12
  import { reset as resetCapabilities } from "../../capability";
@@ -47,6 +48,7 @@ import {
47
48
  buildConversationTitleInput,
48
49
  generateSessionTitle,
49
50
  setSessionTerminalTitle,
51
+ titleInputObfuscator,
50
52
  } from "../../utils/title-generator";
51
53
  import { addChatChild, prepareTranscriptRebuild, syncPendingExecutionComponents } from "../utils/ui-helpers";
52
54
 
@@ -107,6 +109,266 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
107
109
  ].join("\n");
108
110
  }
109
111
 
112
+ const GH_SHARE_TRUST_KEYS = [
113
+ "GH_HOST",
114
+ "GH_CONFIG_DIR",
115
+ "GH_TOKEN",
116
+ "GITHUB_TOKEN",
117
+ "GH_ENTERPRISE_TOKEN",
118
+ "GITHUB_ENTERPRISE_TOKEN",
119
+ "XDG_CONFIG_HOME",
120
+ "HOME",
121
+ "USERPROFILE",
122
+ "APPDATA",
123
+ "BASH_ENV",
124
+ "BUN_OPTIONS",
125
+ "ENV",
126
+ "ZDOTDIR",
127
+ "NODE_OPTIONS",
128
+ "NODE_PATH",
129
+ "PYTHONHOME",
130
+ "PYTHONPATH",
131
+ "PYTHONUSERBASE",
132
+ "PYTHONSTARTUP",
133
+ "JULIA_DEPOT_PATH",
134
+ "JULIA_LOAD_PATH",
135
+ "JULIA_PROJECT",
136
+ "TCL_LIBRARY",
137
+ "TCLLIBPATH",
138
+ "LUA_INIT",
139
+ "LUA_INIT_5_1",
140
+ "LUA_INIT_5_2",
141
+ "LUA_INIT_5_3",
142
+ "LUA_INIT_5_4",
143
+ "LUA_INIT_5_5",
144
+ "LUA_PATH",
145
+ "LUA_CPATH",
146
+ "LUA_PATH_5_1",
147
+ "LUA_PATH_5_2",
148
+ "LUA_PATH_5_3",
149
+ "LUA_PATH_5_4",
150
+ "LUA_PATH_5_5",
151
+ "LUA_CPATH_5_1",
152
+ "LUA_CPATH_5_2",
153
+ "LUA_CPATH_5_3",
154
+ "LUA_CPATH_5_4",
155
+ "LUA_CPATH_5_5",
156
+ "R_ENVIRON",
157
+ "R_ENVIRON_USER",
158
+ "R_PROFILE",
159
+ "R_PROFILE_USER",
160
+ "ELIXIR_ERL_OPTIONS",
161
+ "ERL_AFLAGS",
162
+ "ERL_FLAGS",
163
+ "ERL_ZFLAGS",
164
+ "BUNDLER_SETUP",
165
+ "BUNDLE_GEMFILE",
166
+ "GEM_HOME",
167
+ "GEM_PATH",
168
+ "RUBYGEMS_GEMDEPS",
169
+ "RUBYOPT",
170
+ "RUBYLIB",
171
+ "PERL5OPT",
172
+ "PERL5LIB",
173
+ "PERLLIB",
174
+ "JAVA_HOME",
175
+ "JAVA_TOOL_OPTIONS",
176
+ "JDK_JAVA_OPTIONS",
177
+ "_JAVA_OPTIONS",
178
+ "CLASSPATH",
179
+ "PHPRC",
180
+ "PHP_INI_SCAN_DIR",
181
+ "DOTNET_STARTUP_HOOKS",
182
+ "DOTNET_ROOT",
183
+ "DOTNET_ROOT_X86",
184
+ "DOTNET_ROOT_X64",
185
+ "DOTNET_ROOT_ARM",
186
+ "DOTNET_ROOT_ARM64",
187
+ "CORECLR_ENABLE_PROFILING",
188
+ "DOTNET_ENABLE_PROFILING",
189
+ "CORECLR_PROFILER",
190
+ "DOTNET_PROFILER",
191
+ "CORECLR_PROFILER_PATH",
192
+ "CORECLR_PROFILER_PATH_32",
193
+ "CORECLR_PROFILER_PATH_64",
194
+ "CORECLR_PROFILER_PATH_ARM32",
195
+ "CORECLR_PROFILER_PATH_ARM64",
196
+ "DOTNET_PROFILER_PATH",
197
+ "DOTNET_PROFILER_PATH_32",
198
+ "DOTNET_PROFILER_PATH_64",
199
+ "DOTNET_PROFILER_PATH_ARM32",
200
+ "DOTNET_PROFILER_PATH_ARM64",
201
+ "PATH",
202
+ "HTTP_PROXY",
203
+ "HTTPS_PROXY",
204
+ "ALL_PROXY",
205
+ "NO_PROXY",
206
+ "OPENSSL_CONF",
207
+ "OPENSSL_CONF_INCLUDE",
208
+ "OPENSSL_MODULES",
209
+ "OPENSSL_ENGINES",
210
+ "SSL_CERT_FILE",
211
+ "SSL_CERT_DIR",
212
+ "LD_PRELOAD",
213
+ "LD_LIBRARY_PATH",
214
+ "LD_AUDIT",
215
+ "DYLD_INSERT_LIBRARIES",
216
+ "DYLD_LIBRARY_PATH",
217
+ "DYLD_FRAMEWORK_PATH",
218
+ "DYLD_FALLBACK_LIBRARY_PATH",
219
+ "DYLD_FALLBACK_FRAMEWORK_PATH",
220
+ "DYLD_ROOT_PATH",
221
+ ] as const;
222
+ const GH_SHARE_TRUST_KEY_SET = new Set(GH_SHARE_TRUST_KEYS.map(name => name.toUpperCase()));
223
+
224
+ /**
225
+ * Project dotenv at import, before `/move` changes cwd. Those declarations are
226
+ * already in the process environment, and a later `setProjectDir` does not
227
+ * remove them.
228
+ */
229
+ const ghShareProjectSnapshot = projectEnvSnapshot();
230
+ const ghShareOperatorPath = process.env.PATH;
231
+ const ghShareLaunchRoot = path.resolve(process.cwd());
232
+
233
+ function isInsideDirectory(root: string, candidate: string): boolean {
234
+ const relative = path.relative(root, candidate);
235
+ return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
236
+ }
237
+
238
+ async function findCheckoutRoots(directory: string): Promise<string[] | undefined> {
239
+ const resolvedDirectory = await fs.realpath(directory).catch(() => undefined);
240
+ if (!resolvedDirectory) return undefined;
241
+ const roots: string[] = [];
242
+ let ancestor = resolvedDirectory;
243
+ while (true) {
244
+ try {
245
+ await fs.lstat(path.join(ancestor, ".git"));
246
+ roots.push(ancestor);
247
+ } catch (error) {
248
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") return undefined;
249
+ }
250
+ const parent = path.dirname(ancestor);
251
+ if (parent === ancestor) break;
252
+ ancestor = parent;
253
+ }
254
+ return roots;
255
+ }
256
+
257
+ function isInsideAnyDirectory(roots: readonly string[], candidate: string): boolean {
258
+ return roots.some(root => isInsideDirectory(root, candidate));
259
+ }
260
+
261
+ async function resolveGhShareExecutable(): Promise<{ executable: string; searchPath: string } | undefined> {
262
+ const operatorPath = ghShareOperatorPath;
263
+ const key = canonicalEnvKey("PATH");
264
+ const declared = ghShareProjectSnapshot.values[key];
265
+ if (
266
+ !operatorPath ||
267
+ (declared !== undefined && (ghShareProjectSnapshot.dynamic.has(key) || declared.trim() === operatorPath.trim()))
268
+ )
269
+ return undefined;
270
+ const checkoutRoots = new Set<string>();
271
+ for (const directory of new Set([ghShareLaunchRoot, path.resolve(process.cwd())])) {
272
+ const roots = await findCheckoutRoots(directory);
273
+ if (!roots) return undefined;
274
+ for (const root of roots) checkoutRoots.add(root);
275
+ }
276
+ const untrustedDirectories = [...checkoutRoots];
277
+ const trustedDirectories: string[] = [];
278
+ const seenDirectories = new Set<string>();
279
+ let executable: string | undefined;
280
+ for (const directory of operatorPath.split(path.delimiter)) {
281
+ if (!path.isAbsolute(directory)) continue;
282
+ const absoluteDirectory = path.resolve(directory);
283
+ if (isInsideAnyDirectory(untrustedDirectories, absoluteDirectory)) continue;
284
+ const realDirectory = await fs.realpath(absoluteDirectory).catch(() => undefined);
285
+ if (
286
+ !realDirectory ||
287
+ isInsideAnyDirectory(untrustedDirectories, realDirectory) ||
288
+ seenDirectories.has(realDirectory)
289
+ )
290
+ continue;
291
+ const candidate = Bun.which("gh", { PATH: realDirectory });
292
+ if (candidate) {
293
+ const absoluteCandidate = path.resolve(candidate);
294
+ if (isInsideAnyDirectory(untrustedDirectories, absoluteCandidate)) continue;
295
+ const realExecutable = await fs.realpath(absoluteCandidate).catch(() => undefined);
296
+ if (!realExecutable || isInsideAnyDirectory(untrustedDirectories, realExecutable)) continue;
297
+ executable ??= realExecutable;
298
+ }
299
+ seenDirectories.add(realDirectory);
300
+ trustedDirectories.push(realDirectory);
301
+ }
302
+ return executable ? { executable, searchPath: trustedDirectories.join(path.delimiter) } : undefined;
303
+ }
304
+
305
+ /**
306
+ * Environment for the default `/share` `gh` children. GitHub CLI routing,
307
+ * config, authentication, proxy, certificate, shell/runtime startup, executable-search,
308
+ * and dynamic loader overrides that match the project dotenv are omitted so a repository
309
+ * cannot redirect the upload, substitute the `gh` binary, authenticate with a
310
+ * project-provided token, or intercept its HTTPS request. The executable itself
311
+ * and its child search path use absolute operator PATH directories whose real
312
+ * paths are outside actual launch and active checkout roots. Operator values the project
313
+ * does not declare are kept.
314
+ */
315
+ function ghShareChildEnv(trustedPath: string, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
316
+ const child: NodeJS.ProcessEnv = { ...env };
317
+ for (const name of Object.keys(child)) {
318
+ if (!GH_SHARE_TRUST_KEY_SET.has(name.toUpperCase())) continue;
319
+ const raw = child[name];
320
+ if (typeof raw !== "string") continue;
321
+ const key = canonicalEnvKey(name);
322
+ const declared = ghShareProjectSnapshot.values[key];
323
+ if (declared === undefined) continue;
324
+ if (!(ghShareProjectSnapshot.dynamic.has(key) || declared.trim() === raw.trim())) continue;
325
+ delete child[name];
326
+ if (process.platform === "win32") {
327
+ for (const present of Object.keys(child)) {
328
+ if (canonicalEnvKey(present) === key) delete child[present];
329
+ }
330
+ }
331
+ }
332
+ const ghConfigDirKey = canonicalEnvKey("GH_CONFIG_DIR");
333
+ const xdgConfigHomeKey = canonicalEnvKey("XDG_CONFIG_HOME");
334
+ for (const name of Object.keys(child)) {
335
+ const key = canonicalEnvKey(name);
336
+ if (key !== ghConfigDirKey && key !== xdgConfigHomeKey) continue;
337
+ const value = child[name];
338
+ if (typeof value !== "string" || value.length === 0 || path.isAbsolute(value)) continue;
339
+ // `gh` children use the filesystem root as cwd to avoid project Bun
340
+ // preloads, so retain the launch-directory meaning of relative config paths.
341
+ child[name] = path.resolve(ghShareLaunchRoot, value);
342
+ }
343
+ const certFileKey = canonicalEnvKey("SSL_CERT_FILE");
344
+ const certDirKey = canonicalEnvKey("SSL_CERT_DIR");
345
+ for (const name of Object.keys(child)) {
346
+ const key = canonicalEnvKey(name);
347
+ const value = child[name];
348
+ if (typeof value !== "string" || value.length === 0) continue;
349
+ if (key === certFileKey && !path.isAbsolute(value)) {
350
+ // Certificate overrides retain their launch-directory meaning even
351
+ // though `gh` children run from the filesystem root.
352
+ child[name] = path.resolve(ghShareLaunchRoot, value);
353
+ } else if (key === certDirKey) {
354
+ child[name] = value
355
+ .split(path.delimiter)
356
+ .map(directory =>
357
+ directory.length > 0 && !path.isAbsolute(directory)
358
+ ? path.resolve(ghShareLaunchRoot, directory)
359
+ : directory,
360
+ )
361
+ .join(path.delimiter);
362
+ }
363
+ }
364
+ const pathKey = canonicalEnvKey("PATH");
365
+ for (const name of Object.keys(child)) {
366
+ if (canonicalEnvKey(name) === pathKey) delete child[name];
367
+ }
368
+ child.PATH = trustedPath;
369
+ return child;
370
+ }
371
+
110
372
  export class CommandController {
111
373
  constructor(private readonly ctx: InteractiveModeContext) {}
112
374
 
@@ -147,6 +409,7 @@ export class CommandController {
147
409
  }
148
410
 
149
411
  async handleDebugTranscriptCommand(): Promise<void> {
412
+ let tempDir: string | undefined;
150
413
  try {
151
414
  const width = Math.max(1, this.ctx.ui.terminal.columns);
152
415
  const renderedLines = this.ctx.chatContainer.render(width).map(line => replaceTabs(Bun.stripANSI(line)));
@@ -155,10 +418,22 @@ export class CommandController {
155
418
  this.ctx.showError("No messages to dump yet.");
156
419
  return;
157
420
  }
158
- const tmpPath = path.join(os.tmpdir(), `${Snowflake.next()}-tmp.txt`);
159
- await Bun.write(tmpPath, `${rendered}\n`);
421
+ // Random directory plus an exclusive create: a preexisting symlink under the
422
+ // shared temp directory is not opened, and the transcript is not left world-readable.
423
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "gjc-debug-transcript-"));
424
+ if (process.platform !== "win32") await fs.chmod(tempDir, 0o700);
425
+ const tmpPath = path.join(tempDir, "transcript.txt");
426
+ const handle = await fs.open(tmpPath, "wx", 0o600);
427
+ try {
428
+ await handle.writeFile(`${rendered}\n`);
429
+ } finally {
430
+ await handle.close();
431
+ }
432
+ if (process.platform !== "win32") await fs.chmod(tmpPath, 0o600);
160
433
  this.ctx.showStatus(`Debug transcript written to:\n${tmpPath}`);
434
+ tempDir = undefined;
161
435
  } catch (error: unknown) {
436
+ if (tempDir) await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
162
437
  this.ctx.showError(
163
438
  `Failed to write debug transcript: ${error instanceof Error ? error.message : "Unknown error"}`,
164
439
  );
@@ -242,9 +517,21 @@ export class CommandController {
242
517
  return;
243
518
  }
244
519
 
520
+ const ghShare = await resolveGhShareExecutable();
521
+ if (!ghShare) {
522
+ this.ctx.showError(
523
+ "GitHub CLI (gh) is not installed on the trusted operator PATH. Install it from https://cli.github.com/",
524
+ );
525
+ return;
526
+ }
527
+ const ghShareExecutable = ghShare.executable;
528
+ // Bun wrappers load project bunfig.toml preloads from their working directory.
529
+ const ghShareCwd = path.parse(ghShareLaunchRoot).root;
530
+ const ghEnv = ghShareChildEnv(ghShare.searchPath);
245
531
  try {
246
- const authProcess = Bun.spawn(["gh", "auth", "status"], {
247
- env: process.env,
532
+ const authProcess = Bun.spawn([ghShareExecutable, "auth", "status"], {
533
+ cwd: ghShareCwd,
534
+ env: ghEnv,
248
535
  stdin: "ignore",
249
536
  stdout: "ignore",
250
537
  stderr: "ignore",
@@ -277,8 +564,9 @@ export class CommandController {
277
564
 
278
565
  let cancellationRequested = false;
279
566
  try {
280
- const gistProcess = Bun.spawn(["gh", "gist", "create", "--public=false", tmpFile], {
281
- env: process.env,
567
+ const gistProcess = Bun.spawn([ghShareExecutable, "gist", "create", "--public=false", tmpFile], {
568
+ cwd: ghShareCwd,
569
+ env: ghEnv,
282
570
  stdin: "ignore",
283
571
  stdout: "pipe",
284
572
  stderr: "pipe",
@@ -1176,7 +1464,9 @@ export class CommandController {
1176
1464
  try {
1177
1465
  if (!title?.trim()) {
1178
1466
  const generation = beginSessionTitleGeneration(this.ctx.sessionManager);
1179
- const input = buildConversationTitleInput(this.ctx.session.messages);
1467
+ const activeCwd = this.ctx.sessionManager.getCwd();
1468
+ const obfuscateText = await titleInputObfuscator(this.ctx.settings, activeCwd);
1469
+ const input = buildConversationTitleInput(this.ctx.session.messages, obfuscateText);
1180
1470
  if (!input) {
1181
1471
  this.ctx.showError("Nothing to summarize yet — pass a title: /rename <title>");
1182
1472
  return;
@@ -1189,10 +1479,11 @@ export class CommandController {
1189
1479
  this.ctx.session.credentialSessionId,
1190
1480
  this.ctx.session.model,
1191
1481
  provider => this.ctx.session.agent.metadataForProvider(provider),
1482
+ activeCwd,
1192
1483
  );
1193
1484
  if (
1194
1485
  !isSessionTitleGenerationCurrent(this.ctx.sessionManager, generation) ||
1195
- buildConversationTitleInput(this.ctx.session.messages) !== input
1486
+ buildConversationTitleInput(this.ctx.session.messages, obfuscateText) !== input
1196
1487
  )
1197
1488
  return;
1198
1489
  if (!generated) {
@@ -716,7 +716,7 @@ export class EventController {
716
716
  ): Promise<void> {
717
717
  this.ctx.showStatus(`Fallback model: ${event.from} → ${event.to}`);
718
718
  this.ctx.statusLine.invalidate();
719
- this.ctx.ui.requestRender();
719
+ this.ctx.updateEditorBorderColor();
720
720
  }
721
721
 
722
722
  async #handleNotice(event: Extract<AgentSessionEvent, { type: "notice" }>): Promise<void> {
@@ -84,6 +84,7 @@ const EXTENSION_CONTEXT_MUTATIONS: ReadonlySet<PropertyKey> = new Set([
84
84
  "clearContext",
85
85
  "cycleModel",
86
86
  "setModelProfile",
87
+ "setDefaultModelProfile",
87
88
  "cycleThinkingLevel",
88
89
  "setQueueMode",
89
90
  "invokeSkill",
@@ -93,6 +94,14 @@ const EXTENSION_CONTEXT_MUTATIONS: ReadonlySet<PropertyKey> = new Set([
93
94
  "setSdkClientBridge",
94
95
  "sdkControl",
95
96
  ]);
97
+ const MODEL_CHANGE_MUTATIONS: ReadonlySet<PropertyKey> = new Set([
98
+ "setModel",
99
+ "setModelTemporaryForControl",
100
+ "cycleModel",
101
+ "setModelProfile",
102
+ "setDefaultModelProfile",
103
+ "sdkControl",
104
+ ]);
96
105
  const EXTENSION_COMMAND_MUTATIONS: ReadonlySet<PropertyKey> = new Set([
97
106
  "reload",
98
107
  "newSession",
@@ -250,7 +259,11 @@ export class ExtensionUiController {
250
259
  const slashIndex = selector.indexOf("/");
251
260
  const model =
252
261
  slashIndex > 0
253
- ? session.modelRegistry.find(selector.slice(0, slashIndex), selector.slice(slashIndex + 1))
262
+ ? session.modelRegistry.find(
263
+ selector.slice(0, slashIndex),
264
+ selector.slice(slashIndex + 1),
265
+ session.credentialSessionId,
266
+ )
254
267
  : undefined;
255
268
  if (!model || !thinkingLevel || thinkingLevel === ThinkingLevel.Inherit)
256
269
  throw Object.assign(new Error("model.set requires a valid model id and concrete thinkingLevel."), {
@@ -442,6 +455,14 @@ export class ExtensionUiController {
442
455
  return this.ctx.isStopped?.() === true;
443
456
  }
444
457
 
458
+ async #withModelChangeRefresh<T>(changeModel: () => Promise<T>): Promise<T> {
459
+ try {
460
+ return await changeModel();
461
+ } finally {
462
+ if (!this.#isStopped()) this.ctx.updateEditorChrome();
463
+ }
464
+ }
465
+
445
466
  #assertActive(): void {
446
467
  if (this.#isStopped()) throw Object.assign(new Error("Interactive mode stopped"), { code: "cancelled" });
447
468
  }
@@ -453,6 +474,9 @@ export class ExtensionUiController {
453
474
  if (!mutationNames.has(property) || typeof member !== "function") return member;
454
475
  return (...args: unknown[]) => {
455
476
  this.#assertActive();
477
+ if (MODEL_CHANGE_MUTATIONS.has(property)) {
478
+ return this.#withModelChangeRefresh(async () => Reflect.apply(member, current, args));
479
+ }
456
480
  return Reflect.apply(member, current, args);
457
481
  };
458
482
  },
@@ -646,6 +670,7 @@ export class ExtensionUiController {
646
670
  branch: async () => prohibitBrokerLifecycleOperation("session.branch"),
647
671
  navigateTree: async (targetId, navOptions) => {
648
672
  const result = await this.ctx.session.navigateTree(targetId, { summarize: navOptions?.summarize });
673
+ if (!result.cancelled) await this.ctx.session.notifySessionIdentityChanged();
649
674
  return { cancelled: result.cancelled };
650
675
  },
651
676
  switchSession: async () => prohibitBrokerLifecycleOperation("session.switch"),
@@ -653,6 +678,8 @@ export class ExtensionUiController {
653
678
  const previousSessionId = this.ctx.sessionManager.getSessionId();
654
679
  await this.ctx.session.reload();
655
680
  if (this.#isStopped()) return;
681
+ await this.ctx.session.notifySessionIdentityChanged();
682
+ if (this.#isStopped()) return;
656
683
  const sessionIdentityChanged = previousSessionId !== this.ctx.sessionManager.getSessionId();
657
684
  if (sessionIdentityChanged) this.ctx.resetIrcSidebarSession();
658
685
  this.ctx.rebuildInitialMessages(sessionIdentityChanged ? "replace-identity" : "reconcile-same-transcript");
@@ -876,6 +903,7 @@ export class ExtensionUiController {
876
903
  branch: async () => prohibitBrokerLifecycleOperation("session.branch"),
877
904
  navigateTree: async (targetId, navOptions) => {
878
905
  const result = await this.ctx.session.navigateTree(targetId, { summarize: navOptions?.summarize });
906
+ if (!result.cancelled) await this.ctx.session.notifySessionIdentityChanged();
879
907
  return { cancelled: result.cancelled };
880
908
  },
881
909
  switchSession: async () => prohibitBrokerLifecycleOperation("session.switch"),
@@ -886,6 +914,8 @@ export class ExtensionUiController {
886
914
  const previousSessionId = this.ctx.sessionManager.getSessionId();
887
915
  await this.ctx.session.reload();
888
916
  if (this.#isStopped()) return;
917
+ await this.ctx.session.notifySessionIdentityChanged();
918
+ if (this.#isStopped()) return;
889
919
  const sessionIdentityChanged = previousSessionId !== this.ctx.sessionManager.getSessionId();
890
920
  if (sessionIdentityChanged) this.ctx.resetIrcSidebarSession();
891
921
  this.ctx.rebuildInitialMessages(sessionIdentityChanged ? "replace-identity" : "reconcile-same-transcript");
@@ -54,6 +54,7 @@ export class GoalModeController {
54
54
  #enabled = false;
55
55
  #paused = false;
56
56
  #previousTools: string[] | undefined;
57
+ #previousMCPToolNames: string[] | undefined;
57
58
  #continuationTimer: NodeJS.Timeout | undefined;
58
59
  #turnHadToolCalls = false;
59
60
  #continuationTurnInFlight = false;
@@ -264,8 +265,13 @@ export class GoalModeController {
264
265
  if (this.#enabled || this.#paused) this.ctx.modeGate.enter("goal");
265
266
  if (restored?.goal) {
266
267
  this.#previousTools = this.ctx.session.getActiveToolNames();
268
+ this.#previousMCPToolNames = this.ctx.session.getActiveMCPToolNames();
267
269
  if (!this.ctx.session.isExplicitEmptyToolSelection()) {
268
- await this.ctx.session.setActiveToolsByName([...new Set([...this.#previousTools, "goal"])]);
270
+ await this.ctx.session.setActiveToolsByName([...new Set([...this.#previousTools, "goal"])], {
271
+ explicit: false,
272
+ persistMCPSelection: false,
273
+ retryOnMcpCatalogChange: true,
274
+ });
269
275
  }
270
276
  }
271
277
  this.#updateStatus();
@@ -316,16 +322,63 @@ export class GoalModeController {
316
322
  }): Promise<void> {
317
323
  if (this.#enabled) return;
318
324
  if (!this.ctx.modeGate.enter("goal")) return this.ctx.showWarning("Exit plan mode first.");
325
+ const previousGoalState = this.ctx.session.getGoalModeState();
319
326
  this.#previousTools = this.ctx.session.getActiveToolNames();
327
+ this.#previousMCPToolNames = this.ctx.session.getActiveMCPToolNames();
320
328
  this.#paused = false;
321
- const state = options.resume
322
- ? await this.ctx.session.goalRuntime.resumeGoal()
323
- : await this.ctx.session.goalRuntime.createGoal({
324
- objective: options.objective ?? "",
325
- provenance: options.provenance,
326
- });
327
- if (!this.ctx.session.isExplicitEmptyToolSelection()) {
328
- await this.ctx.session.setActiveToolsByName([...new Set([...this.#previousTools, "goal"])]);
329
+ let state: GoalModeState;
330
+ try {
331
+ state = options.resume
332
+ ? await this.ctx.session.goalRuntime.resumeGoal()
333
+ : await this.ctx.session.goalRuntime.createGoal({
334
+ objective: options.objective ?? "",
335
+ provenance: options.provenance,
336
+ });
337
+ if (!this.ctx.session.isExplicitEmptyToolSelection()) {
338
+ await this.ctx.session.setActiveToolsByName(
339
+ [...new Set([...this.#restoreToolsWithCurrentMCPSelection(this.#previousTools), "goal"])],
340
+ {
341
+ explicit: false,
342
+ persistMCPSelection: false,
343
+ retryOnMcpCatalogChange: true,
344
+ },
345
+ );
346
+ }
347
+ } catch (error) {
348
+ const currentGoalState = this.ctx.session.getGoalModeState();
349
+ const goalWasActivated =
350
+ currentGoalState?.enabled === true &&
351
+ currentGoalState.goal.status === "active" &&
352
+ (options.resume
353
+ ? previousGoalState?.goal.status === "paused" && currentGoalState.goal.id === previousGoalState.goal.id
354
+ : currentGoalState.goal.id !== previousGoalState?.goal.id);
355
+ let rollbackError: unknown;
356
+ if (goalWasActivated) {
357
+ try {
358
+ if (options.resume) await this.ctx.session.goalRuntime.pauseGoal();
359
+ else await this.ctx.session.goalRuntime.dropGoal();
360
+ } catch (goalRollbackError) {
361
+ rollbackError = goalRollbackError;
362
+ }
363
+ }
364
+ const rollbackState = this.ctx.session.getGoalModeState();
365
+ this.#enabled = rollbackState?.enabled === true;
366
+ this.#paused = rollbackState?.enabled !== true && rollbackState?.goal.status === "paused";
367
+ this.#previousTools = undefined;
368
+ this.#previousMCPToolNames = undefined;
369
+ this.#continuationTurnInFlight = false;
370
+ this.#resetContinuationSuppression();
371
+ this.cancelContinuation();
372
+ if (this.#enabled || this.#paused) this.ctx.modeGate.enter("goal");
373
+ else this.ctx.modeGate.exit("goal");
374
+ this.#updateStatus();
375
+ if (rollbackError !== undefined) {
376
+ throw new AggregateError(
377
+ [error, rollbackError],
378
+ "Goal-mode entry failed and goal state rollback also failed",
379
+ );
380
+ }
381
+ throw error;
329
382
  }
330
383
  this.ctx.session.setGoalModeState(state);
331
384
  this.#enabled = true;
@@ -344,7 +397,12 @@ export class GoalModeController {
344
397
  this.#previousTools &&
345
398
  options?.reason !== "dropped" &&
346
399
  (this.#enabled || options?.reason === "completed" || options?.paused);
347
- if (shouldRestoreTools && this.#previousTools) await this.ctx.session.setActiveToolsByName(this.#previousTools);
400
+ if (shouldRestoreTools && this.#previousTools)
401
+ await this.ctx.session.setActiveToolsByName(this.#restoreToolsWithCurrentMCPSelection(this.#previousTools), {
402
+ explicit: false,
403
+ persistMCPSelection: false,
404
+ retryOnMcpCatalogChange: true,
405
+ });
348
406
  const currentState = this.ctx.session.getGoalModeState();
349
407
  if (options?.reason === "completed") {
350
408
  this.ctx.session.setGoalModeState(undefined);
@@ -358,6 +416,7 @@ export class GoalModeController {
358
416
  this.#enabled = false;
359
417
  this.#paused = options?.paused ?? false;
360
418
  this.#previousTools = undefined;
419
+ this.#previousMCPToolNames = undefined;
361
420
  this.#continuationTurnInFlight = false;
362
421
  this.#resetContinuationSuppression();
363
422
  this.cancelContinuation();
@@ -375,6 +434,16 @@ export class GoalModeController {
375
434
  );
376
435
  }
377
436
 
437
+ #restoreToolsWithCurrentMCPSelection(previousTools: readonly string[]): string[] {
438
+ const previousMCPToolNames = new Set(this.#previousMCPToolNames ?? []);
439
+ return [
440
+ ...new Set([
441
+ ...previousTools.filter(name => !previousMCPToolNames.has(name)),
442
+ ...this.ctx.session.getActiveMCPToolNames(),
443
+ ]),
444
+ ];
445
+ }
446
+
378
447
  #getPausedState(): GoalModeState | undefined {
379
448
  const state = this.ctx.session.getGoalModeState();
380
449
  return state?.goal && !state.enabled && state.goal.status === "paused" ? state : undefined;
@@ -1642,6 +1642,7 @@ export class InputController {
1642
1642
  // Keep locally intercepted command arguments away from the title model.
1643
1643
  if (this.ctx.session.isLocallyHandledSlashCommand(text)) return;
1644
1644
  this.#autoTitleInFlightSessionId = sessionId;
1645
+ const activeCwd = this.ctx.sessionManager.getCwd();
1645
1646
  generateSessionTitle(
1646
1647
  text,
1647
1648
  this.ctx.session.modelRegistry,
@@ -1649,6 +1650,7 @@ export class InputController {
1649
1650
  this.ctx.session.credentialSessionId,
1650
1651
  this.ctx.session.model,
1651
1652
  provider => this.ctx.session.agent.metadataForProvider(provider),
1653
+ activeCwd,
1652
1654
  )
1653
1655
  .then(async title => {
1654
1656
  if (!title || this.ctx.sessionManager.getSessionId() !== sessionId) return;