@gajae-code/coding-agent 0.18.12 → 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 (305) hide show
  1. package/CHANGELOG.md +467 -0
  2. package/dist/types/capability/rule.d.ts +7 -2
  3. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  4. package/dist/types/cli/args.d.ts +7 -2
  5. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  6. package/dist/types/cli/customize-doctor.d.ts +4 -3
  7. package/dist/types/cli/public-command-errors.d.ts +1 -0
  8. package/dist/types/cli/root-flags.d.ts +0 -12
  9. package/dist/types/cli/update-cli.d.ts +7 -2
  10. package/dist/types/cli-main.d.ts +0 -6
  11. package/dist/types/commands/launch.d.ts +0 -6
  12. package/dist/types/commands/migrate.d.ts +1 -1
  13. package/dist/types/commands/sdk.d.ts +8 -0
  14. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  15. package/dist/types/config/autorouting-tier-map.d.ts +23 -1
  16. package/dist/types/config/config-hot-reload.d.ts +5 -0
  17. package/dist/types/config/file-lock.d.ts +44 -2
  18. package/dist/types/config/model-profile-activation.d.ts +19 -5
  19. package/dist/types/config/model-registry.d.ts +16 -3
  20. package/dist/types/config/model-resolver.d.ts +5 -4
  21. package/dist/types/config/settings-schema.d.ts +11 -1
  22. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  23. package/dist/types/customization/import.d.ts +2 -0
  24. package/dist/types/customization/types.d.ts +1 -1
  25. package/dist/types/dap/session.d.ts +11 -2
  26. package/dist/types/discovery/helpers.d.ts +7 -2
  27. package/dist/types/edit/index.d.ts +5 -0
  28. package/dist/types/edit/modes/patch.d.ts +9 -0
  29. package/dist/types/eval/py/runtime.d.ts +9 -0
  30. package/dist/types/export/ttsr.d.ts +19 -0
  31. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  32. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  33. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  34. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  35. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  36. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  37. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  38. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  39. package/dist/types/hindsight/content.d.ts +5 -6
  40. package/dist/types/internal-urls/types.d.ts +2 -0
  41. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  42. package/dist/types/main.d.ts +23 -18
  43. package/dist/types/migrate/action-planner.d.ts +10 -1
  44. package/dist/types/migrate/adapters/codex.d.ts +1 -1
  45. package/dist/types/migrate/adapters/index.d.ts +35 -3
  46. package/dist/types/migrate/hook-module.d.ts +23 -0
  47. package/dist/types/migrate/report.d.ts +2 -1
  48. package/dist/types/migrate/types.d.ts +69 -2
  49. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  50. package/dist/types/modes/components/model-selector.d.ts +43 -0
  51. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  52. package/dist/types/modes/utils/host-status.d.ts +15 -11
  53. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  54. package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
  55. package/dist/types/sdk/broker/broker.d.ts +22 -1
  56. package/dist/types/sdk/broker/ensure.d.ts +5 -2
  57. package/dist/types/sdk/broker/hop.d.ts +9 -0
  58. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  59. package/dist/types/sdk/broker/session-index.d.ts +2 -0
  60. package/dist/types/sdk/broker/transport.d.ts +2 -0
  61. package/dist/types/sdk/bus/index.d.ts +21 -0
  62. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
  63. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  64. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  65. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  66. package/dist/types/sdk/host/events.d.ts +4 -0
  67. package/dist/types/sdk/host/host.d.ts +2 -1
  68. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  69. package/dist/types/sdk/host/session-runtime.d.ts +8 -8
  70. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  71. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  72. package/dist/types/sdk/prompt-status.d.ts +11 -4
  73. package/dist/types/session/agent-session.d.ts +50 -8
  74. package/dist/types/session/session-manager.d.ts +16 -0
  75. package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
  76. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  77. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  78. package/dist/types/system-prompt.d.ts +3 -2
  79. package/dist/types/task/role-body.d.ts +11 -0
  80. package/dist/types/tools/awaiting-input.d.ts +28 -0
  81. package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
  82. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  83. package/dist/types/tools/bash.d.ts +7 -0
  84. package/dist/types/tools/descriptors.d.ts +13 -2
  85. package/dist/types/tools/fetch.d.ts +6 -0
  86. package/dist/types/tools/irc.d.ts +5 -3
  87. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  88. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  89. package/dist/types/tools/write.d.ts +2 -0
  90. package/dist/types/utils/file-mentions.d.ts +5 -0
  91. package/dist/types/utils/image-limits.d.ts +7 -0
  92. package/dist/types/utils/image-loading.d.ts +0 -1
  93. package/dist/types/utils/open.d.ts +0 -1
  94. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  95. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  96. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  97. package/dist/types/utils/title-generator.d.ts +13 -3
  98. package/dist/types/web/search/providers/codex.d.ts +6 -7
  99. package/package.json +9 -8
  100. package/scripts/acp-conformance-agent.ts +12 -2
  101. package/scripts/generate-sdk-operation-inventory.ts +16 -0
  102. package/src/capability/fs.ts +31 -3
  103. package/src/capability/rule.ts +11 -2
  104. package/src/cli/append-system-prompt.ts +12 -0
  105. package/src/cli/args.ts +11 -7
  106. package/src/cli/customize-doctor-precedence.ts +31 -0
  107. package/src/cli/customize-doctor.ts +49 -39
  108. package/src/cli/fast-help.ts +0 -2
  109. package/src/cli/migrate-cli.ts +14 -3
  110. package/src/cli/public-command-errors.ts +2 -0
  111. package/src/cli/root-flags.ts +0 -2
  112. package/src/cli/update-cli.ts +247 -6
  113. package/src/commands/harness.ts +25 -15
  114. package/src/commands/migrate.ts +3 -3
  115. package/src/commands/sdk.ts +81 -11
  116. package/src/commit/agentic/agent.ts +6 -6
  117. package/src/commit/analysis/conventional.ts +14 -5
  118. package/src/commit/map-reduce/index.ts +7 -7
  119. package/src/commit/pipeline.ts +12 -12
  120. package/src/config/autorouting-tier-map.ts +20 -1
  121. package/src/config/config-file.ts +106 -3
  122. package/src/config/config-hot-reload.ts +8 -0
  123. package/src/config/file-lock.ts +1431 -304
  124. package/src/config/keybindings.ts +154 -7
  125. package/src/config/model-profile-activation.ts +177 -39
  126. package/src/config/model-registry.ts +380 -55
  127. package/src/config/model-resolver.ts +49 -21
  128. package/src/config/settings-schema.ts +13 -1
  129. package/src/config/settings.ts +2 -0
  130. package/src/coordinator-mcp/server.ts +37 -2
  131. package/src/crash/sanitize.ts +3 -1
  132. package/src/customization/import.ts +276 -7
  133. package/src/customization/inventory.ts +24 -8
  134. package/src/customization/types.ts +1 -0
  135. package/src/dap/client.ts +3 -2
  136. package/src/dap/session.ts +161 -36
  137. package/src/debug/crash-diagnostics.ts +87 -5
  138. package/src/debug/report-bundle.ts +71 -2
  139. package/src/discovery/builtin.ts +4 -3
  140. package/src/discovery/helpers.ts +24 -14
  141. package/src/edit/index.ts +191 -5
  142. package/src/edit/modes/patch.ts +27 -0
  143. package/src/eval/py/runtime.ts +44 -4
  144. package/src/exec/non-interactive-env.ts +0 -1
  145. package/src/export/html/index.ts +60 -20
  146. package/src/export/ttsr.ts +176 -1
  147. package/src/extensibility/extensions/loader.ts +2 -0
  148. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  149. package/src/extensibility/extensions/types.ts +5 -1
  150. package/src/extensibility/hooks/loader.ts +10 -2
  151. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  152. package/src/extensibility/slash-commands.ts +5 -0
  153. package/src/gjc-runtime/gc-runtime.ts +4 -1
  154. package/src/gjc-runtime/launch-tmux.ts +23 -13
  155. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  156. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  157. package/src/gjc-runtime/state-writer.ts +109 -5
  158. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  159. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  160. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  161. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  162. package/src/harness-control-plane/finalize.ts +11 -0
  163. package/src/harness-control-plane/receipt-spool.ts +105 -28
  164. package/src/harness-control-plane/storage.ts +124 -6
  165. package/src/hindsight/content.ts +116 -16
  166. package/src/hindsight/mental-models.ts +22 -10
  167. package/src/hindsight/state.ts +7 -4
  168. package/src/hooks/native-skill-hook.ts +3 -1
  169. package/src/internal-urls/artifact-protocol.ts +3 -1
  170. package/src/internal-urls/docs-index.generated.ts +14 -14
  171. package/src/internal-urls/local-protocol.ts +28 -3
  172. package/src/internal-urls/types.ts +2 -0
  173. package/src/lsp/client.ts +2 -1
  174. package/src/lsp/clients/biome-client.ts +2 -0
  175. package/src/lsp/clients/swiftlint-client.ts +2 -0
  176. package/src/main.ts +330 -92
  177. package/src/memories/index.ts +25 -2
  178. package/src/migrate/action-planner.ts +517 -2
  179. package/src/migrate/adapters/claude-code.ts +141 -3
  180. package/src/migrate/adapters/codex.ts +57 -5
  181. package/src/migrate/adapters/index.ts +240 -5
  182. package/src/migrate/adapters/opencode.ts +4 -0
  183. package/src/migrate/executor.ts +141 -3
  184. package/src/migrate/hook-module.ts +193 -0
  185. package/src/migrate/report.ts +45 -1
  186. package/src/migrate/types.ts +73 -3
  187. package/src/modes/acp/acp-agent.ts +10 -1
  188. package/src/modes/components/agent-dashboard.ts +20 -4
  189. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  190. package/src/modes/components/customization/import-wizard.ts +29 -3
  191. package/src/modes/components/model-selector.ts +157 -103
  192. package/src/modes/components/welcome.ts +3 -1
  193. package/src/modes/controllers/command-controller.ts +271 -16
  194. package/src/modes/controllers/event-controller.ts +1 -1
  195. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  196. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  197. package/src/modes/controllers/input-controller.ts +2 -0
  198. package/src/modes/controllers/plan-mode-controller.ts +49 -4
  199. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  200. package/src/modes/controllers/selector-controller.ts +14 -1
  201. package/src/modes/interactive-mode.ts +55 -4
  202. package/src/modes/prompt-suggestion-controller.ts +1 -1
  203. package/src/modes/runtime-init.ts +5 -1
  204. package/src/modes/utils/host-status.ts +21 -10
  205. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  206. package/src/prompts/system/irc-incoming.md +3 -1
  207. package/src/prompts/tools/irc.md +1 -1
  208. package/src/runtime-mcp/manager.ts +8 -3
  209. package/src/runtime-mcp/project-host-values.ts +12 -8
  210. package/src/runtime-mcp/tool-bridge.ts +68 -4
  211. package/src/runtime-mcp/transports/stdio.ts +2 -1
  212. package/src/sdk/broker/broker-exit.ts +2 -0
  213. package/src/sdk/broker/broker.ts +143 -4
  214. package/src/sdk/broker/ensure.ts +74 -6
  215. package/src/sdk/broker/hop.ts +13 -1
  216. package/src/sdk/broker/lifecycle-ledger.ts +3 -0
  217. package/src/sdk/broker/lifecycle.ts +102 -6
  218. package/src/sdk/broker/session-index.ts +29 -4
  219. package/src/sdk/broker/transport.ts +17 -1
  220. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  221. package/src/sdk/bus/index.ts +408 -14
  222. package/src/sdk/bus/session-control.ts +2 -0
  223. package/src/sdk/bus/slack-daemon.ts +105 -9
  224. package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
  225. package/src/sdk/bus/telegram-daemon.ts +10 -2
  226. package/src/sdk/bus/threaded-render.ts +20 -1
  227. package/src/sdk/cli/session-cli.ts +2 -0
  228. package/src/sdk/host/control/dispatch.ts +13 -1
  229. package/src/sdk/host/control/operations.ts +10 -1
  230. package/src/sdk/host/events.ts +67 -4
  231. package/src/sdk/host/host.ts +8 -2
  232. package/src/sdk/host/prompt-image-upload.ts +438 -0
  233. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  234. package/src/sdk/host/session-runtime.test.ts +1537 -24
  235. package/src/sdk/host/session-runtime.ts +332 -36
  236. package/src/sdk/host/surface-policy.ts +4 -0
  237. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  238. package/src/sdk/host/websocket-transport.ts +19 -7
  239. package/src/sdk/lifecycle/service.ts +14 -1
  240. package/src/sdk/prompt-status.ts +6 -4
  241. package/src/sdk/protocol/adapter-validation.ts +28 -1
  242. package/src/sdk/protocol/operation-inventory.generated.json +160 -0
  243. package/src/sdk/protocol/operation-registry.ts +33 -3
  244. package/src/sdk/session.ts +123 -36
  245. package/src/session/agent-session.ts +1614 -329
  246. package/src/session/history-storage.ts +182 -12
  247. package/src/session/internal/managed-session-scope.ts +47 -18
  248. package/src/session/internal/managed-session-storage.ts +71 -1
  249. package/src/session/session-manager.ts +90 -5
  250. package/src/session-import/redact.ts +53 -2
  251. package/src/skill-state/workflow-mutation-guard.ts +1498 -18
  252. package/src/slash-commands/builtin-registry.ts +6 -3
  253. package/src/slash-commands/helpers/mcp.ts +11 -3
  254. package/src/ssh/ssh-executor.ts +9 -2
  255. package/src/stt/recorder.ts +26 -9
  256. package/src/system-prompt.ts +61 -16
  257. package/src/task/executor.ts +12 -8
  258. package/src/task/role-body.ts +84 -0
  259. package/src/task/token-log-mode.test.ts +162 -0
  260. package/src/task/token-log.ts +91 -2
  261. package/src/tools/archive-reader.ts +650 -37
  262. package/src/tools/ask.ts +1 -0
  263. package/src/tools/ast-grep.ts +7 -2
  264. package/src/tools/auto-generated-guard.ts +12 -3
  265. package/src/tools/awaiting-input.ts +114 -0
  266. package/src/tools/bash-allowed-prefixes.ts +61 -1
  267. package/src/tools/bash-skill-urls.ts +58 -5
  268. package/src/tools/bash.ts +99 -26
  269. package/src/tools/browser/tab-worker.ts +39 -13
  270. package/src/tools/browser.ts +17 -7
  271. package/src/tools/cron.ts +10 -1
  272. package/src/tools/debug.ts +9 -2
  273. package/src/tools/descriptors.test.ts +4 -4
  274. package/src/tools/descriptors.ts +16 -4
  275. package/src/tools/eval.ts +15 -0
  276. package/src/tools/fetch.ts +90 -2
  277. package/src/tools/gh.ts +27 -12
  278. package/src/tools/image-gen.ts +20 -6
  279. package/src/tools/irc.ts +5 -3
  280. package/src/tools/monitor.ts +9 -1
  281. package/src/tools/output-meta.ts +51 -3
  282. package/src/tools/read.ts +9 -2
  283. package/src/tools/recipe/runner.ts +23 -12
  284. package/src/tools/render-utils.ts +2 -2
  285. package/src/tools/search.ts +3 -2
  286. package/src/tools/ssh.ts +274 -8
  287. package/src/tools/subagent.ts +10 -4
  288. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  289. package/src/tools/tool-catalog.generated.ts +1 -1
  290. package/src/tools/ultragoal-ask-guard.ts +13 -1
  291. package/src/tools/write.ts +7 -0
  292. package/src/tui/tree-list.ts +15 -1
  293. package/src/utils/commit-message-generator.ts +3 -2
  294. package/src/utils/external-editor.ts +21 -7
  295. package/src/utils/file-mentions.ts +46 -6
  296. package/src/utils/git.ts +38 -7
  297. package/src/utils/image-limits.ts +8 -0
  298. package/src/utils/image-loading.ts +1 -1
  299. package/src/utils/open.ts +73 -4
  300. package/src/utils/pasted-image-loading.ts +9 -8
  301. package/src/utils/prompt-suggestion.ts +5 -4
  302. package/src/utils/shell-snapshot.ts +9 -1
  303. package/src/utils/title-generator.ts +65 -20
  304. package/src/web/search/index.ts +2 -1
  305. package/src/web/search/providers/codex.ts +29 -32
