@oh-my-pi/pi-coding-agent 15.10.9 → 15.10.11
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 +117 -0
- package/dist/cli.js +23087 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +20 -219
- package/dist/types/config/model-resolver.d.ts +16 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +12 -16
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/debug/terminal-info.d.ts +0 -1
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +31 -26
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/task/discovery.d.ts +1 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/tiny/title-client.d.ts +1 -1
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +7 -12
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +69 -5
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +1 -1
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +308 -1025
- package/src/config/model-resolver.ts +113 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +18 -14
- package/src/config/settings.ts +37 -1
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/debug/terminal-info.ts +0 -3
- package/src/edit/diff.ts +95 -18
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +8 -2
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +2 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +49 -23
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +0 -2
- package/src/eval/py/kernel.ts +19 -0
- package/src/eval/py/runner.py +107 -3
- package/src/exec/bash-executor.ts +3 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/types.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +165 -17
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +2 -1
- package/src/mnemopi/backend.ts +25 -3
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +19 -13
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +1 -1
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +66 -54
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +373 -141
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +10 -3
- package/src/modes/controllers/event-controller.ts +73 -49
- package/src/modes/controllers/input-controller.ts +5 -5
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -5
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/interactive-mode.ts +5 -19
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +2 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +15 -26
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +8 -10
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +6 -2
- package/src/sdk.ts +23 -10
- package/src/session/agent-session.ts +44 -10
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +23 -2
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/ssh/connection-manager.ts +27 -0
- package/src/task/commands.ts +2 -1
- package/src/task/discovery.ts +17 -24
- package/src/task/executor.ts +61 -53
- package/src/task/index.ts +137 -60
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +2 -2
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +2 -1
- package/src/tiny/title-client.ts +32 -14
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +54 -13
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +3 -9
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +51 -12
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/file-mentions.ts +2 -1
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/anthropic.ts +8 -2
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
|
@@ -1,28 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Model resolution, scoping, and initial selection
|
|
2
|
+
* Model resolution, scoping, and initial selection.
|
|
3
|
+
*
|
|
4
|
+
* Layering:
|
|
5
|
+
* - `matchModel` is the single matching engine. Order: exact `provider/id`
|
|
6
|
+
* reference (with OpenRouter routed/date fallbacks) → exact canonical id →
|
|
7
|
+
* exact bare id → provider-scoped fuzzy → substring with alias-vs-dated pick.
|
|
8
|
+
* - `parseModelPatternWithContext`/`parseModelPattern` layer the selector
|
|
9
|
+
* grammar on top: trailing `:level` thinking suffixes (`splitThinkingSuffix`)
|
|
10
|
+
* and `@upstream` provider routing (`splitUpstreamRouting`).
|
|
11
|
+
* - Everything else (`resolveModelFromString`, `resolveModelOverride*`,
|
|
12
|
+
* `resolveRoleSelection`, `resolveModelScope`, `resolveCliModel`,
|
|
13
|
+
* `findSmolModel`/`findSlowModel`) adapts inputs — roles, settings patterns,
|
|
14
|
+
* CLI flags, scope globs — onto that pipeline.
|
|
3
15
|
*/
|
|
4
16
|
|
|
5
17
|
import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
modelsAreEqual,
|
|
14
|
-
} from "@oh-my-pi/pi-ai";
|
|
18
|
+
import type { Api, Effort, KnownProvider, Model, ModelSpec } from "@oh-my-pi/pi-ai";
|
|
19
|
+
import { buildModel } from "@oh-my-pi/pi-catalog/build";
|
|
20
|
+
import { modelMatchesHost } from "@oh-my-pi/pi-catalog/hosts";
|
|
21
|
+
import { buildModelProviderPriorityRank } from "@oh-my-pi/pi-catalog/identity";
|
|
22
|
+
import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
23
|
+
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
24
|
+
import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models";
|
|
15
25
|
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
16
26
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
17
27
|
import chalk from "chalk";
|
|
18
28
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
19
29
|
import { parseThinkingLevel, resolveThinkingLevelForModel } from "../thinking";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
30
|
+
import { isAuthenticated, kNoAuth, type ModelRegistry } from "./model-registry";
|
|
31
|
+
import { MODEL_ROLE_IDS, type ModelRole } from "./model-roles";
|
|
22
32
|
import type { Settings } from "./settings";
|
|
23
33
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Pick the first available model matching a known provider's default id
|
|
36
|
+
* (catalog table order), falling back to the first available model.
|
|
37
|
+
*/
|
|
38
|
+
function pickDefaultAvailableModel(availableModels: Model<Api>[]): Model<Api> | undefined {
|
|
39
|
+
for (const provider of Object.keys(DEFAULT_MODEL_PER_PROVIDER) as KnownProvider[]) {
|
|
40
|
+
const defaultId = DEFAULT_MODEL_PER_PROVIDER[provider];
|
|
41
|
+
const match = availableModels.find(m => m.provider === provider && m.id === defaultId);
|
|
42
|
+
if (match) return match;
|
|
43
|
+
}
|
|
44
|
+
return availableModels[0];
|
|
45
|
+
}
|
|
26
46
|
|
|
27
47
|
export interface ScopedModel {
|
|
28
48
|
model: Model<Api>;
|
|
@@ -30,6 +50,22 @@ export interface ScopedModel {
|
|
|
30
50
|
explicitThinkingLevel: boolean;
|
|
31
51
|
}
|
|
32
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Split a trailing `:<level>` thinking selector off a model pattern.
|
|
55
|
+
*
|
|
56
|
+
* `level` is set only when the suffix parses as a valid thinking level, in
|
|
57
|
+
* which case `base` has the suffix stripped; otherwise `base` is the input.
|
|
58
|
+
* `minColonIndex` requires the colon to appear strictly after that index —
|
|
59
|
+
* role-alias callers pass `PREFIX_MODEL_ROLE.length` so the base is at least
|
|
60
|
+
* as long as the `pi/` prefix.
|
|
61
|
+
*/
|
|
62
|
+
function splitThinkingSuffix(pattern: string, minColonIndex = -1): { base: string; level?: ThinkingLevel } {
|
|
63
|
+
const colonIdx = pattern.lastIndexOf(":");
|
|
64
|
+
if (colonIdx <= minColonIndex) return { base: pattern };
|
|
65
|
+
const level = parseThinkingLevel(pattern.slice(colonIdx + 1));
|
|
66
|
+
return level ? { base: pattern.slice(0, colonIdx), level } : { base: pattern };
|
|
67
|
+
}
|
|
68
|
+
|
|
33
69
|
/**
|
|
34
70
|
* Parse a model string in "provider/modelId" format.
|
|
35
71
|
* Returns undefined if the format is invalid.
|
|
@@ -42,15 +78,8 @@ export function parseModelString(
|
|
|
42
78
|
const id = modelStr.slice(slashIdx + 1);
|
|
43
79
|
const provider = modelStr.slice(0, slashIdx);
|
|
44
80
|
// Strip valid thinking level suffix (e.g., "claude-sonnet-4-6:high" -> id "claude-sonnet-4-6", thinkingLevel "high")
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
const suffix = id.slice(colonIdx + 1);
|
|
48
|
-
const thinkingLevel = parseThinkingLevel(suffix);
|
|
49
|
-
if (thinkingLevel) {
|
|
50
|
-
return { provider, id: id.slice(0, colonIdx), thinkingLevel };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return { provider, id };
|
|
81
|
+
const { base, level } = splitThinkingSuffix(id);
|
|
82
|
+
return level ? { provider, id: base, thinkingLevel: level } : { provider, id };
|
|
54
83
|
}
|
|
55
84
|
|
|
56
85
|
/**
|
|
@@ -142,17 +171,19 @@ function splitUpstreamRouting(pattern: string): { base: string; upstream: string
|
|
|
142
171
|
|
|
143
172
|
/** OpenRouter and Vercel AI Gateway are the aggregators that honor per-request upstream routing. */
|
|
144
173
|
function supportsUpstreamRouting(model: Model<Api>): boolean {
|
|
145
|
-
return model
|
|
174
|
+
return modelMatchesHost(model, "openrouter") || modelMatchesHost(model, "vercelAIGateway");
|
|
146
175
|
}
|
|
147
176
|
|
|
148
177
|
/** Pin a resolved aggregator model to a single upstream provider via its compat routing block. */
|
|
149
178
|
function applyUpstreamRouting(model: Model<Api>, upstream: string): Model<Api> {
|
|
150
179
|
const aggregatorModel = model as Model<"openai-completions">;
|
|
151
180
|
const routing = { only: [upstream] };
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
:
|
|
155
|
-
|
|
181
|
+
return buildModel({
|
|
182
|
+
...model,
|
|
183
|
+
compat: modelMatchesHost(model, "vercelAIGateway")
|
|
184
|
+
? { ...aggregatorModel.compatConfig, vercelGatewayRouting: routing }
|
|
185
|
+
: { ...aggregatorModel.compatConfig, openRouterRouting: routing },
|
|
186
|
+
} as ModelSpec<Api>);
|
|
156
187
|
}
|
|
157
188
|
|
|
158
189
|
const kProviderModelIndex = Symbol("model-resolver.providerIndex");
|
|
@@ -339,10 +370,7 @@ function isAlias(id: string): boolean {
|
|
|
339
370
|
* Find an exact explicit provider/model match.
|
|
340
371
|
* Bare model ids are handled separately so canonical ids can coalesce variants.
|
|
341
372
|
*/
|
|
342
|
-
|
|
343
|
-
modelReference: string,
|
|
344
|
-
availableModels: Model<Api>[],
|
|
345
|
-
): Model<Api> | undefined {
|
|
373
|
+
function findExactModelReferenceMatch(modelReference: string, availableModels: Model<Api>[]): Model<Api> | undefined {
|
|
346
374
|
const trimmedReference = modelReference.trim();
|
|
347
375
|
if (!trimmedReference) {
|
|
348
376
|
return undefined;
|
|
@@ -378,10 +406,15 @@ function findExactCanonicalModelMatch(
|
|
|
378
406
|
}
|
|
379
407
|
|
|
380
408
|
/**
|
|
381
|
-
*
|
|
409
|
+
* The single model-matching engine. Tries, in order:
|
|
410
|
+
* 1. exact `provider/id` reference (OpenRouter routed/date fallbacks included),
|
|
411
|
+
* 2. exact canonical id (coalesces provider variants),
|
|
412
|
+
* 3. exact bare id (preference-ranked),
|
|
413
|
+
* 4. provider-scoped fuzzy match,
|
|
414
|
+
* 5. substring match with the alias-vs-dated pick.
|
|
382
415
|
* Returns the matched model or undefined if no match found.
|
|
383
416
|
*/
|
|
384
|
-
function
|
|
417
|
+
function matchModel(
|
|
385
418
|
modelPattern: string,
|
|
386
419
|
availableModels: Model<Api>[],
|
|
387
420
|
context: ModelPreferenceContext,
|
|
@@ -505,31 +538,21 @@ function parseModelPatternWithContext(
|
|
|
505
538
|
options?: { allowInvalidThinkingSelectorFallback?: boolean; modelRegistry?: CanonicalModelRegistry },
|
|
506
539
|
): ParsedModelResult {
|
|
507
540
|
// Try exact match first
|
|
508
|
-
const exactMatch =
|
|
541
|
+
const exactMatch = matchModel(pattern, availableModels, context, options);
|
|
509
542
|
if (exactMatch) {
|
|
510
543
|
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
511
544
|
}
|
|
512
545
|
|
|
513
|
-
// No match - try
|
|
514
|
-
const
|
|
515
|
-
if (
|
|
516
|
-
|
|
517
|
-
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const prefix = pattern.substring(0, lastColonIndex);
|
|
521
|
-
const suffix = pattern.substring(lastColonIndex + 1);
|
|
522
|
-
|
|
523
|
-
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
524
|
-
if (parsedThinkingLevel) {
|
|
525
|
-
// Valid thinking level - recurse on prefix and use this level
|
|
526
|
-
const result = parseModelPatternWithContext(prefix, availableModels, context, options);
|
|
546
|
+
// No match - try stripping a valid thinking suffix and recursing
|
|
547
|
+
const { base, level } = splitThinkingSuffix(pattern);
|
|
548
|
+
if (level) {
|
|
549
|
+
const result = parseModelPatternWithContext(base, availableModels, context, options);
|
|
527
550
|
if (result.model) {
|
|
528
551
|
// Only use this thinking level if no warning from inner recursion
|
|
529
552
|
const explicitThinkingLevel = !result.warning;
|
|
530
553
|
return {
|
|
531
554
|
model: result.model,
|
|
532
|
-
thinkingLevel: explicitThinkingLevel ?
|
|
555
|
+
thinkingLevel: explicitThinkingLevel ? level : undefined,
|
|
533
556
|
warning: result.warning,
|
|
534
557
|
explicitThinkingLevel,
|
|
535
558
|
};
|
|
@@ -537,6 +560,14 @@ function parseModelPatternWithContext(
|
|
|
537
560
|
return result;
|
|
538
561
|
}
|
|
539
562
|
|
|
563
|
+
const lastColonIndex = pattern.lastIndexOf(":");
|
|
564
|
+
if (lastColonIndex === -1) {
|
|
565
|
+
// No colons, pattern simply doesn't match any model
|
|
566
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
567
|
+
}
|
|
568
|
+
const prefix = pattern.substring(0, lastColonIndex);
|
|
569
|
+
const suffix = pattern.substring(lastColonIndex + 1);
|
|
570
|
+
|
|
540
571
|
const allowFallback = options?.allowInvalidThinkingSelectorFallback ?? true;
|
|
541
572
|
if (!allowFallback) {
|
|
542
573
|
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
@@ -606,10 +637,7 @@ function resolveConfiguredRolePattern(value: string, settings?: Settings): strin
|
|
|
606
637
|
const normalized = value.trim();
|
|
607
638
|
if (!normalized) return undefined;
|
|
608
639
|
|
|
609
|
-
const
|
|
610
|
-
const thinkingLevel =
|
|
611
|
-
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(normalized.slice(lastColonIndex + 1)) : undefined;
|
|
612
|
-
const aliasCandidate = thinkingLevel ? normalized.slice(0, lastColonIndex) : normalized;
|
|
640
|
+
const { base: aliasCandidate, level: thinkingLevel } = splitThinkingSuffix(normalized, PREFIX_MODEL_ROLE.length);
|
|
613
641
|
const role = getModelRoleAlias(aliasCandidate);
|
|
614
642
|
if (!role) return [normalized];
|
|
615
643
|
|
|
@@ -695,7 +723,7 @@ export function resolveModelRoleValue(
|
|
|
695
723
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
696
724
|
}
|
|
697
725
|
|
|
698
|
-
const effectivePatterns =
|
|
726
|
+
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.settings);
|
|
699
727
|
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
700
728
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
701
729
|
}
|
|
@@ -736,9 +764,7 @@ export function extractExplicitThinkingSelector(
|
|
|
736
764
|
let current = normalized;
|
|
737
765
|
while (!visited.has(current)) {
|
|
738
766
|
visited.add(current);
|
|
739
|
-
const
|
|
740
|
-
const thinkingSelector =
|
|
741
|
-
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(current.slice(lastColonIndex + 1)) : undefined;
|
|
767
|
+
const thinkingSelector = splitThinkingSuffix(current, PREFIX_MODEL_ROLE.length).level;
|
|
742
768
|
if (thinkingSelector) {
|
|
743
769
|
return thinkingSelector;
|
|
744
770
|
}
|
|
@@ -903,20 +929,8 @@ function resolveExactCanonicalScopePattern(
|
|
|
903
929
|
modelRegistry: Pick<ModelRegistry, "getCanonicalVariants">,
|
|
904
930
|
availableModels: Model<Api>[],
|
|
905
931
|
): { models: Model<Api>[]; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } | undefined {
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
let thinkingLevel: ThinkingLevel | undefined;
|
|
909
|
-
let explicitThinkingLevel = false;
|
|
910
|
-
|
|
911
|
-
if (lastColonIndex !== -1) {
|
|
912
|
-
const suffix = pattern.substring(lastColonIndex + 1);
|
|
913
|
-
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
914
|
-
if (parsedThinkingLevel) {
|
|
915
|
-
canonicalId = pattern.substring(0, lastColonIndex);
|
|
916
|
-
thinkingLevel = parsedThinkingLevel;
|
|
917
|
-
explicitThinkingLevel = true;
|
|
918
|
-
}
|
|
919
|
-
}
|
|
932
|
+
const { base: canonicalId, level: thinkingLevel } = splitThinkingSuffix(pattern);
|
|
933
|
+
const explicitThinkingLevel = thinkingLevel !== undefined;
|
|
920
934
|
|
|
921
935
|
const variants = modelRegistry
|
|
922
936
|
.getCanonicalVariants(canonicalId, { availableOnly: true, candidates: availableModels })
|
|
@@ -947,25 +961,23 @@ export async function resolveModelScope(
|
|
|
947
961
|
const availableModels = modelRegistry.getAvailable();
|
|
948
962
|
const context = buildPreferenceContext(availableModels, preferences);
|
|
949
963
|
const scopedModels: ScopedModel[] = [];
|
|
964
|
+
const addScopedModel = (model: Model<Api>, thinkingLevel: ThinkingLevel | undefined, explicit: boolean) => {
|
|
965
|
+
if (scopedModels.some(sm => modelsAreEqual(sm.model, model))) return;
|
|
966
|
+
scopedModels.push({
|
|
967
|
+
model,
|
|
968
|
+
thinkingLevel: explicit
|
|
969
|
+
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
970
|
+
: thinkingLevel,
|
|
971
|
+
explicitThinkingLevel: explicit,
|
|
972
|
+
});
|
|
973
|
+
};
|
|
950
974
|
|
|
951
975
|
for (const pattern of patterns) {
|
|
952
976
|
// Check if pattern contains glob characters
|
|
953
977
|
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("[")) {
|
|
954
978
|
// Extract optional thinking level suffix (e.g., "provider/*:high")
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
let thinkingLevel: ThinkingLevel | undefined;
|
|
958
|
-
let explicitThinkingLevel = false;
|
|
959
|
-
|
|
960
|
-
if (colonIdx !== -1) {
|
|
961
|
-
const suffix = pattern.substring(colonIdx + 1);
|
|
962
|
-
const parsedThinkingLevel = parseThinkingLevel(suffix);
|
|
963
|
-
if (parsedThinkingLevel) {
|
|
964
|
-
thinkingLevel = parsedThinkingLevel;
|
|
965
|
-
explicitThinkingLevel = true;
|
|
966
|
-
globPattern = pattern.substring(0, colonIdx);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
979
|
+
const { base: globPattern, level: thinkingLevel } = splitThinkingSuffix(pattern);
|
|
980
|
+
const explicitThinkingLevel = thinkingLevel !== undefined;
|
|
969
981
|
|
|
970
982
|
// Match against "provider/modelId" format OR just model ID
|
|
971
983
|
// This allows "*sonnet*" to match without requiring "anthropic/*sonnet*"
|
|
@@ -981,15 +993,7 @@ export async function resolveModelScope(
|
|
|
981
993
|
}
|
|
982
994
|
|
|
983
995
|
for (const model of matchingModels) {
|
|
984
|
-
|
|
985
|
-
scopedModels.push({
|
|
986
|
-
model,
|
|
987
|
-
thinkingLevel: explicitThinkingLevel
|
|
988
|
-
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
989
|
-
: thinkingLevel,
|
|
990
|
-
explicitThinkingLevel,
|
|
991
|
-
});
|
|
992
|
-
}
|
|
996
|
+
addScopedModel(model, thinkingLevel, explicitThinkingLevel);
|
|
993
997
|
}
|
|
994
998
|
continue;
|
|
995
999
|
}
|
|
@@ -997,16 +1001,7 @@ export async function resolveModelScope(
|
|
|
997
1001
|
const exactCanonical = resolveExactCanonicalScopePattern(pattern, modelRegistry, availableModels);
|
|
998
1002
|
if (exactCanonical) {
|
|
999
1003
|
for (const model of exactCanonical.models) {
|
|
1000
|
-
|
|
1001
|
-
scopedModels.push({
|
|
1002
|
-
model,
|
|
1003
|
-
thinkingLevel: exactCanonical.explicitThinkingLevel
|
|
1004
|
-
? (resolveThinkingLevelForModel(model, exactCanonical.thinkingLevel) ??
|
|
1005
|
-
exactCanonical.thinkingLevel)
|
|
1006
|
-
: exactCanonical.thinkingLevel,
|
|
1007
|
-
explicitThinkingLevel: exactCanonical.explicitThinkingLevel,
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1004
|
+
addScopedModel(model, exactCanonical.thinkingLevel, exactCanonical.explicitThinkingLevel);
|
|
1010
1005
|
}
|
|
1011
1006
|
continue;
|
|
1012
1007
|
}
|
|
@@ -1027,16 +1022,7 @@ export async function resolveModelScope(
|
|
|
1027
1022
|
continue;
|
|
1028
1023
|
}
|
|
1029
1024
|
|
|
1030
|
-
|
|
1031
|
-
if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) {
|
|
1032
|
-
scopedModels.push({
|
|
1033
|
-
model,
|
|
1034
|
-
thinkingLevel: explicitThinkingLevel
|
|
1035
|
-
? (resolveThinkingLevelForModel(model, thinkingLevel) ?? thinkingLevel)
|
|
1036
|
-
: thinkingLevel,
|
|
1037
|
-
explicitThinkingLevel,
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1025
|
+
addScopedModel(model, thinkingLevel, explicitThinkingLevel);
|
|
1040
1026
|
}
|
|
1041
1027
|
|
|
1042
1028
|
return scopedModels;
|
|
@@ -1127,14 +1113,11 @@ export function resolveCliModel(options: {
|
|
|
1127
1113
|
// provider+id match over flat id match. Without this, a model with id
|
|
1128
1114
|
// "zai/glm-5" on provider "vercel-ai-gateway" wins over provider "zai"
|
|
1129
1115
|
// with id "glm-5", because Array.find returns the first catalog hit.
|
|
1130
|
-
|
|
1131
|
-
let exact: (typeof availableModels)[number] | undefined;
|
|
1132
|
-
if (slashIdx !== -1) {
|
|
1133
|
-
const prefix = lower.substring(0, slashIdx);
|
|
1134
|
-
const suffix = trimmedModel.substring(slashIdx + 1);
|
|
1135
|
-
exact = resolveProviderModelReference(prefix, suffix, availableModels);
|
|
1136
|
-
}
|
|
1116
|
+
let exact = findExactModelReferenceMatch(trimmedModel, availableModels);
|
|
1137
1117
|
if (!exact && !trimmedModel.includes(":")) {
|
|
1118
|
+
// CLI flags address the full catalog, so unlike the engine's canonical
|
|
1119
|
+
// step this lookup is unrestricted; the `:`-guard defers suffixed
|
|
1120
|
+
// selectors (thinking levels, ollama-style ids) to the grammar below.
|
|
1138
1121
|
const canonicalMatch = modelRegistry.resolveCanonicalModel?.(trimmedModel, { availableOnly: false });
|
|
1139
1122
|
if (canonicalMatch) {
|
|
1140
1123
|
return {
|
|
@@ -1147,6 +1130,8 @@ export function resolveCliModel(options: {
|
|
|
1147
1130
|
}
|
|
1148
1131
|
}
|
|
1149
1132
|
if (!exact) {
|
|
1133
|
+
// Flat exact id (or full selector) by catalog order: CLI resolution
|
|
1134
|
+
// stays deterministic across runs regardless of usage-based ranking.
|
|
1150
1135
|
exact = availableModels.find(
|
|
1151
1136
|
model => model.id.toLowerCase() === lower || `${model.provider}/${model.id}`.toLowerCase() === lower,
|
|
1152
1137
|
);
|
|
@@ -1213,11 +1198,7 @@ export function resolveCliModel(options: {
|
|
|
1213
1198
|
|
|
1214
1199
|
let selector = provider ? formatModelString(model) : undefined;
|
|
1215
1200
|
if (!provider) {
|
|
1216
|
-
const
|
|
1217
|
-
const canonicalCandidate =
|
|
1218
|
-
lastColonIndex !== -1 && parseThinkingLevel(pattern.substring(lastColonIndex + 1))
|
|
1219
|
-
? pattern.substring(0, lastColonIndex)
|
|
1220
|
-
: pattern;
|
|
1201
|
+
const canonicalCandidate = splitThinkingSuffix(pattern).base;
|
|
1221
1202
|
if (!canonicalCandidate.includes("/")) {
|
|
1222
1203
|
const canonicalResolved = modelRegistry.resolveCanonicalModel?.(canonicalCandidate, { availableOnly: false });
|
|
1223
1204
|
if (canonicalResolved && canonicalResolved.provider === model.provider && canonicalResolved.id === model.id) {
|
|
@@ -1316,18 +1297,9 @@ export async function findInitialModel(options: {
|
|
|
1316
1297
|
// 4. Try first available model with valid API key
|
|
1317
1298
|
const availableModels = modelRegistry.getAvailable();
|
|
1318
1299
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
const defaultId = defaultModelPerProvider[provider];
|
|
1323
|
-
const match = availableModels.find(m => m.provider === provider && m.id === defaultId);
|
|
1324
|
-
if (match) {
|
|
1325
|
-
return { model: match, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
// If no default found, use first available
|
|
1330
|
-
return { model: availableModels[0], thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1300
|
+
const fallback = pickDefaultAvailableModel(availableModels);
|
|
1301
|
+
if (fallback) {
|
|
1302
|
+
return { model: fallback, thinkingLevel: undefined, fallbackMessage: undefined };
|
|
1331
1303
|
}
|
|
1332
1304
|
|
|
1333
1305
|
// 5. No model found
|
|
@@ -1377,23 +1349,8 @@ export async function restoreModelFromSession(
|
|
|
1377
1349
|
// Try to find any available model
|
|
1378
1350
|
const availableModels = modelRegistry.getAvailable();
|
|
1379
1351
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
let fallbackModel: Model<Api> | undefined;
|
|
1383
|
-
for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {
|
|
1384
|
-
const defaultId = defaultModelPerProvider[provider];
|
|
1385
|
-
const match = availableModels.find(m => m.provider === provider && m.id === defaultId);
|
|
1386
|
-
if (match) {
|
|
1387
|
-
fallbackModel = match;
|
|
1388
|
-
break;
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
// If no default found, use first available
|
|
1393
|
-
if (!fallbackModel) {
|
|
1394
|
-
fallbackModel = availableModels[0];
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1352
|
+
const fallbackModel = pickDefaultAvailableModel(availableModels);
|
|
1353
|
+
if (fallbackModel) {
|
|
1397
1354
|
if (shouldPrintMessages) {
|
|
1398
1355
|
console.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`));
|
|
1399
1356
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in model roles and role metadata helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
|
6
|
+
import type { Settings } from "./settings";
|
|
7
|
+
|
|
8
|
+
export type ModelRole = "default" | "smol" | "slow" | "vision" | "plan" | "designer" | "commit" | "task";
|
|
9
|
+
|
|
10
|
+
export interface ModelRoleInfo {
|
|
11
|
+
tag?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
color?: ThemeColor;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const MODEL_ROLES: Record<ModelRole, ModelRoleInfo> = {
|
|
17
|
+
default: { tag: "DEFAULT", name: "Default", color: "success" },
|
|
18
|
+
smol: { tag: "SMOL", name: "Fast", color: "warning" },
|
|
19
|
+
slow: { tag: "SLOW", name: "Thinking", color: "accent" },
|
|
20
|
+
vision: { tag: "VISION", name: "Vision", color: "error" },
|
|
21
|
+
plan: { tag: "PLAN", name: "Architect", color: "muted" },
|
|
22
|
+
designer: { tag: "DESIGNER", name: "Designer", color: "muted" },
|
|
23
|
+
commit: { tag: "COMMIT", name: "Commit", color: "dim" },
|
|
24
|
+
task: { tag: "TASK", name: "Subtask", color: "muted" },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const MODEL_ROLE_IDS: ModelRole[] = ["default", "smol", "slow", "vision", "plan", "designer", "commit", "task"];
|
|
28
|
+
|
|
29
|
+
/** Alias for ModelRoleInfo - used for both built-in and custom roles */
|
|
30
|
+
export type RoleInfo = ModelRoleInfo;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return the canonical set of known roles for selector/carousel UI.
|
|
34
|
+
*
|
|
35
|
+
* Built-ins always come first. Configured cycle order, model assignments, and
|
|
36
|
+
* tag metadata can introduce additional custom roles without requiring duplicate
|
|
37
|
+
* entries across settings.
|
|
38
|
+
*/
|
|
39
|
+
export function getKnownRoleIds(settings: Settings): string[] {
|
|
40
|
+
const roles = [...MODEL_ROLE_IDS] as string[];
|
|
41
|
+
const seen = new Set<string>(roles);
|
|
42
|
+
const addRole = (role: string) => {
|
|
43
|
+
if (seen.has(role)) return;
|
|
44
|
+
seen.add(role);
|
|
45
|
+
roles.push(role);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
for (const role of settings.get("cycleOrder")) addRole(role);
|
|
49
|
+
for (const role in settings.getModelRoles()) addRole(role);
|
|
50
|
+
for (const role in settings.get("modelTags")) addRole(role);
|
|
51
|
+
|
|
52
|
+
return roles;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get role info for a role name (built-in or custom).
|
|
57
|
+
* Configured metadata overrides built-in defaults when present.
|
|
58
|
+
*/
|
|
59
|
+
export function getRoleInfo(role: string, settings: Settings): RoleInfo {
|
|
60
|
+
const builtIn = role in MODEL_ROLES ? MODEL_ROLES[role as ModelRole] : undefined;
|
|
61
|
+
const configured = settings.get("modelTags")[role];
|
|
62
|
+
|
|
63
|
+
if (configured) {
|
|
64
|
+
return {
|
|
65
|
+
tag: builtIn?.tag,
|
|
66
|
+
name: configured.name || builtIn?.name || role,
|
|
67
|
+
color: configured.color && isValidThemeColor(configured.color) ? configured.color : builtIn?.color,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (builtIn) return builtIn;
|
|
72
|
+
|
|
73
|
+
return { name: role, color: "muted" };
|
|
74
|
+
}
|
|
@@ -18,7 +18,7 @@ const ReasoningEffortMapSchema = z.object({
|
|
|
18
18
|
xhigh: z.string().optional(),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
const OpenAICompatFieldsSchema = z.object({
|
|
22
22
|
supportsStore: z.boolean().optional(),
|
|
23
23
|
supportsDeveloperRole: z.boolean().optional(),
|
|
24
24
|
supportsMultipleSystemMessages: z.boolean().optional(),
|
|
@@ -44,6 +44,18 @@ export const OpenAICompatSchema = z.object({
|
|
|
44
44
|
cacheControlFormat: z.enum(["anthropic"]).optional(),
|
|
45
45
|
supportsStrictMode: z.boolean().optional(),
|
|
46
46
|
toolStrictMode: z.enum(["all_strict", "none"]).optional(),
|
|
47
|
+
streamIdleTimeoutMs: z.number().positive().optional(),
|
|
48
|
+
supportsLongPromptCacheRetention: z.boolean().optional(),
|
|
49
|
+
supportsReasoningParams: z.boolean().optional(),
|
|
50
|
+
alwaysSendMaxTokens: z.boolean().optional(),
|
|
51
|
+
strictResponsesPairing: z.boolean().optional(),
|
|
52
|
+
// anthropic-messages compat flags (same `compat` slot, per-api interpretation)
|
|
53
|
+
requiresToolResultId: z.boolean().optional(),
|
|
54
|
+
replayUnsignedThinking: z.boolean().optional(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const OpenAICompatSchema = OpenAICompatFieldsSchema.extend({
|
|
58
|
+
whenThinking: OpenAICompatFieldsSchema.optional(),
|
|
47
59
|
});
|
|
48
60
|
|
|
49
61
|
const EffortSchema = z.enum(["minimal", "low", "medium", "high", "xhigh"]);
|
|
@@ -56,13 +68,50 @@ const ThinkingControlModeSchema = z.enum([
|
|
|
56
68
|
"anthropic-budget-effort",
|
|
57
69
|
]);
|
|
58
70
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
const EFFORT_ORDER = ["minimal", "low", "medium", "high", "xhigh"] as const;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Accepts the canonical `efforts` vocabulary plus the legacy
|
|
75
|
+
* `minLevel`/`maxLevel`/`levels` range shape, normalizing both to
|
|
76
|
+
* `ThinkingConfig` (ordered `efforts`, never empty). Precedence mirrors the
|
|
77
|
+
* old runtime: explicit `levels` beat the min..max range; `efforts` beats both.
|
|
78
|
+
*/
|
|
79
|
+
const ModelThinkingSchema = z
|
|
80
|
+
.object({
|
|
81
|
+
mode: ThinkingControlModeSchema,
|
|
82
|
+
efforts: z.array(EffortSchema).min(1).optional(),
|
|
83
|
+
defaultLevel: EffortSchema.optional(),
|
|
84
|
+
effortMap: ReasoningEffortMapSchema.optional(),
|
|
85
|
+
supportsDisplay: z.boolean().optional(),
|
|
86
|
+
// Legacy range vocabulary (pre-efforts configs).
|
|
87
|
+
minLevel: EffortSchema.optional(),
|
|
88
|
+
maxLevel: EffortSchema.optional(),
|
|
89
|
+
levels: z.array(EffortSchema).min(1).optional(),
|
|
90
|
+
})
|
|
91
|
+
.refine(
|
|
92
|
+
value =>
|
|
93
|
+
value.efforts !== undefined ||
|
|
94
|
+
value.levels !== undefined ||
|
|
95
|
+
(value.minLevel !== undefined && value.maxLevel !== undefined),
|
|
96
|
+
{
|
|
97
|
+
message: "thinking requires `efforts` (or legacy `levels`/`minLevel`+`maxLevel`)",
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
.transform(({ efforts, levels, minLevel, maxLevel, mode, defaultLevel, effortMap, supportsDisplay }) => {
|
|
101
|
+
let resolved = efforts ?? levels;
|
|
102
|
+
if (!resolved) {
|
|
103
|
+
const minIndex = EFFORT_ORDER.indexOf(minLevel!);
|
|
104
|
+
const maxIndex = EFFORT_ORDER.indexOf(maxLevel!);
|
|
105
|
+
resolved = EFFORT_ORDER.slice(minIndex, Math.max(minIndex, maxIndex) + 1);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
mode,
|
|
109
|
+
efforts: resolved,
|
|
110
|
+
...(defaultLevel !== undefined && { defaultLevel }),
|
|
111
|
+
...(effortMap !== undefined && { effortMap }),
|
|
112
|
+
...(supportsDisplay !== undefined && { supportsDisplay }),
|
|
113
|
+
};
|
|
114
|
+
});
|
|
66
115
|
|
|
67
116
|
const ModelDefinitionSchema = z.object({
|
|
68
117
|
id: z.string().min(1),
|