@standardagents/builder 0.27.13 → 0.28.1

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.
@@ -48,6 +48,26 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
48
48
  mod
49
49
  ));
50
50
 
51
+ // src/agents/client-forwarding.ts
52
+ function encodeStreamChunk(bytes) {
53
+ let binary = "";
54
+ const step = 32768;
55
+ for (let i = 0; i < bytes.length; i += step) {
56
+ binary += String.fromCharCode(...bytes.subarray(i, i + step));
57
+ }
58
+ return btoa(binary);
59
+ }
60
+ function decodeStreamChunk(data) {
61
+ const binary = atob(data);
62
+ const bytes = new Uint8Array(binary.length);
63
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
64
+ return bytes;
65
+ }
66
+ var init_client_forwarding = __esm({
67
+ "src/agents/client-forwarding.ts"() {
68
+ }
69
+ });
70
+
51
71
  // src/agents/types.ts
52
72
  var SUBAGENT_INITIAL_ARGUMENTS_THREAD_PROPERTY, MAX_STEPS, MAX_RETRIES_PER_MODEL, TIMESTAMP_MULTIPLIER, STREAM_COOLDOWN, BACKOFF_BASE;
53
73
  var init_types = __esm({
@@ -945,6 +965,20 @@ var init_ProviderRegistry = __esm({
945
965
  const threadEnv = state.thread.metadata?.env ?? state.thread.metadata?.tenvs ?? null;
946
966
  const agentBuilderId = state.env.AGENT_BUILDER.idFromName("singleton");
947
967
  const agentBuilder = state.env.AGENT_BUILDER.get(agentBuilderId);
968
+ let instanceEnv = null;
969
+ if (typeof agentBuilder.getInstanceEnvEntries === "function") {
970
+ try {
971
+ const entries = await agentBuilder.getInstanceEnvEntries();
972
+ if (Array.isArray(entries) && entries.length > 0) {
973
+ instanceEnv = {};
974
+ for (const entry of entries) {
975
+ if (entry?.name) instanceEnv[entry.name] = entry.value;
976
+ }
977
+ }
978
+ } catch (error) {
979
+ console.warn("buildThreadScopedEnv: instance env lookup failed:", error);
980
+ }
981
+ }
948
982
  let userId = state.thread.metadata?.user_id ?? null;
949
983
  let parent = state.thread.metadata?.parent ?? null;
950
984
  let hops = 0;
@@ -981,11 +1015,12 @@ var init_ProviderRegistry = __esm({
981
1015
  console.warn("buildThreadScopedEnv:", diagnostic);
982
1016
  }
983
1017
  }
984
- if (!userEnv && !threadEnv) {
1018
+ if (!instanceEnv && !userEnv && !threadEnv) {
985
1019
  this.scopeDiagnostics.set(state.env, diagnostic);
986
1020
  return state.env;
987
1021
  }
988
- const merged = { ...state.env, ...userEnv ?? {}, ...threadEnv ?? {} };
1022
+ if (instanceEnv) diagnostic = `${diagnostic ? diagnostic + "; " : ""}instance env: ${Object.keys(instanceEnv).length} entries`;
1023
+ const merged = { ...state.env, ...instanceEnv ?? {}, ...userEnv ?? {}, ...threadEnv ?? {} };
989
1024
  this.scopeDiagnostics.set(merged, diagnostic);
990
1025
  return merged;
991
1026
  }
@@ -999,7 +1034,7 @@ var init_ProviderRegistry = __esm({
999
1034
  * @param thread - Thread instance for loading model from virtual modules
1000
1035
  * @returns Provider and model definition
1001
1036
  */
1002
- async getProvider(modelName, env2, thread) {
1037
+ async getProvider(modelName, env2, thread, getState) {
1003
1038
  if (!thread) {
1004
1039
  throw new Error("Thread instance required for model lookup");
1005
1040
  }
@@ -1032,6 +1067,18 @@ var init_ProviderRegistry = __esm({
1032
1067
  );
1033
1068
  }
1034
1069
  const config = resolved.providerConfig;
1070
+ if (typeof providerDefinition.createFetch === "function") {
1071
+ const state = getState ? await getState() : void 0;
1072
+ const configWithFetch = {
1073
+ ...config,
1074
+ fetch: providerDefinition.createFetch({
1075
+ providerName,
1076
+ config,
1077
+ state
1078
+ })
1079
+ };
1080
+ return this.buildResult(providerFactory(configWithFetch), modelDef, false);
1081
+ }
1035
1082
  const fingerprint = this.getProviderFingerprint(providerName, modelDef, config);
1036
1083
  const cached = this.providerCache.get(modelName);
1037
1084
  if (cached?.fingerprint === fingerprint) {
@@ -1904,7 +1951,16 @@ var init_LLMRequest = __esm({
1904
1951
  static async callModel(modelId, context, state, logId) {
1905
1952
  const { ProviderRegistry: ProviderRegistry2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
1906
1953
  const providerEnv = await ProviderRegistry2.buildThreadScopedEnv(state);
1907
- const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(modelId, providerEnv, state.thread.instance);
1954
+ const getThreadState = async () => {
1955
+ const { ThreadStateImpl: ThreadStateImpl2 } = await Promise.resolve().then(() => (init_ThreadStateImpl(), ThreadStateImpl_exports));
1956
+ return ThreadStateImpl2.fromFlowState(state);
1957
+ };
1958
+ const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(
1959
+ modelId,
1960
+ providerEnv,
1961
+ state.thread.instance,
1962
+ getThreadState
1963
+ );
1908
1964
  const streamAbort = new AbortController();
1909
1965
  const parentSignal = state.abortController?.signal;
1910
1966
  let onParentAbort;
@@ -6365,12 +6421,14 @@ var init_FlowEngine = __esm({
6365
6421
  } catch (error) {
6366
6422
  console.error("Fatal error in executeAsync:", error);
6367
6423
  await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
6424
+ await this.settleTerminalFailure(state, error);
6368
6425
  throw error;
6369
6426
  }
6370
6427
  } else {
6371
6428
  this.executeAsync(state, state.threadId).catch(async (error) => {
6372
6429
  console.error("Fatal error in executeAsync:", error);
6373
6430
  await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
6431
+ await this.settleTerminalFailure(state, error);
6374
6432
  });
6375
6433
  }
6376
6434
  return {
@@ -6424,10 +6482,27 @@ var init_FlowEngine = __esm({
6424
6482
  console.warn(
6425
6483
  `[FlowEngine] Finalized ${rows.length} orphaned incomplete log(s) for thread ${state.threadId}`
6426
6484
  );
6485
+ await state.thread.instance.failPendingMessagesAndBroadcast?.();
6427
6486
  } catch (err) {
6428
6487
  console.error("[FlowEngine] Failed to finalize orphaned logs (non-fatal):", err);
6429
6488
  }
6430
6489
  }
6490
+ /**
6491
+ * A terminal execution failure ended the run without a normal assistant
6492
+ * reply. Persist a durable failed assistant message + settle pending
6493
+ * messages so every connected head lands on a visible end state instead of
6494
+ * a stuck "Working" spinner. Best-effort — never masks the original error.
6495
+ */
6496
+ static async settleTerminalFailure(state, error) {
6497
+ try {
6498
+ const detail = error instanceof Error ? error.message : String(error);
6499
+ await state.thread.instance.settleTerminalFailure?.(
6500
+ `The agent run failed unexpectedly: ${detail}`
6501
+ );
6502
+ } catch (settleError) {
6503
+ console.error("[FlowEngine] Failed to settle terminal failure (non-fatal):", settleError);
6504
+ }
6505
+ }
6431
6506
  static async executeAsync(state, threadId) {
6432
6507
  const effectiveMaxSteps = Math.max(
6433
6508
  MAX_STEPS,
@@ -12229,6 +12304,10 @@ var init_code_execution = __esm({
12229
12304
  });
12230
12305
 
12231
12306
  // src/agents/ThreadStateImpl.ts
12307
+ var ThreadStateImpl_exports = {};
12308
+ __export(ThreadStateImpl_exports, {
12309
+ ThreadStateImpl: () => ThreadStateImpl
12310
+ });
12232
12311
  function hasLocalCodeExecutionRuntime(threadInstance) {
12233
12312
  const candidate = threadInstance;
12234
12313
  return Boolean(candidate.env && candidate.ctx);
@@ -12409,6 +12488,7 @@ function runCodeViaThreadRpc(threadInstance, threadId, source, options) {
12409
12488
  var ThreadStateImpl, ExecutionStateImpl;
12410
12489
  var init_ThreadStateImpl = __esm({
12411
12490
  "src/agents/ThreadStateImpl.ts"() {
12491
+ init_client_forwarding();
12412
12492
  init_types();
12413
12493
  init_utilities();
12414
12494
  init_code_execution();
@@ -12993,9 +13073,10 @@ var init_ThreadStateImpl = __esm({
12993
13073
  }
12994
13074
  const instance = this._threadInstance;
12995
13075
  const toolCallId = this._flowState?.currentToolCall?.id;
12996
- const mustPark = request.requestPermission != null && await this.forwardedCallNeedsApproval(request);
12997
- if (mustPark && toolCallId && this._executionState && typeof instance.dispatchClientTool === "function" && typeof instance.hasBridge === "function" && await instance.hasBridge()) {
12998
- const side = this._flowState?.currentSide === "b" ? "b" : "a";
13076
+ const canPark = !!(toolCallId && this._executionState && typeof instance.dispatchClientTool === "function");
13077
+ const side = this._flowState?.currentSide === "b" ? "b" : "a";
13078
+ const mustParkForApproval = request.requestPermission != null && await this.forwardedCallNeedsApproval(request);
13079
+ if (mustParkForApproval && canPark && toolCallId && typeof instance.hasBridge === "function" && await instance.hasBridge()) {
12999
13080
  await instance.dispatchClientTool(request, toolCallId, side);
13000
13081
  this._executionState.stop();
13001
13082
  return {
@@ -13007,6 +13088,55 @@ var init_ThreadStateImpl = __esm({
13007
13088
  const local = await this._threadInstance.callClientTool(request);
13008
13089
  if (local.ok || !this.isNoClient(local)) return local;
13009
13090
  const env2 = this._threadInstance.env;
13091
+ if (env2?.AGENT_BUILDER_THREAD) {
13092
+ const seen = /* @__PURE__ */ new Set([this._metadata.id]);
13093
+ let parentId = await this.resolveParentThreadId(this._metadata.id);
13094
+ while (parentId && !seen.has(parentId)) {
13095
+ seen.add(parentId);
13096
+ try {
13097
+ const stub = env2.AGENT_BUILDER_THREAD.get(env2.AGENT_BUILDER_THREAD.idFromName(parentId));
13098
+ const res = await stub.callClientTool(request);
13099
+ if (res.ok || !this.isNoClient(res)) return res;
13100
+ } catch {
13101
+ }
13102
+ parentId = await this.resolveParentThreadId(parentId);
13103
+ }
13104
+ }
13105
+ if (canPark && toolCallId) {
13106
+ await instance.dispatchClientTool(request, toolCallId, side);
13107
+ if (typeof instance.notifyClientWaiting === "function") {
13108
+ try {
13109
+ await instance.notifyClientWaiting(toolCallId, this._metadata.id, this._metadata.user_id ?? null);
13110
+ } catch {
13111
+ }
13112
+ }
13113
+ this._executionState.stop();
13114
+ return {
13115
+ ok: true,
13116
+ result: `\u23F3 Waiting for your machine to reconnect to run ${request.tool}\u2026`,
13117
+ data: { deferred: true, parked: true, waitingForClient: true, toolCallId }
13118
+ };
13119
+ }
13120
+ return local;
13121
+ }
13122
+ /**
13123
+ * Open a bidirectional byte stream to the connected client (e.g. a raw TCP
13124
+ * tunnel for Worker-side TLS termination). Streams require a LIVE bridge on
13125
+ * this thread and never park — a stream is only meaningful while the client
13126
+ * is attached. Subagents must not call this directly; they proxy whole
13127
+ * requests up to the root thread instead (request-up / stream-down).
13128
+ */
13129
+ async callClientStream(request) {
13130
+ const instance = this._threadInstance;
13131
+ if (typeof instance.openClientStream !== "function") {
13132
+ return {
13133
+ ok: false,
13134
+ error: "This thread runtime cannot open client streams. Client streaming requires the DurableThread bridge."
13135
+ };
13136
+ }
13137
+ const local = await instance.openClientStream(request);
13138
+ if (local.ok || !this.isNoClient(local)) return local;
13139
+ const env2 = this._threadInstance.env;
13010
13140
  if (!env2?.AGENT_BUILDER_THREAD) return local;
13011
13141
  const seen = /* @__PURE__ */ new Set([this._metadata.id]);
13012
13142
  let parentId = await this.resolveParentThreadId(this._metadata.id);
@@ -13014,14 +13144,86 @@ var init_ThreadStateImpl = __esm({
13014
13144
  seen.add(parentId);
13015
13145
  try {
13016
13146
  const stub = env2.AGENT_BUILDER_THREAD.get(env2.AGENT_BUILDER_THREAD.idFromName(parentId));
13017
- const res = await stub.callClientTool(request);
13018
- if (res.ok || !this.isNoClient(res)) return res;
13147
+ if (typeof stub.proxyStreamOpen === "function") {
13148
+ const opened = await stub.proxyStreamOpen(request);
13149
+ if (opened.ok && opened.id) {
13150
+ return { ok: true, duplex: this.buildProxyDuplex(stub, opened.id) };
13151
+ }
13152
+ if (opened.error && !opened.noClient) {
13153
+ return { ok: false, error: opened.error };
13154
+ }
13155
+ }
13019
13156
  } catch {
13020
13157
  }
13021
13158
  parentId = await this.resolveParentThreadId(parentId);
13022
13159
  }
13023
13160
  return local;
13024
13161
  }
13162
+ /**
13163
+ * Build a client duplex that bridges to a stream opened on an ancestor DO
13164
+ * (the bridge holder) via chunk-pull RPCs. `readable` polls `proxyStreamPull`;
13165
+ * `writable` forwards each chunk with `proxyStreamWrite`; teardown calls
13166
+ * `proxyStreamAbort`.
13167
+ */
13168
+ buildProxyDuplex(stub, id) {
13169
+ let settleDone;
13170
+ const done = new Promise((r) => settleDone = r);
13171
+ let closed = false;
13172
+ const readable = new ReadableStream({
13173
+ async pull(controller) {
13174
+ while (!closed) {
13175
+ let res;
13176
+ try {
13177
+ res = await stub.proxyStreamPull(id);
13178
+ } catch (error) {
13179
+ controller.error(error instanceof Error ? error : new Error(String(error)));
13180
+ settleDone({ ok: false, error: "proxy stream pull failed" });
13181
+ return;
13182
+ }
13183
+ for (const chunk of res.chunks) {
13184
+ controller.enqueue(decodeStreamChunk(chunk));
13185
+ }
13186
+ if (res.done) {
13187
+ controller.close();
13188
+ settleDone(res.error ? { ok: false, error: res.error } : { ok: true });
13189
+ return;
13190
+ }
13191
+ if (res.chunks.length > 0) return;
13192
+ await new Promise((r) => setTimeout(r, 25));
13193
+ }
13194
+ },
13195
+ cancel() {
13196
+ closed = true;
13197
+ void stub.proxyStreamAbort(id).catch(() => {
13198
+ });
13199
+ }
13200
+ });
13201
+ const writable = new WritableStream({
13202
+ async write(chunk) {
13203
+ await stub.proxyStreamWrite(id, encodeStreamChunk(chunk));
13204
+ },
13205
+ close() {
13206
+ void stub.proxyStreamEnd?.(id).catch?.(() => {
13207
+ });
13208
+ },
13209
+ abort() {
13210
+ closed = true;
13211
+ void stub.proxyStreamAbort(id).catch(() => {
13212
+ });
13213
+ }
13214
+ });
13215
+ return {
13216
+ readable,
13217
+ writable,
13218
+ close: (error) => {
13219
+ closed = true;
13220
+ void stub.proxyStreamAbort(id).catch(() => {
13221
+ });
13222
+ settleDone(error ? { ok: false, error } : { ok: true });
13223
+ },
13224
+ done
13225
+ };
13226
+ }
13025
13227
  isNoClient(res) {
13026
13228
  return res.ok === false && !!res.data && typeof res.data === "object" && res.data.noClient === true;
13027
13229
  }
@@ -43238,11 +43440,13 @@ var init_dist6 = __esm({
43238
43440
  if (!this.client) {
43239
43441
  const { default: OpenAI } = await import('openai');
43240
43442
  const defaultHeaders = this.config.defaultHeaders;
43443
+ const fetchOverride = this.config.fetch;
43241
43444
  this.client = new OpenAI({
43242
43445
  apiKey: this.config.apiKey,
43243
43446
  baseURL: this.config.baseUrl,
43244
43447
  timeout: this.config.timeout,
43245
- ...defaultHeaders ? { defaultHeaders } : {}
43448
+ ...defaultHeaders ? { defaultHeaders } : {},
43449
+ ...fetchOverride ? { fetch: fetchOverride } : {}
43246
43450
  });
43247
43451
  }
43248
43452
  return this.client;
@@ -43667,26 +43871,26 @@ var init_dist6 = __esm({
43667
43871
  const status = anyError.status || anyError.statusCode;
43668
43872
  const retryAfter = anyError.headers?.["retry-after"] ? parseInt(anyError.headers["retry-after"], 10) : void 0;
43669
43873
  if (status === 429) {
43670
- return new ProviderError(error.message, "rate_limit", status, retryAfter);
43874
+ return new ProviderError(error.message, "rate_limit", status, retryAfter, { cause: error });
43671
43875
  }
43672
43876
  if (status === 403 && _OpenAIProvider.isEdgeBlock(anyError)) {
43673
- return new ProviderError(error.message, "server_error", status, retryAfter ?? 3);
43877
+ return new ProviderError(error.message, "server_error", status, retryAfter ?? 3, { cause: error });
43674
43878
  }
43675
43879
  if (status === 401 || status === 403) {
43676
- return new ProviderError(error.message, "auth_error", status);
43880
+ return new ProviderError(error.message, "auth_error", status, void 0, { cause: error });
43677
43881
  }
43678
43882
  if (status === 400) {
43679
- return new ProviderError(error.message, "invalid_request", status);
43883
+ return new ProviderError(error.message, "invalid_request", status, void 0, { cause: error });
43680
43884
  }
43681
43885
  if (status >= 500) {
43682
- return new ProviderError(error.message, "server_error", status);
43886
+ return new ProviderError(error.message, "server_error", status, void 0, { cause: error });
43683
43887
  }
43684
43888
  if (error.name === "AbortError" || anyError.code === "ETIMEDOUT") {
43685
- return new ProviderError(error.message, "timeout");
43889
+ return new ProviderError(error.message, "timeout", void 0, void 0, { cause: error });
43686
43890
  }
43687
- return new ProviderError(error.message, "unknown", status);
43891
+ return new ProviderError(error.message, "unknown", status, void 0, { cause: error });
43688
43892
  }
43689
- return new ProviderError(String(error), "unknown");
43893
+ return new ProviderError(String(error), "unknown", void 0, void 0, { cause: error });
43690
43894
  }
43691
43895
  // ============================================================================
43692
43896
  // Inspection
@@ -66300,7 +66504,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
66300
66504
  });
66301
66505
 
66302
66506
  // src/version.ts
66303
- var BUILDER_VERSION = "0.27.13".length > 0 ? "0.27.13" : "dev";
66507
+ var BUILDER_VERSION = "0.28.1".length > 0 ? "0.28.1" : "dev";
66304
66508
 
66305
66509
  // src/api/diagnostics.get.ts
66306
66510
  init_LLMRequest();
@@ -72209,6 +72413,44 @@ var env_patch_default2 = defineController2(async ({ req, env: env2 }) => {
72209
72413
  }
72210
72414
  });
72211
72415
 
72416
+ // src/api/users/me/hub.ts
72417
+ var hub_default = defineController2(async ({ req, env: env2 }) => {
72418
+ const authResult = await requireAuth(req, env2);
72419
+ if (authResult instanceof Response) {
72420
+ return authResult;
72421
+ }
72422
+ if (authResult.authType === "super_admin") {
72423
+ return Response.json(
72424
+ { error: "Super admin sessions have no per-user hub. Sign in as a user account." },
72425
+ { status: 400 }
72426
+ );
72427
+ }
72428
+ if (req.headers.get("upgrade")?.toLowerCase() !== "websocket") {
72429
+ return Response.json(
72430
+ { error: "This endpoint only supports WebSocket connections" },
72431
+ { status: 400 }
72432
+ );
72433
+ }
72434
+ const namespace = env2.AGENT_BUILDER_USER_HUB;
72435
+ if (!namespace) {
72436
+ return Response.json(
72437
+ { error: "The per-user hub is not available on this runtime" },
72438
+ { status: 500 }
72439
+ );
72440
+ }
72441
+ try {
72442
+ const hubId = namespace.idFromName(authResult.user.id);
72443
+ const stub = namespace.get(hubId);
72444
+ return await stub.fetch(req);
72445
+ } catch (error) {
72446
+ console.error("Error establishing user hub socket:", error);
72447
+ return Response.json(
72448
+ { error: error.message || "Failed to establish hub connection" },
72449
+ { status: 500 }
72450
+ );
72451
+ }
72452
+ });
72453
+
72212
72454
  // src/api/users/me/kv/index.delete.ts
72213
72455
  var index_delete_default = defineController2(async ({ req, env: env2 }) => {
72214
72456
  try {
@@ -73567,6 +73809,7 @@ var routeHandlers = {
73567
73809
  "GET:/auth/sa/start": start_get_default,
73568
73810
  "GET:/users/me/env": env_get_default2,
73569
73811
  "PATCH:/users/me/env": env_patch_default2,
73812
+ "GET:/users/me/hub": hub_default,
73570
73813
  "DELETE:/users/me/kv": index_delete_default,
73571
73814
  "GET:/users/me/kv": index_get_default4,
73572
73815
  "POST:/users/me/kv": index_post_default8,