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.
- package/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +40 -28
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +26 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +124 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +642 -0
- package/extensions/background/index.ts +614 -248
- package/extensions/background/loop-fire.ts +173 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +56 -0
- package/extensions/background/wakeup.ts +178 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +66 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +250 -78
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/cache-marks/index.ts +42 -0
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +436 -63
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +219 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/budget.ts +16 -2
- package/extensions/context-budget/index.ts +35 -15
- package/extensions/context-management/index.ts +36 -28
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +141 -25
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +420 -42
- package/extensions/file-tracker/replay.ts +137 -24
- package/extensions/file-tracker/restore.ts +66 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +45 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +209 -58
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/prompt-context.ts +59 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +26 -12
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/ledger/index.ts +46 -0
- package/extensions/ledger/wire.ts +154 -0
- package/extensions/lib/agent-cron.ts +146 -0
- package/extensions/lib/agent-loader.ts +18 -3
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-auth.ts +98 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +52 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +97 -0
- package/extensions/lib/covered-messages.ts +23 -0
- package/extensions/lib/deferred.ts +359 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/ledger/fold-memo.ts +49 -0
- package/extensions/lib/ledger/fold.ts +265 -0
- package/extensions/lib/ledger/layout.ts +72 -0
- package/extensions/lib/ledger/plan.ts +102 -0
- package/extensions/lib/ledger/records.ts +276 -0
- package/extensions/lib/ledger/state.ts +47 -0
- package/extensions/lib/ledger/validate.ts +141 -0
- package/extensions/lib/ledger/wire.ts +632 -0
- package/extensions/lib/local-command.ts +71 -19
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +178 -0
- package/extensions/lib/model-catalog/models-dev.json +18533 -0
- package/extensions/lib/model-catalog/openrouter.json +5050 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +357 -73
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +38 -2
- package/extensions/lib/plan-mode-channels.ts +42 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/previous-mark.ts +101 -0
- package/extensions/lib/process-state.ts +48 -0
- package/extensions/lib/process-tree.ts +132 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +370 -358
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +547 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +24 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +112 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +129 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +159 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +76 -12
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +90 -43
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +220 -121
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +164 -73
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/model-default/index.ts +86 -19
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1426 -398
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +11 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +276 -92
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/run-state.ts +26 -0
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +64 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +14 -7
- package/extensions/recap/index.ts +73 -71
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/announce.ts +30 -0
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +469 -132
- package/extensions/skill/invoke.ts +118 -75
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +146 -31
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +82 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1594 -852
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +5 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/parent-mode.ts +50 -0
- package/extensions/subagents/pending-claim.ts +18 -1
- package/extensions/subagents/runner.ts +135 -80
- package/extensions/subagents/runs.ts +44 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +165 -47
- package/extensions/system-prompt/template.ts +34 -51
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +204 -70
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/index.ts +366 -144
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +108 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +157 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +33 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +162 -44
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +21 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
- 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
|
-
}
|