@oh-my-pi/pi-coding-agent 17.0.1 → 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 +92 -20
- package/dist/cli.js +3485 -3448
- 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/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- 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/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/mcp/transports/stdio.d.ts +13 -1
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/hook-editor.d.ts +7 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +2 -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/print-mode.d.ts +1 -1
- 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 +34 -10
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +14 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- 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/essential-tools.d.ts +29 -0
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +1 -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/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 +1304 -42
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +25 -6
- package/src/config/model-resolver.ts +14 -7
- package/src/config/settings-schema.ts +42 -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/helpers.ts +28 -5
- package/src/exec/bash-executor.ts +14 -5
- 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/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/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/components/advisor-config.ts +65 -3
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/hook-editor.ts +18 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/session-selector.ts +4 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/controllers/command-controller.ts +167 -47
- package/src/modes/controllers/event-controller.ts +5 -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 +191 -31
- package/src/modes/interactive-mode.ts +139 -54
- package/src/modes/print-mode.ts +3 -3
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +19 -4
- 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/registry/agent-lifecycle.ts +133 -18
- package/src/sdk.ts +221 -42
- package/src/session/agent-session.ts +1285 -348
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-manager.ts +48 -0
- package/src/slash-commands/builtin-registry.ts +7 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- 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/essential-tools.ts +45 -0
- package/src/tools/gh.ts +169 -2
- package/src/tools/image-gen.ts +69 -7
- package/src/tools/index.ts +7 -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/providers/kimi.ts +18 -12
- 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.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,
|
|
@@ -2306,10 +2323,12 @@ export class ModelRegistry {
|
|
|
2306
2323
|
transportOverride,
|
|
2307
2324
|
);
|
|
2308
2325
|
this.#runtimeProviderOverrides.set(providerName, nextRuntimeOverride);
|
|
2309
|
-
this.#models = this.#
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
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
|
+
);
|
|
2313
2332
|
}
|
|
2314
2333
|
}
|
|
2315
2334
|
|
|
@@ -897,6 +897,10 @@ export function parseModelPattern(
|
|
|
897
897
|
const DEFAULT_MODEL_ROLE = "default";
|
|
898
898
|
const MODEL_ROLE_ALIAS_PREFIXES = [MODEL_ROLE_ALIAS_PREFIX, LEGACY_MODEL_ROLE_ALIAS_PREFIX];
|
|
899
899
|
|
|
900
|
+
export interface ModelRoleLookup {
|
|
901
|
+
getModelRole(role: ModelRole | string): string | undefined;
|
|
902
|
+
}
|
|
903
|
+
|
|
900
904
|
function isModelRole(role: string): role is ModelRole {
|
|
901
905
|
return (MODEL_ROLE_IDS as string[]).includes(role);
|
|
902
906
|
}
|
|
@@ -913,7 +917,7 @@ function modelRoleAliasPrefixLength(value: string): number | undefined {
|
|
|
913
917
|
return MODEL_ROLE_ALIAS_PREFIXES.find(prefix => value.startsWith(prefix))?.length;
|
|
914
918
|
}
|
|
915
919
|
|
|
916
|
-
function getModelRoleAlias(value: string, settings?:
|
|
920
|
+
function getModelRoleAlias(value: string, settings?: ModelRoleLookup): string | undefined {
|
|
917
921
|
const normalized = value.trim();
|
|
918
922
|
const prefixLength = modelRoleAliasPrefixLength(normalized);
|
|
919
923
|
if (prefixLength === undefined) return undefined;
|
|
@@ -969,7 +973,7 @@ function resolveDefaultInheritedPatterns(
|
|
|
969
973
|
role: ModelRole,
|
|
970
974
|
configuredDefault: string | undefined,
|
|
971
975
|
roleDefaults: string[],
|
|
972
|
-
settings:
|
|
976
|
+
settings: ModelRoleLookup | undefined,
|
|
973
977
|
visited: Set<string>,
|
|
974
978
|
): string[] {
|
|
975
979
|
if (!shouldInheritDefaultBeforePriority(role) || !configuredDefault) return [];
|
|
@@ -1007,7 +1011,7 @@ function resolveDefaultInheritedPatterns(
|
|
|
1007
1011
|
|
|
1008
1012
|
function resolveConfiguredRolePattern(
|
|
1009
1013
|
value: string,
|
|
1010
|
-
settings?:
|
|
1014
|
+
settings?: ModelRoleLookup,
|
|
1011
1015
|
visited: Set<string> = new Set(),
|
|
1012
1016
|
): string[] | undefined {
|
|
1013
1017
|
const normalized = value.trim();
|
|
@@ -1044,7 +1048,7 @@ function resolveConfiguredRolePattern(
|
|
|
1044
1048
|
/**
|
|
1045
1049
|
* Expand a role alias like "@smol" to the configured model string.
|
|
1046
1050
|
*/
|
|
1047
|
-
export function expandRoleAlias(value: string, settings?:
|
|
1051
|
+
export function expandRoleAlias(value: string, settings?: ModelRoleLookup): string {
|
|
1048
1052
|
const normalized = value.trim();
|
|
1049
1053
|
if (normalized === DEFAULT_MODEL_ROLE) {
|
|
1050
1054
|
return settings?.getModelRole("default") ?? value;
|
|
@@ -1054,7 +1058,10 @@ export function expandRoleAlias(value: string, settings?: Settings): string {
|
|
|
1054
1058
|
return resolved ?? value;
|
|
1055
1059
|
}
|
|
1056
1060
|
|
|
1057
|
-
export function resolveConfiguredModelPatterns(
|
|
1061
|
+
export function resolveConfiguredModelPatterns(
|
|
1062
|
+
value: string | string[] | undefined,
|
|
1063
|
+
settings?: ModelRoleLookup,
|
|
1064
|
+
): string[] {
|
|
1058
1065
|
const patterns = normalizeModelPatternList(value);
|
|
1059
1066
|
return patterns.flatMap(pattern => {
|
|
1060
1067
|
const resolved = resolveConfiguredRolePattern(pattern, settings);
|
|
@@ -1138,7 +1145,7 @@ export interface ResolvedModelRoleValue {
|
|
|
1138
1145
|
export function resolveModelRoleValue(
|
|
1139
1146
|
roleValue: string | undefined,
|
|
1140
1147
|
availableModels: Model<Api>[],
|
|
1141
|
-
options?: { settings?: Settings; matchPreferences?: ModelMatchPreferences },
|
|
1148
|
+
options?: { settings?: Settings; roleLookup?: ModelRoleLookup; matchPreferences?: ModelMatchPreferences },
|
|
1142
1149
|
): ResolvedModelRoleValue {
|
|
1143
1150
|
if (!roleValue) {
|
|
1144
1151
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
@@ -1149,7 +1156,7 @@ export function resolveModelRoleValue(
|
|
|
1149
1156
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1150
1157
|
}
|
|
1151
1158
|
|
|
1152
|
-
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.settings);
|
|
1159
|
+
const effectivePatterns = resolveConfiguredModelPatterns(normalized, options?.roleLookup ?? options?.settings);
|
|
1153
1160
|
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
1154
1161
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
1155
1162
|
}
|
|
@@ -67,6 +67,8 @@ import {
|
|
|
67
67
|
// Schema Definition Types
|
|
68
68
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
69
|
|
|
70
|
+
export type ModelRoleStorage = "global" | "project";
|
|
71
|
+
|
|
70
72
|
export type SettingTab =
|
|
71
73
|
| "appearance"
|
|
72
74
|
| "model"
|
|
@@ -509,6 +511,30 @@ export const SETTINGS_SCHEMA = {
|
|
|
509
511
|
|
|
510
512
|
disabledExtensions: { type: "array", default: EMPTY_STRING_ARRAY },
|
|
511
513
|
|
|
514
|
+
modelRoleStorage: {
|
|
515
|
+
type: "enum",
|
|
516
|
+
values: ["global", "project"] as const,
|
|
517
|
+
default: "global",
|
|
518
|
+
ui: {
|
|
519
|
+
tab: "model",
|
|
520
|
+
group: "Prompt",
|
|
521
|
+
label: "Model Role Storage",
|
|
522
|
+
description: "Where model selector role assignments are saved",
|
|
523
|
+
options: [
|
|
524
|
+
{
|
|
525
|
+
value: "global",
|
|
526
|
+
label: "Global",
|
|
527
|
+
description: "Save role models in the active profile config (current behavior)",
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
value: "project",
|
|
531
|
+
label: "Per-project",
|
|
532
|
+
description: "Save project role models in .omp/config.yml; missing project roles use global defaults",
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
|
|
512
538
|
modelRoles: { type: "record", default: EMPTY_STRING_RECORD },
|
|
513
539
|
|
|
514
540
|
modelTags: { type: "record", default: EMPTY_MODEL_TAGS_RECORD },
|
|
@@ -1439,7 +1465,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1439
1465
|
group: "Retry & Fallback",
|
|
1440
1466
|
label: "Retry Fallback Chains",
|
|
1441
1467
|
description:
|
|
1442
|
-
'JSON object mapping model roles, model selectors ("provider/model-id"), or provider wildcards ("provider/*") to ordered fallback selectors, e.g. {"default":["openai/gpt-4o-mini"],"google-antigravity/*":["google/*","google-vertex/*"]}. Model-oriented keys apply whenever that model/provider is active, regardless of role; a "provider/*" entry keeps the failing model\'s id and swaps the provider.',
|
|
1468
|
+
'JSON object mapping model roles, model selectors ("provider/model-id"), or provider wildcards ("provider/*") to ordered fallback selectors, e.g. {"default":["openai/gpt-4o-mini"],"google-antigravity/*":["google/*","google-vertex/*"]}. Model-oriented keys apply whenever that model/provider is active, regardless of role; a "provider/*" entry keeps the failing model\'s id and swaps the provider. An id-prefixed wildcard ("openrouter/google/*") re-prefixes the failing model\'s bare id (google-antigravity/gemini-x -> openrouter/google/gemini-x) and, used as a key, matches only that provider\'s ids under the prefix.',
|
|
1443
1469
|
},
|
|
1444
1470
|
},
|
|
1445
1471
|
"retry.fallbackRevertPolicy": {
|
|
@@ -2466,7 +2492,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2466
2492
|
group: "Auto-Learn",
|
|
2467
2493
|
label: "Auto-run capture at stop",
|
|
2468
2494
|
description:
|
|
2469
|
-
"When on, auto-run one capture turn at stop (uses extra tokens).
|
|
2495
|
+
"When on, auto-run one private capture turn at stop (uses extra tokens). When off, only standing auto-learn guidance remains.",
|
|
2470
2496
|
condition: "autolearnActive",
|
|
2471
2497
|
},
|
|
2472
2498
|
},
|
|
@@ -3616,7 +3642,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
3616
3642
|
},
|
|
3617
3643
|
"generate_image.enabled": {
|
|
3618
3644
|
type: "boolean",
|
|
3619
|
-
default:
|
|
3645
|
+
default: false,
|
|
3620
3646
|
ui: {
|
|
3621
3647
|
tab: "tools",
|
|
3622
3648
|
group: "Available Tools",
|
|
@@ -4468,7 +4494,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
4468
4494
|
},
|
|
4469
4495
|
"providers.image": {
|
|
4470
4496
|
type: "enum",
|
|
4471
|
-
values: ["auto", "openai", "antigravity", "xai", "gemini", "openrouter"] as const,
|
|
4497
|
+
values: ["auto", "openai", "openai-codex", "antigravity", "xai", "gemini", "openrouter"] as const,
|
|
4472
4498
|
default: "auto",
|
|
4473
4499
|
ui: {
|
|
4474
4500
|
tab: "providers",
|
|
@@ -4480,9 +4506,19 @@ export const SETTINGS_SCHEMA = {
|
|
|
4480
4506
|
value: "auto",
|
|
4481
4507
|
label: "Auto",
|
|
4482
4508
|
description:
|
|
4483
|
-
"Priority: active session provider > GPT model image tool > Antigravity > xAI > OpenRouter > Gemini",
|
|
4509
|
+
"Priority: per-request provider > configured provider > active session provider > GPT model image tool > Codex subscription > Antigravity > xAI > OpenRouter > Gemini",
|
|
4510
|
+
},
|
|
4511
|
+
{
|
|
4512
|
+
value: "openai",
|
|
4513
|
+
label: "OpenAI",
|
|
4514
|
+
description:
|
|
4515
|
+
"OPENAI_API_KEY (gpt-image-2) or active GPT model; falls back to a connected Codex subscription",
|
|
4516
|
+
},
|
|
4517
|
+
{
|
|
4518
|
+
value: "openai-codex",
|
|
4519
|
+
label: "OpenAI Codex (ChatGPT)",
|
|
4520
|
+
description: "Uses a connected Codex / ChatGPT subscription — no OPENAI_API_KEY needed",
|
|
4484
4521
|
},
|
|
4485
|
-
{ value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
|
|
4486
4522
|
{
|
|
4487
4523
|
value: "antigravity",
|
|
4488
4524
|
label: "Antigravity",
|