@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,7 @@ import type { CompactionOutcome } from "@gajae-code/agent-core/compaction";
5
5
  import { type Model, modelsAreEqual } from "@gajae-code/ai/core";
6
6
  import { Container, type KeyId, Markdown, Spacer, Text } from "@gajae-code/tui";
7
7
  import { isEnoent, prompt } from "@gajae-code/utils";
8
- import { resolveLocalUrlToPath } from "../../internal-urls";
8
+ import { resolveLocalRoot, resolveLocalUrlToPath } from "../../internal-urls";
9
9
  import { humanizePlanTitle, type PlanApprovalDetails, resolvePlanTitle } from "../../plan-mode/approved-plan";
10
10
  import planModeApprovedPrompt from "../../prompts/system/plan-mode-approved.md" with { type: "text" };
11
11
  import planModeCompactInstructionsPrompt from "../../prompts/system/plan-mode-compact-instructions.md" with {
@@ -13,6 +13,7 @@ import planModeCompactInstructionsPrompt from "../../prompts/system/plan-mode-co
13
13
  };
14
14
  import type { AgentSession, TemporaryProviderSessionScope } from "../../session/agent-session";
15
15
  import type { SessionContext, SessionManager } from "../../session/session-manager";
16
+ import { writeFileAtomically } from "../../tools/atomic-file-write";
16
17
  import { normalizeLocalScheme } from "../../tools/path-utils";
17
18
  import { type ResolveToolDetails, runResolveInvocation } from "../../tools/resolve";
18
19
  import { ToolError } from "../../tools/tool-errors";
