@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.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 +128 -0
- package/dist/cli.js +4006 -3966
- package/dist/types/advisor/advise-tool.d.ts +5 -2
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/cli-commands.d.ts +14 -1
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +32 -7
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
- package/dist/types/lib/xai-http.d.ts +11 -0
- package/dist/types/mcp/transports/stdio.d.ts +13 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -2
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +23 -1
- package/dist/types/modes/components/hook-editor.d.ts +16 -2
- package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +4 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/sdk.d.ts +13 -2
- package/dist/types/session/agent-session.d.ts +43 -11
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +14 -0
- package/dist/types/session/streaming-output.d.ts +2 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/slash-commands/types.d.ts +4 -4
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/browser/registry.d.ts +7 -3
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/manage-skill.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +2 -0
- package/dist/types/web/search/providers/base.d.ts +3 -0
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1358 -86
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/advise-tool.ts +7 -3
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/bench-cli.ts +4 -1
- package/src/cli/grep-cli.ts +2 -1
- package/src/cli-commands.ts +57 -23
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +34 -6
- package/src/config/model-resolver.ts +57 -12
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +43 -6
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/codex.ts +14 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/discovery/substitute-plugin-root.ts +23 -7
- package/src/exec/bash-executor.ts +14 -5
- package/src/export/html/template.js +2 -0
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
- package/src/internal-urls/agent-protocol.ts +73 -39
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lib/xai-http.ts +28 -2
- package/src/lsp/client.ts +1 -1
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/transports/stdio.ts +103 -23
- package/src/modes/acp/acp-agent.ts +28 -9
- package/src/modes/acp/acp-event-mapper.ts +8 -0
- package/src/modes/acp/acp-mode.ts +18 -4
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/assistant-message.ts +35 -14
- package/src/modes/components/chat-transcript-builder.ts +2 -0
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
- package/src/modes/components/custom-editor.ts +62 -1
- package/src/modes/components/hook-editor.ts +35 -5
- package/src/modes/components/login-dialog.test.ts +56 -0
- package/src/modes/components/login-dialog.ts +7 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/plan-review-overlay.ts +51 -10
- package/src/modes/components/session-selector.ts +10 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/component.ts +2 -4
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/components/tool-execution.test.ts +101 -0
- package/src/modes/components/tool-execution.ts +117 -9
- package/src/modes/components/transcript-container.ts +30 -2
- package/src/modes/controllers/command-controller-shared.ts +1 -1
- package/src/modes/controllers/command-controller.ts +168 -48
- package/src/modes/controllers/event-controller.ts +6 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +208 -46
- package/src/modes/interactive-mode.ts +205 -65
- package/src/modes/noninteractive-dispose.test.ts +60 -0
- package/src/modes/print-mode.ts +10 -5
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +31 -7
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/types.ts +10 -0
- package/src/modes/utils/interactive-context-helpers.ts +3 -1
- package/src/modes/utils/transcript-render-helpers.ts +3 -2
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/sdk.ts +227 -44
- package/src/session/agent-session.ts +1364 -382
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-listing.ts +6 -5
- package/src/session/session-loader.ts +9 -0
- package/src/session/session-manager.ts +48 -0
- package/src/session/session-persistence.ts +11 -0
- package/src/session/streaming-output.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +9 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/slash-commands/types.ts +4 -4
- package/src/system-prompt.ts +2 -2
- package/src/task/executor.ts +1 -1
- package/src/task/label.ts +2 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/browser/registry.ts +9 -2
- package/src/tools/browser/tab-supervisor.ts +65 -10
- package/src/tools/browser.ts +4 -3
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/eval.ts +3 -0
- package/src/tools/gh.ts +169 -2
- package/src/tools/image-gen.ts +624 -513
- package/src/tools/index.ts +10 -6
- package/src/tools/manage-skill.ts +5 -3
- package/src/tools/output-meta.ts +7 -0
- package/src/tools/path-utils.ts +8 -5
- package/src/tools/read.ts +48 -3
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/utils/title-generator.ts +15 -4
- package/src/web/search/index.ts +21 -6
- package/src/web/search/providers/base.ts +3 -0
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/providers/xai.ts +40 -9
- package/src/web/search/types.ts +6 -1
|
@@ -41,11 +41,13 @@ export function buildAutoLearnInstructions(available: { manageSkill: boolean; le
|
|
|
41
41
|
export interface AutoLearnControllerOptions {
|
|
42
42
|
session: AgentSession;
|
|
43
43
|
settings: Settings;
|
|
44
|
+
capture: (content: string) => Promise<void>;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export class AutoLearnController {
|
|
47
48
|
readonly #session: AgentSession;
|
|
48
49
|
readonly #settings: Settings;
|
|
50
|
+
readonly #capture: (content: string) => Promise<void>;
|
|
49
51
|
#toolCalls = 0;
|
|
50
52
|
/**
|
|
51
53
|
* Whether the in-flight turn BEGAN while goal mode was active. Captured at
|
|
@@ -54,12 +56,15 @@ export class AutoLearnController {
|
|
|
54
56
|
* would let a goal-continuation turn slip through and get nudged.
|
|
55
57
|
*/
|
|
56
58
|
#turnStartedInGoalMode = false;
|
|
57
|
-
/**
|
|
58
|
-
#
|
|
59
|
+
/** Prevent overlapping private capture runs while real primary turns continue. */
|
|
60
|
+
#captureInFlight = false;
|
|
61
|
+
/** One newer eligible primary stop arrived while capture was running. */
|
|
62
|
+
#capturePending = false;
|
|
59
63
|
|
|
60
64
|
constructor(options: AutoLearnControllerOptions) {
|
|
61
65
|
this.#session = options.session;
|
|
62
66
|
this.#settings = options.settings;
|
|
67
|
+
this.#capture = options.capture;
|
|
63
68
|
// The listener closure captures `this`, so the session's listener array
|
|
64
69
|
// keeps the controller alive — no stored unsubscribe needed.
|
|
65
70
|
this.#session.subscribe(event => this.#onEvent(event));
|
|
@@ -91,10 +96,6 @@ export class AutoLearnController {
|
|
|
91
96
|
const startedInGoalMode = this.#turnStartedInGoalMode;
|
|
92
97
|
this.#turnStartedInGoalMode = false;
|
|
93
98
|
|
|
94
|
-
if (this.#suppressNext) {
|
|
95
|
-
this.#suppressNext = false;
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
99
|
// Never nudge a turn that ended in an abort (ESC, cancel, etc.). The
|
|
99
100
|
// abort flag on the session is unreliable by the time agent_end is
|
|
100
101
|
// deferred to subscribers; read stopReason from the event messages.
|
|
@@ -128,30 +129,24 @@ export class AutoLearnController {
|
|
|
128
129
|
const autoContinue = this.#settings.get("autolearn.autoContinue") === true;
|
|
129
130
|
if (!autoContinue) return;
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this.#suppressNext = true;
|
|
132
|
+
if (this.#captureInFlight) {
|
|
133
|
+
this.#capturePending = true;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.#startCapture();
|
|
137
|
+
}
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
customType: "autolearn-nudge",
|
|
143
|
-
content,
|
|
144
|
-
display: false,
|
|
145
|
-
attribution: "user",
|
|
146
|
-
},
|
|
147
|
-
{ deliverAs: "nextTurn", triggerTurn: true, acceptTerminalEmptyStop: true },
|
|
148
|
-
)
|
|
149
|
-
.then(started => {
|
|
150
|
-
if (!started) this.#suppressNext = false;
|
|
151
|
-
})
|
|
139
|
+
#startCapture(): void {
|
|
140
|
+
this.#captureInFlight = true;
|
|
141
|
+
void this.#capture(AUTOLEARN_NUDGE_AUTOCONTINUE)
|
|
152
142
|
.catch(err => {
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
logger.warn("auto-learn capture failed", { err });
|
|
144
|
+
})
|
|
145
|
+
.finally(() => {
|
|
146
|
+
this.#captureInFlight = false;
|
|
147
|
+
if (!this.#capturePending) return;
|
|
148
|
+
this.#capturePending = false;
|
|
149
|
+
this.#startCapture();
|
|
155
150
|
});
|
|
156
151
|
}
|
|
157
152
|
}
|
package/src/cli/bench-cli.ts
CHANGED
|
@@ -159,12 +159,14 @@ function isFirstTokenEvent(event: AssistantMessageEvent): boolean {
|
|
|
159
159
|
case "text_end":
|
|
160
160
|
case "thinking_end":
|
|
161
161
|
return event.content.length > 0;
|
|
162
|
+
case "image_end":
|
|
163
|
+
return true;
|
|
162
164
|
default:
|
|
163
165
|
return false;
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
/** Final message carries visible output — non-empty text/thinking or a tool call. */
|
|
169
|
+
/** Final message carries visible output — non-empty text/thinking, an image, or a tool call. */
|
|
168
170
|
function hasVisibleFinalContent(message: AssistantMessage): boolean {
|
|
169
171
|
return message.content.some(block => {
|
|
170
172
|
switch (block.type) {
|
|
@@ -172,6 +174,7 @@ function hasVisibleFinalContent(message: AssistantMessage): boolean {
|
|
|
172
174
|
return block.text.length > 0;
|
|
173
175
|
case "thinking":
|
|
174
176
|
return block.thinking.length > 0;
|
|
177
|
+
case "image":
|
|
175
178
|
case "redactedThinking":
|
|
176
179
|
case "toolCall":
|
|
177
180
|
return true;
|
package/src/cli/grep-cli.ts
CHANGED
|
@@ -7,6 +7,7 @@ import * as path from "node:path";
|
|
|
7
7
|
import { GrepOutputMode, grep } from "@oh-my-pi/pi-natives";
|
|
8
8
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import chalk from "chalk";
|
|
10
|
+
import { expandPath } from "../tools/path-utils";
|
|
10
11
|
|
|
11
12
|
export interface GrepCommandArgs {
|
|
12
13
|
pattern: string;
|
|
@@ -73,7 +74,7 @@ export async function runGrepCommand(cmd: GrepCommandArgs): Promise<void> {
|
|
|
73
74
|
process.exit(1);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
const searchPath = path.resolve(cmd.path);
|
|
77
|
+
const searchPath = path.resolve(expandPath(cmd.path));
|
|
77
78
|
console.log(chalk.dim(`Searching in: ${searchPath}`));
|
|
78
79
|
console.log(chalk.dim(`Pattern: ${cmd.pattern}`));
|
|
79
80
|
console.log(
|
package/src/cli-commands.ts
CHANGED
|
@@ -46,31 +46,65 @@ export const commands: CommandEntry[] = [
|
|
|
46
46
|
{ name: "search", load: () => import("./commands/web-search").then(m => m.default), aliases: ["q"] },
|
|
47
47
|
];
|
|
48
48
|
|
|
49
|
-
// Documented-looking plugin
|
|
50
|
-
// commands. Without a guard `resolveCliArgv` rewrites e.g. `omp
|
|
51
|
-
// `omp launch
|
|
52
|
-
// instead of managing plugins (#
|
|
53
|
-
//
|
|
54
|
-
// `omp plugin <action
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
// Documented-looking plugin/marketplace verbs that are NOT registered top-level
|
|
50
|
+
// commands. Without a guard `resolveCliArgv` rewrites e.g. `omp marketplace add
|
|
51
|
+
// xyz` to `omp launch marketplace add xyz`, silently forwarding the argv to the
|
|
52
|
+
// model as a prompt instead of managing plugins (#4845; same class as the
|
|
53
|
+
// `list`/`remove` leak fixed in #2935 and the `install` leak in #1496/#1498).
|
|
54
|
+
// The real commands live under `omp plugin <action>`; each entry maps a verb to
|
|
55
|
+
// a hint pointing there. See {@link reservedTopLevelWordMessage} for when a hint
|
|
56
|
+
// fires vs. when the argv still falls through to `launch`.
|
|
57
|
+
const RESERVED_TOP_LEVEL_WORDS: Record<string, string> = {
|
|
58
|
+
extensions:
|
|
59
59
|
'`omp extensions` is not a management command. Use `omp plugin list` / `omp plugin install`, or run `omp launch extensions` if you meant to send "extensions" as a prompt.',
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"list",
|
|
63
|
-
'`omp list` is not a top-level command. Use `omp plugin list` to list installed plugins, or run `omp launch list` if you meant to send "list" as a prompt.',
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"remove",
|
|
60
|
+
list: '`omp list` is not a top-level command. Use `omp plugin list` to list installed plugins, or run `omp launch list` if you meant to send "list" as a prompt.',
|
|
61
|
+
remove:
|
|
67
62
|
'`omp remove` is not a top-level command. Use `omp plugin uninstall <name>` to remove a plugin, or run `omp launch remove` if you meant to send "remove" as a prompt.',
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
uninstall:
|
|
64
|
+
'`omp uninstall` is not a top-level command. Use `omp plugin uninstall <name@marketplace>` to remove a plugin, or run `omp launch uninstall` if you meant to send "uninstall" as a prompt.',
|
|
65
|
+
marketplace:
|
|
66
|
+
'`omp marketplace` is not a top-level command. Use `omp plugin marketplace <add|remove|update|list>` to manage marketplaces, or run `omp launch marketplace` if you meant to send "marketplace" as a prompt.',
|
|
67
|
+
discover:
|
|
68
|
+
'`omp discover` is not a top-level command. Use `omp plugin discover [marketplace]` to browse available plugins, or run `omp launch discover` if you meant to send "discover" as a prompt.',
|
|
69
|
+
upgrade:
|
|
70
|
+
'`omp upgrade` is not a top-level command. Use `omp plugin upgrade [name@marketplace]` to upgrade plugins, or run `omp launch upgrade` if you meant to send "upgrade" as a prompt.',
|
|
71
|
+
enable:
|
|
72
|
+
'`omp enable` is not a top-level command. Use `omp plugin enable <name@marketplace>` to enable a plugin, or run `omp launch enable` if you meant to send "enable" as a prompt.',
|
|
73
|
+
disable:
|
|
74
|
+
'`omp disable` is not a top-level command. Use `omp plugin disable <name@marketplace>` to disable a plugin, or run `omp launch disable` if you meant to send "disable" as a prompt.',
|
|
75
|
+
};
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
// Sub-actions that make `omp marketplace <sub>` unambiguously a management
|
|
78
|
+
// command even when multi-word (the reporter's `omp marketplace add xyz`,
|
|
79
|
+
// #4845). Mirrors the switch in `handleMarketplace` (cli/plugin-cli.ts).
|
|
80
|
+
const MARKETPLACE_SUBCOMMANDS: Record<string, true> = { add: true, remove: true, rm: true, update: true, list: true };
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Hint for a reserved plugin/marketplace verb used as a top-level command, or
|
|
84
|
+
* `undefined` when the argv should fall through to `launch`.
|
|
85
|
+
*
|
|
86
|
+
* A bare verb (`omp marketplace`) always hints. A multi-word invocation only
|
|
87
|
+
* hints when the arguments follow the documented plugin grammar — a marketplace
|
|
88
|
+
* sub-action (`omp marketplace add …`) or a `name@marketplace` plugin id
|
|
89
|
+
* (`omp uninstall foo@bar`) — so genuine prompts that merely begin with one of
|
|
90
|
+
* these words (`omp list all my files`, `omp upgrade the deps`) still launch.
|
|
91
|
+
*
|
|
92
|
+
* Flags (`-…`) and `@file` arguments in the verb slot are never management
|
|
93
|
+
* commands; those fall through to the default `launch` command.
|
|
94
|
+
*/
|
|
95
|
+
export function reservedTopLevelWordMessage(argv: readonly string[]): string | undefined {
|
|
96
|
+
const first = argv[0];
|
|
97
|
+
if (!first || first.startsWith("-") || first.startsWith("@")) return undefined;
|
|
98
|
+
const hint = RESERVED_TOP_LEVEL_WORDS[first];
|
|
99
|
+
if (!hint) return undefined;
|
|
100
|
+
const second = argv[1];
|
|
101
|
+
if (second === undefined) return hint;
|
|
102
|
+
if (first === "marketplace" && MARKETPLACE_SUBCOMMANDS[second]) return hint;
|
|
103
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
104
|
+
const arg = argv[index];
|
|
105
|
+
if (!arg.startsWith("-") && arg.includes("@")) return hint;
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
74
108
|
}
|
|
75
109
|
|
|
76
110
|
/**
|
|
@@ -112,7 +146,7 @@ function leadingSubcommandIndex(argv: string[]): number {
|
|
|
112
146
|
*/
|
|
113
147
|
export function resolveCliArgv(argv: string[]): ResolvedCliArgv {
|
|
114
148
|
const first = argv[0];
|
|
115
|
-
const reservedMessage = reservedTopLevelWordMessage(
|
|
149
|
+
const reservedMessage = reservedTopLevelWordMessage(argv);
|
|
116
150
|
if (reservedMessage) return { error: reservedMessage };
|
|
117
151
|
if (first === "--help" || first === "-h" || first === "--version" || first === "-v" || first === "help") {
|
|
118
152
|
return { argv };
|
package/src/cli.ts
CHANGED
|
@@ -338,7 +338,11 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
338
338
|
// worker's parked initial messages as soon as the entry module's
|
|
339
339
|
// top-level evaluation finishes.
|
|
340
340
|
if (resolvedArgv[0]?.startsWith("__omp_worker_")) {
|
|
341
|
-
await runWorkerEntrypoint(resolvedArgv[0]);
|
|
341
|
+
const dispatched = await runWorkerEntrypoint(resolvedArgv[0]);
|
|
342
|
+
if (!dispatched) {
|
|
343
|
+
process.stderr.write(`Error: unknown worker selector: ${resolvedArgv[0]}\n`);
|
|
344
|
+
process.exitCode = 1;
|
|
345
|
+
}
|
|
342
346
|
return;
|
|
343
347
|
}
|
|
344
348
|
|
|
@@ -10,6 +10,7 @@ import type { Api, Model, RemoteCompactionConfig } from "@oh-my-pi/pi-ai/types";
|
|
|
10
10
|
import { buildModel } from "@oh-my-pi/pi-catalog/build";
|
|
11
11
|
import {
|
|
12
12
|
getBundledModelReferenceIndex,
|
|
13
|
+
isQwenModelId,
|
|
13
14
|
resolveModelReference,
|
|
14
15
|
stripBracketedModelIdAffixes,
|
|
15
16
|
} from "@oh-my-pi/pi-catalog/identity";
|
|
@@ -512,6 +513,48 @@ async function discoverLlamaCppServerMetadata(
|
|
|
512
513
|
}
|
|
513
514
|
}
|
|
514
515
|
|
|
516
|
+
/**
|
|
517
|
+
* PrismLM Ternary/1-bit Bonsai GGUFs are Qwen3.6-27B derivatives served locally
|
|
518
|
+
* via llama.cpp; their ids do not contain "qwen", so match them explicitly here
|
|
519
|
+
* rather than broadening the global `isQwenModelId` predicate.
|
|
520
|
+
*/
|
|
521
|
+
function isBonsaiQwenGguf(id: string): boolean {
|
|
522
|
+
return /(?:ternary-)?bonsai-27b/i.test(id);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* applyLlamaCppQwenThinking rewrites a discovered or cached llama.cpp model so a
|
|
527
|
+
* Qwen-family chat template (which defaults `enable_thinking: true`) can be
|
|
528
|
+
* turned off. Qwen ids and the Qwen3.6-based PrismLM Ternary Bonsai GGUFs are
|
|
529
|
+
* routed through chat-completions (the implicit llama.cpp provider defaults to
|
|
530
|
+
* `openai-responses`, whose disable path has no Qwen encoding) with the
|
|
531
|
+
* `qwen-template-false` dialect; omp emits `preserve_thinking` inside
|
|
532
|
+
* `chat_template_kwargs` for Qwen, so the toggle rides there too and history
|
|
533
|
+
* `<think>` blocks survive (`qwenPreserveThinking`). The runtime base URL gets a
|
|
534
|
+
* `/v1` suffix because the chat-completions request would otherwise POST to the
|
|
535
|
+
* native root, which does not serve it. A model with a custom transport (e.g.
|
|
536
|
+
* `pi-native`, whose client appends `/v1/pi/stream`) keeps its base URL so the
|
|
537
|
+
* suffix is not doubled. Non-Qwen models pass through unchanged. Applied on both
|
|
538
|
+
* fresh discovery and cache load, so an upgraded cache is corrected without
|
|
539
|
+
* waiting for re-discovery.
|
|
540
|
+
*/
|
|
541
|
+
export function applyLlamaCppQwenThinking(model: Model<Api>): Model<Api> {
|
|
542
|
+
if (!isQwenModelId(model.id) && !isBonsaiQwenGguf(model.id)) return model;
|
|
543
|
+
return buildModel({
|
|
544
|
+
...model,
|
|
545
|
+
api: "openai-completions",
|
|
546
|
+
baseUrl: model.transport ? model.baseUrl : ensureLlamaCppV1BaseUrl(normalizeLlamaCppBaseUrl(model.baseUrl)),
|
|
547
|
+
reasoning: true,
|
|
548
|
+
compat: {
|
|
549
|
+
...model.compatConfig,
|
|
550
|
+
supportsReasoningParams: true,
|
|
551
|
+
thinkingFormat: "qwen-chat-template",
|
|
552
|
+
reasoningDisableMode: "qwen-template-false",
|
|
553
|
+
qwenPreserveThinking: true,
|
|
554
|
+
},
|
|
555
|
+
} as unknown as ModelSpec<Api>);
|
|
556
|
+
}
|
|
557
|
+
|
|
515
558
|
export async function discoverLlamaCppModels(
|
|
516
559
|
providerConfig: DiscoveryProviderConfig,
|
|
517
560
|
ctx: DiscoveryContext,
|
|
@@ -553,26 +596,31 @@ export async function discoverLlamaCppModels(
|
|
|
553
596
|
serverMetadata?.contextWindow ??
|
|
554
597
|
item.trainingContextWindow ??
|
|
555
598
|
DISCOVERY_DEFAULT_CONTEXT_WINDOW;
|
|
599
|
+
// Local llama.cpp models stamp `reasoning: false` with a minimal compat;
|
|
600
|
+
// applyLlamaCppQwenThinking upgrades Qwen-family ids (which cannot disable
|
|
601
|
+
// their default-on thinking otherwise) after the base model is built.
|
|
556
602
|
discovered.push(
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
603
|
+
applyLlamaCppQwenThinking(
|
|
604
|
+
buildModel({
|
|
605
|
+
id,
|
|
606
|
+
name: id,
|
|
607
|
+
api: providerConfig.api,
|
|
608
|
+
provider: providerConfig.provider,
|
|
609
|
+
baseUrl,
|
|
610
|
+
reasoning: false,
|
|
611
|
+
input: item.input ?? serverMetadata?.input ?? ["text"],
|
|
612
|
+
imageInputDecoder: "stb",
|
|
613
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
614
|
+
contextWindow,
|
|
615
|
+
maxTokens: resolveLlamaCppMaxTokens(contextWindow, serverMetadata?.maxTokens),
|
|
616
|
+
headers,
|
|
617
|
+
compat: {
|
|
618
|
+
supportsStore: false,
|
|
619
|
+
supportsDeveloperRole: false,
|
|
620
|
+
supportsReasoningEffort: false,
|
|
621
|
+
},
|
|
622
|
+
} as ModelSpec<Api>),
|
|
623
|
+
),
|
|
576
624
|
);
|
|
577
625
|
}
|
|
578
626
|
return discovered;
|
|
@@ -583,7 +631,12 @@ export async function discoverLlamaCppModelRuntimeMetadata(
|
|
|
583
631
|
ctx: DiscoveryContext,
|
|
584
632
|
): Promise<LlamaCppDiscoveredModelRuntimeMetadata | undefined> {
|
|
585
633
|
const baseUrl = normalizeLlamaCppBaseUrl(model.baseUrl);
|
|
586
|
-
|
|
634
|
+
// Probe the native `/models` endpoint (not the OpenAI-compatible `/v1/models`)
|
|
635
|
+
// so the runtime `meta`, `status.args`, and `architecture.input_modalities`
|
|
636
|
+
// fields survive; a Qwen model routed to chat-completions carries a `/v1`
|
|
637
|
+
// base URL, which would otherwise send this to `/v1/models`.
|
|
638
|
+
const nativeBaseUrl = toLlamaCppNativeBaseUrl(baseUrl);
|
|
639
|
+
const modelsUrl = `${nativeBaseUrl}/models`;
|
|
587
640
|
const baseHeaders: Record<string, string> = { ...(model.headers ?? {}) };
|
|
588
641
|
const attempt = async (headers: Record<string, string>) => {
|
|
589
642
|
const [entries, serverMetadata] = await Promise.all([
|
|
@@ -597,7 +650,7 @@ export async function discoverLlamaCppModelRuntimeMetadata(
|
|
|
597
650
|
}
|
|
598
651
|
return parseLlamaCppModelList(await response.json());
|
|
599
652
|
}),
|
|
600
|
-
discoverLlamaCppServerMetadata(ctx,
|
|
653
|
+
discoverLlamaCppServerMetadata(ctx, nativeBaseUrl, headers),
|
|
601
654
|
]);
|
|
602
655
|
if (!entries) {
|
|
603
656
|
return undefined;
|
|
@@ -898,6 +951,13 @@ function normalizeLlamaCppBaseUrl(baseUrl?: string): string {
|
|
|
898
951
|
}
|
|
899
952
|
}
|
|
900
953
|
|
|
954
|
+
// ensureLlamaCppV1BaseUrl appends the OpenAI-compatible `/v1` prefix a
|
|
955
|
+
// chat-completions request needs; native discovery keeps the bare root, which
|
|
956
|
+
// serves `/models` and `/props` but not `/chat/completions`.
|
|
957
|
+
function ensureLlamaCppV1BaseUrl(baseUrl: string): string {
|
|
958
|
+
return baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`;
|
|
959
|
+
}
|
|
960
|
+
|
|
901
961
|
function toLlamaCppNativeBaseUrl(baseUrl: string): string {
|
|
902
962
|
try {
|
|
903
963
|
const parsed = new URL(baseUrl);
|
|
@@ -71,6 +71,7 @@ import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
|
71
71
|
import { type ApiKeyResolverModel, type ApiKeyResolverOptions, createApiKeyResolver } from "./api-key-resolver";
|
|
72
72
|
import type { ConfigError, ConfigFile } from "./config-file";
|
|
73
73
|
import {
|
|
74
|
+
applyLlamaCppQwenThinking,
|
|
74
75
|
DISCOVERY_DEFAULT_MAX_TOKENS,
|
|
75
76
|
type DiscoveryContext,
|
|
76
77
|
type DiscoveryProviderConfig,
|
|
@@ -1020,7 +1021,7 @@ export class ModelRegistry {
|
|
|
1020
1021
|
// Custom/config providers bypass the model-manager merge point —
|
|
1021
1022
|
// collapse effort-tier variants here so X/X-thinking twins fold.
|
|
1022
1023
|
const withModelOverrides = this.#applyModelOverrides(collapseBuiltModelVariants(combined), this.#modelOverrides);
|
|
1023
|
-
this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
|
|
1024
|
+
this.#models = this.#applyLlamaCppQwenThinkingToModels(this.#applyRuntimeProviderOverrides(withModelOverrides));
|
|
1024
1025
|
this.#lastStaticLoadMtime = this.#modelsConfigFile.getMtimeMs();
|
|
1025
1026
|
}
|
|
1026
1027
|
|
|
@@ -1417,7 +1418,7 @@ export class ModelRegistry {
|
|
|
1417
1418
|
// Merge runtime extension models so they survive online discovery completion
|
|
1418
1419
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
1419
1420
|
const withModelOverrides = this.#applyModelOverrides(collapseBuiltModelVariants(combined), this.#modelOverrides);
|
|
1420
|
-
this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
|
|
1421
|
+
this.#models = this.#applyLlamaCppQwenThinkingToModels(this.#applyRuntimeProviderOverrides(withModelOverrides));
|
|
1421
1422
|
}
|
|
1422
1423
|
|
|
1423
1424
|
#configuredDiscoveryCacheProviderId(providerConfig: DiscoveryProviderConfig): string {
|
|
@@ -1779,6 +1780,22 @@ export class ModelRegistry {
|
|
|
1779
1780
|
});
|
|
1780
1781
|
}
|
|
1781
1782
|
|
|
1783
|
+
// #applyLlamaCppQwenThinkingToModels re-runs applyLlamaCppQwenThinking as the
|
|
1784
|
+
// outermost transform for llama.cpp-provider models, after discovery merges,
|
|
1785
|
+
// cache fallbacks, and provider/transport overrides have run. It is
|
|
1786
|
+
// idempotent, so it restores the routed Qwen model's chat-completions api,
|
|
1787
|
+
// `/v1` runtime base URL, and disable dialect even when a configured `baseUrl`
|
|
1788
|
+
// override (which wins in mergeDiscoveredModel) or a fallback to a pre-fix
|
|
1789
|
+
// cached row would otherwise leave the old spec in place.
|
|
1790
|
+
#applyLlamaCppQwenThinkingToModels(models: Model<Api>[]): Model<Api>[] {
|
|
1791
|
+
const llamaCppProviders = new Set<string>();
|
|
1792
|
+
for (const provider of this.#discoverableProviders) {
|
|
1793
|
+
if (provider.discovery.type === "llama.cpp") llamaCppProviders.add(provider.provider);
|
|
1794
|
+
}
|
|
1795
|
+
if (llamaCppProviders.size === 0) return models;
|
|
1796
|
+
return models.map(model => (llamaCppProviders.has(model.provider) ? applyLlamaCppQwenThinking(model) : model));
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1782
1799
|
#mergeProviderOverride(baseOverride: ProviderOverride | undefined, override: ProviderOverride): ProviderOverride {
|
|
1783
1800
|
return {
|
|
1784
1801
|
baseUrl: override.baseUrl ?? baseOverride?.baseUrl,
|
|
@@ -1993,6 +2010,15 @@ export class ModelRegistry {
|
|
|
1993
2010
|
getProviderBaseUrl(provider: string): string | undefined {
|
|
1994
2011
|
return this.#models.find(m => m.provider === provider && m.baseUrl)?.baseUrl;
|
|
1995
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Get provider-level headers without including per-model overrides.
|
|
2015
|
+
*/
|
|
2016
|
+
getProviderHeaders(provider: string): Record<string, string> | undefined {
|
|
2017
|
+
return createLiveConfigHeaders([
|
|
2018
|
+
this.#providerOverrides.get(provider)?.headers,
|
|
2019
|
+
this.#runtimeProviderOverrides.get(provider)?.headers,
|
|
2020
|
+
]);
|
|
2021
|
+
}
|
|
1996
2022
|
|
|
1997
2023
|
/**
|
|
1998
2024
|
* Get API key for a model.
|
|
@@ -2297,10 +2323,12 @@ export class ModelRegistry {
|
|
|
2297
2323
|
transportOverride,
|
|
2298
2324
|
);
|
|
2299
2325
|
this.#runtimeProviderOverrides.set(providerName, nextRuntimeOverride);
|
|
2300
|
-
this.#models = this.#
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2326
|
+
this.#models = this.#applyLlamaCppQwenThinkingToModels(
|
|
2327
|
+
this.#models.map(m => {
|
|
2328
|
+
if (m.provider !== providerName) return m;
|
|
2329
|
+
return this.#applyProviderTransportOverride(m, transportOverride);
|
|
2330
|
+
}),
|
|
2331
|
+
);
|
|
2304
2332
|
}
|
|
2305
2333
|
}
|
|
2306
2334
|
|
|
@@ -623,11 +623,18 @@ function findExactModelReferenceMatch(modelReference: string, availableModels: M
|
|
|
623
623
|
* 4. provider-scoped fuzzy match,
|
|
624
624
|
* 5. substring match with the alias-vs-dated pick.
|
|
625
625
|
* Returns the matched model or undefined if no match found.
|
|
626
|
+
*
|
|
627
|
+
* `exactOnly` stops after the exact phases (1-3), skipping the fuzzy/substring
|
|
628
|
+
* fallbacks (4-5). Callers use it to resolve the full selector exactly before
|
|
629
|
+
* a trailing `:<level>` thinking suffix is split off, so the suffix can never
|
|
630
|
+
* be fuzzily absorbed into a longer sibling id (e.g. `kimi-for-coding:high`
|
|
631
|
+
* must not match `kimi-for-coding-highspeed`).
|
|
626
632
|
*/
|
|
627
633
|
function matchModel(
|
|
628
634
|
modelPattern: string,
|
|
629
635
|
availableModels: Model<Api>[],
|
|
630
636
|
context: ModelPreferenceContext,
|
|
637
|
+
options?: { exactOnly?: boolean },
|
|
631
638
|
): Model<Api> | undefined {
|
|
632
639
|
const exactRefMatch = findExactModelReferenceMatch(modelPattern, availableModels);
|
|
633
640
|
if (exactRefMatch) {
|
|
@@ -663,6 +670,14 @@ function matchModel(
|
|
|
663
670
|
return pickPreferredModel(preferred.length > 0 ? preferred : aliasMatches, context);
|
|
664
671
|
}
|
|
665
672
|
}
|
|
673
|
+
|
|
674
|
+
// Exact phases exhausted. Fuzzy/substring fallbacks (below) subsequence-match
|
|
675
|
+
// the whole pattern and would let a trailing `:<level>` thinking suffix bleed
|
|
676
|
+
// into a longer sibling id; callers that still hold an unstripped suffix ask
|
|
677
|
+
// for exact-only so the suffix is split off before any fuzzy attempt.
|
|
678
|
+
if (options?.exactOnly) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
666
681
|
// Check for provider/modelId format — fuzzy match within provider only.
|
|
667
682
|
const slashIndex = modelPattern.indexOf("/");
|
|
668
683
|
if (slashIndex !== -1) {
|
|
@@ -766,17 +781,33 @@ function parseModelPatternWithContext(
|
|
|
766
781
|
context: ModelPreferenceContext,
|
|
767
782
|
options?: { allowInvalidThinkingSelectorFallback?: boolean },
|
|
768
783
|
): ParsedModelResult {
|
|
769
|
-
//
|
|
770
|
-
|
|
784
|
+
// Exact match on the full pattern first (no fuzzy): a literal id that
|
|
785
|
+
// contains a colon (`coding-router:max`) wins over any suffix split.
|
|
786
|
+
const exactMatch = matchModel(pattern, availableModels, context, { exactOnly: true });
|
|
771
787
|
if (exactMatch) {
|
|
772
788
|
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
773
789
|
}
|
|
774
790
|
|
|
775
|
-
//
|
|
776
|
-
//
|
|
777
|
-
//
|
|
791
|
+
// Prefer a fuzzy match whose actual id ends in the suffix, preserving
|
|
792
|
+
// shorthand selectors for literal tier models such as `router:low`. Other
|
|
793
|
+
// fuzzy results (e.g. `kimi-for-coding-highspeed`) cannot absorb the suffix.
|
|
778
794
|
const { base, level } = splitThinkingSuffix(pattern, -1, MAX_THINKING_SUFFIX_OPTIONS);
|
|
779
795
|
if (level) {
|
|
796
|
+
const literalSuffixMatch = matchModel(pattern, availableModels, context);
|
|
797
|
+
if (literalSuffixMatch?.id.toLowerCase().endsWith(`:${level}`)) {
|
|
798
|
+
return {
|
|
799
|
+
model: literalSuffixMatch,
|
|
800
|
+
thinkingLevel: undefined,
|
|
801
|
+
warning: undefined,
|
|
802
|
+
explicitThinkingLevel: false,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Strip a valid thinking suffix and recurse before accepting any other
|
|
807
|
+
// fuzzy match, so `:<level>` cannot be absorbed into a longer sibling
|
|
808
|
+
// id (e.g. `kimi-for-coding:high` must not match
|
|
809
|
+
// `kimi-for-coding-highspeed`). `max` is accepted only after the exact
|
|
810
|
+
// match above failed, so literal model IDs ending in `:max` keep winning.
|
|
780
811
|
const result = parseModelPatternWithContext(base, availableModels, context, options);
|
|
781
812
|
if (result.model) {
|
|
782
813
|
// Only use this thinking level if no warning from inner recursion
|
|
@@ -791,6 +822,13 @@ function parseModelPatternWithContext(
|
|
|
791
822
|
return result;
|
|
792
823
|
}
|
|
793
824
|
|
|
825
|
+
// No valid thinking suffix: fall back to fuzzy/substring matching on the
|
|
826
|
+
// whole pattern.
|
|
827
|
+
const fallbackMatch = matchModel(pattern, availableModels, context);
|
|
828
|
+
if (fallbackMatch) {
|
|
829
|
+
return { model: fallbackMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
830
|
+
}
|
|
831
|
+
|
|
794
832
|
const lastColonIndex = pattern.lastIndexOf(":");
|
|
795
833
|
if (lastColonIndex === -1) {
|
|
796
834
|
// No colons, pattern simply doesn't match any model
|
|
@@ -859,6 +897,10 @@ export function parseModelPattern(
|
|
|
859
897
|
const DEFAULT_MODEL_ROLE = "default";
|
|
860
898
|
const MODEL_ROLE_ALIAS_PREFIXES = [MODEL_ROLE_ALIAS_PREFIX, LEGACY_MODEL_ROLE_ALIAS_PREFIX];
|
|
861
899
|
|
|
900
|
+
export interface ModelRoleLookup {
|
|
901
|
+
getModelRole(role: ModelRole | string): string | undefined;
|
|
902
|
+
}
|
|
903
|
+
|
|
862
904
|
function isModelRole(role: string): role is ModelRole {
|
|
863
905
|
return (MODEL_ROLE_IDS as string[]).includes(role);
|
|
864
906
|
}
|
|
@@ -875,7 +917,7 @@ function modelRoleAliasPrefixLength(value: string): number | undefined {
|
|
|
875
917
|
return MODEL_ROLE_ALIAS_PREFIXES.find(prefix => value.startsWith(prefix))?.length;
|
|
876
918
|
}
|
|
877
919
|
|
|
878
|
-
function getModelRoleAlias(value: string, settings?:
|
|
920
|
+
function getModelRoleAlias(value: string, settings?: ModelRoleLookup): string | undefined {
|
|
879
921
|
const normalized = value.trim();
|
|
880
922
|
const prefixLength = modelRoleAliasPrefixLength(normalized);
|
|
881
923
|
if (prefixLength === undefined) return undefined;
|
|
@@ -931,7 +973,7 @@ function resolveDefaultInheritedPatterns(
|
|
|
931
973
|
role: ModelRole,
|
|
932
974
|
configuredDefault: string | undefined,
|
|
933
975
|
roleDefaults: string[],
|
|
934
|
-
settings:
|
|
976
|
+
settings: ModelRoleLookup | undefined,
|
|
935
977
|
visited: Set<string>,
|
|
936
978
|
): string[] {
|
|
937
979
|
if (!shouldInheritDefaultBeforePriority(role) || !configuredDefault) return [];
|
|
@@ -969,7 +1011,7 @@ function resolveDefaultInheritedPatterns(
|
|
|
969
1011
|
|
|
970
1012
|
function resolveConfiguredRolePattern(
|
|
971
1013
|
value: string,
|
|
972
|
-
settings?:
|
|
1014
|
+
settings?: ModelRoleLookup,
|
|
973
1015
|
visited: Set<string> = new Set(),
|
|
974
1016
|
): string[] | undefined {
|
|
975
1017
|
const normalized = value.trim();
|
|
@@ -1006,7 +1048,7 @@ function resolveConfiguredRolePattern(
|
|
|
1006
1048
|
/**
|
|
1007
1049
|
* Expand a role alias like "@smol" to the configured model string.
|
|
1008
1050
|
*/
|
|
1009
|
-
export function expandRoleAlias(value: string, settings?:
|
|
1051
|
+
export function expandRoleAlias(value: string, settings?: ModelRoleLookup): string {
|
|
1010
1052
|
const normalized = value.trim();
|
|
1011
1053
|
if (normalized === DEFAULT_MODEL_ROLE) {
|
|
1012
1054
|
return settings?.getModelRole("default") ?? value;
|
|
@@ -1016,7 +1058,10 @@ export function expandRoleAlias(value: string, settings?: Settings): string {
|
|
|
1016
1058
|
return resolved ?? value;
|
|
1017
1059
|
}
|
|
1018
1060
|
|
|
1019
|
-
export function resolveConfiguredModelPatterns(
|
|
1061
|
+
export function resolveConfiguredModelPatterns(
|
|
1062
|
+
value: string | string[] | undefined,
|
|
1063
|
+
settings?: ModelRoleLookup,
|
|
1064
|
+
): string[] {
|
|
1020
1065
|
const patterns = normalizeModelPatternList(value);
|
|
1021
1066
|
return patterns.flatMap(pattern => {
|
|
1022
1067
|
const resolved = resolveConfiguredRolePattern(pattern, settings);
|
|
@@ -1100,7 +1145,7 @@ export interface ResolvedModelRoleValue {
|
|
|
1100
1145
|
export function resolveModelRoleValue(
|
|
1101
1146
|
roleValue: string | undefined,
|
|
1102
1147
|
availableModels: Model<Api>[],
|
|
1103
|
-
options?: { settings?: Settings; matchPreferences?: ModelMatchPreferences },
|
|
1148
|
+
options?: { settings?: Settings; roleLookup?: ModelRoleLookup; matchPreferences?: ModelMatchPreferences },
|
|
1104
1149
|
): ResolvedModelRoleValue {
|
|
1105
1150
|
if (!roleValue) {
|
|
1106
1151
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
@@ -1111,7 +1156,7 @@ export function resolveModelRoleValue(
|
|
|
1111
1156
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1112
1157
|
}
|
|
1113
1158
|
|
|
1114
|
-
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.settings);
|
|
1159
|
+
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.roleLookup ?? options?.settings);
|
|
1115
1160
|
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
1116
1161
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1117
1162
|
}
|
|
@@ -60,6 +60,7 @@ const OpenAICompatFields = {
|
|
|
60
60
|
"strictResponsesPairing?": "boolean",
|
|
61
61
|
"supportsImageDetailOriginal?": "boolean",
|
|
62
62
|
// anthropic-messages compat flags (same `compat` slot, per-api interpretation)
|
|
63
|
+
"supportsEagerToolInputStreaming?": "boolean",
|
|
63
64
|
"requiresToolResultId?": "boolean",
|
|
64
65
|
"replayUnsignedThinking?": "boolean",
|
|
65
66
|
} as const;
|