@code-yeongyu/senpi-ai 2026.11.12 → 2026.11.15
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/dist/api/openai-responses-shared.js +2 -1
- package/dist/api/openai-responses.js +14 -1
- package/dist/api-registry.d.ts +7 -3
- package/dist/api-registry.js +40 -12
- package/dist/compat.js +4 -3
- package/dist/images-api-registry.d.ts +1 -3
- package/dist/images-api-registry.js +24 -12
- package/dist/node/provider-scope.d.ts +5 -1
- package/dist/node/provider-scope.js +61 -11
- package/dist/provider-scope-registry.d.ts +27 -0
- package/dist/provider-scope-registry.js +24 -0
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/venice.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/utils/siwc-errors.d.ts +2 -0
- package/dist/utils/siwc-errors.js +11 -0
- package/package.json +3 -2
|
@@ -235,7 +235,8 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
235
235
|
for (const msg of transformedMessages) {
|
|
236
236
|
const isLeadingSystemMessage = sourceIndex++ === 0 && msg.role === "system";
|
|
237
237
|
if (msg.role === "configurationUpdate") {
|
|
238
|
-
|
|
238
|
+
// Sign in with ChatGPT rejects the item; openai-responses sends the level as reasoning.effort (senpi#6607).
|
|
239
|
+
if (!supportsConfigurationUpdate(model) || model.compat?.siwc)
|
|
239
240
|
continue;
|
|
240
241
|
const previous = messages[messages.length - 1];
|
|
241
242
|
if (previous?.type === "configuration_update") {
|
|
@@ -10,6 +10,7 @@ import { getPiUserAgent } from "../utils/pi-user-agent.js";
|
|
|
10
10
|
import { awaitProviderTransport, openAICompatibleProviderDiagnosticFromError, siwcProviderDiagnosticFromError, } from "../utils/provider-diagnostic-sources.js";
|
|
11
11
|
import { getProviderEnvValue } from "../utils/provider-env.js";
|
|
12
12
|
import { retryProviderRequest } from "../utils/provider-retry.js";
|
|
13
|
+
import { isSiwcMissingResponsesScope, SIWC_MISSING_RESPONSES_SCOPE_GUIDANCE } from "../utils/siwc-errors.js";
|
|
13
14
|
import { sendWithForcedToolChoiceFallback } from "../utils/tool-choice-fallback.js";
|
|
14
15
|
import { getDeclaredTools, normalizeContext, resolveTranscript } from "../utils/transcript.js";
|
|
15
16
|
import { isCloudflareProvider, resolveCloudflareBaseUrl } from "./cloudflare.js";
|
|
@@ -155,6 +156,9 @@ function formatOpenAIResponsesError(error, provider, siwc = false) {
|
|
|
155
156
|
if (typeof param === "string")
|
|
156
157
|
errorMessage += `\nUnsupported parameter: ${param}`;
|
|
157
158
|
}
|
|
159
|
+
if (siwc && isSiwcMissingResponsesScope(errorMessage)) {
|
|
160
|
+
errorMessage += `\n${SIWC_MISSING_RESPONSES_SCOPE_GUIDANCE}`;
|
|
161
|
+
}
|
|
158
162
|
// Sign in with ChatGPT shares the subscription's usage limit with other apps.
|
|
159
163
|
return errorMessage.includes("subscription_sharing_usage_limit_exceeded")
|
|
160
164
|
? `${errorMessage}\nCheck your ChatGPT usage: ${CHATGPT_USAGE_URL}`
|
|
@@ -504,7 +508,16 @@ function createClient(model, context, apiKey, optionsHeaders, fetch, sessionId,
|
|
|
504
508
|
}
|
|
505
509
|
function buildParams(model, context, options, compat = getCompat(model, options?.env), grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(context.messages), compat.supportsOpenAIGrammarTools)) {
|
|
506
510
|
const toolPlacement = resolveResponsesToolPlacement(context.messages, resolveResponsesDeferredToolsMode(compat) !== undefined);
|
|
507
|
-
|
|
511
|
+
// Sign in with ChatGPT rejects configuration_update items (senpi#6607), so the newest one, the level in
|
|
512
|
+
// force, becomes the request effort instead of the cache baseline the session sends, as upstream does.
|
|
513
|
+
const configuredEffort = compat.siwc && compat.supportsConfigurationUpdate
|
|
514
|
+
? context.messages.findLast((message) => message.role === "configurationUpdate")?.effort
|
|
515
|
+
: undefined;
|
|
516
|
+
const requestedReasoningEffort = configuredEffort === undefined
|
|
517
|
+
? (options?.reasoningEffort ?? (options?.reasoningSummary ? "medium" : undefined))
|
|
518
|
+
: configuredEffort === "off"
|
|
519
|
+
? undefined
|
|
520
|
+
: configuredEffort;
|
|
508
521
|
const thinkingLevelMap = inferOpenAIThinkingLevelMap(model);
|
|
509
522
|
const mappedReasoningEffort = requestedReasoningEffort === undefined ? undefined : thinkingLevelMap?.[requestedReasoningEffort];
|
|
510
523
|
const reasoningEffort = mappedReasoningEffort === undefined ? requestedReasoningEffort : mappedReasoningEffort;
|
package/dist/api-registry.d.ts
CHANGED
|
@@ -17,13 +17,11 @@ export type RegisteredApiProvider = {
|
|
|
17
17
|
/** Kept by `clearApiProviders()`; removed only through `unregisterApiProviders(sourceId)`. */
|
|
18
18
|
survivesClear?: boolean;
|
|
19
19
|
};
|
|
20
|
-
/** Browser-neutral shape
|
|
20
|
+
/** Browser-neutral shape of the scope object the node-only provider-scope subpath places in the realm's registry. */
|
|
21
21
|
export interface ProviderScopeAccess {
|
|
22
22
|
state: "active" | "closed";
|
|
23
23
|
overlay: Map<string, RegisteredApiProvider>;
|
|
24
24
|
}
|
|
25
|
-
/** Installs the optional node-only scope lookup without importing node APIs here. */
|
|
26
|
-
export declare function installProviderScopeAccessor(accessor: () => ProviderScopeAccess | undefined): void;
|
|
27
25
|
export declare function setProviderScopeStrictMode(enabled: boolean): void;
|
|
28
26
|
export declare function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(provider: ApiProvider<TApi, TOptions>, sourceId?: string, options?: {
|
|
29
27
|
survivesClear?: boolean;
|
|
@@ -32,6 +30,12 @@ export declare function registerApiProvider<TApi extends Api, TOptions extends S
|
|
|
32
30
|
export declare function registerBuiltinApiProvider<TApi extends Api, TOptions extends StreamOptions>(provider: ApiProvider<TApi, TOptions>): void;
|
|
33
31
|
export declare function getBuiltinApiProvider(api: Api): ApiProviderInternal | undefined;
|
|
34
32
|
export declare function getApiProvider(api: Api): ApiProviderInternal | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* `getApiProvider` for the built-in registration a module runs while it evaluates: the same resolution, but it never
|
|
35
|
+
* throws. Strict mode is shared by every copy in the realm, so a copy evaluated while it is on must still load
|
|
36
|
+
* (omo#12965); a closed scope reads as no scope. Every runtime lookup keeps `getApiProvider`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getApiProviderAtEvaluation(api: Api): ApiProviderInternal | undefined;
|
|
35
39
|
export declare function getApiProviders(): ApiProviderInternal[];
|
|
36
40
|
export declare function unregisterApiProviders(sourceId: string): void;
|
|
37
41
|
export declare function clearApiProviders(): void;
|
package/dist/api-registry.js
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
|
+
import { isActiveScopeWith, lookupProviderScopeRegistry } from "./provider-scope-registry.js";
|
|
1
2
|
import { getRegisteredFauxProvider } from "./providers/faux.js";
|
|
2
3
|
const apiProviderRegistry = new Map();
|
|
3
4
|
const builtinApiProviderRegistry = new Map();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function installProviderScopeAccessor(accessor) {
|
|
8
|
-
getProviderScope = accessor;
|
|
9
|
-
}
|
|
5
|
+
// Strict mode this copy was given while the realm had no provider-scope instance. It stays in force for this copy
|
|
6
|
+
// until this copy sets it again; once an instance exists, every set goes to the instance (omo#12965).
|
|
7
|
+
let strictModeBeforeRegistry = false;
|
|
10
8
|
export function setProviderScopeStrictMode(enabled) {
|
|
11
|
-
|
|
9
|
+
const registry = lookupProviderScopeRegistry();
|
|
10
|
+
strictModeBeforeRegistry = registry === undefined && enabled;
|
|
11
|
+
registry?.setStrictMode(enabled);
|
|
12
|
+
}
|
|
13
|
+
function isProviderScopeAccess(scope) {
|
|
14
|
+
return isActiveScopeWith(scope, "overlay");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The active scope any pi-ai copy in this realm entered, read structurally. `"closed"` covers a closed scope and one
|
|
18
|
+
* whose members another copy shaped differently: both refuse.
|
|
19
|
+
*/
|
|
20
|
+
function currentProviderScope() {
|
|
21
|
+
const scope = lookupProviderScopeRegistry()?.current();
|
|
22
|
+
if (scope === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
return isProviderScopeAccess(scope) ? scope : "closed";
|
|
12
25
|
}
|
|
13
26
|
function getActiveProviderScope() {
|
|
14
|
-
const scope =
|
|
15
|
-
if (scope
|
|
27
|
+
const scope = currentProviderScope();
|
|
28
|
+
if (scope === "closed")
|
|
16
29
|
throw new Error("Provider scope is closed");
|
|
17
|
-
if (
|
|
30
|
+
if (scope !== undefined)
|
|
31
|
+
return scope;
|
|
32
|
+
// A strict mode that is not a boolean (a newer copy's value) reads as on.
|
|
33
|
+
const strict = strictModeBeforeRegistry || (lookupProviderScopeRegistry()?.strictMode() ?? false) !== false;
|
|
34
|
+
if (strict)
|
|
18
35
|
throw new Error("Provider scope is required in strict mode");
|
|
19
|
-
return
|
|
36
|
+
return undefined;
|
|
20
37
|
}
|
|
21
38
|
function wrapStream(api, stream) {
|
|
22
39
|
return (model, context, options) => {
|
|
@@ -59,7 +76,18 @@ export function getBuiltinApiProvider(api) {
|
|
|
59
76
|
return builtinApiProviderRegistry.get(api)?.provider;
|
|
60
77
|
}
|
|
61
78
|
export function getApiProvider(api) {
|
|
62
|
-
|
|
79
|
+
return resolveApiProvider(api, getActiveProviderScope());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* `getApiProvider` for the built-in registration a module runs while it evaluates: the same resolution, but it never
|
|
83
|
+
* throws. Strict mode is shared by every copy in the realm, so a copy evaluated while it is on must still load
|
|
84
|
+
* (omo#12965); a closed scope reads as no scope. Every runtime lookup keeps `getApiProvider`.
|
|
85
|
+
*/
|
|
86
|
+
export function getApiProviderAtEvaluation(api) {
|
|
87
|
+
const scope = currentProviderScope();
|
|
88
|
+
return resolveApiProvider(api, scope === "closed" ? undefined : scope);
|
|
89
|
+
}
|
|
90
|
+
function resolveApiProvider(api, scope) {
|
|
63
91
|
if (scope)
|
|
64
92
|
return scope.overlay.get(api)?.provider ?? builtinApiProviderRegistry.get(api)?.provider;
|
|
65
93
|
const faux = getRegisteredFauxProvider(api);
|
package/dist/compat.js
CHANGED
|
@@ -54,7 +54,7 @@ export const getModels = getBuiltinModels;
|
|
|
54
54
|
/** @deprecated Static catalog read. Use `getBuiltinProviders` from "@earendil-works/pi-ai/providers/all" or `Models.getProviders()`. */
|
|
55
55
|
export const getProviders = getBuiltinProviders;
|
|
56
56
|
export { getApiProvider, getApiProviders, registerApiProvider, unregisterApiProviders, } from "./api-registry.js";
|
|
57
|
-
import { clearApiProviders, getApiProvider, registerApiProvider, registerBuiltinApiProvider, unregisterApiProviders, } from "./api-registry.js";
|
|
57
|
+
import { clearApiProviders, getApiProvider, getApiProviderAtEvaluation, registerApiProvider, registerBuiltinApiProvider, unregisterApiProviders, } from "./api-registry.js";
|
|
58
58
|
export function registerFauxProvider(options = {}) {
|
|
59
59
|
const core = createFauxCore(options);
|
|
60
60
|
const sourceId = `faux-provider-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -99,10 +99,11 @@ const builtinApiProviderInstances = new Map();
|
|
|
99
99
|
export function registerBuiltInApiProviders() {
|
|
100
100
|
for (const [api, streams] of BUILTIN_APIS) {
|
|
101
101
|
registerBuiltinApiProvider({ api, stream: streams.stream, streamSimple: streams.streamSimple });
|
|
102
|
-
|
|
102
|
+
// This runs while the module evaluates, so it must not throw for the realm's strict mode (omo#12965).
|
|
103
|
+
if (!getApiProviderAtEvaluation(api)) {
|
|
103
104
|
registerApiProvider({ api, stream: streams.stream, streamSimple: streams.streamSimple });
|
|
104
105
|
}
|
|
105
|
-
builtinApiProviderInstances.set(api,
|
|
106
|
+
builtinApiProviderInstances.set(api, getApiProviderAtEvaluation(api));
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
export function resetApiProviders() {
|
|
@@ -12,13 +12,11 @@ export type RegisteredImagesApiProvider = {
|
|
|
12
12
|
provider: ImagesApiProviderInternal;
|
|
13
13
|
sourceId?: string;
|
|
14
14
|
};
|
|
15
|
-
/** Browser-neutral shape
|
|
15
|
+
/** Browser-neutral shape of the scope object the node-only provider-scope subpath places in the realm's registry. */
|
|
16
16
|
export interface ImagesProviderScopeAccess {
|
|
17
17
|
state: "active" | "closed";
|
|
18
18
|
imagesOverlay: Map<string, RegisteredImagesApiProvider>;
|
|
19
19
|
}
|
|
20
|
-
/** Installs the optional node-only scope lookup without importing node APIs here. */
|
|
21
|
-
export declare function installImagesProviderScopeAccessor(accessor: () => ImagesProviderScopeAccess | undefined): void;
|
|
22
20
|
export declare function setImagesProviderScopeStrictMode(enabled: boolean): void;
|
|
23
21
|
export declare function registerImagesApiProvider<TApi extends ImageApi, TOptions extends ImagesOptions>(provider: ImagesApiProvider<TApi, TOptions>, sourceId?: string): void;
|
|
24
22
|
/** Registers a builtin once, retaining its immutable identity for active scopes. */
|
|
@@ -1,21 +1,33 @@
|
|
|
1
|
+
import { isActiveScopeWith, lookupProviderScopeRegistry } from "./provider-scope-registry.js";
|
|
1
2
|
const imagesApiProviderRegistry = new Map();
|
|
2
3
|
const builtinImagesApiProviderRegistry = new Map();
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function installImagesProviderScopeAccessor(accessor) {
|
|
7
|
-
getImagesProviderScope = accessor;
|
|
8
|
-
}
|
|
4
|
+
// Strict mode this copy was given while the realm had no provider-scope instance. It stays in force for this copy
|
|
5
|
+
// until this copy sets it again; once an instance exists, every set goes to the instance (omo#12965).
|
|
6
|
+
let strictModeBeforeRegistry = false;
|
|
9
7
|
export function setImagesProviderScopeStrictMode(enabled) {
|
|
10
|
-
|
|
8
|
+
const registry = lookupProviderScopeRegistry();
|
|
9
|
+
strictModeBeforeRegistry = registry === undefined && enabled;
|
|
10
|
+
registry?.setImagesStrictMode(enabled);
|
|
11
|
+
}
|
|
12
|
+
function isImagesProviderScopeAccess(scope) {
|
|
13
|
+
return isActiveScopeWith(scope, "imagesOverlay");
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The active scope any pi-ai copy in this realm entered, read structurally: a closed scope and one whose members
|
|
17
|
+
* another copy shaped differently both refuse. Outside a scope, a strict mode that is not a boolean reads as on.
|
|
18
|
+
*/
|
|
12
19
|
function getActiveImagesProviderScope() {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
const registry = lookupProviderScopeRegistry();
|
|
21
|
+
const scope = registry?.current();
|
|
22
|
+
if (scope !== undefined) {
|
|
23
|
+
if (!isImagesProviderScopeAccess(scope))
|
|
24
|
+
throw new Error("Provider scope is closed");
|
|
25
|
+
return scope;
|
|
26
|
+
}
|
|
27
|
+
const strict = strictModeBeforeRegistry || (registry?.imagesStrictMode() ?? false) !== false;
|
|
28
|
+
if (strict)
|
|
17
29
|
throw new Error("Provider scope is required in strict mode");
|
|
18
|
-
return
|
|
30
|
+
return undefined;
|
|
19
31
|
}
|
|
20
32
|
function wrapGenerateImages(api, generateImages) {
|
|
21
33
|
return (model, context, options) => {
|
|
@@ -7,7 +7,11 @@ export declare class ProviderScope implements ProviderScopeAccess, ImagesProvide
|
|
|
7
7
|
close(): void;
|
|
8
8
|
}
|
|
9
9
|
export declare function setProviderScopeStrictMode(enabled: boolean): void;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* The scope the caller is running in, or undefined outside any scope. A scope this copy cannot read refuses
|
|
12
|
+
* (`code: "PI_PROVIDER_SCOPE_UNREADABLE"`): it is never taken for "no scope", which would let a caller inside a
|
|
13
|
+
* session go on unscoped (omo#12965).
|
|
14
|
+
*/
|
|
11
15
|
export declare function activeProviderScope(): ProviderScope | undefined;
|
|
12
16
|
export declare function runWithProviderScope<T>(scope: ProviderScope, fn: () => T): T;
|
|
13
17
|
export declare function bindToProviderScope<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { realmRegistry } from "@earendil-works/pi-realm-registry";
|
|
3
|
+
import { setProviderScopeStrictMode as setRegistryProviderScopeStrictMode, } from "../api-registry.js";
|
|
4
|
+
import { setImagesProviderScopeStrictMode } from "../images-api-registry.js";
|
|
5
|
+
import { isActiveScopeWith, isProviderScopeRegistry, PROVIDER_SCOPE_REGISTRY_KEY, } from "../provider-scope-registry.js";
|
|
6
|
+
// The one place that creates the realm's provider-scope instance (omo#12965): the storage and both strict modes live
|
|
7
|
+
// in this closure, and every pi-ai copy in the realm reads them through the instance.
|
|
8
|
+
const providerScopes = realmRegistry(PROVIDER_SCOPE_REGISTRY_KEY, () => {
|
|
9
|
+
const storage = new AsyncLocalStorage();
|
|
10
|
+
let strict = false;
|
|
11
|
+
let imagesStrict = false;
|
|
12
|
+
return {
|
|
13
|
+
strictMode: () => strict,
|
|
14
|
+
setStrictMode: (enabled) => {
|
|
15
|
+
strict = enabled;
|
|
16
|
+
},
|
|
17
|
+
imagesStrictMode: () => imagesStrict,
|
|
18
|
+
setImagesStrictMode: (enabled) => {
|
|
19
|
+
imagesStrict = enabled;
|
|
20
|
+
},
|
|
21
|
+
run: (scope, fn) => storage.run(scope, fn),
|
|
22
|
+
current: () => storage.getStore(),
|
|
23
|
+
};
|
|
24
|
+
}, isProviderScopeRegistry);
|
|
5
25
|
export class ProviderScope {
|
|
6
26
|
constructor() {
|
|
7
27
|
this.overlay = new Map();
|
|
@@ -14,25 +34,55 @@ export class ProviderScope {
|
|
|
14
34
|
this.imagesOverlay.clear();
|
|
15
35
|
}
|
|
16
36
|
}
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
/** A scope object any pi-ai copy in this realm entered: the same members, whichever copy's class built it. */
|
|
38
|
+
function isProviderScope(scope) {
|
|
39
|
+
if (typeof scope !== "object" || scope === null)
|
|
40
|
+
return false;
|
|
41
|
+
const state = Reflect.get(scope, "state");
|
|
42
|
+
return ((state === "active" || state === "closed") &&
|
|
43
|
+
Reflect.get(scope, "overlay") instanceof Map &&
|
|
44
|
+
Reflect.get(scope, "imagesOverlay") instanceof Map &&
|
|
45
|
+
typeof Reflect.get(scope, "close") === "function");
|
|
46
|
+
}
|
|
19
47
|
export function setProviderScopeStrictMode(enabled) {
|
|
20
48
|
setRegistryProviderScopeStrictMode(enabled);
|
|
21
49
|
setImagesProviderScopeStrictMode(enabled);
|
|
22
50
|
}
|
|
23
|
-
/** The scope the caller is running in
|
|
51
|
+
/** The scope the caller is running in cannot be read: another pi-ai copy in the realm shaped it differently. */
|
|
52
|
+
class ProviderScopeUnreadableError extends Error {
|
|
53
|
+
constructor() {
|
|
54
|
+
super("The active provider scope cannot be read: another pi-ai copy in this process shaped it differently.");
|
|
55
|
+
this.code = "PI_PROVIDER_SCOPE_UNREADABLE";
|
|
56
|
+
this.name = "ProviderScopeUnreadableError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The scope the caller is running in, or undefined outside any scope. A scope this copy cannot read refuses
|
|
61
|
+
* (`code: "PI_PROVIDER_SCOPE_UNREADABLE"`): it is never taken for "no scope", which would let a caller inside a
|
|
62
|
+
* session go on unscoped (omo#12965).
|
|
63
|
+
*/
|
|
24
64
|
export function activeProviderScope() {
|
|
25
|
-
|
|
65
|
+
const scope = providerScopes.current();
|
|
66
|
+
if (scope === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (!isProviderScope(scope))
|
|
69
|
+
throw new ProviderScopeUnreadableError();
|
|
70
|
+
return scope;
|
|
26
71
|
}
|
|
27
72
|
export function runWithProviderScope(scope, fn) {
|
|
28
73
|
if (scope.state === "closed")
|
|
29
74
|
throw new Error("Provider scope is closed");
|
|
30
|
-
return
|
|
75
|
+
return providerScopes.run(scope, fn);
|
|
31
76
|
}
|
|
32
77
|
export function bindToProviderScope(fn) {
|
|
33
|
-
const scope =
|
|
34
|
-
if (
|
|
78
|
+
const scope = providerScopes.current();
|
|
79
|
+
if (typeof scope !== "object" || scope === null)
|
|
35
80
|
throw new Error("No active provider scope to bind");
|
|
36
|
-
return (...args) =>
|
|
81
|
+
return (...args) => {
|
|
82
|
+
// A scope another copy shaped differently refuses, as a closed one does.
|
|
83
|
+
if (!isActiveScopeWith(scope, "overlay"))
|
|
84
|
+
throw new Error("Provider scope is closed");
|
|
85
|
+
return providerScopes.run(scope, () => fn(...args));
|
|
86
|
+
};
|
|
37
87
|
}
|
|
38
88
|
//# sourceMappingURL=provider-scope.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const PROVIDER_SCOPE_REGISTRY_KEY = "@code-yeongyu/senpi/provider-scope/v1";
|
|
2
|
+
/**
|
|
3
|
+
* Provider strict mode and the active provider scope of every pi-ai copy in this realm (omo#12965).
|
|
4
|
+
*
|
|
5
|
+
* A realm can hold more than one copy of pi-ai (the bundled engine next to the SDK graph a plugin imports). Strict mode
|
|
6
|
+
* set in one copy and a scope entered by one copy must hold in all of them, or a second copy resolves providers from
|
|
7
|
+
* its global registry while the first one refuses. Only `node/provider-scope.ts` creates the instance, because only it
|
|
8
|
+
* may build an `AsyncLocalStorage`; this browser-neutral module looks the instance up and never creates one.
|
|
9
|
+
*
|
|
10
|
+
* Provider entries are not shared: each copy keeps its own maps. Inside a scope the entries live in the scope object,
|
|
11
|
+
* which every copy reads structurally. Values that cross copies are validated by the reader: a scope missing a member
|
|
12
|
+
* reads as closed, and a strict mode that is not a boolean reads as on.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProviderScopeRegistry {
|
|
15
|
+
strictMode(): unknown;
|
|
16
|
+
setStrictMode(enabled: boolean): void;
|
|
17
|
+
imagesStrictMode(): unknown;
|
|
18
|
+
setImagesStrictMode(enabled: boolean): void;
|
|
19
|
+
run<T>(scope: object, fn: () => T): T;
|
|
20
|
+
current(): unknown;
|
|
21
|
+
}
|
|
22
|
+
export declare function isProviderScopeRegistry(value: unknown): value is ProviderScopeRegistry;
|
|
23
|
+
/** The realm's instance, or `undefined` while no copy's `node/provider-scope` module has evaluated. */
|
|
24
|
+
export declare function lookupProviderScopeRegistry(): ProviderScopeRegistry | undefined;
|
|
25
|
+
/** Whether a scope object another copy may have placed is active and carries `overlayName` as a `Map`. */
|
|
26
|
+
export declare function isActiveScopeWith(scope: unknown, overlayName: "overlay" | "imagesOverlay"): boolean;
|
|
27
|
+
//# sourceMappingURL=provider-scope-registry.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { hasFunctions, realmRegistryLookup } from "@earendil-works/pi-realm-registry";
|
|
2
|
+
export const PROVIDER_SCOPE_REGISTRY_KEY = "@code-yeongyu/senpi/provider-scope/v1";
|
|
3
|
+
export function isProviderScopeRegistry(value) {
|
|
4
|
+
return hasFunctions(value, [
|
|
5
|
+
"strictMode",
|
|
6
|
+
"setStrictMode",
|
|
7
|
+
"imagesStrictMode",
|
|
8
|
+
"setImagesStrictMode",
|
|
9
|
+
"run",
|
|
10
|
+
"current",
|
|
11
|
+
]);
|
|
12
|
+
}
|
|
13
|
+
/** The realm's instance, or `undefined` while no copy's `node/provider-scope` module has evaluated. */
|
|
14
|
+
export function lookupProviderScopeRegistry() {
|
|
15
|
+
return realmRegistryLookup(PROVIDER_SCOPE_REGISTRY_KEY, isProviderScopeRegistry);
|
|
16
|
+
}
|
|
17
|
+
/** Whether a scope object another copy may have placed is active and carries `overlayName` as a `Map`. */
|
|
18
|
+
export function isActiveScopeWith(scope, overlayName) {
|
|
19
|
+
return (typeof scope === "object" &&
|
|
20
|
+
scope !== null &&
|
|
21
|
+
Reflect.get(scope, "state") === "active" &&
|
|
22
|
+
Reflect.get(scope, overlayName) instanceof Map);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=provider-scope-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":6,"generatedAt":"2026-11-
|
|
1
|
+
{"schemaVersion":6,"generatedAt":"2026-11-15T04:08:56.120Z","structureHash":"4d17c5b11d7fe951e3c29e503a1caed6c926f74190d61cfeb62c7f9a24814860","files":{"alibaba-token-plan.json":"9f4d42cc765b2a875bcdcaa392bfdc1323c88968309baf9db9f611a26c595c73","amazon-bedrock.json":"bff6e399b377ed5add61402119cc69daed1cfa7084d8e1efa3ae227da34cf323","ant-ling.json":"628feab3ef6c7d80ce96f75999804e1d9f05fec3851e5302e30fff3b69db90f6","anthropic.json":"4e1e3185a5e9c8766d7e3939c26f854e76a7fa95313efeda5c81c31f89a52de8","azure-openai-responses.json":"fd64c6a2a99fa87d768ed2f75f5c7813876df1dbe2f0d1776f7d9bd7ce2e61e4","bai.json":"7e6861aaf27d8af34e1771c6092602194fcec0a0046e6adfab9c3ee54d32b99f","baseten.json":"47d10c8050601eb88757def4fc957cdba769dacec4aeb3b0c3f39c41e442330e","cerebras.json":"2eb969b2956844f9a641d3de52c19f1791293c58f607282fb3b0ebfe85ff66f8","chatgpt-subscription.json":"98320f1d51709d26ca047b3d6e3191cfe2f192f1b27897cbb492f47b0e5a5030","cloudflare-ai-gateway.json":"307da650bbefbd4654191363672410c7b01c93d825b4f6089ce36d86c9f61bc4","cloudflare-workers-ai.json":"8b3502ff0ba0adb6d1388de33fea839289ad1968faed15161fcef8dc6e140a96","deepseek.json":"10a296fb3e898f7715c80890c8af0af4f5fd58f22dbcd6612fdc5d762157ccdc","fireworks.json":"4c6740d5719f5a7ebc5e21aab65f38a3df41963572995de1abb2169fbbc200b9","github-copilot.json":"0e470016348389dab54679a2f26e41a546fc6223685a768dd8de0f26fa4b1e0a","google-vertex.json":"df3ac69f7a22704b75486658bd422e231d345d73c0700f08ea517df1b23397f8","google.json":"5f7a3c61abd19b81a4c926375d4109d4723cd06e36da555a719616abe214eeda","groq.json":"087df37206e9eed8b10f1e558e4c496289bef60074b7e8afba4f20fb102b4ec2","huggingface.json":"d3700227e8b66b5e0f60b22e006c4ebc8978e3340ca18af29eb3b21c5e40c963","meta.json":"5cb9af55aeb384c07afb18ded1eff4cc8aace69556cd019ab758d987afb40495","minimax-cn.json":"70df757e95e9ea2275bf451956b58a746020835f6cbcbd1a644d7fad5b9941d3","minimax.json":"747038b05f4fc24e41405e8bc810f893d7d5121e214e35e6f812f775769a3806","mistral.json":"1dddfa4e11ba07166b54d16ee6408943cd21982c390401333328fdc31ac08c95","moonshotai-cn.json":"a953eace729ef96c91195d477c63c776a1c40aa36085b61236ca1be0fe5b9337","moonshotai.json":"0435f9dc4e532c0764b6d33d8dfa83ce5fccdbcab54224c5ae74ec2d965290f5","nvidia.json":"5001ac6932013cadc675343b29d675ce093eb07da1ea941196cb600ddee5c694","openai.json":"80845240dd2e4cac670f8040ea7bdda867ca84cc0e4943b6b1b88a1676c0dbc7","opencode-go.json":"e7bcec18256bc849dd0250c81974b89e30a51bd47eba0043524c7f4e6a9e91c4","opencode.json":"8c20ad014f86b27ddd313254f27b64cc4554b7152a7b45c6e8ba09c9ee7f72fc","opengateway.json":"b31e8a9b176865e98e9487b600e73b311399a28f4ad93854231a2ed928b39f34","openrouter.json":"e8ed52b87969fc6bb4e8547773ed455e2915b9916a09fb88e4f0342feadfb870","qwen-token-plan-cn.json":"751b18bd443fceb16659ea33013fed90fbf3bbdb4e0ffd7607c93cee2b4cf40d","qwen-token-plan-individual.json":"35ffb0ba1a1ce3f68fc73262dd5e07fbcb5bf5911ad1e6e17bc0621e2894676a","qwen-token-plan.json":"e59b0a45e238578f80d0eea937a093b9cec7cb93751ae20a9e88b16c09eb5cdd","radius.json":"e95ce559b43ca79301b2e53a8cfb8dca3ae4cebd90201eb7d73e02722cb31c64","together.json":"cc8bd70f956304fe01aedd925759eb89a052565af7caec6397d3683e0a202879","typesafe.json":"83409437dbf4c353ac413c0db4e7cf29902fc35e0e0197bdb4eeda2d7f3b07cd","venice.json":"13b57bdfaf359b0a2dd6946e53464035a88298d450e530e1d98632be8fd295eb","vercel-ai-gateway.json":"5fa1054a6d7d3cb2133a6cba2a0a2595f59f913bc7179328736de6bf0878cbb6","xai.json":"cf3f475fe5a13625f858171900ba3456c4d95ebb0bac5060cc25e29bb31d3457","xiaomi-token-plan-ams.json":"7fa339ba5675460b1ff4e5bca9e8ad0aa60b0277d75a989b1c196aa6ca2af34d","xiaomi-token-plan-cn.json":"ef7917c6ea0d3edc655c8a52082ee5f1330658aa6f10199d8f164eb4605ef0a0","xiaomi-token-plan-sgp.json":"addda93f6b70ba879db97e3cfd2e4f2efc7cda47d7daa78c23c5b0e51162de5c","xiaomi.json":"5a42caf49d3e4d7946d60386e9899c2146bf8e3cbaff4c576ee4f49b398cc11c","zai-coding-cn.json":"2b6f85e04b5c568e320b1bde25b1c1614af9342e4c1ba0e1d5bb248aaf50cfe9","zai.json":"e9cc408e1756de8abc70148d63d89d11a63c65b7ef06ad0661cd1328e4209ba3"}}
|