@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/index.js
CHANGED
|
@@ -899,6 +899,42 @@ var init_ProviderRegistry = __esm({
|
|
|
899
899
|
this.providerOverrideFn = fn;
|
|
900
900
|
this.providerOverrideRevision += 1;
|
|
901
901
|
}
|
|
902
|
+
/**
|
|
903
|
+
* The env a provider resolves against for a given execution, scoped to the
|
|
904
|
+
* THREAD rather than just the instance: thread env overrides win, then the
|
|
905
|
+
* thread OWNER's user env, then instance/Worker env. This is what makes
|
|
906
|
+
* per-user BYOK providers work — a credential a user installs in their own
|
|
907
|
+
* account env (e.g. an OAuth token a companion service rotates) is honored
|
|
908
|
+
* for that user's threads no matter which authorized actor plays the turn.
|
|
909
|
+
*
|
|
910
|
+
* Best-effort by design: a missing user id (super-admin/local threads) or
|
|
911
|
+
* an older AgentBuilder DO without user-env storage simply contributes
|
|
912
|
+
* nothing, leaving instance-env behavior untouched.
|
|
913
|
+
*/
|
|
914
|
+
async buildThreadScopedEnv(state) {
|
|
915
|
+
const threadEnv = state.thread.metadata?.env ?? state.thread.metadata?.tenvs ?? null;
|
|
916
|
+
const userId = state.thread.metadata?.user_id ?? null;
|
|
917
|
+
let userEnv = null;
|
|
918
|
+
if (userId) {
|
|
919
|
+
try {
|
|
920
|
+
const agentBuilderId = state.env.AGENT_BUILDER.idFromName("singleton");
|
|
921
|
+
const agentBuilder = state.env.AGENT_BUILDER.get(agentBuilderId);
|
|
922
|
+
if (typeof agentBuilder.getUserEnvEntries === "function") {
|
|
923
|
+
const entries = await agentBuilder.getUserEnvEntries(userId);
|
|
924
|
+
if (Array.isArray(entries) && entries.length > 0) {
|
|
925
|
+
userEnv = {};
|
|
926
|
+
for (const entry of entries) {
|
|
927
|
+
if (entry?.name) userEnv[entry.name] = entry.value;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
} catch (error) {
|
|
932
|
+
console.warn("buildThreadScopedEnv: user env unavailable:", error instanceof Error ? error.message : String(error));
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (!userEnv && !threadEnv) return state.env;
|
|
936
|
+
return { ...state.env, ...userEnv ?? {}, ...threadEnv ?? {} };
|
|
937
|
+
}
|
|
902
938
|
/**
|
|
903
939
|
* Get provider for a model.
|
|
904
940
|
*
|
|
@@ -1810,7 +1846,8 @@ var init_LLMRequest = __esm({
|
|
|
1810
1846
|
*/
|
|
1811
1847
|
static async callModel(modelId, context, state, logId) {
|
|
1812
1848
|
const { ProviderRegistry: ProviderRegistry2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
|
|
1813
|
-
const
|
|
1849
|
+
const providerEnv2 = await ProviderRegistry2.buildThreadScopedEnv(state);
|
|
1850
|
+
const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(modelId, providerEnv2, state.thread.instance);
|
|
1814
1851
|
const streamAbort = new AbortController();
|
|
1815
1852
|
const parentSignal = state.abortController?.signal;
|
|
1816
1853
|
let onParentAbort;
|
|
@@ -24412,9 +24449,11 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
24412
24449
|
codeExecutionBridges = /* @__PURE__ */ new Map();
|
|
24413
24450
|
constructor(ctx, env) {
|
|
24414
24451
|
super(ctx, env);
|
|
24415
|
-
|
|
24416
|
-
|
|
24417
|
-
|
|
24452
|
+
if (typeof WebSocketRequestResponsePair !== "undefined") {
|
|
24453
|
+
ctx.setWebSocketAutoResponse(
|
|
24454
|
+
new WebSocketRequestResponsePair("stream_ping", "stream_pong")
|
|
24455
|
+
);
|
|
24456
|
+
}
|
|
24418
24457
|
this.alarmQueue = new AlarmQueue(ctx);
|
|
24419
24458
|
this.alarmQueue.setExecutor(
|
|
24420
24459
|
async (method, args, itemId) => {
|
|
@@ -27779,14 +27818,13 @@ ${resultContent}${attachmentSummary}`;
|
|
|
27779
27818
|
async setExecutionOwner(owner) {
|
|
27780
27819
|
await this.setValue(EXECUTION_OWNER_KEY, owner);
|
|
27781
27820
|
}
|
|
27782
|
-
/** Whether the given client id has
|
|
27821
|
+
/** Whether the given client id has an open bridge socket (excluding `except`).
|
|
27822
|
+
* Idle bridge heartbeats are handled by Cloudflare without waking this object,
|
|
27823
|
+
* so timestamp liveness is intentionally enforced only for an in-flight call. */
|
|
27783
27824
|
hasLiveBridgeSocketForClient(clientId, except) {
|
|
27784
|
-
const now = Date.now();
|
|
27785
27825
|
for (const ws of this.bridgeSockets) {
|
|
27786
27826
|
if (ws === except) continue;
|
|
27787
27827
|
if (ws.readyState !== WebSocket.OPEN) continue;
|
|
27788
|
-
const lastSeen = this.bridgeLastSeen.get(ws) ?? 0;
|
|
27789
|
-
if (now - lastSeen > BRIDGE_LIVENESS_TIMEOUT_MS) continue;
|
|
27790
27828
|
if ((this.bridgeClientInfo.get(ws)?.clientId ?? null) === clientId) return true;
|
|
27791
27829
|
}
|
|
27792
27830
|
return false;
|
|
@@ -27947,22 +27985,19 @@ ${resultContent}${attachmentSummary}`;
|
|
|
27947
27985
|
}
|
|
27948
27986
|
/**
|
|
27949
27987
|
* Return the bridge socket a forwarded call should be dispatched to, pruning
|
|
27950
|
-
*
|
|
27951
|
-
*
|
|
27952
|
-
*
|
|
27953
|
-
*
|
|
27954
|
-
* gone client — and it means we won't dispatch a forwarded call into the void.
|
|
27988
|
+
* closed ones. Idle clients use Cloudflare's hibernation auto-response, so
|
|
27989
|
+
* their frames deliberately do not update `bridgeLastSeen`. Once a call is
|
|
27990
|
+
* dispatched, the client switches to application-visible pings and the
|
|
27991
|
+
* in-flight liveness timer above detects a half-open connection.
|
|
27955
27992
|
*
|
|
27956
27993
|
* With an execution owner recorded, only the owner's socket (or, as a
|
|
27957
27994
|
* back-compat fallback, an unidentified legacy socket) is eligible; with no
|
|
27958
27995
|
* owner, the first live socket wins as it always has.
|
|
27959
27996
|
*/
|
|
27960
27997
|
getOpenBridgeSocket(ownerClientId) {
|
|
27961
|
-
const now = Date.now();
|
|
27962
27998
|
const live = [];
|
|
27963
27999
|
for (const ws of Array.from(this.bridgeSockets)) {
|
|
27964
|
-
|
|
27965
|
-
if (ws.readyState === WebSocket.OPEN && now - lastSeen <= BRIDGE_LIVENESS_TIMEOUT_MS) {
|
|
28000
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
27966
28001
|
live.push({ socket: ws, clientId: this.bridgeClientInfo.get(ws)?.clientId ?? null });
|
|
27967
28002
|
continue;
|
|
27968
28003
|
}
|
|
@@ -30636,9 +30671,11 @@ var DurableAgentBuilder = class extends DurableObject {
|
|
|
30636
30671
|
eventSockets = /* @__PURE__ */ new Set();
|
|
30637
30672
|
constructor(ctx, env) {
|
|
30638
30673
|
super(ctx, env);
|
|
30639
|
-
|
|
30640
|
-
|
|
30641
|
-
|
|
30674
|
+
if (typeof WebSocketRequestResponsePair !== "undefined") {
|
|
30675
|
+
ctx.setWebSocketAutoResponse(
|
|
30676
|
+
new WebSocketRequestResponsePair("stream_ping", "stream_pong")
|
|
30677
|
+
);
|
|
30678
|
+
}
|
|
30642
30679
|
const sockets = ctx.getWebSockets();
|
|
30643
30680
|
for (const ws of sockets) {
|
|
30644
30681
|
const attachment = ws.deserializeAttachment();
|