@@ -63,6 +64,7 @@ export class PlanModeController {
63
64
  #paused = false;
64
65
  #planFilePath: string | undefined;
65
66
  #previousTools: string[] | undefined;
67
+ #previousMCPToolNames: string[] | undefined;
66
68
  #previousModelState: { model: Model; thinkingLevel?: ThinkingLevel } | undefined;
67
69
  #pendingModelSwitch: { model: Model; thinkingLevel?: ThinkingLevel } | undefined;
68
70
  #providerSessionScope: TemporaryProviderSessionScope | undefined;
@@ -125,6 +127,7 @@ export class PlanModeController {
125
127
  const planFilePath = options?.planFilePath ?? "local://PLAN.md";
126
128
  const previousTools = this.ctx.session.getActiveToolNames();
127
129
  this.#previousTools = previousTools;
130
+ this.#previousMCPToolNames = this.ctx.session.getActiveMCPToolNames();
128
131
  this.#planFilePath = planFilePath;
129
132
  this.#enabled = true;
130
133
  const planTools = this.ctx.session.isExplicitEmptyToolSelection()
@@ -132,7 +135,20 @@ export class PlanModeController {
132
135
  : this.ctx.session.getToolByName("resolve")
133
136
  ? [...new Set([...previousTools, "resolve"])]
134
137
  : previousTools;
135
- await this.ctx.session.setActiveToolsByName(planTools);
138
+ try {
139
+ await this.ctx.session.setActiveToolsByName(planTools, {
140
+ explicit: false,
141
+ persistMCPSelection: false,
142
+ retryOnMcpCatalogChange: true,
143
+ });
144
+ } catch (error) {
145
+ this.#enabled = false;
146
+ this.#planFilePath = undefined;
147
+ this.#previousTools = undefined;
148
+ this.#previousMCPToolNames = undefined;
149
+ this.ctx.modeGate.exit("plan");
150
+ throw error;
151
+ }
136
152
  this.ctx.session.setPlanModeState({
137
153
  enabled: true,
138
154
  planFilePath,
@@ -151,7 +167,12 @@ export class PlanModeController {
151
167
  async exit(options?: { silent?: boolean; paused?: boolean }): Promise<void> {
152
168
  if (!this.#enabled) return;
153
169
  await this.ctx.session.abort({ timeoutMs: ABORT_TIMEOUT_MS });
154
- if (this.#previousTools !== undefined) await this.ctx.session.setActiveToolsByName(this.#previousTools);
170
+ if (this.#previousTools !== undefined)
171
+ await this.ctx.session.setActiveToolsByName(this.#restoreToolsWithCurrentMCPSelection(this.#previousTools), {
172
+ explicit: false,
173
+ persistMCPSelection: false,
174
+ retryOnMcpCatalogChange: true,
175
+ });
155
176
  if (this.#providerSessionScope && !this.ctx.session.isStreaming) {
156
177
  if (await this.ctx.session.restoreTemporaryProviderSessionScope(this.#providerSessionScope))
157
178
  this.#providerSessionScope = undefined;
@@ -175,6 +196,7 @@ export class PlanModeController {
175
196
  this.#paused = options?.paused ?? false;
176
197
  this.#planFilePath = undefined;
177
198
  this.#previousTools = undefined;
199
+ this.#previousMCPToolNames = undefined;
178
200
  this.#previousModelState = undefined;
179
201
  if (!this.#paused) this.ctx.modeGate.exit("plan");
180
202
  this.#updateStatus();
@@ -182,6 +204,16 @@ export class PlanModeController {
182
204
  if (!options?.silent) this.ctx.showStatus(this.#paused ? "Plan mode paused." : "Plan mode disabled.");
183
205
  }
184
206
 
207
+ #restoreToolsWithCurrentMCPSelection(previousTools: readonly string[]): string[] {
208
+ const previousMCPToolNames = new Set(this.#previousMCPToolNames ?? []);
209
+ return [
210
+ ...new Set([
211
+ ...previousTools.filter(name => !previousMCPToolNames.has(name)),
212
+ ...this.ctx.session.getActiveMCPToolNames(),
213
+ ]),
214
+ ];
215
+ }
216
+
185
217
  async flushPendingModelSwitch(): Promise<void> {
186
218
  const pending = this.#pendingModelSwitch;
187
219
  if (!pending) return;
@@ -338,13 +370,16 @@ export class PlanModeController {
338
370
  throw error;
339
371
  }
340
372
  }
373
+ #localProtocolOptions() {
374
+ return {
375
+ getArtifactsDir: () => this.ctx.sessionManager.getArtifactsDir(),
376
+ isManagedDestination: () => this.ctx.sessionManager.isManagedDestination(),
377
+ getSessionId: () => this.ctx.sessionManager.getSessionId(),
378
+ };
379
+ }
341
380
  #resolvePath(planFilePath: string): string {
342
381
  return planFilePath.startsWith("local:")
343
- ? resolveLocalUrlToPath(normalizeLocalScheme(planFilePath), {
344
- getArtifactsDir: () => this.ctx.sessionManager.getArtifactsDir(),
345
- isManagedDestination: () => this.ctx.sessionManager.isManagedDestination(),
346
- getSessionId: () => this.ctx.sessionManager.getSessionId(),
347
- })
382
+ ? resolveLocalUrlToPath(normalizeLocalScheme(planFilePath), this.#localProtocolOptions())
348
383
  : path.resolve(this.ctx.sessionManager.getCwd(), planFilePath);
349
384
  }
350
385
  async #finalizeApprovedPlan(planContent: string, planFilePath: string, finalPlanFilePath: string): Promise<void> {
@@ -420,7 +455,13 @@ export class PlanModeController {
420
455
  trimTrailingNewline: false,
421
456
  });
422
457
  if (result !== null) {
423
- await Bun.write(resolved, result);
458
+ // Lexical local:// checks do not see a symlink. Bind this save to the
459
+ // same local root so a link inside it cannot publish outside.
460
+ if (planFilePath.startsWith("local:")) {
461
+ await writeFileAtomically(resolved, result, {
462
+ trustBoundary: path.resolve(resolveLocalRoot(this.#localProtocolOptions())),
463
+ });
464
+ } else await Bun.write(resolved, result);
424
465
  this.ctx.showStatus("Plan updated in external editor.");
425
466
  }
426
467
  return result;
@@ -445,12 +486,17 @@ export class PlanModeController {
445
486
  },
446
487
  ): Promise<void> {
447
488
  await this.#finalizeApprovedPlan(planContent, options.planFilePath, options.finalPlanFilePath);
448
- const previousTools = this.#previousTools ?? this.ctx.session.getActiveToolNames();
489
+ const previousMCPToolNames = new Set(this.#previousMCPToolNames ?? this.ctx.session.getActiveMCPToolNames());
490
+ const previousNonMCPToolNames = (this.#previousTools ?? this.ctx.session.getActiveToolNames()).filter(
491
+ name => !previousMCPToolNames.has(name),
492
+ );
449
493
  if (options.compactBeforeExecute) this.ctx.session.markPlanCompactAbortPending();
450
494
  let sessionSwitchCompleted = true;
451
495
  let compactOutcome: CompactionOutcome | undefined;
496
+ let mcpToolNamesBeforeSessionSwitch: string[] = [];
452
497
  try {
453
498
  await this.exit({ silent: true });
499
+ mcpToolNamesBeforeSessionSwitch = this.ctx.session.getActiveMCPToolNames();
454
500
  if (!options.preserveContext) {
455
501
  sessionSwitchCompleted = await this.ctx.handleClearCommand();
456
502
  if (sessionSwitchCompleted)
@@ -478,7 +524,16 @@ export class PlanModeController {
478
524
  } finally {
479
525
  this.ctx.session.clearPlanCompactAbortPending();
480
526
  }
481
- if (previousTools.length) await this.ctx.session.setActiveToolsByName(previousTools);
527
+ const currentMCPToolNames = this.ctx.session.getActiveMCPToolNames();
528
+ const restoredMCPToolNames = this.ctx.session.hasMCPToolSelectionAuthority()
529
+ ? currentMCPToolNames
530
+ : [...mcpToolNamesBeforeSessionSwitch, ...currentMCPToolNames];
531
+ const restoredTools = [...new Set([...previousNonMCPToolNames, ...restoredMCPToolNames])];
532
+ if (restoredTools.length)
533
+ await this.ctx.session.setActiveToolsByName(restoredTools, {
534
+ explicit: false,
535
+ persistMCPSelection: !options.preserveContext && sessionSwitchCompleted,
536
+ });
482
537
  if (!sessionSwitchCompleted)
483
538
  return this.ctx.showWarning(
484
539
  "Plan approved, but the new session could not be created — execution was not dispatched.",
@@ -37,6 +37,7 @@ import {
37
37
  } from "../../runtime-mcp/smithery-registry";
38
38
  import type { MCPAuthConfig, MCPServerConfig } from "../../runtime-mcp/types";
39
39
  import type { OAuthCredential } from "../../session/auth-storage";
40
+ import { mcpTerminalLabel } from "../../slash-commands/helpers/mcp";
40
41
  import { shortenPath } from "../../tools/render-utils";
41
42
  import { openPath } from "../../utils/open";
42
43
  import { CommandPalette, type CommandPaletteEntry } from "../components/command-palette";
@@ -1150,7 +1151,7 @@ export class MCPCommandController {
1150
1151
  : state === "connecting"
1151
1152
  ? theme.fg("muted", " ◌ connecting")
1152
1153
  : theme.fg("muted", " ○ not connected");
1153
- lines.push(` ${theme.fg("accent", name)}${status} ${theme.fg("dim", `[${type}]`)}`);
1154
+ lines.push(` ${theme.fg("accent", mcpTerminalLabel(name))}${status} ${theme.fg("dim", `[${type}]`)}`);
1154
1155
  }
1155
1156
  lines.push("");
1156
1157
  }
@@ -1173,7 +1174,7 @@ export class MCPCommandController {
1173
1174
  : state === "connecting"
1174
1175
  ? theme.fg("muted", " ◌ connecting")
1175
1176
  : theme.fg("muted", " ○ not connected");
1176
- lines.push(` ${theme.fg("accent", name)}${status} ${theme.fg("dim", `[${type}]`)}`);
1177
+ lines.push(` ${theme.fg("accent", mcpTerminalLabel(name))}${status} ${theme.fg("dim", `[${type}]`)}`);
1177
1178
  }
1178
1179
  lines.push("");
1179
1180
  }
@@ -1190,7 +1191,7 @@ export class MCPCommandController {
1190
1191
  : state === "connecting"
1191
1192
  ? theme.fg("muted", " ◌ connecting")
1192
1193
  : theme.fg("muted", " ○ not connected");
1193
- lines.push(` ${theme.fg("accent", name)}${status}`);
1194
+ lines.push(` ${theme.fg("accent", mcpTerminalLabel(name))}${status}`);
1194
1195
  }
1195
1196
  lines.push("");
1196
1197
  }
@@ -1201,7 +1202,7 @@ export class MCPCommandController {
1201
1202
  if (relevantDisabled.length > 0) {
1202
1203
  lines.push(theme.fg("accent", "Disabled") + theme.fg("muted", " (discovered servers):"));
1203
1204
  for (const name of relevantDisabled) {
1204
- lines.push(` ${theme.fg("accent", name)}${theme.fg("warning", " ◌ disabled")}`);
1205
+ lines.push(` ${theme.fg("accent", mcpTerminalLabel(name))}${theme.fg("warning", " ◌ disabled")}`);
1205
1206
  }
1206
1207
  lines.push("");
1207
1208
  }
@@ -1310,13 +1311,13 @@ export class MCPCommandController {
1310
1311
  "",
1311
1312
  theme.fg("success", `✓ Successfully connected to "${name}"`),
1312
1313
  "",
1313
- ` Server: ${connection.serverInfo.name} v${connection.serverInfo.version}`,
1314
+ ` Server: ${mcpTerminalLabel(connection.serverInfo.name)} v${mcpTerminalLabel(connection.serverInfo.version)}`,
1314
1315
  ` Tools: ${tools.length}`,
1315
1316
  ];
1316
1317
 
1317
1318
  if (tools.length > 0 && tools.length <= 10) {
1318
1319
  lines.push("", " Available tools:");
1319
- for (const tool of tools) lines.push(` • ${tool.name}`);
1320
+ for (const tool of tools) lines.push(` • ${mcpTerminalLabel(tool.name)}`);
1320
1321
  }
1321
1322
 
1322
1323
  lines.push("");
@@ -107,7 +107,10 @@ import { ensureTelegramDaemonRunningDetailed, resolveTelegramSetupPreflight } fr
107
107
  import { TelegramDaemonController } from "../../sdk/bus/telegram-daemon-control";
108
108
  import { runTelegramSetup, type TelegramSetupPreflight } from "../../sdk/bus/telegram-setup";
109
109
  import { clearPersistentPinForRemovedRows } from "../../session/account-inventory";
110
- import type { DefaultFallbackRuntimeState } from "../../session/agent-session";
110
+ import type {
111
+ AgentSessionProfileInstalledOverrideState,
112
+ DefaultFallbackRuntimeState,
113
+ } from "../../session/agent-session";
111
114
  import { CREDENTIAL_STORE_UNREADABLE_MESSAGE } from "../../session/credential-store-errors";
112
115
  import { type SessionInfo, SessionManager } from "../../session/session-manager";
113
116
  import { getTreeForInternalRead } from "../../session/session-manager-internal";
@@ -1214,6 +1217,7 @@ interface DefaultAssignmentRollbackSnapshot {
1214
1217
  profileOverride: SettingValue<"modelProfile.default"> | undefined;
1215
1218
  chain: { entries: readonly string[]; origin: string; identity?: string; explicitHead?: boolean } | undefined;
1216
1219
  activeProfile: string | undefined;
1220
+ profileInstalledOverrideState: AgentSessionProfileInstalledOverrideState | undefined;
1217
1221
  canonicalVariant: string | undefined;
1218
1222
  resumeDefaultSelector: string | undefined;
1219
1223
  fallbackRuntimeState: DefaultFallbackRuntimeState;
@@ -1248,6 +1252,10 @@ function rawAutoroutingState(current: RawSettings): {
1248
1252
 
1249
1253
  export class SelectorController {
1250
1254
  #smartRoutingInFlight?: Promise<unknown>;
1255
+ #modelSelector?: ModelSelectorComponent;
1256
+ #settingsSelector?: SettingsSelectorComponent;
1257
+ #statusLinePreview?: Partial<StatusLineSettings>;
1258
+ #statusLinePreviewActive = false;
1251
1259
  #transcriptViewerOpen = false;
1252
1260
  #transcriptViewer?: TranscriptViewerOverlay;
1253
1261
  #sessionsDashboardOpen = false;
@@ -1277,6 +1285,7 @@ export class SelectorController {
1277
1285
  profileOverride: this.ctx.settings.getOverride("modelProfile.default"),
1278
1286
  chain: this.ctx.session.getConfiguredModelChainState("default"),
1279
1287
  activeProfile: this.ctx.session.getActiveModelProfile?.(),
1288
+ profileInstalledOverrideState: this.ctx.session.getProfileInstalledOverrideState?.(),
1280
1289
  canonicalVariant: this.ctx.session.modelRegistry.getSessionCanonicalVariant?.(this.ctx.session.sessionId),
1281
1290
  resumeDefaultSelector: this.ctx.session.sessionManager.buildSessionContext().models.default,
1282
1291
  fallbackRuntimeState: this.ctx.session.getDefaultFallbackRuntimeState(),
@@ -1354,6 +1363,11 @@ export class SelectorController {
1354
1363
  restore(() => this.ctx.session.recordResumeDefaultModel(snapshot.resumeDefaultSelector));
1355
1364
  }
1356
1365
  restore(() => this.ctx.session.setActiveModelProfile?.(snapshot.activeProfile));
1366
+ if (snapshot.profileInstalledOverrideState) {
1367
+ restore(() =>
1368
+ this.ctx.session.restoreProfileInstalledOverrideState?.(snapshot.profileInstalledOverrideState!),
1369
+ );
1370
+ }
1357
1371
  try {
1358
1372
  await this.ctx.settings.flushOrThrow();
1359
1373
  } catch (rollbackError) {
@@ -1401,7 +1415,15 @@ export class SelectorController {
1401
1415
  */
1402
1416
  showSelector(create: (done: () => void) => { component: Component; focus: Component }): void {
1403
1417
  if (this.ctx.isStopped?.()) return;
1418
+ this.#modelSelector = undefined;
1419
+ this.#settingsSelector = undefined;
1420
+ this.#statusLinePreview = undefined;
1421
+ this.#statusLinePreviewActive = false;
1404
1422
  const done = () => {
1423
+ this.#modelSelector = undefined;
1424
+ this.#settingsSelector = undefined;
1425
+ this.#statusLinePreview = undefined;
1426
+ this.#statusLinePreviewActive = false;
1405
1427
  if (this.ctx.isStopped?.()) return;
1406
1428
  // Prefer the pet-aware composer restore (InteractiveMode.restoreComposer); fall back
1407
1429
  // to a plain editor swap for contexts that predate it (e.g. lightweight test doubles).
@@ -1424,6 +1446,28 @@ export class SelectorController {
1424
1446
  this.ctx.ui.requestRender();
1425
1447
  }
1426
1448
 
1449
+ /** Refresh accepted disk configuration without discarding an open preview. */
1450
+ refreshConfiguration(): void {
1451
+ if (this.ctx.isStopped?.()) return;
1452
+ this.#applyStatusLineSettings();
1453
+ this.#modelSelector?.refreshRoleAssignments({
1454
+ currentModel: this.ctx.session.model,
1455
+ currentThinkingLevel: this.ctx.session.thinkingLevel,
1456
+ activeModelProfile: this.ctx.session.getActiveModelProfile(),
1457
+ });
1458
+ this.#modelSelector?.refreshPresetProfiles();
1459
+ this.#settingsSelector?.refreshFromConfiguration([...this.ctx.session.modelRegistry.getModelProfiles().keys()]);
1460
+ this.ctx.updateEditorTopBorder();
1461
+ this.ctx.ui.requestRender();
1462
+ }
1463
+
1464
+ #applyStatusLineSettings(): void {
1465
+ this.ctx.statusLine.updateSettings({
1466
+ ...buildStatusLineSettings(this.ctx.settings),
1467
+ ...(this.#statusLinePreviewActive ? this.#statusLinePreview : undefined),
1468
+ });
1469
+ }
1470
+
1427
1471
  showCommandPalette(
1428
1472
  commands: SlashCommand[],
1429
1473
  actions: CommandPaletteAction[],
@@ -1990,11 +2034,19 @@ export class SelectorController {
1990
2034
  },
1991
2035
  onPetCommit: mode => this.ctx.commitPetPreviewMode(mode as PetMode),
1992
2036
  onStatusLinePreview: previewSettings => {
1993
- // Update status line with preview settings
1994
- this.ctx.statusLine.updateSettings({
1995
- ...buildStatusLineSettings(settings),
1996
- ...previewSettings,
1997
- });
2037
+ if (this.#statusLinePreviewActive) this.#statusLinePreview = previewSettings;
2038
+ this.#applyStatusLineSettings();
2039
+ this.ctx.updateEditorTopBorder();
2040
+ this.ctx.ui.requestRender();
2041
+ },
2042
+ onStatusLinePreviewStart: () => {
2043
+ this.#statusLinePreviewActive = true;
2044
+ this.#statusLinePreview = undefined;
2045
+ },
2046
+ onStatusLinePreviewEnd: () => {
2047
+ this.#statusLinePreviewActive = false;
2048
+ this.#statusLinePreview = undefined;
2049
+ this.#applyStatusLineSettings();
1998
2050
  this.ctx.updateEditorTopBorder();
1999
2051
  this.ctx.ui.requestRender();
2000
2052
  },
@@ -2068,6 +2120,7 @@ export class SelectorController {
2068
2120
  },
2069
2121
  notificationsOperations,
2070
2122
  );
2123
+ this.#settingsSelector = selector;
2071
2124
  return { component: selector, focus: selector };
2072
2125
  });
2073
2126
  });
@@ -2520,7 +2573,9 @@ export class SelectorController {
2520
2573
  if (issues.length > 0)
2521
2574
  throw new Error(issues.map(issue => `${issue.path || "setup"}: ${issue.detail}`).join("; "));
2522
2575
  const setup = structuredClone(draft);
2523
- const generated = generateTierChains(setup, CURATED_TIER_MAP, [...this.ctx.session.modelRegistry.getAll()]);
2576
+ const generated = generateTierChains(setup, CURATED_TIER_MAP, [
2577
+ ...this.ctx.session.modelRegistry.getAll(this.ctx.session.credentialSessionId),
2578
+ ]);
2524
2579
  const provenance: AutoroutingProvenance = {
2525
2580
  schema: 1,
2526
2581
  source: structuredClone(generated.sourceIdentity),
@@ -2849,6 +2904,7 @@ export class SelectorController {
2849
2904
  if (!materializedProfile) {
2850
2905
  for (const targetRole of targetRoles) {
2851
2906
  const target = GJC_MODEL_ASSIGNMENT_TARGETS[targetRole];
2907
+ this.ctx.session.markProfileRoleOverrideManual?.(target.settingsPath, targetRole);
2852
2908
  if (target.settingsPath === "modelRoles") {
2853
2909
  this.ctx.settings.setModelRole(targetRole, value);
2854
2910
  } else {
@@ -2952,6 +3008,7 @@ export class SelectorController {
2952
3008
  });
2953
3009
  if (!materializedProfile) {
2954
3010
  const target = GJC_MODEL_ASSIGNMENT_TARGETS[role];
3011
+ this.ctx.session.markProfileRoleOverrideManual?.(target.settingsPath, role);
2955
3012
  if (target.settingsPath === "modelRoles") {
2956
3013
  this.ctx.settings.setModelRole(role, value);
2957
3014
  } else {
@@ -2996,6 +3053,7 @@ export class SelectorController {
2996
3053
  smartRoutingOnly: options?.smartRoutingOnly,
2997
3054
  },
2998
3055
  );
3056
+ this.#modelSelector = modelSelector;
2999
3057
  return { component: modelSelector, focus: modelSelector };
3000
3058
  });
3001
3059
  }
@@ -3185,6 +3243,16 @@ export class SelectorController {
3185
3243
  // cancelled transition or roll it back to the parent's UI.
3186
3244
  branchFailure = error;
3187
3245
  }
3246
+ if (
3247
+ this.ctx.sessionManager.getSessionId() !== openedSessionId ||
3248
+ this.ctx.sessionManager.getSessionFile() !== openedSessionFile
3249
+ ) {
3250
+ try {
3251
+ await this.ctx.refreshSlashCommandState();
3252
+ } catch (error) {
3253
+ branchFailure ??= error;
3254
+ }
3255
+ }
3188
3256
 
3189
3257
  try {
3190
3258
  onCommitted?.();
@@ -3606,6 +3674,7 @@ export class SelectorController {
3606
3674
  if (option === `Use ${currentDefault.id}`) {
3607
3675
  try {
3608
3676
  await this.ctx.session.setModel(currentDefault);
3677
+ this.ctx.updateEditorChrome();
3609
3678
  this.ctx.showStatus(`Switched to ${currentDefault.provider}/${currentDefault.id}`);
3610
3679
  } catch (err) {
3611
3680
  this.ctx.showError(err instanceof Error ? err.message : String(err));
@@ -16,6 +16,8 @@ import {
16
16
  import { APP_NAME, adjustHsv, getAgentDir, getProjectDir, logger, postmortem, sanitizeText } from "@gajae-code/utils";
17
17
  import chalk from "chalk";
18
18
  import { AsyncJobManager } from "../async";
19
+ import { ConfigHotReloadWatcher } from "../config/config-hot-reload";
20
+ import { isConfigHotReloadSupported, isConfigHotReloadTrusted } from "../config/config-hot-reload-trust";
19
21
  import {
20
22
  type AppKeybinding,
21
23
  defaultMessageQueueKeysForPlatform,
@@ -37,7 +39,11 @@ import type {
37
39
  } from "../extensibility/extensions";
38
40
  import type { CompactOptions } from "../extensibility/extensions/types";
39
41
  import { resolveSkillSlashCommands, type Skill } from "../extensibility/skills";
40
- import { BUILTIN_SLASH_COMMANDS, loadSlashCommands } from "../extensibility/slash-commands";
42
+ import {
43
+ BUILTIN_SLASH_COMMANDS,
44
+ getBuiltinSlashCommandsForSession,
45
+ loadSlashCommands,
46
+ } from "../extensibility/slash-commands";
41
47
  import { getLspStartupWarningMessage, LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
42
48
  import {
43
49
  createStarReminderBeforeAgentStartContributor,
@@ -471,6 +477,8 @@ export class InteractiveMode implements InteractiveModeContext {
471
477
  #baseSlashCommands: SlashCommand[] = [];
472
478
  #resolvedSlashCommands: SlashCommand[] = [];
473
479
  #baseReservedSlashCommandNames: Set<string> = new Set();
480
+ #sessionBuiltinMcpVisible = false;
481
+ #slashCommandRefreshVersion = 0;
474
482
  #cleanupUnsubscribe?: () => void;
475
483
  #itermPetTransport?: ItermPetTransport;
476
484
 
@@ -573,6 +581,7 @@ export class InteractiveMode implements InteractiveModeContext {
573
581
  // paint, which renders the full session anyway.
574
582
  #initialTranscriptPainted = false;
575
583
  #stopListeners = new Set<() => void>();
584
+ #configHotReload?: ConfigHotReloadWatcher;
576
585
  #eventBus?: EventBus;
577
586
  #eventBusUnsubscribers: Array<() => void> = [];
578
587
  #welcomeComponent?: WelcomeComponent;
@@ -768,6 +777,8 @@ export class InteractiveMode implements InteractiveModeContext {
768
777
  this.hideThinkingBlock = settings.get("hideThinkingBlock");
769
778
 
770
779
  const builtinCommandNames = new Set(BUILTIN_SLASH_COMMANDS.map(c => c.name));
780
+ this.#sessionBuiltinMcpVisible = this.session.hasExactMcpControls;
781
+ const sessionBuiltinSlashCommands = getBuiltinSlashCommandsForSession(this.#sessionBuiltinMcpVisible);
771
782
  const activeProvider = this.session.model?.provider;
772
783
  const hookCommands: SlashCommand[] = (
773
784
  this.session.extensionRunner?.getRegisteredCommands(builtinCommandNames) ?? []
@@ -785,8 +796,13 @@ export class InteractiveMode implements InteractiveModeContext {
785
796
  description: `${loaded.command.description} (${loaded.source})`,
786
797
  }));
787
798
 
788
- this.#baseSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...hookCommands, ...customCommands];
789
- this.#baseReservedSlashCommandNames = new Set(this.#baseSlashCommands.map(command => command.name));
799
+ this.#baseSlashCommands = [...sessionBuiltinSlashCommands, ...hookCommands, ...customCommands];
800
+ // Hidden builtin metadata must still reserve its name from extensions and skills.
801
+ this.#baseReservedSlashCommandNames = new Set([
802
+ ...builtinCommandNames,
803
+ ...hookCommands.map(command => command.name),
804
+ ...customCommands.map(command => command.name),
805
+ ]);
790
806
  this.#resolvedSlashCommands = [...this.#baseSlashCommands, ...this.#rebuildSkillSlashCommands()];
791
807
 
792
808
  this.#uiHelpers = new UiHelpers(this);
@@ -999,6 +1015,8 @@ export class InteractiveMode implements InteractiveModeContext {
999
1015
  this.updateEditorChrome();
1000
1016
  this.#syncEditorMaxHeight();
1001
1017
  this.isInitialized = true;
1018
+ await this.#startConfigHotReload();
1019
+ if (this.#stopped) return;
1002
1020
  this.syncActivityIndicator();
1003
1021
  if (this.settings.get("tasksPane.defaultVisible")) this.showTasksPane();
1004
1022
  this.#syncIrcSidebarAvailabilityFromSettings();
@@ -1151,6 +1169,18 @@ export class InteractiveMode implements InteractiveModeContext {
1151
1169
 
1152
1170
  // Subscribe to agent events
1153
1171
  this.#subscribeToAgent();
1172
+ this.#eventBusUnsubscribers.push(
1173
+ this.session.subscribeSessionIdentityChanges(async () => {
1174
+ try {
1175
+ await this.refreshSlashCommandState();
1176
+ } catch (error) {
1177
+ if (this.#stopped) return;
1178
+ logger.warn("Failed to refresh slash-command state after extension session transition", {
1179
+ error: error instanceof Error ? error.message : String(error),
1180
+ });
1181
+ }
1182
+ }),
1183
+ );
1154
1184
 
1155
1185
  this.#eventBusUnsubscribers.push(
1156
1186
  this.session.subscribe(event => {
@@ -1215,9 +1245,12 @@ export class InteractiveMode implements InteractiveModeContext {
1215
1245
  /** Reload slash commands and autocomplete for the provided working directory. */
1216
1246
  async refreshSlashCommandState(cwd?: string): Promise<void> {
1217
1247
  if (this.#stopped) return;
1248
+ const refreshVersion = ++this.#slashCommandRefreshVersion;
1218
1249
  const basePath = cwd ?? this.sessionManager.getCwd();
1250
+ this.#refreshSessionBuiltinSlashCommandVisibility(basePath);
1219
1251
  const fileCommands = await loadSlashCommands({ cwd: basePath });
1220
- if (this.#stopped) return;
1252
+ if (this.#stopped || refreshVersion !== this.#slashCommandRefreshVersion) return;
1253
+ this.#refreshSessionBuiltinSlashCommandVisibility(basePath);
1221
1254
  const fileCommandNames = new Set(fileCommands.map(cmd => cmd.name));
1222
1255
  this.fileSlashCommands = fileCommandNames;
1223
1256
  const fileSlashCommands: SlashCommand[] = fileCommands.map(cmd => ({
@@ -1234,6 +1267,23 @@ export class InteractiveMode implements InteractiveModeContext {
1234
1267
  this.session.setSlashCommands(fileCommands);
1235
1268
  }
1236
1269
 
1270
+ #refreshSessionBuiltinSlashCommandVisibility(cwd: string): void {
1271
+ const hasExactMcpControls = this.session.hasExactMcpControls;
1272
+ if (hasExactMcpControls === this.#sessionBuiltinMcpVisible) return;
1273
+
1274
+ const previousSlashCommandTail = this.#resolvedSlashCommands.slice(this.#baseSlashCommands.length);
1275
+ const builtinCommandNames = new Set(BUILTIN_SLASH_COMMANDS.map(command => command.name));
1276
+ this.#baseSlashCommands = [
1277
+ ...getBuiltinSlashCommandsForSession(hasExactMcpControls),
1278
+ ...this.#baseSlashCommands.filter(command => !builtinCommandNames.has(command.name)),
1279
+ ];
1280
+ this.#sessionBuiltinMcpVisible = hasExactMcpControls;
1281
+ this.#resolvedSlashCommands = [...this.#baseSlashCommands, ...previousSlashCommandTail];
1282
+ this.editor.setAutocompleteProvider(
1283
+ this.#inputController.createAutocompleteProvider(this.#resolvedSlashCommands, cwd),
1284
+ );
1285
+ }
1286
+
1237
1287
  #rebuildSkillSlashCommands(fileCommandNames: ReadonlySet<string> = new Set()): SlashCommand[] {
1238
1288
  this.skillCommands.clear();
1239
1289
  if (!settings.get("skills.enableSkillCommands")) {
@@ -1475,6 +1525,12 @@ export class InteractiveMode implements InteractiveModeContext {
1475
1525
  }
1476
1526
  this.editor.setPlaceholder(this.#getComposerPlaceholder());
1477
1527
  this.#setComposerTopBorder();
1528
+ // Every live model switch (/model, profiles, cycling, fallback) refreshes the
1529
+ // editor chrome; keep the welcome model/provider pills on the same signal.
1530
+ this.#welcomeComponent?.setModel(
1531
+ this.session.model?.name ?? "Unknown",
1532
+ this.session.model?.provider ?? "Unknown",
1533
+ );
1478
1534
  this.ui.requestRender();
1479
1535
  }
1480
1536
 
@@ -1691,6 +1747,65 @@ export class InteractiveMode implements InteractiveModeContext {
1691
1747
  return this.#stopped;
1692
1748
  }
1693
1749
 
1750
+ async #startConfigHotReload(): Promise<void> {
1751
+ const paths = this.session.getConfigurationPaths();
1752
+ if (!paths) return;
1753
+ if (!isConfigHotReloadSupported()) {
1754
+ logger.debug("Configuration hot reload is unsupported on this platform", { platform: process.platform });
1755
+ return;
1756
+ }
1757
+ const trusted = await isConfigHotReloadTrusted(paths);
1758
+ if (this.#stopped || this.#isShuttingDown) return;
1759
+ if (!trusted) {
1760
+ this.showWarning(
1761
+ "Configuration watching could not start because directory ownership and privacy could not be verified.",
1762
+ );
1763
+ return;
1764
+ }
1765
+ const assertTrustedPaths = async (): Promise<void> => {
1766
+ if (await isConfigHotReloadTrusted(paths)) return;
1767
+ const error = new Error("Configuration directory ownership and privacy could not be verified");
1768
+ Object.assign(error, { code: "EACCES" });
1769
+ throw error;
1770
+ };
1771
+ const watcher = new ConfigHotReloadWatcher({
1772
+ onValidate: async candidate => {
1773
+ await assertTrustedPaths();
1774
+ await this.session.validateConfiguration(candidate);
1775
+ },
1776
+ onCandidate: async (candidate, signal) => {
1777
+ if (signal.aborted || this.#stopped || this.#isShuttingDown) return;
1778
+ await assertTrustedPaths();
1779
+ if (signal.aborted || this.#stopped || this.#isShuttingDown) return;
1780
+ const result = await this.session.reloadConfiguration(candidate, signal);
1781
+ if (signal.aborted || this.#stopped || this.#isShuttingDown || !result.applied) return;
1782
+ this.#selectorController.refreshConfiguration();
1783
+ this.#syncEditorMaxHeight();
1784
+ this.#syncIrcSidebarAvailabilityFromSettings();
1785
+ this.updateEditorChrome();
1786
+ this.showStatus(
1787
+ result.settingsChanged
1788
+ ? "Configuration reloaded. Startup-only services require a restart."
1789
+ : "Model configuration reloaded.",
1790
+ );
1791
+ this.ui.requestRender();
1792
+ },
1793
+ onError: error => {
1794
+ if (!this.#stopped && !this.#isShuttingDown) {
1795
+ this.showWarning(`${error.message}. Repair the configuration file and save again.`);
1796
+ }
1797
+ },
1798
+ });
1799
+ this.#configHotReload = watcher;
1800
+ try {
1801
+ await watcher.start(paths);
1802
+ } catch {
1803
+ watcher.dispose();
1804
+ this.#configHotReload = undefined;
1805
+ this.showWarning("Configuration watching could not start. Restart after repairing file access.");
1806
+ }
1807
+ }
1808
+
1694
1809
  onStop(callback: () => void): () => void {
1695
1810
  if (this.#stopped) {
1696
1811
  callback();
@@ -1703,6 +1818,8 @@ export class InteractiveMode implements InteractiveModeContext {
1703
1818
  stop(): void {
1704
1819
  const wasInitialized = this.isInitialized;
1705
1820
  this.#stopped = true;
1821
+ this.#configHotReload?.dispose();
1822
+ this.#configHotReload = undefined;
1706
1823
  this.ui.setRenderPreparationLifecycleCallbacks(undefined);
1707
1824
  this.#inputController.discardDeferredSubmission();
1708
1825
  for (const listener of this.#stopListeners) {
@@ -1776,6 +1893,8 @@ export class InteractiveMode implements InteractiveModeContext {
1776
1893
  return;
1777
1894
  }
1778
1895
  this.#isShuttingDown = true;
1896
+ this.#configHotReload?.dispose();
1897
+ this.#configHotReload = undefined;
1779
1898
 
1780
1899
  // `/btw` owns the shared composer while its panel is open. Never persist a
1781
1900
  // side-chat draft or pending side-chat images into the main-session draft.
@@ -6,7 +6,7 @@ import type { InteractiveModeContext } from "./types";
6
6
  * Owns the ghost-text next-prompt prediction lifecycle for interactive mode.
7
7
  *
8
8
  * After each agent turn ends (and only when `promptSuggestions` is enabled
9
- * and the composer is empty), a smol-model prediction of the user's next
9
+ * and the composer is empty), a model prediction of the user's next
10
10
  * prompt is generated in the background. The result renders as dim ghost
11
11
  * text in the empty composer via the autocomplete provider's inline hint;
12
12
  * Tab accepts it, typing dismisses it, and a new turn clears it.
@@ -262,7 +262,11 @@ export async function initializeExtensions(session: AgentSession, options: Initi
262
262
  const slashIndex = selector.indexOf("/");
263
263
  const model =
264
264
  slashIndex > 0
265
- ? session.modelRegistry.find(selector.slice(0, slashIndex), selector.slice(slashIndex + 1))
265
+ ? session.modelRegistry.find(
266
+ selector.slice(0, slashIndex),
267
+ selector.slice(slashIndex + 1),
268
+ session.credentialSessionId,
269
+ )
266
270
  : undefined;
267
271
  if (!model || !thinkingLevel || thinkingLevel === ThinkingLevel.Inherit)
268
272
  throw Object.assign(new Error("model.set requires a valid model id and concrete thinkingLevel."), {