@gajae-code/coding-agent 0.12.21 → 0.13.0
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 +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
|
@@ -15,7 +15,7 @@ import { logger } from "@gajae-code/utils";
|
|
|
15
15
|
import chalk from "chalk";
|
|
16
16
|
import { parseThinkingLevel, resolveThinkingLevelForModel, splitSelectorThinkingSuffix } from "../thinking";
|
|
17
17
|
import { isAuthenticatedOrKeyless } from "./model-auth";
|
|
18
|
-
import { compareEquivalentModelVariants } from "./model-equivalence";
|
|
18
|
+
import { compareEquivalentModelVariants, getFinalSlashSegmentAliasKey } from "./model-equivalence";
|
|
19
19
|
import { MODEL_ROLE_IDS, type ModelRegistry, type ModelRole } from "./model-registry";
|
|
20
20
|
import { type ModelSelectorValue, normalizeModelSelectorValue } from "./model-selector-value";
|
|
21
21
|
import type { Settings } from "./settings";
|
|
@@ -210,7 +210,16 @@ export interface ModelMatchPreferences {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
export type CanonicalModelRegistry = Partial<
|
|
213
|
-
Pick<
|
|
213
|
+
Pick<
|
|
214
|
+
ModelRegistry,
|
|
215
|
+
| "resolveCanonicalModel"
|
|
216
|
+
| "getCanonicalVariants"
|
|
217
|
+
| "getCanonicalId"
|
|
218
|
+
| "seedCanonicalVariant"
|
|
219
|
+
| "resolveModelByLookupAlias"
|
|
220
|
+
| "lookupAliasExists"
|
|
221
|
+
| "clearCanonicalVariant"
|
|
222
|
+
>
|
|
214
223
|
>;
|
|
215
224
|
export type ModelLookupRegistry = Pick<ModelRegistry, "getAvailable"> & Partial<CanonicalModelRegistry>;
|
|
216
225
|
type CliModelRegistry = Pick<ModelRegistry, "getAll"> & Partial<CanonicalModelRegistry>;
|
|
@@ -362,6 +371,7 @@ function findExactCanonicalModelMatch(
|
|
|
362
371
|
availableModels: Model<Api>[],
|
|
363
372
|
modelRegistry: CanonicalModelRegistry | undefined,
|
|
364
373
|
sessionId?: string,
|
|
374
|
+
credentialSessionId?: string,
|
|
365
375
|
): Model<Api> | undefined {
|
|
366
376
|
if (!modelRegistry) {
|
|
367
377
|
return undefined;
|
|
@@ -374,6 +384,7 @@ function findExactCanonicalModelMatch(
|
|
|
374
384
|
availableOnly: false,
|
|
375
385
|
candidates: availableModels,
|
|
376
386
|
sessionId,
|
|
387
|
+
credentialSessionId,
|
|
377
388
|
});
|
|
378
389
|
}
|
|
379
390
|
|
|
@@ -386,6 +397,7 @@ function findExactEquivalentModelMatch(
|
|
|
386
397
|
availableModels: Model<Api>[],
|
|
387
398
|
modelRegistry: CanonicalModelRegistry | undefined,
|
|
388
399
|
sessionId?: string,
|
|
400
|
+
credentialSessionId?: string,
|
|
389
401
|
): Model<Api> | undefined {
|
|
390
402
|
if (!modelRegistry?.getCanonicalId || !modelRegistry.resolveCanonicalModel) return undefined;
|
|
391
403
|
const trimmedReference = modelReference.trim();
|
|
@@ -402,18 +414,26 @@ function findExactEquivalentModelMatch(
|
|
|
402
414
|
availableOnly: false,
|
|
403
415
|
candidates: availableModels,
|
|
404
416
|
sessionId,
|
|
417
|
+
credentialSessionId,
|
|
405
418
|
});
|
|
406
419
|
}
|
|
407
420
|
|
|
408
421
|
/**
|
|
409
|
-
*
|
|
410
|
-
*
|
|
422
|
+
* Match a pattern through the strict-exact stages (provider-qualified,
|
|
423
|
+
* canonical, equivalent, exact-ID, and provider-scoped) only. This deliberately
|
|
424
|
+
* excludes substring/alias fuzzy matching so preset-alias resolution can run
|
|
425
|
+
* between strict-exact and fuzzy stages.
|
|
411
426
|
*/
|
|
412
|
-
function
|
|
427
|
+
function findStrictExactModelMatch(
|
|
413
428
|
modelPattern: string,
|
|
414
429
|
availableModels: Model<Api>[],
|
|
415
430
|
context: ModelPreferenceContext,
|
|
416
|
-
options?: {
|
|
431
|
+
options?: {
|
|
432
|
+
modelRegistry?: CanonicalModelRegistry;
|
|
433
|
+
sessionId?: string;
|
|
434
|
+
credentialSessionId?: string;
|
|
435
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
436
|
+
},
|
|
417
437
|
): Model<Api> | undefined {
|
|
418
438
|
// Explicit provider/model selectors always bypass canonical coalescing.
|
|
419
439
|
const exactRefMatch = findExactModelReferenceMatch(modelPattern, availableModels);
|
|
@@ -427,6 +447,7 @@ function tryMatchModel(
|
|
|
427
447
|
availableModels,
|
|
428
448
|
options?.modelRegistry,
|
|
429
449
|
options?.sessionId,
|
|
450
|
+
options?.credentialSessionId,
|
|
430
451
|
);
|
|
431
452
|
if (exactCanonicalMatch) {
|
|
432
453
|
return exactCanonicalMatch;
|
|
@@ -437,22 +458,36 @@ function tryMatchModel(
|
|
|
437
458
|
availableModels,
|
|
438
459
|
options?.modelRegistry,
|
|
439
460
|
options?.sessionId,
|
|
461
|
+
options?.credentialSessionId,
|
|
440
462
|
);
|
|
441
463
|
if (exactEquivalentMatch) {
|
|
442
464
|
return exactEquivalentMatch;
|
|
443
465
|
}
|
|
444
466
|
|
|
445
467
|
// Exact ID match (case-insensitive) — this must happen before provider-scoped
|
|
446
|
-
//
|
|
468
|
+
// matching so raw IDs that contain slashes (for example OpenRouter route-suffix
|
|
447
469
|
// IDs like "openai/gpt-4o:extended") still resolve as IDs instead of being
|
|
448
|
-
// misread as a provider-qualified selector.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
470
|
+
// misread as a provider-qualified selector. Cross-provider exact-ID lookup is
|
|
471
|
+
// confined to bare IDs and route-suffix IDs: a plain provider pin (provider/id
|
|
472
|
+
// with no route suffix, e.g. "openai/gpt-5") must never cross to a different
|
|
473
|
+
// provider, even when a slash-embedded catalog id collides with the pin's text.
|
|
474
|
+
const slashIndex = modelPattern.indexOf("/");
|
|
475
|
+
const thinkingSuffix = splitSelectorThinkingSuffix(modelPattern);
|
|
476
|
+
const providerSegment = slashIndex === -1 ? "" : modelPattern.slice(0, slashIndex);
|
|
477
|
+
const modelIdSegment = slashIndex === -1 ? modelPattern : modelPattern.slice(slashIndex + 1);
|
|
478
|
+
const isRouteSuffixId =
|
|
479
|
+
slashIndex !== -1 &&
|
|
480
|
+
!providerSegment.includes(":") &&
|
|
481
|
+
modelIdSegment.includes(":") &&
|
|
482
|
+
thinkingSuffix.thinkingLevel === undefined;
|
|
483
|
+
if (slashIndex === -1 || isRouteSuffixId) {
|
|
484
|
+
const exactMatches = availableModels.filter(m => m.id.toLowerCase() === modelPattern.toLowerCase());
|
|
485
|
+
if (exactMatches.length > 0) {
|
|
486
|
+
return pickPreferredModel(exactMatches, context);
|
|
487
|
+
}
|
|
452
488
|
}
|
|
453
489
|
// Provider-qualified selectors are exact-only. Case-only duplicate catalog
|
|
454
490
|
// entries still rank deterministically within the explicitly named provider.
|
|
455
|
-
const slashIndex = modelPattern.indexOf("/");
|
|
456
491
|
if (slashIndex !== -1) {
|
|
457
492
|
const provider = modelPattern.slice(0, slashIndex).trim().toLowerCase();
|
|
458
493
|
const modelId = modelPattern
|
|
@@ -465,7 +500,23 @@ function tryMatchModel(
|
|
|
465
500
|
return providerExactMatches.length > 0 ? pickPreferredModel(providerExactMatches, context) : undefined;
|
|
466
501
|
}
|
|
467
502
|
|
|
468
|
-
|
|
503
|
+
return undefined;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Substring/alias fuzzy matching, run only after strict-exact and preset-alias
|
|
508
|
+
* resolution have both failed so a preset alias never loses to a coincidental
|
|
509
|
+
* substring match.
|
|
510
|
+
*/
|
|
511
|
+
function findFuzzyModelMatch(
|
|
512
|
+
modelPattern: string,
|
|
513
|
+
availableModels: Model<Api>[],
|
|
514
|
+
context: ModelPreferenceContext,
|
|
515
|
+
): Model<Api> | undefined {
|
|
516
|
+
// Provider-qualified selectors are exact pins: they never enter substring/name
|
|
517
|
+
// fuzzy matching, even when a slash-embedded catalog id shares the pin's text.
|
|
518
|
+
// Explicit glob patterns are handled separately by findGlobMatch.
|
|
519
|
+
if (modelPattern.includes("/")) return undefined;
|
|
469
520
|
const matches = availableModels.filter(
|
|
470
521
|
m =>
|
|
471
522
|
m.id.toLowerCase().includes(modelPattern.toLowerCase()) ||
|
|
@@ -496,6 +547,27 @@ function tryMatchModel(
|
|
|
496
547
|
return pickPreferredModel(topCandidates, context);
|
|
497
548
|
}
|
|
498
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Match a pattern against the available models using the full staged policy:
|
|
552
|
+
* strict-exact first, then substring/alias fuzzy matching.
|
|
553
|
+
*/
|
|
554
|
+
function tryMatchModel(
|
|
555
|
+
modelPattern: string,
|
|
556
|
+
availableModels: Model<Api>[],
|
|
557
|
+
context: ModelPreferenceContext,
|
|
558
|
+
options?: {
|
|
559
|
+
modelRegistry?: CanonicalModelRegistry;
|
|
560
|
+
sessionId?: string;
|
|
561
|
+
credentialSessionId?: string;
|
|
562
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
563
|
+
},
|
|
564
|
+
): Model<Api> | undefined {
|
|
565
|
+
return (
|
|
566
|
+
findStrictExactModelMatch(modelPattern, availableModels, context, options) ??
|
|
567
|
+
findFuzzyModelMatch(modelPattern, availableModels, context)
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
499
571
|
export interface ParsedModelResult {
|
|
500
572
|
model: Model<Api> | undefined;
|
|
501
573
|
/** Thinking level if explicitly specified in pattern, undefined otherwise */
|
|
@@ -509,12 +581,16 @@ export interface ResolveSelectorOptions {
|
|
|
509
581
|
modelRegistry?: CanonicalModelRegistry;
|
|
510
582
|
preferences?: ModelMatchPreferences;
|
|
511
583
|
sessionId?: string;
|
|
584
|
+
credentialSessionId?: string;
|
|
585
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
512
586
|
}
|
|
513
587
|
|
|
514
588
|
/**
|
|
515
589
|
* Resolve one selector through ordered exact, canonical, bare-id, provider-fuzzy,
|
|
516
|
-
* substring/alias, and glob stages.
|
|
517
|
-
*
|
|
590
|
+
* substring/alias, and glob stages. A preset-equivalent alias intent adds a
|
|
591
|
+
* final-slash-segment alias stage between strict-exact and substring/fuzzy.
|
|
592
|
+
* Thinking is split only after a full selector cannot resolve, which preserves
|
|
593
|
+
* OpenRouter route suffixes in concrete IDs.
|
|
518
594
|
*/
|
|
519
595
|
export function resolveSelector(
|
|
520
596
|
selector: string,
|
|
@@ -522,10 +598,38 @@ export function resolveSelector(
|
|
|
522
598
|
options?: ResolveSelectorOptions,
|
|
523
599
|
): ParsedModelResult {
|
|
524
600
|
const context = buildPreferenceContext(candidates, options?.preferences);
|
|
525
|
-
const exact =
|
|
601
|
+
const exact = findStrictExactModelMatch(selector, candidates, context, options);
|
|
526
602
|
if (exact) return { model: exact, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
603
|
+
|
|
604
|
+
// Preset-equivalent intent resolves a final-slash-segment alias only after
|
|
605
|
+
// strict exact full-selector/canonical/exact-ID resolution fails and BEFORE
|
|
606
|
+
// substring/fuzzy matching. A known alias that is unavailable in the caller's
|
|
607
|
+
// candidate set fails closed rather than falling through to glob/substring.
|
|
608
|
+
if (options?.aliasIntent === "preset-equivalent") {
|
|
609
|
+
const alias = resolvePresetEquivalentAlias(selector, candidates, options);
|
|
610
|
+
if (alias.model) {
|
|
611
|
+
return {
|
|
612
|
+
model: alias.model,
|
|
613
|
+
thinkingLevel: alias.thinkingLevel,
|
|
614
|
+
warning: alias.warning,
|
|
615
|
+
explicitThinkingLevel: alias.explicitThinkingLevel,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
if (alias.knownButUnavailable) {
|
|
619
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const fuzzy = findFuzzyModelMatch(selector, candidates, context);
|
|
624
|
+
if (fuzzy) return { model: fuzzy, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
625
|
+
|
|
527
626
|
const glob = findGlobMatch(selector, candidates, context);
|
|
528
627
|
if (glob) return { model: glob, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
628
|
+
const selectorSlashIndex = selector.indexOf("/");
|
|
629
|
+
const selectorColonIndex = selector.lastIndexOf(":");
|
|
630
|
+
if (selectorSlashIndex !== -1 && selectorColonIndex !== -1 && selectorColonIndex < selectorSlashIndex) {
|
|
631
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
632
|
+
}
|
|
529
633
|
|
|
530
634
|
const suffix = splitSelectorThinkingSuffix(selector);
|
|
531
635
|
if (
|
|
@@ -535,7 +639,12 @@ export function resolveSelector(
|
|
|
535
639
|
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
536
640
|
}
|
|
537
641
|
|
|
642
|
+
const suffixExact = findStrictExactModelMatch(suffix.selector, candidates, context, options);
|
|
643
|
+
if (options?.aliasIntent === "preset-equivalent" && !suffix.selector.includes("/") && !suffixExact) {
|
|
644
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
645
|
+
}
|
|
538
646
|
const model =
|
|
647
|
+
suffixExact ??
|
|
539
648
|
tryMatchModel(suffix.selector, candidates, context, options) ??
|
|
540
649
|
findGlobMatch(suffix.selector, candidates, context);
|
|
541
650
|
if (!model) return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
@@ -550,6 +659,84 @@ export function resolveSelector(
|
|
|
550
659
|
};
|
|
551
660
|
}
|
|
552
661
|
|
|
662
|
+
interface PresetEquivalentAliasResolution {
|
|
663
|
+
model?: Model<Api>;
|
|
664
|
+
thinkingLevel?: ThinkingLevel;
|
|
665
|
+
explicitThinkingLevel: boolean;
|
|
666
|
+
warning?: string;
|
|
667
|
+
knownButUnavailable?: boolean;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Resolve an unqualified preset assignment through the canonical registry's
|
|
672
|
+
* final-segment alias lookup. Preserves exact full-selector/canonical/exact-ID
|
|
673
|
+
* precedence: it is only consulted after exact resolution fails. A selector
|
|
674
|
+
* containing `/` is an explicit provider pin and never aliases. A known-but-
|
|
675
|
+
* unavailable bare alias fails closed (never falls through to glob). Thinking
|
|
676
|
+
* suffixes are split once so `alias:high` keeps its thinking level.
|
|
677
|
+
*/
|
|
678
|
+
function resolvePresetEquivalentAlias(
|
|
679
|
+
selector: string,
|
|
680
|
+
availableModels: Model<Api>[],
|
|
681
|
+
options: Pick<
|
|
682
|
+
ResolveSelectorOptions,
|
|
683
|
+
"allowInvalidThinkingSelectorFallback" | "credentialSessionId" | "modelRegistry" | "sessionId"
|
|
684
|
+
>,
|
|
685
|
+
): PresetEquivalentAliasResolution {
|
|
686
|
+
const registry = options.modelRegistry;
|
|
687
|
+
const lookupAliasExists = registry?.lookupAliasExists?.bind(registry);
|
|
688
|
+
const resolveModelByLookupAlias = registry?.resolveModelByLookupAlias?.bind(registry);
|
|
689
|
+
if (!lookupAliasExists || !resolveModelByLookupAlias) {
|
|
690
|
+
return { explicitThinkingLevel: false };
|
|
691
|
+
}
|
|
692
|
+
const candidates = availableModels;
|
|
693
|
+
const sessionId = options.sessionId;
|
|
694
|
+
|
|
695
|
+
const tryAlias = (candidate: string): { model?: Model<Api>; knownButUnavailable: boolean } | undefined => {
|
|
696
|
+
if (candidate.includes("/")) return undefined;
|
|
697
|
+
const aliasKey = getFinalSlashSegmentAliasKey(candidate);
|
|
698
|
+
if (!aliasKey) return undefined;
|
|
699
|
+
if (!lookupAliasExists(aliasKey)) return undefined;
|
|
700
|
+
const model = resolveModelByLookupAlias(aliasKey, {
|
|
701
|
+
availableOnly: false,
|
|
702
|
+
candidates,
|
|
703
|
+
sessionId,
|
|
704
|
+
credentialSessionId: options.credentialSessionId,
|
|
705
|
+
});
|
|
706
|
+
return { model, knownButUnavailable: model === undefined };
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// Full selector first — preserves route-suffix aliases without a thinking suffix.
|
|
710
|
+
const full = tryAlias(selector);
|
|
711
|
+
if (full) {
|
|
712
|
+
if (full.model) return { model: full.model, thinkingLevel: undefined, explicitThinkingLevel: false };
|
|
713
|
+
return { knownButUnavailable: true, explicitThinkingLevel: false };
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Then the base selector after splitting a single thinking suffix.
|
|
717
|
+
const suffix = splitSelectorThinkingSuffix(selector);
|
|
718
|
+
if (suffix.invalidSuffix !== undefined && !(options.allowInvalidThinkingSelectorFallback ?? true)) {
|
|
719
|
+
return { explicitThinkingLevel: false };
|
|
720
|
+
}
|
|
721
|
+
if (suffix.selector !== selector) {
|
|
722
|
+
const base = tryAlias(suffix.selector);
|
|
723
|
+
if (base) {
|
|
724
|
+
if (base.model) {
|
|
725
|
+
return {
|
|
726
|
+
model: base.model,
|
|
727
|
+
thinkingLevel: suffix.thinkingLevel,
|
|
728
|
+
explicitThinkingLevel: suffix.thinkingLevel !== undefined,
|
|
729
|
+
warning: suffix.thinkingLevel
|
|
730
|
+
? undefined
|
|
731
|
+
: `Invalid thinking level "${suffix.invalidSuffix}" in pattern "${selector}". Using default instead.`,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
return { knownButUnavailable: true, explicitThinkingLevel: false };
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return { explicitThinkingLevel: false };
|
|
739
|
+
}
|
|
553
740
|
function findGlobMatch(
|
|
554
741
|
selector: string,
|
|
555
742
|
candidates: Model<Api>[],
|
|
@@ -575,6 +762,8 @@ export function parseModelPattern(
|
|
|
575
762
|
allowInvalidThinkingSelectorFallback?: boolean;
|
|
576
763
|
modelRegistry?: CanonicalModelRegistry;
|
|
577
764
|
sessionId?: string;
|
|
765
|
+
credentialSessionId?: string;
|
|
766
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
578
767
|
},
|
|
579
768
|
): ParsedModelResult {
|
|
580
769
|
return resolveSelector(pattern, availableModels, { ...options, preferences });
|
|
@@ -690,6 +879,8 @@ export function resolveModelRoleValue(
|
|
|
690
879
|
matchPreferences?: ModelMatchPreferences;
|
|
691
880
|
modelRegistry?: CanonicalModelRegistry;
|
|
692
881
|
sessionId?: string;
|
|
882
|
+
credentialSessionId?: string;
|
|
883
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
693
884
|
},
|
|
694
885
|
): ResolvedModelRoleValue {
|
|
695
886
|
const effectivePatterns = normalizeModelPatternList(roleValue).flatMap(
|
|
@@ -704,6 +895,8 @@ export function resolveModelRoleValue(
|
|
|
704
895
|
const resolved = parseModelPattern(effectivePattern, availableModels, options?.matchPreferences, {
|
|
705
896
|
modelRegistry: options?.modelRegistry,
|
|
706
897
|
sessionId: options?.sessionId,
|
|
898
|
+
credentialSessionId: options?.credentialSessionId,
|
|
899
|
+
aliasIntent: options?.aliasIntent,
|
|
707
900
|
});
|
|
708
901
|
if (resolved.model) {
|
|
709
902
|
return {
|
|
@@ -795,6 +988,7 @@ export function resolveModelOverride(
|
|
|
795
988
|
modelRegistry: ModelLookupRegistry,
|
|
796
989
|
settings?: Settings,
|
|
797
990
|
sessionId?: string,
|
|
991
|
+
aliasIntent?: "preset-equivalent" | "reject",
|
|
798
992
|
): { model?: Model<Api>; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } {
|
|
799
993
|
if (modelPatterns.length === 0) return { explicitThinkingLevel: false };
|
|
800
994
|
const availableModels = modelRegistry.getAvailable();
|
|
@@ -805,6 +999,7 @@ export function resolveModelOverride(
|
|
|
805
999
|
matchPreferences,
|
|
806
1000
|
modelRegistry,
|
|
807
1001
|
sessionId,
|
|
1002
|
+
aliasIntent,
|
|
808
1003
|
});
|
|
809
1004
|
if (model) {
|
|
810
1005
|
return { model, thinkingLevel, explicitThinkingLevel };
|
|
@@ -822,6 +1017,9 @@ export function resolveModelOverride(
|
|
|
822
1017
|
*/
|
|
823
1018
|
export interface ModelChainResolutionOptions {
|
|
824
1019
|
managedFallback?: boolean;
|
|
1020
|
+
aliasIntent?: "preset-equivalent" | "reject";
|
|
1021
|
+
canonicalSessionId?: string | null;
|
|
1022
|
+
credentialSessionId?: string;
|
|
825
1023
|
}
|
|
826
1024
|
|
|
827
1025
|
export async function resolveModelChainWithAuth(
|
|
@@ -840,30 +1038,54 @@ export async function resolveModelChainWithAuth(
|
|
|
840
1038
|
const availableModels = modelRegistry.getAvailable();
|
|
841
1039
|
const matchPreferences = { usageOrder: settings?.getStorage()?.getModelUsageOrder() };
|
|
842
1040
|
const skips: Array<{ selector: string; reason: string }> = [];
|
|
1041
|
+
const authFailedModels = new Set<string>();
|
|
1042
|
+
const canonicalSessionId =
|
|
1043
|
+
options?.canonicalSessionId === null ? undefined : (options?.canonicalSessionId ?? sessionId);
|
|
1044
|
+
const credentialSessionId = options?.credentialSessionId ?? sessionId;
|
|
843
1045
|
for (let activeIndex = 0; activeIndex < modelPatterns.length; activeIndex += 1) {
|
|
844
1046
|
const selector = modelPatterns[activeIndex];
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1047
|
+
const suffix = splitSelectorThinkingSuffix(selector);
|
|
1048
|
+
const aliasSelector = suffix.thinkingLevel ? suffix.selector : selector;
|
|
1049
|
+
const aliasKey = aliasSelector.includes("/") ? undefined : getFinalSlashSegmentAliasKey(aliasSelector);
|
|
1050
|
+
const retryEquivalentAlias =
|
|
1051
|
+
options?.aliasIntent === "preset-equivalent" &&
|
|
1052
|
+
aliasKey !== undefined &&
|
|
1053
|
+
(modelRegistry.lookupAliasExists?.(aliasKey) ?? false);
|
|
1054
|
+
let attemptedCandidate = false;
|
|
1055
|
+
while (true) {
|
|
1056
|
+
const candidates = availableModels.filter(
|
|
1057
|
+
model => !authFailedModels.has(`${model.provider}\u0000${model.id}`),
|
|
1058
|
+
);
|
|
1059
|
+
const candidate = resolveModelRoleValue(selector, candidates, {
|
|
1060
|
+
settings,
|
|
1061
|
+
matchPreferences,
|
|
1062
|
+
modelRegistry,
|
|
1063
|
+
sessionId: canonicalSessionId,
|
|
1064
|
+
credentialSessionId,
|
|
1065
|
+
aliasIntent: options?.aliasIntent,
|
|
1066
|
+
});
|
|
1067
|
+
if (!candidate.model) {
|
|
1068
|
+
if (!attemptedCandidate) skips.push({ selector, reason: "unknown_model" });
|
|
1069
|
+
break;
|
|
860
1070
|
}
|
|
1071
|
+
if (options?.managedFallback && modelPatterns.length > 1) {
|
|
1072
|
+
const cursorReason = managedCursorFallbackUnavailableReason(candidate.model, selector);
|
|
1073
|
+
if (cursorReason) {
|
|
1074
|
+
skips.push({ selector, reason: cursorReason });
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
const key = await modelRegistry.getApiKey(candidate.model, credentialSessionId);
|
|
1079
|
+
if (isAuthenticatedOrKeyless(key)) {
|
|
1080
|
+
return { ...candidate, activeIndex, skips };
|
|
1081
|
+
}
|
|
1082
|
+
skips.push({ selector, reason: "unauthenticated" });
|
|
1083
|
+
attemptedCandidate = true;
|
|
1084
|
+
authFailedModels.add(`${candidate.model.provider}\u0000${candidate.model.id}`);
|
|
1085
|
+
if (!retryEquivalentAlias) break;
|
|
1086
|
+
const stickySessionId = canonicalSessionId;
|
|
1087
|
+
if (stickySessionId) modelRegistry.clearCanonicalVariant?.(stickySessionId);
|
|
861
1088
|
}
|
|
862
|
-
const key = await modelRegistry.getApiKey(candidate.model, sessionId);
|
|
863
|
-
if (isAuthenticatedOrKeyless(key)) {
|
|
864
|
-
return { ...candidate, activeIndex, skips };
|
|
865
|
-
}
|
|
866
|
-
skips.push({ selector, reason: "unauthenticated" });
|
|
867
1089
|
}
|
|
868
1090
|
return { explicitThinkingLevel: false, activeIndex: modelPatterns.length, skips };
|
|
869
1091
|
}
|
|
@@ -916,7 +1138,13 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
916
1138
|
let activeIndex = 0;
|
|
917
1139
|
const canonicalScope = canonicalSessionId ?? authSessionId;
|
|
918
1140
|
if (canonicalScope && parentActiveModelPattern) {
|
|
919
|
-
const parentActiveModel = resolveModelOverride(
|
|
1141
|
+
const parentActiveModel = resolveModelOverride(
|
|
1142
|
+
[parentActiveModelPattern],
|
|
1143
|
+
modelRegistry,
|
|
1144
|
+
settings,
|
|
1145
|
+
undefined,
|
|
1146
|
+
options?.aliasIntent,
|
|
1147
|
+
).model;
|
|
920
1148
|
if (parentActiveModel) {
|
|
921
1149
|
modelRegistry.seedCanonicalVariant?.(canonicalScope, parentActiveModel);
|
|
922
1150
|
}
|
|
@@ -927,6 +1155,8 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
927
1155
|
matchPreferences,
|
|
928
1156
|
modelRegistry,
|
|
929
1157
|
sessionId: canonicalScope,
|
|
1158
|
+
credentialSessionId: authSessionId,
|
|
1159
|
+
aliasIntent: options?.aliasIntent,
|
|
930
1160
|
});
|
|
931
1161
|
if (!requestedModel && candidate.model) {
|
|
932
1162
|
requestedModel = candidate.model;
|
|
@@ -949,11 +1179,32 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
949
1179
|
if (isAuthenticatedOrKeyless(key)) {
|
|
950
1180
|
return { ...candidate, requestedModel: candidate.model, authFallbackUsed: false, activeIndex, skips };
|
|
951
1181
|
}
|
|
1182
|
+
if (
|
|
1183
|
+
options?.aliasIntent === "preset-equivalent" &&
|
|
1184
|
+
!splitSelectorThinkingSuffix(pattern).selector.includes("/")
|
|
1185
|
+
) {
|
|
1186
|
+
const equivalentRetry = await resolveModelChainWithAuth([pattern], modelRegistry, settings, authSessionId, {
|
|
1187
|
+
managedFallback: options.managedFallback,
|
|
1188
|
+
aliasIntent: options.aliasIntent,
|
|
1189
|
+
canonicalSessionId: canonicalScope,
|
|
1190
|
+
});
|
|
1191
|
+
if (equivalentRetry.model) {
|
|
1192
|
+
return {
|
|
1193
|
+
model: equivalentRetry.model,
|
|
1194
|
+
thinkingLevel: equivalentRetry.thinkingLevel,
|
|
1195
|
+
explicitThinkingLevel: equivalentRetry.explicitThinkingLevel,
|
|
1196
|
+
requestedModel: candidate.model,
|
|
1197
|
+
authFallbackUsed: !modelsAreEqual(candidate.model, equivalentRetry.model),
|
|
1198
|
+
activeIndex,
|
|
1199
|
+
skips: [...skips, ...equivalentRetry.skips],
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
952
1203
|
skips.push({ selector: pattern, reason: "unauthenticated" });
|
|
953
1204
|
activeIndex += 1;
|
|
954
1205
|
}
|
|
955
1206
|
const fallback = parentActiveModelPattern
|
|
956
|
-
? resolveModelOverride([parentActiveModelPattern], modelRegistry, settings, authSessionId)
|
|
1207
|
+
? resolveModelOverride([parentActiveModelPattern], modelRegistry, settings, authSessionId, options?.aliasIntent)
|
|
957
1208
|
: { explicitThinkingLevel: false };
|
|
958
1209
|
if (fallback.model) {
|
|
959
1210
|
const fallbackKey = await modelRegistry.getApiKey(fallback.model, authSessionId);
|
|
@@ -57,7 +57,14 @@ export const ModelCompatSchema = z.object({
|
|
|
57
57
|
export const OpenAICompatSchema = ModelCompatSchema;
|
|
58
58
|
|
|
59
59
|
export const GJC_MODEL_EFFORT_IDS = ["minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
60
|
-
export const GJC_MODEL_ASSIGNMENT_TARGET_IDS = [
|
|
60
|
+
export const GJC_MODEL_ASSIGNMENT_TARGET_IDS = [
|
|
61
|
+
"default",
|
|
62
|
+
"executor",
|
|
63
|
+
"architect",
|
|
64
|
+
"planner",
|
|
65
|
+
"critic",
|
|
66
|
+
"image",
|
|
67
|
+
] as const;
|
|
61
68
|
export const EffortSchema = z.enum(GJC_MODEL_EFFORT_IDS);
|
|
62
69
|
const CacheRetentionSchema = z.enum(["none", "short", "long"]);
|
|
63
70
|
|
|
@@ -94,13 +101,13 @@ export const ModelBindingsSchema = z.object({
|
|
|
94
101
|
});
|
|
95
102
|
|
|
96
103
|
export const ProfileRoleSchema = z.enum(GJC_MODEL_ASSIGNMENT_TARGET_IDS);
|
|
97
|
-
export const ProfileModelSelectorPattern =
|
|
104
|
+
export const ProfileModelSelectorPattern = "^(?:[^,/]+/[^,]*[^,:]|[^/,]*[^/,:])$";
|
|
98
105
|
|
|
99
106
|
export const ProfileModelSelectorSchema = z
|
|
100
107
|
.string()
|
|
101
108
|
.trim()
|
|
102
109
|
.min(1)
|
|
103
|
-
.regex(new RegExp(ProfileModelSelectorPattern), "Expected provider/modelId with optional :effort suffix");
|
|
110
|
+
.regex(new RegExp(ProfileModelSelectorPattern), "Expected modelId or provider/modelId with optional :effort suffix");
|
|
104
111
|
export const ProfileModelMappingSchema = z.partialRecord(
|
|
105
112
|
ProfileRoleSchema,
|
|
106
113
|
stringOrNonEmptyArray(ProfileModelSelectorSchema),
|
|
@@ -108,7 +115,7 @@ export const ProfileModelMappingSchema = z.partialRecord(
|
|
|
108
115
|
|
|
109
116
|
export const ProfileDefinitionSchema = z
|
|
110
117
|
.object({
|
|
111
|
-
required_providers: z.array(z.string().min(1))
|
|
118
|
+
required_providers: z.array(z.string().min(1)),
|
|
112
119
|
display_name: z.string().min(1).optional(),
|
|
113
120
|
model_mapping: ProfileModelMappingSchema,
|
|
114
121
|
})
|