@kr78/pi-coding-agent 17.2.10
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/CHANGELOG.md +14861 -0
- package/README.md +35 -0
- package/dist/CHANGELOG-chtdxh1z.md +14861 -0
- package/dist/cli.js +20483 -0
- package/dist/template-c2hyaytt.js +1653 -0
- package/dist/template-f8wx9vfn.css +1355 -0
- package/dist/template-qat058wr.html +55 -0
- package/dist/tool-views.generated-jdfmzwmn.js +35 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +104 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/extensions/README.md +142 -0
- package/examples/extensions/api-demo.ts +78 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +31 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +549 -0
- package/examples/extensions/reload-runtime.ts +38 -0
- package/examples/extensions/thinking-note.ts +13 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +17 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +46 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +82 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/12-redis-sessions.ts +54 -0
- package/examples/sdk/13-sql-sessions.ts +61 -0
- package/examples/sdk/README.md +169 -0
- package/package.json +587 -0
- package/scripts/bench-guard.ts +71 -0
- package/scripts/bench-title-models.ts +332 -0
- package/scripts/build-binary.ts +117 -0
- package/scripts/bundle-dist.ts +123 -0
- package/scripts/compile-binary.ts +69 -0
- package/scripts/embed-mupdf-wasm.ts +67 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-aria-snapshot.ts +134 -0
- package/scripts/generate-docs-index.ts +58 -0
- package/scripts/generate-share-viewer.ts +34 -0
- package/scripts/legacy-pi-virtual-module.ts +205 -0
- package/scripts/measure-prompt-tokens.ts +63 -0
- package/scripts/omp +51 -0
- package/scripts/omp.ts +19 -0
- package/scripts/security-compare.ts +40 -0
- package/src/advisor/advise-tool.ts +234 -0
- package/src/advisor/config.ts +341 -0
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +6 -0
- package/src/advisor/runtime.ts +1412 -0
- package/src/advisor/transcript-recorder.ts +215 -0
- package/src/advisor/watchdog.ts +135 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +847 -0
- package/src/auto-thinking/classifier.ts +220 -0
- package/src/autolearn/controller.ts +152 -0
- package/src/autolearn/managed-skills.ts +255 -0
- package/src/autoresearch/command-resume.md +14 -0
- package/src/autoresearch/dashboard.ts +436 -0
- package/src/autoresearch/git.ts +331 -0
- package/src/autoresearch/helpers.ts +218 -0
- package/src/autoresearch/index.ts +541 -0
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +103 -0
- package/src/autoresearch/resume-message.md +10 -0
- package/src/autoresearch/state.ts +273 -0
- package/src/autoresearch/storage.ts +700 -0
- package/src/autoresearch/tools/init-experiment.ts +268 -0
- package/src/autoresearch/tools/log-experiment.ts +520 -0
- package/src/autoresearch/tools/run-experiment.ts +407 -0
- package/src/autoresearch/tools/update-notes.ts +109 -0
- package/src/autoresearch/types.ts +168 -0
- package/src/capability/context-file.ts +44 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +117 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +467 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +115 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule-buckets.ts +66 -0
- package/src/capability/rule.ts +298 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +69 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +187 -0
- package/src/cleanse/agent.ts +226 -0
- package/src/cleanse/balance.ts +79 -0
- package/src/cleanse/checkers.ts +996 -0
- package/src/cleanse/index.ts +190 -0
- package/src/cleanse/loop.ts +51 -0
- package/src/cleanse/parsers.ts +726 -0
- package/src/cleanse/progress.ts +50 -0
- package/src/cleanse/prompts/assignment.md +47 -0
- package/src/cleanse/types.ts +72 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +368 -0
- package/src/cli/auth-broker-cli.ts +940 -0
- package/src/cli/auth-gateway-cli.ts +674 -0
- package/src/cli/bench-cli.ts +990 -0
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -0
- package/src/cli/command-help.ts +107 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/config-cli.ts +459 -0
- package/src/cli/dry-balance-cli.ts +864 -0
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/file-processor.ts +132 -0
- package/src/cli/flag-tables.ts +367 -0
- package/src/cli/gallery-cli.ts +272 -0
- package/src/cli/gallery-fixtures/agentic.ts +420 -0
- package/src/cli/gallery-fixtures/codeintel.ts +187 -0
- package/src/cli/gallery-fixtures/edit.ts +254 -0
- package/src/cli/gallery-fixtures/fs.ts +245 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +46 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +177 -0
- package/src/cli/gallery-fixtures/search.ts +135 -0
- package/src/cli/gallery-fixtures/shell.ts +241 -0
- package/src/cli/gallery-fixtures/types.ts +57 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli/gc-cli.ts +1566 -0
- package/src/cli/grep-cli.ts +161 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/models-cli.ts +385 -0
- package/src/cli/plugin-cli.ts +996 -0
- package/src/cli/profile-alias.ts +369 -0
- package/src/cli/profile-bootstrap.ts +233 -0
- package/src/cli/read-cli.ts +99 -0
- package/src/cli/session-picker.ts +110 -0
- package/src/cli/setup-cli.ts +312 -0
- package/src/cli/setup-model-picker.ts +43 -0
- package/src/cli/shell-cli.ts +176 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/startup-cwd.ts +58 -0
- package/src/cli/stats-cli.ts +229 -0
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/tiny-models-cli.ts +153 -0
- package/src/cli/ttsr-cli.ts +1013 -0
- package/src/cli/update-cli.ts +1184 -0
- package/src/cli/usage-cli.ts +1084 -0
- package/src/cli/usage-error.ts +7 -0
- package/src/cli/web-search-cli.ts +144 -0
- package/src/cli/worktree-cli.ts +311 -0
- package/src/cli-commands.ts +308 -0
- package/src/cli.ts +428 -0
- package/src/collab/crypto.ts +63 -0
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +764 -0
- package/src/collab/host.ts +692 -0
- package/src/collab/protocol.ts +296 -0
- package/src/collab/relay-client.ts +282 -0
- package/src/collab/replication-shrink.ts +111 -0
- package/src/commands/acp.ts +35 -0
- package/src/commands/agents.ts +58 -0
- package/src/commands/auth-broker.ts +100 -0
- package/src/commands/auth-gateway.ts +70 -0
- package/src/commands/bench.ts +64 -0
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +45 -0
- package/src/commands/commit.ts +47 -0
- package/src/commands/complete.ts +67 -0
- package/src/commands/completions.ts +61 -0
- package/src/commands/config.ts +52 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +61 -0
- package/src/commands/gc.ts +47 -0
- package/src/commands/grep.ts +49 -0
- package/src/commands/grievances.ts +52 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/join.ts +40 -0
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +34 -0
- package/src/commands/models.ts +61 -0
- package/src/commands/plugin.ts +79 -0
- package/src/commands/read.ts +39 -0
- package/src/commands/say.ts +146 -0
- package/src/commands/setup.ts +68 -0
- package/src/commands/share.ts +71 -0
- package/src/commands/shell.ts +30 -0
- package/src/commands/ssh.ts +61 -0
- package/src/commands/stats.ts +30 -0
- package/src/commands/tiny-models.ts +36 -0
- package/src/commands/token.ts +164 -0
- package/src/commands/ttsr.ts +125 -0
- package/src/commands/update.ts +34 -0
- package/src/commands/usage.ts +55 -0
- package/src/commands/web-search.ts +43 -0
- package/src/commands/worktree.ts +63 -0
- package/src/commit/agentic/agent.ts +322 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +383 -0
- package/src/commit/agentic/lock-files.ts +107 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +148 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +52 -0
- package/src/commit/agentic/tools/git-overview.ts +62 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +147 -0
- package/src/commit/agentic/tools/propose-commit.ts +109 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +11 -0
- package/src/commit/agentic/tools/split-commit.ts +241 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +64 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +107 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +101 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +49 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +95 -0
- package/src/commit/pipeline.ts +244 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/shared-llm.ts +70 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +58 -0
- package/src/config/api-key-resolver.ts +81 -0
- package/src/config/append-only-context-mode.ts +76 -0
- package/src/config/config-file.ts +347 -0
- package/src/config/inline-tool-descriptors-mode.ts +26 -0
- package/src/config/keybindings.ts +703 -0
- package/src/config/mcp-schema.json +252 -0
- package/src/config/model-discovery.ts +1077 -0
- package/src/config/model-registry.ts +2822 -0
- package/src/config/model-resolver.ts +2115 -0
- package/src/config/model-roles.ts +113 -0
- package/src/config/models-config-schema-bundle.ts +315 -0
- package/src/config/models-config-schema.ts +14 -0
- package/src/config/models-config.ts +130 -0
- package/src/config/prompt-templates.ts +205 -0
- package/src/config/provider-globals.ts +25 -0
- package/src/config/resolve-config-value.ts +95 -0
- package/src/config/service-tier.ts +146 -0
- package/src/config/settings-schema.ts +5887 -0
- package/src/config/settings.ts +2394 -0
- package/src/config.ts +242 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +960 -0
- package/src/dap/client.ts +1043 -0
- package/src/dap/config.ts +480 -0
- package/src/dap/defaults.json +212 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1841 -0
- package/src/dap/types.ts +611 -0
- package/src/debug/index.ts +584 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +966 -0
- package/src/debug/profiler.ts +168 -0
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/raw-sse-buffer.ts +421 -0
- package/src/debug/raw-sse.ts +312 -0
- package/src/debug/remote-debugger.ts +151 -0
- package/src/debug/report-bundle.ts +411 -0
- package/src/debug/system-info.ts +111 -0
- package/src/debug/terminal-info.ts +124 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +300 -0
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
- package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
- package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
- package/src/discovery/builtin-rules/go-ioutil.md +37 -0
- package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
- package/src/discovery/builtin-rules/go-new-expr.md +44 -0
- package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
- package/src/discovery/builtin-rules/go-range-int.md +45 -0
- package/src/discovery/builtin-rules/index.ts +74 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
- package/src/discovery/builtin-rules/rs-result-type.md +20 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
- package/src/discovery/builtin-rules/ts-import-type.md +43 -0
- package/src/discovery/builtin-rules/ts-no-any.md +66 -0
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/builtin.ts +945 -0
- package/src/discovery/claude-plugins.ts +640 -0
- package/src/discovery/claude.ts +591 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +553 -0
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/cursor.ts +223 -0
- package/src/discovery/gemini.ts +386 -0
- package/src/discovery/github.ts +337 -0
- package/src/discovery/helpers.ts +1183 -0
- package/src/discovery/index.ts +82 -0
- package/src/discovery/mcp-json.ts +182 -0
- package/src/discovery/omp-extension-roots.ts +272 -0
- package/src/discovery/omp-plugins.ts +409 -0
- package/src/discovery/opencode.ts +441 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +77 -0
- package/src/discovery/vscode.ts +106 -0
- package/src/discovery/windsurf.ts +149 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +1008 -0
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/file-snapshot-store.ts +148 -0
- package/src/edit/hashline/block-resolver.ts +33 -0
- package/src/edit/hashline/diff.ts +402 -0
- package/src/edit/hashline/execute.ts +313 -0
- package/src/edit/hashline/filesystem.ts +247 -0
- package/src/edit/hashline/index.ts +5 -0
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/hashline/params.ts +12 -0
- package/src/edit/index.ts +699 -0
- package/src/edit/modes/apply-patch.lark +19 -0
- package/src/edit/modes/apply-patch.ts +53 -0
- package/src/edit/modes/patch.ts +1958 -0
- package/src/edit/modes/replace.ts +1191 -0
- package/src/edit/normalize.ts +345 -0
- package/src/edit/notebook.ts +242 -0
- package/src/edit/read-file.ts +25 -0
- package/src/edit/renderer.ts +998 -0
- package/src/edit/snapshot-details.ts +77 -0
- package/src/edit/streaming.ts +710 -0
- package/src/eval/agent-bridge.ts +224 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/backend.ts +71 -0
- package/src/eval/bridge-timeout.ts +65 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +212 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/executor-base.ts +562 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +6 -0
- package/src/eval/jl/executor.ts +551 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +236 -0
- package/src/eval/jl/prelude.jl +736 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +666 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/context-manager.ts +872 -0
- package/src/eval/js/executor.ts +178 -0
- package/src/eval/js/index.ts +41 -0
- package/src/eval/js/process-entry.ts +31 -0
- package/src/eval/js/shared/helpers.ts +170 -0
- package/src/eval/js/shared/indirect-eval.ts +30 -0
- package/src/eval/js/shared/local-module-loader.ts +364 -0
- package/src/eval/js/shared/prelude.ts +2 -0
- package/src/eval/js/shared/prelude.txt +294 -0
- package/src/eval/js/shared/rewrite-imports.ts +550 -0
- package/src/eval/js/shared/runtime.ts +590 -0
- package/src/eval/js/shared/types.ts +18 -0
- package/src/eval/js/tool-bridge.ts +163 -0
- package/src/eval/js/worker-core.ts +380 -0
- package/src/eval/js/worker-entry.ts +37 -0
- package/src/eval/js/worker-protocol.ts +47 -0
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/display.ts +71 -0
- package/src/eval/py/executor.ts +643 -0
- package/src/eval/py/index.ts +57 -0
- package/src/eval/py/kernel.ts +235 -0
- package/src/eval/py/prelude.py +673 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1425 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +134 -0
- package/src/eval/py/tool-bridge.ts +201 -0
- package/src/eval/rb/executor.ts +511 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +231 -0
- package/src/eval/rb/prelude.rb +551 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +581 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/session-id.ts +8 -0
- package/src/eval/types.ts +48 -0
- package/src/exa/index.ts +2 -0
- package/src/exa/mcp-client.ts +370 -0
- package/src/exa/types.ts +69 -0
- package/src/exec/bash-executor.ts +627 -0
- package/src/exec/direnv.ts +145 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/non-interactive-env.ts +118 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +320 -0
- package/src/export/html/share-loader.js +102 -0
- package/src/export/html/template.css +1355 -0
- package/src/export/html/template.html +55 -0
- package/src/export/html/template.js +1653 -0
- package/src/export/html/tool-views.generated.js +35 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/html/web-palette.ts +126 -0
- package/src/export/share.ts +686 -0
- package/src/export/ttsr.ts +590 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +242 -0
- package/src/extensibility/custom-commands/types.ts +119 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +301 -0
- package/src/extensibility/custom-tools/types.ts +287 -0
- package/src/extensibility/custom-tools/wrapper.ts +50 -0
- package/src/extensibility/extensions/compact-handler.ts +40 -0
- package/src/extensibility/extensions/get-commands-handler.ts +78 -0
- package/src/extensibility/extensions/index.ts +16 -0
- package/src/extensibility/extensions/load-errors.ts +13 -0
- package/src/extensibility/extensions/loader.ts +663 -0
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/model-api.ts +39 -0
- package/src/extensibility/extensions/runner.ts +1396 -0
- package/src/extensibility/extensions/types.ts +1603 -0
- package/src/extensibility/extensions/wrapper.ts +395 -0
- package/src/extensibility/hooks/index.ts +6 -0
- package/src/extensibility/hooks/loader.ts +243 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +124 -0
- package/src/extensibility/hooks/types.ts +612 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
- package/src/extensibility/legacy-typebox.ts +101 -0
- package/src/extensibility/plugins/bun-git-cache.ts +91 -0
- package/src/extensibility/plugins/doctor.ts +65 -0
- package/src/extensibility/plugins/git-url.ts +367 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +201 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
- package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
- package/src/extensibility/plugins/loader.ts +535 -0
- package/src/extensibility/plugins/manager.ts +1142 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +927 -0
- package/src/extensibility/plugins/marketplace/registry.ts +192 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +192 -0
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/extensibility/plugins/parser.ts +107 -0
- package/src/extensibility/plugins/runtime-config.ts +9 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/extensibility/shared-events.ts +401 -0
- package/src/extensibility/skills.ts +531 -0
- package/src/extensibility/slash-commands.ts +131 -0
- package/src/extensibility/tool-event-input.ts +80 -0
- package/src/extensibility/tool-proxy.ts +28 -0
- package/src/extensibility/utils.ts +192 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +521 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +251 -0
- package/src/hindsight/backend.ts +354 -0
- package/src/hindsight/bank.ts +156 -0
- package/src/hindsight/client.ts +680 -0
- package/src/hindsight/config.ts +193 -0
- package/src/hindsight/content.ts +266 -0
- package/src/hindsight/index.ts +8 -0
- package/src/hindsight/mental-models.ts +429 -0
- package/src/hindsight/seeds.json +32 -0
- package/src/hindsight/state.ts +530 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/index.ts +66 -0
- package/src/internal-urls/agent-protocol.ts +180 -0
- package/src/internal-urls/artifact-protocol.ts +151 -0
- package/src/internal-urls/docs-index.ts +102 -0
- package/src/internal-urls/filesystem-resource.ts +34 -0
- package/src/internal-urls/history-protocol.ts +198 -0
- package/src/internal-urls/index.ts +28 -0
- package/src/internal-urls/issue-pr-protocol.ts +594 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +471 -0
- package/src/internal-urls/mcp-protocol.ts +168 -0
- package/src/internal-urls/memory-protocol.ts +380 -0
- package/src/internal-urls/omp-protocol.ts +94 -0
- package/src/internal-urls/parse.ts +103 -0
- package/src/internal-urls/registry-helpers.ts +132 -0
- package/src/internal-urls/router.ts +153 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/security-protocol.ts +261 -0
- package/src/internal-urls/skill-protocol.ts +131 -0
- package/src/internal-urls/ssh-protocol.ts +368 -0
- package/src/internal-urls/types.ts +196 -0
- package/src/internal-urls/vault-protocol.ts +940 -0
- package/src/internal-urls/xd-protocol.ts +46 -0
- package/src/irc/bus.ts +380 -0
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/launch/broker.ts +1368 -0
- package/src/launch/client.ts +521 -0
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +14 -0
- package/src/launch/presence.ts +82 -0
- package/src/launch/protocol.ts +448 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/launch/terminal-output.ts +46 -0
- package/src/lib/xai-http.ts +150 -0
- package/src/live/attestation.ts +91 -0
- package/src/live/controller.ts +517 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +422 -0
- package/src/live/visualizer.ts +221 -0
- package/src/live/voices.ts +18 -0
- package/src/lsp/client.ts +1465 -0
- package/src/lsp/clients/biome-client.ts +263 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +85 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +549 -0
- package/src/lsp/defaults.json +499 -0
- package/src/lsp/deferred-diagnostics.ts +66 -0
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/edits.ts +288 -0
- package/src/lsp/format-options.ts +119 -0
- package/src/lsp/index.ts +2821 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/render.ts +668 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +479 -0
- package/src/lsp/utils.ts +747 -0
- package/src/main.ts +1799 -0
- package/src/markit/NOTICE +32 -0
- package/src/markit/converters/docx.ts +56 -0
- package/src/markit/converters/epub.ts +136 -0
- package/src/markit/converters/pdf/columns.ts +103 -0
- package/src/markit/converters/pdf/extract.ts +598 -0
- package/src/markit/converters/pdf/grid.ts +780 -0
- package/src/markit/converters/pdf/headers.ts +106 -0
- package/src/markit/converters/pdf/index.ts +146 -0
- package/src/markit/converters/pdf/render.ts +501 -0
- package/src/markit/converters/pdf/types.ts +84 -0
- package/src/markit/converters/pptx.ts +325 -0
- package/src/markit/converters/xlsx.ts +173 -0
- package/src/markit/index.ts +2 -0
- package/src/markit/registry.ts +59 -0
- package/src/markit/types.ts +35 -0
- package/src/mcp/client.ts +511 -0
- package/src/mcp/config-writer.ts +377 -0
- package/src/mcp/config.ts +385 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +122 -0
- package/src/mcp/loader.ts +125 -0
- package/src/mcp/manager.ts +1524 -0
- package/src/mcp/oauth-credentials.ts +104 -0
- package/src/mcp/oauth-discovery.ts +587 -0
- package/src/mcp/oauth-flow.ts +830 -0
- package/src/mcp/render.ts +214 -0
- package/src/mcp/request-id.ts +24 -0
- package/src/mcp/smithery-auth.ts +108 -0
- package/src/mcp/smithery-connect.ts +154 -0
- package/src/mcp/smithery-registry.ts +500 -0
- package/src/mcp/startup-events.ts +116 -0
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/tool-bridge.ts +691 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +508 -0
- package/src/mcp/transports/index.ts +7 -0
- package/src/mcp/transports/sse.ts +372 -0
- package/src/mcp/transports/stdio.ts +905 -0
- package/src/mcp/types.ts +467 -0
- package/src/memories/index.ts +1434 -0
- package/src/memories/storage.ts +578 -0
- package/src/memory-backend/index.ts +19 -0
- package/src/memory-backend/local-backend.ts +47 -0
- package/src/memory-backend/messages.ts +19 -0
- package/src/memory-backend/off-backend.ts +25 -0
- package/src/memory-backend/resolve.ts +25 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/memory-backend/types.ts +166 -0
- package/src/mnemopi/backend.ts +629 -0
- package/src/mnemopi/config.ts +267 -0
- package/src/mnemopi/embed-client.ts +293 -0
- package/src/mnemopi/embed-protocol.ts +35 -0
- package/src/mnemopi/embed-worker.ts +114 -0
- package/src/mnemopi/index.ts +3 -0
- package/src/mnemopi/state.ts +940 -0
- package/src/modes/acp/acp-agent.ts +2595 -0
- package/src/modes/acp/acp-client-bridge.ts +154 -0
- package/src/modes/acp/acp-event-mapper.ts +1084 -0
- package/src/modes/acp/acp-mode.ts +48 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/advisor-config.ts +635 -0
- package/src/modes/components/advisor-message.ts +109 -0
- package/src/modes/components/agent-dashboard.ts +1252 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +1126 -0
- package/src/modes/components/agent-transcript-viewer.ts +647 -0
- package/src/modes/components/ask-dialog.ts +1017 -0
- package/src/modes/components/assistant-message.ts +978 -0
- package/src/modes/components/background-tan-message.ts +36 -0
- package/src/modes/components/bash-execution.ts +233 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/btw-panel.ts +161 -0
- package/src/modes/components/cache-invalidation-marker.ts +110 -0
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/chat-transcript-builder.ts +518 -0
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/collab-prompt-message.ts +32 -0
- package/src/modes/components/compaction-summary-message.ts +221 -0
- package/src/modes/components/copy-selector.ts +218 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +1036 -0
- package/src/modes/components/custom-message.ts +70 -0
- package/src/modes/components/diff.ts +254 -0
- package/src/modes/components/dynamic-border.ts +37 -0
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/eval-execution.ts +169 -0
- package/src/modes/components/execution-shared.ts +101 -0
- package/src/modes/components/extensions/extension-dashboard.ts +492 -0
- package/src/modes/components/extensions/extension-list.ts +507 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +326 -0
- package/src/modes/components/extensions/state-manager.ts +648 -0
- package/src/modes/components/extensions/types.ts +186 -0
- package/src/modes/components/footer.ts +274 -0
- package/src/modes/components/history-search.ts +268 -0
- package/src/modes/components/hook-editor.ts +213 -0
- package/src/modes/components/hook-input.ts +87 -0
- package/src/modes/components/hook-message.ts +67 -0
- package/src/modes/components/hook-selector.ts +691 -0
- package/src/modes/components/index.ts +42 -0
- package/src/modes/components/keybinding-hints.ts +56 -0
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/login-dialog.ts +197 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1413 -0
- package/src/modes/components/message-frame.ts +98 -0
- package/src/modes/components/model-browser.ts +888 -0
- package/src/modes/components/model-hub.ts +2014 -0
- package/src/modes/components/model-picker.ts +237 -0
- package/src/modes/components/move-overlay.ts +293 -0
- package/src/modes/components/oauth-selector.ts +474 -0
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/overlay-box.ts +109 -0
- package/src/modes/components/pause-screen.ts +208 -0
- package/src/modes/components/plan-review-overlay.ts +1226 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/plugin-selector.ts +100 -0
- package/src/modes/components/plugin-settings.ts +745 -0
- package/src/modes/components/queue-mode-selector.ts +61 -0
- package/src/modes/components/read-tool-group.ts +856 -0
- package/src/modes/components/reset-usage-selector.ts +161 -0
- package/src/modes/components/segment-track.ts +89 -0
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/session-account-selector.ts +62 -0
- package/src/modes/components/session-selector.ts +1035 -0
- package/src/modes/components/settings-defs.ts +267 -0
- package/src/modes/components/settings-selector.ts +1445 -0
- package/src/modes/components/show-images-selector.ts +50 -0
- package/src/modes/components/skill-message.ts +110 -0
- package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
- package/src/modes/components/snapcompact-shape-preview.ts +192 -0
- package/src/modes/components/status-line/component.ts +1843 -0
- package/src/modes/components/status-line/context-thresholds.ts +86 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/index.ts +5 -0
- package/src/modes/components/status-line/presets.ts +106 -0
- package/src/modes/components/status-line/segments.ts +710 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/types.ts +159 -0
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/theme-selector.ts +68 -0
- package/src/modes/components/thinking-selector.ts +57 -0
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +26 -0
- package/src/modes/components/todo-reminder.ts +43 -0
- package/src/modes/components/tool-execution.ts +1467 -0
- package/src/modes/components/transcript-container.ts +527 -0
- package/src/modes/components/tree-selector.ts +1006 -0
- package/src/modes/components/ttsr-notification.ts +123 -0
- package/src/modes/components/usage-row.ts +52 -0
- package/src/modes/components/user-message-selector.ts +227 -0
- package/src/modes/components/user-message.ts +167 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +578 -0
- package/src/modes/controllers/btw-controller.ts +246 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +2024 -0
- package/src/modes/controllers/event-controller.ts +2173 -0
- package/src/modes/controllers/extension-ui-controller.ts +1243 -0
- package/src/modes/controllers/input-controller.ts +2037 -0
- package/src/modes/controllers/live-command-controller.ts +261 -0
- package/src/modes/controllers/mcp-command-controller.ts +2552 -0
- package/src/modes/controllers/omfg-controller.ts +287 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +2058 -0
- package/src/modes/controllers/session-focus-controller.ts +117 -0
- package/src/modes/controllers/ssh-command-controller.ts +385 -0
- package/src/modes/controllers/streaming-reveal.ts +399 -0
- package/src/modes/controllers/tan-command-controller.ts +243 -0
- package/src/modes/controllers/todo-command-controller.ts +487 -0
- package/src/modes/controllers/tool-args-reveal.ts +591 -0
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/github-ref-autocomplete.ts +75 -0
- package/src/modes/gradient-highlight.ts +99 -0
- package/src/modes/image-references.ts +137 -0
- package/src/modes/index.ts +10 -0
- package/src/modes/interactive-mode.ts +5055 -0
- package/src/modes/internal-url-autocomplete.ts +158 -0
- package/src/modes/loop-limit.ts +192 -0
- package/src/modes/magic-keyword-boundary.ts +23 -0
- package/src/modes/magic-keywords.ts +42 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/oauth-manual-input.ts +69 -0
- package/src/modes/orchestrate.ts +43 -0
- package/src/modes/print-mode.ts +282 -0
- package/src/modes/prompt-action-autocomplete.ts +322 -0
- package/src/modes/queue-input.ts +132 -0
- package/src/modes/rpc/host-tools.ts +204 -0
- package/src/modes/rpc/host-uris.ts +241 -0
- package/src/modes/rpc/rpc-client.ts +1213 -0
- package/src/modes/rpc/rpc-frame.ts +316 -0
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +1519 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +544 -0
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/runtime-init.ts +144 -0
- package/src/modes/session-observer-registry.ts +223 -0
- package/src/modes/session-teardown.ts +82 -0
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +103 -0
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
- package/src/modes/setup-wizard/scenes/model.ts +132 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +105 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +330 -0
- package/src/modes/setup-wizard/scenes/types.ts +65 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
- package/src/modes/shared.ts +49 -0
- package/src/modes/skill-command.ts +91 -0
- package/src/modes/theme/dark.json +95 -0
- package/src/modes/theme/defaults/alabaster.json +93 -0
- package/src/modes/theme/defaults/amethyst.json +96 -0
- package/src/modes/theme/defaults/anthracite.json +93 -0
- package/src/modes/theme/defaults/basalt.json +91 -0
- package/src/modes/theme/defaults/birch.json +95 -0
- package/src/modes/theme/defaults/dark-abyss.json +91 -0
- package/src/modes/theme/defaults/dark-arctic.json +104 -0
- package/src/modes/theme/defaults/dark-aurora.json +95 -0
- package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
- package/src/modes/theme/defaults/dark-cavern.json +91 -0
- package/src/modes/theme/defaults/dark-copper.json +95 -0
- package/src/modes/theme/defaults/dark-cosmos.json +90 -0
- package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/modes/theme/defaults/dark-dracula.json +98 -0
- package/src/modes/theme/defaults/dark-eclipse.json +91 -0
- package/src/modes/theme/defaults/dark-ember.json +95 -0
- package/src/modes/theme/defaults/dark-equinox.json +90 -0
- package/src/modes/theme/defaults/dark-forest.json +96 -0
- package/src/modes/theme/defaults/dark-github.json +105 -0
- package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
- package/src/modes/theme/defaults/dark-lavender.json +95 -0
- package/src/modes/theme/defaults/dark-lunar.json +89 -0
- package/src/modes/theme/defaults/dark-midnight.json +95 -0
- package/src/modes/theme/defaults/dark-monochrome.json +94 -0
- package/src/modes/theme/defaults/dark-monokai.json +98 -0
- package/src/modes/theme/defaults/dark-nebula.json +90 -0
- package/src/modes/theme/defaults/dark-nord.json +97 -0
- package/src/modes/theme/defaults/dark-ocean.json +101 -0
- package/src/modes/theme/defaults/dark-one.json +100 -0
- package/src/modes/theme/defaults/dark-poimandres.json +143 -0
- package/src/modes/theme/defaults/dark-rainforest.json +91 -0
- package/src/modes/theme/defaults/dark-reef.json +91 -0
- package/src/modes/theme/defaults/dark-retro.json +92 -0
- package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
- package/src/modes/theme/defaults/dark-sakura.json +95 -0
- package/src/modes/theme/defaults/dark-slate.json +95 -0
- package/src/modes/theme/defaults/dark-solarized.json +97 -0
- package/src/modes/theme/defaults/dark-solstice.json +90 -0
- package/src/modes/theme/defaults/dark-starfall.json +91 -0
- package/src/modes/theme/defaults/dark-sunset.json +99 -0
- package/src/modes/theme/defaults/dark-swamp.json +90 -0
- package/src/modes/theme/defaults/dark-synthwave.json +103 -0
- package/src/modes/theme/defaults/dark-taiga.json +91 -0
- package/src/modes/theme/defaults/dark-terminal.json +95 -0
- package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/modes/theme/defaults/dark-tundra.json +91 -0
- package/src/modes/theme/defaults/dark-twilight.json +91 -0
- package/src/modes/theme/defaults/dark-volcanic.json +91 -0
- package/src/modes/theme/defaults/graphite.json +92 -0
- package/src/modes/theme/defaults/index.ts +199 -0
- package/src/modes/theme/defaults/light-arctic.json +107 -0
- package/src/modes/theme/defaults/light-aurora-day.json +91 -0
- package/src/modes/theme/defaults/light-canyon.json +91 -0
- package/src/modes/theme/defaults/light-catppuccin.json +106 -0
- package/src/modes/theme/defaults/light-cirrus.json +90 -0
- package/src/modes/theme/defaults/light-coral.json +95 -0
- package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
- package/src/modes/theme/defaults/light-dawn.json +90 -0
- package/src/modes/theme/defaults/light-dunes.json +91 -0
- package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
- package/src/modes/theme/defaults/light-forest.json +100 -0
- package/src/modes/theme/defaults/light-frost.json +95 -0
- package/src/modes/theme/defaults/light-github.json +115 -0
- package/src/modes/theme/defaults/light-glacier.json +91 -0
- package/src/modes/theme/defaults/light-gruvbox.json +108 -0
- package/src/modes/theme/defaults/light-haze.json +90 -0
- package/src/modes/theme/defaults/light-honeycomb.json +95 -0
- package/src/modes/theme/defaults/light-lagoon.json +91 -0
- package/src/modes/theme/defaults/light-lavender.json +95 -0
- package/src/modes/theme/defaults/light-meadow.json +91 -0
- package/src/modes/theme/defaults/light-mint.json +95 -0
- package/src/modes/theme/defaults/light-monochrome.json +101 -0
- package/src/modes/theme/defaults/light-ocean.json +99 -0
- package/src/modes/theme/defaults/light-one.json +99 -0
- package/src/modes/theme/defaults/light-opal.json +91 -0
- package/src/modes/theme/defaults/light-orchard.json +91 -0
- package/src/modes/theme/defaults/light-paper.json +95 -0
- package/src/modes/theme/defaults/light-poimandres.json +143 -0
- package/src/modes/theme/defaults/light-prism.json +90 -0
- package/src/modes/theme/defaults/light-retro.json +98 -0
- package/src/modes/theme/defaults/light-sand.json +95 -0
- package/src/modes/theme/defaults/light-savanna.json +91 -0
- package/src/modes/theme/defaults/light-solarized.json +102 -0
- package/src/modes/theme/defaults/light-soleil.json +90 -0
- package/src/modes/theme/defaults/light-sunset.json +99 -0
- package/src/modes/theme/defaults/light-synthwave.json +98 -0
- package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
- package/src/modes/theme/defaults/light-wetland.json +91 -0
- package/src/modes/theme/defaults/light-zenith.json +89 -0
- package/src/modes/theme/defaults/limestone.json +94 -0
- package/src/modes/theme/defaults/mahogany.json +97 -0
- package/src/modes/theme/defaults/marble.json +93 -0
- package/src/modes/theme/defaults/obsidian.json +91 -0
- package/src/modes/theme/defaults/onyx.json +91 -0
- package/src/modes/theme/defaults/pearl.json +93 -0
- package/src/modes/theme/defaults/porcelain.json +91 -0
- package/src/modes/theme/defaults/quartz.json +96 -0
- package/src/modes/theme/defaults/sandstone.json +95 -0
- package/src/modes/theme/defaults/titanium.json +90 -0
- package/src/modes/theme/light.json +93 -0
- package/src/modes/theme/mermaid-cache.ts +92 -0
- package/src/modes/theme/shimmer.ts +305 -0
- package/src/modes/theme/theme-schema.json +463 -0
- package/src/modes/theme/theme.ts +3171 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +494 -0
- package/src/modes/ultrathink.ts +42 -0
- package/src/modes/utils/context-usage.ts +518 -0
- package/src/modes/utils/copy-targets.ts +378 -0
- package/src/modes/utils/hotkeys-markdown.ts +65 -0
- package/src/modes/utils/interactive-context-helpers.ts +31 -0
- package/src/modes/utils/keybinding-matchers.ts +86 -0
- package/src/modes/utils/tools-markdown.ts +31 -0
- package/src/modes/utils/transcript-render-helpers.ts +253 -0
- package/src/modes/utils/ui-helpers.ts +976 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/modes/workflow.ts +55 -0
- package/src/plan-mode/approved-plan.ts +194 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/plan-mode/plan-protection.ts +31 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +60 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/advise-tool.md +3 -0
- package/src/prompts/advisor/context-files.md +8 -0
- package/src/prompts/advisor/system.md +98 -0
- package/src/prompts/agents/designer.md +74 -0
- package/src/prompts/agents/frontmatter.md +12 -0
- package/src/prompts/agents/init.md +33 -0
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/reviewer.md +139 -0
- package/src/prompts/agents/scout.md +58 -0
- package/src/prompts/agents/security-reviewer.md +75 -0
- package/src/prompts/agents/task.md +17 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/bench.md +6 -0
- package/src/prompts/ci-green-request.md +36 -0
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/goals/goal-mode-context.md +4 -0
- package/src/prompts/goals/goal-todo-context.md +12 -0
- package/src/prompts/goals/guided-goal-interview.md +43 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/consolidation_system.md +4 -0
- package/src/prompts/memories/read-path.md +17 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-custom-request.md +21 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +68 -0
- package/src/prompts/security/scan-coordinator.md +7 -0
- package/src/prompts/security/scan-request.md +21 -0
- package/src/prompts/security/validate-request.md +8 -0
- package/src/prompts/session/launch-completion.md +1 -0
- package/src/prompts/skills/autoload.md +8 -0
- package/src/prompts/skills/user-invocation.md +11 -0
- package/src/prompts/steering/parent-irc.md +5 -0
- package/src/prompts/steering/user-interjection.md +6 -0
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/agent-creation-architect.md +50 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-continue.md +1 -0
- package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
- package/src/prompts/system/auto-thinking-difficulty.md +14 -0
- package/src/prompts/system/autolearn-guidance-learn.md +1 -0
- package/src/prompts/system/autolearn-guidance.md +7 -0
- package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +14 -0
- package/src/prompts/system/computer-safety.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +64 -0
- package/src/prompts/system/eager-task.md +7 -0
- package/src/prompts/system/eager-todo.md +18 -0
- package/src/prompts/system/empty-stop-retry.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/irc-autoreply.md +6 -0
- package/src/prompts/system/irc-incoming.md +9 -0
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/mcp-xdev-guidance.md +11 -0
- package/src/prompts/system/memory-consolidation-system.md +8 -0
- package/src/prompts/system/memory-extraction-system.md +26 -0
- package/src/prompts/system/mid-run-todo-nudge.md +3 -0
- package/src/prompts/system/omfg-user.md +50 -0
- package/src/prompts/system/orchestrate-notice.md +40 -0
- package/src/prompts/system/personalities/default.md +18 -0
- package/src/prompts/system/personalities/friendly.md +17 -0
- package/src/prompts/system/personalities/pragmatic.md +15 -0
- package/src/prompts/system/plan-mode-active.md +125 -0
- package/src/prompts/system/plan-mode-approved.md +22 -0
- package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
- package/src/prompts/system/plan-mode-reference.md +10 -0
- package/src/prompts/system/plan-mode-subagent.md +33 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/plan-yolo-handoff.md +5 -0
- package/src/prompts/system/prewalk-checklist.md +7 -0
- package/src/prompts/system/prewalk-continue.md +1 -0
- package/src/prompts/system/prewalk-plan.md +13 -0
- package/src/prompts/system/project-prompt.md +61 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/resolve-device-reminder.md +3 -0
- package/src/prompts/system/rewind-report.md +6 -0
- package/src/prompts/system/side-channel-no-tools.md +3 -0
- package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-context-stub.md +1 -0
- package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-system-stub.md +1 -0
- package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
- package/src/prompts/system/speech-rewrite.md +15 -0
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/subagent-system-prompt.md +73 -0
- package/src/prompts/system/subagent-user-prompt.md +3 -0
- package/src/prompts/system/subagent-yield-reminder.md +23 -0
- package/src/prompts/system/system-prompt.md +263 -0
- package/src/prompts/system/tan-context-switch.md +17 -0
- package/src/prompts/system/task-label.md +23 -0
- package/src/prompts/system/thinking-loop-redirect.md +10 -0
- package/src/prompts/system/title-marker-instruction.md +1 -0
- package/src/prompts/system/title-system.md +16 -0
- package/src/prompts/system/tool-call-loop-redirect.md +8 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/system/ultrathink-notice.md +3 -0
- package/src/prompts/system/unexpected-stop-classifier.md +17 -0
- package/src/prompts/system/unexpected-stop-retry.md +4 -0
- package/src/prompts/system/vibe-mode-active.md +26 -0
- package/src/prompts/system/web-search.md +25 -0
- package/src/prompts/system/workflow-notice.md +112 -0
- package/src/prompts/system/xdev-mount-notice.md +20 -0
- package/src/prompts/tools/apply-patch.md +65 -0
- package/src/prompts/tools/ask.md +22 -0
- package/src/prompts/tools/ast-edit.md +11 -0
- package/src/prompts/tools/ast-grep.md +19 -0
- package/src/prompts/tools/async-result.md +8 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/browser.md +29 -0
- package/src/prompts/tools/checkpoint.md +15 -0
- package/src/prompts/tools/computer.md +26 -0
- package/src/prompts/tools/debug.md +3 -0
- package/src/prompts/tools/eval.md +45 -0
- package/src/prompts/tools/github.md +22 -0
- package/src/prompts/tools/glob.md +16 -0
- package/src/prompts/tools/goal.md +11 -0
- package/src/prompts/tools/grep.md +13 -0
- package/src/prompts/tools/hub.md +34 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/prompts/tools/image-gen.md +7 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +22 -0
- package/src/prompts/tools/learn.md +7 -0
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/lsp.md +19 -0
- package/src/prompts/tools/manage-skill.md +9 -0
- package/src/prompts/tools/memory-edit.md +12 -0
- package/src/prompts/tools/patch.md +57 -0
- package/src/prompts/tools/read.md +27 -0
- package/src/prompts/tools/recall.md +7 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/replace.md +30 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +14 -0
- package/src/prompts/tools/security-publish.md +1 -0
- package/src/prompts/tools/security-scan.md +1 -0
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task-summary.md +20 -0
- package/src/prompts/tools/task.md +83 -0
- package/src/prompts/tools/todo.md +42 -0
- package/src/prompts/tools/vibe-kill.md +3 -0
- package/src/prompts/tools/vibe-list.md +3 -0
- package/src/prompts/tools/vibe-send.md +9 -0
- package/src/prompts/tools/vibe-spawn.md +10 -0
- package/src/prompts/tools/vibe-turn-result.md +19 -0
- package/src/prompts/tools/vibe-wait.md +8 -0
- package/src/prompts/tools/web-search.md +8 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/registry/agent-lifecycle.ts +505 -0
- package/src/registry/agent-registry.ts +287 -0
- package/src/registry/persisted-agents.ts +423 -0
- package/src/sdk.ts +3820 -0
- package/src/secrets/index.ts +375 -0
- package/src/secrets/obfuscator.ts +2629 -0
- package/src/secrets/regex.ts +21 -0
- package/src/security/auth.ts +98 -0
- package/src/security/cloud.ts +686 -0
- package/src/security/comparison.ts +255 -0
- package/src/security/contracts/ids.ts +111 -0
- package/src/security/contracts/index.ts +4 -0
- package/src/security/contracts/schemas.ts +219 -0
- package/src/security/contracts/types.ts +254 -0
- package/src/security/contracts/validation.ts +69 -0
- package/src/security/coordinator.ts +708 -0
- package/src/security/importers/codex-security.ts +387 -0
- package/src/security/importers/index.ts +2 -0
- package/src/security/importers/sarif.ts +357 -0
- package/src/security/index.ts +13 -0
- package/src/security/preflight.ts +405 -0
- package/src/security/provenance.ts +106 -0
- package/src/security/publication.ts +326 -0
- package/src/security/remediation.ts +93 -0
- package/src/security/resource-output.ts +50 -0
- package/src/security/sarif.ts +78 -0
- package/src/security/store.ts +430 -0
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-events.ts +67 -0
- package/src/session/agent-session-types.ts +407 -0
- package/src/session/agent-session.ts +9000 -0
- package/src/session/agent-storage.ts +807 -0
- package/src/session/artifacts.ts +154 -0
- package/src/session/async-job-delivery.ts +82 -0
- package/src/session/auth-broker-config.ts +92 -0
- package/src/session/auth-storage.ts +25 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.ts +295 -0
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/client-bridge.ts +85 -0
- package/src/session/codex-auto-reset.ts +673 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/compact-modes.ts +105 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/eval-runner.ts +219 -0
- package/src/session/exit-diagnostics.ts +310 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/history-storage.ts +329 -0
- package/src/session/indexed-session-storage.ts +553 -0
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/launch-completion.ts +37 -0
- package/src/session/messages.ts +1304 -0
- package/src/session/model-controls.ts +757 -0
- package/src/session/prewalk.ts +279 -0
- package/src/session/provider-image-budget.ts +86 -0
- package/src/session/queued-messages.ts +99 -0
- package/src/session/redis-session-storage.ts +257 -0
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1896 -0
- package/src/session/session-context.ts +584 -0
- package/src/session/session-dump-format.ts +216 -0
- package/src/session/session-entries.ts +306 -0
- package/src/session/session-handoff.ts +325 -0
- package/src/session/session-history-format.ts +459 -0
- package/src/session/session-listing.ts +715 -0
- package/src/session/session-loader.ts +362 -0
- package/src/session/session-maintenance.ts +3115 -0
- package/src/session/session-manager.ts +2747 -0
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-metadata.ts +53 -0
- package/src/session/session-migrations.ts +78 -0
- package/src/session/session-paths.ts +280 -0
- package/src/session/session-persistence.ts +293 -0
- package/src/session/session-provider-boundary.ts +306 -0
- package/src/session/session-stats.ts +349 -0
- package/src/session/session-storage.ts +774 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/session-tools.ts +1385 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/settings-stream-fn.ts +79 -0
- package/src/session/shake-types.ts +43 -0
- package/src/session/snapcompact-inline.ts +545 -0
- package/src/session/snapcompact-savings-journal.ts +113 -0
- package/src/session/sql-session-storage.ts +374 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/streaming-output.ts +1459 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/tool-choice-queue.ts +305 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-persistence.ts +142 -0
- package/src/session/turn-recovery.ts +2005 -0
- package/src/session/unexpected-stop-classifier.ts +141 -0
- package/src/session/yield-queue.ts +288 -0
- package/src/slash-commands/acp-builtins.ts +70 -0
- package/src/slash-commands/available-commands.ts +105 -0
- package/src/slash-commands/builtin-registry.ts +3149 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
- package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
- package/src/slash-commands/helpers/context-report.ts +66 -0
- package/src/slash-commands/helpers/format.ts +46 -0
- package/src/slash-commands/helpers/logout.ts +108 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +533 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/reset-usage.ts +68 -0
- package/src/slash-commands/helpers/security.ts +451 -0
- package/src/slash-commands/helpers/session-pin.ts +44 -0
- package/src/slash-commands/helpers/ssh.ts +196 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
- package/src/slash-commands/helpers/todo.ts +285 -0
- package/src/slash-commands/helpers/usage-report.ts +198 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/types.ts +139 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +667 -0
- package/src/ssh/file-transfer.ts +214 -0
- package/src/ssh/sshfs-mount.ts +163 -0
- package/src/ssh/utils.ts +51 -0
- package/src/startup-splash.ts +19 -0
- package/src/stt/asr-client.ts +401 -0
- package/src/stt/asr-protocol.ts +65 -0
- package/src/stt/asr-worker.ts +603 -0
- package/src/stt/downloader.ts +142 -0
- package/src/stt/endpointer.ts +259 -0
- package/src/stt/index.ts +6 -0
- package/src/stt/models.ts +150 -0
- package/src/stt/sherpa-runtime.ts +71 -0
- package/src/stt/stt-controller.ts +320 -0
- package/src/stt/submit-trigger.ts +74 -0
- package/src/subprocess/worker-client.ts +463 -0
- package/src/subprocess/worker-runtime.ts +494 -0
- package/src/system-prompt.ts +921 -0
- package/src/task/agents.ts +170 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +145 -0
- package/src/task/executor.ts +3431 -0
- package/src/task/index.ts +1515 -0
- package/src/task/isolation-ownership.ts +106 -0
- package/src/task/isolation-runner.ts +444 -0
- package/src/task/label.ts +40 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/omp-command.ts +26 -0
- package/src/task/output-manager.ts +115 -0
- package/src/task/parallel.ts +221 -0
- package/src/task/persisted-revive.ts +173 -0
- package/src/task/prewalk.ts +6 -0
- package/src/task/prompt-policy.ts +8 -0
- package/src/task/provider-concurrency.ts +100 -0
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/render.ts +1830 -0
- package/src/task/renderer.ts +14 -0
- package/src/task/repair-args.ts +118 -0
- package/src/task/spawn-policy.ts +72 -0
- package/src/task/structured-subagent.ts +676 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +555 -0
- package/src/task/worktree.ts +967 -0
- package/src/task/yield-assembly.ts +198 -0
- package/src/telemetry-export.ts +504 -0
- package/src/thinking.ts +377 -0
- package/src/tiny/device.ts +111 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/message-preproc.ts +155 -0
- package/src/tiny/models.ts +268 -0
- package/src/tiny/text.ts +290 -0
- package/src/tiny/title-client.ts +460 -0
- package/src/tiny/title-protocol.ts +56 -0
- package/src/tiny/worker.ts +354 -0
- package/src/tools/acp-bridge.ts +125 -0
- package/src/tools/approval.ts +245 -0
- package/src/tools/ask.ts +1459 -0
- package/src/tools/ast-edit.ts +718 -0
- package/src/tools/ast-grep.ts +526 -0
- package/src/tools/auto-generated-guard.ts +335 -0
- package/src/tools/bash-interactive.ts +435 -0
- package/src/tools/bash-interceptor.ts +148 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +350 -0
- package/src/tools/bash.ts +1826 -0
- package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
- package/src/tools/browser/aria/aria-snapshot.ts +131 -0
- package/src/tools/browser/attach.ts +219 -0
- package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
- package/src/tools/browser/cmux/rpc.ts +200 -0
- package/src/tools/browser/cmux/socket-client.ts +445 -0
- package/src/tools/browser/launch.ts +939 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +447 -0
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +229 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/shared-daemon.ts +139 -0
- package/src/tools/browser/tab-protocol.ts +123 -0
- package/src/tools/browser/tab-supervisor.ts +1077 -0
- package/src/tools/browser/tab-worker-entry.ts +29 -0
- package/src/tools/browser/tab-worker.ts +1982 -0
- package/src/tools/browser.ts +487 -0
- package/src/tools/builtin-names.ts +67 -0
- package/src/tools/checkpoint.ts +137 -0
- package/src/tools/computer/exposure.ts +14 -0
- package/src/tools/computer/protocol.ts +69 -0
- package/src/tools/computer/supervisor.ts +409 -0
- package/src/tools/computer/worker-entry.ts +39 -0
- package/src/tools/computer/worker.ts +745 -0
- package/src/tools/computer-renderer.ts +147 -0
- package/src/tools/computer.ts +222 -0
- package/src/tools/conflict-detect.ts +815 -0
- package/src/tools/context.ts +49 -0
- package/src/tools/debug.ts +1121 -0
- package/src/tools/default-renderer.ts +154 -0
- package/src/tools/essential-tools.ts +46 -0
- package/src/tools/eval-backends.ts +34 -0
- package/src/tools/eval-format/index.ts +24 -0
- package/src/tools/eval-format/javascript.ts +952 -0
- package/src/tools/eval-format/julia.ts +446 -0
- package/src/tools/eval-format/python.ts +544 -0
- package/src/tools/eval-format/ruby.ts +380 -0
- package/src/tools/eval-render.ts +784 -0
- package/src/tools/eval.ts +774 -0
- package/src/tools/fetch.ts +1889 -0
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gh-cache-invalidation.ts +175 -0
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +484 -0
- package/src/tools/gh.ts +3958 -0
- package/src/tools/github-cache.ts +663 -0
- package/src/tools/glob.ts +691 -0
- package/src/tools/grep.ts +1920 -0
- package/src/tools/grouped-file-output.ts +210 -0
- package/src/tools/hub/index.ts +579 -0
- package/src/tools/hub/jobs.ts +714 -0
- package/src/tools/hub/launch.ts +690 -0
- package/src/tools/hub/messaging.ts +735 -0
- package/src/tools/hub/types.ts +117 -0
- package/src/tools/image-gen.ts +1689 -0
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +736 -0
- package/src/tools/inspect-image-renderer.ts +133 -0
- package/src/tools/inspect-image.ts +316 -0
- package/src/tools/json-tree.ts +260 -0
- package/src/tools/jtd-to-json-schema.ts +219 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/learn.ts +141 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/manage-skill.ts +102 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/memory-edit.ts +61 -0
- package/src/tools/memory-recall.ts +102 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +211 -0
- package/src/tools/memory-retain.ts +89 -0
- package/src/tools/output-meta.ts +860 -0
- package/src/tools/output-schema-validator.ts +307 -0
- package/src/tools/path-utils.ts +1489 -0
- package/src/tools/plan-mode-guard.ts +155 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
- package/src/tools/read.ts +3762 -0
- package/src/tools/render-utils.ts +926 -0
- package/src/tools/renderers.ts +133 -0
- package/src/tools/report-tool-issue.ts +568 -0
- package/src/tools/resolve.ts +423 -0
- package/src/tools/review.ts +103 -0
- package/src/tools/run-scope.ts +417 -0
- package/src/tools/security-scan.ts +287 -0
- package/src/tools/shell-tokenize.ts +213 -0
- package/src/tools/sqlite-reader.ts +884 -0
- package/src/tools/terminal-output.ts +141 -0
- package/src/tools/todo.ts +1226 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +102 -0
- package/src/tools/tool-timeouts.ts +38 -0
- package/src/tools/tts.ts +266 -0
- package/src/tools/vibe.ts +608 -0
- package/src/tools/write.ts +1646 -0
- package/src/tools/xdev.ts +560 -0
- package/src/tools/yield.ts +486 -0
- package/src/tts/downloader.ts +64 -0
- package/src/tts/index.ts +10 -0
- package/src/tts/models.ts +137 -0
- package/src/tts/runtime.ts +21 -0
- package/src/tts/speakable.ts +392 -0
- package/src/tts/speech-enhancer.ts +206 -0
- package/src/tts/streaming-player.ts +120 -0
- package/src/tts/tts-client.ts +475 -0
- package/src/tts/tts-protocol.ts +69 -0
- package/src/tts/tts-worker.ts +434 -0
- package/src/tts/vocalizer.ts +419 -0
- package/src/tts/wav.ts +58 -0
- package/src/tui/code-cell.ts +268 -0
- package/src/tui/file-list.ts +55 -0
- package/src/tui/hyperlink.ts +178 -0
- package/src/tui/index.ts +13 -0
- package/src/tui/output-block.ts +268 -0
- package/src/tui/status-line.ts +54 -0
- package/src/tui/tree-list.ts +172 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/block-context.ts +312 -0
- package/src/utils/changelog.ts +373 -0
- package/src/utils/clipboard.ts +361 -0
- package/src/utils/command-args.ts +74 -0
- package/src/utils/commit-message-generator.ts +148 -0
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/edit-mode.ts +61 -0
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +80 -0
- package/src/utils/fetch-timeout.ts +10 -0
- package/src/utils/file-display-mode.ts +44 -0
- package/src/utils/file-mentions.ts +293 -0
- package/src/utils/git.ts +2472 -0
- package/src/utils/image-loading.ts +236 -0
- package/src/utils/image-resize.ts +420 -0
- package/src/utils/image-vision-fallback.ts +196 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/ipc.ts +38 -0
- package/src/utils/jj.ts +422 -0
- package/src/utils/lang-from-path.ts +251 -0
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/local-date.ts +7 -0
- package/src/utils/mac-file-urls.applescript +37 -0
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +223 -0
- package/src/utils/mupdf-wasm-embed.ts +12 -0
- package/src/utils/open.ts +126 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/qrcode.ts +535 -0
- package/src/utils/sample-profile.ts +437 -0
- package/src/utils/session-color.ts +142 -0
- package/src/utils/shell-snapshot-fn-env.sh +63 -0
- package/src/utils/shell-snapshot.ts +326 -0
- package/src/utils/sixel.ts +69 -0
- package/src/utils/thinking-display.ts +163 -0
- package/src/utils/title-generator.ts +597 -0
- package/src/utils/token-rate.ts +72 -0
- package/src/utils/tool-choice.ts +50 -0
- package/src/utils/tools-manager.ts +411 -0
- package/src/utils/turndown.ts +82 -0
- package/src/utils/zip.ts +1106 -0
- package/src/vibe/runtime.ts +1684 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/kagi.ts +305 -0
- package/src/web/parallel.ts +354 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +97 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +663 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +800 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +129 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +173 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +121 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +189 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +94 -0
- package/src/web/scrapers/types.ts +354 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +325 -0
- package/src/web/search/index.ts +393 -0
- package/src/web/search/provider.ts +272 -0
- package/src/web/search/providers/anthropic.ts +406 -0
- package/src/web/search/providers/base.ts +112 -0
- package/src/web/search/providers/brave.ts +181 -0
- package/src/web/search/providers/browser-headers.ts +82 -0
- package/src/web/search/providers/browser-page.ts +125 -0
- package/src/web/search/providers/codex.ts +788 -0
- package/src/web/search/providers/duckduckgo.ts +382 -0
- package/src/web/search/providers/ecosia.ts +183 -0
- package/src/web/search/providers/exa.ts +513 -0
- package/src/web/search/providers/firecrawl.ts +213 -0
- package/src/web/search/providers/gemini.ts +634 -0
- package/src/web/search/providers/google.ts +195 -0
- package/src/web/search/providers/jina.ts +144 -0
- package/src/web/search/providers/kagi.ts +98 -0
- package/src/web/search/providers/kimi.ts +221 -0
- package/src/web/search/providers/mojeek.ts +220 -0
- package/src/web/search/providers/parallel.ts +186 -0
- package/src/web/search/providers/perplexity-auth.ts +142 -0
- package/src/web/search/providers/perplexity.ts +997 -0
- package/src/web/search/providers/public.ts +199 -0
- package/src/web/search/providers/searxng.ts +467 -0
- package/src/web/search/providers/startpage.ts +225 -0
- package/src/web/search/providers/synthetic.ts +126 -0
- package/src/web/search/providers/tavily.ts +244 -0
- package/src/web/search/providers/tinyfish.ts +166 -0
- package/src/web/search/providers/utils.ts +130 -0
- package/src/web/search/providers/xai.ts +372 -0
- package/src/web/search/providers/zai.ts +452 -0
- package/src/web/search/query.ts +850 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +518 -0
- package/src/web/search/utils.ts +17 -0
- package/src/workspace-tree.ts +326 -0
|
@@ -0,0 +1,2115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model resolution, scoping, and initial selection.
|
|
3
|
+
*
|
|
4
|
+
* Layering:
|
|
5
|
+
* - `matchModel` is the single matching engine. Order: exact `provider/id`
|
|
6
|
+
* reference (with variant-alias and OpenRouter routed/date fallbacks) →
|
|
7
|
+
* exact bare id → retired variant alias → provider-scoped fuzzy → substring
|
|
8
|
+
* with alias-vs-dated pick.
|
|
9
|
+
* - `parseModelPatternWithContext`/`parseModelPattern` layer the selector
|
|
10
|
+
* grammar on top: trailing `:level` thinking suffixes (`splitThinkingSuffix`)
|
|
11
|
+
* and `@upstream` provider routing (`splitUpstreamRouting`).
|
|
12
|
+
* - Everything else (`resolveModelFromString`, `resolveModelOverride*`,
|
|
13
|
+
* `resolveRoleSelection`, `resolveModelScope`, `resolveCliModel`,
|
|
14
|
+
* `findSmolModel`/`findSlowModel`) adapts inputs — roles, settings patterns,
|
|
15
|
+
* CLI flags, scope globs — onto that pipeline.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
19
|
+
import type { Api, Effort, KnownProvider, Model, ModelSpec } from "@oh-my-pi/pi-ai";
|
|
20
|
+
import { buildModel } from "@oh-my-pi/pi-catalog/build";
|
|
21
|
+
import { modelMatchesHost } from "@oh-my-pi/pi-catalog/hosts";
|
|
22
|
+
import { buildModelProviderPriorityRank } from "@oh-my-pi/pi-catalog/identity";
|
|
23
|
+
import { stripThinkingVariantToken } from "@oh-my-pi/pi-catalog/identity/family";
|
|
24
|
+
import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
25
|
+
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
26
|
+
import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models";
|
|
27
|
+
import { resolveBareVariantAlias, resolveVariantAlias } from "@oh-my-pi/pi-catalog/variant-collapse";
|
|
28
|
+
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
29
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
30
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
31
|
+
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
32
|
+
import {
|
|
33
|
+
AUTO_THINKING,
|
|
34
|
+
type ConfiguredThinkingLevel,
|
|
35
|
+
concreteThinkingLevel,
|
|
36
|
+
parseThinkingLevel,
|
|
37
|
+
resolveThinkingLevelForModel,
|
|
38
|
+
} from "../thinking";
|
|
39
|
+
import { isAuthenticated, kNoAuth, type ModelRegistry } from "./model-registry";
|
|
40
|
+
import {
|
|
41
|
+
DEFAULT_MODEL_ROLE_ALIAS,
|
|
42
|
+
formatModelRoleAlias,
|
|
43
|
+
LEGACY_MODEL_ROLE_ALIAS_PREFIX,
|
|
44
|
+
MODEL_ROLE_ALIAS_PREFIX,
|
|
45
|
+
MODEL_ROLE_IDS,
|
|
46
|
+
type ModelRole,
|
|
47
|
+
} from "./model-roles";
|
|
48
|
+
import type { Settings } from "./settings";
|
|
49
|
+
|
|
50
|
+
function isKnownProvider(provider: string): provider is KnownProvider {
|
|
51
|
+
return provider in DEFAULT_MODEL_PER_PROVIDER;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pick the first provider-default model in availability order.
|
|
56
|
+
*
|
|
57
|
+
* If multiple providers expose that same default id, rank only that shared-id
|
|
58
|
+
* group by canonical provider priority so native/OAuth transports beat mirrors
|
|
59
|
+
* without changing unrelated provider fallback precedence.
|
|
60
|
+
*/
|
|
61
|
+
export function pickDefaultAvailableModel(availableModels: Model<Api>[]): Model<Api> | undefined {
|
|
62
|
+
const firstDefault = availableModels.find(
|
|
63
|
+
model => isKnownProvider(model.provider) && DEFAULT_MODEL_PER_PROVIDER[model.provider] === model.id,
|
|
64
|
+
);
|
|
65
|
+
if (!firstDefault) return availableModels[0];
|
|
66
|
+
|
|
67
|
+
const providerPriority = buildModelProviderPriorityRank();
|
|
68
|
+
const sharedDefaultMatches = availableModels.filter(
|
|
69
|
+
model =>
|
|
70
|
+
model.id === firstDefault.id &&
|
|
71
|
+
isKnownProvider(model.provider) &&
|
|
72
|
+
DEFAULT_MODEL_PER_PROVIDER[model.provider] === model.id,
|
|
73
|
+
);
|
|
74
|
+
return [...sharedDefaultMatches].sort((a, b) => {
|
|
75
|
+
const aRank = providerPriority.get(a.provider.toLowerCase()) ?? Number.POSITIVE_INFINITY;
|
|
76
|
+
const bRank = providerPriority.get(b.provider.toLowerCase()) ?? Number.POSITIVE_INFINITY;
|
|
77
|
+
if (aRank !== bRank) return aRank - bRank;
|
|
78
|
+
return availableModels.indexOf(a) - availableModels.indexOf(b);
|
|
79
|
+
})[0];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ScopedModel {
|
|
83
|
+
model: Model<Api>;
|
|
84
|
+
thinkingLevel?: ThinkingLevel;
|
|
85
|
+
explicitThinkingLevel: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface ThinkingSuffixOptions {
|
|
89
|
+
allowMaxSuffix?: boolean;
|
|
90
|
+
allowAutoAlias?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface ModelStringParseOptions extends ThinkingSuffixOptions {
|
|
94
|
+
isLiteralModelId?: (provider: string, id: string) => boolean;
|
|
95
|
+
}
|
|
96
|
+
// Suffix recognition for the model-pattern parser: `:max` is a real thinking
|
|
97
|
+
// level and `:auto` maps to the auto sentinel. Both are gated behind flags
|
|
98
|
+
// (and the literal-id / exact-match guards on the callers) because real model
|
|
99
|
+
// ids end in `:max` (e.g. `glm-4.7:max`) — an ungated split would silently
|
|
100
|
+
// reinterpret them as a thinking suffix.
|
|
101
|
+
const MAX_THINKING_SUFFIX_OPTIONS: ThinkingSuffixOptions = { allowMaxSuffix: true, allowAutoAlias: true };
|
|
102
|
+
|
|
103
|
+
function parseThinkingSuffix(value: string, options?: ThinkingSuffixOptions): ConfiguredThinkingLevel | undefined {
|
|
104
|
+
const level = parseThinkingLevel(value);
|
|
105
|
+
if (level === ThinkingLevel.Max) return options?.allowMaxSuffix === true ? level : undefined;
|
|
106
|
+
if (level !== undefined) return level;
|
|
107
|
+
if (options?.allowAutoAlias === true && value === AUTO_THINKING) return AUTO_THINKING;
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Split a trailing `:<level>` thinking selector off a model pattern.
|
|
113
|
+
*
|
|
114
|
+
* `level` is set when the suffix parses as a concrete thinking level (or, when
|
|
115
|
+
* the caller opts in via `allowMaxSuffix`/`allowAutoAlias`, the guarded `:max`
|
|
116
|
+
* level / `:auto` sentinel); `base` then has the suffix stripped. Otherwise
|
|
117
|
+
* `base` is the input.
|
|
118
|
+
* `minColonIndex` requires the colon to appear strictly after that index —
|
|
119
|
+
* role-alias callers pass the matched alias prefix length.
|
|
120
|
+
*/
|
|
121
|
+
function splitThinkingSuffix(
|
|
122
|
+
pattern: string,
|
|
123
|
+
minColonIndex = -1,
|
|
124
|
+
options?: ThinkingSuffixOptions,
|
|
125
|
+
): { base: string; level?: ConfiguredThinkingLevel } {
|
|
126
|
+
const colonIdx = pattern.lastIndexOf(":");
|
|
127
|
+
if (colonIdx <= minColonIndex) return { base: pattern };
|
|
128
|
+
const level = parseThinkingSuffix(pattern.slice(colonIdx + 1), options);
|
|
129
|
+
return level ? { base: pattern.slice(0, colonIdx), level } : { base: pattern };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function matchingGlobModels(pattern: string, availableModels: readonly Model<Api>[]): Model<Api>[] {
|
|
133
|
+
const glob = new Bun.Glob(pattern.toLowerCase());
|
|
134
|
+
return availableModels.filter(model => {
|
|
135
|
+
const fullId = `${model.provider}/${model.id}`;
|
|
136
|
+
return glob.match(fullId.toLowerCase()) || glob.match(model.id.toLowerCase());
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function resolveGlobScopePattern(
|
|
141
|
+
pattern: string,
|
|
142
|
+
availableModels: readonly Model<Api>[],
|
|
143
|
+
): { models: Model<Api>[]; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } {
|
|
144
|
+
// Glob scopes describe which models are enabled, not per-role thinking.
|
|
145
|
+
// Coerce the `auto` sentinel to a concrete-only view so scope callers stay
|
|
146
|
+
// typed on `ThinkingLevel` and `enabledModels: [\"openai/*:auto\"]` doesn't
|
|
147
|
+
// pin a stray per-model level.
|
|
148
|
+
const strictSuffix = splitThinkingSuffix(pattern);
|
|
149
|
+
if (strictSuffix.level !== undefined) {
|
|
150
|
+
const thinkingLevel = concreteThinkingLevel(strictSuffix.level);
|
|
151
|
+
return {
|
|
152
|
+
models: matchingGlobModels(strictSuffix.base, availableModels),
|
|
153
|
+
thinkingLevel,
|
|
154
|
+
explicitThinkingLevel: thinkingLevel !== undefined,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const maxSuffix = splitThinkingSuffix(pattern, -1, MAX_THINKING_SUFFIX_OPTIONS);
|
|
159
|
+
if (maxSuffix.level !== undefined) {
|
|
160
|
+
const literalMatches = matchingGlobModels(pattern, availableModels);
|
|
161
|
+
if (literalMatches.length > 0) {
|
|
162
|
+
return { models: literalMatches, thinkingLevel: undefined, explicitThinkingLevel: false };
|
|
163
|
+
}
|
|
164
|
+
const thinkingLevel = concreteThinkingLevel(maxSuffix.level);
|
|
165
|
+
return {
|
|
166
|
+
models: matchingGlobModels(maxSuffix.base, availableModels),
|
|
167
|
+
thinkingLevel,
|
|
168
|
+
explicitThinkingLevel: thinkingLevel !== undefined,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
models: matchingGlobModels(pattern, availableModels),
|
|
174
|
+
thinkingLevel: undefined,
|
|
175
|
+
explicitThinkingLevel: false,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Parse a model string in "provider/modelId" format.
|
|
181
|
+
* Returns undefined if the format is invalid.
|
|
182
|
+
*/
|
|
183
|
+
export function parseModelString(
|
|
184
|
+
modelStr: string,
|
|
185
|
+
options?: ModelStringParseOptions,
|
|
186
|
+
): { provider: string; id: string; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
187
|
+
const slashIdx = modelStr.indexOf("/");
|
|
188
|
+
if (slashIdx <= 0) return undefined;
|
|
189
|
+
const id = modelStr.slice(slashIdx + 1);
|
|
190
|
+
const provider = modelStr.slice(0, slashIdx);
|
|
191
|
+
// Strip strict thinking level suffixes first (e.g. "claude-sonnet-4-6:high" -> id "claude-sonnet-4-6", thinkingLevel "high").
|
|
192
|
+
const strict = splitThinkingSuffix(id);
|
|
193
|
+
if (strict.level) return { provider, id: strict.base, thinkingLevel: strict.level };
|
|
194
|
+
// `max` is a real thinking level, but real model IDs can also end in
|
|
195
|
+
// `:max`. Context-aware callers pass a literal lookup so those models win.
|
|
196
|
+
const maxAlias = splitThinkingSuffix(id, -1, options);
|
|
197
|
+
if (maxAlias.level) {
|
|
198
|
+
return options?.isLiteralModelId?.(provider, id) === true
|
|
199
|
+
? { provider, id }
|
|
200
|
+
: { provider, id: maxAlias.base, thinkingLevel: maxAlias.level };
|
|
201
|
+
}
|
|
202
|
+
return { provider, id };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Format a model as "provider/modelId" string.
|
|
207
|
+
*/
|
|
208
|
+
export function formatModelString(model: Model<Api>): string {
|
|
209
|
+
return `${model.provider}/${model.id}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function getSingleRoutingOnly(routing: unknown): string | undefined {
|
|
213
|
+
if (!routing || typeof routing !== "object" || !("only" in routing) || !Array.isArray(routing.only)) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
if (routing.only.length !== 1) return undefined;
|
|
217
|
+
const upstream = routing.only[0];
|
|
218
|
+
return typeof upstream === "string" && upstream ? upstream : undefined;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function getSingleUpstreamRoute(model: Model<Api>): string | undefined {
|
|
222
|
+
const compat = model.compat;
|
|
223
|
+
if (!compat || typeof compat !== "object") return undefined;
|
|
224
|
+
if (modelMatchesHost(model, "vercelAIGateway") && "vercelGatewayRouting" in compat) {
|
|
225
|
+
return getSingleRoutingOnly(compat.vercelGatewayRouting);
|
|
226
|
+
}
|
|
227
|
+
if (modelMatchesHost(model, "openrouter") && "openRouterRouting" in compat) {
|
|
228
|
+
return getSingleRoutingOnly(compat.openRouterRouting);
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function formatModelStringWithRouting(model: Model<Api>): string {
|
|
234
|
+
const selector = formatModelString(model);
|
|
235
|
+
const upstream = getSingleUpstreamRoute(model);
|
|
236
|
+
return upstream ? `${selector}@${upstream}` : selector;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function formatModelSelectorValue(selector: string, thinkingLevel: ConfiguredThinkingLevel | undefined): string {
|
|
240
|
+
return thinkingLevel && thinkingLevel !== ThinkingLevel.Inherit ? `${selector}:${thinkingLevel}` : selector;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function getOpenRouterRouteSuffix(modelId: string): { baseId: string; suffix: string } | undefined {
|
|
244
|
+
const colonIdx = modelId.lastIndexOf(":");
|
|
245
|
+
if (colonIdx === -1) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const suffix = modelId.slice(colonIdx + 1).trim();
|
|
250
|
+
// `max` is a thinking-level suffix, never an OpenRouter route suffix, so
|
|
251
|
+
// `openrouter/<id>:max` falls through to the max-aware selector split instead of
|
|
252
|
+
// being cloned into a literal `<id>:max` model id with the reasoning level lost.
|
|
253
|
+
if (!suffix || parseThinkingSuffix(suffix, MAX_THINKING_SUFFIX_OPTIONS)) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { baseId: modelId.slice(0, colonIdx), suffix };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function stripOpenRouterDateSuffix(modelId: string): string | undefined {
|
|
261
|
+
const stripped = modelId.replace(/-\d{8}(?=$|:)/i, "");
|
|
262
|
+
return stripped !== modelId ? stripped : undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function getOpenRouterFallbackModelIds(modelId: string): string[] {
|
|
266
|
+
const orderedCandidates: string[] = [];
|
|
267
|
+
const queue = [modelId];
|
|
268
|
+
const seen = new Set<string>();
|
|
269
|
+
|
|
270
|
+
while (queue.length > 0) {
|
|
271
|
+
const candidate = queue.shift();
|
|
272
|
+
if (!candidate || seen.has(candidate)) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
seen.add(candidate);
|
|
276
|
+
orderedCandidates.push(candidate);
|
|
277
|
+
|
|
278
|
+
const routedSuffix = getOpenRouterRouteSuffix(candidate);
|
|
279
|
+
if (routedSuffix) {
|
|
280
|
+
queue.push(routedSuffix.baseId);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const strippedDate = stripOpenRouterDateSuffix(candidate);
|
|
284
|
+
if (strippedDate) {
|
|
285
|
+
queue.push(strippedDate);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return orderedCandidates;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function cloneModelWithRequestedId(model: Model<Api>, requestedId: string): Model<Api> {
|
|
293
|
+
return {
|
|
294
|
+
...model,
|
|
295
|
+
id: requestedId,
|
|
296
|
+
...(model.name === model.id ? { name: requestedId } : {}),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const AMAZON_BEDROCK_PROVIDER = "amazon-bedrock";
|
|
301
|
+
const BEDROCK_INFERENCE_PROFILE_ARN =
|
|
302
|
+
/^arn:aws(?:-[a-z]+)*:bedrock:[a-z0-9-]+:[0-9]*:(?:application-inference-profile|inference-profile)\/[a-z0-9][a-z0-9._:-]*$/i;
|
|
303
|
+
|
|
304
|
+
function hasBedrockInferenceProfileThinkingSuffix(modelId: string): boolean {
|
|
305
|
+
const { base, level } = splitThinkingSuffix(modelId);
|
|
306
|
+
return level !== undefined && BEDROCK_INFERENCE_PROFILE_ARN.test(base.trim());
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resolveBedrockInferenceProfileModelId(
|
|
310
|
+
modelId: string,
|
|
311
|
+
availableModels: readonly Model<Api>[],
|
|
312
|
+
): Model<Api> | undefined {
|
|
313
|
+
const requestedId = modelId.trim();
|
|
314
|
+
if (hasBedrockInferenceProfileThinkingSuffix(requestedId) || !BEDROCK_INFERENCE_PROFILE_ARN.test(requestedId)) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const template = availableModels.find(model => model.provider.toLowerCase() === AMAZON_BEDROCK_PROVIDER);
|
|
319
|
+
if (!template) return undefined;
|
|
320
|
+
|
|
321
|
+
return buildModel({
|
|
322
|
+
id: requestedId,
|
|
323
|
+
name: "Bedrock inference profile",
|
|
324
|
+
api: "bedrock-converse-stream",
|
|
325
|
+
provider: AMAZON_BEDROCK_PROVIDER,
|
|
326
|
+
baseUrl: template.baseUrl,
|
|
327
|
+
reasoning: false,
|
|
328
|
+
input: ["text"],
|
|
329
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
330
|
+
contextWindow: null,
|
|
331
|
+
maxTokens: null,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function resolveBedrockInferenceProfileReference(
|
|
336
|
+
provider: string,
|
|
337
|
+
modelId: string,
|
|
338
|
+
availableModels: readonly Model<Api>[],
|
|
339
|
+
): Model<Api> | undefined {
|
|
340
|
+
if (provider.toLowerCase() !== AMAZON_BEDROCK_PROVIDER) return undefined;
|
|
341
|
+
return resolveBedrockInferenceProfileModelId(modelId, availableModels);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const UPSTREAM_ROUTING_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Split a trailing `@<upstream>` provider-routing selector off a model pattern.
|
|
348
|
+
*
|
|
349
|
+
* `openrouter/z-ai/glm-4.7@cerebras` -> base `openrouter/z-ai/glm-4.7`, upstream
|
|
350
|
+
* `cerebras`. A `:thinking` suffix after the slug is kept on the base
|
|
351
|
+
* (`...@cerebras:high` -> base `...:high`). Returns undefined when there is no
|
|
352
|
+
* `@` or the suffix is not a bare provider slug, so model ids that legitimately
|
|
353
|
+
* contain `@` (`claude-opus-4-8@default`, `workers-ai/@cf/...`) are never split.
|
|
354
|
+
*/
|
|
355
|
+
export function splitUpstreamRouting(pattern: string): { base: string; upstream: string } | undefined {
|
|
356
|
+
const at = pattern.lastIndexOf("@");
|
|
357
|
+
if (at <= 0) return undefined;
|
|
358
|
+
const rest = pattern.slice(at + 1);
|
|
359
|
+
const colon = rest.indexOf(":");
|
|
360
|
+
const upstream = colon === -1 ? rest : rest.slice(0, colon);
|
|
361
|
+
if (!UPSTREAM_ROUTING_SLUG.test(upstream)) return undefined;
|
|
362
|
+
const trailing = colon === -1 ? "" : rest.slice(colon);
|
|
363
|
+
return { base: pattern.slice(0, at) + trailing, upstream };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** OpenRouter and Vercel AI Gateway are the aggregators that honor per-request upstream routing. */
|
|
367
|
+
function supportsUpstreamRouting(model: Model<Api>): boolean {
|
|
368
|
+
return modelMatchesHost(model, "openrouter") || modelMatchesHost(model, "vercelAIGateway");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Pin a resolved aggregator model to a single upstream provider via its compat routing block. */
|
|
372
|
+
function applyUpstreamRouting(model: Model<Api>, upstream: string): Model<Api> {
|
|
373
|
+
const aggregatorModel = model as Model<"openai-completions">;
|
|
374
|
+
const routing = { only: [upstream] };
|
|
375
|
+
return buildModel({
|
|
376
|
+
...model,
|
|
377
|
+
compat: modelMatchesHost(model, "vercelAIGateway")
|
|
378
|
+
? { ...aggregatorModel.compatConfig, vercelGatewayRouting: routing }
|
|
379
|
+
: { ...aggregatorModel.compatConfig, openRouterRouting: routing },
|
|
380
|
+
} as ModelSpec<Api>);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const kProviderModelIndex = Symbol("model-resolver.providerIndex");
|
|
384
|
+
type ModelsWithProviderIndex = readonly Model<Api>[] & {
|
|
385
|
+
[kProviderModelIndex]?: Map<string, Model<Api> | null>;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
function getProviderModelIndex(availableModels: readonly Model<Api>[]): Map<string, Model<Api> | null> {
|
|
389
|
+
const tagged = availableModels as ModelsWithProviderIndex;
|
|
390
|
+
const cached = tagged[kProviderModelIndex];
|
|
391
|
+
if (cached) return cached;
|
|
392
|
+
const index = new Map<string, Model<Api> | null>();
|
|
393
|
+
for (const m of availableModels) {
|
|
394
|
+
const key = `${m.provider.toLowerCase()}\u0000${m.id.toLowerCase()}`;
|
|
395
|
+
if (index.has(key)) {
|
|
396
|
+
index.set(key, null); // ambiguous sentinel; do not overwrite back
|
|
397
|
+
} else {
|
|
398
|
+
index.set(key, m);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
tagged[kProviderModelIndex] = index;
|
|
402
|
+
return index;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function resolveProviderModelReference(
|
|
406
|
+
provider: string,
|
|
407
|
+
modelId: string,
|
|
408
|
+
availableModels: readonly Model<Api>[],
|
|
409
|
+
): Model<Api> | undefined {
|
|
410
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
411
|
+
const normalizedModelId = modelId.trim().toLowerCase();
|
|
412
|
+
if (!normalizedProvider || !normalizedModelId) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const index = getProviderModelIndex(availableModels);
|
|
417
|
+
const exact = index.get(`${normalizedProvider}\u0000${normalizedModelId}`);
|
|
418
|
+
if (exact === null) {
|
|
419
|
+
return undefined; // ambiguous
|
|
420
|
+
}
|
|
421
|
+
if (exact !== undefined) {
|
|
422
|
+
return exact;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Retired effort-tier variant ids resolve to their collapsed logical
|
|
426
|
+
// model: hand-table aliases first, then the `X-thinking` → `X` grammar
|
|
427
|
+
// for auto-derived pairs. Exact lookup above always wins while raw is live.
|
|
428
|
+
const variantAliasId =
|
|
429
|
+
resolveVariantAlias(normalizedProvider, normalizedModelId) ?? stripThinkingVariantToken(normalizedModelId);
|
|
430
|
+
if (variantAliasId) {
|
|
431
|
+
const aliased = index.get(`${normalizedProvider}\u0000${variantAliasId.toLowerCase()}`);
|
|
432
|
+
if (aliased) {
|
|
433
|
+
return aliased;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const bedrockInferenceProfile = resolveBedrockInferenceProfileReference(provider, modelId, availableModels);
|
|
438
|
+
if (bedrockInferenceProfile) {
|
|
439
|
+
return bedrockInferenceProfile;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (normalizedProvider !== "openrouter") {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
for (const fallbackId of getOpenRouterFallbackModelIds(modelId).slice(1)) {
|
|
447
|
+
const fallback = index.get(`${normalizedProvider}\u0000${fallbackId.toLowerCase()}`);
|
|
448
|
+
if (fallback === null) {
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
if (fallback !== undefined) {
|
|
452
|
+
return cloneModelWithRequestedId(fallback, modelId);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface ModelMatchPreferences {
|
|
460
|
+
/** Most-recently-used model keys (provider/modelId) to prefer when ambiguous. */
|
|
461
|
+
usageOrder?: string[];
|
|
462
|
+
/** Provider precedence used for ambiguous unqualified model patterns. */
|
|
463
|
+
providerOrder?: readonly string[];
|
|
464
|
+
/** Providers to deprioritize when no recent usage or provider priority is available. */
|
|
465
|
+
deprioritizeProviders?: string[];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export type ModelLookupRegistry = Pick<ModelRegistry, "getAvailable">;
|
|
469
|
+
type CliModelRegistry = Pick<ModelRegistry, "getAll" | "getAvailable">;
|
|
470
|
+
type InitialModelRegistry = Pick<ModelRegistry, "getAvailable" | "find">;
|
|
471
|
+
type RestorableModelRegistry = Pick<ModelRegistry, "getAvailable" | "find" | "getApiKey">;
|
|
472
|
+
|
|
473
|
+
interface ModelPreferenceContext {
|
|
474
|
+
modelUsageRank: Map<string, number>;
|
|
475
|
+
providerUsageRank: Map<string, number>;
|
|
476
|
+
providerPriorityRank: Map<string, number>;
|
|
477
|
+
deprioritizedProviders: Set<string>;
|
|
478
|
+
modelOrder: Map<string, number>;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function buildPreferenceContext(
|
|
482
|
+
availableModels: Model<Api>[],
|
|
483
|
+
preferences: ModelMatchPreferences | undefined,
|
|
484
|
+
): ModelPreferenceContext {
|
|
485
|
+
const modelUsageRank = new Map<string, number>();
|
|
486
|
+
const providerUsageRank = new Map<string, number>();
|
|
487
|
+
const usageOrder = preferences?.usageOrder ?? [];
|
|
488
|
+
for (let i = 0; i < usageOrder.length; i += 1) {
|
|
489
|
+
const key = usageOrder[i];
|
|
490
|
+
if (!modelUsageRank.has(key)) {
|
|
491
|
+
modelUsageRank.set(key, i);
|
|
492
|
+
}
|
|
493
|
+
const parsed = parseModelString(key);
|
|
494
|
+
if (parsed && !providerUsageRank.has(parsed.provider)) {
|
|
495
|
+
providerUsageRank.set(parsed.provider, i);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const providerPriorityRank = buildModelProviderPriorityRank(preferences?.providerOrder);
|
|
499
|
+
const deprioritizedProviders = new Set(preferences?.deprioritizeProviders ?? []);
|
|
500
|
+
const modelOrder = new Map<string, number>();
|
|
501
|
+
for (let i = 0; i < availableModels.length; i += 1) {
|
|
502
|
+
modelOrder.set(formatModelString(availableModels[i]), i);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return { modelUsageRank, providerUsageRank, providerPriorityRank, deprioritizedProviders, modelOrder };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export function getModelMatchPreferences(
|
|
509
|
+
settings?: Partial<Pick<Settings, "get" | "getStorage">>,
|
|
510
|
+
): ModelMatchPreferences {
|
|
511
|
+
return {
|
|
512
|
+
usageOrder: settings?.getStorage?.()?.getModelUsageOrder(),
|
|
513
|
+
providerOrder: settings?.get?.("modelProviderOrder"),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function mergeModelMatchPreferences(
|
|
518
|
+
settings: Settings | undefined,
|
|
519
|
+
preferences: ModelMatchPreferences | undefined,
|
|
520
|
+
): ModelMatchPreferences {
|
|
521
|
+
const settingsPreferences = getModelMatchPreferences(settings);
|
|
522
|
+
return {
|
|
523
|
+
usageOrder: preferences?.usageOrder ?? settingsPreferences.usageOrder,
|
|
524
|
+
providerOrder: preferences?.providerOrder ?? settingsPreferences.providerOrder,
|
|
525
|
+
deprioritizeProviders: preferences?.deprioritizeProviders,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function pickPreferredModel(candidates: Model<Api>[], context: ModelPreferenceContext): Model<Api> {
|
|
530
|
+
if (candidates.length <= 1) return candidates[0];
|
|
531
|
+
return [...candidates].sort((a, b) => {
|
|
532
|
+
const aKey = formatModelString(a);
|
|
533
|
+
const bKey = formatModelString(b);
|
|
534
|
+
const aUsage = context.modelUsageRank.get(aKey);
|
|
535
|
+
const bUsage = context.modelUsageRank.get(bKey);
|
|
536
|
+
if (aUsage !== undefined || bUsage !== undefined) {
|
|
537
|
+
return (aUsage ?? Number.POSITIVE_INFINITY) - (bUsage ?? Number.POSITIVE_INFINITY);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const aProviderPriority = context.providerPriorityRank.get(a.provider.toLowerCase());
|
|
541
|
+
const bProviderPriority = context.providerPriorityRank.get(b.provider.toLowerCase());
|
|
542
|
+
if (aProviderPriority !== undefined || bProviderPriority !== undefined) {
|
|
543
|
+
return (aProviderPriority ?? Number.POSITIVE_INFINITY) - (bProviderPriority ?? Number.POSITIVE_INFINITY);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const aProviderUsage = context.providerUsageRank.get(a.provider);
|
|
547
|
+
const bProviderUsage = context.providerUsageRank.get(b.provider);
|
|
548
|
+
if (aProviderUsage !== undefined || bProviderUsage !== undefined) {
|
|
549
|
+
return (aProviderUsage ?? Number.POSITIVE_INFINITY) - (bProviderUsage ?? Number.POSITIVE_INFINITY);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const aDeprioritized = context.deprioritizedProviders.has(a.provider);
|
|
553
|
+
const bDeprioritized = context.deprioritizedProviders.has(b.provider);
|
|
554
|
+
if (aDeprioritized !== bDeprioritized) {
|
|
555
|
+
return aDeprioritized ? 1 : -1;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const aOrder = context.modelOrder.get(aKey) ?? 0;
|
|
559
|
+
const bOrder = context.modelOrder.get(bKey) ?? 0;
|
|
560
|
+
return aOrder - bOrder;
|
|
561
|
+
})[0];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Helper to check if a model ID looks like an alias (no date suffix)
|
|
566
|
+
* Dates are typically in format: -20241022 or -20250929
|
|
567
|
+
*/
|
|
568
|
+
function isAlias(id: string): boolean {
|
|
569
|
+
// Check if ID ends with -latest
|
|
570
|
+
if (id.endsWith("-latest")) return true;
|
|
571
|
+
|
|
572
|
+
// Check if ID ends with a date pattern (-YYYYMMDD)
|
|
573
|
+
const datePattern = /-\d{8}$/;
|
|
574
|
+
return !datePattern.test(id);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function includeSyntheticAllowedModels(available: Model<Api>[], allowedModels: Iterable<Model<Api>>): Model<Api>[] {
|
|
578
|
+
const allowedByKey = new Map<string, Model<Api>>();
|
|
579
|
+
for (const model of allowedModels) {
|
|
580
|
+
const key = formatModelString(model);
|
|
581
|
+
if (!allowedByKey.has(key)) {
|
|
582
|
+
allowedByKey.set(key, model);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (allowedByKey.size === 0) return [];
|
|
586
|
+
|
|
587
|
+
const result: Model<Api>[] = [];
|
|
588
|
+
for (const model of available) {
|
|
589
|
+
if (allowedByKey.delete(formatModelString(model))) {
|
|
590
|
+
result.push(model);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
result.push(...allowedByKey.values());
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Find an exact explicit provider/model match.
|
|
600
|
+
*/
|
|
601
|
+
function findExactModelReferenceMatch(modelReference: string, availableModels: Model<Api>[]): Model<Api> | undefined {
|
|
602
|
+
const trimmedReference = modelReference.trim();
|
|
603
|
+
if (!trimmedReference) {
|
|
604
|
+
return undefined;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const slashIndex = trimmedReference.indexOf("/");
|
|
608
|
+
if (slashIndex !== -1) {
|
|
609
|
+
const provider = trimmedReference.substring(0, slashIndex).trim();
|
|
610
|
+
const modelId = trimmedReference.substring(slashIndex + 1).trim();
|
|
611
|
+
if (provider && modelId) {
|
|
612
|
+
return resolveProviderModelReference(provider, modelId, availableModels);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return undefined;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* The single model-matching engine. Tries, in order:
|
|
619
|
+
* 1. exact `provider/id` reference (variant-alias and OpenRouter routed/date
|
|
620
|
+
* fallbacks included),
|
|
621
|
+
* 2. exact bare id (preference-ranked),
|
|
622
|
+
* 3. retired effort-tier variant alias (collapsed catalog entries),
|
|
623
|
+
* 4. provider-scoped fuzzy match,
|
|
624
|
+
* 5. substring match with the alias-vs-dated pick.
|
|
625
|
+
* Returns the matched model or undefined if no match found.
|
|
626
|
+
*
|
|
627
|
+
* `exactOnly` stops after the exact phases (1-3), skipping the fuzzy/substring
|
|
628
|
+
* fallbacks (4-5). Callers use it to resolve the full selector exactly before
|
|
629
|
+
* a trailing `:<level>` thinking suffix is split off, so the suffix can never
|
|
630
|
+
* be fuzzily absorbed into a longer sibling id (e.g. `kimi-for-coding:high`
|
|
631
|
+
* must not match `kimi-for-coding-highspeed`).
|
|
632
|
+
*/
|
|
633
|
+
function matchModel(
|
|
634
|
+
modelPattern: string,
|
|
635
|
+
availableModels: Model<Api>[],
|
|
636
|
+
context: ModelPreferenceContext,
|
|
637
|
+
options?: { exactOnly?: boolean },
|
|
638
|
+
): Model<Api> | undefined {
|
|
639
|
+
const exactRefMatch = findExactModelReferenceMatch(modelPattern, availableModels);
|
|
640
|
+
if (exactRefMatch) {
|
|
641
|
+
return exactRefMatch;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Exact ID match (case-insensitive) — this must happen before provider-scoped
|
|
645
|
+
// fuzzy matching so raw IDs that contain slashes (for example OpenRouter model
|
|
646
|
+
// IDs like "openai/gpt-4o:extended") still resolve as IDs instead of being
|
|
647
|
+
// misread as a provider-qualified selector.
|
|
648
|
+
const lowerPattern = modelPattern.toLowerCase();
|
|
649
|
+
const exactMatches = availableModels.filter(m => m.id.toLowerCase() === lowerPattern);
|
|
650
|
+
if (exactMatches.length > 0) {
|
|
651
|
+
return pickPreferredModel(exactMatches, context);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const bedrockInferenceProfile = resolveBedrockInferenceProfileModelId(modelPattern, availableModels);
|
|
655
|
+
if (bedrockInferenceProfile) {
|
|
656
|
+
return bedrockInferenceProfile;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Retired effort-tier variant ids (bare, no provider prefix) resolve to
|
|
660
|
+
// their collapsed logical model; models from the providers whose table
|
|
661
|
+
// declared the alias win ties. Auto-derived `X-thinking` pairs resolve
|
|
662
|
+
// through the grammar fallback.
|
|
663
|
+
const bareAlias = resolveBareVariantAlias(modelPattern);
|
|
664
|
+
const bareAliasTargetId = bareAlias?.id ?? stripThinkingVariantToken(modelPattern);
|
|
665
|
+
if (bareAliasTargetId) {
|
|
666
|
+
const lowerAliasTarget = bareAliasTargetId.toLowerCase();
|
|
667
|
+
const aliasMatches = availableModels.filter(m => m.id.toLowerCase() === lowerAliasTarget);
|
|
668
|
+
if (aliasMatches.length > 0) {
|
|
669
|
+
const preferred = bareAlias ? aliasMatches.filter(m => bareAlias.providers.includes(m.provider)) : [];
|
|
670
|
+
return pickPreferredModel(preferred.length > 0 ? preferred : aliasMatches, context);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Exact phases exhausted. Fuzzy/substring fallbacks (below) subsequence-match
|
|
675
|
+
// the whole pattern and would let a trailing `:<level>` thinking suffix bleed
|
|
676
|
+
// into a longer sibling id; callers that still hold an unstripped suffix ask
|
|
677
|
+
// for exact-only so the suffix is split off before any fuzzy attempt.
|
|
678
|
+
if (options?.exactOnly) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
681
|
+
// Check for provider/modelId format — fuzzy match within provider only.
|
|
682
|
+
const slashIndex = modelPattern.indexOf("/");
|
|
683
|
+
if (slashIndex !== -1) {
|
|
684
|
+
const provider = modelPattern.substring(0, slashIndex);
|
|
685
|
+
const modelId = modelPattern.substring(slashIndex + 1);
|
|
686
|
+
const lowerProvider = provider.toLowerCase();
|
|
687
|
+
const providerModels = availableModels.filter(m => m.provider.toLowerCase() === lowerProvider);
|
|
688
|
+
if (providerModels.length === 0) {
|
|
689
|
+
// The prefix is not a known provider in this candidate set, so treat the
|
|
690
|
+
// slash as part of the raw model ID and continue with generic matching.
|
|
691
|
+
} else {
|
|
692
|
+
// Let the routing fallback apply `@upstream` before fuzzy matching can consume the
|
|
693
|
+
// slug — but only for aggregator providers (OpenRouter / Vercel Gateway). Other
|
|
694
|
+
// providers have ids that legitimately end in `@` (Vertex `claude-opus-4-8@default`),
|
|
695
|
+
// and the fallback never routes them, so they must keep fuzzy matching.
|
|
696
|
+
if (splitUpstreamRouting(modelId) && providerModels.some(supportsUpstreamRouting)) {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
const scored = providerModels
|
|
700
|
+
.map(model => ({ model, match: fuzzyMatch(modelId, model.id) }))
|
|
701
|
+
.filter(entry => entry.match.matches);
|
|
702
|
+
if (scored.length === 0) {
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
scored.sort((a, b) => {
|
|
707
|
+
if (a.match.score !== b.match.score) return a.match.score - b.match.score;
|
|
708
|
+
const aKey = formatModelString(a.model);
|
|
709
|
+
const bKey = formatModelString(b.model);
|
|
710
|
+
const aUsage = context.modelUsageRank.get(aKey) ?? Number.POSITIVE_INFINITY;
|
|
711
|
+
const bUsage = context.modelUsageRank.get(bKey) ?? Number.POSITIVE_INFINITY;
|
|
712
|
+
if (aUsage !== bUsage) return aUsage - bUsage;
|
|
713
|
+
|
|
714
|
+
const aProviderUsage = context.providerUsageRank.get(a.model.provider) ?? Number.POSITIVE_INFINITY;
|
|
715
|
+
const bProviderUsage = context.providerUsageRank.get(b.model.provider) ?? Number.POSITIVE_INFINITY;
|
|
716
|
+
if (aProviderUsage !== bProviderUsage) return aProviderUsage - bProviderUsage;
|
|
717
|
+
|
|
718
|
+
const aOrder = context.modelOrder.get(aKey) ?? 0;
|
|
719
|
+
const bOrder = context.modelOrder.get(bKey) ?? 0;
|
|
720
|
+
return aOrder - bOrder;
|
|
721
|
+
});
|
|
722
|
+
return scored[0]?.model;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// No exact match - fall back to partial matching
|
|
727
|
+
const matches = availableModels.filter(
|
|
728
|
+
m => m.id.toLowerCase().includes(lowerPattern) || m.name?.toLowerCase().includes(lowerPattern),
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
if (matches.length === 0) {
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Separate into aliases and dated versions
|
|
736
|
+
const aliases = matches.filter(m => isAlias(m.id));
|
|
737
|
+
const datedVersions = matches.filter(m => !isAlias(m.id));
|
|
738
|
+
|
|
739
|
+
if (aliases.length > 0) {
|
|
740
|
+
return pickPreferredModel(aliases, context);
|
|
741
|
+
}
|
|
742
|
+
if (datedVersions.length === 0) return undefined;
|
|
743
|
+
|
|
744
|
+
if (datedVersions.length === 1) {
|
|
745
|
+
return datedVersions[0];
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const sortedById = [...datedVersions].sort((a, b) => b.id.localeCompare(a.id));
|
|
749
|
+
const topId = sortedById[0]?.id;
|
|
750
|
+
if (!topId) return undefined;
|
|
751
|
+
const topCandidates = sortedById.filter(model => model.id === topId);
|
|
752
|
+
return pickPreferredModel(topCandidates, context);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
export interface ParsedModelResult {
|
|
756
|
+
model: Model<Api> | undefined;
|
|
757
|
+
/** Thinking level if explicitly specified in pattern, undefined otherwise */
|
|
758
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
759
|
+
/** Upstream provider slug from an `@upstream` routing selector, if present. */
|
|
760
|
+
upstream?: string;
|
|
761
|
+
warning: string | undefined;
|
|
762
|
+
explicitThinkingLevel: boolean;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Parse a pattern to extract model and thinking level.
|
|
767
|
+
* Handles models with colons in their IDs (e.g., OpenRouter's :exacto suffix).
|
|
768
|
+
*
|
|
769
|
+
* Algorithm:
|
|
770
|
+
* 1. Try to match full pattern as a model
|
|
771
|
+
* 2. If found, return it with undefined thinking level
|
|
772
|
+
* 3. If not found and has colons, split on last colon:
|
|
773
|
+
* - If suffix is valid thinking level, use it and recurse on prefix
|
|
774
|
+
* - If suffix is invalid, warn and recurse on prefix
|
|
775
|
+
*
|
|
776
|
+
* @internal Exported for testing
|
|
777
|
+
*/
|
|
778
|
+
function parseModelPatternWithContext(
|
|
779
|
+
pattern: string,
|
|
780
|
+
availableModels: Model<Api>[],
|
|
781
|
+
context: ModelPreferenceContext,
|
|
782
|
+
options?: { allowInvalidThinkingSelectorFallback?: boolean },
|
|
783
|
+
): ParsedModelResult {
|
|
784
|
+
// Exact match on the full pattern first (no fuzzy): a literal id that
|
|
785
|
+
const exactMatch = matchModel(pattern, availableModels, context, { exactOnly: true });
|
|
786
|
+
if (exactMatch) {
|
|
787
|
+
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Prefer a fuzzy match whose actual id ends in the suffix, preserving
|
|
791
|
+
// shorthand selectors for literal tier models such as `router:low`. Other
|
|
792
|
+
// fuzzy results (e.g. `kimi-for-coding-highspeed`) cannot absorb the suffix.
|
|
793
|
+
const { base, level } = splitThinkingSuffix(pattern, -1, MAX_THINKING_SUFFIX_OPTIONS);
|
|
794
|
+
if (level) {
|
|
795
|
+
const literalSuffixMatch = matchModel(pattern, availableModels, context);
|
|
796
|
+
if (literalSuffixMatch?.id.toLowerCase().endsWith(`:${level}`)) {
|
|
797
|
+
return {
|
|
798
|
+
model: literalSuffixMatch,
|
|
799
|
+
thinkingLevel: undefined,
|
|
800
|
+
warning: undefined,
|
|
801
|
+
explicitThinkingLevel: false,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Strip a valid thinking suffix and recurse before accepting any other
|
|
806
|
+
// fuzzy match, so `:<level>` cannot be absorbed into a longer sibling
|
|
807
|
+
// id (e.g. `kimi-for-coding:high` must not match
|
|
808
|
+
// `kimi-for-coding-highspeed`). `max` is accepted only after the exact
|
|
809
|
+
// match above failed, so literal model IDs ending in `:max` keep winning.
|
|
810
|
+
const result = parseModelPatternWithContext(base, availableModels, context, options);
|
|
811
|
+
if (result.model) {
|
|
812
|
+
// Only use this thinking level if no warning from inner recursion
|
|
813
|
+
const explicitThinkingLevel = !result.warning;
|
|
814
|
+
return {
|
|
815
|
+
model: result.model,
|
|
816
|
+
thinkingLevel: explicitThinkingLevel ? level : undefined,
|
|
817
|
+
warning: result.warning,
|
|
818
|
+
explicitThinkingLevel,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// No valid thinking suffix: fall back to fuzzy/substring matching on the
|
|
825
|
+
// whole pattern.
|
|
826
|
+
const fallbackMatch = matchModel(pattern, availableModels, context);
|
|
827
|
+
if (fallbackMatch) {
|
|
828
|
+
return { model: fallbackMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const lastColonIndex = pattern.lastIndexOf(":");
|
|
832
|
+
if (lastColonIndex === -1) {
|
|
833
|
+
// No colons, pattern simply doesn't match any model
|
|
834
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
835
|
+
}
|
|
836
|
+
const prefix = pattern.substring(0, lastColonIndex);
|
|
837
|
+
const suffix = pattern.substring(lastColonIndex + 1);
|
|
838
|
+
|
|
839
|
+
const allowFallback = options?.allowInvalidThinkingSelectorFallback ?? true;
|
|
840
|
+
if (!allowFallback) {
|
|
841
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Invalid suffix - recurse on prefix and warn
|
|
845
|
+
const result = parseModelPatternWithContext(prefix, availableModels, context, options);
|
|
846
|
+
if (result.model) {
|
|
847
|
+
return {
|
|
848
|
+
model: result.model,
|
|
849
|
+
thinkingLevel: undefined,
|
|
850
|
+
warning: `Invalid thinking level "${suffix}" in pattern "${pattern}". Using default instead.`,
|
|
851
|
+
explicitThinkingLevel: false,
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
return result;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/** Match a single pattern with a pre-built preference context (direct match plus
|
|
858
|
+
* the `@upstream` routing fallback), so role resolution can reuse one context
|
|
859
|
+
* across every fallback pattern instead of rebuilding it per pattern. */
|
|
860
|
+
function matchPatternWithContext(
|
|
861
|
+
pattern: string,
|
|
862
|
+
availableModels: Model<Api>[],
|
|
863
|
+
context: ModelPreferenceContext,
|
|
864
|
+
options?: { allowInvalidThinkingSelectorFallback?: boolean },
|
|
865
|
+
): ParsedModelResult {
|
|
866
|
+
const direct = parseModelPatternWithContext(pattern, availableModels, context, options);
|
|
867
|
+
if (direct.model) return direct;
|
|
868
|
+
|
|
869
|
+
// No direct match: a trailing `@upstream` may be a provider-routing selector.
|
|
870
|
+
// Only honor it when the base resolves to an aggregator model (OpenRouter /
|
|
871
|
+
// Vercel Gateway); otherwise `@` stays part of the id and `direct` stands.
|
|
872
|
+
const routing = splitUpstreamRouting(pattern);
|
|
873
|
+
if (routing) {
|
|
874
|
+
const routed = parseModelPatternWithContext(routing.base, availableModels, context, options);
|
|
875
|
+
if (routed.model && supportsUpstreamRouting(routed.model)) {
|
|
876
|
+
return { ...routed, model: applyUpstreamRouting(routed.model, routing.upstream), upstream: routing.upstream };
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return direct;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
export function parseModelPattern(
|
|
883
|
+
pattern: string,
|
|
884
|
+
availableModels: Model<Api>[],
|
|
885
|
+
preferences?: ModelMatchPreferences,
|
|
886
|
+
options?: { allowInvalidThinkingSelectorFallback?: boolean },
|
|
887
|
+
): ParsedModelResult {
|
|
888
|
+
return matchPatternWithContext(
|
|
889
|
+
pattern,
|
|
890
|
+
availableModels,
|
|
891
|
+
buildPreferenceContext(availableModels, preferences),
|
|
892
|
+
options,
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const DEFAULT_MODEL_ROLE = "default";
|
|
897
|
+
const MODEL_ROLE_ALIAS_PREFIXES = [MODEL_ROLE_ALIAS_PREFIX, LEGACY_MODEL_ROLE_ALIAS_PREFIX];
|
|
898
|
+
|
|
899
|
+
export interface ModelRoleLookup {
|
|
900
|
+
getModelRole(role: ModelRole | string): string | undefined;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function isModelRole(role: string): role is ModelRole {
|
|
904
|
+
return (MODEL_ROLE_IDS as string[]).includes(role);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Minimum colon index for splitting a `:<level>` suffix off a role alias, or
|
|
909
|
+
* `undefined` when `value` is not role-alias shaped. Doubles as the slice
|
|
910
|
+
* offset of the role name for prefixed aliases (`@role`, `pi/role`); the bare
|
|
911
|
+
* `*` default alias returns 0 because its colon sits immediately after the
|
|
912
|
+
* one-character token (`*:xhigh`).
|
|
913
|
+
*/
|
|
914
|
+
function modelRoleAliasPrefixLength(value: string): number | undefined {
|
|
915
|
+
if (value === DEFAULT_MODEL_ROLE_ALIAS || value.startsWith(`${DEFAULT_MODEL_ROLE_ALIAS}:`)) return 0;
|
|
916
|
+
return MODEL_ROLE_ALIAS_PREFIXES.find(prefix => value.startsWith(prefix))?.length;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function getModelRoleAlias(value: string, settings?: ModelRoleLookup): string | undefined {
|
|
920
|
+
const normalized = value.trim();
|
|
921
|
+
const prefixLength = modelRoleAliasPrefixLength(normalized);
|
|
922
|
+
if (prefixLength === undefined) return undefined;
|
|
923
|
+
|
|
924
|
+
const candidate = normalized === DEFAULT_MODEL_ROLE_ALIAS ? DEFAULT_MODEL_ROLE : normalized.slice(prefixLength);
|
|
925
|
+
if (isModelRole(candidate) || settings?.getModelRole(candidate) !== undefined) return candidate;
|
|
926
|
+
return undefined;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function normalizeModelPatternList(value: string | string[] | undefined): string[] {
|
|
930
|
+
if (!value) return [];
|
|
931
|
+
const patterns = Array.isArray(value) ? value.flatMap(pattern => pattern.split(",")) : value.split(",");
|
|
932
|
+
return patterns.map(pattern => pattern.trim()).filter(Boolean);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Extract the first explicit model-role alias from a raw model selection.
|
|
937
|
+
*
|
|
938
|
+
* This intentionally runs before role expansion so callers can retain the
|
|
939
|
+
* source identity (`@smol`, `pi/slow`, or `*`) even when it resolves to a
|
|
940
|
+
* concrete provider/model or inherited fallback. Bare role names and explicit
|
|
941
|
+
* provider/model selectors are not role aliases.
|
|
942
|
+
*/
|
|
943
|
+
export function resolveExplicitModelRole(
|
|
944
|
+
value: string | string[] | undefined,
|
|
945
|
+
settings?: ModelRoleLookup,
|
|
946
|
+
): string | undefined {
|
|
947
|
+
for (const pattern of normalizeModelPatternList(value)) {
|
|
948
|
+
const prefixLength = modelRoleAliasPrefixLength(pattern);
|
|
949
|
+
if (prefixLength === undefined) continue;
|
|
950
|
+
const { base } = splitThinkingSuffix(pattern, prefixLength, MAX_THINKING_SUFFIX_OPTIONS);
|
|
951
|
+
const role = getModelRoleAlias(base, settings);
|
|
952
|
+
if (role) return role;
|
|
953
|
+
}
|
|
954
|
+
return undefined;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function isSessionInheritedAgentPattern(value: string): boolean {
|
|
958
|
+
return (
|
|
959
|
+
value === DEFAULT_MODEL_ROLE ||
|
|
960
|
+
value === formatModelRoleAlias(DEFAULT_MODEL_ROLE) ||
|
|
961
|
+
value === DEFAULT_MODEL_ROLE_ALIAS ||
|
|
962
|
+
value === `${LEGACY_MODEL_ROLE_ALIAS_PREFIX}${DEFAULT_MODEL_ROLE}` ||
|
|
963
|
+
value === formatModelRoleAlias("task") ||
|
|
964
|
+
value === `${LEGACY_MODEL_ROLE_ALIAS_PREFIX}task`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function shouldInheritDefaultBeforePriority(role: ModelRole): boolean {
|
|
969
|
+
return role === "smol" || role === "slow" || role === "designer";
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Roles that have no priority.json chain of their own reuse another role's
|
|
974
|
+
* list. The advisor — a second-opinion reviewer — defaults to the `slow`
|
|
975
|
+
* reasoning chain, but (unlike the `slow` role, see
|
|
976
|
+
* {@link shouldInheritDefaultBeforePriority}) never inherits the primary's
|
|
977
|
+
* model, so it stays a distinct strong model out of the box. The `tiny` role —
|
|
978
|
+
* the override for online title/memory/classifier tasks — reuses the `smol`
|
|
979
|
+
* fast chain so an unset tiny role auto-resolves to the same fast model smol
|
|
980
|
+
* would pick.
|
|
981
|
+
*/
|
|
982
|
+
const ROLE_PRIORITY_ALIAS: Partial<Record<ModelRole, keyof typeof MODEL_PRIO>> = {
|
|
983
|
+
advisor: "slow",
|
|
984
|
+
tiny: "smol",
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
/** Built-in priority patterns for a role, following {@link ROLE_PRIORITY_ALIAS}. */
|
|
988
|
+
function rolePriorityDefaults(role: ModelRole): string[] {
|
|
989
|
+
const key = ROLE_PRIORITY_ALIAS[role] ?? (role as keyof typeof MODEL_PRIO);
|
|
990
|
+
return normalizeModelPatternList(MODEL_PRIO[key]);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function resolveDefaultInheritedPatterns(
|
|
994
|
+
role: ModelRole,
|
|
995
|
+
configuredDefault: string | undefined,
|
|
996
|
+
roleDefaults: string[],
|
|
997
|
+
settings: ModelRoleLookup | undefined,
|
|
998
|
+
visited: Set<string>,
|
|
999
|
+
): string[] {
|
|
1000
|
+
if (!shouldInheritDefaultBeforePriority(role) || !configuredDefault) return [];
|
|
1001
|
+
|
|
1002
|
+
const resolved: string[] = [];
|
|
1003
|
+
for (const pattern of normalizeModelPatternList(configuredDefault)) {
|
|
1004
|
+
const { base: aliasCandidate, level: thinkingLevel } = splitThinkingSuffix(
|
|
1005
|
+
pattern,
|
|
1006
|
+
modelRoleAliasPrefixLength(pattern) ?? LEGACY_MODEL_ROLE_ALIAS_PREFIX.length,
|
|
1007
|
+
MAX_THINKING_SUFFIX_OPTIONS,
|
|
1008
|
+
);
|
|
1009
|
+
const aliasRole = getModelRoleAlias(aliasCandidate, settings);
|
|
1010
|
+
if (aliasRole === role) {
|
|
1011
|
+
// Self-alias (e.g. modelRoles.default = "@smol") would loop back to the
|
|
1012
|
+
resolved.push(
|
|
1013
|
+
...(thinkingLevel
|
|
1014
|
+
? roleDefaults.map(defaultPattern => `${defaultPattern}:${thinkingLevel}`)
|
|
1015
|
+
: roleDefaults),
|
|
1016
|
+
);
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
if (aliasRole && !visited.has(aliasRole)) {
|
|
1020
|
+
// Cross-role alias (e.g. modelRoles.default = "@slow"): resolve the
|
|
1021
|
+
// concrete model patterns instead of another role alias.
|
|
1022
|
+
const recursed = resolveConfiguredRolePattern(pattern, settings, new Set(visited));
|
|
1023
|
+
if (recursed && recursed.length > 0) {
|
|
1024
|
+
resolved.push(...recursed);
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
resolved.push(pattern);
|
|
1029
|
+
}
|
|
1030
|
+
return resolved;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function resolveConfiguredRolePattern(
|
|
1034
|
+
value: string,
|
|
1035
|
+
settings?: ModelRoleLookup,
|
|
1036
|
+
visited: Set<string> = new Set(),
|
|
1037
|
+
): string[] | undefined {
|
|
1038
|
+
const normalized = value.trim();
|
|
1039
|
+
if (!normalized) return undefined;
|
|
1040
|
+
|
|
1041
|
+
const { base: aliasCandidate, level: thinkingLevel } = splitThinkingSuffix(
|
|
1042
|
+
normalized,
|
|
1043
|
+
modelRoleAliasPrefixLength(normalized) ?? LEGACY_MODEL_ROLE_ALIAS_PREFIX.length,
|
|
1044
|
+
MAX_THINKING_SUFFIX_OPTIONS,
|
|
1045
|
+
);
|
|
1046
|
+
const role = getModelRoleAlias(aliasCandidate, settings);
|
|
1047
|
+
if (!role) return [normalized];
|
|
1048
|
+
if (visited.has(role)) return undefined;
|
|
1049
|
+
visited.add(role);
|
|
1050
|
+
|
|
1051
|
+
const configured = settings?.getModelRole(role)?.trim();
|
|
1052
|
+
const configuredDefault = settings?.getModelRole(DEFAULT_MODEL_ROLE)?.trim();
|
|
1053
|
+
const roleDefaults = isModelRole(role) ? rolePriorityDefaults(role) : [];
|
|
1054
|
+
const resolved = configured
|
|
1055
|
+
? normalizeModelPatternList(configured)
|
|
1056
|
+
: isModelRole(role)
|
|
1057
|
+
? resolveDefaultInheritedPatterns(role, configuredDefault, roleDefaults, settings, visited)
|
|
1058
|
+
: roleDefaults;
|
|
1059
|
+
if (resolved.length === 0) {
|
|
1060
|
+
resolved.push(...roleDefaults);
|
|
1061
|
+
}
|
|
1062
|
+
if (resolved.length === 0) {
|
|
1063
|
+
return undefined;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
return thinkingLevel ? resolved.map(pattern => `${pattern}:${thinkingLevel}`) : resolved;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Expand a role alias like "@smol" to the configured model string.
|
|
1071
|
+
*/
|
|
1072
|
+
export function expandRoleAlias(value: string, settings?: ModelRoleLookup): string {
|
|
1073
|
+
const normalized = value.trim();
|
|
1074
|
+
if (normalized === DEFAULT_MODEL_ROLE) {
|
|
1075
|
+
return settings?.getModelRole("default") ?? value;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const resolved = resolveConfiguredRolePattern(value, settings)?.[0];
|
|
1079
|
+
return resolved ?? value;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
export function resolveConfiguredModelPatterns(
|
|
1083
|
+
value: string | string[] | undefined,
|
|
1084
|
+
settings?: ModelRoleLookup,
|
|
1085
|
+
): string[] {
|
|
1086
|
+
const patterns = normalizeModelPatternList(value);
|
|
1087
|
+
return patterns.flatMap(pattern => {
|
|
1088
|
+
const resolved = resolveConfiguredRolePattern(pattern, settings);
|
|
1089
|
+
return resolved ?? [];
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
export interface AgentModelPatternResolutionOptions {
|
|
1093
|
+
/** Highest-priority request selector, when supplied by a caller. */
|
|
1094
|
+
requestModel?: string | string[];
|
|
1095
|
+
settingsOverride?: string | string[];
|
|
1096
|
+
agentModel?: string | string[];
|
|
1097
|
+
settings?: Settings;
|
|
1098
|
+
activeModelPattern?: string;
|
|
1099
|
+
fallbackModelPattern?: string;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
interface EffectiveAgentModelSelection {
|
|
1103
|
+
source?: string | string[];
|
|
1104
|
+
patterns: string[];
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
function resolveEffectiveAgentModelSelection(
|
|
1108
|
+
options: AgentModelPatternResolutionOptions,
|
|
1109
|
+
): EffectiveAgentModelSelection {
|
|
1110
|
+
const { requestModel, settingsOverride, agentModel, settings, activeModelPattern, fallbackModelPattern } = options;
|
|
1111
|
+
|
|
1112
|
+
const requestPatterns = resolveConfiguredModelPatterns(requestModel, settings);
|
|
1113
|
+
if (requestPatterns.length > 0) {
|
|
1114
|
+
return { source: requestModel, patterns: requestPatterns };
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const overridePatterns = resolveConfiguredModelPatterns(settingsOverride, settings);
|
|
1118
|
+
if (overridePatterns.length > 0) {
|
|
1119
|
+
return { source: settingsOverride, patterns: overridePatterns };
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const normalizedAgentPatterns = normalizeModelPatternList(agentModel);
|
|
1123
|
+
const configuredAgentPatterns = resolveConfiguredModelPatterns(agentModel, settings);
|
|
1124
|
+
const singleAgentPattern = normalizedAgentPatterns.length === 1 ? normalizedAgentPatterns[0] : undefined;
|
|
1125
|
+
const agentInheritsSessionModel = singleAgentPattern ? isSessionInheritedAgentPattern(singleAgentPattern) : false;
|
|
1126
|
+
if (configuredAgentPatterns.length > 0) {
|
|
1127
|
+
if (
|
|
1128
|
+
singleAgentPattern === formatModelRoleAlias("task") ||
|
|
1129
|
+
singleAgentPattern === `${LEGACY_MODEL_ROLE_ALIAS_PREFIX}task`
|
|
1130
|
+
) {
|
|
1131
|
+
return { source: agentModel, patterns: configuredAgentPatterns };
|
|
1132
|
+
}
|
|
1133
|
+
if (!agentInheritsSessionModel) return { source: agentModel, patterns: configuredAgentPatterns };
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const fallback =
|
|
1137
|
+
activeModelPattern?.trim() || fallbackModelPattern?.trim() || settings?.getModelRole("default")?.trim() || "";
|
|
1138
|
+
return { patterns: resolveConfiguredModelPatterns(fallback, settings) };
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/** Return the raw selector source that supplies the effective agent patterns. */
|
|
1142
|
+
export function resolveAgentModelSource(options: AgentModelPatternResolutionOptions): string | string[] | undefined {
|
|
1143
|
+
return resolveEffectiveAgentModelSelection(options).source;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[] {
|
|
1147
|
+
return resolveEffectiveAgentModelSelection(options).patterns;
|
|
1148
|
+
}
|
|
1149
|
+
/** Default prewalk hand-off target when no explicit target is configured. */
|
|
1150
|
+
export const DEFAULT_PREWALK_TARGET = "@smol";
|
|
1151
|
+
|
|
1152
|
+
export interface AgentPrewalkResolutionOptions {
|
|
1153
|
+
/** `task.agentPrewalk` settings value for this agent: `"on"`, `"off"`, or a model pattern. */
|
|
1154
|
+
settingsOverride?: string;
|
|
1155
|
+
/** Agent definition `prewalk` frontmatter: `true` = default target, string = custom target pattern. */
|
|
1156
|
+
agentPrewalk?: boolean | string;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Effective prewalk target pattern for a subagent, or `undefined` when prewalk
|
|
1161
|
+
* is disabled. The settings override decides enablement first ("off" wins,
|
|
1162
|
+
* "on" enables with the agent's own target or {@link DEFAULT_PREWALK_TARGET},
|
|
1163
|
+
* any other value is a custom target pattern); otherwise the agent
|
|
1164
|
+
* definition's `prewalk` field applies. Role aliases in the returned pattern
|
|
1165
|
+
* are expanded later by {@link resolveModelOverride}.
|
|
1166
|
+
*/
|
|
1167
|
+
export function resolveAgentPrewalkPattern(options: AgentPrewalkResolutionOptions): string | undefined {
|
|
1168
|
+
const agentPattern =
|
|
1169
|
+
typeof options.agentPrewalk === "string" && options.agentPrewalk.trim() ? options.agentPrewalk.trim() : undefined;
|
|
1170
|
+
const override = options.settingsOverride?.trim();
|
|
1171
|
+
if (override) {
|
|
1172
|
+
const lowered = override.toLowerCase();
|
|
1173
|
+
if (lowered === "off" || lowered === "false") return undefined;
|
|
1174
|
+
if (lowered === "on" || lowered === "true") return agentPattern ?? DEFAULT_PREWALK_TARGET;
|
|
1175
|
+
return override;
|
|
1176
|
+
}
|
|
1177
|
+
if (options.agentPrewalk === true) return DEFAULT_PREWALK_TARGET;
|
|
1178
|
+
return agentPattern;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Resolve a model role value into a concrete model and thinking metadata.
|
|
1183
|
+
*/
|
|
1184
|
+
export interface ResolvedModelRoleValue {
|
|
1185
|
+
model: Model<Api> | undefined;
|
|
1186
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
1187
|
+
/** matchedPatternIndex identifies the first configured pattern that matched an available model. */
|
|
1188
|
+
matchedPatternIndex?: number;
|
|
1189
|
+
explicitThinkingLevel: boolean;
|
|
1190
|
+
warning: string | undefined;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
export function resolveModelRoleValue(
|
|
1194
|
+
roleValue: string | undefined,
|
|
1195
|
+
availableModels: Model<Api>[],
|
|
1196
|
+
options?: { settings?: Settings; roleLookup?: ModelRoleLookup; matchPreferences?: ModelMatchPreferences },
|
|
1197
|
+
): ResolvedModelRoleValue {
|
|
1198
|
+
if (!roleValue) {
|
|
1199
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const normalized = roleValue.trim();
|
|
1203
|
+
if (!normalized || normalized === DEFAULT_MODEL_ROLE) {
|
|
1204
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.roleLookup ?? options?.settings);
|
|
1208
|
+
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
1209
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
let warning: string | undefined;
|
|
1213
|
+
const matchPreferences = mergeModelMatchPreferences(options?.settings, options?.matchPreferences);
|
|
1214
|
+
// Build the O(n) preference context (model-order map over all available
|
|
1215
|
+
// models) once and reuse it across every fallback pattern instead of
|
|
1216
|
+
// rebuilding it per pattern inside parseModelPattern.
|
|
1217
|
+
const preferenceContext = buildPreferenceContext(availableModels, matchPreferences);
|
|
1218
|
+
for (const [patternIndex, effectivePattern] of effectivePatterns.entries()) {
|
|
1219
|
+
const resolved = matchPatternWithContext(effectivePattern, availableModels, preferenceContext);
|
|
1220
|
+
if (resolved.model) {
|
|
1221
|
+
return {
|
|
1222
|
+
model: resolved.model,
|
|
1223
|
+
matchedPatternIndex: patternIndex,
|
|
1224
|
+
thinkingLevel: resolved.explicitThinkingLevel
|
|
1225
|
+
? resolved.thinkingLevel === AUTO_THINKING
|
|
1226
|
+
? AUTO_THINKING
|
|
1227
|
+
: (resolveThinkingLevelForModel(resolved.model, resolved.thinkingLevel) ?? resolved.thinkingLevel)
|
|
1228
|
+
: resolved.thinkingLevel,
|
|
1229
|
+
explicitThinkingLevel: resolved.explicitThinkingLevel,
|
|
1230
|
+
warning: resolved.warning,
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
if (!warning && resolved.warning) {
|
|
1234
|
+
warning = resolved.warning;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
interface ExplicitThinkingSelectorOptions {
|
|
1242
|
+
isLiteralModelId?: (provider: string, id: string) => boolean;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function isLiteralModelSelector(value: string, options?: ExplicitThinkingSelectorOptions): boolean {
|
|
1246
|
+
const parsed = parseModelString(value);
|
|
1247
|
+
return parsed !== undefined && options?.isLiteralModelId?.(parsed.provider, parsed.id) === true;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
export function extractExplicitThinkingSelector(
|
|
1251
|
+
value: string | undefined,
|
|
1252
|
+
settings?: Settings,
|
|
1253
|
+
options?: ExplicitThinkingSelectorOptions,
|
|
1254
|
+
): ConfiguredThinkingLevel | undefined {
|
|
1255
|
+
if (!value) return undefined;
|
|
1256
|
+
const normalized = value.trim();
|
|
1257
|
+
if (!normalized || normalized === DEFAULT_MODEL_ROLE) return undefined;
|
|
1258
|
+
|
|
1259
|
+
const visited = new Set<string>();
|
|
1260
|
+
let current = normalized;
|
|
1261
|
+
while (!visited.has(current)) {
|
|
1262
|
+
visited.add(current);
|
|
1263
|
+
const rolePrefixLength = modelRoleAliasPrefixLength(current) ?? LEGACY_MODEL_ROLE_ALIAS_PREFIX.length;
|
|
1264
|
+
const strictSelector = splitThinkingSuffix(current, rolePrefixLength).level;
|
|
1265
|
+
if (strictSelector) {
|
|
1266
|
+
return strictSelector;
|
|
1267
|
+
}
|
|
1268
|
+
const maxSelector = splitThinkingSuffix(current, rolePrefixLength, MAX_THINKING_SUFFIX_OPTIONS).level;
|
|
1269
|
+
if (
|
|
1270
|
+
maxSelector &&
|
|
1271
|
+
(modelRoleAliasPrefixLength(current) !== undefined || !isLiteralModelSelector(current, options))
|
|
1272
|
+
) {
|
|
1273
|
+
return maxSelector;
|
|
1274
|
+
}
|
|
1275
|
+
const expanded = expandRoleAlias(current, settings).trim();
|
|
1276
|
+
if (!expanded || expanded === current) break;
|
|
1277
|
+
if (expanded === DEFAULT_MODEL_ROLE) return undefined;
|
|
1278
|
+
current = expanded;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
return undefined;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* Resolve a model identifier or pattern to a Model instance.
|
|
1286
|
+
*/
|
|
1287
|
+
export function resolveModelFromString(
|
|
1288
|
+
value: string,
|
|
1289
|
+
available: Model<Api>[],
|
|
1290
|
+
matchPreferences?: ModelMatchPreferences,
|
|
1291
|
+
): Model<Api> | undefined {
|
|
1292
|
+
const exact = available.find(model => `${model.provider}/${model.id}` === value);
|
|
1293
|
+
if (exact) return exact;
|
|
1294
|
+
const parsed = parseModelString(value, {
|
|
1295
|
+
...MAX_THINKING_SUFFIX_OPTIONS,
|
|
1296
|
+
isLiteralModelId: (provider, id) => available.some(model => model.provider === provider && model.id === id),
|
|
1297
|
+
});
|
|
1298
|
+
if (parsed) {
|
|
1299
|
+
const parsedExact = available.find(model => model.provider === parsed.provider && model.id === parsed.id);
|
|
1300
|
+
if (parsedExact) return parsedExact;
|
|
1301
|
+
}
|
|
1302
|
+
return parseModelPattern(value, available, matchPreferences).model;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Resolve a model from configured roles, honoring order and overrides.
|
|
1307
|
+
*/
|
|
1308
|
+
export function resolveModelFromSettings(options: {
|
|
1309
|
+
settings: Settings;
|
|
1310
|
+
availableModels: Model<Api>[];
|
|
1311
|
+
matchPreferences?: ModelMatchPreferences;
|
|
1312
|
+
roleOrder?: readonly ModelRole[];
|
|
1313
|
+
}): Model<Api> | undefined {
|
|
1314
|
+
const { settings, availableModels, matchPreferences, roleOrder } = options;
|
|
1315
|
+
const roles = roleOrder ?? MODEL_ROLE_IDS;
|
|
1316
|
+
let sawConfiguredProviderQualifiedRole = false;
|
|
1317
|
+
for (const role of roles) {
|
|
1318
|
+
const configured = settings.getModelRole(role);
|
|
1319
|
+
if (!configured) continue;
|
|
1320
|
+
const expanded = expandRoleAlias(configured, settings).trim();
|
|
1321
|
+
if (expanded.includes("/")) {
|
|
1322
|
+
sawConfiguredProviderQualifiedRole = true;
|
|
1323
|
+
}
|
|
1324
|
+
const resolved = resolveModelFromString(expanded, availableModels, matchPreferences);
|
|
1325
|
+
if (resolved) return resolved;
|
|
1326
|
+
}
|
|
1327
|
+
return sawConfiguredProviderQualifiedRole ? undefined : availableModels[0];
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* Resolve a list of override patterns to the first matching model.
|
|
1332
|
+
*/
|
|
1333
|
+
export function resolveModelOverride(
|
|
1334
|
+
modelPatterns: string[],
|
|
1335
|
+
modelRegistry: ModelLookupRegistry,
|
|
1336
|
+
settings?: Settings,
|
|
1337
|
+
): { model?: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel; explicitThinkingLevel: boolean; warning?: string } {
|
|
1338
|
+
if (modelPatterns.length === 0) return { explicitThinkingLevel: false };
|
|
1339
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1340
|
+
const matchPreferences = getModelMatchPreferences(settings);
|
|
1341
|
+
let warning: string | undefined;
|
|
1342
|
+
for (const pattern of modelPatterns) {
|
|
1343
|
+
const {
|
|
1344
|
+
model,
|
|
1345
|
+
thinkingLevel,
|
|
1346
|
+
explicitThinkingLevel,
|
|
1347
|
+
warning: patternWarning,
|
|
1348
|
+
} = resolveModelRoleValue(pattern, availableModels, {
|
|
1349
|
+
settings,
|
|
1350
|
+
matchPreferences,
|
|
1351
|
+
});
|
|
1352
|
+
if (model) {
|
|
1353
|
+
return { model, thinkingLevel, explicitThinkingLevel, warning: patternWarning };
|
|
1354
|
+
}
|
|
1355
|
+
if (!warning && patternWarning) warning = patternWarning;
|
|
1356
|
+
}
|
|
1357
|
+
return { explicitThinkingLevel: false, warning };
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* Resolve a list of override patterns to the first matching model, with an
|
|
1362
|
+
* auth-aware fallback to the parent session's active model.
|
|
1363
|
+
*
|
|
1364
|
+
* If the resolved subagent model has no working credentials (provider has no
|
|
1365
|
+
* usable auth), and the parent's active model resolves with working auth,
|
|
1366
|
+
* use the parent's model instead. This prevents subagent dispatch from
|
|
1367
|
+
* silently routing to a provider the user can't actually call (e.g.
|
|
1368
|
+
* `modelRoles.task` pointing at an unqualified id whose only available
|
|
1369
|
+
* provider variant has no configured credentials — see #985).
|
|
1370
|
+
*
|
|
1371
|
+
* `sessionId` is forwarded to `getApiKey` so that session-sticky OAuth
|
|
1372
|
+
* credentials resolve correctly during the pre-flight auth check. Without it,
|
|
1373
|
+
* providers with multiple OAuth accounts may return `undefined` even though
|
|
1374
|
+
* the credential is usable once the subagent session starts — see #5325.
|
|
1375
|
+
*
|
|
1376
|
+
* Keyless-by-design providers (llama.cpp, ollama, lm-studio) advertise the
|
|
1377
|
+
* `kNoAuth` sentinel from `getApiKey` to signal that they do not require
|
|
1378
|
+
* credentials. Those are treated as authenticated here so an explicitly
|
|
1379
|
+
* configured local model is never silently rerouted to the parent's remote
|
|
1380
|
+
* provider (see #1008).
|
|
1381
|
+
*
|
|
1382
|
+
* If neither the subagent nor the parent has working auth, returns the
|
|
1383
|
+
* primary resolution unchanged so the existing error path still surfaces
|
|
1384
|
+
* a meaningful failure downstream.
|
|
1385
|
+
*/
|
|
1386
|
+
export async function resolveModelOverrideWithAuthFallback(
|
|
1387
|
+
modelPatterns: string[],
|
|
1388
|
+
parentActiveModelPattern: string | undefined,
|
|
1389
|
+
modelRegistry: ModelLookupRegistry & Pick<ModelRegistry, "getApiKey">,
|
|
1390
|
+
settings?: Settings,
|
|
1391
|
+
sessionId?: string,
|
|
1392
|
+
): Promise<{
|
|
1393
|
+
model?: Model<Api>;
|
|
1394
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
1395
|
+
explicitThinkingLevel: boolean;
|
|
1396
|
+
authFallbackUsed: boolean;
|
|
1397
|
+
warning?: string;
|
|
1398
|
+
}> {
|
|
1399
|
+
const primary = resolveModelOverride(modelPatterns, modelRegistry, settings);
|
|
1400
|
+
if (!primary.model || !parentActiveModelPattern) {
|
|
1401
|
+
return { ...primary, authFallbackUsed: false };
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
const primaryKey = await modelRegistry.getApiKey(primary.model, sessionId);
|
|
1405
|
+
if (primaryKey === kNoAuth || isAuthenticated(primaryKey)) {
|
|
1406
|
+
return { ...primary, authFallbackUsed: false };
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
const fallback = resolveModelOverride([parentActiveModelPattern], modelRegistry, settings);
|
|
1410
|
+
if (!fallback.model) {
|
|
1411
|
+
return { ...primary, authFallbackUsed: false };
|
|
1412
|
+
}
|
|
1413
|
+
if (modelsAreEqual(fallback.model, primary.model)) {
|
|
1414
|
+
return { ...primary, authFallbackUsed: false };
|
|
1415
|
+
}
|
|
1416
|
+
const fallbackKey = await modelRegistry.getApiKey(fallback.model, sessionId);
|
|
1417
|
+
if (!isAuthenticated(fallbackKey)) {
|
|
1418
|
+
return { ...primary, authFallbackUsed: false };
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
return { ...fallback, authFallbackUsed: true, warning: primary.warning ?? fallback.warning };
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
/**
|
|
1425
|
+
* Resolve a list of role patterns to the first matching model.
|
|
1426
|
+
*/
|
|
1427
|
+
export function resolveRoleSelection(
|
|
1428
|
+
roles: readonly string[],
|
|
1429
|
+
settings: Settings,
|
|
1430
|
+
availableModels: Model<Api>[],
|
|
1431
|
+
): { model: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
1432
|
+
const matchPreferences = getModelMatchPreferences(settings);
|
|
1433
|
+
for (const role of roles) {
|
|
1434
|
+
const resolved = resolveModelRoleValue(settings.getModelRole(role), availableModels, {
|
|
1435
|
+
settings,
|
|
1436
|
+
matchPreferences,
|
|
1437
|
+
});
|
|
1438
|
+
if (resolved.model) {
|
|
1439
|
+
return { model: resolved.model, thinkingLevel: resolved.thinkingLevel };
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return undefined;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Resolve the model for the `advisor` role. A configured `modelRoles.advisor`
|
|
1447
|
+
* wins outright (a bad override surfaces as no model rather than silently
|
|
1448
|
+
* running something else); when unset it falls back to the `slow` priority
|
|
1449
|
+
* chain via {@link ROLE_PRIORITY_ALIAS} — a strong reasoning model that, unlike
|
|
1450
|
+
* the `slow` role itself, never inherits the primary's model. Returns undefined
|
|
1451
|
+
* only when no candidate in the resolved chain is available.
|
|
1452
|
+
*/
|
|
1453
|
+
export function resolveAdvisorRoleSelection(
|
|
1454
|
+
settings: Settings,
|
|
1455
|
+
availableModels: Model<Api>[],
|
|
1456
|
+
): { model: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
1457
|
+
const resolved = resolveModelRoleValue(formatModelRoleAlias("advisor"), availableModels, {
|
|
1458
|
+
settings,
|
|
1459
|
+
matchPreferences: getModelMatchPreferences(settings),
|
|
1460
|
+
});
|
|
1461
|
+
return resolved.model ? { model: resolved.model, thinkingLevel: resolved.thinkingLevel } : undefined;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* Resolve model patterns to actual Model objects with optional thinking levels
|
|
1466
|
+
* Format: "pattern:level" where :level is optional
|
|
1467
|
+
* For each pattern, finds all matching models and picks the best version:
|
|
1468
|
+
* 1. Prefer alias (e.g., claude-sonnet-4-5) over dated versions (claude-sonnet-4-5-20250929)
|
|
1469
|
+
* 2. If no alias, pick the latest dated version
|
|
1470
|
+
*
|
|
1471
|
+
* Supports models with colons in their IDs (e.g., OpenRouter's model:exacto).
|
|
1472
|
+
* The algorithm tries to match the full pattern first, then progressively
|
|
1473
|
+
* strips colon-suffixes to find a match.
|
|
1474
|
+
*/
|
|
1475
|
+
export async function resolveModelScope(
|
|
1476
|
+
patterns: string[],
|
|
1477
|
+
modelRegistry: Pick<ModelRegistry, "getAvailable">,
|
|
1478
|
+
preferences?: ModelMatchPreferences,
|
|
1479
|
+
settings?: Settings,
|
|
1480
|
+
): Promise<ScopedModel[]> {
|
|
1481
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1482
|
+
const context = buildPreferenceContext(availableModels, preferences);
|
|
1483
|
+
const scopedModels: ScopedModel[] = [];
|
|
1484
|
+
const addScopedModel = (model: Model<Api>, thinkingLevel: ThinkingLevel | undefined, explicit: boolean) => {
|
|
1485
|
+
if (scopedModels.some(sm => modelsAreEqual(sm.model, model))) return;
|
|
1486
|
+
scopedModels.push({
|
|
1487
|
+
model,
|
|
1488
|
+
thinkingLevel: explicit
|
|
1489
|
+
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
1490
|
+
: thinkingLevel,
|
|
1491
|
+
explicitThinkingLevel: explicit,
|
|
1492
|
+
});
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
for (const pattern of patterns) {
|
|
1496
|
+
// Check if pattern contains glob characters
|
|
1497
|
+
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("[")) {
|
|
1498
|
+
// Extract optional thinking level suffix (e.g., "provider/*:high") only
|
|
1499
|
+
// after literal `:max` globs had a chance to match real model IDs.
|
|
1500
|
+
const {
|
|
1501
|
+
models: matchingModels,
|
|
1502
|
+
thinkingLevel,
|
|
1503
|
+
explicitThinkingLevel,
|
|
1504
|
+
} = resolveGlobScopePattern(pattern, availableModels);
|
|
1505
|
+
|
|
1506
|
+
if (matchingModels.length === 0) {
|
|
1507
|
+
logger.warn(`No models match pattern "${pattern}"`);
|
|
1508
|
+
continue;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
for (const model of matchingModels) {
|
|
1512
|
+
addScopedModel(model, thinkingLevel, explicitThinkingLevel);
|
|
1513
|
+
}
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// Role aliases (`@smol`, `pi/slow`) resolve to the role's single concrete
|
|
1518
|
+
// model — not its whole fallback chain — so a role contributes one scope
|
|
1519
|
+
// entry exactly like `--model` would pick. (Bare `*` stays a match-all
|
|
1520
|
+
// glob above; scope semantics, not the default-role alias.)
|
|
1521
|
+
if (settings && modelRoleAliasPrefixLength(pattern) !== undefined) {
|
|
1522
|
+
const resolved = resolveModelRoleValue(pattern, availableModels, { settings, matchPreferences: preferences });
|
|
1523
|
+
if (resolved.warning) logger.warn(resolved.warning);
|
|
1524
|
+
if (!resolved.model) {
|
|
1525
|
+
logger.warn(`No models match pattern "${pattern}"`);
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (resolved.thinkingLevel === AUTO_THINKING) {
|
|
1529
|
+
addScopedModel(resolved.model, undefined, false);
|
|
1530
|
+
} else {
|
|
1531
|
+
addScopedModel(resolved.model, resolved.thinkingLevel, resolved.explicitThinkingLevel);
|
|
1532
|
+
}
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
const { model, thinkingLevel, warning, explicitThinkingLevel } = parseModelPatternWithContext(
|
|
1537
|
+
pattern,
|
|
1538
|
+
availableModels,
|
|
1539
|
+
context,
|
|
1540
|
+
);
|
|
1541
|
+
|
|
1542
|
+
if (warning) {
|
|
1543
|
+
logger.warn(warning);
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if (!model) {
|
|
1547
|
+
logger.warn(`No models match pattern "${pattern}"`);
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// Scoped models (Ctrl+P cycling) carry concrete per-model overrides;
|
|
1552
|
+
// `auto` lives on the session, so drop the sentinel here.
|
|
1553
|
+
if (thinkingLevel === AUTO_THINKING) {
|
|
1554
|
+
addScopedModel(model, undefined, false);
|
|
1555
|
+
} else {
|
|
1556
|
+
addScopedModel(model, thinkingLevel, explicitThinkingLevel);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
return scopedModels;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Resolve the set of models a session is allowed to use, given the active
|
|
1565
|
+
* settings. Starts from `modelRegistry.getAvailable()` (so disabled providers
|
|
1566
|
+
* and providers without credentials are already filtered out) and, when
|
|
1567
|
+
* `enabledModels` is configured for the current path scope, further restricts
|
|
1568
|
+
* the result to models matching those patterns.
|
|
1569
|
+
*
|
|
1570
|
+
* Returns the unfiltered available list when `enabledModels` is empty.
|
|
1571
|
+
* Returns an empty list when `enabledModels` is configured but no model matches
|
|
1572
|
+
* any pattern — callers MUST treat this as "no usable model" rather than
|
|
1573
|
+
* falling back to the global default (see issue #1022).
|
|
1574
|
+
*/
|
|
1575
|
+
export async function resolveAllowedModels(
|
|
1576
|
+
modelRegistry: Pick<ModelRegistry, "getAvailable">,
|
|
1577
|
+
settings: Settings | undefined,
|
|
1578
|
+
preferences?: ModelMatchPreferences,
|
|
1579
|
+
): Promise<Model<Api>[]> {
|
|
1580
|
+
const available = modelRegistry.getAvailable();
|
|
1581
|
+
const patterns = settings?.get("enabledModels");
|
|
1582
|
+
if (!patterns || patterns.length === 0) {
|
|
1583
|
+
return available;
|
|
1584
|
+
}
|
|
1585
|
+
const scoped = await resolveModelScope(patterns, modelRegistry, preferences, settings);
|
|
1586
|
+
if (scoped.length === 0) {
|
|
1587
|
+
return [];
|
|
1588
|
+
}
|
|
1589
|
+
return includeSyntheticAllowedModels(
|
|
1590
|
+
available,
|
|
1591
|
+
scoped.map(entry => entry.model),
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Synchronous subset of {@link resolveAllowedModels} for contexts where async is unavailable
|
|
1597
|
+
* (e.g. `getAvailableModels()` which is called from the ACP model-list advertisement, RPC
|
|
1598
|
+
* `get_available_models`, and the `/model` slash command). Uses the same effective
|
|
1599
|
+
* `enabledModels` scope semantics as startup resolution:
|
|
1600
|
+
*
|
|
1601
|
+
* - Glob selectors match `provider/modelId` and bare model id
|
|
1602
|
+
* - Exact `provider/modelId`, bare ids, provider-scoped fuzzy, and substring selectors
|
|
1603
|
+
* resolve through the shared model-pattern matcher
|
|
1604
|
+
* - Optional `:thinkingLevel` suffixes are stripped only when valid
|
|
1605
|
+
*
|
|
1606
|
+
* When no pattern resolves to any model (misconfiguration / typo) an empty list is returned,
|
|
1607
|
+
* consistent with the empty-list contract of {@link resolveAllowedModels}. Callers that render
|
|
1608
|
+
* a UI picker should treat an empty list as "hide the picker entry", matching how the SDK
|
|
1609
|
+
* surfaces the same misconfiguration during session initialization.
|
|
1610
|
+
*/
|
|
1611
|
+
export function filterAvailableModelsByEnabledPatterns(
|
|
1612
|
+
available: Model<Api>[],
|
|
1613
|
+
patterns: readonly string[],
|
|
1614
|
+
settings?: Settings,
|
|
1615
|
+
): Model<Api>[] {
|
|
1616
|
+
if (patterns.length === 0) return available;
|
|
1617
|
+
|
|
1618
|
+
const context = buildPreferenceContext(available, undefined);
|
|
1619
|
+
const allowedModels: Model<Api>[] = [];
|
|
1620
|
+
const addAllowed = (model: Model<Api>) => {
|
|
1621
|
+
allowedModels.push(model);
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
for (const pattern of patterns) {
|
|
1625
|
+
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("[")) {
|
|
1626
|
+
for (const model of resolveGlobScopePattern(pattern, available).models) {
|
|
1627
|
+
addAllowed(model);
|
|
1628
|
+
}
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// Mirror resolveModelScope: role aliases resolve to the role's model.
|
|
1633
|
+
if (settings && modelRoleAliasPrefixLength(pattern) !== undefined) {
|
|
1634
|
+
const { model } = resolveModelRoleValue(pattern, available, { settings });
|
|
1635
|
+
if (model) addAllowed(model);
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
const { model } = parseModelPatternWithContext(pattern, available, context);
|
|
1640
|
+
if (model) {
|
|
1641
|
+
addAllowed(model);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
return includeSyntheticAllowedModels(available, allowedModels);
|
|
1646
|
+
}
|
|
1647
|
+
function findExactCliModel(
|
|
1648
|
+
selector: string,
|
|
1649
|
+
allModels: Model<Api>[],
|
|
1650
|
+
availableModels: Model<Api>[],
|
|
1651
|
+
options?: { catalogFallback?: boolean },
|
|
1652
|
+
): Model<Api> | undefined {
|
|
1653
|
+
// Explicit provider/id references stay authoritative against the full catalog.
|
|
1654
|
+
const referenced = findExactModelReferenceMatch(selector, allModels);
|
|
1655
|
+
if (referenced) return referenced;
|
|
1656
|
+
|
|
1657
|
+
// Flat-id (or full-selector-string) matches prefer authenticated providers,
|
|
1658
|
+
// then fall back to catalog order. This covers aggregator-style flat ids
|
|
1659
|
+
// that merely look provider-qualified (e.g. "openai/gpt-oss-120b" hosted on
|
|
1660
|
+
// OpenRouter), where the provider/id decomposition above found nothing.
|
|
1661
|
+
const lower = selector.toLowerCase();
|
|
1662
|
+
const isFlatMatch = (model: Model<Api>) =>
|
|
1663
|
+
model.id.toLowerCase() === lower || formatModelString(model).toLowerCase() === lower;
|
|
1664
|
+
const preferred = availableModels.find(isFlatMatch);
|
|
1665
|
+
if (preferred) return preferred;
|
|
1666
|
+
// The unauthenticated catalog fallback is a weak match: a bare id like
|
|
1667
|
+
// `default` collides with the bundled `cursor/default` model, which must not
|
|
1668
|
+
// shadow a configured `modelRoles.default` role the user can actually run.
|
|
1669
|
+
// Callers resolving a possible role name pass `catalogFallback: false` so the
|
|
1670
|
+
// role gets a chance first; the deferred fuzzy fallback below still recovers
|
|
1671
|
+
// the catalog id when no role matches.
|
|
1672
|
+
if (options?.catalogFallback === false) return undefined;
|
|
1673
|
+
return availableModels === allModels ? undefined : allModels.find(isFlatMatch);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
export interface ResolveCliModelResult {
|
|
1677
|
+
model: Model<Api> | undefined;
|
|
1678
|
+
/** configuredPatterns contains the role's ordered primary candidates. */
|
|
1679
|
+
configuredPatterns?: string[];
|
|
1680
|
+
/** configuredRole identifies the role expanded into configuredPatterns. */
|
|
1681
|
+
configuredRole?: string;
|
|
1682
|
+
/** configuredPatternIndex identifies the configured role pattern that matched an available model. */
|
|
1683
|
+
configuredPatternIndex?: number;
|
|
1684
|
+
selector?: string;
|
|
1685
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
1686
|
+
warning: string | undefined;
|
|
1687
|
+
error: string | undefined;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* Resolve a single model from CLI flags.
|
|
1692
|
+
*
|
|
1693
|
+
* Explicit `provider/id` references and authenticated bare ids take precedence
|
|
1694
|
+
* over configured role names, which in turn take precedence over an
|
|
1695
|
+
* unauthenticated catalog-only id (so a bundled `cursor/default` never shadows a
|
|
1696
|
+
* configured `modelRoles.default`).
|
|
1697
|
+
*/
|
|
1698
|
+
export function resolveCliModel(options: {
|
|
1699
|
+
cliProvider?: string;
|
|
1700
|
+
cliModel?: string;
|
|
1701
|
+
modelRegistry: CliModelRegistry;
|
|
1702
|
+
/** Authenticated models to prefer for unqualified selectors; defaults to the registry's authenticated set. */
|
|
1703
|
+
availableModels?: Model<Api>[];
|
|
1704
|
+
settings?: Settings;
|
|
1705
|
+
preferences?: ModelMatchPreferences;
|
|
1706
|
+
}): ResolveCliModelResult {
|
|
1707
|
+
const { cliProvider, cliModel, modelRegistry, settings, preferences, availableModels: preferredModels } = options;
|
|
1708
|
+
|
|
1709
|
+
if (!cliModel) {
|
|
1710
|
+
return { model: undefined, selector: undefined, warning: undefined, error: undefined };
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
const allModels = modelRegistry.getAll();
|
|
1714
|
+
if (allModels.length === 0) {
|
|
1715
|
+
return {
|
|
1716
|
+
model: undefined,
|
|
1717
|
+
selector: undefined,
|
|
1718
|
+
warning: undefined,
|
|
1719
|
+
error: "No models available. Check your installation or add models to models.json.",
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
const availableModels = preferredModels ?? modelRegistry.getAvailable();
|
|
1724
|
+
const providerMap = new Map<string, string>();
|
|
1725
|
+
for (const model of allModels) {
|
|
1726
|
+
providerMap.set(model.provider.toLowerCase(), model.provider);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
let provider = cliProvider ? providerMap.get(cliProvider.toLowerCase()) : undefined;
|
|
1730
|
+
if (cliProvider && !provider) {
|
|
1731
|
+
return {
|
|
1732
|
+
model: undefined,
|
|
1733
|
+
selector: undefined,
|
|
1734
|
+
warning: undefined,
|
|
1735
|
+
error: `Unknown provider "${cliProvider}". Run "omp models" to see available providers/models.`,
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
const trimmedModel = cliModel.trim();
|
|
1740
|
+
if (!provider) {
|
|
1741
|
+
const exact = findExactCliModel(trimmedModel, allModels, availableModels, { catalogFallback: false });
|
|
1742
|
+
if (exact) {
|
|
1743
|
+
return {
|
|
1744
|
+
model: exact,
|
|
1745
|
+
selector: formatModelString(exact),
|
|
1746
|
+
warning: undefined,
|
|
1747
|
+
thinkingLevel: undefined,
|
|
1748
|
+
error: undefined,
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
const { base: exactBase, level: exactThinkingLevel } = splitThinkingSuffix(
|
|
1752
|
+
trimmedModel,
|
|
1753
|
+
-1,
|
|
1754
|
+
MAX_THINKING_SUFFIX_OPTIONS,
|
|
1755
|
+
);
|
|
1756
|
+
if (exactThinkingLevel) {
|
|
1757
|
+
const exactSuffixed = findExactCliModel(exactBase, allModels, availableModels, { catalogFallback: false });
|
|
1758
|
+
if (exactSuffixed) {
|
|
1759
|
+
return {
|
|
1760
|
+
model: exactSuffixed,
|
|
1761
|
+
selector: formatModelString(exactSuffixed),
|
|
1762
|
+
warning: undefined,
|
|
1763
|
+
thinkingLevel: exactThinkingLevel,
|
|
1764
|
+
error: undefined,
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
let configuredPatterns: string[] | undefined;
|
|
1770
|
+
if (!cliProvider) {
|
|
1771
|
+
const { base: bareRoleName, level: bareRoleThinkingLevel } = splitThinkingSuffix(
|
|
1772
|
+
trimmedModel,
|
|
1773
|
+
-1,
|
|
1774
|
+
MAX_THINKING_SUFFIX_OPTIONS,
|
|
1775
|
+
);
|
|
1776
|
+
const roleSelector =
|
|
1777
|
+
modelRoleAliasPrefixLength(trimmedModel) !== undefined
|
|
1778
|
+
? trimmedModel
|
|
1779
|
+
: settings?.getModelRole(bareRoleName) !== undefined
|
|
1780
|
+
? `${formatModelRoleAlias(bareRoleName)}${bareRoleThinkingLevel ? `:${bareRoleThinkingLevel}` : ""}`
|
|
1781
|
+
: undefined;
|
|
1782
|
+
if (roleSelector) {
|
|
1783
|
+
const { base: roleAlias } = splitThinkingSuffix(
|
|
1784
|
+
roleSelector,
|
|
1785
|
+
modelRoleAliasPrefixLength(roleSelector) ?? -1,
|
|
1786
|
+
MAX_THINKING_SUFFIX_OPTIONS,
|
|
1787
|
+
);
|
|
1788
|
+
const configuredRole = getModelRoleAlias(roleAlias, settings);
|
|
1789
|
+
configuredPatterns = resolveConfiguredModelPatterns([roleSelector], settings);
|
|
1790
|
+
const availableResolved = resolveModelRoleValue(roleSelector, availableModels, {
|
|
1791
|
+
settings,
|
|
1792
|
+
matchPreferences: preferences,
|
|
1793
|
+
});
|
|
1794
|
+
const resolved = availableResolved.model
|
|
1795
|
+
? availableResolved
|
|
1796
|
+
: resolveModelRoleValue(roleSelector, allModels, {
|
|
1797
|
+
settings,
|
|
1798
|
+
matchPreferences: preferences,
|
|
1799
|
+
});
|
|
1800
|
+
if (resolved.model) {
|
|
1801
|
+
return {
|
|
1802
|
+
model: resolved.model,
|
|
1803
|
+
selector: formatModelString(resolved.model),
|
|
1804
|
+
configuredRole,
|
|
1805
|
+
configuredPatterns,
|
|
1806
|
+
configuredPatternIndex: resolved.matchedPatternIndex,
|
|
1807
|
+
thinkingLevel: resolved.thinkingLevel,
|
|
1808
|
+
warning: resolved.warning,
|
|
1809
|
+
error: undefined,
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
if (configuredPatterns && configuredPatterns.length > 0) {
|
|
1813
|
+
return {
|
|
1814
|
+
model: undefined,
|
|
1815
|
+
configuredPatterns,
|
|
1816
|
+
configuredRole,
|
|
1817
|
+
selector: undefined,
|
|
1818
|
+
thinkingLevel: undefined,
|
|
1819
|
+
warning: resolved.warning,
|
|
1820
|
+
error: `Model "${trimmedModel}" not found. Run "omp models" to see available models.`,
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
let pattern = trimmedModel;
|
|
1827
|
+
|
|
1828
|
+
if (!provider) {
|
|
1829
|
+
const slashIndex = cliModel.indexOf("/");
|
|
1830
|
+
if (slashIndex !== -1) {
|
|
1831
|
+
const maybeProvider = cliModel.substring(0, slashIndex);
|
|
1832
|
+
const canonical = providerMap.get(maybeProvider.toLowerCase());
|
|
1833
|
+
if (canonical) {
|
|
1834
|
+
provider = canonical;
|
|
1835
|
+
pattern = cliModel.substring(slashIndex + 1);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
} else {
|
|
1839
|
+
const prefix = `${provider}/`;
|
|
1840
|
+
if (cliModel.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
1841
|
+
pattern = cliModel.substring(prefix.length);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
if (provider) {
|
|
1846
|
+
const exactProviderMatch = resolveProviderModelReference(provider, pattern, allModels);
|
|
1847
|
+
if (exactProviderMatch) {
|
|
1848
|
+
return {
|
|
1849
|
+
model: exactProviderMatch,
|
|
1850
|
+
selector: formatModelString(exactProviderMatch),
|
|
1851
|
+
warning: undefined,
|
|
1852
|
+
thinkingLevel: undefined,
|
|
1853
|
+
error: undefined,
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
const candidates = provider ? allModels.filter(model => model.provider === provider) : availableModels;
|
|
1859
|
+
let parsed = parseModelPattern(pattern, candidates, preferences, {
|
|
1860
|
+
allowInvalidThinkingSelectorFallback: false,
|
|
1861
|
+
});
|
|
1862
|
+
if (!parsed.model && !provider) {
|
|
1863
|
+
parsed = parseModelPattern(pattern, allModels, preferences, {
|
|
1864
|
+
allowInvalidThinkingSelectorFallback: false,
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
const { model, thinkingLevel, warning, upstream } = parsed;
|
|
1868
|
+
|
|
1869
|
+
if (!model) {
|
|
1870
|
+
const display = provider ? `${provider}/${pattern}` : cliModel;
|
|
1871
|
+
return {
|
|
1872
|
+
model: undefined,
|
|
1873
|
+
configuredPatterns,
|
|
1874
|
+
selector: undefined,
|
|
1875
|
+
thinkingLevel: undefined,
|
|
1876
|
+
warning,
|
|
1877
|
+
error: `Model "${display}" not found. Run "omp models" to see available models.`,
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
let selector = provider ? formatModelString(model) : undefined;
|
|
1882
|
+
if (selector !== undefined && upstream) {
|
|
1883
|
+
selector = `${selector}@${upstream}`;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
return {
|
|
1887
|
+
model,
|
|
1888
|
+
selector,
|
|
1889
|
+
thinkingLevel,
|
|
1890
|
+
warning,
|
|
1891
|
+
error: undefined,
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
export interface InitialModelResult {
|
|
1896
|
+
model: Model<Api> | undefined;
|
|
1897
|
+
thinkingLevel?: ThinkingLevel;
|
|
1898
|
+
fallbackMessage: string | undefined;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/**
|
|
1902
|
+
* Find the initial model to use based on priority:
|
|
1903
|
+
* 1. CLI args (provider + model)
|
|
1904
|
+
* 2. First model from scoped models (if not continuing/resuming)
|
|
1905
|
+
* 3. Restored from session (if continuing/resuming)
|
|
1906
|
+
* 4. Saved default from settings
|
|
1907
|
+
* 5. First available model with valid API key
|
|
1908
|
+
*/
|
|
1909
|
+
export async function findInitialModel(options: {
|
|
1910
|
+
cliProvider?: string;
|
|
1911
|
+
cliModel?: string;
|
|
1912
|
+
scopedModels: ScopedModel[];
|
|
1913
|
+
isContinuing: boolean;
|
|
1914
|
+
defaultProvider?: string;
|
|
1915
|
+
defaultModelId?: string;
|
|
1916
|
+
defaultThinkingSelector?: Effort;
|
|
1917
|
+
modelRegistry: InitialModelRegistry;
|
|
1918
|
+
}): Promise<InitialModelResult> {
|
|
1919
|
+
const {
|
|
1920
|
+
cliProvider,
|
|
1921
|
+
cliModel,
|
|
1922
|
+
scopedModels,
|
|
1923
|
+
isContinuing,
|
|
1924
|
+
defaultProvider,
|
|
1925
|
+
defaultModelId,
|
|
1926
|
+
defaultThinkingSelector,
|
|
1927
|
+
modelRegistry,
|
|
1928
|
+
} = options;
|
|
1929
|
+
|
|
1930
|
+
let model: Model<Api> | undefined;
|
|
1931
|
+
let thinkingLevel: Effort | undefined;
|
|
1932
|
+
|
|
1933
|
+
// 1. CLI args take priority
|
|
1934
|
+
if (cliProvider && cliModel) {
|
|
1935
|
+
const found = modelRegistry.find(cliProvider, cliModel);
|
|
1936
|
+
if (!found) {
|
|
1937
|
+
console.error(chalk.red(`Model ${cliProvider}/${cliModel} not found`));
|
|
1938
|
+
process.exit(1);
|
|
1939
|
+
}
|
|
1940
|
+
return { model: found, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// 2. Use first model from scoped models (skip if continuing/resuming)
|
|
1944
|
+
if (scopedModels.length > 0 && !isContinuing) {
|
|
1945
|
+
const scoped = scopedModels[0];
|
|
1946
|
+
const scopedThinkingSelector =
|
|
1947
|
+
scoped.thinkingLevel === ThinkingLevel.Inherit
|
|
1948
|
+
? defaultThinkingSelector
|
|
1949
|
+
: (scoped.thinkingLevel ?? defaultThinkingSelector);
|
|
1950
|
+
return {
|
|
1951
|
+
model: scoped.model,
|
|
1952
|
+
thinkingLevel:
|
|
1953
|
+
scopedThinkingSelector === ThinkingLevel.Off
|
|
1954
|
+
? ThinkingLevel.Off
|
|
1955
|
+
: clampThinkingLevelForModel(scoped.model, scopedThinkingSelector),
|
|
1956
|
+
fallbackMessage: undefined,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// 3. Try saved default from settings
|
|
1961
|
+
if (defaultProvider && defaultModelId) {
|
|
1962
|
+
const found = modelRegistry.find(defaultProvider, defaultModelId);
|
|
1963
|
+
if (found) {
|
|
1964
|
+
model = found;
|
|
1965
|
+
thinkingLevel = clampThinkingLevelForModel(found, defaultThinkingSelector);
|
|
1966
|
+
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// 4. Try first available model with valid API key
|
|
1971
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1972
|
+
|
|
1973
|
+
const fallback = pickDefaultAvailableModel(availableModels);
|
|
1974
|
+
if (fallback) {
|
|
1975
|
+
return { model: fallback, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// 5. No model found
|
|
1979
|
+
return { model: undefined, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* Restore model from session, with fallback to available models
|
|
1984
|
+
*/
|
|
1985
|
+
export async function restoreModelFromSession(
|
|
1986
|
+
savedProvider: string,
|
|
1987
|
+
savedModelId: string,
|
|
1988
|
+
currentModel: Model<Api> | undefined,
|
|
1989
|
+
shouldPrintMessages: boolean,
|
|
1990
|
+
modelRegistry: RestorableModelRegistry,
|
|
1991
|
+
): Promise<{ model: Model<Api> | undefined; fallbackMessage: string | undefined }> {
|
|
1992
|
+
const restoredModel = modelRegistry.find(savedProvider, savedModelId);
|
|
1993
|
+
|
|
1994
|
+
// Check if restored model exists and has a valid API key
|
|
1995
|
+
const hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;
|
|
1996
|
+
|
|
1997
|
+
if (restoredModel && hasApiKey) {
|
|
1998
|
+
if (shouldPrintMessages) {
|
|
1999
|
+
console.log(chalk.dim(`Restored model: ${savedProvider}/${savedModelId}`));
|
|
2000
|
+
}
|
|
2001
|
+
return { model: restoredModel, fallbackMessage: undefined };
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// Model not found or no API key - fall back
|
|
2005
|
+
const reason = !restoredModel ? "model no longer exists" : "no API key available";
|
|
2006
|
+
|
|
2007
|
+
if (shouldPrintMessages) {
|
|
2008
|
+
console.error(chalk.yellow(`Warning: Could not restore model ${savedProvider}/${savedModelId} (${reason}).`));
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// If we already have a model, use it as fallback
|
|
2012
|
+
if (currentModel) {
|
|
2013
|
+
if (shouldPrintMessages) {
|
|
2014
|
+
console.log(chalk.dim(`Falling back to: ${currentModel.provider}/${currentModel.id}`));
|
|
2015
|
+
}
|
|
2016
|
+
return {
|
|
2017
|
+
model: currentModel,
|
|
2018
|
+
fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${currentModel.provider}/${currentModel.id}.`,
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// Try to find any available model
|
|
2023
|
+
const availableModels = modelRegistry.getAvailable();
|
|
2024
|
+
|
|
2025
|
+
const fallbackModel = pickDefaultAvailableModel(availableModels);
|
|
2026
|
+
if (fallbackModel) {
|
|
2027
|
+
if (shouldPrintMessages) {
|
|
2028
|
+
console.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`));
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
return {
|
|
2032
|
+
model: fallbackModel,
|
|
2033
|
+
fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${fallbackModel.provider}/${fallbackModel.id}.`,
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// No models available
|
|
2038
|
+
return { model: undefined, fallbackMessage: undefined };
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* Find a smol/fast model using the priority chain.
|
|
2043
|
+
* Tries exact matches first, then fuzzy matches.
|
|
2044
|
+
*
|
|
2045
|
+
* @param modelRegistry The model registry to search
|
|
2046
|
+
* @param savedModel Optional saved model string from settings (provider/modelId)
|
|
2047
|
+
* @returns The best available smol model, or undefined if none found
|
|
2048
|
+
*/
|
|
2049
|
+
export async function findSmolModel(
|
|
2050
|
+
modelRegistry: ModelLookupRegistry,
|
|
2051
|
+
savedModel?: string,
|
|
2052
|
+
): Promise<Model<Api> | undefined> {
|
|
2053
|
+
const availableModels = modelRegistry.getAvailable();
|
|
2054
|
+
if (availableModels.length === 0) return undefined;
|
|
2055
|
+
|
|
2056
|
+
// 1. Try saved model from settings
|
|
2057
|
+
if (savedModel) {
|
|
2058
|
+
const match = resolveModelFromString(savedModel, availableModels, undefined);
|
|
2059
|
+
if (match) return match;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// 2. Try priority chain
|
|
2063
|
+
for (const pattern of MODEL_PRIO.smol) {
|
|
2064
|
+
// Try exact match with provider prefix
|
|
2065
|
+
const providerMatch = availableModels.find(m => `${m.provider}/${m.id}`.toLowerCase() === pattern);
|
|
2066
|
+
if (providerMatch) return providerMatch;
|
|
2067
|
+
|
|
2068
|
+
// Try exact match first
|
|
2069
|
+
const exactMatch = parseModelPattern(pattern, availableModels, undefined).model;
|
|
2070
|
+
if (exactMatch) return exactMatch;
|
|
2071
|
+
|
|
2072
|
+
// Try fuzzy match (substring)
|
|
2073
|
+
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern));
|
|
2074
|
+
if (fuzzyMatch) return fuzzyMatch;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// 3. Fallback to first available (same as default)
|
|
2078
|
+
return availableModels[0];
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
/**
|
|
2082
|
+
* Find a slow/comprehensive model using the priority chain.
|
|
2083
|
+
* Prioritizes reasoning and codex models for thorough analysis.
|
|
2084
|
+
*
|
|
2085
|
+
* @param modelRegistry The model registry to search
|
|
2086
|
+
* @param savedModel Optional saved model string from settings (provider/modelId)
|
|
2087
|
+
* @returns The best available slow model, or undefined if none found
|
|
2088
|
+
*/
|
|
2089
|
+
export async function findSlowModel(
|
|
2090
|
+
modelRegistry: ModelLookupRegistry,
|
|
2091
|
+
savedModel?: string,
|
|
2092
|
+
): Promise<Model<Api> | undefined> {
|
|
2093
|
+
const availableModels = modelRegistry.getAvailable();
|
|
2094
|
+
if (availableModels.length === 0) return undefined;
|
|
2095
|
+
|
|
2096
|
+
// 1. Try saved model from settings
|
|
2097
|
+
if (savedModel) {
|
|
2098
|
+
const match = resolveModelFromString(savedModel, availableModels, undefined);
|
|
2099
|
+
if (match) return match;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// 2. Try priority chain
|
|
2103
|
+
for (const pattern of MODEL_PRIO.slow) {
|
|
2104
|
+
// Try exact match first
|
|
2105
|
+
const exactMatch = parseModelPattern(pattern, availableModels, undefined).model;
|
|
2106
|
+
if (exactMatch) return exactMatch;
|
|
2107
|
+
|
|
2108
|
+
// Try fuzzy match (substring)
|
|
2109
|
+
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase()));
|
|
2110
|
+
if (fuzzyMatch) return fuzzyMatch;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
// 3. Fallback to first available (same as default)
|
|
2114
|
+
return availableModels[0];
|
|
2115
|
+
}
|