@gajae-code/coding-agent 0.14.2 → 0.15.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 (345) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/dist/types/async/job-manager.d.ts +4 -1
  3. package/dist/types/autoresearch/dashboard.d.ts +29 -4
  4. package/dist/types/autoresearch/data-context.d.ts +25 -0
  5. package/dist/types/autoresearch/git.d.ts +65 -7
  6. package/dist/types/autoresearch/harness.d.ts +58 -0
  7. package/dist/types/autoresearch/prompts.d.ts +51 -0
  8. package/dist/types/autoresearch/runs.d.ts +173 -0
  9. package/dist/types/autoresearch/types.d.ts +9 -139
  10. package/dist/types/capability/types.d.ts +20 -0
  11. package/dist/types/cli/accounts-cli.d.ts +1 -0
  12. package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
  13. package/dist/types/config/autorouting-contract.d.ts +146 -0
  14. package/dist/types/config/autorouting-generator.d.ts +40 -0
  15. package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
  16. package/dist/types/config/autorouting.d.ts +35 -0
  17. package/dist/types/config/model-discovery-manager.d.ts +8 -1
  18. package/dist/types/config/model-registry.d.ts +30 -1
  19. package/dist/types/config/provider-selection-policy.d.ts +23 -0
  20. package/dist/types/config/settings-schema.d.ts +61 -3
  21. package/dist/types/config/settings.d.ts +3 -0
  22. package/dist/types/coordinator/contract.d.ts +1 -1
  23. package/dist/types/coordinator-mcp/durability.d.ts +29 -0
  24. package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
  26. package/dist/types/coordinator-mcp/server.d.ts +14 -0
  27. package/dist/types/crash/index-store.d.ts +3 -0
  28. package/dist/types/crash/record-loader.d.ts +2 -0
  29. package/dist/types/crash/upstream/relay.d.ts +5 -3
  30. package/dist/types/cursor.d.ts +12 -0
  31. package/dist/types/defaults/gjc-defaults.d.ts +35 -1
  32. package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
  33. package/dist/types/discovery/helpers.d.ts +1 -1
  34. package/dist/types/edit/index.d.ts +2 -1
  35. package/dist/types/eval/py/executor.d.ts +2 -0
  36. package/dist/types/extensibility/extensions/loader.d.ts +32 -0
  37. package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
  40. package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
  41. package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
  42. package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
  44. package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
  45. package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
  46. package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
  47. package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
  48. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
  49. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
  50. package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
  51. package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
  52. package/dist/types/hooks/skill-keywords.d.ts +1 -1
  53. package/dist/types/main.d.ts +0 -10
  54. package/dist/types/modes/acp/acp-agent.d.ts +13 -6
  55. package/dist/types/modes/components/model-selector.d.ts +14 -0
  56. package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
  57. package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
  58. package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
  59. package/dist/types/modes/interactive-mode.d.ts +3 -0
  60. package/dist/types/modes/types.d.ts +9 -0
  61. package/dist/types/rlm/artifacts.d.ts +4 -4
  62. package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
  63. package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
  64. package/dist/types/runtime-mcp/config.d.ts +6 -0
  65. package/dist/types/runtime-mcp/manager.d.ts +10 -0
  66. package/dist/types/sdk/acp/adapter.d.ts +0 -4
  67. package/dist/types/sdk/broker/broker.d.ts +1 -1
  68. package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
  69. package/dist/types/sdk/broker/session-index.d.ts +15 -0
  70. package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
  71. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
  72. package/dist/types/sdk/bus/index.d.ts +8 -0
  73. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
  74. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
  75. package/dist/types/sdk/client/client.d.ts +39 -4
  76. package/dist/types/sdk/host/host.d.ts +1 -0
  77. package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
  78. package/dist/types/sdk/host/model-pin.d.ts +29 -0
  79. package/dist/types/sdk/host/session-runtime.d.ts +2 -1
  80. package/dist/types/sdk/lifecycle-session.d.ts +13 -0
  81. package/dist/types/sdk/router/session-router.d.ts +27 -1
  82. package/dist/types/sdk/session.d.ts +11 -1
  83. package/dist/types/session/agent-session.d.ts +36 -6
  84. package/dist/types/session/artifacts.d.ts +16 -1
  85. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  86. package/dist/types/session/session-manager.d.ts +84 -2
  87. package/dist/types/session/session-staging-paths.d.ts +3 -0
  88. package/dist/types/skill-state/canonical-skills.d.ts +1 -1
  89. package/dist/types/skill-state/workflow-hud.d.ts +12 -21
  90. package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
  91. package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
  92. package/dist/types/system-prompt.d.ts +23 -0
  93. package/dist/types/task/executor.d.ts +35 -4
  94. package/dist/types/task/index.d.ts +17 -3
  95. package/dist/types/task/receipt.d.ts +3 -1
  96. package/dist/types/task/types.d.ts +144 -0
  97. package/dist/types/tools/atomic-file-write.d.ts +72 -0
  98. package/dist/types/tools/descriptors.d.ts +1 -0
  99. package/dist/types/tools/index.d.ts +36 -1
  100. package/dist/types/tools/move-session.d.ts +40 -0
  101. package/dist/types/tools/python.d.ts +12 -0
  102. package/dist/types/tools/resource-gc.d.ts +0 -3
  103. package/dist/types/tools/search.d.ts +8 -1
  104. package/dist/types/tools/skill.d.ts +9 -4
  105. package/dist/types/tools/subagent.d.ts +33 -3
  106. package/dist/types/utils/xml-escape.d.ts +2 -0
  107. package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
  108. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
  109. package/package.json +11 -12
  110. package/scripts/build-sdk-package-smoke.ts +7 -5
  111. package/scripts/check-autorouting-tier-map.ts +183 -0
  112. package/scripts/generate-sdk-operation-inventory.ts +12 -0
  113. package/scripts/generate-tool-catalog.ts +40 -5
  114. package/src/async/job-manager.ts +5 -2
  115. package/src/autoresearch/dashboard.ts +134 -314
  116. package/src/autoresearch/data-context.ts +35 -0
  117. package/src/autoresearch/git.ts +186 -26
  118. package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
  119. package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
  120. package/src/autoresearch/prompts/prompt.md +103 -0
  121. package/src/autoresearch/prompts.ts +126 -0
  122. package/src/autoresearch/runs.ts +708 -0
  123. package/src/autoresearch/types.ts +9 -150
  124. package/src/capability/index.ts +4 -4
  125. package/src/capability/types.ts +20 -0
  126. package/src/cli/accounts-cli.ts +21 -5
  127. package/src/cli/config-cli.ts +25 -1
  128. package/src/cli/fast-help.ts +1 -2
  129. package/src/cli/list-models.ts +5 -8
  130. package/src/cli/setup-cli.ts +8 -0
  131. package/src/cli.ts +2 -21
  132. package/src/commands/autoresearch.ts +33 -0
  133. package/src/commands/coordinator.ts +15 -0
  134. package/src/commands/launch.ts +2 -4
  135. package/src/commands/quick-lane.ts +1 -1
  136. package/src/commands/sdk.ts +24 -0
  137. package/src/commands/state.ts +1 -1
  138. package/src/config/autorouting-contract.ts +474 -0
  139. package/src/config/autorouting-generator.ts +238 -0
  140. package/src/config/autorouting-tier-map.ts +6108 -0
  141. package/src/config/autorouting.ts +120 -0
  142. package/src/config/model-discovery-manager.ts +42 -11
  143. package/src/config/model-registry.ts +348 -68
  144. package/src/config/provider-selection-policy.ts +60 -9
  145. package/src/config/settings-schema.ts +195 -6
  146. package/src/config/settings.ts +62 -1
  147. package/src/config.ts +2 -3
  148. package/src/coordinator/contract.ts +0 -1
  149. package/src/coordinator-mcp/codex-handoff.ts +68 -42
  150. package/src/coordinator-mcp/durability.ts +200 -0
  151. package/src/coordinator-mcp/event-webhook.ts +278 -0
  152. package/src/coordinator-mcp/question-state.ts +109 -46
  153. package/src/coordinator-mcp/server.ts +932 -504
  154. package/src/crash/index-store.ts +126 -22
  155. package/src/crash/record-loader.ts +7 -0
  156. package/src/crash/upstream/relay.ts +82 -34
  157. package/src/cursor.ts +84 -1
  158. package/src/debug/index.ts +26 -0
  159. package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
  160. package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
  161. package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
  162. package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
  163. package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
  164. package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
  165. package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
  166. package/src/defaults/gjc-defaults.ts +89 -2
  167. package/src/defaults/gjc-skills.generated.ts +24 -17
  168. package/src/discovery/builtin.ts +18 -5
  169. package/src/discovery/helpers.ts +6 -6
  170. package/src/edit/index.ts +5 -2
  171. package/src/eval/py/executor.ts +8 -0
  172. package/src/extensibility/extensions/loader.ts +135 -13
  173. package/src/extensibility/extensions/wrapper.ts +1 -2
  174. package/src/extensibility/gjc-plugins/schema.ts +1 -1
  175. package/src/extensibility/runtime-skill-discovery.ts +2 -2
  176. package/src/extensibility/skill-management.ts +2 -3
  177. package/src/extensibility/skills.ts +16 -8
  178. package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
  179. package/src/gjc-runtime/gc-render.ts +0 -1
  180. package/src/gjc-runtime/gc-runtime.ts +1 -11
  181. package/src/gjc-runtime/launch-tmux.ts +3 -3
  182. package/src/gjc-runtime/psmux-detect.ts +2 -2
  183. package/src/gjc-runtime/python-transcript.ts +58 -0
  184. package/src/gjc-runtime/ralplan-runtime.ts +6 -18
  185. package/src/gjc-runtime/session-layout.ts +18 -7
  186. package/src/gjc-runtime/session-state-sidecar.ts +58 -9
  187. package/src/gjc-runtime/state-renderer.ts +0 -38
  188. package/src/gjc-runtime/state-runtime.ts +43 -22
  189. package/src/gjc-runtime/state-schema.ts +1 -1
  190. package/src/gjc-runtime/tmux-common.ts +7 -7
  191. package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
  192. package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
  193. package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
  194. package/src/gjc-runtime/workflow-command-ref.ts +15 -11
  195. package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
  196. package/src/gjc-runtime/workflow-manifest.ts +43 -83
  197. package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
  198. package/src/hooks/skill-keywords.ts +3 -3
  199. package/src/hooks/skill-state.ts +2 -2
  200. package/src/internal-urls/docs-index.generated.ts +26 -24
  201. package/src/lsp/index.ts +25 -5
  202. package/src/main.ts +2 -27
  203. package/src/modes/acp/acp-agent.ts +200 -75
  204. package/src/modes/components/model-selector.ts +216 -13
  205. package/src/modes/components/oauth-selector.ts +5 -5
  206. package/src/modes/components/skill-hud/render.ts +173 -35
  207. package/src/modes/components/smart-routing-panel.ts +531 -0
  208. package/src/modes/components/tool-status-header.ts +6 -3
  209. package/src/modes/controllers/command-controller.ts +17 -7
  210. package/src/modes/controllers/input-controller.ts +44 -9
  211. package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
  212. package/src/modes/controllers/selector-controller.ts +298 -65
  213. package/src/modes/interactive-mode.ts +8 -1
  214. package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
  215. package/src/modes/types.ts +9 -1
  216. package/src/prompts/agents/planner.md +1 -1
  217. package/src/prompts/system/system-prompt.md +1 -1
  218. package/src/prompts/system/volatile-project-context.md +2 -1
  219. package/src/prompts/tools/move-session.md +15 -0
  220. package/src/prompts/tools/python.md +12 -0
  221. package/src/prompts/tools/skill-discovery.md +1 -1
  222. package/src/prompts/tools/skill.md +2 -2
  223. package/src/prompts/tools/task-summary.md +1 -0
  224. package/src/prompts/tools/task.md +6 -0
  225. package/src/rlm/artifacts.ts +12 -10
  226. package/src/runtime/optional-runtime-services.ts +3 -2
  227. package/src/runtime/workspace-tree-service.ts +11 -3
  228. package/src/runtime-mcp/config.ts +8 -1
  229. package/src/runtime-mcp/manager.ts +33 -0
  230. package/src/sdk/acp/adapter.ts +48 -1
  231. package/src/sdk/broker/broker.ts +30 -2
  232. package/src/sdk/broker/discovery.ts +217 -2
  233. package/src/sdk/broker/lifecycle.ts +385 -59
  234. package/src/sdk/broker/session-index.ts +243 -48
  235. package/src/sdk/broker/transport.ts +1 -0
  236. package/src/sdk/bus/chat-daemon-control.ts +8 -2
  237. package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
  238. package/src/sdk/bus/index.ts +208 -11
  239. package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
  240. package/src/sdk/bus/reconciliation-store.ts +27 -5
  241. package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
  242. package/src/sdk/client/client.ts +204 -22
  243. package/src/sdk/host/host.ts +16 -0
  244. package/src/sdk/host/internal-autorouting-state.ts +13 -0
  245. package/src/sdk/host/model-pin.ts +101 -0
  246. package/src/sdk/host/reverse-leases.ts +2 -1
  247. package/src/sdk/host/session-runtime.test.ts +224 -1
  248. package/src/sdk/host/session-runtime.ts +125 -37
  249. package/src/sdk/lifecycle/service.ts +3 -1
  250. package/src/sdk/lifecycle-session.ts +42 -1
  251. package/src/sdk/protocol/operation-inventory.generated.json +66 -0
  252. package/src/sdk/router/session-router.ts +375 -36
  253. package/src/sdk/session.ts +547 -47
  254. package/src/session/account-inventory.ts +1 -1
  255. package/src/session/agent-session.ts +765 -189
  256. package/src/session/artifacts.ts +234 -4
  257. package/src/session/internal/managed-session-storage.ts +91 -4
  258. package/src/session/provider-safety-stop-hint.ts +3 -2
  259. package/src/session/session-manager.ts +775 -16
  260. package/src/session/session-staging-paths.ts +8 -0
  261. package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
  262. package/src/setup/host-plugin-setup.ts +2 -2
  263. package/src/skill-state/active-state.ts +2 -2
  264. package/src/skill-state/canonical-skills.ts +1 -1
  265. package/src/skill-state/initial-phase.ts +1 -1
  266. package/src/skill-state/workflow-hud.ts +41 -41
  267. package/src/skill-state/workflow-mutation-guard.ts +26 -7
  268. package/src/slash-commands/builtin-registry.ts +77 -0
  269. package/src/slash-commands/helpers/autorouting-status.ts +55 -0
  270. package/src/system-prompt.ts +102 -5
  271. package/src/task/discovery.ts +2 -3
  272. package/src/task/executor.ts +617 -43
  273. package/src/task/index.ts +262 -15
  274. package/src/task/receipt.ts +30 -0
  275. package/src/task/render.ts +11 -1
  276. package/src/task/types.ts +316 -0
  277. package/src/tools/atomic-file-write.ts +594 -0
  278. package/src/tools/bisect.ts +3 -2
  279. package/src/tools/descriptors.test.ts +3 -0
  280. package/src/tools/descriptors.ts +24 -0
  281. package/src/tools/image-gen.ts +5 -1
  282. package/src/tools/index.ts +62 -5
  283. package/src/tools/move-session.ts +107 -0
  284. package/src/tools/python.ts +205 -0
  285. package/src/tools/read.ts +88 -15
  286. package/src/tools/renderers.ts +2 -0
  287. package/src/tools/resource-gc.ts +8 -177
  288. package/src/tools/search.ts +30 -6
  289. package/src/tools/skill-discovery.ts +1 -0
  290. package/src/tools/skill.ts +37 -7
  291. package/src/tools/subagent-render.ts +119 -13
  292. package/src/tools/subagent.ts +91 -74
  293. package/src/tools/tool-catalog.generated.ts +64 -2
  294. package/src/tools/write.ts +16 -12
  295. package/src/utils/xml-escape.ts +14 -0
  296. package/src/web/search/providers/codex.ts +2 -2
  297. package/src/workflow/workflow-intent-diff.ts +13 -12
  298. package/src/workflow/workflow-intent-report-consumer.ts +6 -6
  299. package/dist/types/autoresearch/helpers.d.ts +0 -24
  300. package/dist/types/autoresearch/index.d.ts +0 -2
  301. package/dist/types/autoresearch/state.d.ts +0 -17
  302. package/dist/types/autoresearch/storage.d.ts +0 -142
  303. package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
  304. package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
  305. package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
  306. package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
  307. package/dist/types/commands/team.d.ts +0 -24
  308. package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
  309. package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
  310. package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
  311. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
  312. package/dist/types/gjc-runtime/team-store.d.ts +0 -168
  313. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
  314. package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
  315. package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
  316. package/dist/types/rlm/index.d.ts +0 -47
  317. package/dist/types/rlm/preset.d.ts +0 -23
  318. package/dist/types/rlm/python-tool.d.ts +0 -16
  319. package/src/autoresearch/command-resume.md +0 -14
  320. package/src/autoresearch/index.ts +0 -536
  321. package/src/autoresearch/prompt.md +0 -103
  322. package/src/autoresearch/resume-message.md +0 -10
  323. package/src/autoresearch/state.ts +0 -273
  324. package/src/autoresearch/storage.ts +0 -699
  325. package/src/autoresearch/tools/init-experiment.ts +0 -272
  326. package/src/autoresearch/tools/log-experiment.ts +0 -524
  327. package/src/autoresearch/tools/run-experiment.ts +0 -475
  328. package/src/autoresearch/tools/update-notes.ts +0 -109
  329. package/src/commands/rlm.ts +0 -19
  330. package/src/commands/team.ts +0 -238
  331. package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
  332. package/src/defaults/gjc/skills/team/SKILL.md +0 -467
  333. package/src/gjc-runtime/team-gc.ts +0 -49
  334. package/src/gjc-runtime/team-launch.ts +0 -404
  335. package/src/gjc-runtime/team-notify.ts +0 -326
  336. package/src/gjc-runtime/team-runtime.ts +0 -5923
  337. package/src/gjc-runtime/team-store.ts +0 -878
  338. package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
  339. package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
  340. package/src/gjc-runtime/team-workers.ts +0 -1123
  341. package/src/prompts/system/rlm-report-command.md +0 -1
  342. package/src/prompts/system/rlm-research.md +0 -23
  343. package/src/rlm/index.ts +0 -365
  344. package/src/rlm/preset.ts +0 -76
  345. package/src/rlm/python-tool.ts +0 -68
