@earendil-works/pi-coding-agent 0.82.1 → 0.84.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 +243 -0
- package/README.md +6 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +27 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +2 -2
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/experimental/auth.d.ts +16 -0
- package/dist/cli/experimental/auth.d.ts.map +1 -0
- package/dist/cli/experimental/auth.js +13 -0
- package/dist/cli/experimental/auth.js.map +1 -0
- package/dist/cli/experimental/cli.d.ts +6 -0
- package/dist/cli/experimental/cli.d.ts.map +1 -0
- package/dist/cli/experimental/cli.js +5 -0
- package/dist/cli/experimental/cli.js.map +1 -0
- package/dist/cli/experimental/command-options.d.ts +17 -0
- package/dist/cli/experimental/command-options.d.ts.map +1 -0
- package/dist/cli/experimental/command-options.js +35 -0
- package/dist/cli/experimental/command-options.js.map +1 -0
- package/dist/cli/experimental/command.d.ts +63 -0
- package/dist/cli/experimental/command.d.ts.map +1 -0
- package/dist/cli/experimental/command.js +130 -0
- package/dist/cli/experimental/command.js.map +1 -0
- package/dist/cli/experimental/commands/client.d.ts +13 -0
- package/dist/cli/experimental/commands/client.d.ts.map +1 -0
- package/dist/cli/experimental/commands/client.js +25 -0
- package/dist/cli/experimental/commands/client.js.map +1 -0
- package/dist/cli/experimental/commands/pi.d.ts +15 -0
- package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
- package/dist/cli/experimental/commands/pi.js +28 -0
- package/dist/cli/experimental/commands/pi.js.map +1 -0
- package/dist/cli/experimental/commands/server.d.ts +13 -0
- package/dist/cli/experimental/commands/server.d.ts.map +1 -0
- package/dist/cli/experimental/commands/server.js +25 -0
- package/dist/cli/experimental/commands/server.js.map +1 -0
- package/dist/cli/experimental/transport-address.d.ts +10 -0
- package/dist/cli/experimental/transport-address.d.ts.map +1 -0
- package/dist/cli/experimental/transport-address.js +38 -0
- package/dist/cli/experimental/transport-address.js.map +1 -0
- package/dist/cli/list-models.d.ts +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +2 -2
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +2 -2
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/remote-session.d.ts +53 -0
- package/dist/client/remote-session.d.ts.map +1 -0
- package/dist/client/remote-session.js +340 -0
- package/dist/client/remote-session.js.map +1 -0
- package/dist/client/transcript.d.ts +12 -0
- package/dist/client/transcript.d.ts.map +1 -0
- package/dist/client/transcript.js +98 -0
- package/dist/client/transcript.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -11
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +85 -76
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +15 -9
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +159 -38
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +43 -20
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +10 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +28 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -0
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts +36 -4
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/model-config.d.ts +29 -5
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +4 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +13 -10
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +4 -3
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +41 -10
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +30 -13
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +231 -73
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.d.ts +12 -7
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +83 -14
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +76 -42
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts +8 -0
- package/dist/core/pi-manifest.d.ts.map +1 -0
- package/dist/core/pi-manifest.js +25 -0
- package/dist/core/pi-manifest.js.map +1 -0
- package/dist/core/provider-composer.d.ts +4 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +36 -15
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +73 -67
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +64 -10
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +5 -5
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +11 -8
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +3 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +40 -16
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/find.d.ts +3 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +11 -18
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +11 -2
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +22 -8
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +57 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +13 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +7 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.js +19 -0
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
- package/dist/modes/interactive/components/mermaid.d.ts +11 -0
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
- package/dist/modes/interactive/components/mermaid.js +75 -0
- package/dist/modes/interactive/components/mermaid.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +20 -5
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +26 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +30 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +3 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +9 -2
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +33 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +443 -144
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -0
- package/dist/modes/interactive/theme/light.json +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +10 -1
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +5 -1
- package/dist/modes/interactive/theme/theme.d.ts +2 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +13 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +28 -0
- package/dist/modes/json-event.d.ts.map +1 -0
- package/dist/modes/json-event.js +12 -0
- package/dist/modes/json-event.js.map +1 -0
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +12 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +5 -4
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +17 -3
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +11 -9
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js +1 -0
- package/dist/rpc-entry.js.map +1 -1
- package/dist/utils/abort.d.ts +5 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +48 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +22 -2
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/management-http.d.ts +23 -0
- package/dist/utils/management-http.d.ts.map +1 -0
- package/dist/utils/management-http.js +50 -0
- package/dist/utils/management-http.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +23 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/tool-result-images.d.ts +19 -0
- package/dist/utils/tool-result-images.d.ts.map +1 -0
- package/dist/utils/tool-result-images.js +45 -0
- package/dist/utils/tool-result-images.js.map +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +4 -6
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +4 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +20 -2
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +2 -2
- package/docs/custom-provider.md +16 -8
- package/docs/extensions.md +30 -4
- package/docs/json.md +19 -14
- package/docs/keybindings.md +38 -10
- package/docs/models.md +24 -3
- package/docs/providers.md +2 -0
- package/docs/quickstart.md +2 -0
- package/docs/rpc.md +13 -11
- package/docs/sdk.md +21 -2
- package/docs/security.md +1 -1
- package/docs/session-format.md +2 -0
- package/docs/settings.md +4 -1
- package/docs/themes.md +5 -3
- package/docs/usage.md +7 -0
- package/examples/extensions/custom-compaction.ts +1 -16
- package/examples/extensions/custom-provider-anthropic/index.ts +10 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +2 -11
- package/examples/extensions/qna.ts +3 -7
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +7 -18
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +11 -2
- package/examples/sdk/02-custom-model.ts +1 -2
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +70 -26
- package/package.json +14 -7
|
@@ -2,12 +2,26 @@ import { dirname, join } from "node:path";
|
|
|
2
2
|
import { createModels, lazyStream, ModelsError, } from "@earendil-works/pi-ai";
|
|
3
3
|
import * as builtinProviderCatalog from "@earendil-works/pi-ai/providers/all";
|
|
4
4
|
import { getAgentDir } from "../config.js";
|
|
5
|
+
import { operationSignal, raceWithAbortSignal } from "../utils/abort.js";
|
|
5
6
|
import { AuthStorage as DefaultAuthStorage } from "./auth-storage.js";
|
|
6
7
|
import { ModelConfig } from "./model-config.js";
|
|
7
8
|
import { FileModelsStore, InMemoryCodingAgentModelsStore } from "./models-store.js";
|
|
8
9
|
import { composeModelProvider, configuredRequestAuthStatus, resolveCompatibilityRequestConfig, resolveConfiguredModelHeaders, validateExtensionProvider, } from "./provider-composer.js";
|
|
9
10
|
import { withRemoteCatalog } from "./remote-catalog-provider.js";
|
|
10
11
|
import { RuntimeCredentials } from "./runtime-credentials.js";
|
|
12
|
+
/** Credentials changed successfully, but the local model/auth snapshot could not be synchronized. */
|
|
13
|
+
export class CredentialSynchronizationError extends Error {
|
|
14
|
+
providerId;
|
|
15
|
+
operation;
|
|
16
|
+
credential;
|
|
17
|
+
constructor(providerId, operation, credential, options) {
|
|
18
|
+
super(`Credential ${operation} committed for ${providerId}, but local synchronization failed`, options);
|
|
19
|
+
this.name = "CredentialSynchronizationError";
|
|
20
|
+
this.providerId = providerId;
|
|
21
|
+
this.operation = operation;
|
|
22
|
+
this.credential = credential;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
11
25
|
function mergeHeaders(base, override) {
|
|
12
26
|
if (!base && !override)
|
|
13
27
|
return undefined;
|
|
@@ -41,8 +55,11 @@ export class ModelRuntime {
|
|
|
41
55
|
storedProviders: new Set(),
|
|
42
56
|
auth: new Map(),
|
|
43
57
|
};
|
|
44
|
-
|
|
58
|
+
availabilityRefreshSeq = 0;
|
|
59
|
+
availabilityErrorSeq = 0;
|
|
60
|
+
providerAvailabilitySeq = new Map();
|
|
45
61
|
availabilityError;
|
|
62
|
+
credentialOperations = new Map();
|
|
46
63
|
constructor(credentials, config, modelsPath, modelsStore, providers, modelNetworkEnabled) {
|
|
47
64
|
this.credentials = credentials;
|
|
48
65
|
this.config = config;
|
|
@@ -72,12 +89,15 @@ export class ModelRuntime {
|
|
|
72
89
|
runtime.configureRadiusProviders();
|
|
73
90
|
runtime.rebuildProviders();
|
|
74
91
|
const refreshFromNetwork = runtime.modelNetworkEnabled && options.allowModelNetwork === true;
|
|
75
|
-
const controller = refreshFromNetwork ? new AbortController() : undefined;
|
|
76
|
-
const timeout = controller
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
const controller = refreshFromNetwork && options.modelRefreshTimeoutMs !== undefined ? new AbortController() : undefined;
|
|
93
|
+
const timeout = controller ? setTimeout(() => controller.abort(), options.modelRefreshTimeoutMs) : undefined;
|
|
94
|
+
const signal = controller
|
|
95
|
+
? options.signal
|
|
96
|
+
? AbortSignal.any([options.signal, controller.signal])
|
|
97
|
+
: controller.signal
|
|
98
|
+
: options.signal;
|
|
79
99
|
try {
|
|
80
|
-
await runtime.refresh({ allowNetwork: refreshFromNetwork, signal
|
|
100
|
+
await runtime.refresh({ allowNetwork: refreshFromNetwork, signal });
|
|
81
101
|
}
|
|
82
102
|
finally {
|
|
83
103
|
if (timeout)
|
|
@@ -149,16 +169,18 @@ export class ModelRuntime {
|
|
|
149
169
|
available: all.filter((model) => this.snapshot.configuredProviders.has(model.provider)),
|
|
150
170
|
};
|
|
151
171
|
}
|
|
152
|
-
async runAvailabilityRefresh() {
|
|
172
|
+
async runAvailabilityRefresh(seq, errorSeq, signal) {
|
|
153
173
|
const providers = this.models.getProviders();
|
|
154
174
|
const [available, checks, credentials] = await Promise.all([
|
|
155
|
-
this.models.getAvailable(),
|
|
175
|
+
this.models.getAvailable(undefined, { signal }),
|
|
156
176
|
Promise.all(providers.map(async (provider) => [
|
|
157
177
|
provider.id,
|
|
158
|
-
await this.models.checkAuth(provider.id),
|
|
178
|
+
await this.models.checkAuth(provider.id, { signal }),
|
|
159
179
|
])),
|
|
160
|
-
this.credentials.list(),
|
|
180
|
+
this.credentials.list({ signal }),
|
|
161
181
|
]);
|
|
182
|
+
if (seq !== this.availabilityRefreshSeq)
|
|
183
|
+
return;
|
|
162
184
|
const auth = new Map(checks);
|
|
163
185
|
const configuredProviders = new Set(checks
|
|
164
186
|
.filter((entry) => entry[1] !== undefined)
|
|
@@ -170,28 +192,76 @@ export class ModelRuntime {
|
|
|
170
192
|
storedProviders: new Set(credentials.map((entry) => entry.providerId)),
|
|
171
193
|
auth,
|
|
172
194
|
};
|
|
173
|
-
this.
|
|
195
|
+
if (errorSeq === this.availabilityErrorSeq)
|
|
196
|
+
this.availabilityError = undefined;
|
|
174
197
|
}
|
|
175
|
-
queueAvailabilityRefresh(
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
this.
|
|
198
|
+
queueAvailabilityRefresh(signal) {
|
|
199
|
+
const seq = ++this.availabilityRefreshSeq;
|
|
200
|
+
for (const [providerId, providerSeq] of this.providerAvailabilitySeq) {
|
|
201
|
+
this.providerAvailabilitySeq.set(providerId, providerSeq + 1);
|
|
202
|
+
}
|
|
203
|
+
const errorSeq = ++this.availabilityErrorSeq;
|
|
204
|
+
const effectiveSignal = operationSignal(signal);
|
|
205
|
+
return this.runAvailabilityRefresh(seq, errorSeq, effectiveSignal).catch((error) => {
|
|
206
|
+
if (errorSeq === this.availabilityErrorSeq && !effectiveSignal.aborted) {
|
|
207
|
+
this.availabilityError = error instanceof Error ? error.message : String(error);
|
|
208
|
+
}
|
|
179
209
|
throw error;
|
|
180
210
|
});
|
|
181
|
-
const tracked = recorded.finally(() => {
|
|
182
|
-
if (this.availabilityRefresh === tracked)
|
|
183
|
-
this.availabilityRefresh = undefined;
|
|
184
|
-
});
|
|
185
|
-
this.availabilityRefresh = tracked;
|
|
186
|
-
return tracked;
|
|
187
211
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
212
|
+
async refreshProviderAvailability(providerId, signal) {
|
|
213
|
+
// Invalidate any full availability pass that started before this credential change.
|
|
214
|
+
++this.availabilityRefreshSeq;
|
|
215
|
+
const providerSeq = (this.providerAvailabilitySeq.get(providerId) ?? 0) + 1;
|
|
216
|
+
this.providerAvailabilitySeq.set(providerId, providerSeq);
|
|
217
|
+
const errorSeq = ++this.availabilityErrorSeq;
|
|
218
|
+
try {
|
|
219
|
+
const [available, auth, credential] = await Promise.all([
|
|
220
|
+
this.models.getAvailable(providerId, { signal }),
|
|
221
|
+
this.models.checkAuth(providerId, { signal }),
|
|
222
|
+
this.credentials.read(providerId, { signal }),
|
|
223
|
+
]);
|
|
224
|
+
signal.throwIfAborted();
|
|
225
|
+
if (this.providerAvailabilitySeq.get(providerId) !== providerSeq)
|
|
226
|
+
return;
|
|
227
|
+
const configuredProviders = new Set(this.snapshot.configuredProviders);
|
|
228
|
+
const storedProviders = new Set(this.snapshot.storedProviders);
|
|
229
|
+
const authByProvider = new Map(this.snapshot.auth);
|
|
230
|
+
if (auth) {
|
|
231
|
+
configuredProviders.add(providerId);
|
|
232
|
+
authByProvider.set(providerId, auth);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
configuredProviders.delete(providerId);
|
|
236
|
+
authByProvider.delete(providerId);
|
|
237
|
+
}
|
|
238
|
+
if (credential)
|
|
239
|
+
storedProviders.add(providerId);
|
|
240
|
+
else
|
|
241
|
+
storedProviders.delete(providerId);
|
|
242
|
+
const all = [...this.models.getModels()];
|
|
243
|
+
const availableById = new Map([...this.snapshot.available.filter((model) => model.provider !== providerId), ...available].map((model) => [
|
|
244
|
+
`${model.provider}\0${model.id}`,
|
|
245
|
+
model,
|
|
246
|
+
]));
|
|
247
|
+
this.snapshot = {
|
|
248
|
+
all,
|
|
249
|
+
available: all.flatMap((model) => availableById.get(`${model.provider}\0${model.id}`) ?? []),
|
|
250
|
+
configuredProviders,
|
|
251
|
+
storedProviders,
|
|
252
|
+
auth: authByProvider,
|
|
253
|
+
};
|
|
254
|
+
if (errorSeq === this.availabilityErrorSeq)
|
|
255
|
+
this.availabilityError = undefined;
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
if (this.providerAvailabilitySeq.get(providerId) === providerSeq &&
|
|
259
|
+
errorSeq === this.availabilityErrorSeq &&
|
|
260
|
+
!signal.aborted) {
|
|
261
|
+
this.availabilityError = error instanceof Error ? error.message : String(error);
|
|
262
|
+
}
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
195
265
|
}
|
|
196
266
|
getProviders() {
|
|
197
267
|
return this.models.getProviders();
|
|
@@ -205,24 +275,26 @@ export class ModelRuntime {
|
|
|
205
275
|
getModel(providerId, modelId) {
|
|
206
276
|
return this.models.getModel(providerId, modelId);
|
|
207
277
|
}
|
|
208
|
-
async checkAuth(providerId) {
|
|
209
|
-
return this.models.checkAuth(providerId);
|
|
278
|
+
async checkAuth(providerId, options) {
|
|
279
|
+
return this.models.checkAuth(providerId, options);
|
|
210
280
|
}
|
|
211
|
-
async getAvailable(providerId) {
|
|
281
|
+
async getAvailable(providerId, options) {
|
|
212
282
|
if (providerId) {
|
|
213
|
-
|
|
214
|
-
await this.availabilityRefresh;
|
|
215
|
-
return this.snapshot.available.filter((model) => model.provider === providerId);
|
|
216
|
-
}
|
|
283
|
+
const errorSeq = ++this.availabilityErrorSeq;
|
|
217
284
|
try {
|
|
218
|
-
|
|
285
|
+
const available = await this.models.getAvailable(providerId, options);
|
|
286
|
+
if (errorSeq === this.availabilityErrorSeq)
|
|
287
|
+
this.availabilityError = undefined;
|
|
288
|
+
return available;
|
|
219
289
|
}
|
|
220
290
|
catch (error) {
|
|
221
|
-
|
|
291
|
+
if (errorSeq === this.availabilityErrorSeq && !options?.signal?.aborted) {
|
|
292
|
+
this.availabilityError = error instanceof Error ? error.message : String(error);
|
|
293
|
+
}
|
|
222
294
|
throw error;
|
|
223
295
|
}
|
|
224
296
|
}
|
|
225
|
-
await this.
|
|
297
|
+
await this.queueAvailabilityRefresh(options?.signal);
|
|
226
298
|
return this.snapshot.available;
|
|
227
299
|
}
|
|
228
300
|
getAvailableSnapshot() {
|
|
@@ -256,6 +328,9 @@ export class ModelRuntime {
|
|
|
256
328
|
isUsingOAuth(providerId) {
|
|
257
329
|
return this.snapshot.auth.get(providerId)?.type === "oauth";
|
|
258
330
|
}
|
|
331
|
+
isUsingSubscription(providerId) {
|
|
332
|
+
return this.isUsingOAuth(providerId) && this.models.getProvider(providerId)?.auth.oauth?.isSubscription === true;
|
|
333
|
+
}
|
|
259
334
|
hasConfiguredAuth(providerId) {
|
|
260
335
|
return this.snapshot.configuredProviders.has(providerId);
|
|
261
336
|
}
|
|
@@ -274,26 +349,62 @@ export class ModelRuntime {
|
|
|
274
349
|
},
|
|
275
350
|
};
|
|
276
351
|
}
|
|
277
|
-
|
|
278
|
-
this.
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
};
|
|
289
|
-
|
|
352
|
+
enqueueCredentialOperation(providerId, signal, task) {
|
|
353
|
+
const previous = this.credentialOperations.get(providerId) ?? Promise.resolve();
|
|
354
|
+
let markStarted;
|
|
355
|
+
const started = new Promise((resolve) => {
|
|
356
|
+
markStarted = resolve;
|
|
357
|
+
});
|
|
358
|
+
const operation = (async () => {
|
|
359
|
+
await previous.catch(() => { });
|
|
360
|
+
signal.throwIfAborted();
|
|
361
|
+
markStarted?.();
|
|
362
|
+
return task();
|
|
363
|
+
})();
|
|
364
|
+
const tail = operation.catch(() => { });
|
|
365
|
+
this.credentialOperations.set(providerId, tail);
|
|
366
|
+
void tail.then(() => {
|
|
367
|
+
if (this.credentialOperations.get(providerId) === tail)
|
|
368
|
+
this.credentialOperations.delete(providerId);
|
|
369
|
+
});
|
|
370
|
+
return raceWithAbortSignal(started, signal).then(() => operation);
|
|
371
|
+
}
|
|
372
|
+
async synchronizeCredentialState(providerId, operation, credential, signal) {
|
|
373
|
+
try {
|
|
374
|
+
signal.throwIfAborted();
|
|
375
|
+
this.recomposeProvider(providerId);
|
|
376
|
+
const compositionError = this.compositionErrors.get(providerId);
|
|
377
|
+
if (compositionError)
|
|
378
|
+
throw new Error(compositionError);
|
|
379
|
+
const result = await this.models.refresh({ allowNetwork: false, providers: [providerId], signal });
|
|
380
|
+
if (result.aborted)
|
|
381
|
+
signal.throwIfAborted();
|
|
382
|
+
const refreshError = result.errors.get(providerId);
|
|
383
|
+
if (refreshError)
|
|
384
|
+
throw refreshError;
|
|
385
|
+
this.updateModelSnapshot();
|
|
386
|
+
await this.refreshProviderAvailability(providerId, signal);
|
|
387
|
+
}
|
|
388
|
+
catch (cause) {
|
|
389
|
+
throw new CredentialSynchronizationError(providerId, operation, credential, { cause });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
setRuntimeApiKey(providerId, apiKey, options = {}) {
|
|
393
|
+
const signal = operationSignal(options.signal);
|
|
394
|
+
return this.enqueueCredentialOperation(providerId, signal, async () => {
|
|
395
|
+
this.credentials.setRuntimeApiKey(providerId, apiKey);
|
|
396
|
+
await this.synchronizeCredentialState(providerId, "setRuntimeApiKey", { type: "api_key", key: apiKey }, signal);
|
|
397
|
+
});
|
|
290
398
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
399
|
+
removeRuntimeApiKey(providerId, options = {}) {
|
|
400
|
+
const signal = operationSignal(options.signal);
|
|
401
|
+
return this.enqueueCredentialOperation(providerId, signal, async () => {
|
|
402
|
+
this.credentials.removeRuntimeApiKey(providerId);
|
|
403
|
+
await this.synchronizeCredentialState(providerId, "removeRuntimeApiKey", undefined, signal);
|
|
404
|
+
});
|
|
294
405
|
}
|
|
295
|
-
listCredentials() {
|
|
296
|
-
return this.credentials.list();
|
|
406
|
+
listCredentials(options) {
|
|
407
|
+
return this.credentials.list(options);
|
|
297
408
|
}
|
|
298
409
|
getProviderAuthStatus(providerId) {
|
|
299
410
|
if (this.credentials.hasRuntimeApiKey(providerId))
|
|
@@ -310,10 +421,15 @@ export class ModelRuntime {
|
|
|
310
421
|
const provider = this.models.getProvider(model.provider);
|
|
311
422
|
if (!provider)
|
|
312
423
|
throw new ModelsError("provider", `Unknown provider: ${model.provider}`);
|
|
313
|
-
const resolution = await this.getAuth(model, {
|
|
424
|
+
const resolution = await this.getAuth(model, {
|
|
425
|
+
apiKey: options?.apiKey,
|
|
426
|
+
env: options?.env,
|
|
427
|
+
signal: options?.signal,
|
|
428
|
+
});
|
|
314
429
|
if (!resolution)
|
|
315
430
|
throw new ModelsError("auth", `Provider is not configured: ${model.provider}`);
|
|
316
|
-
const { transformHeaders, ...
|
|
431
|
+
const { transformHeaders, ...rawProviderOptions } = options ?? {};
|
|
432
|
+
const providerOptions = rawProviderOptions;
|
|
317
433
|
let headers = mergeHeaders(resolution.auth.headers, providerOptions.headers);
|
|
318
434
|
if (transformHeaders)
|
|
319
435
|
headers = await transformHeaders(headers ?? {});
|
|
@@ -349,21 +465,48 @@ export class ModelRuntime {
|
|
|
349
465
|
completeSimple(model, context, options) {
|
|
350
466
|
return this.streamSimple(model, context, options).result();
|
|
351
467
|
}
|
|
352
|
-
async
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
468
|
+
async fetchDeferred(model, handle, options) {
|
|
469
|
+
return lazyStream(model, async () => {
|
|
470
|
+
const prepared = await this.prepareRequest(model, options);
|
|
471
|
+
if (!prepared.provider.fetchDeferred) {
|
|
472
|
+
throw new ModelsError("provider", `Provider ${model.provider} does not support deferred responses`);
|
|
473
|
+
}
|
|
474
|
+
return prepared.provider.fetchDeferred(prepared.model, handle, prepared.options);
|
|
475
|
+
}).result();
|
|
476
|
+
}
|
|
477
|
+
async cancelDeferred(model, handle, options) {
|
|
478
|
+
const prepared = await this.prepareRequest(model, options);
|
|
479
|
+
if (!prepared.provider.cancelDeferred) {
|
|
480
|
+
throw new ModelsError("provider", `Provider ${model.provider} does not support deferred responses`);
|
|
481
|
+
}
|
|
482
|
+
await prepared.provider.cancelDeferred(prepared.model, handle, prepared.options);
|
|
483
|
+
}
|
|
484
|
+
login(providerId, type, interaction) {
|
|
485
|
+
const signal = operationSignal(interaction.signal);
|
|
486
|
+
return this.enqueueCredentialOperation(providerId, signal, async () => {
|
|
487
|
+
const credential = await this.models.login(providerId, type, { ...interaction, signal });
|
|
488
|
+
await this.synchronizeCredentialState(providerId, "login", credential, signal);
|
|
489
|
+
return credential;
|
|
490
|
+
});
|
|
356
491
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
492
|
+
logout(providerId, options = {}) {
|
|
493
|
+
const signal = operationSignal(options.signal);
|
|
494
|
+
return this.enqueueCredentialOperation(providerId, signal, async () => {
|
|
495
|
+
await this.models.logout(providerId, { signal });
|
|
496
|
+
await this.synchronizeCredentialState(providerId, "logout", undefined, signal);
|
|
497
|
+
});
|
|
362
498
|
}
|
|
363
499
|
async refresh(options = {}) {
|
|
364
500
|
this.config = await ModelConfig.load(this.modelsPath);
|
|
365
501
|
this.configureRadiusProviders();
|
|
366
|
-
|
|
502
|
+
if (options.providers) {
|
|
503
|
+
for (const providerId of new Set(options.providers))
|
|
504
|
+
this.recomposeProvider(providerId);
|
|
505
|
+
this.updateModelSnapshot();
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
this.rebuildProviders();
|
|
509
|
+
}
|
|
367
510
|
const refreshOptions = {
|
|
368
511
|
...options,
|
|
369
512
|
allowNetwork: options.allowNetwork ?? this.modelNetworkEnabled,
|
|
@@ -374,14 +517,29 @@ export class ModelRuntime {
|
|
|
374
517
|
aborted: refreshOptions.signal?.aborted ?? false,
|
|
375
518
|
errors: new Map(),
|
|
376
519
|
};
|
|
520
|
+
const errors = new Map(result.errors);
|
|
377
521
|
this.updateModelSnapshot();
|
|
378
|
-
|
|
379
|
-
await
|
|
522
|
+
if (options.providers) {
|
|
523
|
+
await Promise.all([...new Set(options.providers)].map(async (providerId) => {
|
|
524
|
+
try {
|
|
525
|
+
await this.refreshProviderAvailability(providerId, operationSignal(options.signal));
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
if (!options.signal?.aborted) {
|
|
529
|
+
errors.set(providerId, error instanceof Error ? error : new Error(String(error)));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}));
|
|
380
533
|
}
|
|
381
|
-
|
|
382
|
-
|
|
534
|
+
else {
|
|
535
|
+
try {
|
|
536
|
+
await this.queueAvailabilityRefresh(options.signal);
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// Availability errors are recorded by the latest pass; refreshed models remain usable.
|
|
540
|
+
}
|
|
383
541
|
}
|
|
384
|
-
return result;
|
|
542
|
+
return { aborted: result.aborted || (options.signal?.aborted ?? false), errors };
|
|
385
543
|
}
|
|
386
544
|
registerNativeProvider(provider) {
|
|
387
545
|
if (!provider.id.trim())
|