@gajae-code/coding-agent 0.18.12 → 0.19.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 +467 -0
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +7 -2
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +8 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +23 -1
- package/dist/types/config/config-hot-reload.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +44 -2
- package/dist/types/config/model-profile-activation.d.ts +19 -5
- package/dist/types/config/model-registry.d.ts +16 -3
- package/dist/types/config/model-resolver.d.ts +5 -4
- package/dist/types/config/settings-schema.d.ts +11 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +9 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +1 -1
- package/dist/types/migrate/adapters/index.d.ts +35 -3
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +69 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +22 -1
- package/dist/types/sdk/broker/ensure.d.ts +5 -2
- package/dist/types/sdk/broker/hop.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/session-index.d.ts +2 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/index.d.ts +21 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +8 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/session/agent-session.d.ts +50 -8
- package/dist/types/session/session-manager.d.ts +16 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +13 -2
- package/dist/types/tools/fetch.d.ts +6 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/package.json +9 -8
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/generate-sdk-operation-inventory.ts +16 -0
- package/src/capability/fs.ts +31 -3
- package/src/capability/rule.ts +11 -2
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/fast-help.ts +0 -2
- package/src/cli/migrate-cli.ts +14 -3
- package/src/cli/public-command-errors.ts +2 -0
- package/src/cli/root-flags.ts +0 -2
- package/src/cli/update-cli.ts +247 -6
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +81 -11
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +20 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload.ts +8 -0
- package/src/config/file-lock.ts +1431 -304
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-profile-activation.ts +177 -39
- package/src/config/model-registry.ts +380 -55
- package/src/config/model-resolver.ts +49 -21
- package/src/config/settings-schema.ts +13 -1
- package/src/config/settings.ts +2 -0
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +3 -1
- package/src/customization/import.ts +276 -7
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +87 -5
- package/src/debug/report-bundle.ts +71 -2
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +24 -14
- package/src/edit/index.ts +191 -5
- package/src/edit/modes/patch.ts +27 -0
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +60 -20
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/extensions/loader.ts +2 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +4 -1
- package/src/gjc-runtime/launch-tmux.ts +23 -13
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +3 -1
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +14 -14
- package/src/internal-urls/local-protocol.ts +28 -3
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +517 -2
- package/src/migrate/adapters/claude-code.ts +141 -3
- package/src/migrate/adapters/codex.ts +57 -5
- package/src/migrate/adapters/index.ts +240 -5
- package/src/migrate/adapters/opencode.ts +4 -0
- package/src/migrate/executor.ts +141 -3
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +45 -1
- package/src/migrate/types.ts +73 -3
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +271 -16
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +49 -4
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +14 -1
- package/src/modes/interactive-mode.ts +55 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/runtime-mcp/manager.ts +8 -3
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/tool-bridge.ts +68 -4
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +2 -0
- package/src/sdk/broker/broker.ts +143 -4
- package/src/sdk/broker/ensure.ts +74 -6
- package/src/sdk/broker/hop.ts +13 -1
- package/src/sdk/broker/lifecycle-ledger.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/session-index.ts +29 -4
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +408 -14
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1537 -24
- package/src/sdk/host/session-runtime.ts +332 -36
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +14 -1
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +28 -1
- package/src/sdk/protocol/operation-inventory.generated.json +160 -0
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/session.ts +123 -36
- package/src/session/agent-session.ts +1614 -329
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +47 -18
- package/src/session/internal/managed-session-storage.ts +71 -1
- package/src/session/session-manager.ts +90 -5
- package/src/session-import/redact.ts +53 -2
- package/src/skill-state/workflow-mutation-guard.ts +1498 -18
- package/src/slash-commands/builtin-registry.ts +6 -3
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +12 -8
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/auto-generated-guard.ts +12 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +61 -1
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +99 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +9 -2
- package/src/tools/descriptors.test.ts +4 -4
- package/src/tools/descriptors.ts +16 -4
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +90 -2
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +51 -3
- package/src/tools/read.ts +9 -2
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent.ts +10 -4
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +7 -0
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +3 -2
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +46 -6
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +65 -20
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
} from "@gajae-code/ai/core";
|
|
46
46
|
import { fetchModelsDevPayload } from "@gajae-code/ai/provider-models/openai-compat";
|
|
47
47
|
import { isDirectXaiReasoningEffortModel } from "@gajae-code/ai/providers/openai-completions-compat";
|
|
48
|
+
import { getAntigravityDiscoveryUserAgent } from "@gajae-code/ai/utils/discovery/antigravity";
|
|
48
49
|
import {
|
|
49
50
|
detectDiscoveredApiFamily,
|
|
50
51
|
isSafeCatalogModelId,
|
|
@@ -1115,7 +1116,8 @@ function filterMaterializedRegistryProfiles(
|
|
|
1115
1116
|
type SpecialProviderDescriptor = {
|
|
1116
1117
|
providerId: string;
|
|
1117
1118
|
resolveKey: (value: string | undefined) => string | undefined;
|
|
1118
|
-
|
|
1119
|
+
resolveProjectId?: (key: string, rawValue: string | undefined) => string | undefined;
|
|
1120
|
+
createOptions: (key: string, projectId?: string) => ModelManagerOptions<Api>;
|
|
1119
1121
|
};
|
|
1120
1122
|
|
|
1121
1123
|
const PROVIDER_BASE_URL_ENV_ALIASES: Record<string, readonly string[]> = {
|
|
@@ -1297,17 +1299,33 @@ function fingerprintConfiguredDiscoveryRequestShape(
|
|
|
1297
1299
|
return crypto.createHash("sha256").update("gajae:model-discovery-provenance\0").update(context).digest("hex");
|
|
1298
1300
|
}
|
|
1299
1301
|
|
|
1300
|
-
function fingerprintDescriptorDiscoveryProvenance(
|
|
1302
|
+
function fingerprintDescriptorDiscoveryProvenance(
|
|
1303
|
+
providerId: string,
|
|
1304
|
+
authEvidence: string,
|
|
1305
|
+
endpoint: string,
|
|
1306
|
+
projectId?: string,
|
|
1307
|
+
): string | undefined {
|
|
1301
1308
|
try {
|
|
1302
1309
|
const parsed = new URL(endpoint);
|
|
1303
1310
|
if (parsed.username || parsed.password || parsed.search) return undefined;
|
|
1304
1311
|
} catch {
|
|
1305
1312
|
return undefined;
|
|
1306
1313
|
}
|
|
1314
|
+
const discoveryIdentity =
|
|
1315
|
+
providerId === "google-antigravity" || providerId === "google-gemini-cli"
|
|
1316
|
+
? getAntigravityDiscoveryUserAgent()
|
|
1317
|
+
: undefined;
|
|
1307
1318
|
return crypto
|
|
1308
1319
|
.createHash("sha256")
|
|
1309
1320
|
.update("gajae:model-discovery-provenance\0")
|
|
1310
|
-
.update(
|
|
1321
|
+
.update(
|
|
1322
|
+
JSON.stringify({
|
|
1323
|
+
authEvidence,
|
|
1324
|
+
endpoint,
|
|
1325
|
+
...(projectId !== undefined ? { projectId } : {}),
|
|
1326
|
+
...(discoveryIdentity !== undefined ? { discoveryIdentity } : {}),
|
|
1327
|
+
}),
|
|
1328
|
+
)
|
|
1311
1329
|
.digest("hex");
|
|
1312
1330
|
}
|
|
1313
1331
|
|
|
@@ -1444,6 +1462,48 @@ function getOAuthCredentialsForProvider(authStorage: AuthStorage, provider: stri
|
|
|
1444
1462
|
return entries.filter((entry): entry is OAuthCredential => entry.type === "oauth");
|
|
1445
1463
|
}
|
|
1446
1464
|
|
|
1465
|
+
/** Internal pure parser shared by discovery; exported for regression coverage. */
|
|
1466
|
+
export function extractGoogleOAuthProjectId(value: string | undefined, accessToken: string): string | undefined {
|
|
1467
|
+
if (!value) return undefined;
|
|
1468
|
+
try {
|
|
1469
|
+
const parsed = JSON.parse(value) as { token?: unknown; projectId?: unknown; project_id?: unknown };
|
|
1470
|
+
if (typeof parsed.token !== "string" || parsed.token.trim() !== accessToken) return undefined;
|
|
1471
|
+
const projectId = typeof parsed.projectId === "string" ? parsed.projectId : parsed.project_id;
|
|
1472
|
+
return typeof projectId === "string" ? projectId.trim() || undefined : undefined;
|
|
1473
|
+
} catch {
|
|
1474
|
+
return undefined;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function resolveGoogleOAuthProjectId(
|
|
1479
|
+
authStorage: AuthStorage,
|
|
1480
|
+
provider: string,
|
|
1481
|
+
accessToken: string,
|
|
1482
|
+
rawValue?: string,
|
|
1483
|
+
sessionId?: string,
|
|
1484
|
+
owner?: object,
|
|
1485
|
+
): string | undefined {
|
|
1486
|
+
const configuredProjectId = extractGoogleOAuthProjectId(rawValue, accessToken);
|
|
1487
|
+
if (authStorage.getEffectiveCredentialType(provider, sessionId, owner ? { owner } : undefined) !== "oauth") {
|
|
1488
|
+
return configuredProjectId;
|
|
1489
|
+
}
|
|
1490
|
+
const selector = authStorage.resolveEffectiveCredentialSelector(provider, sessionId);
|
|
1491
|
+
if (selector) {
|
|
1492
|
+
const selectedCredential = authStorage.getOAuthCredential(provider, sessionId, owner ? { owner } : undefined);
|
|
1493
|
+
if (selectedCredential?.access !== accessToken) return configuredProjectId;
|
|
1494
|
+
return selectedCredential.projectId?.trim() || configuredProjectId;
|
|
1495
|
+
}
|
|
1496
|
+
const matchingCredentials = getOAuthCredentialsForProvider(authStorage, provider).filter(
|
|
1497
|
+
credential => credential.access === accessToken,
|
|
1498
|
+
);
|
|
1499
|
+
if (matchingCredentials.length === 0) return configuredProjectId;
|
|
1500
|
+
const projectId = matchingCredentials[0]?.projectId?.trim();
|
|
1501
|
+
if (matchingCredentials.some(credential => (credential.projectId?.trim() ?? "") !== (projectId ?? ""))) {
|
|
1502
|
+
return configuredProjectId;
|
|
1503
|
+
}
|
|
1504
|
+
return projectId || configuredProjectId;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1447
1507
|
function resolveOAuthAccountIdForAccessToken(
|
|
1448
1508
|
authStorage: AuthStorage,
|
|
1449
1509
|
provider: string,
|
|
@@ -2010,6 +2070,12 @@ interface ModelManagerDiscoveryOptions {
|
|
|
2010
2070
|
apiKey: string | undefined;
|
|
2011
2071
|
endpoint: string;
|
|
2012
2072
|
endpointContainsUserinfo: boolean;
|
|
2073
|
+
projectId?: string;
|
|
2074
|
+
isCurrentContext?: () => boolean;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
interface GoogleSessionDiscoveryCatalog {
|
|
2078
|
+
models: Model<Api>[];
|
|
2013
2079
|
}
|
|
2014
2080
|
|
|
2015
2081
|
interface ConfiguredDiscoveryResult {
|
|
@@ -2232,9 +2298,11 @@ export class ModelRegistry {
|
|
|
2232
2298
|
bySelector: new Map(),
|
|
2233
2299
|
aliases: new Map(),
|
|
2234
2300
|
};
|
|
2235
|
-
#availableModelsCache
|
|
2236
|
-
|
|
2237
|
-
|
|
2301
|
+
#availableModelsCache = new Map<
|
|
2302
|
+
string,
|
|
2303
|
+
{ models: Model<Api>[]; disabledProviders: string; envFingerprint: string }
|
|
2304
|
+
>();
|
|
2305
|
+
#googleSessionDiscoveryCatalogs = new Map<string, Map<string, GoogleSessionDiscoveryCatalog>>();
|
|
2238
2306
|
#sessionCanonicalVariants = new Map<string, string>();
|
|
2239
2307
|
#customProviderApiKeys: Map<string, string> = new Map();
|
|
2240
2308
|
#customProviderApiKeyEnvNames: Map<string, string> = new Map();
|
|
@@ -2254,6 +2322,7 @@ export class ModelRegistry {
|
|
|
2254
2322
|
modelIds: ReadonlySet<string>;
|
|
2255
2323
|
authGeneration: string;
|
|
2256
2324
|
endpoint: string;
|
|
2325
|
+
projectId?: string;
|
|
2257
2326
|
}
|
|
2258
2327
|
>();
|
|
2259
2328
|
#descriptorDiscoveryGenerations = new Map<string, number>();
|
|
@@ -2295,7 +2364,7 @@ export class ModelRegistry {
|
|
|
2295
2364
|
#cacheDbPath?: string;
|
|
2296
2365
|
#suppressedSelectors: Map<string, number> = new Map();
|
|
2297
2366
|
#selectorCircuits: Map<string, SelectorCircuit> = new Map();
|
|
2298
|
-
#
|
|
2367
|
+
#backgroundRefreshes: Map<string, Promise<void>> = new Map();
|
|
2299
2368
|
#catalogMutationTail: Promise<void> = Promise.resolve();
|
|
2300
2369
|
#pendingCatalogMutations = 0;
|
|
2301
2370
|
#catalogRefreshGeneration = 0;
|
|
@@ -2392,6 +2461,12 @@ export class ModelRegistry {
|
|
|
2392
2461
|
|
|
2393
2462
|
#copyRuntimeStateFrom(source: ModelRegistry): void {
|
|
2394
2463
|
this.#sessionCanonicalVariants = new Map(source.#sessionCanonicalVariants);
|
|
2464
|
+
this.#googleSessionDiscoveryCatalogs = new Map(
|
|
2465
|
+
[...source.#googleSessionDiscoveryCatalogs].map(([provider, catalogs]) => [
|
|
2466
|
+
provider,
|
|
2467
|
+
new Map([...catalogs].map(([identity, catalog]) => [identity, { models: [...catalog.models] }])),
|
|
2468
|
+
]),
|
|
2469
|
+
);
|
|
2395
2470
|
this.#suppressedSelectors = new Map(source.#suppressedSelectors);
|
|
2396
2471
|
this.#selectorCircuits = new Map(
|
|
2397
2472
|
[...source.#selectorCircuits].map(([selector, circuit]) => [selector, structuredClone(circuit)]),
|
|
@@ -2471,13 +2546,9 @@ export class ModelRegistry {
|
|
|
2471
2546
|
other.#availableModelsCache,
|
|
2472
2547
|
this.#availableModelsCache,
|
|
2473
2548
|
];
|
|
2474
|
-
[this.#
|
|
2475
|
-
other.#
|
|
2476
|
-
this.#
|
|
2477
|
-
];
|
|
2478
|
-
[this.#availableModelsEnvFingerprint, other.#availableModelsEnvFingerprint] = [
|
|
2479
|
-
other.#availableModelsEnvFingerprint,
|
|
2480
|
-
this.#availableModelsEnvFingerprint,
|
|
2549
|
+
[this.#googleSessionDiscoveryCatalogs, other.#googleSessionDiscoveryCatalogs] = [
|
|
2550
|
+
other.#googleSessionDiscoveryCatalogs,
|
|
2551
|
+
this.#googleSessionDiscoveryCatalogs,
|
|
2481
2552
|
];
|
|
2482
2553
|
[this.#customProviderApiKeys, other.#customProviderApiKeys] = [
|
|
2483
2554
|
other.#customProviderApiKeys,
|
|
@@ -2626,7 +2697,7 @@ export class ModelRegistry {
|
|
|
2626
2697
|
other.#modelBindingsApplier.setBindings(previousBindings);
|
|
2627
2698
|
this.#modelBindingsApplier.apply();
|
|
2628
2699
|
this.#modelsConfigFile.invalidate();
|
|
2629
|
-
this.#
|
|
2700
|
+
this.#invalidateAvailableModels();
|
|
2630
2701
|
}
|
|
2631
2702
|
|
|
2632
2703
|
#reconcileOwnedConfigApiKeys(
|
|
@@ -2792,6 +2863,7 @@ export class ModelRegistry {
|
|
|
2792
2863
|
for (const provider of changedProviders) {
|
|
2793
2864
|
copyProviderEntry(registry.#configuredDiscoveryEvidence, this.#configuredDiscoveryEvidence, provider);
|
|
2794
2865
|
copyProviderEntry(registry.#descriptorDiscoveryEvidence, this.#descriptorDiscoveryEvidence, provider);
|
|
2866
|
+
registry.#googleSessionDiscoveryCatalogs.delete(provider);
|
|
2795
2867
|
copyProviderEntry(registry.#descriptorDiscoveryGenerations, this.#descriptorDiscoveryGenerations, provider);
|
|
2796
2868
|
copyProviderEntry(registry.#providerEvidenceApiKeys, this.#providerEvidenceApiKeys, provider);
|
|
2797
2869
|
copyProviderEntry(
|
|
@@ -3088,9 +3160,12 @@ export class ModelRegistry {
|
|
|
3088
3160
|
}
|
|
3089
3161
|
|
|
3090
3162
|
refreshInBackground(strategy: ModelRefreshStrategy = "online-if-uncached", credentialSessionId?: string): void {
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3163
|
+
// Each credential context has an isolated discovery catalog. Coalescing every
|
|
3164
|
+
// request behind one global promise discarded B's refresh when A was pending.
|
|
3165
|
+
// Equivalent contexts still coalesce; distinct ones queue through the catalog
|
|
3166
|
+
// mutation tail so their overlays remain independently populated.
|
|
3167
|
+
const contextKey = `${strategy}\u0000${credentialSessionId ?? ""}`;
|
|
3168
|
+
if (this.#backgroundRefreshes.has(contextKey)) return;
|
|
3094
3169
|
const refreshPromise = this.refresh(strategy, credentialSessionId)
|
|
3095
3170
|
.catch(error => {
|
|
3096
3171
|
logger.warn("background model refresh failed", {
|
|
@@ -3098,11 +3173,11 @@ export class ModelRegistry {
|
|
|
3098
3173
|
});
|
|
3099
3174
|
})
|
|
3100
3175
|
.finally(() => {
|
|
3101
|
-
if (this.#
|
|
3102
|
-
this.#
|
|
3176
|
+
if (this.#backgroundRefreshes.get(contextKey) === refreshPromise) {
|
|
3177
|
+
this.#backgroundRefreshes.delete(contextKey);
|
|
3103
3178
|
}
|
|
3104
3179
|
});
|
|
3105
|
-
this.#
|
|
3180
|
+
this.#backgroundRefreshes.set(contextKey, refreshPromise);
|
|
3106
3181
|
}
|
|
3107
3182
|
|
|
3108
3183
|
async refreshProvider(
|
|
@@ -3698,6 +3773,7 @@ export class ModelRegistry {
|
|
|
3698
3773
|
}
|
|
3699
3774
|
const cache = readModelCache<Api>(descriptor.providerId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
|
|
3700
3775
|
const expectedProvenance = fingerprintDescriptorDiscoveryProvenance(
|
|
3776
|
+
descriptor.providerId,
|
|
3701
3777
|
authEvidence ?? this.#getProviderEvidenceGeneration(descriptor.providerId),
|
|
3702
3778
|
this.#normalizeDiscoveryEvidenceEndpoint(this.#getProviderBaseUrlForDiscovery(descriptor.providerId) ?? ""),
|
|
3703
3779
|
);
|
|
@@ -4367,6 +4443,12 @@ export class ModelRegistry {
|
|
|
4367
4443
|
credentialSessionId?: string,
|
|
4368
4444
|
): Promise<void> {
|
|
4369
4445
|
const profileAvailabilityEvidenceBefore = this.#profileAvailabilityEvidenceFingerprint();
|
|
4446
|
+
const googleDescriptorGenerationsBefore = new Map(
|
|
4447
|
+
(["google-antigravity", "google-gemini-cli"] as const).map(provider => [
|
|
4448
|
+
provider,
|
|
4449
|
+
this.#descriptorDiscoveryGenerations.get(provider) ?? 0,
|
|
4450
|
+
]),
|
|
4451
|
+
);
|
|
4370
4452
|
const disabledProviders = getDisabledProviderIdsFromSettings(this.#settings);
|
|
4371
4453
|
const selectedDiscoverableProviders = (
|
|
4372
4454
|
providerFilter
|
|
@@ -4518,6 +4600,38 @@ export class ModelRegistry {
|
|
|
4518
4600
|
return false;
|
|
4519
4601
|
}
|
|
4520
4602
|
});
|
|
4603
|
+
let sessionCatalogChanged = false;
|
|
4604
|
+
for (const provider of ["google-antigravity", "google-gemini-cli"] as const) {
|
|
4605
|
+
if (
|
|
4606
|
+
this.authStorage.getEffectiveCredentialType(provider, credentialSessionId, {
|
|
4607
|
+
owner: this.#authStorageConfigOwner,
|
|
4608
|
+
}) !== "oauth"
|
|
4609
|
+
) {
|
|
4610
|
+
continue;
|
|
4611
|
+
}
|
|
4612
|
+
if (
|
|
4613
|
+
(this.#descriptorDiscoveryGenerations.get(provider) ?? 0) ===
|
|
4614
|
+
googleDescriptorGenerationsBefore.get(provider)
|
|
4615
|
+
) {
|
|
4616
|
+
continue;
|
|
4617
|
+
}
|
|
4618
|
+
const evidence = this.#descriptorDiscoveryEvidence.get(provider);
|
|
4619
|
+
if (!evidence) continue;
|
|
4620
|
+
const identity = this.#googleDescriptorContextIdentity(
|
|
4621
|
+
provider,
|
|
4622
|
+
evidence.authGeneration,
|
|
4623
|
+
evidence.endpoint,
|
|
4624
|
+
evidence.projectId,
|
|
4625
|
+
);
|
|
4626
|
+
if (!identity || identity !== this.#googleSessionDescriptorIdentity(provider, credentialSessionId)) continue;
|
|
4627
|
+
const catalogs = this.#googleSessionDiscoveryCatalogs.get(provider) ?? new Map();
|
|
4628
|
+
const models = currentBuiltInDiscovered.filter(model => model.provider === provider);
|
|
4629
|
+
const previous = catalogs.get(identity);
|
|
4630
|
+
if (models.length === 0 && evidence.fresh === false && previous !== undefined) continue;
|
|
4631
|
+
if (!isDeepStrictEqual(previous?.models, models)) sessionCatalogChanged = true;
|
|
4632
|
+
catalogs.set(identity, { models });
|
|
4633
|
+
this.#googleSessionDiscoveryCatalogs.set(provider, catalogs);
|
|
4634
|
+
}
|
|
4521
4635
|
const configuredDiscoveryEvidence = new Map(
|
|
4522
4636
|
currentConfiguredDiscoveryResults
|
|
4523
4637
|
.filter(result => result.current)
|
|
@@ -4585,6 +4699,10 @@ export class ModelRegistry {
|
|
|
4585
4699
|
);
|
|
4586
4700
|
});
|
|
4587
4701
|
}
|
|
4702
|
+
if (sessionCatalogChanged) {
|
|
4703
|
+
this.#invalidateAvailableModels();
|
|
4704
|
+
this.#notifyCatalogChanged();
|
|
4705
|
+
}
|
|
4588
4706
|
if (discovered.length === 0) {
|
|
4589
4707
|
if (
|
|
4590
4708
|
clearPublishedModelIds.size > 0 ||
|
|
@@ -4597,6 +4715,53 @@ export class ModelRegistry {
|
|
|
4597
4715
|
this.#mergeDiscoveredModels(discovered);
|
|
4598
4716
|
}
|
|
4599
4717
|
|
|
4718
|
+
#googleDescriptorContextIdentity(
|
|
4719
|
+
provider: "google-antigravity" | "google-gemini-cli",
|
|
4720
|
+
authGeneration: string,
|
|
4721
|
+
endpoint: string,
|
|
4722
|
+
projectId?: string,
|
|
4723
|
+
): string | undefined {
|
|
4724
|
+
return fingerprintDescriptorDiscoveryProvenance(provider, authGeneration, endpoint, projectId);
|
|
4725
|
+
}
|
|
4726
|
+
|
|
4727
|
+
#googleSessionDescriptorIdentity(
|
|
4728
|
+
provider: "google-antigravity" | "google-gemini-cli",
|
|
4729
|
+
credentialSessionId?: string,
|
|
4730
|
+
): string | undefined {
|
|
4731
|
+
if (
|
|
4732
|
+
this.authStorage.getEffectiveCredentialType(provider, credentialSessionId, {
|
|
4733
|
+
owner: this.#authStorageConfigOwner,
|
|
4734
|
+
}) !== "oauth"
|
|
4735
|
+
) {
|
|
4736
|
+
return undefined;
|
|
4737
|
+
}
|
|
4738
|
+
const credential = this.authStorage.getOAuthCredential(provider, credentialSessionId, {
|
|
4739
|
+
owner: this.#authStorageConfigOwner,
|
|
4740
|
+
});
|
|
4741
|
+
if (!credential) return undefined;
|
|
4742
|
+
const endpoint = this.#normalizeDiscoveryEvidenceEndpoint(this.#getProviderBaseUrlForDiscovery(provider) ?? "");
|
|
4743
|
+
try {
|
|
4744
|
+
return this.#googleDescriptorContextIdentity(
|
|
4745
|
+
provider,
|
|
4746
|
+
this.#getProviderEvidenceGeneration(provider, credential.access),
|
|
4747
|
+
endpoint,
|
|
4748
|
+
credential.projectId?.trim() || undefined,
|
|
4749
|
+
);
|
|
4750
|
+
} catch {
|
|
4751
|
+
return undefined;
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4755
|
+
#getGoogleSessionDiscoveryModels(credentialSessionId?: string): Model<Api>[] {
|
|
4756
|
+
const models: Model<Api>[] = [];
|
|
4757
|
+
for (const provider of ["google-antigravity", "google-gemini-cli"] as const) {
|
|
4758
|
+
const identity = this.#googleSessionDescriptorIdentity(provider, credentialSessionId);
|
|
4759
|
+
if (!identity) continue;
|
|
4760
|
+
models.push(...(this.#googleSessionDiscoveryCatalogs.get(provider)?.get(identity)?.models ?? []));
|
|
4761
|
+
}
|
|
4762
|
+
return models;
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4600
4765
|
#profileAvailabilityEvidenceFingerprint(): string {
|
|
4601
4766
|
return JSON.stringify({
|
|
4602
4767
|
descriptor: [...this.#descriptorDiscoveryEvidence.entries()]
|
|
@@ -4606,6 +4771,7 @@ export class ModelRegistry {
|
|
|
4606
4771
|
evidence.fresh,
|
|
4607
4772
|
evidence.authGeneration,
|
|
4608
4773
|
evidence.endpoint,
|
|
4774
|
+
evidence.projectId,
|
|
4609
4775
|
[...evidence.modelIds].sort(),
|
|
4610
4776
|
]),
|
|
4611
4777
|
configured: [...this.#configuredDiscoveryEvidence.entries()]
|
|
@@ -4619,23 +4785,37 @@ export class ModelRegistry {
|
|
|
4619
4785
|
});
|
|
4620
4786
|
}
|
|
4621
4787
|
|
|
4788
|
+
#removeDescriptorDiscoveredModels(provider: string, modelIds: ReadonlySet<string>): boolean {
|
|
4789
|
+
const staticModelIds = this.#providerActivity.get(provider)?.staticModelIds;
|
|
4790
|
+
const previousLength = this.#models.length;
|
|
4791
|
+
this.#models = this.#models.filter(model => {
|
|
4792
|
+
if (model.provider !== provider || !modelIds.has(model.id)) return true;
|
|
4793
|
+
return staticModelIds?.has(model.id) === true;
|
|
4794
|
+
});
|
|
4795
|
+
return this.#models.length !== previousLength;
|
|
4796
|
+
}
|
|
4797
|
+
|
|
4622
4798
|
#mergeDiscoveredModels(discovered: readonly Model<Api>[]): void {
|
|
4799
|
+
this.#models = this.#resolveDiscoveredModelCatalog(this.#models, discovered);
|
|
4800
|
+
this.#rebuildCanonicalIndex();
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
#resolveDiscoveredModelCatalog(baseModels: readonly Model<Api>[], discovered: readonly Model<Api>[]): Model<Api>[] {
|
|
4623
4804
|
const discoveredModels = this.#applyHardcodedModelPolicies(
|
|
4624
4805
|
discovered.map(model =>
|
|
4625
4806
|
mergeDiscoveredModel(
|
|
4626
4807
|
model,
|
|
4627
|
-
|
|
4808
|
+
baseModels.find(candidate => candidate.provider === model.provider && candidate.id === model.id),
|
|
4628
4809
|
this.#resolveProviderOverride(model.provider),
|
|
4629
4810
|
),
|
|
4630
4811
|
),
|
|
4631
4812
|
);
|
|
4632
|
-
const resolved = this.#mergeResolvedModels(
|
|
4813
|
+
const resolved = this.#mergeResolvedModels([...baseModels], discoveredModels);
|
|
4633
4814
|
const withConfigModels = this.#mergeCustomModels(resolved, this.#customModelOverlays);
|
|
4634
4815
|
// Merge runtime extension models so they survive online discovery completion
|
|
4635
4816
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
4636
4817
|
const withModelOverrides = this.#applyModelOverrides(combined, this.#modelOverrides);
|
|
4637
|
-
|
|
4638
|
-
this.#rebuildCanonicalIndex();
|
|
4818
|
+
return this.#finalizeModels(this.#applyRuntimeProviderOverrides(withModelOverrides));
|
|
4639
4819
|
}
|
|
4640
4820
|
|
|
4641
4821
|
async #discoverProviderModels(
|
|
@@ -4966,19 +5146,39 @@ export class ModelRegistry {
|
|
|
4966
5146
|
{
|
|
4967
5147
|
providerId: "google-antigravity",
|
|
4968
5148
|
resolveKey: extractGoogleOAuthToken,
|
|
4969
|
-
|
|
5149
|
+
resolveProjectId: (oauthToken, rawValue) =>
|
|
5150
|
+
resolveGoogleOAuthProjectId(
|
|
5151
|
+
this.authStorage,
|
|
5152
|
+
"google-antigravity",
|
|
5153
|
+
oauthToken,
|
|
5154
|
+
rawValue,
|
|
5155
|
+
credentialSessionId,
|
|
5156
|
+
this.#authStorageConfigOwner,
|
|
5157
|
+
),
|
|
5158
|
+
createOptions: (oauthToken, projectId) =>
|
|
4970
5159
|
googleAntigravityModelManagerOptions({
|
|
4971
5160
|
oauthToken,
|
|
4972
5161
|
endpoint: this.getProviderBaseUrl("google-antigravity"),
|
|
5162
|
+
project: projectId,
|
|
4973
5163
|
}),
|
|
4974
5164
|
},
|
|
4975
5165
|
{
|
|
4976
5166
|
providerId: "google-gemini-cli",
|
|
4977
5167
|
resolveKey: extractGoogleOAuthToken,
|
|
4978
|
-
|
|
5168
|
+
resolveProjectId: (oauthToken, rawValue) =>
|
|
5169
|
+
resolveGoogleOAuthProjectId(
|
|
5170
|
+
this.authStorage,
|
|
5171
|
+
"google-gemini-cli",
|
|
5172
|
+
oauthToken,
|
|
5173
|
+
rawValue,
|
|
5174
|
+
credentialSessionId,
|
|
5175
|
+
this.#authStorageConfigOwner,
|
|
5176
|
+
),
|
|
5177
|
+
createOptions: (oauthToken, projectId) =>
|
|
4979
5178
|
googleGeminiCliModelManagerOptions({
|
|
4980
5179
|
oauthToken,
|
|
4981
5180
|
endpoint: this.getProviderBaseUrl("google-gemini-cli"),
|
|
5181
|
+
project: projectId,
|
|
4982
5182
|
}),
|
|
4983
5183
|
},
|
|
4984
5184
|
{
|
|
@@ -5087,7 +5287,11 @@ export class ModelRegistry {
|
|
|
5087
5287
|
authGeneration === this.#getProviderEvidenceGeneration(descriptor.providerId, apiKeyValue) &&
|
|
5088
5288
|
isAuthenticated(key)
|
|
5089
5289
|
) {
|
|
5090
|
-
const
|
|
5290
|
+
const projectId = descriptor.resolveProjectId?.(key, apiKeyValue);
|
|
5291
|
+
const isCurrentContext = descriptor.resolveProjectId
|
|
5292
|
+
? () => descriptor.resolveProjectId?.(key, apiKeyValue) === projectId
|
|
5293
|
+
: undefined;
|
|
5294
|
+
const managerOptions = descriptor.createOptions(key, projectId);
|
|
5091
5295
|
const baseUrl = this.#getProviderBaseUrlForDiscovery(descriptor.providerId) ?? "";
|
|
5092
5296
|
options.push({
|
|
5093
5297
|
options: managerOptions,
|
|
@@ -5095,6 +5299,8 @@ export class ModelRegistry {
|
|
|
5095
5299
|
apiKey: apiKeyValue,
|
|
5096
5300
|
endpoint: this.#normalizeDiscoveryEvidenceEndpoint(baseUrl),
|
|
5097
5301
|
endpointContainsUserinfo: this.#urlContainsUserinfo(baseUrl),
|
|
5302
|
+
projectId,
|
|
5303
|
+
...(isCurrentContext ? { isCurrentContext } : {}),
|
|
5098
5304
|
});
|
|
5099
5305
|
}
|
|
5100
5306
|
}
|
|
@@ -5102,12 +5308,28 @@ export class ModelRegistry {
|
|
|
5102
5308
|
}
|
|
5103
5309
|
|
|
5104
5310
|
async #discoverWithModelManager(
|
|
5105
|
-
{
|
|
5311
|
+
{
|
|
5312
|
+
options,
|
|
5313
|
+
authGeneration,
|
|
5314
|
+
apiKey,
|
|
5315
|
+
endpoint,
|
|
5316
|
+
endpointContainsUserinfo,
|
|
5317
|
+
projectId,
|
|
5318
|
+
isCurrentContext,
|
|
5319
|
+
}: ModelManagerDiscoveryOptions,
|
|
5106
5320
|
strategy: ModelRefreshStrategy,
|
|
5107
5321
|
providerRefresh?: ProviderRefreshFence,
|
|
5108
5322
|
): Promise<Model<Api>[]> {
|
|
5109
5323
|
const generation = (this.#descriptorDiscoveryGenerations.get(options.providerId) ?? 0) + 1;
|
|
5110
5324
|
this.#descriptorDiscoveryGenerations.set(options.providerId, generation);
|
|
5325
|
+
const previousEvidence = this.#descriptorDiscoveryEvidence.get(options.providerId);
|
|
5326
|
+
const discoveryContextChanged =
|
|
5327
|
+
previousEvidence !== undefined &&
|
|
5328
|
+
(previousEvidence.authGeneration !== authGeneration ||
|
|
5329
|
+
previousEvidence.endpoint !== endpoint ||
|
|
5330
|
+
previousEvidence.projectId !== projectId);
|
|
5331
|
+
const isGoogleProjectScopedProvider =
|
|
5332
|
+
options.providerId === "google-antigravity" || options.providerId === "google-gemini-cli";
|
|
5111
5333
|
const canUseCredentialDerivedXiaomiEndpoint =
|
|
5112
5334
|
options.providerId === "xiaomi" &&
|
|
5113
5335
|
apiKey?.startsWith("tp-") === true &&
|
|
@@ -5117,8 +5339,7 @@ export class ModelRegistry {
|
|
|
5117
5339
|
let credentialDerivedEndpoint: string | undefined;
|
|
5118
5340
|
const reusableCacheProvenance = endpointContainsUserinfo
|
|
5119
5341
|
? undefined
|
|
5120
|
-
: fingerprintDescriptorDiscoveryProvenance(authGeneration, endpoint);
|
|
5121
|
-
if (reusableCacheProvenance === undefined && strategy === "offline") return [];
|
|
5342
|
+
: fingerprintDescriptorDiscoveryProvenance(options.providerId, authGeneration, endpoint, projectId);
|
|
5122
5343
|
// Signed/userinfo endpoints may still publish a sanitized best-effort row,
|
|
5123
5344
|
// but each refresh gets a new opaque nonce. The nonce contains no endpoint
|
|
5124
5345
|
// secret and can never match a previous invocation, so constructor loads,
|
|
@@ -5128,6 +5349,7 @@ export class ModelRegistry {
|
|
|
5128
5349
|
const isCurrentDiscoveryContext = () =>
|
|
5129
5350
|
(this.#descriptorDiscoveryGenerations.get(options.providerId) ?? 0) === generation &&
|
|
5130
5351
|
this.#getProviderEvidenceGeneration(options.providerId, apiKey) === authGeneration &&
|
|
5352
|
+
(isCurrentContext?.() ?? true) &&
|
|
5131
5353
|
(endpoint ===
|
|
5132
5354
|
this.#normalizeDiscoveryEvidenceEndpoint(this.#getProviderBaseUrlForDiscovery(options.providerId) ?? "") ||
|
|
5133
5355
|
(canUseCredentialDerivedXiaomiEndpoint && credentialDerivedEndpoint !== undefined));
|
|
@@ -5148,6 +5370,40 @@ export class ModelRegistry {
|
|
|
5148
5370
|
? strategy !== "online-if-uncached" || isCurrentDiscovery()
|
|
5149
5371
|
: isCurrentProviderRefresh()));
|
|
5150
5372
|
try {
|
|
5373
|
+
if (!isCurrentDiscovery()) return [];
|
|
5374
|
+
if (discoveryContextChanged && previousEvidence) {
|
|
5375
|
+
const removedPreviousModels = this.#removeDescriptorDiscoveredModels(
|
|
5376
|
+
options.providerId,
|
|
5377
|
+
previousEvidence.modelIds,
|
|
5378
|
+
);
|
|
5379
|
+
if (removedPreviousModels) {
|
|
5380
|
+
this.#rebuildProviderActivity();
|
|
5381
|
+
this.#rebuildCanonicalIndex();
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
if (strategy === "offline") {
|
|
5385
|
+
const cache =
|
|
5386
|
+
reusableCacheProvenance !== undefined && isGoogleProjectScopedProvider
|
|
5387
|
+
? readModelCache<Api>(options.providerId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath)
|
|
5388
|
+
: null;
|
|
5389
|
+
const hasCurrentProjectCache =
|
|
5390
|
+
cache?.dynamicModelIds !== undefined && cache.dynamicModelProvenance === reusableCacheProvenance;
|
|
5391
|
+
if (reusableCacheProvenance === undefined || (isGoogleProjectScopedProvider && !hasCurrentProjectCache)) {
|
|
5392
|
+
if (discoveryContextChanged) {
|
|
5393
|
+
this.#providerEvidenceApiKeys.set(options.providerId, apiKey);
|
|
5394
|
+
this.#descriptorDiscoveryEvidence.set(options.providerId, {
|
|
5395
|
+
fresh: false,
|
|
5396
|
+
modelIds: new Set(),
|
|
5397
|
+
authGeneration,
|
|
5398
|
+
endpoint,
|
|
5399
|
+
projectId,
|
|
5400
|
+
});
|
|
5401
|
+
this.#rebuildProviderActivity();
|
|
5402
|
+
this.#rebuildCanonicalIndex();
|
|
5403
|
+
}
|
|
5404
|
+
return [];
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5151
5407
|
const manager = createModelManager({
|
|
5152
5408
|
...options,
|
|
5153
5409
|
cacheDbPath: this.#cacheDbPath,
|
|
@@ -5179,7 +5435,9 @@ export class ModelRegistry {
|
|
|
5179
5435
|
const evidence = this.#descriptorDiscoveryEvidence.get(options.providerId);
|
|
5180
5436
|
const refreshStrategy =
|
|
5181
5437
|
strategy === "online-if-uncached" &&
|
|
5182
|
-
(evidence?.authGeneration !== authGeneration ||
|
|
5438
|
+
(evidence?.authGeneration !== authGeneration ||
|
|
5439
|
+
evidence.endpoint !== endpoint ||
|
|
5440
|
+
evidence.projectId !== projectId)
|
|
5183
5441
|
? "online"
|
|
5184
5442
|
: strategy;
|
|
5185
5443
|
const result = await manager.refresh(refreshStrategy);
|
|
@@ -5194,7 +5452,7 @@ export class ModelRegistry {
|
|
|
5194
5452
|
const preservesExistingDescriptorEvidence =
|
|
5195
5453
|
(result.dynamicModelIds === undefined && !result.fetched && !result.stale) ||
|
|
5196
5454
|
(result.stale && result.cacheFresh && result.cacheAuthoritative && evidence?.fresh === true) ||
|
|
5197
|
-
(strategy === "offline" && result.dynamicModelIds === undefined);
|
|
5455
|
+
(strategy === "offline" && result.dynamicModelIds === undefined && !discoveryContextChanged);
|
|
5198
5456
|
if (
|
|
5199
5457
|
isCurrentDiscovery() &&
|
|
5200
5458
|
!preservesExistingDescriptorEvidence &&
|
|
@@ -5202,13 +5460,15 @@ export class ModelRegistry {
|
|
|
5202
5460
|
result.dynamicModelIds !== undefined ||
|
|
5203
5461
|
result.stale ||
|
|
5204
5462
|
this.#descriptorDiscoveryEvidence.get(options.providerId)?.authGeneration !== authGeneration ||
|
|
5205
|
-
this.#descriptorDiscoveryEvidence.get(options.providerId)?.endpoint !== endpoint
|
|
5463
|
+
this.#descriptorDiscoveryEvidence.get(options.providerId)?.endpoint !== endpoint ||
|
|
5464
|
+
this.#descriptorDiscoveryEvidence.get(options.providerId)?.projectId !== projectId)
|
|
5206
5465
|
) {
|
|
5207
5466
|
this.#descriptorDiscoveryEvidence.set(options.providerId, {
|
|
5208
5467
|
fresh: result.fetched,
|
|
5209
5468
|
modelIds: new Set(models.map(model => model.id)),
|
|
5210
5469
|
authGeneration,
|
|
5211
5470
|
endpoint: this.#normalizeDiscoveryEvidenceEndpoint(models[0]?.baseUrl ?? endpoint),
|
|
5471
|
+
projectId,
|
|
5212
5472
|
});
|
|
5213
5473
|
}
|
|
5214
5474
|
if (!isCurrentDiscovery()) {
|
|
@@ -5234,6 +5494,7 @@ export class ModelRegistry {
|
|
|
5234
5494
|
modelIds: new Set(),
|
|
5235
5495
|
authGeneration,
|
|
5236
5496
|
endpoint,
|
|
5497
|
+
projectId,
|
|
5237
5498
|
});
|
|
5238
5499
|
}
|
|
5239
5500
|
logger.warn("model discovery failed for provider", {
|
|
@@ -6213,9 +6474,7 @@ export class ModelRegistry {
|
|
|
6213
6474
|
}
|
|
6214
6475
|
|
|
6215
6476
|
#invalidateAvailableModels(): void {
|
|
6216
|
-
this.#availableModelsCache
|
|
6217
|
-
this.#availableModelsDisabledProviders = undefined;
|
|
6218
|
-
this.#availableModelsEnvFingerprint = undefined;
|
|
6477
|
+
this.#availableModelsCache.clear();
|
|
6219
6478
|
}
|
|
6220
6479
|
|
|
6221
6480
|
#notifyCatalogChanged(): void {
|
|
@@ -6290,10 +6549,11 @@ export class ModelRegistry {
|
|
|
6290
6549
|
|
|
6291
6550
|
/**
|
|
6292
6551
|
* Get all models (embedded + accepted registry + custom).
|
|
6552
|
+
* Supplying a credential session selects only that session's Google OAuth discovery catalog.
|
|
6293
6553
|
* If models.yml had errors, returns embedded plus accepted registry models.
|
|
6294
6554
|
*/
|
|
6295
|
-
getAll(): Model<Api>[] {
|
|
6296
|
-
return this.#models;
|
|
6555
|
+
getAll(credentialSessionId?: string): Model<Api>[] {
|
|
6556
|
+
return credentialSessionId === undefined ? this.#models : this.#getCredentialScopedCatalog(credentialSessionId);
|
|
6297
6557
|
}
|
|
6298
6558
|
|
|
6299
6559
|
/** Provider ids declared in models.yml, including override-only providers. */
|
|
@@ -6381,12 +6641,16 @@ export class ModelRegistry {
|
|
|
6381
6641
|
);
|
|
6382
6642
|
}
|
|
6383
6643
|
|
|
6384
|
-
#isModelAvailable(
|
|
6644
|
+
#isModelAvailable(
|
|
6645
|
+
model: Model<Api>,
|
|
6646
|
+
disabledProviders?: ReadonlySet<string>,
|
|
6647
|
+
credentialSessionId?: string,
|
|
6648
|
+
): boolean {
|
|
6385
6649
|
const disabled = disabledProviders ?? getDisabledProviderIdsFromSettings(this.#settings);
|
|
6386
6650
|
return (
|
|
6387
6651
|
!disabled.has(model.provider) &&
|
|
6388
6652
|
(this.#keylessProviders.has(model.provider) ||
|
|
6389
|
-
this.authStorage.hasAuth(model.provider,
|
|
6653
|
+
this.authStorage.hasAuth(model.provider, credentialSessionId, { owner: this.#authStorageConfigOwner }))
|
|
6390
6654
|
);
|
|
6391
6655
|
}
|
|
6392
6656
|
/** Per-query precomputed variant filter inputs; plan fields are authoritative when provided. */
|
|
@@ -6821,23 +7085,54 @@ export class ModelRegistry {
|
|
|
6821
7085
|
* is authoritative for visibility: live discovery only enriches entries and
|
|
6822
7086
|
* adds unknown ids, and never removes a bundled model. A provider that cannot
|
|
6823
7087
|
* serve a model reports its own error when the model is used.
|
|
7088
|
+
* Without a credential session, project-scoped Google OAuth discoveries are omitted.
|
|
6824
7089
|
*/
|
|
6825
|
-
getAvailable(): Model<Api>[] {
|
|
7090
|
+
getAvailable(credentialSessionId?: string): Model<Api>[] {
|
|
6826
7091
|
this.#synchronizeEnvironmentCredentials();
|
|
6827
7092
|
const disabledProviders = getDisabledProviderIdsFromSettings(this.#settings);
|
|
6828
7093
|
const disabledProviderKey = [...disabledProviders].sort().join("\u0000");
|
|
6829
7094
|
const envFingerprint = envAvailabilityFingerprint(this.#configuredApiKeyEnvNames);
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
this.#availableModelsCache
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
7095
|
+
const cacheKey = credentialSessionId ?? "";
|
|
7096
|
+
const cached = this.#availableModelsCache.get(cacheKey);
|
|
7097
|
+
if (cached?.disabledProviders === disabledProviderKey && cached.envFingerprint === envFingerprint) {
|
|
7098
|
+
return cached.models;
|
|
7099
|
+
}
|
|
7100
|
+
const catalog = this.#getCredentialScopedCatalog(credentialSessionId);
|
|
7101
|
+
const models = catalog.filter(model => this.#isModelAvailable(model, disabledProviders, credentialSessionId));
|
|
7102
|
+
this.#availableModelsCache.set(cacheKey, {
|
|
7103
|
+
models,
|
|
7104
|
+
disabledProviders: disabledProviderKey,
|
|
7105
|
+
envFingerprint,
|
|
7106
|
+
});
|
|
7107
|
+
return models;
|
|
7108
|
+
}
|
|
7109
|
+
|
|
7110
|
+
#getCredentialScopedCatalog(credentialSessionId?: string): Model<Api>[] {
|
|
7111
|
+
const sessionScopedProviders = new Set(
|
|
7112
|
+
(["google-antigravity", "google-gemini-cli"] as const).filter(
|
|
7113
|
+
provider =>
|
|
7114
|
+
this.authStorage.getEffectiveCredentialType(provider, credentialSessionId, {
|
|
7115
|
+
owner: this.#authStorageConfigOwner,
|
|
7116
|
+
}) === "oauth",
|
|
7117
|
+
),
|
|
7118
|
+
);
|
|
7119
|
+
const commonModels = this.#models.filter(model => {
|
|
7120
|
+
if (model.provider !== "google-antigravity" && model.provider !== "google-gemini-cli") {
|
|
7121
|
+
return true;
|
|
7122
|
+
}
|
|
7123
|
+
if (!sessionScopedProviders.has(model.provider) || this.#discoveryManager.providerIds().has(model.provider))
|
|
7124
|
+
return true;
|
|
7125
|
+
return this.#providerActivity.get(model.provider)?.staticModelIds.has(model.id) === true;
|
|
7126
|
+
});
|
|
7127
|
+
// Commands such as `--list-models` read without a credential session. The
|
|
7128
|
+
// effective (default) credential still has a descriptor identity, so its
|
|
7129
|
+
// discovery overlay must be visible here instead of being dropped.
|
|
7130
|
+
const scopedDiscovered = this.#getGoogleSessionDiscoveryModels(credentialSessionId);
|
|
7131
|
+
const catalog =
|
|
7132
|
+
scopedDiscovered.length > 0
|
|
7133
|
+
? this.#resolveDiscoveredModelCatalog(commonModels, scopedDiscovered)
|
|
7134
|
+
: commonModels;
|
|
7135
|
+
return catalog;
|
|
6841
7136
|
}
|
|
6842
7137
|
|
|
6843
7138
|
#synchronizeEnvironmentCredentials(): void {
|
|
@@ -7023,8 +7318,9 @@ export class ModelRegistry {
|
|
|
7023
7318
|
/**
|
|
7024
7319
|
* Find a model by provider and ID.
|
|
7025
7320
|
*/
|
|
7026
|
-
find(provider: string, modelId: string): Model<Api> | undefined {
|
|
7027
|
-
|
|
7321
|
+
find(provider: string, modelId: string, credentialSessionId?: string): Model<Api> | undefined {
|
|
7322
|
+
const models = credentialSessionId === undefined ? this.#models : this.getAll(credentialSessionId);
|
|
7323
|
+
return resolveProviderModelReference(provider, modelId, models);
|
|
7028
7324
|
}
|
|
7029
7325
|
|
|
7030
7326
|
/**
|
|
@@ -7115,6 +7411,35 @@ export class ModelRegistry {
|
|
|
7115
7411
|
return apiKey;
|
|
7116
7412
|
}
|
|
7117
7413
|
|
|
7414
|
+
/** Get a model API key together with the stored row that resolved it, when any. */
|
|
7415
|
+
async getApiKeyWithCredentialRowId(
|
|
7416
|
+
model: Model<Api>,
|
|
7417
|
+
sessionId?: string,
|
|
7418
|
+
options: {
|
|
7419
|
+
credentialSelector?: AuthCredentialSelector;
|
|
7420
|
+
preferredCredentialSelector?: AuthCredentialSelector;
|
|
7421
|
+
signal?: AbortSignal;
|
|
7422
|
+
} = {},
|
|
7423
|
+
): Promise<{ apiKey: string | undefined; credentialRowId: number | undefined }> {
|
|
7424
|
+
this.#refreshRotatingConfigApiKey(model.provider);
|
|
7425
|
+
let credentialRowId: number | undefined;
|
|
7426
|
+
const apiKey = await this.#getApiKeyOrNoAuth(model.provider, async () => {
|
|
7427
|
+
const resolved = await this.authStorage.getApiKeyWithCredentialRowId(model.provider, sessionId, {
|
|
7428
|
+
baseUrl: model.baseUrl,
|
|
7429
|
+
modelId: model.id,
|
|
7430
|
+
credentialSelector: options.credentialSelector,
|
|
7431
|
+
preferredCredentialSelector: options.preferredCredentialSelector,
|
|
7432
|
+
owner: this.#authStorageConfigOwner,
|
|
7433
|
+
signal: options.signal,
|
|
7434
|
+
});
|
|
7435
|
+
credentialRowId = isVllmNoAuthToken(model.provider, resolved.apiKey) ? undefined : resolved.credentialRowId;
|
|
7436
|
+
return resolved.apiKey;
|
|
7437
|
+
});
|
|
7438
|
+
if (apiKey === kNoAuth) credentialRowId = undefined;
|
|
7439
|
+
this.#applyEffectiveAuthHeader(model, apiKey);
|
|
7440
|
+
return { apiKey, credentialRowId };
|
|
7441
|
+
}
|
|
7442
|
+
|
|
7118
7443
|
/**
|
|
7119
7444
|
* Get API key for a provider (e.g., "openai").
|
|
7120
7445
|
*/
|