@@ -0,0 +1,474 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ /**
4
+ * Dependency-free autorouting vocabulary and settings validators.
5
+ *
6
+ * This module deliberately does not import Settings, task code, or model
7
+ * profiles. It is the shared contract used by settings and (later) routing
8
+ * policy code.
9
+ */
10
+
11
+ export const AUTOROUTING_TIERS = ["fast", "balanced", "strong"] as const;
12
+ export type AutoroutingTier = (typeof AUTOROUTING_TIERS)[number];
13
+ export const DEFAULT_AUTOROUTING_TIER: AutoroutingTier = "balanced";
14
+
15
+ /**
16
+ * The single wording for "autorouting is switched on but cannot route".
17
+ *
18
+ * One constant so every delivery surface (interactive, print, and the SDK/ACP
19
+ * host) reports the identical sentence and cannot drift apart. Bounded and free
20
+ * of interpolation so it is always safe to render.
21
+ */
22
+ export const AUTOROUTING_INACTIVE_WARNING =
23
+ "Autorouting is enabled but has no usable tier chains; Task items fall back to manual model resolution. Run /routing to inspect, or /model \u2192 smart routing to generate tiers.";
24
+
25
+ /** The normalized tier map consumed by routing policy. */
26
+ export type TierMap = Partial<Record<AutoroutingTier, string[]>>;
27
+
28
+ /** The permissive input shape accepted by the settings surface. */
29
+ export type AutoroutingTierMapInput = Partial<Record<AutoroutingTier, string | string[]>>;
30
+
31
+ export type AutoroutingSetup = {
32
+ schema: 1;
33
+ providers: string[];
34
+ models?: string[];
35
+ };
36
+
37
+ /** Fingerprints describing the generated autorouting declaration and materialized tiers. */
38
+ export type AutoroutingProvenance = {
39
+ schema: 1;
40
+ source: {
41
+ catalogFingerprint: string;
42
+ mapFingerprint: string;
43
+ generatorVersion: number;
44
+ };
45
+ declarationFingerprint: string;
46
+ tiersFingerprint: string;
47
+ };
48
+
49
+ /** Hard upper bound shared with the routing-evidence invariant in task/types.ts. */
50
+ export const AUTOROUTING_SELECTOR_MAX_LENGTH = 256;
51
+
52
+ /** The exact selector grammar published by the generated config schema. */
53
+ export const AUTOROUTING_SELECTOR_PATTERN =
54
+ "^[^/\\s*?\\[\\x00-\\x1F\\x7F-\\x9F\\u2028\\u2029]+\\/[^\\s*?\\[\\x00-\\x1F\\x7F-\\x9F\\u2028\\u2029]+(?::(?:minimal|low|medium|high|xhigh))?$";
55
+
56
+ export const AUTOROUTING_SELECTOR_DESCRIPTION =
57
+ "provider/modelId with an optional valid thinking suffix (:minimal|low|medium|high|xhigh), no globs, no bare model ids, no pi/<role> role aliases.";
58
+
59
+ export type AutoroutingReasonCode =
60
+ | "tier_unmatched"
61
+ | "tier_missing_in_map"
62
+ | "config_invalid"
63
+ | "map_absent"
64
+ | "selector_not_provider_qualified"
65
+ | "auth_substituted"
66
+ | "assistant_model_mismatch"
67
+ | "provider_disabled"
68
+ | "snapshot_missing"
69
+ | "credential_unavailable"
70
+ | "preflight_spawn_failed"
71
+ | "preflight_exhausted";
72
+
73
+ export type AutoroutingLocalIssue = {
74
+ path: string;
75
+ code: AutoroutingReasonCode;
76
+ /** Alias retained for callers that describe diagnostics as reasons. */
77
+ reason: AutoroutingReasonCode;
78
+ detail: string;
79
+ };
80
+
81
+ export type AutoroutingEffectiveIssue = {
82
+ code: Extract<AutoroutingReasonCode, "config_invalid" | "map_absent">;
83
+ /** Alias retained for callers that describe diagnostics as reasons. */
84
+ reason: Extract<AutoroutingReasonCode, "config_invalid" | "map_absent">;
85
+ detail: string;
86
+ };
87
+
88
+ export type AutoroutingEffective =
89
+ | { active: true; map: TierMap }
90
+ | { active: false; issue?: AutoroutingEffectiveIssue };
91
+
92
+ function issue(path: string, code: AutoroutingReasonCode, detail: string): AutoroutingLocalIssue {
93
+ return { path, code, reason: code, detail };
94
+ }
95
+
96
+ function effectiveIssue(
97
+ code: Extract<AutoroutingReasonCode, "config_invalid" | "map_absent">,
98
+ detail: string,
99
+ ): AutoroutingEffectiveIssue {
100
+ return { code, reason: code, detail };
101
+ }
102
+
103
+ function isRecord(value: unknown): value is Record<string, unknown> {
104
+ return value !== null && typeof value === "object" && !Array.isArray(value);
105
+ }
106
+
107
+ /**
108
+ * Validate one provider-qualified selector. A selector has one provider
109
+ * segment, a non-empty model remainder, and may carry one supported thinking
110
+ * suffix. Model ids may themselves contain slashes; the provider is always
111
+ * the segment before the first slash.
112
+ */
113
+ export function isValidAutoroutingSelector(value: unknown): value is string {
114
+ if (typeof value !== "string" || value.length === 0 || value.trim() !== value) return false;
115
+ if (value.length > AUTOROUTING_SELECTOR_MAX_LENGTH) return false;
116
+ if (/[*?[]/.test(value)) return false;
117
+ if (!new RegExp(AUTOROUTING_SELECTOR_PATTERN).test(value)) return false;
118
+ const separator = value.indexOf("/");
119
+ if (separator <= 0) return false;
120
+ const provider = value.slice(0, separator);
121
+ return provider.toLowerCase() !== "pi";
122
+ }
123
+
124
+ export function normalizeTierMap(value: unknown): TierMap {
125
+ if (!isRecord(value)) return {};
126
+ const normalized: TierMap = {};
127
+ for (const tier of AUTOROUTING_TIERS) {
128
+ const raw = value[tier];
129
+ const selectors = typeof raw === "string" ? [raw] : Array.isArray(raw) ? raw : [];
130
+ const usable = selectors.filter(isValidAutoroutingSelector);
131
+ if (usable.length > 0) normalized[tier] = [...usable];
132
+ }
133
+ return normalized;
134
+ }
135
+
136
+ /** True when at least one known tier contains one grammatically valid selector. */
137
+ export function isMeaningfulTierMap(value: unknown): value is TierMap {
138
+ return Object.values(normalizeTierMap(value)).some(selectors => selectors.length > 0);
139
+ }
140
+
141
+ function validateSelectorValue(path: string, value: unknown, issues: AutoroutingLocalIssue[]): void {
142
+ const selectors = typeof value === "string" ? [value] : Array.isArray(value) ? value : null;
143
+ if (!selectors || selectors.length === 0 || selectors.some(selector => typeof selector !== "string")) {
144
+ issues.push(issue(path, "config_invalid", "Expected a non-empty selector string or array of selector strings."));
145
+ return;
146
+ }
147
+ for (let index = 0; index < selectors.length; index++) {
148
+ if (!isValidAutoroutingSelector(selectors[index])) {
149
+ issues.push(
150
+ issue(
151
+ Array.isArray(value) ? `${path}.${index}` : path,
152
+ "selector_not_provider_qualified",
153
+ `Expected ${AUTOROUTING_SELECTOR_DESCRIPTION}`,
154
+ ),
155
+ );
156
+ }
157
+ }
158
+ }
159
+
160
+ /** Validate the typed auto-setup declaration without consulting the model catalog. */
161
+ export function validateAutoroutingSetup(value: unknown): AutoroutingLocalIssue[] {
162
+ const issues: AutoroutingLocalIssue[] = [];
163
+ if (!isRecord(value)) {
164
+ issues.push(issue("", "config_invalid", "Expected an autorouting setup object."));
165
+ return issues;
166
+ }
167
+ for (const key of Object.keys(value)) {
168
+ if (key !== "schema" && key !== "providers" && key !== "models") {
169
+ issues.push(issue(key, "config_invalid", "Unknown autorouting setup key."));
170
+ }
171
+ }
172
+ if (value.schema !== 1) issues.push(issue("schema", "config_invalid", "Expected schema version 1."));
173
+ if (!Array.isArray(value.providers)) {
174
+ issues.push(issue("providers", "config_invalid", "Expected a non-empty array of provider names."));
175
+ } else {
176
+ if (value.providers.length === 0) {
177
+ issues.push(issue("providers", "config_invalid", "Expected a non-empty array of provider names."));
178
+ }
179
+ const seen = new Set<string>();
180
+ for (let index = 0; index < value.providers.length; index++) {
181
+ const provider = value.providers[index];
182
+ if (typeof provider !== "string" || provider.length === 0 || provider.trim() !== provider) {
183
+ issues.push(issue(`providers.${index}`, "config_invalid", "Expected a non-empty provider name."));
184
+ continue;
185
+ }
186
+ if (seen.has(provider)) {
187
+ issues.push(issue(`providers.${index}`, "config_invalid", "Provider declarations must be unique."));
188
+ continue;
189
+ }
190
+ seen.add(provider);
191
+ }
192
+ }
193
+ if (value.models !== undefined) {
194
+ if (!Array.isArray(value.models)) {
195
+ issues.push(issue("models", "config_invalid", "Expected an array of provider-qualified model selectors."));
196
+ } else {
197
+ for (let index = 0; index < value.models.length; index++) {
198
+ if (!isValidAutoroutingSelector(value.models[index])) {
199
+ issues.push(
200
+ issue(
201
+ `models.${index}`,
202
+ "selector_not_provider_qualified",
203
+ `Expected ${AUTOROUTING_SELECTOR_DESCRIPTION}`,
204
+ ),
205
+ );
206
+ }
207
+ }
208
+ }
209
+ }
210
+ return issues;
211
+ }
212
+
213
+ const FINGERPRINT_PATTERN = /^[0-9a-f]{64}$/u;
214
+
215
+ function validateFingerprint(path: string, value: unknown, issues: AutoroutingLocalIssue[]): void {
216
+ if (typeof value !== "string" || !FINGERPRINT_PATTERN.test(value)) {
217
+ issues.push(issue(path, "config_invalid", "Expected a lowercase 64-character SHA-256 fingerprint."));
218
+ }
219
+ }
220
+
221
+ /** Validate generated-tier provenance and its source identity. */
222
+ export function validateAutoroutingProvenance(value: unknown): AutoroutingLocalIssue[] {
223
+ const issues: AutoroutingLocalIssue[] = [];
224
+ if (!isRecord(value)) {
225
+ issues.push(issue("", "config_invalid", "Expected an autorouting provenance object."));
226
+ return issues;
227
+ }
228
+ for (const key of Object.keys(value)) {
229
+ if (key !== "schema" && key !== "source" && key !== "declarationFingerprint" && key !== "tiersFingerprint") {
230
+ issues.push(issue(key, "config_invalid", "Unknown autorouting provenance key."));
231
+ }
232
+ }
233
+ if (value.schema !== 1) issues.push(issue("schema", "config_invalid", "Expected schema version 1."));
234
+ if (!isRecord(value.source)) {
235
+ issues.push(issue("source", "config_invalid", "Expected a provenance source object."));
236
+ } else {
237
+ for (const key of Object.keys(value.source)) {
238
+ if (key !== "catalogFingerprint" && key !== "mapFingerprint" && key !== "generatorVersion") {
239
+ issues.push(issue(`source.${key}`, "config_invalid", "Unknown provenance source key."));
240
+ }
241
+ }
242
+ validateFingerprint("source.catalogFingerprint", value.source.catalogFingerprint, issues);
243
+ validateFingerprint("source.mapFingerprint", value.source.mapFingerprint, issues);
244
+ if (
245
+ typeof value.source.generatorVersion !== "number" ||
246
+ !Number.isSafeInteger(value.source.generatorVersion) ||
247
+ value.source.generatorVersion < 1
248
+ ) {
249
+ issues.push(issue("source.generatorVersion", "config_invalid", "Expected an integer generator version >= 1."));
250
+ }
251
+ }
252
+ validateFingerprint("declarationFingerprint", value.declarationFingerprint, issues);
253
+ validateFingerprint("tiersFingerprint", value.tiersFingerprint, issues);
254
+ return issues;
255
+ }
256
+
257
+ /** Validate only local types, keys, and selector grammar for one source layer. */
258
+ export function validateAutoroutingLocal(fragment: unknown): AutoroutingLocalIssue[] {
259
+ const issues: AutoroutingLocalIssue[] = [];
260
+ if (fragment === undefined) return issues;
261
+ if (!isRecord(fragment)) {
262
+ issues.push(issue("", "config_invalid", "Expected task.autorouting to be an object."));
263
+ return issues;
264
+ }
265
+
266
+ for (const key of Object.keys(fragment)) {
267
+ if (!new Set(["enabled", "tiers", "setup", "provenance"]).has(key)) {
268
+ issues.push(issue(key, "config_invalid", "Unknown autorouting setting key."));
269
+ }
270
+ }
271
+ if (fragment.enabled !== undefined && typeof fragment.enabled !== "boolean") {
272
+ issues.push(issue("enabled", "config_invalid", "Expected a boolean."));
273
+ }
274
+
275
+ if (fragment.setup !== undefined) {
276
+ for (const setupIssue of validateAutoroutingSetup(fragment.setup)) {
277
+ issues.push({ ...setupIssue, path: setupIssue.path ? `setup.${setupIssue.path}` : "setup" });
278
+ }
279
+ }
280
+ if (fragment.provenance !== undefined) {
281
+ for (const provenanceIssue of validateAutoroutingProvenance(fragment.provenance)) {
282
+ issues.push({
283
+ ...provenanceIssue,
284
+ path: provenanceIssue.path ? `provenance.${provenanceIssue.path}` : "provenance",
285
+ });
286
+ }
287
+ }
288
+ if (fragment.tiers === undefined) return issues;
289
+ if (!isRecord(fragment.tiers)) {
290
+ issues.push(issue("tiers", "config_invalid", "Expected an object with only fast, balanced, and strong keys."));
291
+ return issues;
292
+ }
293
+ for (const key of Object.keys(fragment.tiers)) {
294
+ if (!AUTOROUTING_TIERS.includes(key as AutoroutingTier)) {
295
+ issues.push(issue(`tiers.${key}`, "config_invalid", "Unknown tier key; expected fast, balanced, or strong."));
296
+ continue;
297
+ }
298
+ validateSelectorValue(`tiers.${key}`, fragment.tiers[key], issues);
299
+ }
300
+ return issues;
301
+ }
302
+
303
+ /** Return a canonical JSON representation with sorted object keys and stored array order. */
304
+ function canonicalAutoroutingJson(value: unknown): string {
305
+ if (value === null) return "null";
306
+ if (value === undefined) return "undefined";
307
+ switch (typeof value) {
308
+ case "string":
309
+ case "boolean":
310
+ return JSON.stringify(value);
311
+ case "number":
312
+ return Number.isFinite(value) ? JSON.stringify(value) : "null";
313
+ case "bigint":
314
+ throw new TypeError("Cannot canonicalize bigint");
315
+ case "function":
316
+ case "symbol":
317
+ return "undefined";
318
+ case "object":
319
+ if (Array.isArray(value)) {
320
+ return `[${value
321
+ .map(item => {
322
+ const encoded = canonicalAutoroutingJson(item);
323
+ return encoded === "undefined" ? "null" : encoded;
324
+ })
325
+ .join(",")}]`;
326
+ }
327
+ return `{${Object.keys(value as Record<string, unknown>)
328
+ .sort()
329
+ .flatMap(key => {
330
+ const encoded = canonicalAutoroutingJson((value as Record<string, unknown>)[key]);
331
+ return encoded === "undefined" ? [] : [`${JSON.stringify(key)}:${encoded}`];
332
+ })
333
+ .join(",")}}`;
334
+ default:
335
+ return "undefined";
336
+ }
337
+ }
338
+
339
+ function autoroutingSha256(value: unknown): string {
340
+ return createHash("sha256")
341
+ .update(new TextEncoder().encode(canonicalAutoroutingJson(value)))
342
+ .digest("hex");
343
+ }
344
+
345
+ export type AutoroutingProvenanceState = {
346
+ staleMap: boolean;
347
+ staleCatalog: boolean;
348
+ handEdited: boolean;
349
+ };
350
+
351
+ /** Compare recorded provenance with the current catalog/map/tier materialization. */
352
+ export function evaluateAutoroutingProvenanceState(
353
+ provenance: AutoroutingProvenance | undefined,
354
+ current: { catalogFingerprint: string; mapFingerprint: string; tiers: unknown },
355
+ ): AutoroutingProvenanceState {
356
+ if (!provenance) return { staleMap: false, staleCatalog: false, handEdited: false };
357
+ return {
358
+ staleMap: provenance.source.mapFingerprint !== current.mapFingerprint,
359
+ staleCatalog: provenance.source.catalogFingerprint !== current.catalogFingerprint,
360
+ handEdited: provenance.tiersFingerprint !== autoroutingSha256(current.tiers),
361
+ };
362
+ }
363
+
364
+ /** Compare a recorded tier fingerprint with the current raw tier map. */
365
+ export function matchesRecordedTiersFingerprint(
366
+ provenance: AutoroutingProvenance | undefined,
367
+ tiers: unknown,
368
+ ): boolean {
369
+ return provenance !== undefined && provenance.tiersFingerprint === autoroutingSha256(tiers);
370
+ }
371
+
372
+ /** Advisory comparison between a recorded declaration and the current provider priority. */
373
+ export type AutoroutingProviderOrderHint = {
374
+ /** The declaration lists the same providers in a different relative order. */
375
+ reordered: boolean;
376
+ /** Declared providers that the current catalog no longer offers, in declaration order. */
377
+ missing: string[];
378
+ };
379
+
380
+ /**
381
+ * Compare a recorded `setup.providers` declaration against the current provider
382
+ * priority, for an advisory panel hint only.
383
+ *
384
+ * Pure string comparison over two arrays, normalized exactly the way provider
385
+ * selection normalizes ids (trim + lowercase). It deliberately never reaches
386
+ * provenance, effective state, routing, preflight, or evidence: a changed priority
387
+ * is a new suggestion, not proof that persisted tiers went stale.
388
+ */
389
+ export function autoroutingProviderOrderHint(
390
+ setupProviders: readonly string[],
391
+ currentOrder: readonly string[],
392
+ ): AutoroutingProviderOrderHint {
393
+ const normalize = (value: string): string => value.trim().toLowerCase();
394
+ const current = currentOrder.map(normalize).filter(id => id.length > 0);
395
+ const currentSet = new Set(current);
396
+ const declared: string[] = [];
397
+ const missing: string[] = [];
398
+ const seen = new Set<string>();
399
+ for (const raw of setupProviders) {
400
+ const id = normalize(raw);
401
+ if (!id || seen.has(id)) continue;
402
+ seen.add(id);
403
+ if (currentSet.has(id)) declared.push(id);
404
+ else missing.push(raw);
405
+ }
406
+ // Only the providers common to both sides can disagree about order, so a
407
+ // declaration that is a subset in the same relative order is not reordered.
408
+ const expected = current.filter(id => seen.has(id));
409
+ const reordered = declared.length === expected.length && declared.some((id, index) => id !== expected[index]);
410
+ return { reordered, missing };
411
+ }
412
+
413
+ export type AutoroutingSettingsBatchPatch =
414
+ | { path: "task.autorouting.tiers"; op: "set"; value: TierMap }
415
+ | { path: "task.autorouting.setup"; op: "set"; value: AutoroutingSetup }
416
+ | { path: "task.autorouting.provenance"; op: "set"; value: AutoroutingProvenance }
417
+ | { path: "task.autorouting.tiers" | "task.autorouting.setup" | "task.autorouting.provenance"; op: "unset" };
418
+
419
+ /** Build the one atomic settings batch used by autorouting Apply/Refresh or Clear. */
420
+ export function buildAutoroutingSettingsBatch(
421
+ input: { tiers: TierMap; setup: AutoroutingSetup; provenance: AutoroutingProvenance } | { clear: true },
422
+ ): readonly AutoroutingSettingsBatchPatch[] {
423
+ if ("clear" in input) {
424
+ return [
425
+ { path: "task.autorouting.tiers", op: "unset" },
426
+ { path: "task.autorouting.setup", op: "unset" },
427
+ { path: "task.autorouting.provenance", op: "unset" },
428
+ ];
429
+ }
430
+ return [
431
+ { path: "task.autorouting.tiers", op: "set", value: structuredClone(input.tiers) },
432
+ { path: "task.autorouting.setup", op: "set", value: structuredClone(input.setup) },
433
+ { path: "task.autorouting.provenance", op: "set", value: structuredClone(input.provenance) },
434
+ ];
435
+ }
436
+
437
+ /** Convenience aliases for controller intents that all use one atomic batch. */
438
+ export const buildAutoroutingApplyPatches = buildAutoroutingSettingsBatch;
439
+ export const buildAutoroutingRefreshPatches = buildAutoroutingSettingsBatch;
440
+ export const buildAutoroutingClearPatches = () => buildAutoroutingSettingsBatch({ clear: true });
441
+
442
+ /** Build the separate single-key enabled toggle mutation. */
443
+ export function buildAutoroutingEnabledPatch(enabled: boolean): {
444
+ path: "task.autorouting.enabled";
445
+ op: "set";
446
+ value: boolean;
447
+ } {
448
+ return { path: "task.autorouting.enabled", op: "set", value: enabled };
449
+ }
450
+
451
+ /** Validate effective enablement and map cross-field semantics. */
452
+ export function validateAutoroutingEffective(fragment: unknown): AutoroutingEffective {
453
+ if (fragment === undefined || !isRecord(fragment)) return { active: false };
454
+ if (fragment.enabled === undefined || fragment.enabled === false) return { active: false };
455
+ if (fragment.enabled !== true) {
456
+ return {
457
+ active: false,
458
+ issue: effectiveIssue(
459
+ "config_invalid",
460
+ "task.autorouting.enabled must be a boolean true to enable autorouting.",
461
+ ),
462
+ };
463
+ }
464
+ if (isMeaningfulTierMap(fragment.tiers)) {
465
+ return { active: true, map: normalizeTierMap(fragment.tiers) };
466
+ }
467
+ return {
468
+ active: false,
469
+ issue: effectiveIssue(
470
+ "map_absent",
471
+ "Autorouting is enabled but has no usable tiers. Generate them from the /model smart-routing panel.",
472
+ ),
473
+ };
474
+ }