@f5-sales-demo/xcsh 19.51.2
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 +7073 -0
- package/README.md +14 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +109 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/todo/index.ts +206 -0
- package/examples/extensions/README.md +143 -0
- package/examples/extensions/api-demo.ts +89 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +32 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/todo.ts +296 -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 +16 -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 +43 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +80 -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/README.md +172 -0
- package/package.json +539 -0
- package/scripts/build-info/resolvers.ts +51 -0
- package/scripts/capture-ax-fixture.ts +84 -0
- package/scripts/capture-login-fixture.ts +30 -0
- package/scripts/check-workflow-field-coverage.ts +101 -0
- package/scripts/extension-uat-harness.ts +340 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-api-spec-index.ts +580 -0
- package/scripts/generate-branding-index.ts +81 -0
- package/scripts/generate-build-info.ts +106 -0
- package/scripts/generate-console-catalog.ts +88 -0
- package/scripts/generate-console-field-metadata.ts +74 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +32 -0
- package/scripts/generate-terraform-index.ts +81 -0
- package/scripts/mermaid-gallery.ts +132 -0
- package/scripts/uat-matrix-modalities.ts +451 -0
- package/scripts/uat-matrix-report.ts +175 -0
- package/scripts/uat-matrix.ts +353 -0
- package/src/async/index.ts +2 -0
- package/src/async/job-manager.ts +416 -0
- package/src/async/support.ts +5 -0
- package/src/autoresearch/apply-contract-to-state.ts +1 -0
- package/src/autoresearch/command-resume.md +17 -0
- package/src/autoresearch/contract.ts +205 -0
- package/src/autoresearch/dashboard.ts +374 -0
- package/src/autoresearch/git.ts +191 -0
- package/src/autoresearch/helpers.ts +328 -0
- package/src/autoresearch/index.ts +402 -0
- package/src/autoresearch/prompt.md +236 -0
- package/src/autoresearch/resume-message.md +16 -0
- package/src/autoresearch/state.ts +303 -0
- package/src/autoresearch/tools/init-experiment.ts +400 -0
- package/src/autoresearch/tools/log-experiment.ts +793 -0
- package/src/autoresearch/tools/run-experiment.ts +679 -0
- package/src/autoresearch/types.ts +114 -0
- package/src/browser/acquire.ts +249 -0
- package/src/browser/actions.ts +126 -0
- package/src/browser/auth.ts +176 -0
- package/src/browser/ax.ts +134 -0
- package/src/browser/cdp-core.ts +45 -0
- package/src/browser/cdp-page-actions.ts +51 -0
- package/src/browser/chrome-locate.ts +66 -0
- package/src/browser/dom-context.ts +89 -0
- package/src/browser/dt-context.ts +100 -0
- package/src/browser/extension-bridge.ts +182 -0
- package/src/browser/extension-page-actions.ts +397 -0
- package/src/browser/extension-provider.ts +373 -0
- package/src/browser/index.ts +17 -0
- package/src/browser/input-commit.ts +74 -0
- package/src/browser/native-messaging.ts +20 -0
- package/src/browser/page-actions.ts +18 -0
- package/src/browser/provider.ts +155 -0
- package/src/browser/resolver.ts +126 -0
- package/src/browser/selector.ts +40 -0
- package/src/bun-imports.d.ts +22 -0
- package/src/capability/context-file.ts +43 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +107 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +438 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +74 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +224 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +50 -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 +168 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +286 -0
- package/src/cli/chrome-cli.ts +88 -0
- package/src/cli/classify-install-target.ts +50 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/jupyter-cli.ts +106 -0
- package/src/cli/list-models.ts +128 -0
- package/src/cli/plugin-cli.ts +964 -0
- package/src/cli/read-cli.ts +68 -0
- package/src/cli/session-picker.ts +52 -0
- package/src/cli/setup-cli.ts +479 -0
- package/src/cli/shell-cli.ts +174 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/stats-cli.ts +182 -0
- package/src/cli/update-cli.ts +446 -0
- package/src/cli/web-search-cli.ts +203 -0
- package/src/cli.ts +123 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/chrome-host.ts +71 -0
- package/src/commands/chrome.ts +28 -0
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +20 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/launch.ts +144 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +33 -0
- package/src/commands/setup.ts +42 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/web-search.ts +46 -0
- package/src/commit/agentic/agent.ts +314 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +354 -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 +127 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +81 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +139 -0
- package/src/commit/agentic/tools/propose-commit.ts +122 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +251 -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 +122 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +105 -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 +108 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +66 -0
- package/src/commit/pipeline.ts +242 -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/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +44 -0
- package/src/config/auto-config.ts +698 -0
- package/src/config/context-schema.json +115 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +493 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +2265 -0
- package/src/config/model-resolver.ts +1289 -0
- package/src/config/prompt-templates.ts +271 -0
- package/src/config/resolve-config-value.ts +110 -0
- package/src/config/settings-schema.ts +2155 -0
- package/src/config/settings.ts +730 -0
- package/src/config.ts +418 -0
- package/src/cursor.ts +323 -0
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/index.ts +440 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +158 -0
- package/src/debug/report-bundle.ts +365 -0
- package/src/debug/system-info.ts +107 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +219 -0
- package/src/discovery/builtin.ts +851 -0
- package/src/discovery/claude-plugins.ts +297 -0
- package/src/discovery/claude.ts +543 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +535 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +396 -0
- package/src/discovery/github.ts +118 -0
- package/src/discovery/helpers.ts +934 -0
- package/src/discovery/index.ts +79 -0
- package/src/discovery/language.ts +46 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/opencode.ts +393 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +29 -0
- package/src/discovery/vscode.ts +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +481 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +762 -0
- package/src/edit/modes/hashline.ts +1366 -0
- package/src/edit/modes/patch.ts +1791 -0
- package/src/edit/modes/replace.ts +1115 -0
- package/src/edit/normalize.ts +375 -0
- package/src/edit/renderer.ts +627 -0
- package/src/exa/factory.ts +61 -0
- package/src/exa/index.ts +27 -0
- package/src/exa/mcp-client.ts +293 -0
- package/src/exa/render.ts +232 -0
- package/src/exa/researcher.ts +47 -0
- package/src/exa/search.ts +86 -0
- package/src/exa/types.ts +166 -0
- package/src/exa/websets.ts +247 -0
- package/src/exec/bash-executor.ts +332 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +161 -0
- package/src/export/html/template.css +1001 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +1950 -0
- package/src/export/html/template.macro.ts +24 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/ttsr.ts +434 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +25 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +456 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +236 -0
- package/src/extensibility/custom-commands/types.ts +111 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +238 -0
- package/src/extensibility/custom-tools/types.ts +251 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -0
- package/src/extensibility/extensions/index.ts +15 -0
- package/src/extensibility/extensions/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +848 -0
- package/src/extensibility/extensions/types.ts +1394 -0
- package/src/extensibility/extensions/wrapper.ts +191 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +255 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +108 -0
- package/src/extensibility/hooks/types.ts +826 -0
- package/src/extensibility/plugins/doctor.ts +66 -0
- package/src/extensibility/plugins/git-url.ts +281 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/loader.ts +267 -0
- package/src/extensibility/plugins/manager.ts +731 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +318 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +781 -0
- package/src/extensibility/plugins/marketplace/prerequisites.ts +255 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +201 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/skills.ts +269 -0
- package/src/extensibility/slash-commands.ts +246 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +61 -0
- package/src/internal-urls/agent-protocol.ts +136 -0
- package/src/internal-urls/api-catalog-resolve.ts +495 -0
- package/src/internal-urls/api-catalog-types.ts +94 -0
- package/src/internal-urls/api-spec-resolve.ts +1036 -0
- package/src/internal-urls/api-spec-types.ts +239 -0
- package/src/internal-urls/artifact-protocol.ts +97 -0
- package/src/internal-urls/branding-index.generated.ts +88 -0
- package/src/internal-urls/build-info-runtime.ts +302 -0
- package/src/internal-urls/build-info.generated.ts +33 -0
- package/src/internal-urls/computer-profile.ts +692 -0
- package/src/internal-urls/console-catalog-types.ts +21 -0
- package/src/internal-urls/console-catalog.generated.ts +532 -0
- package/src/internal-urls/console-field-metadata-types.ts +59 -0
- package/src/internal-urls/console-field-metadata.generated.ts +4841 -0
- package/src/internal-urls/console-resolve.ts +178 -0
- package/src/internal-urls/docs-index.generated.ts +698 -0
- package/src/internal-urls/extension-api.md +147 -0
- package/src/internal-urls/index.ts +46 -0
- package/src/internal-urls/jobs-protocol.ts +119 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +206 -0
- package/src/internal-urls/mcp-protocol.ts +156 -0
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/profile-collectors.ts +106 -0
- package/src/internal-urls/router.ts +55 -0
- package/src/internal-urls/rule-protocol.ts +55 -0
- package/src/internal-urls/skill-protocol.ts +111 -0
- package/src/internal-urls/terraform-index.generated.ts +1852 -0
- package/src/internal-urls/terraform-resolve.ts +184 -0
- package/src/internal-urls/terraform-types.ts +45 -0
- package/src/internal-urls/types.ts +52 -0
- package/src/internal-urls/user-profile.ts +363 -0
- package/src/internal-urls/xcsh-protocol.ts +644 -0
- package/src/ipy/cancellation.ts +28 -0
- package/src/ipy/executor.ts +1155 -0
- package/src/ipy/gateway-coordinator.ts +424 -0
- package/src/ipy/kernel.ts +1091 -0
- package/src/ipy/modules.ts +144 -0
- package/src/ipy/prelude.py +849 -0
- package/src/ipy/prelude.ts +3 -0
- package/src/ipy/runtime.ts +221 -0
- package/src/locales/ar.json +512 -0
- package/src/locales/de.json +512 -0
- package/src/locales/en.json +549 -0
- package/src/locales/es.json +512 -0
- package/src/locales/fr.json +512 -0
- package/src/locales/hi.json +512 -0
- package/src/locales/index.ts +29 -0
- package/src/locales/it.json +512 -0
- package/src/locales/ja.json +512 -0
- package/src/locales/ko.json +512 -0
- package/src/locales/pt-br.json +512 -0
- package/src/locales/th.json +512 -0
- package/src/locales/zh-cn.json +512 -0
- package/src/locales/zh-tw.json +512 -0
- package/src/lsp/client.ts +941 -0
- package/src/lsp/clients/biome-client.ts +202 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +93 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +418 -0
- package/src/lsp/defaults.json +999 -0
- package/src/lsp/edits.ts +109 -0
- package/src/lsp/index.ts +1746 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +692 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +444 -0
- package/src/lsp/utils.ts +691 -0
- package/src/main.ts +944 -0
- package/src/mcp/client.ts +482 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/discoverable-tool-metadata.ts +202 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +84 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1152 -0
- package/src/mcp/oauth-discovery.ts +349 -0
- package/src/mcp/oauth-flow.ts +387 -0
- package/src/mcp/render.ts +157 -0
- package/src/mcp/smithery-auth.ts +104 -0
- package/src/mcp/smithery-connect.ts +145 -0
- package/src/mcp/smithery-registry.ts +477 -0
- package/src/mcp/tool-bridge.ts +417 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +475 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +325 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1115 -0
- package/src/memories/storage.ts +577 -0
- package/src/modes/acp/acp-agent.ts +1361 -0
- package/src/modes/acp/acp-event-mapper.ts +538 -0
- package/src/modes/acp/acp-mode.ts +15 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/components/agent-dashboard.ts +1124 -0
- package/src/modes/components/assistant-message.ts +213 -0
- package/src/modes/components/bash-execution.ts +345 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/btw-panel.ts +103 -0
- package/src/modes/components/compaction-summary-message.ts +51 -0
- package/src/modes/components/context-add-wizard.ts +534 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +238 -0
- package/src/modes/components/custom-message.ts +91 -0
- package/src/modes/components/diff.ts +210 -0
- package/src/modes/components/dynamic-border.ts +25 -0
- package/src/modes/components/extensions/extension-dashboard.ts +326 -0
- package/src/modes/components/extensions/extension-list.ts +492 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +317 -0
- package/src/modes/components/extensions/state-manager.ts +587 -0
- package/src/modes/components/extensions/types.ts +191 -0
- package/src/modes/components/footer.ts +262 -0
- package/src/modes/components/gutter-block.ts +315 -0
- package/src/modes/components/history-search.ts +158 -0
- package/src/modes/components/hook-editor.ts +151 -0
- package/src/modes/components/hook-input.ts +81 -0
- package/src/modes/components/hook-message.ts +100 -0
- package/src/modes/components/hook-selector.ts +192 -0
- package/src/modes/components/index.ts +36 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1314 -0
- package/src/modes/components/model-selector.ts +946 -0
- package/src/modes/components/oauth-selector.ts +211 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +477 -0
- package/src/modes/components/plugins/index.ts +5 -0
- package/src/modes/components/plugins/plugin-dashboard.ts +523 -0
- package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
- package/src/modes/components/plugins/plugin-list-pane.ts +109 -0
- package/src/modes/components/plugins/state-manager.ts +229 -0
- package/src/modes/components/plugins/types.ts +51 -0
- package/src/modes/components/plugins/utils.ts +6 -0
- package/src/modes/components/python-execution.ts +244 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +137 -0
- package/src/modes/components/session-observer-overlay.ts +483 -0
- package/src/modes/components/session-selector.ts +343 -0
- package/src/modes/components/settings-defs.ts +568 -0
- package/src/modes/components/settings-selector.ts +641 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +90 -0
- package/src/modes/components/status-line/context-gradient.ts +64 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/hex-ansi.ts +29 -0
- package/src/modes/components/status-line/index.ts +4 -0
- package/src/modes/components/status-line/presets.ts +174 -0
- package/src/modes/components/status-line/segments.ts +494 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/token-rate.ts +66 -0
- package/src/modes/components/status-line/types.ts +96 -0
- package/src/modes/components/status-line-segment-editor.ts +361 -0
- package/src/modes/components/status-line.ts +729 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/todo-reminder.ts +54 -0
- package/src/modes/components/tool-execution.ts +781 -0
- package/src/modes/components/tree-selector.ts +908 -0
- package/src/modes/components/ttsr-notification.ts +80 -0
- package/src/modes/components/user-message-selector.ts +141 -0
- package/src/modes/components/user-message.ts +100 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome-checks.ts +313 -0
- package/src/modes/components/welcome.ts +270 -0
- package/src/modes/controllers/btw-controller.ts +193 -0
- package/src/modes/controllers/chord-routing.ts +37 -0
- package/src/modes/controllers/command-controller.ts +1293 -0
- package/src/modes/controllers/context-command-controller.ts +61 -0
- package/src/modes/controllers/event-controller.ts +743 -0
- package/src/modes/controllers/extension-ui-controller.ts +983 -0
- package/src/modes/controllers/input-controller.ts +877 -0
- package/src/modes/controllers/mcp-command-controller.ts +1940 -0
- package/src/modes/controllers/selector-controller.ts +1292 -0
- package/src/modes/controllers/ssh-command-controller.ts +421 -0
- package/src/modes/index.ts +34 -0
- package/src/modes/interactive-mode.ts +1781 -0
- package/src/modes/oauth-manual-input.ts +42 -0
- package/src/modes/print-mode.ts +196 -0
- package/src/modes/prompt-action-autocomplete.ts +231 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +762 -0
- package/src/modes/rpc/rpc-mode.ts +877 -0
- package/src/modes/rpc/rpc-types.ts +322 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +30 -0
- package/src/modes/theme/defaults/index.ts +7 -0
- package/src/modes/theme/defaults/xcsh-dark.json +113 -0
- package/src/modes/theme/defaults/xcsh-light.json +115 -0
- package/src/modes/theme/mermaid-cache.ts +151 -0
- package/src/modes/theme/mermaid-palette.ts +39 -0
- package/src/modes/theme/theme-schema.json +553 -0
- package/src/modes/theme/theme.ts +2563 -0
- package/src/modes/types.ts +273 -0
- package/src/modes/utils/hotkeys-markdown.ts +60 -0
- package/src/modes/utils/keybinding-matchers.ts +30 -0
- package/src/modes/utils/read-group-outcome-aggregator.ts +55 -0
- package/src/modes/utils/sanitize-error-message.ts +60 -0
- package/src/modes/utils/tools-markdown.ts +28 -0
- package/src/modes/utils/ui-helpers.ts +734 -0
- package/src/plan-mode/approved-plan.ts +55 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +37 -0
- package/src/prompts/agents/designer.md +67 -0
- package/src/prompts/agents/explore.md +61 -0
- package/src/prompts/agents/frontmatter.md +10 -0
- package/src/prompts/agents/init.md +36 -0
- package/src/prompts/agents/librarian.md +121 -0
- package/src/prompts/agents/plan.md +49 -0
- package/src/prompts/agents/reviewer.md +127 -0
- package/src/prompts/agents/task.md +16 -0
- package/src/prompts/ci-green-request.md +35 -0
- package/src/prompts/compaction/branch-summary-context.md +5 -0
- package/src/prompts/compaction/branch-summary-preamble.md +2 -0
- package/src/prompts/compaction/branch-summary.md +30 -0
- package/src/prompts/compaction/compaction-short-summary.md +9 -0
- package/src/prompts/compaction/compaction-summary-context.md +5 -0
- package/src/prompts/compaction/compaction-summary.md +38 -0
- package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
- package/src/prompts/compaction/compaction-update-summary.md +45 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/read-path.md +11 -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-request.md +70 -0
- package/src/prompts/system/agent-creation-architect.md +65 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +2 -0
- package/src/prompts/system/custom-system-prompt.md +82 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/file-operations.md +10 -0
- package/src/prompts/system/handoff-document.md +48 -0
- package/src/prompts/system/plan-mode-active.md +114 -0
- package/src/prompts/system/plan-mode-approved.md +23 -0
- package/src/prompts/system/plan-mode-reference.md +14 -0
- package/src/prompts/system/plan-mode-subagent.md +34 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/subagent-submit-reminder.md +7 -0
- package/src/prompts/system/subagent-system-prompt.md +36 -0
- package/src/prompts/system/subagent-user-prompt.md +12 -0
- package/src/prompts/system/summarization-system.md +3 -0
- package/src/prompts/system/system-prompt.md +698 -0
- package/src/prompts/system/title-system.md +2 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/web-search.md +28 -0
- package/src/prompts/tools/ask.md +28 -0
- package/src/prompts/tools/ast-edit.md +48 -0
- package/src/prompts/tools/ast-grep.md +54 -0
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +67 -0
- package/src/prompts/tools/browser.md +26 -0
- package/src/prompts/tools/calculator.md +10 -0
- package/src/prompts/tools/cancel-job.md +5 -0
- package/src/prompts/tools/catalog-workflow-runner.md +13 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/chunk-edit.md +279 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/display-image.md +23 -0
- package/src/prompts/tools/exit-plan-mode.md +41 -0
- package/src/prompts/tools/find.md +26 -0
- package/src/prompts/tools/gemini-image.md +7 -0
- package/src/prompts/tools/grep.md +29 -0
- package/src/prompts/tools/hashline.md +131 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +32 -0
- package/src/prompts/tools/lsp.md +33 -0
- package/src/prompts/tools/patch.md +77 -0
- package/src/prompts/tools/poll.md +5 -0
- package/src/prompts/tools/python.md +60 -0
- package/src/prompts/tools/read-chunk.md +35 -0
- package/src/prompts/tools/read.md +63 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +36 -0
- package/src/prompts/tools/resolve.md +8 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +34 -0
- package/src/prompts/tools/ssh.md +44 -0
- package/src/prompts/tools/task-summary.md +28 -0
- package/src/prompts/tools/task.md +131 -0
- package/src/prompts/tools/todo-write.md +89 -0
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/prompts/tools/xcsh-api.md +283 -0
- package/src/resource-management/index.ts +42 -0
- package/src/sdk.ts +2123 -0
- package/src/secrets/index.ts +146 -0
- package/src/secrets/obfuscator.ts +290 -0
- package/src/secrets/regex.ts +21 -0
- package/src/services/context-env.ts +102 -0
- package/src/services/xcsh-api-client.ts +230 -0
- package/src/services/xcsh-context-command.ts +803 -0
- package/src/services/xcsh-context-display.ts +49 -0
- package/src/services/xcsh-context-indicators.ts +17 -0
- package/src/services/xcsh-context-segment.ts +39 -0
- package/src/services/xcsh-context.ts +1410 -0
- package/src/services/xcsh-env.ts +107 -0
- package/src/services/xcsh-knowledge.ts +177 -0
- package/src/services/xcsh-table.ts +164 -0
- package/src/session/agent-session.ts +6690 -0
- package/src/session/agent-storage.ts +451 -0
- package/src/session/artifacts.ts +132 -0
- package/src/session/auth-storage.ts +17 -0
- package/src/session/blob-store.ts +135 -0
- package/src/session/compaction/branch-summarization.ts +322 -0
- package/src/session/compaction/compaction.ts +1388 -0
- package/src/session/compaction/index.ts +7 -0
- package/src/session/compaction/pruning.ts +91 -0
- package/src/session/compaction/utils.ts +184 -0
- package/src/session/history-storage.ts +253 -0
- package/src/session/messages.ts +597 -0
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +2816 -0
- package/src/session/session-storage.ts +370 -0
- package/src/session/streaming-output.ts +770 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +1614 -0
- package/src/slash-commands/export-command.ts +178 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/resource-commands.ts +250 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +445 -0
- package/src/ssh/ssh-executor.ts +131 -0
- package/src/ssh/sshfs-mount.ts +137 -0
- package/src/ssh/utils.ts +8 -0
- package/src/stt/downloader.ts +71 -0
- package/src/stt/index.ts +3 -0
- package/src/stt/recorder.ts +351 -0
- package/src/stt/setup.ts +52 -0
- package/src/stt/stt-controller.ts +160 -0
- package/src/stt/transcribe.py +70 -0
- package/src/stt/transcriber.ts +91 -0
- package/src/system-prompt.ts +688 -0
- package/src/task/agents.ts +166 -0
- package/src/task/commands.ts +131 -0
- package/src/task/discovery.ts +126 -0
- package/src/task/executor.ts +1324 -0
- package/src/task/index.ts +1197 -0
- package/src/task/isolation-backend.ts +72 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/output-manager.ts +107 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1035 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/template.ts +33 -0
- package/src/task/types.ts +230 -0
- package/src/task/worktree.ts +580 -0
- package/src/task/xcsh-command.ts +26 -0
- package/src/thinking.ts +87 -0
- package/src/tools/action-renderer.ts +198 -0
- package/src/tools/archive-reader.ts +315 -0
- package/src/tools/ask.ts +791 -0
- package/src/tools/ast-edit.ts +492 -0
- package/src/tools/ast-grep.ts +453 -0
- package/src/tools/auto-generated-guard.ts +305 -0
- package/src/tools/bash-interactive.ts +383 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-normalize.ts +107 -0
- package/src/tools/bash-skill-urls.ts +246 -0
- package/src/tools/bash.ts +878 -0
- package/src/tools/browser-renderer.ts +129 -0
- package/src/tools/browser.ts +1366 -0
- package/src/tools/calculator.ts +543 -0
- package/src/tools/cancel-job.ts +100 -0
- package/src/tools/catalog-workflow-runner.ts +718 -0
- package/src/tools/checkpoint.ts +133 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1004 -0
- package/src/tools/display-image-renderer.ts +96 -0
- package/src/tools/display-image.ts +109 -0
- package/src/tools/exit-plan-mode.ts +101 -0
- package/src/tools/fetch.ts +1462 -0
- package/src/tools/find.ts +437 -0
- package/src/tools/fs-cache-invalidation.ts +33 -0
- package/src/tools/gemini-image.ts +1004 -0
- package/src/tools/grep.ts +705 -0
- package/src/tools/index.ts +442 -0
- package/src/tools/inspect-image-renderer.ts +101 -0
- package/src/tools/inspect-image.ts +176 -0
- package/src/tools/json-tree.ts +266 -0
- package/src/tools/jtd-to-json-schema.ts +199 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/mermaid-renderer.ts +127 -0
- package/src/tools/notebook.ts +288 -0
- package/src/tools/output-meta.ts +588 -0
- package/src/tools/path-utils.ts +560 -0
- package/src/tools/plan-mode-guard.ts +41 -0
- package/src/tools/poll-tool.ts +177 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
- package/src/tools/python.ts +1184 -0
- package/src/tools/read.ts +1591 -0
- package/src/tools/render-mermaid.ts +93 -0
- package/src/tools/render-utils.ts +677 -0
- package/src/tools/renderers.ts +82 -0
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +196 -0
- package/src/tools/review.ts +238 -0
- package/src/tools/search-tool-bm25.ts +288 -0
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/ssh.ts +322 -0
- package/src/tools/submit-result.ts +229 -0
- package/src/tools/todo-render.ts +33 -0
- package/src/tools/todo-write.ts +440 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +93 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/vim.ts +981 -0
- package/src/tools/write.ts +671 -0
- package/src/tools/xcsh-api-renderer.ts +402 -0
- package/src/tools/xcsh-api.ts +887 -0
- package/src/tui/code-cell.ts +101 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +175 -0
- package/src/tui/status-line.ts +39 -0
- package/src/tui/tree-list.ts +84 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/utils/changelog.ts +98 -0
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +137 -0
- package/src/utils/edit-mode.ts +50 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +37 -0
- package/src/utils/file-mentions.ts +376 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/git.ts +1495 -0
- package/src/utils/gitstatus.ts +140 -0
- package/src/utils/image-convert.ts +27 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/image-passthrough.ts +118 -0
- package/src/utils/image-resize.ts +272 -0
- package/src/utils/image-viewer.ts +17 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +81 -0
- package/src/utils/open.ts +20 -0
- package/src/utils/session-color.ts +55 -0
- package/src/utils/shell-snapshot.ts +188 -0
- package/src/utils/title-generator.ts +193 -0
- package/src/utils/tool-choice.ts +28 -0
- package/src/utils/tools-manager.ts +356 -0
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- package/src/vim/types.ts +197 -0
- package/src/web/kagi.ts +174 -0
- package/src/web/parallel.ts +346 -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 +110 -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 +653 -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 +452 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +140 -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 +169 -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 +120 -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 +190 -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 +93 -0
- package/src/web/scrapers/types.ts +267 -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 +313 -0
- package/src/web/search/errors.ts +120 -0
- package/src/web/search/index.ts +346 -0
- package/src/web/search/params.ts +129 -0
- package/src/web/search/provider.ts +97 -0
- package/src/web/search/providers/anthropic.ts +339 -0
- package/src/web/search/providers/base.ts +35 -0
- package/src/web/search/providers/brave.ts +150 -0
- package/src/web/search/providers/codex.ts +402 -0
- package/src/web/search/providers/exa.ts +264 -0
- package/src/web/search/providers/firecrawl.ts +115 -0
- package/src/web/search/providers/gemini.ts +566 -0
- package/src/web/search/providers/jina.ts +99 -0
- package/src/web/search/providers/kagi.ts +70 -0
- package/src/web/search/providers/kimi.ts +157 -0
- package/src/web/search/providers/parallel.ts +63 -0
- package/src/web/search/providers/perplexity.ts +546 -0
- package/src/web/search/providers/synthetic.ts +113 -0
- package/src/web/search/providers/tavily.ts +162 -0
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +311 -0
- package/src/web/search/render.ts +348 -0
- package/src/web/search/types.ts +438 -0
- package/src/web/search/utils.ts +17 -0
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model resolution, scoping, and initial selection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ThinkingLevel } from "@f5xc-salesdemos/pi-agent-core";
|
|
6
|
+
import {
|
|
7
|
+
type Api,
|
|
8
|
+
clampThinkingLevelForModel,
|
|
9
|
+
DEFAULT_MODEL_PER_PROVIDER,
|
|
10
|
+
type Effort,
|
|
11
|
+
type KnownProvider,
|
|
12
|
+
type Model,
|
|
13
|
+
modelsAreEqual,
|
|
14
|
+
} from "@f5xc-salesdemos/pi-ai";
|
|
15
|
+
import chalk from "chalk";
|
|
16
|
+
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
17
|
+
import { parseThinkingLevel, resolveThinkingLevelForModel } from "../thinking";
|
|
18
|
+
import { fuzzyMatch } from "../utils/fuzzy";
|
|
19
|
+
import { MODEL_ROLE_IDS, type ModelRegistry, type ModelRole } from "./model-registry";
|
|
20
|
+
import type { Settings } from "./settings";
|
|
21
|
+
|
|
22
|
+
/** Default model IDs for each known provider */
|
|
23
|
+
export const defaultModelPerProvider: Record<KnownProvider, string> = DEFAULT_MODEL_PER_PROVIDER;
|
|
24
|
+
|
|
25
|
+
export interface ScopedModel {
|
|
26
|
+
model: Model<Api>;
|
|
27
|
+
thinkingLevel?: ThinkingLevel;
|
|
28
|
+
explicitThinkingLevel: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse a model string in "provider/modelId" format.
|
|
33
|
+
* Returns undefined if the format is invalid.
|
|
34
|
+
*/
|
|
35
|
+
export function parseModelString(
|
|
36
|
+
modelStr: string,
|
|
37
|
+
): { provider: string; id: string; thinkingLevel?: ThinkingLevel } | undefined {
|
|
38
|
+
const slashIdx = modelStr.indexOf("/");
|
|
39
|
+
if (slashIdx <= 0) return undefined;
|
|
40
|
+
const id = modelStr.slice(slashIdx + 1);
|
|
41
|
+
const provider = modelStr.slice(0, slashIdx);
|
|
42
|
+
// Strip valid thinking level suffix (e.g., "claude-sonnet-4-6:high" -> id "claude-sonnet-4-6", thinkingLevel "high")
|
|
43
|
+
const colonIdx = id.lastIndexOf(":");
|
|
44
|
+
if (colonIdx !== -1) {
|
|
45
|
+
const suffix = id.slice(colonIdx + 1);
|
|
46
|
+
const thinkingLevel = parseThinkingLevel(suffix);
|
|
47
|
+
if (thinkingLevel) {
|
|
48
|
+
return { provider, id: id.slice(0, colonIdx), thinkingLevel };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { provider, id };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format a model as "provider/modelId" string.
|
|
56
|
+
*/
|
|
57
|
+
export function formatModelString(model: Model<Api>): string {
|
|
58
|
+
return `${model.provider}/${model.id}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatModelSelectorValue(selector: string, thinkingLevel: ThinkingLevel | undefined): string {
|
|
62
|
+
return thinkingLevel && thinkingLevel !== ThinkingLevel.Inherit ? `${selector}:${thinkingLevel}` : selector;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getOpenRouterRouteSuffix(modelId: string): { baseId: string; suffix: string } | undefined {
|
|
66
|
+
const colonIdx = modelId.lastIndexOf(":");
|
|
67
|
+
if (colonIdx === -1) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const suffix = modelId.slice(colonIdx + 1).trim();
|
|
72
|
+
if (!suffix || parseThinkingLevel(suffix)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { baseId: modelId.slice(0, colonIdx), suffix };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stripOpenRouterDateSuffix(modelId: string): string | undefined {
|
|
80
|
+
const stripped = modelId.replace(/-\d{8}(?=$|:)/i, "");
|
|
81
|
+
return stripped !== modelId ? stripped : undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getOpenRouterFallbackModelIds(modelId: string): string[] {
|
|
85
|
+
const orderedCandidates: string[] = [];
|
|
86
|
+
const queue = [modelId];
|
|
87
|
+
const seen = new Set<string>();
|
|
88
|
+
|
|
89
|
+
while (queue.length > 0) {
|
|
90
|
+
const candidate = queue.shift();
|
|
91
|
+
if (!candidate || seen.has(candidate)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
seen.add(candidate);
|
|
95
|
+
orderedCandidates.push(candidate);
|
|
96
|
+
|
|
97
|
+
const routedSuffix = getOpenRouterRouteSuffix(candidate);
|
|
98
|
+
if (routedSuffix) {
|
|
99
|
+
queue.push(routedSuffix.baseId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const strippedDate = stripOpenRouterDateSuffix(candidate);
|
|
103
|
+
if (strippedDate) {
|
|
104
|
+
queue.push(strippedDate);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return orderedCandidates;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function cloneModelWithRequestedId(model: Model<Api>, requestedId: string): Model<Api> {
|
|
112
|
+
return {
|
|
113
|
+
...model,
|
|
114
|
+
id: requestedId,
|
|
115
|
+
...(model.name === model.id ? { name: requestedId } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function resolveProviderModelReference(
|
|
120
|
+
provider: string,
|
|
121
|
+
modelId: string,
|
|
122
|
+
availableModels: readonly Model<Api>[],
|
|
123
|
+
): Model<Api> | undefined {
|
|
124
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
125
|
+
const normalizedModelId = modelId.trim().toLowerCase();
|
|
126
|
+
if (!normalizedProvider || !normalizedModelId) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const exactMatches = availableModels.filter(
|
|
131
|
+
model => model.provider.toLowerCase() === normalizedProvider && model.id.toLowerCase() === normalizedModelId,
|
|
132
|
+
);
|
|
133
|
+
if (exactMatches.length === 1) {
|
|
134
|
+
return exactMatches[0];
|
|
135
|
+
}
|
|
136
|
+
if (exactMatches.length > 1) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (normalizedProvider !== "openrouter") {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const fallbackId of getOpenRouterFallbackModelIds(modelId).slice(1)) {
|
|
145
|
+
const baseMatches = availableModels.filter(
|
|
146
|
+
model =>
|
|
147
|
+
model.provider.toLowerCase() === normalizedProvider && model.id.toLowerCase() === fallbackId.toLowerCase(),
|
|
148
|
+
);
|
|
149
|
+
if (baseMatches.length === 1) {
|
|
150
|
+
return cloneModelWithRequestedId(baseMatches[0], modelId);
|
|
151
|
+
}
|
|
152
|
+
if (baseMatches.length > 1) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ModelMatchPreferences {
|
|
161
|
+
/** Most-recently-used model keys (provider/modelId) to prefer when ambiguous. */
|
|
162
|
+
usageOrder?: string[];
|
|
163
|
+
/** Providers to deprioritize when no recent usage is available. */
|
|
164
|
+
deprioritizeProviders?: string[];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export type CanonicalModelRegistry = Partial<
|
|
168
|
+
Pick<ModelRegistry, "resolveCanonicalModel" | "getCanonicalVariants" | "getCanonicalId">
|
|
169
|
+
>;
|
|
170
|
+
export type ModelLookupRegistry = Pick<ModelRegistry, "getAvailable"> & Partial<CanonicalModelRegistry>;
|
|
171
|
+
type CliModelRegistry = Pick<ModelRegistry, "getAll"> & Partial<CanonicalModelRegistry>;
|
|
172
|
+
type InitialModelRegistry = Pick<ModelRegistry, "getAvailable" | "find">;
|
|
173
|
+
type RestorableModelRegistry = Pick<ModelRegistry, "getAvailable" | "find" | "getApiKey">;
|
|
174
|
+
|
|
175
|
+
interface ModelPreferenceContext {
|
|
176
|
+
modelUsageRank: Map<string, number>;
|
|
177
|
+
providerUsageRank: Map<string, number>;
|
|
178
|
+
deprioritizedProviders: Set<string>;
|
|
179
|
+
modelOrder: Map<string, number>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function buildPreferenceContext(
|
|
183
|
+
availableModels: Model<Api>[],
|
|
184
|
+
preferences: ModelMatchPreferences | undefined,
|
|
185
|
+
): ModelPreferenceContext {
|
|
186
|
+
const modelUsageRank = new Map<string, number>();
|
|
187
|
+
const providerUsageRank = new Map<string, number>();
|
|
188
|
+
const usageOrder = preferences?.usageOrder ?? [];
|
|
189
|
+
for (let i = 0; i < usageOrder.length; i += 1) {
|
|
190
|
+
const key = usageOrder[i];
|
|
191
|
+
if (!modelUsageRank.has(key)) {
|
|
192
|
+
modelUsageRank.set(key, i);
|
|
193
|
+
}
|
|
194
|
+
const parsed = parseModelString(key);
|
|
195
|
+
if (parsed && !providerUsageRank.has(parsed.provider)) {
|
|
196
|
+
providerUsageRank.set(parsed.provider, i);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const deprioritizedProviders = new Set(preferences?.deprioritizeProviders ?? ["openrouter"]);
|
|
201
|
+
const modelOrder = new Map<string, number>();
|
|
202
|
+
for (let i = 0; i < availableModels.length; i += 1) {
|
|
203
|
+
modelOrder.set(formatModelString(availableModels[i]), i);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { modelUsageRank, providerUsageRank, deprioritizedProviders, modelOrder };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function pickPreferredModel(candidates: Model<Api>[], context: ModelPreferenceContext): Model<Api> {
|
|
210
|
+
if (candidates.length <= 1) return candidates[0];
|
|
211
|
+
return [...candidates].sort((a, b) => {
|
|
212
|
+
const aKey = formatModelString(a);
|
|
213
|
+
const bKey = formatModelString(b);
|
|
214
|
+
const aUsage = context.modelUsageRank.get(aKey);
|
|
215
|
+
const bUsage = context.modelUsageRank.get(bKey);
|
|
216
|
+
if (aUsage !== undefined || bUsage !== undefined) {
|
|
217
|
+
return (aUsage ?? Number.POSITIVE_INFINITY) - (bUsage ?? Number.POSITIVE_INFINITY);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const aProviderUsage = context.providerUsageRank.get(a.provider);
|
|
221
|
+
const bProviderUsage = context.providerUsageRank.get(b.provider);
|
|
222
|
+
if (aProviderUsage !== undefined || bProviderUsage !== undefined) {
|
|
223
|
+
return (aProviderUsage ?? Number.POSITIVE_INFINITY) - (bProviderUsage ?? Number.POSITIVE_INFINITY);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const aDeprioritized = context.deprioritizedProviders.has(a.provider);
|
|
227
|
+
const bDeprioritized = context.deprioritizedProviders.has(b.provider);
|
|
228
|
+
if (aDeprioritized !== bDeprioritized) {
|
|
229
|
+
return aDeprioritized ? 1 : -1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const aOrder = context.modelOrder.get(aKey) ?? 0;
|
|
233
|
+
const bOrder = context.modelOrder.get(bKey) ?? 0;
|
|
234
|
+
return aOrder - bOrder;
|
|
235
|
+
})[0];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Helper to check if a model ID looks like an alias (no date suffix)
|
|
240
|
+
* Dates are typically in format: -20241022 or -20250929
|
|
241
|
+
*/
|
|
242
|
+
function isAlias(id: string): boolean {
|
|
243
|
+
// Check if ID ends with -latest
|
|
244
|
+
if (id.endsWith("-latest")) return true;
|
|
245
|
+
|
|
246
|
+
// Check if ID ends with a date pattern (-YYYYMMDD)
|
|
247
|
+
const datePattern = /-\d{8}$/;
|
|
248
|
+
return !datePattern.test(id);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Find an exact explicit provider/model match.
|
|
253
|
+
* Bare model ids are handled separately so canonical ids can coalesce variants.
|
|
254
|
+
*/
|
|
255
|
+
export function findExactModelReferenceMatch(
|
|
256
|
+
modelReference: string,
|
|
257
|
+
availableModels: Model<Api>[],
|
|
258
|
+
): Model<Api> | undefined {
|
|
259
|
+
const trimmedReference = modelReference.trim();
|
|
260
|
+
if (!trimmedReference) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const slashIndex = trimmedReference.indexOf("/");
|
|
265
|
+
if (slashIndex !== -1) {
|
|
266
|
+
const provider = trimmedReference.substring(0, slashIndex).trim();
|
|
267
|
+
const modelId = trimmedReference.substring(slashIndex + 1).trim();
|
|
268
|
+
if (provider && modelId) {
|
|
269
|
+
return resolveProviderModelReference(provider, modelId, availableModels);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function findExactCanonicalModelMatch(
|
|
276
|
+
modelReference: string,
|
|
277
|
+
availableModels: Model<Api>[],
|
|
278
|
+
modelRegistry: CanonicalModelRegistry | undefined,
|
|
279
|
+
): Model<Api> | undefined {
|
|
280
|
+
if (!modelRegistry) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
const trimmedReference = modelReference.trim();
|
|
284
|
+
if (!trimmedReference || trimmedReference.includes("/")) {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
return modelRegistry.resolveCanonicalModel?.(trimmedReference, {
|
|
288
|
+
availableOnly: false,
|
|
289
|
+
candidates: availableModels,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Try to match a pattern to a model from the available models list.
|
|
295
|
+
* Returns the matched model or undefined if no match found.
|
|
296
|
+
*/
|
|
297
|
+
function tryMatchModel(
|
|
298
|
+
modelPattern: string,
|
|
299
|
+
availableModels: Model<Api>[],
|
|
300
|
+
context: ModelPreferenceContext,
|
|
301
|
+
options?: { modelRegistry?: CanonicalModelRegistry },
|
|
302
|
+
): Model<Api> | undefined {
|
|
303
|
+
// Explicit provider/model selectors always bypass canonical coalescing.
|
|
304
|
+
const exactRefMatch = findExactModelReferenceMatch(modelPattern, availableModels);
|
|
305
|
+
if (exactRefMatch) {
|
|
306
|
+
return exactRefMatch;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Exact canonical ids coalesce provider variants before bare-id matching.
|
|
310
|
+
const exactCanonicalMatch = findExactCanonicalModelMatch(modelPattern, availableModels, options?.modelRegistry);
|
|
311
|
+
if (exactCanonicalMatch) {
|
|
312
|
+
return exactCanonicalMatch;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check for provider/modelId format — fuzzy match within provider
|
|
316
|
+
const slashIndex = modelPattern.indexOf("/");
|
|
317
|
+
if (slashIndex !== -1) {
|
|
318
|
+
const provider = modelPattern.substring(0, slashIndex);
|
|
319
|
+
const modelId = modelPattern.substring(slashIndex + 1);
|
|
320
|
+
|
|
321
|
+
const providerModels = availableModels.filter(m => m.provider.toLowerCase() === provider.toLowerCase());
|
|
322
|
+
if (providerModels.length > 0) {
|
|
323
|
+
const scored = providerModels
|
|
324
|
+
.map(model => ({ model, match: fuzzyMatch(modelId, model.id) }))
|
|
325
|
+
.filter(entry => entry.match.matches);
|
|
326
|
+
if (scored.length > 0) {
|
|
327
|
+
scored.sort((a, b) => {
|
|
328
|
+
if (a.match.score !== b.match.score) return a.match.score - b.match.score;
|
|
329
|
+
const aKey = formatModelString(a.model);
|
|
330
|
+
const bKey = formatModelString(b.model);
|
|
331
|
+
const aUsage = context.modelUsageRank.get(aKey) ?? Number.POSITIVE_INFINITY;
|
|
332
|
+
const bUsage = context.modelUsageRank.get(bKey) ?? Number.POSITIVE_INFINITY;
|
|
333
|
+
if (aUsage !== bUsage) return aUsage - bUsage;
|
|
334
|
+
|
|
335
|
+
const aProviderUsage = context.providerUsageRank.get(a.model.provider) ?? Number.POSITIVE_INFINITY;
|
|
336
|
+
const bProviderUsage = context.providerUsageRank.get(b.model.provider) ?? Number.POSITIVE_INFINITY;
|
|
337
|
+
if (aProviderUsage !== bProviderUsage) return aProviderUsage - bProviderUsage;
|
|
338
|
+
|
|
339
|
+
const aOrder = context.modelOrder.get(aKey) ?? 0;
|
|
340
|
+
const bOrder = context.modelOrder.get(bKey) ?? 0;
|
|
341
|
+
return aOrder - bOrder;
|
|
342
|
+
});
|
|
343
|
+
return scored[0]?.model;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Exact ID match (case-insensitive) — with ambiguity across providers handled by preference
|
|
349
|
+
const exactMatches = availableModels.filter(m => m.id.toLowerCase() === modelPattern.toLowerCase());
|
|
350
|
+
if (exactMatches.length > 0) {
|
|
351
|
+
return pickPreferredModel(exactMatches, context);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// No exact match - fall back to partial matching
|
|
355
|
+
const matches = availableModels.filter(
|
|
356
|
+
m =>
|
|
357
|
+
m.id.toLowerCase().includes(modelPattern.toLowerCase()) ||
|
|
358
|
+
m.name?.toLowerCase().includes(modelPattern.toLowerCase()),
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (matches.length === 0) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Separate into aliases and dated versions
|
|
366
|
+
const aliases = matches.filter(m => isAlias(m.id));
|
|
367
|
+
const datedVersions = matches.filter(m => !isAlias(m.id));
|
|
368
|
+
|
|
369
|
+
if (aliases.length > 0) {
|
|
370
|
+
return pickPreferredModel(aliases, context);
|
|
371
|
+
}
|
|
372
|
+
if (datedVersions.length === 0) return undefined;
|
|
373
|
+
|
|
374
|
+
if (datedVersions.length === 1) {
|
|
375
|
+
return datedVersions[0];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const sortedById = [...datedVersions].sort((a, b) => b.id.localeCompare(a.id));
|
|
379
|
+
const topId = sortedById[0]?.id;
|
|
380
|
+
if (!topId) return undefined;
|
|
381
|
+
const topCandidates = sortedById.filter(model => model.id === topId);
|
|
382
|
+
return pickPreferredModel(topCandidates, context);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface ParsedModelResult {
|
|
386
|
+
model: Model<Api> | undefined;
|
|
387
|
+
/** Thinking level if explicitly specified in pattern, undefined otherwise */
|
|
388
|
+
thinkingLevel?: ThinkingLevel;
|
|
389
|
+
warning: string | undefined;
|
|
390
|
+
explicitThinkingLevel: boolean;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Parse a pattern to extract model and thinking level.
|
|
395
|
+
* Handles models with colons in their IDs (e.g., OpenRouter's :exacto suffix).
|
|
396
|
+
*
|
|
397
|
+
* Algorithm:
|
|
398
|
+
* 1. Try to match full pattern as a model
|
|
399
|
+
* 2. If found, return it with undefined thinking level
|
|
400
|
+
* 3. If not found and has colons, split on last colon:
|
|
401
|
+
* - If suffix is valid thinking level, use it and recurse on prefix
|
|
402
|
+
* - If suffix is invalid, warn and recurse on prefix
|
|
403
|
+
*
|
|
404
|
+
* @internal Exported for testing
|
|
405
|
+
*/
|
|
406
|
+
function parseModelPatternWithContext(
|
|
407
|
+
pattern: string,
|
|
408
|
+
availableModels: Model<Api>[],
|
|
409
|
+
context: ModelPreferenceContext,
|
|
410
|
+
options?: { allowInvalidThinkingSelectorFallback?: boolean; modelRegistry?: CanonicalModelRegistry },
|
|
411
|
+
): ParsedModelResult {
|
|
412
|
+
// Try exact match first
|
|
413
|
+
const exactMatch = tryMatchModel(pattern, availableModels, context, options);
|
|
414
|
+
if (exactMatch) {
|
|
415
|
+
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// No match - try splitting on last colon if present
|
|
419
|
+
const lastColonIndex = pattern.lastIndexOf(":");
|
|
420
|
+
if (lastColonIndex === -1) {
|
|
421
|
+
// No colons, pattern simply doesn't match any model
|
|
422
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const prefix = pattern.substring(0, lastColonIndex);
|
|
426
|
+
const suffix = pattern.substring(lastColonIndex + 1);
|
|
427
|
+
|
|
428
|
+
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
429
|
+
if (parsedThinkingLevel) {
|
|
430
|
+
// Valid thinking level - recurse on prefix and use this level
|
|
431
|
+
const result = parseModelPatternWithContext(prefix, availableModels, context, options);
|
|
432
|
+
if (result.model) {
|
|
433
|
+
// Only use this thinking level if no warning from inner recursion
|
|
434
|
+
const explicitThinkingLevel = !result.warning;
|
|
435
|
+
return {
|
|
436
|
+
model: result.model,
|
|
437
|
+
thinkingLevel: explicitThinkingLevel ? parsedThinkingLevel : undefined,
|
|
438
|
+
warning: result.warning,
|
|
439
|
+
explicitThinkingLevel,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const allowFallback = options?.allowInvalidThinkingSelectorFallback ?? true;
|
|
446
|
+
if (!allowFallback) {
|
|
447
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Invalid suffix - recurse on prefix and warn
|
|
451
|
+
const result = parseModelPatternWithContext(prefix, availableModels, context, options);
|
|
452
|
+
if (result.model) {
|
|
453
|
+
return {
|
|
454
|
+
model: result.model,
|
|
455
|
+
thinkingLevel: undefined,
|
|
456
|
+
warning: `Invalid thinking level "${suffix}" in pattern "${pattern}". Using default instead.`,
|
|
457
|
+
explicitThinkingLevel: false,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function parseModelPattern(
|
|
464
|
+
pattern: string,
|
|
465
|
+
availableModels: Model<Api>[],
|
|
466
|
+
preferences?: ModelMatchPreferences,
|
|
467
|
+
options?: { allowInvalidThinkingSelectorFallback?: boolean; modelRegistry?: CanonicalModelRegistry },
|
|
468
|
+
): ParsedModelResult {
|
|
469
|
+
const context = buildPreferenceContext(availableModels, preferences);
|
|
470
|
+
return parseModelPatternWithContext(pattern, availableModels, context, options);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const PREFIX_MODEL_ROLE = "pi/";
|
|
474
|
+
const DEFAULT_MODEL_ROLE = "default";
|
|
475
|
+
|
|
476
|
+
function getModelRoleAlias(value: string): ModelRole | undefined {
|
|
477
|
+
const normalized = value.trim();
|
|
478
|
+
if (!normalized.startsWith(PREFIX_MODEL_ROLE)) return undefined;
|
|
479
|
+
|
|
480
|
+
const candidate = normalized.slice(PREFIX_MODEL_ROLE.length);
|
|
481
|
+
for (const role of MODEL_ROLE_IDS) {
|
|
482
|
+
if (candidate === role) return role;
|
|
483
|
+
}
|
|
484
|
+
return undefined;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function normalizeModelPatternList(value: string | string[] | undefined): string[] {
|
|
488
|
+
if (!value) return [];
|
|
489
|
+
const patterns = Array.isArray(value) ? value : value.split(",");
|
|
490
|
+
return patterns.map(pattern => pattern.trim()).filter(Boolean);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function isSessionInheritedAgentPattern(value: string): boolean {
|
|
494
|
+
return value === DEFAULT_MODEL_ROLE || value === `${PREFIX_MODEL_ROLE}${DEFAULT_MODEL_ROLE}` || value === "pi/task";
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function resolveConfiguredRolePattern(value: string, settings?: Settings): string[] | undefined {
|
|
498
|
+
const normalized = value.trim();
|
|
499
|
+
if (!normalized) return undefined;
|
|
500
|
+
|
|
501
|
+
const lastColonIndex = normalized.lastIndexOf(":");
|
|
502
|
+
const thinkingLevel =
|
|
503
|
+
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(normalized.slice(lastColonIndex + 1)) : undefined;
|
|
504
|
+
const aliasCandidate = thinkingLevel ? normalized.slice(0, lastColonIndex) : normalized;
|
|
505
|
+
const role = getModelRoleAlias(aliasCandidate);
|
|
506
|
+
if (!role) return [normalized];
|
|
507
|
+
|
|
508
|
+
const configured = settings?.getModelRole(role)?.trim();
|
|
509
|
+
const roleDefaults = normalizeModelPatternList(MODEL_PRIO[role as keyof typeof MODEL_PRIO]);
|
|
510
|
+
const resolved = configured ? normalizeModelPatternList(configured) : roleDefaults;
|
|
511
|
+
if (!resolved || resolved.length === 0) {
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return thinkingLevel ? resolved.map(pattern => `${pattern}:${thinkingLevel}`) : resolved;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Expand a role alias like "pi/smol" to the configured model string.
|
|
520
|
+
*/
|
|
521
|
+
export function expandRoleAlias(value: string, settings?: Settings): string {
|
|
522
|
+
const normalized = value.trim();
|
|
523
|
+
if (normalized === DEFAULT_MODEL_ROLE) {
|
|
524
|
+
return settings?.getModelRole("default") ?? value;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const resolved = resolveConfiguredRolePattern(value, settings)?.[0];
|
|
528
|
+
return resolved ?? value;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function resolveConfiguredModelPatterns(value: string | string[] | undefined, settings?: Settings): string[] {
|
|
532
|
+
const patterns = normalizeModelPatternList(value);
|
|
533
|
+
return patterns.flatMap(pattern => {
|
|
534
|
+
const resolved = resolveConfiguredRolePattern(pattern, settings);
|
|
535
|
+
return resolved ?? [];
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
export interface AgentModelPatternResolutionOptions {
|
|
539
|
+
settingsOverride?: string | string[];
|
|
540
|
+
agentModel?: string | string[];
|
|
541
|
+
settings?: Settings;
|
|
542
|
+
activeModelPattern?: string;
|
|
543
|
+
fallbackModelPattern?: string;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[] {
|
|
547
|
+
const { settingsOverride, agentModel, settings, activeModelPattern, fallbackModelPattern } = options;
|
|
548
|
+
|
|
549
|
+
const overridePatterns = resolveConfiguredModelPatterns(settingsOverride, settings);
|
|
550
|
+
if (overridePatterns.length > 0) return overridePatterns;
|
|
551
|
+
|
|
552
|
+
const normalizedAgentPatterns = normalizeModelPatternList(agentModel);
|
|
553
|
+
const configuredAgentPatterns = resolveConfiguredModelPatterns(agentModel, settings);
|
|
554
|
+
const singleAgentPattern = normalizedAgentPatterns.length === 1 ? normalizedAgentPatterns[0] : undefined;
|
|
555
|
+
const agentInheritsSessionModel = singleAgentPattern ? isSessionInheritedAgentPattern(singleAgentPattern) : false;
|
|
556
|
+
if (configuredAgentPatterns.length > 0) {
|
|
557
|
+
if (!agentInheritsSessionModel) return configuredAgentPatterns;
|
|
558
|
+
if (singleAgentPattern === "pi/task") return configuredAgentPatterns;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const fallback =
|
|
562
|
+
activeModelPattern?.trim() || fallbackModelPattern?.trim() || settings?.getModelRole("default")?.trim() || "";
|
|
563
|
+
return resolveConfiguredModelPatterns(fallback, settings);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Resolve a model role value into a concrete model and thinking metadata.
|
|
568
|
+
*/
|
|
569
|
+
export interface ResolvedModelRoleValue {
|
|
570
|
+
model: Model<Api> | undefined;
|
|
571
|
+
thinkingLevel?: ThinkingLevel;
|
|
572
|
+
explicitThinkingLevel: boolean;
|
|
573
|
+
warning: string | undefined;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export function resolveModelRoleValue(
|
|
577
|
+
roleValue: string | undefined,
|
|
578
|
+
availableModels: Model<Api>[],
|
|
579
|
+
options?: { settings?: Settings; matchPreferences?: ModelMatchPreferences; modelRegistry?: CanonicalModelRegistry },
|
|
580
|
+
): ResolvedModelRoleValue {
|
|
581
|
+
if (!roleValue) {
|
|
582
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const normalized = roleValue.trim();
|
|
586
|
+
if (!normalized || normalized === DEFAULT_MODEL_ROLE) {
|
|
587
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const lastColonIndex = normalized.lastIndexOf(":");
|
|
591
|
+
const _thinkingSelector =
|
|
592
|
+
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(normalized.slice(lastColonIndex + 1)) : undefined;
|
|
593
|
+
const effectivePatterns = resolveConfiguredRolePattern(normalized, options?.settings);
|
|
594
|
+
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
595
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
let warning: string | undefined;
|
|
599
|
+
for (const effectivePattern of effectivePatterns) {
|
|
600
|
+
const resolved = parseModelPattern(effectivePattern, availableModels, options?.matchPreferences, {
|
|
601
|
+
modelRegistry: options?.modelRegistry,
|
|
602
|
+
});
|
|
603
|
+
if (resolved.model) {
|
|
604
|
+
return {
|
|
605
|
+
model: resolved.model,
|
|
606
|
+
thinkingLevel: resolved.explicitThinkingLevel
|
|
607
|
+
? (resolveThinkingLevelForModel(resolved.model, resolved.thinkingLevel) ?? resolved.thinkingLevel)
|
|
608
|
+
: resolved.thinkingLevel,
|
|
609
|
+
explicitThinkingLevel: resolved.explicitThinkingLevel,
|
|
610
|
+
warning: resolved.warning,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (!warning && resolved.warning) {
|
|
614
|
+
warning = resolved.warning;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning };
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function extractExplicitThinkingSelector(
|
|
622
|
+
value: string | undefined,
|
|
623
|
+
settings?: Settings,
|
|
624
|
+
): ThinkingLevel | undefined {
|
|
625
|
+
if (!value) return undefined;
|
|
626
|
+
const normalized = value.trim();
|
|
627
|
+
if (!normalized || normalized === DEFAULT_MODEL_ROLE) return undefined;
|
|
628
|
+
|
|
629
|
+
const visited = new Set<string>();
|
|
630
|
+
let current = normalized;
|
|
631
|
+
while (!visited.has(current)) {
|
|
632
|
+
visited.add(current);
|
|
633
|
+
const lastColonIndex = current.lastIndexOf(":");
|
|
634
|
+
const thinkingSelector =
|
|
635
|
+
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(current.slice(lastColonIndex + 1)) : undefined;
|
|
636
|
+
if (thinkingSelector) {
|
|
637
|
+
return thinkingSelector;
|
|
638
|
+
}
|
|
639
|
+
const expanded = expandRoleAlias(current, settings).trim();
|
|
640
|
+
if (!expanded || expanded === current) break;
|
|
641
|
+
if (expanded === DEFAULT_MODEL_ROLE) return undefined;
|
|
642
|
+
current = expanded;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Resolve a model identifier or pattern to a Model instance.
|
|
650
|
+
*/
|
|
651
|
+
export function resolveModelFromString(
|
|
652
|
+
value: string,
|
|
653
|
+
available: Model<Api>[],
|
|
654
|
+
matchPreferences?: ModelMatchPreferences,
|
|
655
|
+
modelRegistry?: CanonicalModelRegistry,
|
|
656
|
+
): Model<Api> | undefined {
|
|
657
|
+
const parsed = parseModelString(value);
|
|
658
|
+
if (parsed) {
|
|
659
|
+
const exact = available.find(model => model.provider === parsed.provider && model.id === parsed.id);
|
|
660
|
+
if (exact) return exact;
|
|
661
|
+
}
|
|
662
|
+
return parseModelPattern(value, available, matchPreferences, { modelRegistry }).model;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Resolve a model from configured roles, honoring order and overrides.
|
|
667
|
+
*/
|
|
668
|
+
export function resolveModelFromSettings(options: {
|
|
669
|
+
settings: Settings;
|
|
670
|
+
availableModels: Model<Api>[];
|
|
671
|
+
matchPreferences?: ModelMatchPreferences;
|
|
672
|
+
roleOrder?: readonly ModelRole[];
|
|
673
|
+
modelRegistry?: CanonicalModelRegistry;
|
|
674
|
+
}): Model<Api> | undefined {
|
|
675
|
+
const { settings, availableModels, matchPreferences, roleOrder, modelRegistry } = options;
|
|
676
|
+
const roles = roleOrder ?? MODEL_ROLE_IDS;
|
|
677
|
+
for (const role of roles) {
|
|
678
|
+
const configured = settings.getModelRole(role);
|
|
679
|
+
if (!configured) continue;
|
|
680
|
+
const resolved = resolveModelFromString(
|
|
681
|
+
expandRoleAlias(configured, settings),
|
|
682
|
+
availableModels,
|
|
683
|
+
matchPreferences,
|
|
684
|
+
modelRegistry,
|
|
685
|
+
);
|
|
686
|
+
if (resolved) return resolved;
|
|
687
|
+
}
|
|
688
|
+
return availableModels[0];
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Resolve a list of override patterns to the first matching model.
|
|
693
|
+
*/
|
|
694
|
+
export function resolveModelOverride(
|
|
695
|
+
modelPatterns: string[],
|
|
696
|
+
modelRegistry: ModelLookupRegistry,
|
|
697
|
+
settings?: Settings,
|
|
698
|
+
): { model?: Model<Api>; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } {
|
|
699
|
+
if (modelPatterns.length === 0) return { explicitThinkingLevel: false };
|
|
700
|
+
const availableModels = modelRegistry.getAvailable();
|
|
701
|
+
const matchPreferences = { usageOrder: settings?.getStorage()?.getModelUsageOrder() };
|
|
702
|
+
for (const pattern of modelPatterns) {
|
|
703
|
+
const { model, thinkingLevel, explicitThinkingLevel } = resolveModelRoleValue(pattern, availableModels, {
|
|
704
|
+
settings,
|
|
705
|
+
matchPreferences,
|
|
706
|
+
modelRegistry,
|
|
707
|
+
});
|
|
708
|
+
if (model) {
|
|
709
|
+
return { model, thinkingLevel, explicitThinkingLevel };
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return { explicitThinkingLevel: false };
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Resolve a list of role patterns to the first matching model.
|
|
717
|
+
*/
|
|
718
|
+
export function resolveRoleSelection(
|
|
719
|
+
roles: readonly string[],
|
|
720
|
+
settings: Settings,
|
|
721
|
+
availableModels: Model<Api>[],
|
|
722
|
+
modelRegistry?: CanonicalModelRegistry,
|
|
723
|
+
): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {
|
|
724
|
+
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
|
725
|
+
for (const role of roles) {
|
|
726
|
+
const resolved = resolveModelRoleValue(settings.getModelRole(role), availableModels, {
|
|
727
|
+
settings,
|
|
728
|
+
matchPreferences,
|
|
729
|
+
modelRegistry,
|
|
730
|
+
});
|
|
731
|
+
if (resolved.model) {
|
|
732
|
+
return { model: resolved.model, thinkingLevel: resolved.thinkingLevel };
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return undefined;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function resolveExactCanonicalScopePattern(
|
|
739
|
+
pattern: string,
|
|
740
|
+
modelRegistry: Pick<ModelRegistry, "getCanonicalVariants">,
|
|
741
|
+
availableModels: Model<Api>[],
|
|
742
|
+
): { models: Model<Api>[]; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } | undefined {
|
|
743
|
+
const lastColonIndex = pattern.lastIndexOf(":");
|
|
744
|
+
let canonicalId = pattern;
|
|
745
|
+
let thinkingLevel: ThinkingLevel | undefined;
|
|
746
|
+
let explicitThinkingLevel = false;
|
|
747
|
+
|
|
748
|
+
if (lastColonIndex !== -1) {
|
|
749
|
+
const suffix = pattern.substring(lastColonIndex + 1);
|
|
750
|
+
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
751
|
+
if (parsedThinkingLevel) {
|
|
752
|
+
canonicalId = pattern.substring(0, lastColonIndex);
|
|
753
|
+
thinkingLevel = parsedThinkingLevel;
|
|
754
|
+
explicitThinkingLevel = true;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const variants = modelRegistry
|
|
759
|
+
.getCanonicalVariants(canonicalId, { availableOnly: true, candidates: availableModels })
|
|
760
|
+
.map(variant => variant.model);
|
|
761
|
+
if (variants.length === 0) {
|
|
762
|
+
return undefined;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return { models: variants, thinkingLevel, explicitThinkingLevel };
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Resolve model patterns to actual Model objects with optional thinking levels
|
|
770
|
+
* Format: "pattern:level" where :level is optional
|
|
771
|
+
* For each pattern, finds all matching models and picks the best version:
|
|
772
|
+
* 1. Prefer alias (e.g., claude-sonnet-4-5) over dated versions (claude-sonnet-4-5-20250929)
|
|
773
|
+
* 2. If no alias, pick the latest dated version
|
|
774
|
+
*
|
|
775
|
+
* Supports models with colons in their IDs (e.g., OpenRouter's model:exacto).
|
|
776
|
+
* The algorithm tries to match the full pattern first, then progressively
|
|
777
|
+
* strips colon-suffixes to find a match.
|
|
778
|
+
*/
|
|
779
|
+
export async function resolveModelScope(
|
|
780
|
+
patterns: string[],
|
|
781
|
+
modelRegistry: Pick<ModelRegistry, "getAvailable" | "getCanonicalVariants">,
|
|
782
|
+
preferences?: ModelMatchPreferences,
|
|
783
|
+
): Promise<ScopedModel[]> {
|
|
784
|
+
const availableModels = modelRegistry.getAvailable();
|
|
785
|
+
const context = buildPreferenceContext(availableModels, preferences);
|
|
786
|
+
const scopedModels: ScopedModel[] = [];
|
|
787
|
+
|
|
788
|
+
for (const pattern of patterns) {
|
|
789
|
+
// Check if pattern contains glob characters
|
|
790
|
+
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("[")) {
|
|
791
|
+
// Extract optional thinking level suffix (e.g., "provider/*:high")
|
|
792
|
+
const colonIdx = pattern.lastIndexOf(":");
|
|
793
|
+
let globPattern = pattern;
|
|
794
|
+
let thinkingLevel: ThinkingLevel | undefined;
|
|
795
|
+
let explicitThinkingLevel = false;
|
|
796
|
+
|
|
797
|
+
if (colonIdx !== -1) {
|
|
798
|
+
const suffix = pattern.substring(colonIdx + 1);
|
|
799
|
+
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
800
|
+
if (parsedThinkingLevel) {
|
|
801
|
+
thinkingLevel = parsedThinkingLevel;
|
|
802
|
+
explicitThinkingLevel = true;
|
|
803
|
+
globPattern = pattern.substring(0, colonIdx);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Match against "provider/modelId" format OR just model ID
|
|
808
|
+
// This allows "*sonnet*" to match without requiring "anthropic/*sonnet*"
|
|
809
|
+
const matchingModels = availableModels.filter(m => {
|
|
810
|
+
const fullId = `${m.provider}/${m.id}`;
|
|
811
|
+
const glob = new Bun.Glob(globPattern.toLowerCase());
|
|
812
|
+
return glob.match(fullId.toLowerCase()) || glob.match(m.id.toLowerCase());
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
if (matchingModels.length === 0) {
|
|
816
|
+
console.warn(chalk.yellow(`Warning: No models match pattern "${pattern}"`));
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
for (const model of matchingModels) {
|
|
821
|
+
if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) {
|
|
822
|
+
scopedModels.push({
|
|
823
|
+
model,
|
|
824
|
+
thinkingLevel: explicitThinkingLevel
|
|
825
|
+
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
826
|
+
: thinkingLevel,
|
|
827
|
+
explicitThinkingLevel,
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const exactCanonical = resolveExactCanonicalScopePattern(pattern, modelRegistry, availableModels);
|
|
835
|
+
if (exactCanonical) {
|
|
836
|
+
for (const model of exactCanonical.models) {
|
|
837
|
+
if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) {
|
|
838
|
+
scopedModels.push({
|
|
839
|
+
model,
|
|
840
|
+
thinkingLevel: exactCanonical.explicitThinkingLevel
|
|
841
|
+
? (resolveThinkingLevelForModel(model, exactCanonical.thinkingLevel) ??
|
|
842
|
+
exactCanonical.thinkingLevel)
|
|
843
|
+
: exactCanonical.thinkingLevel,
|
|
844
|
+
explicitThinkingLevel: exactCanonical.explicitThinkingLevel,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const { model, thinkingLevel, warning, explicitThinkingLevel } = parseModelPatternWithContext(
|
|
852
|
+
pattern,
|
|
853
|
+
availableModels,
|
|
854
|
+
context,
|
|
855
|
+
{ modelRegistry },
|
|
856
|
+
);
|
|
857
|
+
|
|
858
|
+
if (warning) {
|
|
859
|
+
console.warn(chalk.yellow(`Warning: ${warning}`));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (!model) {
|
|
863
|
+
console.warn(chalk.yellow(`Warning: No models match pattern "${pattern}"`));
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Avoid duplicates
|
|
868
|
+
if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) {
|
|
869
|
+
scopedModels.push({
|
|
870
|
+
model,
|
|
871
|
+
thinkingLevel: explicitThinkingLevel
|
|
872
|
+
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
873
|
+
: thinkingLevel,
|
|
874
|
+
explicitThinkingLevel,
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return scopedModels;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
export interface ResolveCliModelResult {
|
|
883
|
+
model: Model<Api> | undefined;
|
|
884
|
+
selector?: string;
|
|
885
|
+
thinkingLevel?: ThinkingLevel;
|
|
886
|
+
warning: string | undefined;
|
|
887
|
+
error: string | undefined;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Resolve a single model from CLI flags.
|
|
892
|
+
*/
|
|
893
|
+
export function resolveCliModel(options: {
|
|
894
|
+
cliProvider?: string;
|
|
895
|
+
cliModel?: string;
|
|
896
|
+
modelRegistry: CliModelRegistry;
|
|
897
|
+
preferences?: ModelMatchPreferences;
|
|
898
|
+
}): ResolveCliModelResult {
|
|
899
|
+
const { cliProvider, cliModel, modelRegistry, preferences } = options;
|
|
900
|
+
|
|
901
|
+
if (!cliModel) {
|
|
902
|
+
return { model: undefined, selector: undefined, warning: undefined, error: undefined };
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const availableModels = modelRegistry.getAll();
|
|
906
|
+
if (availableModels.length === 0) {
|
|
907
|
+
return {
|
|
908
|
+
model: undefined,
|
|
909
|
+
selector: undefined,
|
|
910
|
+
warning: undefined,
|
|
911
|
+
error: "No models available. Check your installation or add models to models.json.",
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const providerMap = new Map<string, string>();
|
|
916
|
+
for (const model of availableModels) {
|
|
917
|
+
providerMap.set(model.provider.toLowerCase(), model.provider);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
let provider = cliProvider ? providerMap.get(cliProvider.toLowerCase()) : undefined;
|
|
921
|
+
if (cliProvider && !provider) {
|
|
922
|
+
return {
|
|
923
|
+
model: undefined,
|
|
924
|
+
selector: undefined,
|
|
925
|
+
warning: undefined,
|
|
926
|
+
error: `Unknown provider "${cliProvider}". Use --list-models to see available providers/models.`,
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const trimmedModel = cliModel.trim();
|
|
931
|
+
if (!provider) {
|
|
932
|
+
const lower = trimmedModel.toLowerCase();
|
|
933
|
+
// When input has provider/id format (e.g. "zai/glm-5"), prefer decomposed
|
|
934
|
+
// provider+id match over flat id match. Without this, a model with id
|
|
935
|
+
// "zai/glm-5" on provider "vercel-ai-gateway" wins over provider "zai"
|
|
936
|
+
// with id "glm-5", because Array.find returns the first catalog hit.
|
|
937
|
+
const slashIdx = lower.indexOf("/");
|
|
938
|
+
let exact: (typeof availableModels)[number] | undefined;
|
|
939
|
+
if (slashIdx !== -1) {
|
|
940
|
+
const prefix = lower.substring(0, slashIdx);
|
|
941
|
+
const suffix = trimmedModel.substring(slashIdx + 1);
|
|
942
|
+
exact = resolveProviderModelReference(prefix, suffix, availableModels);
|
|
943
|
+
}
|
|
944
|
+
if (!exact && !trimmedModel.includes(":")) {
|
|
945
|
+
const canonicalMatch = modelRegistry.resolveCanonicalModel?.(trimmedModel, { availableOnly: false });
|
|
946
|
+
if (canonicalMatch) {
|
|
947
|
+
return {
|
|
948
|
+
model: canonicalMatch,
|
|
949
|
+
selector: modelRegistry.getCanonicalId?.(canonicalMatch) ?? trimmedModel,
|
|
950
|
+
warning: undefined,
|
|
951
|
+
thinkingLevel: undefined,
|
|
952
|
+
error: undefined,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (!exact) {
|
|
957
|
+
exact = availableModels.find(
|
|
958
|
+
model => model.id.toLowerCase() === lower || `${model.provider}/${model.id}`.toLowerCase() === lower,
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
if (exact) {
|
|
962
|
+
return {
|
|
963
|
+
model: exact,
|
|
964
|
+
selector: formatModelString(exact),
|
|
965
|
+
warning: undefined,
|
|
966
|
+
thinkingLevel: undefined,
|
|
967
|
+
error: undefined,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
let pattern = trimmedModel;
|
|
973
|
+
|
|
974
|
+
if (!provider) {
|
|
975
|
+
const slashIndex = cliModel.indexOf("/");
|
|
976
|
+
if (slashIndex !== -1) {
|
|
977
|
+
const maybeProvider = cliModel.substring(0, slashIndex);
|
|
978
|
+
const canonical = providerMap.get(maybeProvider.toLowerCase());
|
|
979
|
+
if (canonical) {
|
|
980
|
+
provider = canonical;
|
|
981
|
+
pattern = cliModel.substring(slashIndex + 1);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
const prefix = `${provider}/`;
|
|
986
|
+
if (cliModel.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
987
|
+
pattern = cliModel.substring(prefix.length);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (provider) {
|
|
992
|
+
const exactProviderMatch = resolveProviderModelReference(provider, pattern, availableModels);
|
|
993
|
+
if (exactProviderMatch) {
|
|
994
|
+
return {
|
|
995
|
+
model: exactProviderMatch,
|
|
996
|
+
selector: formatModelString(exactProviderMatch),
|
|
997
|
+
warning: undefined,
|
|
998
|
+
thinkingLevel: undefined,
|
|
999
|
+
error: undefined,
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const candidates = provider ? availableModels.filter(model => model.provider === provider) : availableModels;
|
|
1005
|
+
const { model, thinkingLevel, warning } = parseModelPattern(pattern, candidates, preferences, {
|
|
1006
|
+
allowInvalidThinkingSelectorFallback: false,
|
|
1007
|
+
modelRegistry,
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
if (!model) {
|
|
1011
|
+
const display = provider ? `${provider}/${pattern}` : cliModel;
|
|
1012
|
+
return {
|
|
1013
|
+
model: undefined,
|
|
1014
|
+
selector: undefined,
|
|
1015
|
+
thinkingLevel: undefined,
|
|
1016
|
+
warning,
|
|
1017
|
+
error: `Model "${display}" not found. Use --list-models to see available models.`,
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
let selector = provider ? formatModelString(model) : undefined;
|
|
1022
|
+
if (!provider) {
|
|
1023
|
+
const lastColonIndex = pattern.lastIndexOf(":");
|
|
1024
|
+
const canonicalCandidate =
|
|
1025
|
+
lastColonIndex !== -1 && parseThinkingLevel(pattern.substring(lastColonIndex + 1))
|
|
1026
|
+
? pattern.substring(0, lastColonIndex)
|
|
1027
|
+
: pattern;
|
|
1028
|
+
if (!canonicalCandidate.includes("/")) {
|
|
1029
|
+
const canonicalResolved = modelRegistry.resolveCanonicalModel?.(canonicalCandidate, { availableOnly: false });
|
|
1030
|
+
if (canonicalResolved && canonicalResolved.provider === model.provider && canonicalResolved.id === model.id) {
|
|
1031
|
+
selector = modelRegistry.getCanonicalId?.(canonicalResolved) ?? canonicalCandidate;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
return {
|
|
1037
|
+
model,
|
|
1038
|
+
selector,
|
|
1039
|
+
thinkingLevel,
|
|
1040
|
+
warning,
|
|
1041
|
+
error: undefined,
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
export interface InitialModelResult {
|
|
1046
|
+
model: Model<Api> | undefined;
|
|
1047
|
+
thinkingLevel?: ThinkingLevel;
|
|
1048
|
+
fallbackMessage: string | undefined;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Find the initial model to use based on priority:
|
|
1053
|
+
* 1. CLI args (provider + model)
|
|
1054
|
+
* 2. First model from scoped models (if not continuing/resuming)
|
|
1055
|
+
* 3. Restored from session (if continuing/resuming)
|
|
1056
|
+
* 4. Saved default from settings
|
|
1057
|
+
* 5. First available model with valid API key
|
|
1058
|
+
*/
|
|
1059
|
+
export async function findInitialModel(options: {
|
|
1060
|
+
cliProvider?: string;
|
|
1061
|
+
cliModel?: string;
|
|
1062
|
+
scopedModels: ScopedModel[];
|
|
1063
|
+
isContinuing: boolean;
|
|
1064
|
+
defaultProvider?: string;
|
|
1065
|
+
defaultModelId?: string;
|
|
1066
|
+
defaultThinkingSelector?: Effort;
|
|
1067
|
+
modelRegistry: InitialModelRegistry;
|
|
1068
|
+
}): Promise<InitialModelResult> {
|
|
1069
|
+
const {
|
|
1070
|
+
cliProvider,
|
|
1071
|
+
cliModel,
|
|
1072
|
+
scopedModels,
|
|
1073
|
+
isContinuing,
|
|
1074
|
+
defaultProvider,
|
|
1075
|
+
defaultModelId,
|
|
1076
|
+
defaultThinkingSelector,
|
|
1077
|
+
modelRegistry,
|
|
1078
|
+
} = options;
|
|
1079
|
+
|
|
1080
|
+
let model: Model<Api> | undefined;
|
|
1081
|
+
let thinkingLevel: Effort | undefined;
|
|
1082
|
+
|
|
1083
|
+
// 1. CLI args take priority
|
|
1084
|
+
if (cliProvider && cliModel) {
|
|
1085
|
+
const found = modelRegistry.find(cliProvider, cliModel);
|
|
1086
|
+
if (!found) {
|
|
1087
|
+
console.error(chalk.red(`Model ${cliProvider}/${cliModel} not found`));
|
|
1088
|
+
process.exit(1);
|
|
1089
|
+
}
|
|
1090
|
+
return { model: found, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// 2. Use first model from scoped models (skip if continuing/resuming)
|
|
1094
|
+
if (scopedModels.length > 0 && !isContinuing) {
|
|
1095
|
+
const scoped = scopedModels[0];
|
|
1096
|
+
const scopedThinkingSelector =
|
|
1097
|
+
scoped.thinkingLevel === ThinkingLevel.Inherit
|
|
1098
|
+
? defaultThinkingSelector
|
|
1099
|
+
: (scoped.thinkingLevel ?? defaultThinkingSelector);
|
|
1100
|
+
return {
|
|
1101
|
+
model: scoped.model,
|
|
1102
|
+
thinkingLevel:
|
|
1103
|
+
scopedThinkingSelector === ThinkingLevel.Off
|
|
1104
|
+
? ThinkingLevel.Off
|
|
1105
|
+
: clampThinkingLevelForModel(scoped.model, scopedThinkingSelector),
|
|
1106
|
+
fallbackMessage: undefined,
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// 3. Try saved default from settings
|
|
1111
|
+
if (defaultProvider && defaultModelId) {
|
|
1112
|
+
const found = modelRegistry.find(defaultProvider, defaultModelId);
|
|
1113
|
+
if (found) {
|
|
1114
|
+
model = found;
|
|
1115
|
+
thinkingLevel = clampThinkingLevelForModel(found, defaultThinkingSelector);
|
|
1116
|
+
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// 4. Try first available model with valid API key
|
|
1121
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1122
|
+
|
|
1123
|
+
if (availableModels.length > 0) {
|
|
1124
|
+
// Try to find a default model from known providers
|
|
1125
|
+
for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {
|
|
1126
|
+
const defaultId = defaultModelPerProvider[provider];
|
|
1127
|
+
const match = availableModels.find(m => m.provider === provider && m.id === defaultId);
|
|
1128
|
+
if (match) {
|
|
1129
|
+
return { model: match, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// If no default found, use first available
|
|
1134
|
+
return { model: availableModels[0], thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// 5. No model found
|
|
1138
|
+
return { model: undefined, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Restore model from session, with fallback to available models
|
|
1143
|
+
*/
|
|
1144
|
+
export async function restoreModelFromSession(
|
|
1145
|
+
savedProvider: string,
|
|
1146
|
+
savedModelId: string,
|
|
1147
|
+
currentModel: Model<Api> | undefined,
|
|
1148
|
+
shouldPrintMessages: boolean,
|
|
1149
|
+
modelRegistry: RestorableModelRegistry,
|
|
1150
|
+
): Promise<{ model: Model<Api> | undefined; fallbackMessage: string | undefined }> {
|
|
1151
|
+
const restoredModel = modelRegistry.find(savedProvider, savedModelId);
|
|
1152
|
+
|
|
1153
|
+
// Check if restored model exists and has a valid API key
|
|
1154
|
+
const hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;
|
|
1155
|
+
|
|
1156
|
+
if (restoredModel && hasApiKey) {
|
|
1157
|
+
if (shouldPrintMessages) {
|
|
1158
|
+
console.log(chalk.dim(`Restored model: ${savedProvider}/${savedModelId}`));
|
|
1159
|
+
}
|
|
1160
|
+
return { model: restoredModel, fallbackMessage: undefined };
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Model not found or no API key - fall back
|
|
1164
|
+
const reason = !restoredModel ? "model no longer exists" : "no API key available";
|
|
1165
|
+
|
|
1166
|
+
if (shouldPrintMessages) {
|
|
1167
|
+
console.error(chalk.yellow(`Warning: Could not restore model ${savedProvider}/${savedModelId} (${reason}).`));
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// If we already have a model, use it as fallback
|
|
1171
|
+
if (currentModel) {
|
|
1172
|
+
if (shouldPrintMessages) {
|
|
1173
|
+
console.log(chalk.dim(`Falling back to: ${currentModel.provider}/${currentModel.id}`));
|
|
1174
|
+
}
|
|
1175
|
+
return {
|
|
1176
|
+
model: currentModel,
|
|
1177
|
+
fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${currentModel.provider}/${currentModel.id}.`,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Try to find any available model
|
|
1182
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1183
|
+
|
|
1184
|
+
if (availableModels.length > 0) {
|
|
1185
|
+
// Try to find a default model from known providers
|
|
1186
|
+
let fallbackModel: Model<Api> | undefined;
|
|
1187
|
+
for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {
|
|
1188
|
+
const defaultId = defaultModelPerProvider[provider];
|
|
1189
|
+
const match = availableModels.find(m => m.provider === provider && m.id === defaultId);
|
|
1190
|
+
if (match) {
|
|
1191
|
+
fallbackModel = match;
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// If no default found, use first available
|
|
1197
|
+
if (!fallbackModel) {
|
|
1198
|
+
fallbackModel = availableModels[0];
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (shouldPrintMessages) {
|
|
1202
|
+
console.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`));
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
return {
|
|
1206
|
+
model: fallbackModel,
|
|
1207
|
+
fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${fallbackModel.provider}/${fallbackModel.id}.`,
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// No models available
|
|
1212
|
+
return { model: undefined, fallbackMessage: undefined };
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Find a smol/fast model using the priority chain.
|
|
1217
|
+
* Tries exact matches first, then fuzzy matches.
|
|
1218
|
+
*
|
|
1219
|
+
* @param modelRegistry The model registry to search
|
|
1220
|
+
* @param savedModel Optional saved model string from settings (provider/modelId)
|
|
1221
|
+
* @returns The best available smol model, or undefined if none found
|
|
1222
|
+
*/
|
|
1223
|
+
export async function findSmolModel(
|
|
1224
|
+
modelRegistry: ModelLookupRegistry,
|
|
1225
|
+
savedModel?: string,
|
|
1226
|
+
): Promise<Model<Api> | undefined> {
|
|
1227
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1228
|
+
if (availableModels.length === 0) return undefined;
|
|
1229
|
+
|
|
1230
|
+
// 1. Try saved model from settings
|
|
1231
|
+
if (savedModel) {
|
|
1232
|
+
const match = resolveModelFromString(savedModel, availableModels, undefined, modelRegistry);
|
|
1233
|
+
if (match) return match;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// 2. Try priority chain
|
|
1237
|
+
for (const pattern of MODEL_PRIO.smol) {
|
|
1238
|
+
// Try exact match with provider prefix
|
|
1239
|
+
const providerMatch = availableModels.find(m => `${m.provider}/${m.id}`.toLowerCase() === pattern);
|
|
1240
|
+
if (providerMatch) return providerMatch;
|
|
1241
|
+
|
|
1242
|
+
// Try exact match first
|
|
1243
|
+
const exactMatch = parseModelPattern(pattern, availableModels, undefined, { modelRegistry }).model;
|
|
1244
|
+
if (exactMatch) return exactMatch;
|
|
1245
|
+
|
|
1246
|
+
// Try fuzzy match (substring)
|
|
1247
|
+
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern));
|
|
1248
|
+
if (fuzzyMatch) return fuzzyMatch;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// 3. Fallback to first available (same as default)
|
|
1252
|
+
return availableModels[0];
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* Find a slow/comprehensive model using the priority chain.
|
|
1257
|
+
* Prioritizes reasoning and codex models for thorough analysis.
|
|
1258
|
+
*
|
|
1259
|
+
* @param modelRegistry The model registry to search
|
|
1260
|
+
* @param savedModel Optional saved model string from settings (provider/modelId)
|
|
1261
|
+
* @returns The best available slow model, or undefined if none found
|
|
1262
|
+
*/
|
|
1263
|
+
export async function findSlowModel(
|
|
1264
|
+
modelRegistry: ModelLookupRegistry,
|
|
1265
|
+
savedModel?: string,
|
|
1266
|
+
): Promise<Model<Api> | undefined> {
|
|
1267
|
+
const availableModels = modelRegistry.getAvailable();
|
|
1268
|
+
if (availableModels.length === 0) return undefined;
|
|
1269
|
+
|
|
1270
|
+
// 1. Try saved model from settings
|
|
1271
|
+
if (savedModel) {
|
|
1272
|
+
const match = resolveModelFromString(savedModel, availableModels, undefined, modelRegistry);
|
|
1273
|
+
if (match) return match;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// 2. Try priority chain
|
|
1277
|
+
for (const pattern of MODEL_PRIO.slow) {
|
|
1278
|
+
// Try exact match first
|
|
1279
|
+
const exactMatch = parseModelPattern(pattern, availableModels, undefined, { modelRegistry }).model;
|
|
1280
|
+
if (exactMatch) return exactMatch;
|
|
1281
|
+
|
|
1282
|
+
// Try fuzzy match (substring)
|
|
1283
|
+
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase()));
|
|
1284
|
+
if (fuzzyMatch) return fuzzyMatch;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// 3. Fallback to first available (same as default)
|
|
1288
|
+
return availableModels[0];
|
|
1289
|
+
}
|