one-code-extension 0.4.1 → 0.6.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 (439) hide show
  1. package/README.md +16 -5
  2. package/agents/general-purpose.md +2 -2
  3. package/docs/guide/README.md +2 -1
  4. package/docs/guide/bring-your-claude-code-setup.md +60 -9
  5. package/docs/guide/configuration.md +48 -14
  6. package/docs/guide/differences-from-claude-code.md +40 -28
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +26 -4
  10. package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
  11. package/docs/guide/providers-and-models.md +137 -54
  12. package/docs/guide/reference.md +27 -15
  13. package/docs/guide/sessions-and-context.md +69 -16
  14. package/docs/guide/skills-plugins-and-mcp.md +57 -20
  15. package/docs/guide/subagents-and-workflows.md +95 -29
  16. package/docs/guide/tasks-and-background-work.md +101 -26
  17. package/docs/guide/terminal-interface.md +24 -12
  18. package/docs/guide/tools.md +46 -5
  19. package/docs/guide/troubleshooting.md +13 -9
  20. package/docs/guide/windows.md +58 -31
  21. package/extensions/artifacts/gallery.ts +227 -0
  22. package/extensions/artifacts/index.ts +288 -0
  23. package/extensions/artifacts/store.ts +570 -0
  24. package/extensions/artifacts/viewer.ts +199 -0
  25. package/extensions/ask-user/description.ts +67 -0
  26. package/extensions/ask-user/dialogs.ts +80 -0
  27. package/extensions/ask-user/index.ts +37 -33
  28. package/extensions/ask-user/questions.ts +32 -7
  29. package/extensions/ask-user/widget.ts +45 -25
  30. package/extensions/auto-mode/actions.ts +6 -0
  31. package/extensions/auto-mode/cache.ts +88 -0
  32. package/extensions/auto-mode/classifier-prompt.ts +5 -4
  33. package/extensions/auto-mode/classifier.ts +290 -65
  34. package/extensions/auto-mode/config.ts +72 -20
  35. package/extensions/auto-mode/decision-log.ts +2 -0
  36. package/extensions/auto-mode/defaults.ts +1 -1
  37. package/extensions/auto-mode/denials.ts +124 -0
  38. package/extensions/auto-mode/git-checkout-programs.ts +187 -0
  39. package/extensions/auto-mode/git-status-meta.ts +159 -0
  40. package/extensions/auto-mode/history.ts +189 -0
  41. package/extensions/auto-mode/instructions.ts +58 -52
  42. package/extensions/auto-mode/model-select.ts +159 -134
  43. package/extensions/auto-mode/paths.ts +57 -5
  44. package/extensions/auto-mode/pause.ts +57 -2
  45. package/extensions/auto-mode/prompt.ts +37 -21
  46. package/extensions/auto-mode/read-only-options.ts +724 -0
  47. package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
  48. package/extensions/auto-mode/rules.ts +18 -1
  49. package/extensions/auto-mode/safety-floor.ts +361 -18
  50. package/extensions/auto-mode/setup-run.ts +20 -14
  51. package/extensions/auto-mode/shell-analysis.ts +764 -490
  52. package/extensions/auto-mode/shell-parse.ts +885 -0
  53. package/extensions/auto-mode/system-note.ts +33 -0
  54. package/extensions/auto-mode/transcript.ts +80 -51
  55. package/extensions/background/cron.ts +642 -0
  56. package/extensions/background/index.ts +614 -248
  57. package/extensions/background/loop-fire.ts +173 -0
  58. package/extensions/background/loop-preamble.ts +59 -0
  59. package/extensions/background/loop-skill.ts +163 -0
  60. package/extensions/background/monitor-batch.ts +53 -0
  61. package/extensions/background/noop-fold.ts +128 -0
  62. package/extensions/background/switch-guard.ts +56 -0
  63. package/extensions/background/wakeup.ts +178 -55
  64. package/extensions/bash/background.ts +53 -17
  65. package/extensions/bash/child.ts +7 -0
  66. package/extensions/bash/description.ts +188 -0
  67. package/extensions/bash/guards.ts +15 -67
  68. package/extensions/bash/index.ts +66 -35
  69. package/extensions/branding/compaction-keep.ts +63 -0
  70. package/extensions/branding/declined.ts +22 -0
  71. package/extensions/branding/index.ts +133 -9
  72. package/extensions/branding/prompt-editor.ts +53 -6
  73. package/extensions/branding/queued-edit.ts +31 -0
  74. package/extensions/branding/replaced-builtins.ts +68 -0
  75. package/extensions/branding/startup.ts +33 -71
  76. package/extensions/btw/index.ts +250 -78
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/cache-marks/index.ts +42 -0
  80. package/extensions/claude-compat/index.ts +7 -11
  81. package/extensions/claude-context/index.ts +436 -63
  82. package/extensions/compaction/cut.ts +157 -0
  83. package/extensions/compaction/index.ts +219 -75
  84. package/extensions/compaction/kept-thinking.ts +62 -0
  85. package/extensions/compaction/prompt.ts +15 -12
  86. package/extensions/context-budget/budget.ts +16 -2
  87. package/extensions/context-budget/index.ts +35 -15
  88. package/extensions/context-management/index.ts +36 -28
  89. package/extensions/doctor/build.ts +59 -8
  90. package/extensions/doctor/builtins.ts +84 -0
  91. package/extensions/doctor/cli.ts +7 -1
  92. package/extensions/doctor/compat.ts +88 -23
  93. package/extensions/doctor/dependencies.ts +41 -5
  94. package/extensions/doctor/fix-prompt.ts +9 -2
  95. package/extensions/doctor/index.ts +102 -58
  96. package/extensions/doctor/models.ts +102 -63
  97. package/extensions/doctor/presets.ts +42 -27
  98. package/extensions/doctor/report.ts +19 -2
  99. package/extensions/doctor/viewer.ts +49 -27
  100. package/extensions/effort/index.ts +141 -25
  101. package/extensions/effort/settings.ts +21 -0
  102. package/extensions/effort/slider.ts +24 -20
  103. package/extensions/exit/index.ts +32 -1
  104. package/extensions/file-tracker/index.ts +420 -42
  105. package/extensions/file-tracker/replay.ts +137 -24
  106. package/extensions/file-tracker/restore.ts +66 -0
  107. package/extensions/file-tracker/results.ts +66 -0
  108. package/extensions/file-tracker/shell-reads.ts +211 -0
  109. package/extensions/file-tracker/tracker.ts +45 -15
  110. package/extensions/footer/footer-line.ts +45 -24
  111. package/extensions/footer/index.ts +47 -2
  112. package/extensions/footer/pr.ts +34 -5
  113. package/extensions/hooks/compaction.ts +7 -0
  114. package/extensions/hooks/executor.ts +32 -30
  115. package/extensions/hooks/index.ts +209 -58
  116. package/extensions/hooks/matcher.ts +3 -0
  117. package/extensions/hooks/prompt-context.ts +59 -0
  118. package/extensions/hooks/protocol.ts +41 -3
  119. package/extensions/hooks/settings.ts +50 -19
  120. package/extensions/hooks/subagent-bridge.ts +2 -0
  121. package/extensions/hooks/trust.ts +26 -12
  122. package/extensions/idle-compact/index.ts +119 -0
  123. package/extensions/idle-compact/policy.ts +143 -0
  124. package/extensions/init/index.ts +11 -10
  125. package/extensions/init/prompt.ts +25 -32
  126. package/extensions/interrupted/index.ts +13 -1
  127. package/extensions/kept-thinking/index.ts +27 -0
  128. package/extensions/ledger/index.ts +46 -0
  129. package/extensions/ledger/wire.ts +154 -0
  130. package/extensions/lib/agent-cron.ts +146 -0
  131. package/extensions/lib/agent-loader.ts +18 -3
  132. package/extensions/lib/agent-view.ts +85 -0
  133. package/extensions/lib/anthropic-auth.ts +98 -0
  134. package/extensions/lib/anthropic-payload.ts +82 -1
  135. package/extensions/lib/anthropic-server-call.ts +230 -0
  136. package/extensions/lib/anthropic-server-tools.ts +485 -0
  137. package/extensions/lib/app-launch.mjs +94 -0
  138. package/extensions/lib/argument-hints.ts +190 -0
  139. package/extensions/lib/atomic-write.ts +6 -2
  140. package/extensions/lib/auto-memory.ts +75 -0
  141. package/extensions/lib/bash-parser.ts +84 -0
  142. package/extensions/lib/btw-fork.ts +93 -0
  143. package/extensions/lib/child-extensions.ts +52 -0
  144. package/extensions/lib/child-writes.ts +45 -0
  145. package/extensions/lib/claude-context.ts +515 -100
  146. package/extensions/lib/claude-external-includes.ts +68 -0
  147. package/extensions/lib/claude-id.ts +22 -0
  148. package/extensions/lib/claude-rules.ts +323 -0
  149. package/extensions/lib/claude-settings.ts +76 -4
  150. package/extensions/lib/compaction-boundary.ts +36 -0
  151. package/extensions/lib/compaction-keep.mjs +83 -0
  152. package/extensions/lib/config-mode.ts +112 -0
  153. package/extensions/lib/consent-dialogs.ts +63 -0
  154. package/extensions/lib/consent-preview.ts +5 -1
  155. package/extensions/lib/consent-stores.ts +48 -0
  156. package/extensions/lib/context-facts.ts +97 -0
  157. package/extensions/lib/covered-messages.ts +23 -0
  158. package/extensions/lib/deferred.ts +359 -25
  159. package/extensions/lib/dock-rows.ts +60 -0
  160. package/extensions/lib/empty-tool-result.ts +27 -0
  161. package/extensions/lib/environment-block.ts +76 -0
  162. package/extensions/lib/fetch-timeout.ts +24 -6
  163. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  164. package/extensions/lib/git.ts +75 -13
  165. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  166. package/extensions/lib/grammars/README.md +2 -0
  167. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  168. package/extensions/lib/guide-docs.ts +45 -0
  169. package/extensions/lib/headless-output.ts +46 -0
  170. package/extensions/lib/hook-gate.ts +7 -7
  171. package/extensions/lib/interrupt.ts +20 -4
  172. package/extensions/lib/key-input.ts +455 -0
  173. package/extensions/lib/keys.ts +14 -1
  174. package/extensions/lib/ledger/fold-memo.ts +49 -0
  175. package/extensions/lib/ledger/fold.ts +265 -0
  176. package/extensions/lib/ledger/layout.ts +72 -0
  177. package/extensions/lib/ledger/plan.ts +102 -0
  178. package/extensions/lib/ledger/records.ts +276 -0
  179. package/extensions/lib/ledger/state.ts +47 -0
  180. package/extensions/lib/ledger/validate.ts +141 -0
  181. package/extensions/lib/ledger/wire.ts +632 -0
  182. package/extensions/lib/local-command.ts +71 -19
  183. package/extensions/lib/mcp-overrides.ts +1 -1
  184. package/extensions/lib/mcp-share.ts +46 -0
  185. package/extensions/lib/memory-content.ts +146 -0
  186. package/extensions/lib/memory-index.ts +18 -0
  187. package/extensions/lib/memory.ts +134 -123
  188. package/extensions/lib/model-catalog/huggingface.json +178 -0
  189. package/extensions/lib/model-catalog/models-dev.json +18533 -0
  190. package/extensions/lib/model-catalog/openrouter.json +5050 -0
  191. package/extensions/lib/model-catalog-data.ts +513 -0
  192. package/extensions/lib/model-catalog.ts +462 -0
  193. package/extensions/lib/model-choice-warnings.ts +137 -0
  194. package/extensions/lib/model-pairings.ts +152 -0
  195. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  196. package/extensions/lib/model-policy.ts +112 -15
  197. package/extensions/lib/model-tier.ts +283 -355
  198. package/extensions/lib/model-unusable.ts +1 -1
  199. package/extensions/lib/newer-model.ts +100 -0
  200. package/extensions/lib/notices.ts +87 -0
  201. package/extensions/lib/notifications.ts +357 -73
  202. package/extensions/lib/one-code-settings.ts +74 -8
  203. package/extensions/lib/open-browser.ts +63 -0
  204. package/extensions/lib/openrouter-generation.ts +94 -0
  205. package/extensions/lib/operation-deadline.ts +30 -0
  206. package/extensions/lib/original-command.ts +4 -4
  207. package/extensions/lib/package-version.ts +1 -1
  208. package/extensions/lib/panel-focus.ts +134 -0
  209. package/extensions/lib/paths.ts +17 -2
  210. package/extensions/lib/permission-gate.ts +72 -6
  211. package/extensions/lib/persisted-output.ts +60 -8
  212. package/extensions/lib/pi-ai-estimate.ts +19 -3
  213. package/extensions/lib/pi-version.ts +38 -2
  214. package/extensions/lib/plan-mode-channels.ts +42 -5
  215. package/extensions/lib/plugins.ts +26 -17
  216. package/extensions/lib/powershell-parse-script.ts +208 -0
  217. package/extensions/lib/powershell-parser.ts +344 -0
  218. package/extensions/lib/previous-mark.ts +101 -0
  219. package/extensions/lib/process-state.ts +48 -0
  220. package/extensions/lib/process-tree.ts +132 -8
  221. package/extensions/lib/prompt-options.ts +82 -0
  222. package/extensions/lib/queued-delivery.ts +44 -0
  223. package/extensions/lib/reminders.ts +370 -358
  224. package/extensions/lib/replaced-builtins.mjs +199 -0
  225. package/extensions/lib/replay-call.ts +70 -0
  226. package/extensions/lib/request-replay.ts +547 -0
  227. package/extensions/lib/scratchpad.ts +81 -14
  228. package/extensions/lib/send-now-channel.ts +7 -0
  229. package/extensions/lib/session-model-tier.ts +51 -0
  230. package/extensions/lib/session-work.ts +64 -0
  231. package/extensions/lib/settings-channels.ts +4 -4
  232. package/extensions/lib/shell-exit-semantics.ts +170 -0
  233. package/extensions/lib/shell-quote.ts +11 -0
  234. package/extensions/lib/shell-result.ts +70 -0
  235. package/extensions/lib/shell-spawn.ts +70 -6
  236. package/extensions/lib/shell-tool.ts +75 -25
  237. package/extensions/lib/side-call-cache.ts +124 -0
  238. package/extensions/lib/side-call-run.ts +96 -0
  239. package/extensions/lib/side-call-usage.ts +38 -0
  240. package/extensions/lib/side-call.ts +24 -1
  241. package/extensions/lib/single-flight.ts +18 -0
  242. package/extensions/lib/skill-body.ts +42 -0
  243. package/extensions/lib/skill-scan.ts +63 -24
  244. package/extensions/lib/spill-file.ts +140 -0
  245. package/extensions/lib/subagent-handback.ts +112 -0
  246. package/extensions/lib/system-role.ts +156 -0
  247. package/extensions/lib/terminal-text.ts +114 -0
  248. package/extensions/lib/text-width.ts +47 -16
  249. package/extensions/lib/timer-ops.ts +20 -0
  250. package/extensions/lib/tool-additions.ts +129 -0
  251. package/extensions/lib/tool-call-corruption.ts +134 -0
  252. package/extensions/lib/tool-path.ts +66 -0
  253. package/extensions/lib/tool-variants.ts +69 -0
  254. package/extensions/lib/tui-render.ts +255 -27
  255. package/extensions/lib/turn-budget-layout.ts +159 -0
  256. package/extensions/lib/update-check.mjs +15 -2
  257. package/extensions/lib/usage-bus.ts +1 -1
  258. package/extensions/lib/user-denial.ts +38 -0
  259. package/extensions/lib/workspace-channel.ts +16 -0
  260. package/extensions/lib/worktree-channel.ts +31 -3
  261. package/extensions/lib/worktree-isolation.ts +40 -10
  262. package/extensions/lsp/client.ts +8 -9
  263. package/extensions/lsp/index.ts +76 -12
  264. package/extensions/lsp/keep-alive.ts +1 -1
  265. package/extensions/lsp/trust.ts +119 -0
  266. package/extensions/lsp/watcher.ts +1 -1
  267. package/extensions/mcp/announce.ts +90 -43
  268. package/extensions/mcp/client.ts +161 -48
  269. package/extensions/mcp/config.ts +132 -8
  270. package/extensions/mcp/discovery.ts +19 -0
  271. package/extensions/mcp/index.ts +220 -121
  272. package/extensions/mcp/oauth/browser.ts +38 -17
  273. package/extensions/mcp/oauth/flow.ts +25 -17
  274. package/extensions/mcp/oauth/provider.ts +14 -8
  275. package/extensions/mcp/oauth/store.ts +34 -16
  276. package/extensions/mcp/panel/keys.ts +12 -11
  277. package/extensions/mcp/panel/render.ts +9 -6
  278. package/extensions/mcp/schema.ts +104 -5
  279. package/extensions/mcp/trust.ts +164 -73
  280. package/extensions/memory/entries.ts +23 -14
  281. package/extensions/memory/index.ts +134 -28
  282. package/extensions/memory/open-external.ts +33 -18
  283. package/extensions/memory/panel.ts +135 -26
  284. package/extensions/model-default/index.ts +86 -19
  285. package/extensions/newer-model/index.ts +31 -0
  286. package/extensions/notebook/description.ts +18 -0
  287. package/extensions/notebook/index.ts +6 -5
  288. package/extensions/notebook/notebook.ts +1 -1
  289. package/extensions/permissions/index.ts +1426 -398
  290. package/extensions/permissions/matcher.ts +425 -74
  291. package/extensions/permissions/modes.ts +11 -4
  292. package/extensions/permissions/panel/host.ts +130 -0
  293. package/extensions/permissions/panel/keys.ts +48 -0
  294. package/extensions/permissions/panel/render.ts +450 -0
  295. package/extensions/permissions/panel/state.ts +550 -0
  296. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  297. package/extensions/permissions/powershell-paths.ts +197 -0
  298. package/extensions/permissions/powershell-rules.ts +207 -280
  299. package/extensions/permissions/powershell-tree.ts +557 -0
  300. package/extensions/permissions/project-trust.ts +27 -11
  301. package/extensions/permissions/protected-paths.ts +14 -2
  302. package/extensions/permissions/session-grant.ts +6 -0
  303. package/extensions/permissions/settings.ts +166 -31
  304. package/extensions/permissions/subagent-gate.ts +25 -0
  305. package/extensions/permissions/workspace.ts +78 -0
  306. package/extensions/plan-mode/index.ts +276 -92
  307. package/extensions/plan-mode/reminder.ts +89 -21
  308. package/extensions/plan-mode/run-state.ts +26 -0
  309. package/extensions/plan-mode/texts.ts +177 -0
  310. package/extensions/plan-mode/viewer.ts +92 -27
  311. package/extensions/plugins/counts.ts +3 -3
  312. package/extensions/plugins/index.ts +64 -46
  313. package/extensions/plugins/install/install.ts +16 -0
  314. package/extensions/plugins/marketplace/sync.ts +9 -3
  315. package/extensions/plugins/marketplace/types.ts +3 -1
  316. package/extensions/plugins/panel/keys.ts +21 -24
  317. package/extensions/plugins/panel/render.ts +12 -6
  318. package/extensions/plugins/shell-expand.ts +107 -0
  319. package/extensions/plugins/template.ts +32 -15
  320. package/extensions/powershell/child.ts +7 -0
  321. package/extensions/powershell/description.ts +38 -6
  322. package/extensions/powershell/guards.ts +1 -1
  323. package/extensions/powershell/index.ts +14 -7
  324. package/extensions/recap/index.ts +73 -71
  325. package/extensions/recap/prompt.ts +2 -2
  326. package/extensions/recap/scheduler.ts +3 -5
  327. package/extensions/search-tools/grep-merge.ts +39 -0
  328. package/extensions/search-tools/index.ts +0 -0
  329. package/extensions/send-now/chord.ts +62 -0
  330. package/extensions/send-now/index.ts +133 -0
  331. package/extensions/session-title/index.ts +3 -0
  332. package/extensions/session-title/title.ts +4 -1
  333. package/extensions/skill/announce.ts +30 -0
  334. package/extensions/skill/code-review.ts +97 -0
  335. package/extensions/skill/index.ts +469 -132
  336. package/extensions/skill/invoke.ts +118 -75
  337. package/extensions/skill/listing.ts +133 -9
  338. package/extensions/skill/panel/keys.ts +15 -16
  339. package/extensions/skill/panel/render.ts +5 -2
  340. package/extensions/skill/panel/state.ts +12 -1
  341. package/extensions/subagents/agent-args.ts +39 -0
  342. package/extensions/subagents/agent-description.ts +132 -0
  343. package/extensions/subagents/agents.ts +146 -31
  344. package/extensions/subagents/default-model.ts +2 -3
  345. package/extensions/subagents/delegation-steer.ts +4 -4
  346. package/extensions/subagents/fork-cache.ts +82 -0
  347. package/extensions/subagents/guide-agent.ts +189 -0
  348. package/extensions/subagents/hand-back-review.ts +3 -0
  349. package/extensions/subagents/index.ts +1594 -852
  350. package/extensions/subagents/live-runs.ts +26 -6
  351. package/extensions/subagents/model-select.ts +38 -25
  352. package/extensions/subagents/outcome.ts +5 -2
  353. package/extensions/subagents/panel-keys.ts +73 -31
  354. package/extensions/subagents/panel-render.ts +120 -96
  355. package/extensions/subagents/panel-widget.ts +60 -89
  356. package/extensions/subagents/parent-mode.ts +50 -0
  357. package/extensions/subagents/pending-claim.ts +18 -1
  358. package/extensions/subagents/runner.ts +135 -80
  359. package/extensions/subagents/runs.ts +44 -5
  360. package/extensions/subagents/send-to-main-tool.ts +1 -1
  361. package/extensions/subagents/session-turns.ts +32 -8
  362. package/extensions/subagents/shell-panel.ts +36 -178
  363. package/extensions/subagents/tasks-dialog.ts +170 -0
  364. package/extensions/subagents/viewport-mirror.ts +95 -0
  365. package/extensions/subagents/worktree.ts +57 -13
  366. package/extensions/system-prompt/environment.ts +19 -19
  367. package/extensions/system-prompt/idle-turn.ts +65 -0
  368. package/extensions/system-prompt/index.ts +165 -47
  369. package/extensions/system-prompt/template.ts +34 -51
  370. package/extensions/system-prompt/tiers/common.ts +23 -64
  371. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  372. package/extensions/system-prompt/tiers/low.ts +67 -45
  373. package/extensions/system-prompt/tiers/mid.ts +96 -38
  374. package/extensions/system-reminder/index.ts +204 -70
  375. package/extensions/tasks/descriptions.ts +190 -0
  376. package/extensions/tasks/index.ts +120 -41
  377. package/extensions/tasks/store.ts +83 -16
  378. package/extensions/tool-call-corruption/child.ts +7 -0
  379. package/extensions/tool-call-corruption/index.ts +237 -0
  380. package/extensions/tool-search/index.ts +366 -144
  381. package/extensions/tool-style/index.ts +68 -10
  382. package/extensions/tool-style/write-description.ts +40 -0
  383. package/extensions/tool-style/write-render.ts +237 -0
  384. package/extensions/turn-duration/index.ts +7 -3
  385. package/extensions/turn-duration/line.ts +47 -4
  386. package/extensions/web/backends.ts +6 -9
  387. package/extensions/web/description.ts +50 -0
  388. package/extensions/web/index.ts +118 -53
  389. package/extensions/web/native-call.ts +15 -0
  390. package/extensions/web/native-results.ts +41 -0
  391. package/extensions/web-fetch/description.ts +42 -0
  392. package/extensions/web-fetch/extract.ts +53 -6
  393. package/extensions/web-fetch/index.ts +125 -76
  394. package/extensions/web-fetch/page-content.ts +19 -0
  395. package/extensions/web-fetch/response.ts +49 -0
  396. package/extensions/web-fetch/summarize.ts +9 -10
  397. package/extensions/workflow/agent-session.ts +108 -18
  398. package/extensions/workflow/args.ts +24 -0
  399. package/extensions/workflow/description.ts +67 -0
  400. package/extensions/workflow/globals.ts +17 -3
  401. package/extensions/workflow/index.ts +157 -162
  402. package/extensions/workflow/records.ts +1 -0
  403. package/extensions/workflow/run-manager.ts +33 -6
  404. package/extensions/workflow/saved-workflows.ts +3 -2
  405. package/extensions/workflow/types.ts +22 -1
  406. package/extensions/workflow/viewer.ts +54 -24
  407. package/extensions/workflow/widget.ts +20 -0
  408. package/extensions/worktree/descriptions.ts +91 -0
  409. package/extensions/worktree/guards.ts +316 -54
  410. package/extensions/worktree/index.ts +162 -44
  411. package/extensions/worktree/powershell-guards.ts +167 -0
  412. package/extensions/worktree/rewrite.ts +14 -16
  413. package/package.json +21 -7
  414. package/skills/artifact-design/SKILL.md +85 -0
  415. package/skills/code-review/SKILL.md +20 -20
  416. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  417. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  418. package/skills/code-review/cells/high-8-inline.md +122 -0
  419. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  420. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  421. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  422. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  423. package/skills/code-review/cells/minimal-prompt.md +32 -0
  424. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  425. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  426. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  427. package/skills/loop/SKILL.md +11 -0
  428. package/skills/security-review/SKILL.md +2 -5
  429. package/skills/simplify/SKILL.md +7 -5
  430. package/skills/workflow-authoring/SKILL.md +143 -0
  431. package/themes/onecode-light.json +1 -1
  432. package/themes/onecode.json +1 -1
  433. package/types/app.d.ts +58 -0
  434. package/types/community-packages.d.ts +26 -1
  435. package/extensions/auto-mode/recoverability.ts +0 -229
  436. package/extensions/lib/capability-index.ts +0 -466
  437. package/extensions/lib/model-facts.generated.json +0 -2675
  438. package/extensions/lib/model-facts.ts +0 -162
  439. package/extensions/tool-search/announce.ts +0 -42
