@standardagents/builder 0.27.5 → 0.27.7
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/built-in-routes.js +39 -2
- package/dist/built-in-routes.js.map +1 -1
- package/dist/index.js +56 -19
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +29 -6
- package/dist/runtime.js +56 -19
- package/dist/runtime.js.map +1 -1
- package/package.json +5 -5
package/dist/runtime.d.ts
CHANGED
|
@@ -1003,7 +1003,9 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
|
|
|
1003
1003
|
private currentExecutionOwnerId;
|
|
1004
1004
|
/** Persist + cache the execution-owner record (null releases ownership). */
|
|
1005
1005
|
private setExecutionOwner;
|
|
1006
|
-
/** Whether the given client id has
|
|
1006
|
+
/** Whether the given client id has an open bridge socket (excluding `except`).
|
|
1007
|
+
* Idle bridge heartbeats are handled by Cloudflare without waking this object,
|
|
1008
|
+
* so timestamp liveness is intentionally enforced only for an in-flight call. */
|
|
1007
1009
|
private hasLiveBridgeSocketForClient;
|
|
1008
1010
|
/** Tell every other bridge socket that execution ownership moved. */
|
|
1009
1011
|
private broadcastOwnerChanged;
|
|
@@ -1031,11 +1033,10 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
|
|
|
1031
1033
|
private markBridgeDead;
|
|
1032
1034
|
/**
|
|
1033
1035
|
* Return the bridge socket a forwarded call should be dispatched to, pruning
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1038
|
-
* gone client — and it means we won't dispatch a forwarded call into the void.
|
|
1036
|
+
* closed ones. Idle clients use Cloudflare's hibernation auto-response, so
|
|
1037
|
+
* their frames deliberately do not update `bridgeLastSeen`. Once a call is
|
|
1038
|
+
* dispatched, the client switches to application-visible pings and the
|
|
1039
|
+
* in-flight liveness timer above detects a half-open connection.
|
|
1039
1040
|
*
|
|
1040
1041
|
* With an execution owner recorded, only the owner's socket (or, as a
|
|
1041
1042
|
* back-compat fallback, an unidentified legacy socket) is eligible; with no
|
|
@@ -3604,6 +3605,28 @@ declare class ProviderRegistryImpl {
|
|
|
3604
3605
|
private providerOverrideFn;
|
|
3605
3606
|
private providerOverrideRevision;
|
|
3606
3607
|
setProviderOverride(fn: ProviderOverrideFn | null): void;
|
|
3608
|
+
/**
|
|
3609
|
+
* The env a provider resolves against for a given execution, scoped to the
|
|
3610
|
+
* THREAD rather than just the instance: thread env overrides win, then the
|
|
3611
|
+
* thread OWNER's user env, then instance/Worker env. This is what makes
|
|
3612
|
+
* per-user BYOK providers work — a credential a user installs in their own
|
|
3613
|
+
* account env (e.g. an OAuth token a companion service rotates) is honored
|
|
3614
|
+
* for that user's threads no matter which authorized actor plays the turn.
|
|
3615
|
+
*
|
|
3616
|
+
* Best-effort by design: a missing user id (super-admin/local threads) or
|
|
3617
|
+
* an older AgentBuilder DO without user-env storage simply contributes
|
|
3618
|
+
* nothing, leaving instance-env behavior untouched.
|
|
3619
|
+
*/
|
|
3620
|
+
buildThreadScopedEnv(state: {
|
|
3621
|
+
env: Env;
|
|
3622
|
+
thread: {
|
|
3623
|
+
metadata?: {
|
|
3624
|
+
user_id?: string | null;
|
|
3625
|
+
env?: Record<string, unknown> | null;
|
|
3626
|
+
tenvs?: Record<string, unknown> | null;
|
|
3627
|
+
};
|
|
3628
|
+
};
|
|
3629
|
+
}): Promise<Env>;
|
|
3607
3630
|
/**
|
|
3608
3631
|
* Get provider for a model.
|
|
3609
3632
|
*
|
package/dist/runtime.js
CHANGED
|
@@ -890,6 +890,42 @@ var init_ProviderRegistry = __esm({
|
|
|
890
890
|
this.providerOverrideFn = fn;
|
|
891
891
|
this.providerOverrideRevision += 1;
|
|
892
892
|
}
|
|
893
|
+
/**
|
|
894
|
+
* The env a provider resolves against for a given execution, scoped to the
|
|
895
|
+
* THREAD rather than just the instance: thread env overrides win, then the
|
|
896
|
+
* thread OWNER's user env, then instance/Worker env. This is what makes
|
|
897
|
+
* per-user BYOK providers work — a credential a user installs in their own
|
|
898
|
+
* account env (e.g. an OAuth token a companion service rotates) is honored
|
|
899
|
+
* for that user's threads no matter which authorized actor plays the turn.
|
|
900
|
+
*
|
|
901
|
+
* Best-effort by design: a missing user id (super-admin/local threads) or
|
|
902
|
+
* an older AgentBuilder DO without user-env storage simply contributes
|
|
903
|
+
* nothing, leaving instance-env behavior untouched.
|
|
904
|
+
*/
|
|
905
|
+
async buildThreadScopedEnv(state) {
|
|
906
|
+
const threadEnv = state.thread.metadata?.env ?? state.thread.metadata?.tenvs ?? null;
|
|
907
|
+
const userId = state.thread.metadata?.user_id ?? null;
|
|
908
|
+
let userEnv = null;
|
|
909
|
+
if (userId) {
|
|
910
|
+
try {
|
|
911
|
+
const agentBuilderId = state.env.AGENT_BUILDER.idFromName("singleton");
|
|
912
|
+
const agentBuilder = state.env.AGENT_BUILDER.get(agentBuilderId);
|
|
913
|
+
if (typeof agentBuilder.getUserEnvEntries === "function") {
|
|
914
|
+
const entries = await agentBuilder.getUserEnvEntries(userId);
|
|
915
|
+
if (Array.isArray(entries) && entries.length > 0) {
|
|
916
|
+
userEnv = {};
|
|
917
|
+
for (const entry of entries) {
|
|
918
|
+
if (entry?.name) userEnv[entry.name] = entry.value;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
} catch (error) {
|
|
923
|
+
console.warn("buildThreadScopedEnv: user env unavailable:", error instanceof Error ? error.message : String(error));
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (!userEnv && !threadEnv) return state.env;
|
|
927
|
+
return { ...state.env, ...userEnv ?? {}, ...threadEnv ?? {} };
|
|
928
|
+
}
|
|
893
929
|
/**
|
|
894
930
|
* Get provider for a model.
|
|
895
931
|
*
|
|
@@ -1801,7 +1837,8 @@ var init_LLMRequest = __esm({
|
|
|
1801
1837
|
*/
|
|
1802
1838
|
static async callModel(modelId, context, state, logId) {
|
|
1803
1839
|
const { ProviderRegistry: ProviderRegistry2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
|
|
1804
|
-
const
|
|
1840
|
+
const providerEnv = await ProviderRegistry2.buildThreadScopedEnv(state);
|
|
1841
|
+
const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(modelId, providerEnv, state.thread.instance);
|
|
1805
1842
|
const streamAbort = new AbortController();
|
|
1806
1843
|
const parentSignal = state.abortController?.signal;
|
|
1807
1844
|
let onParentAbort;
|
|
@@ -16093,9 +16130,11 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
16093
16130
|
codeExecutionBridges = /* @__PURE__ */ new Map();
|
|
16094
16131
|
constructor(ctx, env) {
|
|
16095
16132
|
super(ctx, env);
|
|
16096
|
-
|
|
16097
|
-
|
|
16098
|
-
|
|
16133
|
+
if (typeof WebSocketRequestResponsePair !== "undefined") {
|
|
16134
|
+
ctx.setWebSocketAutoResponse(
|
|
16135
|
+
new WebSocketRequestResponsePair("stream_ping", "stream_pong")
|
|
16136
|
+
);
|
|
16137
|
+
}
|
|
16099
16138
|
this.alarmQueue = new AlarmQueue(ctx);
|
|
16100
16139
|
this.alarmQueue.setExecutor(
|
|
16101
16140
|
async (method, args, itemId) => {
|
|
@@ -19460,14 +19499,13 @@ ${resultContent}${attachmentSummary}`;
|
|
|
19460
19499
|
async setExecutionOwner(owner) {
|
|
19461
19500
|
await this.setValue(EXECUTION_OWNER_KEY, owner);
|
|
19462
19501
|
}
|
|
19463
|
-
/** Whether the given client id has
|
|
19502
|
+
/** Whether the given client id has an open bridge socket (excluding `except`).
|
|
19503
|
+
* Idle bridge heartbeats are handled by Cloudflare without waking this object,
|
|
19504
|
+
* so timestamp liveness is intentionally enforced only for an in-flight call. */
|
|
19464
19505
|
hasLiveBridgeSocketForClient(clientId, except) {
|
|
19465
|
-
const now = Date.now();
|
|
19466
19506
|
for (const ws of this.bridgeSockets) {
|
|
19467
19507
|
if (ws === except) continue;
|
|
19468
19508
|
if (ws.readyState !== WebSocket.OPEN) continue;
|
|
19469
|
-
const lastSeen = this.bridgeLastSeen.get(ws) ?? 0;
|
|
19470
|
-
if (now - lastSeen > BRIDGE_LIVENESS_TIMEOUT_MS) continue;
|
|
19471
19509
|
if ((this.bridgeClientInfo.get(ws)?.clientId ?? null) === clientId) return true;
|
|
19472
19510
|
}
|
|
19473
19511
|
return false;
|
|
@@ -19628,22 +19666,19 @@ ${resultContent}${attachmentSummary}`;
|
|
|
19628
19666
|
}
|
|
19629
19667
|
/**
|
|
19630
19668
|
* Return the bridge socket a forwarded call should be dispatched to, pruning
|
|
19631
|
-
*
|
|
19632
|
-
*
|
|
19633
|
-
*
|
|
19634
|
-
*
|
|
19635
|
-
* gone client — and it means we won't dispatch a forwarded call into the void.
|
|
19669
|
+
* closed ones. Idle clients use Cloudflare's hibernation auto-response, so
|
|
19670
|
+
* their frames deliberately do not update `bridgeLastSeen`. Once a call is
|
|
19671
|
+
* dispatched, the client switches to application-visible pings and the
|
|
19672
|
+
* in-flight liveness timer above detects a half-open connection.
|
|
19636
19673
|
*
|
|
19637
19674
|
* With an execution owner recorded, only the owner's socket (or, as a
|
|
19638
19675
|
* back-compat fallback, an unidentified legacy socket) is eligible; with no
|
|
19639
19676
|
* owner, the first live socket wins as it always has.
|
|
19640
19677
|
*/
|
|
19641
19678
|
getOpenBridgeSocket(ownerClientId) {
|
|
19642
|
-
const now = Date.now();
|
|
19643
19679
|
const live = [];
|
|
19644
19680
|
for (const ws of Array.from(this.bridgeSockets)) {
|
|
19645
|
-
|
|
19646
|
-
if (ws.readyState === WebSocket.OPEN && now - lastSeen <= BRIDGE_LIVENESS_TIMEOUT_MS) {
|
|
19681
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
19647
19682
|
live.push({ socket: ws, clientId: this.bridgeClientInfo.get(ws)?.clientId ?? null });
|
|
19648
19683
|
continue;
|
|
19649
19684
|
}
|
|
@@ -22317,9 +22352,11 @@ var DurableAgentBuilder = class extends DurableObject {
|
|
|
22317
22352
|
eventSockets = /* @__PURE__ */ new Set();
|
|
22318
22353
|
constructor(ctx, env) {
|
|
22319
22354
|
super(ctx, env);
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22355
|
+
if (typeof WebSocketRequestResponsePair !== "undefined") {
|
|
22356
|
+
ctx.setWebSocketAutoResponse(
|
|
22357
|
+
new WebSocketRequestResponsePair("stream_ping", "stream_pong")
|
|
22358
|
+
);
|
|
22359
|
+
}
|
|
22323
22360
|
const sockets = ctx.getWebSockets();
|
|
22324
22361
|
for (const ws of sockets) {
|
|
22325
22362
|
const attachment = ws.deserializeAttachment();
|