@@ -59,6 +59,7 @@ function safe(text: string): string {
59
59
 
60
60
  const STATUS_COLORS: Record<InventoryRow["status"], (text: string) => string> = {
61
61
  enabled: text => theme.fg("success", text),
62
+ "stored-only": text => theme.fg("warning", text),
62
63
  disabled: text => theme.fg("muted", text),
63
64
  invalid: text => theme.fg("error", text),
64
65
  shadowed: text => theme.fg("warning", text),
@@ -281,6 +282,12 @@ export class CustomizationDashboard extends Container {
281
282
  async #toggleSelected(): Promise<void> {
282
283
  const row = this.#selectedRow();
283
284
  if (!row || !this.#guardScope(row)) return;
285
+ if (row.status === "stored-only") {
286
+ this.#flashStatus(
287
+ "project hooks are stored-only; import into Global .gjc or configure an explicit hook path to load them",
288
+ );
289
+ return;
290
+ }
284
291
  if (row.status === "invalid" || row.status === "shadowed" || row.status === "quarantined") {
285
292
  this.#flashStatus(`cannot toggle a ${row.status} entry; resolve its diagnostics first`);
286
293
  return;
@@ -313,20 +313,46 @@ export class ImportWizard extends Container {
313
313
  counts.set(entry.outcome, (counts.get(entry.outcome) ?? 0) + 1);
314
314
  }
315
315
  const summary = [...counts.entries()].map(([outcome, count]) => `${count} ${outcome}`).join(", ");
316
+ const storedOnlyProjectHooks =
317
+ this.#result.ok &&
318
+ this.#plan?.preview.destinationScope === "project" &&
319
+ this.#result.entries.some(
320
+ entry =>
321
+ entry.surface === "hooks" &&
322
+ (entry.outcome === "imported" || entry.outcome === "renamed" || entry.outcome === "overwritten"),
323
+ );
316
324
  this.#headerText.setText(
317
325
  this.#result.ok
318
326
  ? theme.bold(theme.fg("success", "Import complete"))
319
327
  : theme.bold(theme.fg("error", "Import failed — rolled back, no partial import")),
320
328
  );
321
329
  const failed = this.#result.entries.filter(e => e.outcome === "failed").slice(0, 5);
330
+ const completionMessages: string[] = [];
331
+ if (this.#result.ok) {
332
+ if (storedOnlyProjectHooks) {
333
+ completionMessages.push(
334
+ theme.fg(
335
+ "warning",
336
+ "Project-scope hooks are stored-only; ordinary sessions do not load repository hook modules.",
337
+ ),
338
+ theme.fg("muted", "Import hooks into Global .gjc or configure an explicit hook path to activate them."),
339
+ theme.fg(
340
+ "muted",
341
+ "Other imported entries take effect after the documented reload/new-session boundary.",
342
+ ),
343
+ );
344
+ } else {
345
+ completionMessages.push(
346
+ theme.fg("muted", "Imported entries take effect after the documented reload/new-session boundary."),
347
+ );
348
+ }
349
+ }
322
350
  this.#bodyText.setText(
323
351
  [
324
352
  summary,
325
353
  "",
326
354
  ...failed.map(e => theme.fg("error", ` ✗ ${e.surface}: ${safe(e.sourceName)} — ${safe(e.reason ?? "")}`)),
327
- ...(this.#result.ok
328
- ? [theme.fg("muted", "Imported entries take effect after the documented reload/new-session boundary.")]
329
- : []),
355
+ ...completionMessages,
330
356
  ].join("\n"),
331
357
  );
332
358
  this.#footerText.setText(theme.fg("muted", "enter/esc: close"));
@@ -9,6 +9,7 @@ import {
9
9
  import {
10
10
  Container,
11
11
  fuzzyFilter,
12
+ fuzzyMatch,
12
13
  getKeybindings,
13
14
  Input,
14
15
  matchesKey,
@@ -99,6 +100,150 @@ function computeModelRank(model: Model, roles: Record<string, RoleAssignment | u
99
100
  return roles.default && modelsAreEqual(roles.default.model, model) ? 0 : 1;
100
101
  }
101
102
 
103
+ /**
104
+ * Compute and assign fuzzy scores to model items based on a query.
105
+ * Scores are based on fuzzy matching against the model's searchable text.
106
+ */
107
+ function fuzzyTokensScore(query: string, text: string): number | undefined {
108
+ const tokens = query
109
+ .trim()
110
+ .split(/\s+/)
111
+ .filter(token => token.length > 0);
112
+ if (tokens.length === 0) return undefined;
113
+ let total = 0;
114
+ for (const token of tokens) {
115
+ const match = fuzzyMatch(token, text);
116
+ if (!match.matches) return undefined;
117
+ total += match.score;
118
+ }
119
+ return total;
120
+ }
121
+
122
+ export function assignFuzzyScoresToModels(items: ModelItem[], query: string): void {
123
+ if (!query.trim()) {
124
+ for (const item of items) item.fuzzyScore = undefined;
125
+ return;
126
+ }
127
+ for (const item of items) {
128
+ // Overwrite every item so a raw-query miss cannot retain an earlier score.
129
+ item.fuzzyScore = fuzzyTokensScore(query, `${item.id} ${item.provider}`);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Compute and assign fuzzy scores to canonical model items based on a query.
135
+ */
136
+ export function assignFuzzyScoresToCanonicalModels(items: CanonicalModelItem[], query: string): void {
137
+ if (!query.trim()) {
138
+ for (const item of items) item.fuzzyScore = undefined;
139
+ return;
140
+ }
141
+ for (const item of items) {
142
+ item.fuzzyScore = fuzzyTokensScore(query, item.searchText);
143
+ }
144
+ }
145
+
146
+ export interface RankedModelOrderContext {
147
+ roles: RoleAssignments;
148
+ mruIndex: ReadonlyMap<string, number>;
149
+ providerAuthStateById: ReadonlyMap<string, ProviderAuthState>;
150
+ }
151
+
152
+ const MODEL_DATE_RE = /-(\d{8})$/;
153
+ const MODEL_LATEST_RE = /-latest$/;
154
+
155
+ /** Ranking shared by the provider view: fuzzy quality, default, MRU, provider, recency. */
156
+ export function compareModelItemsForRanking(a: ModelItem, b: ModelItem, context: RankedModelOrderContext): number {
157
+ const { roles, mruIndex, providerAuthStateById } = context;
158
+ const aHasFuzzyScore = a.fuzzyScore !== undefined;
159
+ const bHasFuzzyScore = b.fuzzyScore !== undefined;
160
+ if (aHasFuzzyScore || bHasFuzzyScore) {
161
+ const aScore = a.fuzzyScore ?? Number.MAX_SAFE_INTEGER;
162
+ const bScore = b.fuzzyScore ?? Number.MAX_SAFE_INTEGER;
163
+ if (aScore !== bScore) return aScore - bScore;
164
+ // Ties keep the established default-first, then MRU/provider ordering.
165
+ }
166
+
167
+ const aRank = computeModelRank(a.model, roles);
168
+ const bRank = computeModelRank(b.model, roles);
169
+ if (aRank !== bRank) return aRank - bRank;
170
+
171
+ const aMru = mruIndex.get(a.selector) ?? Number.MAX_SAFE_INTEGER;
172
+ const bMru = mruIndex.get(b.selector) ?? Number.MAX_SAFE_INTEGER;
173
+ if (aMru !== bMru) return aMru - bMru;
174
+
175
+ const providerCmp = compareRankedProviders(
176
+ { id: a.provider, label: a.provider, authState: providerAuthStateById.get(a.provider) ?? "none" },
177
+ { id: b.provider, label: b.provider, authState: providerAuthStateById.get(b.provider) ?? "none" },
178
+ );
179
+ if (providerCmp !== 0) return providerCmp;
180
+
181
+ // Priority field (lower = better, e.g. OpenAI code backend priority values)
182
+ const aPri = a.model.priority ?? Number.MAX_SAFE_INTEGER;
183
+ const bPri = b.model.priority ?? Number.MAX_SAFE_INTEGER;
184
+ if (aPri !== bPri) return aPri - bPri;
185
+
186
+ // Version number descending (higher version = better model)
187
+ const aVer = extractVersionNumber(a.id);
188
+ const bVer = extractVersionNumber(b.id);
189
+ if (aVer !== bVer) return bVer - aVer;
190
+
191
+ const aIsLatest = MODEL_LATEST_RE.test(a.id);
192
+ const bIsLatest = MODEL_LATEST_RE.test(b.id);
193
+ const aDate = a.id.match(MODEL_DATE_RE)?.[1] ?? "";
194
+ const bDate = b.id.match(MODEL_DATE_RE)?.[1] ?? "";
195
+
196
+ const aHasRecency = aIsLatest || aDate !== "";
197
+ const bHasRecency = bIsLatest || bDate !== "";
198
+ if (aHasRecency !== bHasRecency) return aHasRecency ? -1 : 1;
199
+ if (!aHasRecency) return a.id.localeCompare(b.id);
200
+ if (aIsLatest !== bIsLatest) return aIsLatest ? -1 : 1;
201
+ if (aDate && bDate) return bDate.localeCompare(aDate);
202
+ return aIsLatest ? -1 : bIsLatest ? 1 : a.id.localeCompare(b.id);
203
+ }
204
+
205
+ /** Ranking shared by the canonical view: fuzzy quality, default, MRU, provider, then id. */
206
+ export function compareCanonicalModelItemsForRanking(
207
+ a: CanonicalModelItem,
208
+ b: CanonicalModelItem,
209
+ context: RankedModelOrderContext,
210
+ ): number {
211
+ const { roles, mruIndex, providerAuthStateById } = context;
212
+ const aHasFuzzyScore = a.fuzzyScore !== undefined;
213
+ const bHasFuzzyScore = b.fuzzyScore !== undefined;
214
+ if (aHasFuzzyScore || bHasFuzzyScore) {
215
+ const aScore = a.fuzzyScore ?? Number.MAX_SAFE_INTEGER;
216
+ const bScore = b.fuzzyScore ?? Number.MAX_SAFE_INTEGER;
217
+ if (aScore !== bScore) return aScore - bScore;
218
+ // Ties keep the established default-first, then MRU/provider ordering.
219
+ }
220
+
221
+ const aRank = computeModelRank(a.model, roles);
222
+ const bRank = computeModelRank(b.model, roles);
223
+ if (aRank !== bRank) return aRank - bRank;
224
+
225
+ const aKey = `${a.model.provider}/${a.model.id}`;
226
+ const bKey = `${b.model.provider}/${b.model.id}`;
227
+ const aMru = mruIndex.get(aKey) ?? Number.MAX_SAFE_INTEGER;
228
+ const bMru = mruIndex.get(bKey) ?? Number.MAX_SAFE_INTEGER;
229
+ if (aMru !== bMru) return aMru - bMru;
230
+
231
+ const providerCmp = compareRankedProviders(
232
+ {
233
+ id: a.model.provider,
234
+ label: a.model.provider,
235
+ authState: providerAuthStateById.get(a.model.provider) ?? "none",
236
+ },
237
+ {
238
+ id: b.model.provider,
239
+ label: b.model.provider,
240
+ authState: providerAuthStateById.get(b.model.provider) ?? "none",
241
+ },
242
+ );
243
+ if (providerCmp !== 0) return providerCmp;
244
+ return a.id.localeCompare(b.id);
245
+ }
246
+
102
247
  interface ModelItem {
103
248
  kind: "provider";
104
249
  provider: string;
@@ -107,6 +252,7 @@ interface ModelItem {
107
252
  selector: string;
108
253
  thinkingLevel?: ThinkingLevel;
109
254
  explicitThinkingLevel?: boolean;
255
+ fuzzyScore?: number;
110
256
  }
111
257
 
112
258
  interface CanonicalModelItem {
@@ -120,6 +266,7 @@ interface CanonicalModelItem {
120
266
  compactSearchText: string;
121
267
  thinkingLevel?: ThinkingLevel;
122
268
  explicitThinkingLevel?: boolean;
269
+ fuzzyScore?: number;
123
270
  }
124
271
 
125
272
  type ScopedModelItem = ScopedModelSelection;
@@ -646,7 +793,7 @@ export class ModelSelectorComponent extends Container {
646
793
  this.#configuredDefaultChain ?? normalizeModelSelectorValue(this.#settings.getModelRole("default"));
647
794
  if (configuredChain.length < 2) return false;
648
795
 
649
- const allModels = this.#modelRegistry.getAll();
796
+ const allModels = this.#modelRegistry.getAll(this.#authSessionId);
650
797
  const matchPreferences = { usageOrder: this.#settings.getStorage()?.getModelUsageOrder() };
651
798
  return configuredChain.slice(1).some(selector => {
652
799
  const resolved = resolveModelRoleValue(selector, allModels, {
@@ -662,7 +809,7 @@ export class ModelSelectorComponent extends Container {
662
809
 
663
810
  #loadRoleModels(): RoleAssignments {
664
811
  const roles: RoleAssignments = {};
665
- const allModels = this.#modelRegistry.getAll();
812
+ const allModels = this.#modelRegistry.getAll(this.#authSessionId);
666
813
  const matchPreferences = { usageOrder: this.#settings.getStorage()?.getModelUsageOrder() };
667
814
  const agentModelOverrides = this.#settings.get("task.agentModelOverrides");
668
815
  const activeProfile = this.#activeModelProfile
@@ -738,7 +885,6 @@ export class ModelSelectorComponent extends Container {
738
885
  }
739
886
 
740
887
  #sortModels(models: ModelItem[], roles: RoleAssignments = this.#roles): void {
741
- // Sort: default-tagged model first, then MRU, then provider ranking
742
888
  const mruOrder = this.#settings.getStorage()?.getModelUsageOrder() ?? [];
743
889
  const mruIndex = new Map(mruOrder.map((key, i) => [key, i]));
744
890
  const providerAuthStateById = new Map<string, ProviderAuthState>();
@@ -747,74 +893,7 @@ export class ModelSelectorComponent extends Container {
747
893
  providerAuthStateById.set(item.provider, this.#resolveProviderAuthState(item.provider));
748
894
  }
749
895
  }
750
-
751
- const modelRank = (item: ModelItem) => computeModelRank(item.model, roles);
752
-
753
- const dateRe = /-(\d{8})$/;
754
- const latestRe = /-latest$/;
755
-
756
- models.sort((a, b) => {
757
- const aKey = a.selector;
758
- const bKey = b.selector;
759
-
760
- const aRank = modelRank(a);
761
- const bRank = modelRank(b);
762
- if (aRank !== bRank) return aRank - bRank;
763
-
764
- // Then MRU order (models in mruIndex come before those not in it)
765
- const aMru = mruIndex.get(aKey) ?? Number.MAX_SAFE_INTEGER;
766
- const bMru = mruIndex.get(bKey) ?? Number.MAX_SAFE_INTEGER;
767
- if (aMru !== bMru) return aMru - bMru;
768
-
769
- // By provider, then recency within provider
770
- const providerCmp = compareRankedProviders(
771
- {
772
- id: a.provider,
773
- label: a.provider,
774
- authState: providerAuthStateById.get(a.provider) ?? "none",
775
- },
776
- {
777
- id: b.provider,
778
- label: b.provider,
779
- authState: providerAuthStateById.get(b.provider) ?? "none",
780
- },
781
- );
782
- if (providerCmp !== 0) return providerCmp;
783
-
784
- // Priority field (lower = better, e.g. OpenAI code backend priority values)
785
- const aPri = a.model.priority ?? Number.MAX_SAFE_INTEGER;
786
- const bPri = b.model.priority ?? Number.MAX_SAFE_INTEGER;
787
- if (aPri !== bPri) return aPri - bPri;
788
-
789
- // Version number descending (higher version = better model)
790
- const aVer = extractVersionNumber(a.id);
791
- const bVer = extractVersionNumber(b.id);
792
- if (aVer !== bVer) return bVer - aVer;
793
-
794
- const aIsLatest = latestRe.test(a.id);
795
- const bIsLatest = latestRe.test(b.id);
796
- const aDate = a.id.match(dateRe)?.[1] ?? "";
797
- const bDate = b.id.match(dateRe)?.[1] ?? "";
798
-
799
- // Both have dates or latest tags — sort by recency
800
- const aHasRecency = aIsLatest || aDate !== "";
801
- const bHasRecency = bIsLatest || bDate !== "";
802
-
803
- // Models with recency info come before those without
804
- if (aHasRecency !== bHasRecency) return aHasRecency ? -1 : 1;
805
-
806
- // If neither has recency info, fall back to alphabetical
807
- if (!aHasRecency) return a.id.localeCompare(b.id);
808
-
809
- // -latest always sorts first within recency group
810
- if (aIsLatest !== bIsLatest) return aIsLatest ? -1 : 1;
811
-
812
- // Both have dates — descending (newest first)
813
- if (aDate && bDate) return bDate.localeCompare(aDate);
814
-
815
- // One has date, other is latest — latest first
816
- return aIsLatest ? -1 : bIsLatest ? 1 : a.id.localeCompare(b.id);
817
- });
896
+ models.sort((a, b) => compareModelItemsForRanking(a, b, { roles, mruIndex, providerAuthStateById }));
818
897
  }
819
898
 
820
899
  #sortCanonicalModels(models: CanonicalModelItem[], roles: RoleAssignments = this.#roles): void {
@@ -826,34 +905,7 @@ export class ModelSelectorComponent extends Container {
826
905
  providerAuthStateById.set(item.model.provider, this.#resolveProviderAuthState(item.model.provider));
827
906
  }
828
907
  }
829
-
830
- const modelRank = (item: CanonicalModelItem) => computeModelRank(item.model, roles);
831
-
832
- models.sort((a, b) => {
833
- const aRank = modelRank(a);
834
- const bRank = modelRank(b);
835
- if (aRank !== bRank) return aRank - bRank;
836
-
837
- const aMru = mruIndex.get(`${a.model.provider}/${a.model.id}`) ?? Number.MAX_SAFE_INTEGER;
838
- const bMru = mruIndex.get(`${b.model.provider}/${b.model.id}`) ?? Number.MAX_SAFE_INTEGER;
839
- if (aMru !== bMru) return aMru - bMru;
840
-
841
- const providerCmp = compareRankedProviders(
842
- {
843
- id: a.model.provider,
844
- label: a.model.provider,
845
- authState: providerAuthStateById.get(a.model.provider) ?? "none",
846
- },
847
- {
848
- id: b.model.provider,
849
- label: b.model.provider,
850
- authState: providerAuthStateById.get(b.model.provider) ?? "none",
851
- },
852
- );
853
- if (providerCmp !== 0) return providerCmp;
854
-
855
- return a.id.localeCompare(b.id);
856
- });
908
+ models.sort((a, b) => compareCanonicalModelItemsForRanking(a, b, { roles, mruIndex, providerAuthStateById }));
857
909
  }
858
910
 
859
911
  #buildScopedModelItems(): ModelItem[] {
@@ -869,7 +921,7 @@ export class ModelSelectorComponent extends Container {
869
921
  }
870
922
 
871
923
  #buildAvailableModelItems(): ModelItem[] {
872
- return this.#modelRegistry.getAvailable().map((model: Model) => ({
924
+ return this.#modelRegistry.getAvailable(this.#authSessionId).map((model: Model) => ({
873
925
  kind: "provider",
874
926
  provider: model.provider,
875
927
  id: model.id,
@@ -1205,10 +1257,12 @@ export class ModelSelectorComponent extends Container {
1205
1257
  ? alphaFiltered
1206
1258
  : baseCanonicalModels;
1207
1259
  const fuzzyMatches = fuzzyFilter(fuzzySource, query, ({ searchText }) => searchText);
1260
+ assignFuzzyScoresToCanonicalModels(fuzzyMatches, query);
1208
1261
  this.#sortCanonicalModels(fuzzyMatches);
1209
1262
  this.#filteredCanonicalModels = fuzzyMatches;
1210
1263
  } else {
1211
1264
  const fuzzyMatches = fuzzyFilter(baseModels, query, ({ id, provider }) => `${id} ${provider}`);
1265
+ assignFuzzyScoresToModels(fuzzyMatches, query);
1212
1266
  this.#sortModels(fuzzyMatches);
1213
1267
  this.#filteredModels = fuzzyMatches;
1214
1268
  }
@@ -1256,7 +1310,7 @@ export class ModelSelectorComponent extends Container {
1256
1310
  if (isInheritedRoleSelector(configuredSelector)) continue;
1257
1311
  const selectors = resolveConfiguredModelPatterns(configuredSelector, this.#settings);
1258
1312
  for (const selector of selectors.length > 0 ? selectors : [configuredSelector]) {
1259
- const resolved = resolveModelRoleValue(selector, this.#modelRegistry.getAll(), {
1313
+ const resolved = resolveModelRoleValue(selector, this.#modelRegistry.getAll(this.#authSessionId), {
1260
1314
  settings: this.#settings,
1261
1315
  matchPreferences: { usageOrder: this.#settings.getStorage()?.getModelUsageOrder() },
1262
1316
  modelRegistry: this.#modelRegistry,
@@ -1369,7 +1423,7 @@ export class ModelSelectorComponent extends Container {
1369
1423
  }
1370
1424
 
1371
1425
  #getProfileAvailableModels(): Model[] {
1372
- return this.#modelRegistry.getAvailable();
1426
+ return this.#modelRegistry.getAvailable(this.#authSessionId);
1373
1427
  }
1374
1428
 
1375
1429
  #createProfileResolutionRegistry(availableModels: readonly Model[]) {
@@ -2108,7 +2162,7 @@ export class ModelSelectorComponent extends Container {
2108
2162
  for (const role of PROFILE_ROLE_PREVIEW_ORDER) {
2109
2163
  const selector = profile.modelMapping[role];
2110
2164
  if (!selector) continue;
2111
- const resolved = resolveModelRoleValue(selector, this.#modelRegistry.getAll(), {
2165
+ const resolved = resolveModelRoleValue(selector, this.#modelRegistry.getAll(this.#authSessionId), {
2112
2166
  settings: this.#settings,
2113
2167
  matchPreferences: { usageOrder: this.#settings.getStorage()?.getModelUsageOrder() },
2114
2168
  modelRegistry: this.#modelRegistry,
@@ -3,6 +3,7 @@ import { APP_NAME } from "@gajae-code/utils";
3
3
  import { formatBuildLabel } from "../../build-metadata";
4
4
  import { formatKeyHint, type KeyDisplayContext } from "../../config/keybindings";
5
5
  import { type ThemeColor, theme } from "../../modes/theme/theme";
6
+ import { sanitizeStatusText } from "../shared";
6
7
 
7
8
  export interface RecentSession {
8
9
  name: string;
@@ -536,7 +537,8 @@ export class WelcomeComponent implements Component {
536
537
  }
537
538
 
538
539
  #pill(icon: string, text: string, color: ThemeColor): string {
539
- return `${theme.fg("borderMuted", "[")} ${theme.fg(color, icon)} ${theme.fg("muted", text)} ${theme.fg(
540
+ const safeText = sanitizeStatusText(text);
541
+ return `${theme.fg("borderMuted", "[")} ${theme.fg(color, icon)} ${theme.fg("muted", safeText)} ${theme.fg(
540
542
  "borderMuted",
541
543
  "]",
542
544
  )}`;