@@ -1,466 +0,0 @@
1
- /**
2
- * Measured model capability from the Artificial Analysis free API, as an
3
- * OPTIONAL input to automatic model selection (pure: no pi imports).
4
- *
5
- * ## What it is for, and what it is not
6
- *
7
- * The tier classifier (`model-tier.ts`) decides a model's prompt REGISTER by
8
- * name class and generation, and the index must never move that: on the
9
- * coding index current flash-class models (Gemini 3.7 Flash 76.1, GLM-5.3 Flash
10
- * 71.5, GPT-5.6 Luna 71.4) score at or above Claude Sonnet 5 (71.5), so any
11
- * cutoff that keeps Sonnet in workhorse promotes every flash with it, and a
12
- * benchmark does not measure harness reliability at the effort a user runs.
13
- * Those scores are TRUE about coding ability, though — so they are exactly the
14
- * right input for the question the register cannot answer: is this cheaper
15
- * same-vendor model capable enough to delegate to or to screen permissions
16
- * with? Claude Code's classifier rule is min(main, Sonnet); with a measured
17
- * scale it can mean what it says: a candidate whose coding index reaches the
18
- * lower of the session model's and Sonnet 5's, both read from the SAME
19
- * snapshot so the index rescaling every version cancels out.
20
- * docs/features/tiering/plan.md (Phase 2), docs/decisions/model-tiers.md.
21
- *
22
- * ## Data handling (the API's terms, findings §9)
23
- *
24
- * A personal key is required (401 without), the free tier is 1,000 requests a
25
- * day, responses must be cached, attribution is required wherever scores are
26
- * shown (`ATTRIBUTION`). So: the key comes from `AA_API_KEY` or One Code's own
27
- * `~/.onecode/settings.json` (`capabilityIndex.artificialAnalysisApiKey`, never
28
- * `~/.claude`); the response is cached under the One Code state dir and
29
- * refreshed at most once a day, only from an interactive session, never awaited
30
- * in `session_start`; a one-shot run reads the cache alone. Without a key every
31
- * verdict is "unscored" and selection keeps its name-class rules.
32
- *
33
- * ## Matching (the hard part)
34
- *
35
- * pi ids and Artificial Analysis slugs differ (`deepseek-chat-v3.1` vs
36
- * `deepseek-v3-1`, `claude-haiku-4-5` vs `claude-4-5-haiku`), effort variants
37
- * are separate rows (`-high`, `-non-reasoning`, …), and a slug can name a NEWER
38
- * build than pi's row: OpenRouter's `deepseek/deepseek-v4-flash` is the April
39
- * build (models.dev 2026-04-24) while the slug `deepseek-v4-flash` is the July
40
- * build (coding 69.1; the April build, `deepseek-v4-flash-0420`, scores 56.2).
41
- * A match therefore counts only when the row's release date agrees with the
42
- * models.dev date (`model-facts.ts`) to within `DATE_TOLERANCE_DAYS`; a model
43
- * without facts cannot be confirmed and stays unscored.
44
- */
45
-
46
- import { existsSync, readFileSync, statSync } from "node:fs";
47
- import { join } from "node:path";
48
- import { readJsonFile, writeJsonAtomic } from "./atomic-write.ts";
49
- import { fetchWithTimeout } from "./fetch-timeout.ts";
50
- import { modelFacts } from "./model-facts.ts";
51
- import { baseModelId, DAY_MS, modelIdentity, stripSnapshotDate } from "./model-policy.ts";
52
- import { oneCodeSettingsPath } from "./one-code-settings.ts";
53
-
54
- export const AA_KEY_ENV = "AA_API_KEY";
55
- export const AA_MODELS_URL = "https://artificialanalysis.ai/api/v2/data/llms/models";
56
- export const ATTRIBUTION = "Scores: Artificial Analysis (https://artificialanalysis.ai)";
57
- export const SETTINGS_SNIPPET = '{ "capabilityIndex": { "artificialAnalysisApiKey": "<key>" } }';
58
- /** The advice the doctor gives when no key is configured. */
59
- export const KEY_ADVICE =
60
- `Add a free Artificial Analysis API key (create one at https://artificialanalysis.ai) as ${AA_KEY_ENV} in the environment ` +
61
- `or ${SETTINGS_SNIPPET} in ~/.onecode/settings.json. With it, subagent and classifier picks are judged by measured coding ability ` +
62
- "instead of model names alone.";
63
-
64
- export const REFRESH_AFTER_MS = 24 * 60 * 60 * 1000;
65
- export const FETCH_TIMEOUT_MS = 20_000;
66
- /** A slug match is trusted only when the two release dates agree this closely. */
67
- export const DATE_TOLERANCE_DAYS = 31;
68
- /** The reference Claude Code's classifier rule names: min(main, Sonnet). */
69
- export const REFERENCE_SLUG = "claude-sonnet-5";
70
- /**
71
- * The prompt REGISTER's measured caps, as fractions of the Sonnet 5 reference
72
- * (the index rescales per version, so never absolute numbers): a model whose
73
- * coding index sits below `REGISTER_CHEAP_RATIO` × reference gets at most the
74
- * cheap register, below `REGISTER_TINY_RATIO` × reference at most tiny. A score
75
- * only ever ADDS scaffolding — it never lifts a lean-named model to a leaner
76
- * register (docs/decisions/model-tiers.md, 2026-09-11). On the 2026-09 snapshot
77
- * (reference 71.5) the lines are 60.8 and 42.9: gpt-5.1 (49.4), glm-4.7 (45.3)
78
- * and kimi-k2.5 (46.8) drop to cheap; gpt-5 (37.8), grok-4.3 (42.2) and
79
- * mistral-medium-3.1 (20.5) to tiny; gpt-5.4-mini (56.1) stays cheap.
80
- */
81
- export const REGISTER_CHEAP_RATIO = 0.85;
82
- export const REGISTER_TINY_RATIO = 0.6;
83
-
84
- /**
85
- * How far below the floor the low-stakes reader (web_fetch answers, recaps) may
86
- * score. The classifier and delegated workers allow nothing: a subagent writes
87
- * code and calls tools for many turns, and since 2026-09-11 it is held to the
88
- * same floor as the permission screener (docs/decisions/model-policy.md).
89
- */
90
- export const READER_TOLERANCE = 0.9;
91
-
92
- export interface CapabilityRow {
93
- id: string;
94
- slug: string;
95
- releaseDate: string;
96
- creator: string;
97
- coding?: number;
98
- intelligence?: number;
99
- }
100
-
101
- export interface CapabilitySnapshot {
102
- fetchedAt: string;
103
- source: string;
104
- rows: CapabilityRow[];
105
- }
106
-
107
- // ---------------------------------------------------------------------------
108
- // Key and cache
109
- // ---------------------------------------------------------------------------
110
-
111
- /** The key, env first (the ecosystem convention), else One Code's own settings file. Never `~/.claude`. */
112
- export function capabilityIndexKey(env: Record<string, string | undefined>, settings: unknown): string | undefined {
113
- const fromEnv = env[AA_KEY_ENV]?.trim();
114
- if (fromEnv) return fromEnv;
115
- if (settings && typeof settings === "object" && !Array.isArray(settings)) {
116
- const block = (settings as Record<string, unknown>).capabilityIndex;
117
- if (block && typeof block === "object" && !Array.isArray(block)) {
118
- const key = (block as Record<string, unknown>).artificialAnalysisApiKey;
119
- if (typeof key === "string" && key.trim()) return key.trim();
120
- }
121
- }
122
- return undefined;
123
- }
124
-
125
- /** The key as configured for a home: env first, then `~/.onecode/settings.json`. */
126
- export function configuredCapabilityKey(home: string, env: Record<string, string | undefined> = process.env): string | undefined {
127
- return capabilityIndexKey(env, readJsonFile(oneCodeSettingsPath(home, env as NodeJS.ProcessEnv)));
128
- }
129
-
130
- export function capabilityCachePath(stateDir: string): string {
131
- return join(stateDir, "cache", "artificial-analysis.json");
132
- }
133
-
134
- /** Reduce the API response to the fields selection needs; anything malformed is dropped. */
135
- export function snapshotFromResponse(body: unknown, fetchedAt: Date): CapabilitySnapshot {
136
- const rows: CapabilityRow[] = [];
137
- const data = body && typeof body === "object" ? (body as { data?: unknown }).data : undefined;
138
- if (Array.isArray(data)) {
139
- for (const raw of data) {
140
- if (!raw || typeof raw !== "object") continue;
141
- const row = raw as Record<string, unknown>;
142
- if (typeof row.id !== "string" || typeof row.slug !== "string" || typeof row.release_date !== "string") continue;
143
- const evaluations = (row.evaluations ?? {}) as Record<string, unknown>;
144
- const creator = (row.model_creator ?? {}) as Record<string, unknown>;
145
- const num = (value: unknown) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
146
- rows.push({
147
- id: row.id,
148
- slug: row.slug.toLowerCase(),
149
- releaseDate: row.release_date,
150
- creator: typeof creator.slug === "string" ? creator.slug : "",
151
- coding: num(evaluations.artificial_analysis_coding_index),
152
- intelligence: num(evaluations.artificial_analysis_intelligence_index),
153
- });
154
- }
155
- }
156
- return { fetchedAt: fetchedAt.toISOString(), source: AA_MODELS_URL, rows };
157
- }
158
-
159
- let memo: { path: string; mtimeMs: number; snapshot: CapabilitySnapshot | undefined; checkedAt: number } | undefined;
160
- /** How long a stat result is trusted before the cache file is stat'ed again. */
161
- const RESTAT_AFTER_MS = 1000;
162
- let pinned: { value: CapabilitySnapshot | undefined } | undefined;
163
-
164
- /** Test seam: pin the snapshot every selection call sees (`undefined` pins "none"). */
165
- export function setCapabilitySnapshotForTest(snapshot: CapabilitySnapshot | undefined): void {
166
- pinned = { value: snapshot };
167
- memo = undefined;
168
- }
169
-
170
- /** Test seam: unpin, so `loadCapabilitySnapshot` reads the disk cache again. */
171
- export function clearCapabilitySnapshotForTest(): void {
172
- pinned = undefined;
173
- memo = undefined;
174
- }
175
-
176
- /**
177
- * The cached snapshot, memoized on the file's mtime so a background refresh is
178
- * picked up without a restart. The stat itself is throttled to once a second:
179
- * since the register consults the snapshot (`measuredRegisterCap`), a /doctor
180
- * or presets render classifies every catalog row — hundreds of calls — and
181
- * without the throttle each was a `statSync` (an ENOENT throw for the keyless
182
- * majority). A refresh is still seen within a second.
183
- */
184
- export function loadCapabilitySnapshot(stateDir: string): CapabilitySnapshot | undefined {
185
- if (pinned) return pinned.value;
186
- const path = capabilityCachePath(stateDir);
187
- const now = Date.now();
188
- if (memo && memo.path === path && now - memo.checkedAt < RESTAT_AFTER_MS) return memo.snapshot;
189
- let mtimeMs: number;
190
- try {
191
- mtimeMs = statSync(path).mtimeMs;
192
- } catch {
193
- memo = { path, mtimeMs: -1, snapshot: undefined, checkedAt: now };
194
- return undefined;
195
- }
196
- if (memo && memo.path === path && memo.mtimeMs === mtimeMs) {
197
- memo.checkedAt = now;
198
- return memo.snapshot;
199
- }
200
- let snapshot: CapabilitySnapshot | undefined;
201
- try {
202
- const parsed = JSON.parse(readFileSync(path, "utf8")) as Partial<CapabilitySnapshot>;
203
- if (parsed && typeof parsed.fetchedAt === "string" && Array.isArray(parsed.rows)) {
204
- snapshot = { fetchedAt: parsed.fetchedAt, source: parsed.source ?? AA_MODELS_URL, rows: parsed.rows as CapabilityRow[] };
205
- }
206
- } catch {
207
- snapshot = undefined; // a corrupt cache is "no snapshot"; the next refresh rewrites it
208
- }
209
- memo = { path, mtimeMs, snapshot, checkedAt: now };
210
- return snapshot;
211
- }
212
-
213
- export function snapshotAgeMs(snapshot: CapabilitySnapshot, now: Date = new Date()): number {
214
- return Math.max(0, now.getTime() - Date.parse(snapshot.fetchedAt));
215
- }
216
-
217
- export function snapshotIsStale(snapshot: CapabilitySnapshot | undefined, now: Date = new Date()): boolean {
218
- return !snapshot || Number.isNaN(Date.parse(snapshot.fetchedAt)) || snapshotAgeMs(snapshot, now) > REFRESH_AFTER_MS;
219
- }
220
-
221
- export type RefreshOutcome = { status: "no-key" } | { status: "fresh" } | { status: "refreshed"; rows: number } | { status: "failed"; error: string };
222
-
223
- /**
224
- * Fetch and cache a new snapshot when the cached one is older than a day.
225
- * Bounded by `FETCH_TIMEOUT_MS`; never throws (a failure is an outcome the
226
- * caller may log once). Callers decide WHEN this may run: only from a session
227
- * that outlives the turn, never awaited in `session_start` (findings §15, §19).
228
- */
229
- export async function refreshCapabilitySnapshot(options: {
230
- key: string | undefined;
231
- stateDir: string;
232
- fetchImpl?: typeof fetch;
233
- now?: Date;
234
- force?: boolean;
235
- }): Promise<RefreshOutcome> {
236
- const { key, stateDir, fetchImpl, now = new Date(), force = false } = options;
237
- if (!key) return { status: "no-key" };
238
- if (!force && !snapshotIsStale(loadCapabilitySnapshot(stateDir), now)) return { status: "fresh" };
239
- // One live fetch per state dir at a time: the session_start refresh and a
240
- // /doctor run inside the same 20 s window must not both spend a quota call.
241
- const running = inflight.get(stateDir);
242
- if (running) return running;
243
- const attempt = fetchSnapshot(key, stateDir, fetchImpl, now).finally(() => inflight.delete(stateDir));
244
- inflight.set(stateDir, attempt);
245
- return attempt;
246
- }
247
-
248
- const inflight = new Map<string, Promise<RefreshOutcome>>();
249
-
250
- async function fetchSnapshot(key: string, stateDir: string, fetchImpl: typeof fetch | undefined, now: Date): Promise<RefreshOutcome> {
251
- try {
252
- const init: RequestInit = { headers: { "x-api-key": key, accept: "application/json" } };
253
- const response = fetchImpl
254
- ? await fetchImpl(AA_MODELS_URL, { ...init, signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) })
255
- : await fetchWithTimeout(AA_MODELS_URL, FETCH_TIMEOUT_MS, init);
256
- if (!response.ok) return { status: "failed", error: `Artificial Analysis responded ${response.status}` };
257
- const snapshot = snapshotFromResponse(await response.json(), now);
258
- if (snapshot.rows.length === 0) return { status: "failed", error: "Artificial Analysis returned no model rows" };
259
- writeJsonAtomic(capabilityCachePath(stateDir), snapshot);
260
- memo = undefined;
261
- return { status: "refreshed", rows: snapshot.rows.length };
262
- } catch (error) {
263
- return { status: "failed", error: error instanceof Error ? error.message : String(error) };
264
- }
265
- }
266
-
267
- export function capabilityCacheExists(stateDir: string): boolean {
268
- return existsSync(capabilityCachePath(stateDir));
269
- }
270
-
271
- // ---------------------------------------------------------------------------
272
- // Matching pi rows to snapshot rows
273
- // ---------------------------------------------------------------------------
274
-
275
- const EFFORT_SUFFIX = /-(non-reasoning|reasoning|thinking|adaptive|xhigh|high|medium|low|minimal|max)$/;
276
-
277
- /**
278
- * The slug with its effort suffixes removed — the family key a pi id is matched
279
- * to. `-max` (and `-high`, …) is an effort variant only when the shorter slug is
280
- * itself a row: `gpt-5-6-sol-max` belongs to `gpt-5-6-sol`, but `qwen3-8-max`
281
- * IS the flagship (there is no `qwen3-8` row) and must stay its own family.
282
- * Without `known`, every suffix is stripped (the tests' plain form).
283
- */
284
- export function baseSlug(slug: string, known?: ReadonlySet<string>): string {
285
- let current = slug;
286
- for (;;) {
287
- const next = current.replace(EFFORT_SUFFIX, "");
288
- if (next === current) return current;
289
- if (known && !known.has(next) && !hasKnownPrefix(next, known)) return current;
290
- current = next;
291
- }
292
- }
293
-
294
- /** Whether stripping further would still land on a known row (`gpt-5-6-sol-high-non-reasoning` → `…-high` → `gpt-5-6-sol`). */
295
- function hasKnownPrefix(slug: string, known: ReadonlySet<string>): boolean {
296
- let current = slug;
297
- for (;;) {
298
- const next = current.replace(EFFORT_SUFFIX, "");
299
- if (next === current) return false;
300
- if (known.has(next)) return true;
301
- current = next;
302
- }
303
- }
304
-
305
- /**
306
- * Candidate Artificial Analysis base slugs for a pi id, most specific first.
307
- * Exported for the tests; the spelling fixes here are the ones the catalog
308
- * needed on 2026-09-10 (findings §9) — add to them as mismatches are found.
309
- */
310
- export function slugCandidates(id: string): string[] {
311
- let s = baseModelId(id).toLowerCase(); // no `~` marker, no `:variant`
312
- const slash = s.indexOf("/");
313
- if (slash > 0) s = s.slice(slash + 1); // gateway route prefix
314
- s = stripSnapshotDate(s); // -YYYYMMDD / -MMDD
315
- s = s.replace(/\./g, "-");
316
- s = s.replace(/-(preview|latest|exp)(-\d{2}-\d{2})?$/, "");
317
- s = s.replace(/^deepseek-chat-v3/, "deepseek-v3").replace(/^deepseek-chat$/, "deepseek-v3");
318
- const out = new Set<string>([s]);
319
- out.add(s.replace(/-instruct$/, ""));
320
- out.add(s.replace(/-it$/, ""));
321
- // Artificial Analysis spells the Claude 4.x line `claude-4-5-haiku`, the 5.x line `claude-sonnet-5`.
322
- const claude = s.match(/^claude-(opus|sonnet|haiku)-(\d+(?:-\d+)?)$/);
323
- if (claude) out.add(`claude-${claude[2]}-${claude[1]}`);
324
- return [...out];
325
- }
326
-
327
- export type ScoreVariant = "default" | "non-reasoning";
328
-
329
- export interface CapabilityScore {
330
- /** The coding index — the number selection compares. */
331
- coding: number;
332
- intelligence?: number;
333
- slug: string;
334
- variant: ScoreVariant;
335
- releaseDate: string;
336
- }
337
-
338
- function withinTolerance(a: string, b: string): boolean {
339
- const ta = Date.parse(a);
340
- const tb = Date.parse(b);
341
- return !Number.isNaN(ta) && !Number.isNaN(tb) && Math.abs(ta - tb) <= DATE_TOLERANCE_DAYS * DAY_MS;
342
- }
343
-
344
- function rowsByBase(snapshot: CapabilitySnapshot): Map<string, CapabilityRow[]> {
345
- const cached = indexMemo.get(snapshot);
346
- if (cached) return cached;
347
- const map = new Map<string, CapabilityRow[]>();
348
- const known = new Set(snapshot.rows.map((row) => row.slug));
349
- for (const row of snapshot.rows) {
350
- const base = baseSlug(row.slug, known);
351
- const list = map.get(base);
352
- if (list) list.push(row);
353
- else map.set(base, [row]);
354
- }
355
- indexMemo.set(snapshot, map);
356
- return map;
357
- }
358
- const indexMemo = new WeakMap<CapabilitySnapshot, Map<string, CapabilityRow[]>>();
359
-
360
- /**
361
- * The score for a model at one variant, or undefined when it cannot be matched
362
- * with confidence. `expectedReleaseDate` is the models.dev date the match must
363
- * agree with; pass `undefined` only for the fixed reference row, which is
364
- * looked up by exact slug rather than matched.
365
- */
366
- export function scoreFor(
367
- snapshot: CapabilitySnapshot,
368
- model: { provider: string; id: string },
369
- variant: ScoreVariant,
370
- ): CapabilityScore | undefined {
371
- const facts = modelFacts(model);
372
- if (!facts) return undefined; // nothing to confirm a slug match against
373
- const families = rowsByBase(snapshot);
374
- // The identity's normalized id already peels gateway routes and hosted-provider
375
- // prefixes (Bedrock's `anthropic.` / `us.`, Cloudflare's `workers-ai/@cf/…`).
376
- for (const candidate of slugCandidates(modelIdentity(model as never).normalizedId)) {
377
- const rows = families.get(candidate);
378
- if (!rows) continue;
379
- const base = rows.find((row) => row.slug === candidate);
380
- if (!base || !withinTolerance(base.releaseDate, facts.releaseDate)) continue;
381
- return pick(rows, candidate, variant);
382
- }
383
- return undefined;
384
- }
385
-
386
- /** The reference model's score, by exact slug (no pi row to confirm it against). */
387
- export function referenceScore(snapshot: CapabilitySnapshot, variant: ScoreVariant, slug = REFERENCE_SLUG): CapabilityScore | undefined {
388
- const rows = rowsByBase(snapshot).get(slug);
389
- return rows ? pick(rows, slug, variant) : undefined;
390
- }
391
-
392
- function pick(rows: CapabilityRow[], base: string, variant: ScoreVariant): CapabilityScore | undefined {
393
- const wanted = variant === "default" ? base : `${base}-non-reasoning`;
394
- const row = rows.find((candidate) => candidate.slug === wanted);
395
- if (!row || row.coding === undefined) return undefined;
396
- return { coding: row.coding, intelligence: row.intelligence, slug: row.slug, variant, releaseDate: row.releaseDate };
397
- }
398
-
399
- // ---------------------------------------------------------------------------
400
- // The floor
401
- // ---------------------------------------------------------------------------
402
-
403
- export type FloorRole = "classifier" | "subagent" | "reader";
404
-
405
- export interface FloorVerdict {
406
- verdict: "pass" | "fail" | "unscored";
407
- /** Present for pass/fail: what was compared. */
408
- candidate?: CapabilityScore;
409
- session?: CapabilityScore;
410
- reference?: CapabilityScore;
411
- floor?: number;
412
- /** Why "unscored", for the doctor report. */
413
- reason?: string;
414
- }
415
-
416
- /**
417
- * Whether `candidate` measurably reaches min(session, Sonnet 5) for the role.
418
- * The classifier calls with thinking OFF, so it compares non-reasoning variant
419
- * scores (Sonnet 5 drops from 71.5 to 66.4, GLM-5.2 from 68.8 to 46.5); when
420
- * any of the three lacks a non-reasoning row, all three fall back to the default
421
- * variant so the comparison stays on one basis. Delegated workers run with
422
- * thinking, so they are judged on the default variant, at the full floor; only
423
- * the reader may sit `READER_TOLERANCE` below it. Unscored means the caller
424
- * applies its name-class rule instead — the score never lowers a floor it
425
- * cannot measure.
426
- */
427
- export function capabilityFloor(
428
- snapshot: CapabilitySnapshot | undefined,
429
- candidate: { provider: string; id: string },
430
- session: { provider: string; id: string },
431
- role: FloorRole,
432
- ): FloorVerdict {
433
- if (!snapshot) return { verdict: "unscored", reason: "no capability snapshot (no key)" };
434
- // Memoized per snapshot: the permission badge re-derives the classifier chain
435
- // on every repaint, and the verdict for a (candidate, session, role) triple
436
- // cannot change while the snapshot object lives.
437
- const key = `${role}|${candidate.provider}/${candidate.id}|${session.provider}/${session.id}`;
438
- let memoized = verdictMemo.get(snapshot);
439
- if (!memoized) verdictMemo.set(snapshot, (memoized = new Map()));
440
- const cached = memoized.get(key);
441
- if (cached) return cached;
442
- const verdict = computeFloor(snapshot, candidate, session, role);
443
- memoized.set(key, verdict);
444
- return verdict;
445
- }
446
- const verdictMemo = new WeakMap<CapabilitySnapshot, Map<string, FloorVerdict>>();
447
-
448
- function computeFloor(
449
- snapshot: CapabilitySnapshot,
450
- candidate: { provider: string; id: string },
451
- session: { provider: string; id: string },
452
- role: FloorRole,
453
- ): FloorVerdict {
454
- const variants: ScoreVariant[] = role === "classifier" ? ["non-reasoning", "default"] : ["default"];
455
- let last: { c?: CapabilityScore; s?: CapabilityScore; r?: CapabilityScore } = {};
456
- for (const variant of variants) {
457
- last = { c: scoreFor(snapshot, candidate, variant), s: scoreFor(snapshot, session, variant), r: referenceScore(snapshot, variant) };
458
- const { c, s, r } = last;
459
- if (!c || !s || !r) continue;
460
- const floor = Math.min(s.coding, r.coding) * (role === "reader" ? READER_TOLERANCE : 1);
461
- return { verdict: c.coding >= floor ? "pass" : "fail", candidate: c, session: s, reference: r, floor };
462
- }
463
- // `last` holds the "default" variant's lookups (always the final one tried).
464
- const missing = [!last.c && "candidate", !last.s && "session model", !last.r && "reference"].filter(Boolean).join(", ");
465
- return { verdict: "unscored", reason: `no confirmed score for: ${missing}` };
466
- }