@gajae-code/coding-agent 0.15.5 → 0.15.6
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 +26 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/update-cli.d.ts +2 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +96 -0
- package/dist/types/config/file-lock.d.ts +49 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +46 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/sdk/broker/broker.d.ts +3 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +15 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +19 -7
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/index.d.ts +6 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +3 -3
- package/src/async/job-manager.ts +671 -39
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +3 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/update-cli.ts +40 -5
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/model-presets.ts +24 -0
- package/src/config/autorouting-tier-map.ts +32 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +775 -59
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2799 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +51 -9
- package/src/config/model-registry.ts +1021 -122
- package/src/config/settings-schema.ts +11 -0
- package/src/config/settings.ts +6 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +571 -0
- package/src/coordinator-mcp/server.ts +19 -24
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/session-state-lock.ts +1242 -141
- package/src/gjc-runtime/session-state-sidecar.ts +76 -3
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +9 -2
- package/src/modes/acp/acp-agent.ts +11 -5
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +23 -6
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +48 -21
- package/src/sdk/broker/lifecycle.ts +32 -6
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +11 -1
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/cli/session-cli.ts +12 -1
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +3 -0
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +371 -37
- package/src/sdk/host/session-runtime.ts +364 -52
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +2 -2
- package/src/sdk/protocol/operation-registry.ts +4 -1
- package/src/sdk/router/session-router.ts +228 -96
- package/src/sdk/session.ts +83 -12
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +678 -103
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +5 -4
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +142 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +772 -67
- package/src/tools/index.ts +7 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
package/src/sdk/session.ts
CHANGED
|
@@ -70,6 +70,7 @@ import { Settings, type SkillsSettings } from "../config/settings";
|
|
|
70
70
|
import { resolveEagerTaskDelegation } from "../config/task-delegation";
|
|
71
71
|
import { CursorExecHandlers } from "../cursor";
|
|
72
72
|
import { EditTool } from "../edit";
|
|
73
|
+
import { describeFoldReceipt } from "../session/fold-coordinator";
|
|
73
74
|
import type { BashRestrictionProfile } from "../tools/bash-allowed-prefixes";
|
|
74
75
|
import { SearchTool } from "../tools/search";
|
|
75
76
|
import "../discovery";
|
|
@@ -1345,14 +1346,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1345
1346
|
const startupAuthConfig = hasInjectedAuth
|
|
1346
1347
|
? undefined
|
|
1347
1348
|
: (options.startupAuthConfig ?? (await resolveStartupAuthConfig(agentDir)));
|
|
1349
|
+
const ownsModelRegistry = options.modelRegistry === undefined;
|
|
1348
1350
|
const ownsAuthStorage = options.modelRegistry === undefined && options.authStorage === undefined;
|
|
1349
1351
|
const modelRegistry =
|
|
1350
1352
|
options.modelRegistry ??
|
|
1351
1353
|
new ModelRegistry(
|
|
1352
1354
|
options.authStorage ??
|
|
1353
1355
|
(await logger.time("discoverModels", () => discoverAuthStorage(agentDir, startupAuthConfig))),
|
|
1356
|
+
path.join(agentDir, "models.yml"),
|
|
1357
|
+
undefined,
|
|
1358
|
+
{ agentDir },
|
|
1354
1359
|
);
|
|
1355
1360
|
const authStorage = modelRegistry.authStorage;
|
|
1361
|
+
const authStorageOwner = modelRegistry.getAuthStorageOwner();
|
|
1356
1362
|
if (options.authStorage && options.authStorage !== authStorage) {
|
|
1357
1363
|
throw new Error(
|
|
1358
1364
|
"options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided",
|
|
@@ -1390,7 +1396,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1390
1396
|
let credentialScopeId: string | undefined;
|
|
1391
1397
|
let credentialScopeLeased = false;
|
|
1392
1398
|
let closeOwnedSettings: () => Promise<void> = async () => {};
|
|
1393
|
-
const closeOwnedAuthStorage = (): void => {
|
|
1399
|
+
const closeOwnedAuthStorage = async (): Promise<void> => {
|
|
1400
|
+
if (ownsModelRegistry) await modelRegistry.dispose();
|
|
1394
1401
|
if (!hasSession && credentialScopeLeased && credentialScopeId) {
|
|
1395
1402
|
authStorage.releaseCredentialScope(credentialScopeId);
|
|
1396
1403
|
credentialScopeLeased = false;
|
|
@@ -1422,10 +1429,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1422
1429
|
}
|
|
1423
1430
|
});
|
|
1424
1431
|
const applyCredentialSelector = (scopeId: string, provider: string, selector: AuthCredentialSelector): void => {
|
|
1425
|
-
authStorage.setSessionCredentialSelector(scopeId, provider, selector);
|
|
1432
|
+
authStorage.setSessionCredentialSelector(scopeId, provider, selector, authStorageOwner);
|
|
1426
1433
|
};
|
|
1427
1434
|
const ownsScopedSettings = options.settings === undefined;
|
|
1428
1435
|
const settings = options.settings ?? (await logger.time("settings", Settings.loadForScope, { cwd, agentDir }));
|
|
1436
|
+
if (ownsModelRegistry) modelRegistry.setScopedSettings(settings);
|
|
1429
1437
|
const autoroutingInactive =
|
|
1430
1438
|
settings.get("task.autorouting.enabled") === true && !settings.getEffectiveAutorouting().active;
|
|
1431
1439
|
closeOwnedSettings = async (): Promise<void> => {
|
|
@@ -1695,10 +1703,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1695
1703
|
) {
|
|
1696
1704
|
throw new Error("Durable numeric credential pin authority changed");
|
|
1697
1705
|
}
|
|
1698
|
-
authStorage.setSessionCredentialSelector(
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1706
|
+
authStorage.setSessionCredentialSelector(
|
|
1707
|
+
credentialSessionId,
|
|
1708
|
+
record.provider,
|
|
1709
|
+
{
|
|
1710
|
+
kind: pin.kind,
|
|
1711
|
+
value: pin.value,
|
|
1712
|
+
},
|
|
1713
|
+
authStorageOwner,
|
|
1714
|
+
);
|
|
1702
1715
|
staleDurableCredentialPins.delete(resolveOAuthStorageProvider(record.provider));
|
|
1703
1716
|
}
|
|
1704
1717
|
} catch {
|
|
@@ -2066,13 +2079,49 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2066
2079
|
if (asyncJobManager!.isDeliverySuppressed(jobId, job?.generation)) return;
|
|
2067
2080
|
const deniedOwnedDelivery =
|
|
2068
2081
|
ownedCompletion !== undefined && !isOwnedCompletionEnvelopeAllowed(ownedCompletion);
|
|
2082
|
+
// Fold disposition is decided by the coordinator's durable slot state.
|
|
2083
|
+
// `parked` means the fold transaction has not finished capturing its
|
|
2084
|
+
// receipt yet; it replays this completion itself, so enqueuing here
|
|
2085
|
+
// would double-deliver.
|
|
2086
|
+
const foldDisposition = job
|
|
2087
|
+
? session.foldCoordinator.onDelivery(job, result)
|
|
2088
|
+
: ({ kind: "ordinary" } as const);
|
|
2089
|
+
if (foldDisposition.kind === "parked") {
|
|
2090
|
+
if (job) asyncJobManager?.retainParkedDelivery(job, result);
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2069
2093
|
const formattedResult = await formatAsyncResultForFollowUp(result, !deniedOwnedDelivery);
|
|
2094
|
+
if (
|
|
2095
|
+
foldDisposition.kind === "receipt" &&
|
|
2096
|
+
job &&
|
|
2097
|
+
!session.foldCoordinator.claimCompletionDelivery(job)
|
|
2098
|
+
)
|
|
2099
|
+
return;
|
|
2100
|
+
// A folded job's result must arrive with its receipt so the wake turn
|
|
2101
|
+
// completes the original task rather than merely reporting output.
|
|
2102
|
+
const deliveredResult =
|
|
2103
|
+
foldDisposition.kind === "receipt"
|
|
2104
|
+
? `${formattedResult}\n\n${describeFoldReceipt(foldDisposition.receipt)}`
|
|
2105
|
+
: formattedResult;
|
|
2106
|
+
// Exactly one transcript notice per completion: a retried delivery
|
|
2107
|
+
// reuses the same job object, so the claim guards against repeats.
|
|
2108
|
+
if (
|
|
2109
|
+
foldDisposition.kind === "receipt" &&
|
|
2110
|
+
job &&
|
|
2111
|
+
session.foldCoordinator.claimCompletionNotice(job)
|
|
2112
|
+
) {
|
|
2113
|
+
session.emitNotice(
|
|
2114
|
+
"info",
|
|
2115
|
+
`Folded job ${foldDisposition.receipt.jobId} (${foldDisposition.receipt.label}) finished.`,
|
|
2116
|
+
"fold",
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2070
2119
|
|
|
2071
2120
|
const durationMs = job ? jobElapsedMs(job) : undefined;
|
|
2072
2121
|
session.yieldQueue.enqueue<AsyncResultEntry>("async-result", {
|
|
2073
2122
|
jobId,
|
|
2074
2123
|
generation: job?.generation ?? "",
|
|
2075
|
-
result:
|
|
2124
|
+
result: deliveredResult,
|
|
2076
2125
|
job,
|
|
2077
2126
|
durationMs,
|
|
2078
2127
|
...(ownedCompletion
|
|
@@ -2085,6 +2134,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2085
2134
|
}
|
|
2086
2135
|
: {}),
|
|
2087
2136
|
});
|
|
2137
|
+
if (job) asyncJobManager?.retainDeliveryClaim(job);
|
|
2088
2138
|
},
|
|
2089
2139
|
})
|
|
2090
2140
|
: options.inheritedAsyncJobManager;
|
|
@@ -2540,6 +2590,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2540
2590
|
// (review thread P1). For a top-level session this equals the
|
|
2541
2591
|
// session id.
|
|
2542
2592
|
getSessionId: () => AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
|
|
2593
|
+
registerForegroundFoldParticipant: adapter =>
|
|
2594
|
+
session?.registerForegroundFoldParticipant(adapter) ?? (() => {}),
|
|
2595
|
+
hasForegroundBashBackgroundRequestHandler: () => session?.hasForegroundBashBackgroundRequestHandler() ?? false,
|
|
2596
|
+
requestForegroundBashBackground: () => Promise.resolve(session?.requestForegroundBashBackground() ?? false),
|
|
2597
|
+
|
|
2543
2598
|
getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
|
|
2544
2599
|
getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
|
|
2545
2600
|
isManagedSessionDestination: () => sessionManager.isManagedDestination(),
|
|
@@ -4086,18 +4141,24 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4086
4141
|
const requestStartedAt = performance.now();
|
|
4087
4142
|
let stream: Awaited<ReturnType<typeof streamSimple>>;
|
|
4088
4143
|
try {
|
|
4144
|
+
const effectiveApiKey = await modelRegistry.getApiKey(streamModel, credentialSessionId);
|
|
4089
4145
|
stream = await streamSimple(streamModel, context, {
|
|
4090
4146
|
...streamOptions,
|
|
4147
|
+
apiKey: effectiveApiKey,
|
|
4148
|
+
authCredentialType: modelRegistry.getSessionCredentialType(streamModel.provider, credentialSessionId),
|
|
4091
4149
|
onAuthError: async (provider, oldKey, error) => {
|
|
4092
4150
|
await modelRegistry.authStorage.invalidateCredentialMatching(provider, oldKey, {
|
|
4093
4151
|
signal: streamOptions?.signal,
|
|
4094
4152
|
sessionId: credentialSessionId,
|
|
4153
|
+
owner: modelRegistry.getAuthStorageOwner(),
|
|
4095
4154
|
});
|
|
4096
4155
|
logger.debug("Retrying provider request after credential invalidation", {
|
|
4097
4156
|
provider,
|
|
4098
4157
|
error: error instanceof Error ? error.message : String(error),
|
|
4099
4158
|
});
|
|
4100
|
-
return modelRegistry.
|
|
4159
|
+
return modelRegistry.getApiKey(streamModel, credentialSessionId, {
|
|
4160
|
+
signal: streamOptions?.signal,
|
|
4161
|
+
});
|
|
4101
4162
|
},
|
|
4102
4163
|
});
|
|
4103
4164
|
} catch (error) {
|
|
@@ -4285,7 +4346,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4285
4346
|
const sessionAsyncJobManager = asyncJobManager;
|
|
4286
4347
|
if (sessionAsyncJobManager) {
|
|
4287
4348
|
session.yieldQueue.register<AsyncResultEntry>("async-result", {
|
|
4288
|
-
isStale: entry =>
|
|
4349
|
+
isStale: entry => {
|
|
4350
|
+
const stale = sessionAsyncJobManager.isDeliverySuppressed(entry.jobId, entry.generation);
|
|
4351
|
+
if (stale) sessionAsyncJobManager.releaseDeliveryClaim(entry.generation);
|
|
4352
|
+
return stale;
|
|
4353
|
+
},
|
|
4289
4354
|
// Build one message per ownership origin so an owned-scope drop of
|
|
4290
4355
|
// one turn's message never suppresses other turns'/ordinary
|
|
4291
4356
|
// completions batched in the same flush (review thread P2).
|
|
@@ -4293,7 +4358,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4293
4358
|
entry.ownedCompletion
|
|
4294
4359
|
? `${entry.ownedCompletion.lineageIdHash}\u0000${entry.ownedCompletion.promptAttemptEpoch}`
|
|
4295
4360
|
: "ordinary",
|
|
4296
|
-
build:
|
|
4361
|
+
build: entries => {
|
|
4362
|
+
try {
|
|
4363
|
+
return buildAsyncResultBatchMessage(entries);
|
|
4364
|
+
} finally {
|
|
4365
|
+
for (const entry of entries) sessionAsyncJobManager.releaseDeliveryClaim(entry.generation);
|
|
4366
|
+
}
|
|
4367
|
+
},
|
|
4297
4368
|
});
|
|
4298
4369
|
}
|
|
4299
4370
|
session.yieldQueue.register<McpNotificationEntry>("mcp-notification", {
|
|
@@ -4329,7 +4400,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4329
4400
|
);
|
|
4330
4401
|
AsyncJobManager.unregisterManager(asyncJobManager);
|
|
4331
4402
|
}
|
|
4332
|
-
closeOwnedAuthStorage();
|
|
4403
|
+
await closeOwnedAuthStorage();
|
|
4333
4404
|
await closeOwnedSettings();
|
|
4334
4405
|
}
|
|
4335
4406
|
}
|
|
@@ -4599,7 +4670,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4599
4670
|
}
|
|
4600
4671
|
releaseLocalProtocolOverride();
|
|
4601
4672
|
try {
|
|
4602
|
-
closeOwnedAuthStorage();
|
|
4673
|
+
await closeOwnedAuthStorage();
|
|
4603
4674
|
} catch (authCleanupError) {
|
|
4604
4675
|
logger.warn("Failed to close owned auth storage after startup error", { error: authCleanupError });
|
|
4605
4676
|
}
|
|
@@ -76,6 +76,7 @@ export interface AccountInventoryInput {
|
|
|
76
76
|
modelRegistry?: {
|
|
77
77
|
getAvailable?: () => Array<{ provider: string }>;
|
|
78
78
|
getProviderBaseUrl?: (provider: string) => string | undefined;
|
|
79
|
+
getAuthStorageOwner?: () => object;
|
|
79
80
|
};
|
|
80
81
|
sessionId?: string;
|
|
81
82
|
provider?: string;
|
|
@@ -323,8 +324,8 @@ function freshUsageCache(report: CachedUsageReport["report"]): AccountUsageCache
|
|
|
323
324
|
};
|
|
324
325
|
}
|
|
325
326
|
|
|
326
|
-
function canPinStoredOAuth(authStorage: AuthStorage, provider: string): boolean {
|
|
327
|
-
if (authStorage.hasRuntimeApiKey(provider) || authStorage.hasConfigApiKey(provider)) return false;
|
|
327
|
+
function canPinStoredOAuth(authStorage: AuthStorage, provider: string, owner?: object): boolean {
|
|
328
|
+
if (authStorage.hasRuntimeApiKey(provider) || authStorage.hasConfigApiKey(provider, owner)) return false;
|
|
328
329
|
return !getEnvApiKey(provider);
|
|
329
330
|
}
|
|
330
331
|
|
|
@@ -368,6 +369,7 @@ function addStoredRows(
|
|
|
368
369
|
inventory: CredentialInventoryRecord[],
|
|
369
370
|
sessionId: string | undefined,
|
|
370
371
|
baseUrlResolver?: (provider: string) => string | undefined,
|
|
372
|
+
authStorageOwner?: object,
|
|
371
373
|
): void {
|
|
372
374
|
const removalTargetIds = new Set(
|
|
373
375
|
(typeof authStorage.listCredentialRemovalTargets === "function"
|
|
@@ -382,7 +384,9 @@ function addStoredRows(
|
|
|
382
384
|
typeof record.provider === "string" &&
|
|
383
385
|
authStorage.getSessionCredentialRowId(record.provider, sessionId) === record.id;
|
|
384
386
|
const canPin =
|
|
385
|
-
record.credentialKind === "oauth" &&
|
|
387
|
+
record.credentialKind === "oauth" &&
|
|
388
|
+
!record.disabled &&
|
|
389
|
+
canPinStoredOAuth(authStorage, record.provider, authStorageOwner);
|
|
386
390
|
const canRemove = removalTargetIds.has(record.id);
|
|
387
391
|
rows.push({
|
|
388
392
|
id: sourceId(record.provider, "stored", record.id),
|
|
@@ -417,12 +421,17 @@ function addSyntheticRows(
|
|
|
417
421
|
authStorage: AuthStorage,
|
|
418
422
|
providers: Set<string>,
|
|
419
423
|
sessionId: string | undefined,
|
|
424
|
+
authStorageOwner?: object,
|
|
420
425
|
): void {
|
|
421
426
|
for (const provider of [...providers].sort((a, b) => a.localeCompare(b))) {
|
|
422
427
|
const runtime = authStorage.hasRuntimeApiKey(provider);
|
|
423
|
-
const config = authStorage.hasConfigApiKey(provider);
|
|
428
|
+
const config = authStorage.hasConfigApiKey(provider, authStorageOwner);
|
|
424
429
|
const env = Boolean(getEnvApiKey(provider));
|
|
425
|
-
const effectiveType = authStorage.getEffectiveCredentialType(
|
|
430
|
+
const effectiveType = authStorage.getEffectiveCredentialType(
|
|
431
|
+
provider,
|
|
432
|
+
sessionId,
|
|
433
|
+
authStorageOwner ? { owner: authStorageOwner } : undefined,
|
|
434
|
+
);
|
|
426
435
|
|
|
427
436
|
const add = (source: AccountInventorySource): void => {
|
|
428
437
|
const selected =
|
|
@@ -469,10 +478,16 @@ export function buildAccountInventorySnapshot(input: AccountInventoryInput): Acc
|
|
|
469
478
|
const nowMs = input.nowMs ?? Date.now();
|
|
470
479
|
const inventory = input.authStorage.listCredentialInventory();
|
|
471
480
|
const rows: AccountInventoryRow[] = [];
|
|
472
|
-
|
|
473
|
-
|
|
481
|
+
const authStorageOwner = input.modelRegistry?.getAuthStorageOwner?.();
|
|
482
|
+
addStoredRows(
|
|
483
|
+
rows,
|
|
484
|
+
input.authStorage,
|
|
485
|
+
inventory,
|
|
486
|
+
input.sessionId,
|
|
487
|
+
provider => input.modelRegistry?.getProviderBaseUrl?.(provider),
|
|
488
|
+
authStorageOwner,
|
|
474
489
|
);
|
|
475
|
-
addSyntheticRows(rows, input.authStorage, providerSet(input, inventory), input.sessionId);
|
|
490
|
+
addSyntheticRows(rows, input.authStorage, providerSet(input, inventory), input.sessionId, authStorageOwner);
|
|
476
491
|
rows.sort((left, right) => left.id.localeCompare(right.id));
|
|
477
492
|
return { generatedAt: nowMs, generation: input.authStorage.getGeneration(), rows };
|
|
478
493
|
}
|
|
@@ -518,6 +533,7 @@ export async function checkAccountInventory(input: AccountInventoryInput): Promi
|
|
|
518
533
|
const rows = input.provider ? fullSnapshot.rows.filter(row => row.provider === input.provider) : fullSnapshot.rows;
|
|
519
534
|
const snapshot: AccountInventorySnapshot = { ...fullSnapshot, rows };
|
|
520
535
|
const authStorage = input.authStorage;
|
|
536
|
+
const authStorageOwner = input.modelRegistry?.getAuthStorageOwner?.();
|
|
521
537
|
applyStoredCheck(
|
|
522
538
|
rows,
|
|
523
539
|
await authStorage.checkCredentials({
|
|
@@ -530,13 +546,13 @@ export async function checkAccountInventory(input: AccountInventoryInput): Promi
|
|
|
530
546
|
let key: string | undefined;
|
|
531
547
|
if (row.source === "env") key = getEnvApiKey(row.provider);
|
|
532
548
|
else if (row.source === "runtime" && authStorage.hasRuntimeApiKey(row.provider))
|
|
533
|
-
key = await authStorage.peekApiKey(row.provider);
|
|
549
|
+
key = await authStorage.peekApiKey(row.provider, authStorageOwner ? { owner: authStorageOwner } : undefined);
|
|
534
550
|
else if (
|
|
535
551
|
row.source === "config" &&
|
|
536
|
-
authStorage.hasConfigApiKey(row.provider) &&
|
|
552
|
+
authStorage.hasConfigApiKey(row.provider, authStorageOwner) &&
|
|
537
553
|
!authStorage.hasRuntimeApiKey(row.provider)
|
|
538
554
|
) {
|
|
539
|
-
key = await authStorage.peekApiKey(row.provider);
|
|
555
|
+
key = await authStorage.peekApiKey(row.provider, authStorageOwner ? { owner: authStorageOwner } : undefined);
|
|
540
556
|
}
|
|
541
557
|
const result = key
|
|
542
558
|
? await authStorage.checkApiKeyCredential(row.provider as Provider, key, {
|