@standardagents/builder 0.40.5 → 0.40.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.
Files changed (34) hide show
  1. package/dist/built-in-routes.js +46 -76
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-DoYvZZ7e.js → ApiKeysView-BxzKL9jO.js} +1 -1
  4. package/dist/client/{CenteredContentView-53PY0cqY.js → CenteredContentView-CV4-cE9D.js} +1 -1
  5. package/dist/client/{CompositionView-fp-0YnPt.js → CompositionView-DUqt-Wt6.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-Cvcagja2.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-Byhp8Dqp.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-jYvO2v1K.js → CopyButton.vue_vue_type_script_setup_true_lang-CRIQcs5M.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-DQ3Ufa7y.js → DataTable.vue_vue_type_script_setup_true_lang-CF8Rkus3.js} +1 -1
  9. package/dist/client/{JsonViewer-6aaGzutE.js → JsonViewer-DTVICtSp.js} +1 -1
  10. package/dist/client/{LoginView-DwpCmNw9.js → LoginView-ZH47dgDr.js} +1 -1
  11. package/dist/client/{MarketplaceView-Da02L0J5.js → MarketplaceView-BZjX33dc.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-Bi8OjiBf.js → Modal.vue_vue_type_script_setup_true_lang-BWSRc4ps.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-cYZLOYY7.js → ModelModal.vue_vue_type_script_setup_true_lang-BHxhT6vM.js} +1 -1
  14. package/dist/client/{ModelsView-BLlHO9Ud.js → ModelsView-2apBcrqz.js} +1 -1
  15. package/dist/client/{PromptEditView-EeNwTDtT.js → PromptEditView-Cx086YBJ.js} +1 -1
  16. package/dist/client/{PromptModal-BkwrKJ2d.js → PromptModal-BMsTD8dU.js} +1 -1
  17. package/dist/client/{PromptsView-D1r7tsLu.js → PromptsView-l_FfpOuf.js} +1 -1
  18. package/dist/client/{ProvidersView-CmUDl5nM.js → ProvidersView-PEV9VV4X.js} +1 -1
  19. package/dist/client/{ThreadInspectorPane.vue_vue_type_script_setup_true_lang-cRs3tApQ.js → ThreadInspectorPane.vue_vue_type_script_setup_true_lang-Cu2SwqZ1.js} +1 -1
  20. package/dist/client/{ThreadsView-Bl5fSIO1.js → ThreadsView-CPStu4IQ.js} +1 -1
  21. package/dist/client/{ToolsView-DlVEF8-7.js → ToolsView-spgLDP4C.js} +1 -1
  22. package/dist/client/{UsersView-CGlA7a7R.js → UsersView-BznU6bH2.js} +1 -1
  23. package/dist/client/{VariablesView-BpYmJL4V.js → VariablesView-No5zHXK8.js} +1 -1
  24. package/dist/client/{index-CKtIkI0N.js → index-9HZDcx7i.js} +6 -6
  25. package/dist/client/index.html +1 -1
  26. package/dist/{index-0NI3m5DR.d.ts → index-DYhbT5Eq.d.ts} +44 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +256 -154
  29. package/dist/index.js.map +1 -1
  30. package/dist/runtime.d.ts +17 -17
  31. package/dist/runtime.js +256 -154
  32. package/dist/runtime.js.map +1 -1
  33. package/dist/test.d.ts +1 -1
  34. package/package.json +5 -5
@@ -26,7 +26,7 @@
26
26
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
27
  <link rel="icon" type="image/svg+xml" href="/agents/assets/img/favicon-9uH1rb2F.svg">
28
28
  <title>AgentBuilder Admin</title>
29
- <script type="module" crossorigin src="/agents/index-CKtIkI0N.js"></script>
29
+ <script type="module" crossorigin src="/agents/index-9HZDcx7i.js"></script>
30
30
  <link rel="modulepreload" crossorigin href="/agents/formkit-DJxUzqRQ.js">
31
31
  <link rel="modulepreload" crossorigin href="/agents/vendor-c_uwtQ-S.js">
32
32
  <link rel="modulepreload" crossorigin href="/agents/syntax-D68HCEYm.js">
@@ -164,6 +164,41 @@ interface ClientDuplex {
164
164
  error?: string;
165
165
  }>;
166
166
  }
167
+ /** Builder-internal destination for an event-driven cross-thread stream. */
168
+ interface ProxyStreamTarget {
169
+ /** Thread Durable Object that owns the receiver. */
170
+ threadId: string;
171
+ /** In-memory receiver registered before the ancestor opens the client stream. */
172
+ receiverId: string;
173
+ }
174
+ /** Builder-internal stream event delivered from an ancestor to a child thread. */
175
+ type ProxyStreamFrame = {
176
+ type: "data";
177
+ data: string;
178
+ } | {
179
+ type: "end";
180
+ } | {
181
+ type: "abort";
182
+ error?: string;
183
+ };
184
+ /** Local handle returned when a child thread registers a proxy receiver. */
185
+ interface ProxyStreamReceiver {
186
+ id: string;
187
+ readable: ReadableStream<Uint8Array>;
188
+ done: Promise<{
189
+ ok: boolean;
190
+ error?: string;
191
+ }>;
192
+ }
193
+ /** Result of asking an ancestor to open an event-driven proxy stream. */
194
+ interface ProxyStreamOpenResult {
195
+ ok: boolean;
196
+ id?: string;
197
+ error?: string;
198
+ noClient?: boolean;
199
+ eventDriven?: boolean;
200
+ code?: "unsupported" | "unavailable" | "refused" | "timeout";
201
+ }
167
202
  type ClientStreamResponse = {
168
203
  ok: true;
169
204
  duplex: ClientDuplex;
@@ -406,6 +441,8 @@ interface ThreadInstance {
406
441
  side: "a" | "b";
407
442
  }): Promise<ClientToolResponse>;
408
443
  openClientStream?(request: ClientStreamRequest): Promise<ClientStreamResponse>;
444
+ createProxyStreamReceiver?(onCancel: (error?: string) => void): ProxyStreamReceiver;
445
+ proxyStreamReceive?(receiverId: string, frame: ProxyStreamFrame): Promise<boolean>;
409
446
  dispatchClientTool?(request: ClientToolRequest, toolCallId: string, side: "a" | "b"): Promise<{
410
447
  dispatched: boolean;
411
448
  }>;
@@ -1363,6 +1400,12 @@ interface DurableThreadRpc {
1363
1400
  releaseForensicHold?: boolean;
1364
1401
  }): Promise<unknown>;
1365
1402
  getRuntimeSafetyStatus?(): Promise<unknown>;
1403
+ proxyStreamReceiverExists(receiverId: string): Promise<boolean>;
1404
+ proxyStreamReceive(receiverId: string, frame: ProxyStreamFrame): Promise<boolean>;
1405
+ proxyStreamOpen(request: ClientStreamRequest, target: ProxyStreamTarget): Promise<ProxyStreamOpenResult>;
1406
+ proxyStreamWrite(id: string, data: string): Promise<void>;
1407
+ proxyStreamEnd(id: string): Promise<void>;
1408
+ proxyStreamAbort(id: string): Promise<void>;
1366
1409
  /** Newest non-empty user message on the thread, for thread-list previews. */
1367
1410
  getLatestUserMessagePreview(): Promise<string | null>;
1368
1411
  executeBlocking?(threadId: string, agentId: string, initial_messages?: unknown[], data?: unknown): Promise<{
@@ -1934,4 +1977,4 @@ declare function defineController<Env extends ThreadEnv = ThreadEnv>(controller:
1934
1977
  */
1935
1978
  declare function createThreadEndpointHandler<Env extends ThreadEnv = ThreadEnv>(handler: ThreadEndpointHandler): BuilderController<Env>;
1936
1979
 
1937
- export { type AuthUser as A, type BuilderController as B, CLIENT_STREAM_CHUNK_BYTES as C, type FlowResult as D, type TelemetryEvent as E, type FlowState as F, type Env as G, type FileRecord as H, type ImageMetadata as I, type AttachmentRef as J, type GrepResult as K, type LLMResponse as L, type Message as M, type FileStats as N, type TextContentPart as O, type ImageContentPart as P, type MultimodalContent as Q, type RequestContext as R, type StorageBackend as S, type ToolCall as T, type MessageContent as U, type SkillStorageScope as V, type BuilderControllerContext as a, type ThreadEndpointContext as b, createThreadEndpointHandler as c, defineController as d, type ThreadEnv as e, type BuilderThreadEndpointHandler as f, authenticate as g, requireAdmin as h, endpointCallerFromAuth as i, type AuthContext as j, callClient as k, callClientStream as l, encodeStreamChunk as m, decodeStreamChunk as n, type ClientToolRequest as o, type ClientToolResponse as p, type ClientStreamRequest as q, requireAuth as r, supportsClientForwarding as s, type ClientStreamResponse as t, type ClientDuplex as u, type ClientForwarding as v, type Agent as w, type ThreadInstance as x, type ThreadMetadata as y, type ToolResult as z };
1980
+ export { type AuthUser as A, type BuilderController as B, CLIENT_STREAM_CHUNK_BYTES as C, type FlowResult as D, type TelemetryEvent as E, type FlowState as F, type Env as G, type FileRecord as H, type ImageMetadata as I, type AttachmentRef as J, type GrepResult as K, type LLMResponse as L, type Message as M, type FileStats as N, type TextContentPart as O, type ImageContentPart as P, type MultimodalContent as Q, type RequestContext as R, type StorageBackend as S, type ToolCall as T, type MessageContent as U, type ProxyStreamReceiver as V, type ProxyStreamFrame as W, type ProxyStreamTarget as X, type ProxyStreamOpenResult as Y, type SkillStorageScope as Z, type BuilderControllerContext as a, type ThreadEndpointContext as b, createThreadEndpointHandler as c, defineController as d, type ThreadEnv as e, type BuilderThreadEndpointHandler as f, authenticate as g, requireAdmin as h, endpointCallerFromAuth as i, type AuthContext as j, callClient as k, callClientStream as l, encodeStreamChunk as m, decodeStreamChunk as n, type ClientToolRequest as o, type ClientToolResponse as p, type ClientStreamRequest as q, requireAuth as r, supportsClientForwarding as s, type ClientStreamResponse as t, type ClientDuplex as u, type ClientForwarding as v, type Agent as w, type ThreadInstance as x, type ThreadMetadata as y, type ToolResult as z };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ModelDefinition, AgentDefinition } from './runtime.js';
2
2
  export { AgentBuilderEnv, AgentBuilderEvent, BroadcastOptions, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, FlowStateWithSdk, HubClientInfo, ImageContextConfig, InjectMessageOptions, LLMProviderInterface, NamespaceResolutionError, PromptContent, PromptDefinition, PromptIncludePart, PromptPart, PromptToolConfig, Provider, ProviderContentPart, ProviderFilePart, ProviderImagePart, ProviderRegistry, ProviderTextPart, SessionToolBinding, SessionToolConfig, SideConfig, SubagentToolConfig, SubpromptConfig, ThreadEventPayload, ThreadRegistryEntry, ThreadWatchdog, ThreadWatchdogState, ToolConfig, UpdateThreadParams, User, WatchdogCheckin, WatchdogProbeResult, applyRuntimeCors, buildImageDescription, canAccessThread, cat, createNamespaceContext, emitThreadEvent, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isSameOriginBrowserRequest, isTrustedLocalDevelopmentRequest, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, runtimeCorsHeaders, runtimePreflightResponse, stat, tail, unlink, updateThread, writeFile, writeImage } from './runtime.js';
3
3
  export { AgentPluginOptions, agentbuilder, builder } from './plugin.js';
4
- export { w as Agent, J as AttachmentRef, j as AuthContext, A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, u as ClientDuplex, v as ClientForwarding, q as ClientStreamRequest, t as ClientStreamResponse, o as ClientToolRequest, p as ClientToolResponse, B as Controller, a as ControllerContext, G as Env, H as FileRecord, N as FileStats, D as FlowResult, F as FlowState, K as GrepResult, P as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, U as MessageContent, Q as MultimodalContent, R as RequestContext, S as StorageBackend, E as TelemetryEvent, O as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, x as ThreadInstance, y as ThreadMetadata, T as ToolCall, z as ToolResult, g as authenticate, k as callClient, l as callClientStream, c as createThreadEndpointHandler, n as decodeStreamChunk, d as defineController, m as encodeStreamChunk, i as endpointCallerFromAuth, h as requireAdmin, r as requireAuth, s as supportsClientForwarding } from './index-0NI3m5DR.js';
4
+ export { w as Agent, J as AttachmentRef, j as AuthContext, A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, u as ClientDuplex, v as ClientForwarding, q as ClientStreamRequest, t as ClientStreamResponse, o as ClientToolRequest, p as ClientToolResponse, B as Controller, a as ControllerContext, G as Env, H as FileRecord, N as FileStats, D as FlowResult, F as FlowState, K as GrepResult, P as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, U as MessageContent, Q as MultimodalContent, R as RequestContext, S as StorageBackend, E as TelemetryEvent, O as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, x as ThreadInstance, y as ThreadMetadata, T as ToolCall, z as ToolResult, g as authenticate, k as callClient, l as callClientStream, c as createThreadEndpointHandler, n as decodeStreamChunk, d as defineController, m as encodeStreamChunk, i as endpointCallerFromAuth, h as requireAdmin, r as requireAuth, s as supportsClientForwarding } from './index-DYhbT5Eq.js';
5
5
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookExecutionContext, HookExecutionSource, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, ParentHookRequirement, ParentHookStage, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderConfigSlot, ProviderConfigSlots, ProviderConfigValueSource, ProviderDefinition, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ProviderMethodOverrides, ModelCapabilities as ProviderModelCapabilities, ProviderOverride, ProviderOverrideContext, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, ProviderSyncOverride, ProviderSystemMessage, ProviderTool, ProviderToolCallPart, ProviderToolMessage, ProviderToolResultContent, ProviderUsage, ProviderUserMessage, ReasoningConfig, StructuredPrompt, TextContent, ThreadHookName, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineProvider, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel, providerConst, providerEnv, providerValue } from '@standardagents/spec';
6
6
  export { C as ConstituentItem, D as DiscoveredPackage, N as NamespacedRegistry, P as PackingAnalysis, a as PackingOptions, b as PackingResult, c as UnpackOptions, d as UnpackResult } from './types-BMnBYNwn.js';
7
7
  export { D as DiscoveryConfig, P as PackageDiscoveryService, d as discoverPackages } from './discovery-DZ7fv-b4.js';
package/dist/index.js CHANGED
@@ -14722,27 +14722,51 @@ var init_ThreadStateImpl = __esm({
14722
14722
  const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
14723
14723
  const local = await instance.openClientStream(withWake(this._metadata.id));
14724
14724
  if (local.ok || !this.isNoClient(local)) return local;
14725
- const env = this._threadInstance.env;
14726
- if (!env?.AGENT_BUILDER_THREAD) return local;
14725
+ if (typeof instance.createProxyStreamReceiver !== "function" || typeof instance.proxyStreamReceive !== "function") {
14726
+ return local;
14727
+ }
14728
+ let abortRemote = null;
14729
+ const receiver = instance.createProxyStreamReceiver((error) => abortRemote?.(error));
14730
+ const discardReceiver = async (error) => {
14731
+ await instance.proxyStreamReceive(receiver.id, { type: "abort", error });
14732
+ };
14733
+ const namespace = instance.env.AGENT_BUILDER_THREAD;
14727
14734
  const seen = /* @__PURE__ */ new Set([this._metadata.id]);
14728
14735
  let parentId = await this.resolveParentThreadId(this._metadata.id);
14729
14736
  while (parentId && !seen.has(parentId)) {
14730
14737
  seen.add(parentId);
14731
14738
  try {
14732
- const stub = env.AGENT_BUILDER_THREAD.get(env.AGENT_BUILDER_THREAD.idFromName(parentId));
14739
+ const stub = namespace.get(namespace.idFromName(parentId));
14733
14740
  if (typeof stub.proxyStreamOpen === "function") {
14734
- const opened = await stub.proxyStreamOpen(withWake(parentId));
14741
+ const opened = await stub.proxyStreamOpen(withWake(parentId), {
14742
+ threadId: this._metadata.id,
14743
+ receiverId: receiver.id
14744
+ });
14735
14745
  if (opened.ok && opened.id) {
14746
+ if (opened.eventDriven !== true) {
14747
+ await stub.proxyStreamAbort(opened.id).catch(() => {
14748
+ });
14749
+ await discardReceiver("The ancestor runtime does not support event-driven streams.");
14750
+ return {
14751
+ ok: false,
14752
+ error: "The ancestor runtime does not support event-driven streams.",
14753
+ data: { code: "unsupported" }
14754
+ };
14755
+ }
14756
+ abortRemote = (error) => {
14757
+ void stub.proxyStreamAbort(opened.id).catch(() => {
14758
+ });
14759
+ if (error) {
14760
+ void instance.proxyStreamReceive(receiver.id, { type: "abort", error });
14761
+ }
14762
+ };
14736
14763
  return {
14737
14764
  ok: true,
14738
- duplex: this.buildProxyDuplex(
14739
- stub,
14740
- opened.id,
14741
- opened.parkedPull === true
14742
- )
14765
+ duplex: this.buildEventProxyDuplex(stub, opened.id, receiver, instance)
14743
14766
  };
14744
14767
  }
14745
14768
  if (opened.error && !opened.noClient) {
14769
+ await discardReceiver(opened.error);
14746
14770
  return {
14747
14771
  ok: false,
14748
14772
  error: opened.error,
@@ -14754,69 +14778,21 @@ var init_ThreadStateImpl = __esm({
14754
14778
  }
14755
14779
  parentId = await this.resolveParentThreadId(parentId);
14756
14780
  }
14781
+ await discardReceiver(local.error ?? "No execution-owner bridge is available.");
14757
14782
  return local;
14758
14783
  }
14759
14784
  /**
14760
14785
  * Build a client duplex that bridges to a stream opened on an ancestor DO
14761
- * (the bridge holder) via chunk-pull RPCs. `readable` issues PARKED pulls —
14762
- * a current ancestor holds one `proxyStreamPull` until bytes, completion,
14763
- * error, or abort, so an idle open stream performs zero repeated pulls.
14764
- * Older ancestors use the bounded long-pull compatibility path. `writable`
14765
- * forwards each chunk with `proxyStreamWrite`; teardown calls
14766
- * `proxyStreamAbort`.
14786
+ * (the bridge holder). The readable is the receiver registered on this DO;
14787
+ * writes and terminal actions are the only child-to-ancestor RPCs.
14767
14788
  */
14768
- static PROXY_PULL_MAX_WAIT_MS = 2e4;
14769
- buildProxyDuplex(stub, id, supportsParkedPull = false) {
14770
- let settleDone;
14771
- const done = new Promise((r) => settleDone = r);
14789
+ buildEventProxyDuplex(stub, id, receiver, instance) {
14772
14790
  let closed = false;
14773
- let settled = false;
14774
- const settle = (result) => {
14775
- if (settled) return;
14776
- settled = true;
14777
- settleDone(result);
14791
+ const settleLocal = (error) => {
14792
+ if (typeof instance.proxyStreamReceive !== "function") return;
14793
+ const frame = error ? { type: "abort", error } : { type: "end" };
14794
+ void instance.proxyStreamReceive(receiver.id, frame);
14778
14795
  };
14779
- const readable = new ReadableStream({
14780
- async pull(controller) {
14781
- while (!closed) {
14782
- const pullStartedAt = Date.now();
14783
- let res;
14784
- try {
14785
- res = await stub.proxyStreamPull(
14786
- id,
14787
- supportsParkedPull ? void 0 : _ThreadStateImpl.PROXY_PULL_MAX_WAIT_MS
14788
- );
14789
- } catch (error) {
14790
- controller.error(error instanceof Error ? error : new Error(String(error)));
14791
- settle({ ok: false, error: "proxy stream pull failed" });
14792
- return;
14793
- }
14794
- for (const chunk of res.chunks) {
14795
- controller.enqueue(decodeStreamChunk(chunk));
14796
- }
14797
- if (res.done) {
14798
- controller.close();
14799
- settle(res.error ? { ok: false, error: res.error } : { ok: true });
14800
- return;
14801
- }
14802
- if (res.chunks.length > 0) return;
14803
- if (supportsParkedPull) {
14804
- controller.error(new Error("parked proxy pull returned without data"));
14805
- settle({ ok: false, error: "parked proxy pull returned without data" });
14806
- return;
14807
- }
14808
- if (Date.now() - pullStartedAt < 250) {
14809
- await new Promise((r) => setTimeout(r, 250));
14810
- }
14811
- }
14812
- },
14813
- cancel() {
14814
- closed = true;
14815
- void stub.proxyStreamAbort(id).catch(() => {
14816
- });
14817
- settle({ ok: false, error: "proxy stream reader canceled" });
14818
- }
14819
- });
14820
14796
  const writable = new WritableStream({
14821
14797
  async write(chunk) {
14822
14798
  await stub.proxyStreamWrite(id, encodeStreamChunk(chunk));
@@ -14829,20 +14805,20 @@ var init_ThreadStateImpl = __esm({
14829
14805
  closed = true;
14830
14806
  void stub.proxyStreamAbort(id).catch(() => {
14831
14807
  });
14832
- settle({ ok: false, error: "proxy stream writer aborted" });
14808
+ settleLocal("The proxy stream writer was aborted.");
14833
14809
  }
14834
14810
  });
14835
14811
  return {
14836
- readable,
14812
+ readable: receiver.readable,
14837
14813
  writable,
14838
14814
  close: (error) => {
14839
14815
  if (closed) return;
14840
14816
  closed = true;
14841
14817
  void stub.proxyStreamAbort(id).catch(() => {
14842
14818
  });
14843
- settle(error ? { ok: false, error } : { ok: true });
14819
+ settleLocal(error);
14844
14820
  },
14845
- done
14821
+ done: receiver.done
14846
14822
  };
14847
14823
  }
14848
14824
  isNoClient(res) {
@@ -31096,6 +31072,8 @@ var DurableThread = class _DurableThread extends DurableObject {
31096
31072
  // opened on and dies with it. Streams are in-memory only — they exist solely
31097
31073
  // while the client is attached and never park durably.
31098
31074
  clientStreams = /* @__PURE__ */ new Map();
31075
+ proxyStreamReceivers = /* @__PURE__ */ new Map();
31076
+ eventProxyStreams = /* @__PURE__ */ new Map();
31099
31077
  alarmQueue;
31100
31078
  runtimeSafety;
31101
31079
  alarmQueueRearmed = false;
@@ -31207,6 +31185,10 @@ var DurableThread = class _DurableThread extends DurableObject {
31207
31185
  clientName: typeof attachment.clientName === "string" ? attachment.clientName : null,
31208
31186
  clientKind: typeof attachment.clientKind === "string" ? attachment.clientKind : null
31209
31187
  });
31188
+ const interrupted = this.parseProxyStreamAttachments(attachment.proxyStreams);
31189
+ if (interrupted.length > 0) {
31190
+ this.recoverInterruptedProxyStreams(ws, interrupted);
31191
+ }
31210
31192
  }
31211
31193
  }
31212
31194
  }
@@ -35462,8 +35444,138 @@ ${resultContent}${attachmentSummary}`;
35462
35444
  });
35463
35445
  }
35464
35446
  }
35447
+ parseProxyStreamAttachments(value) {
35448
+ if (!Array.isArray(value)) return [];
35449
+ const parsed = [];
35450
+ for (const item of value) {
35451
+ if (!item || typeof item !== "object") continue;
35452
+ const candidate = item;
35453
+ if (typeof candidate.id === "string" && candidate.target && typeof candidate.target.threadId === "string" && typeof candidate.target.receiverId === "string") {
35454
+ parsed.push({
35455
+ id: candidate.id,
35456
+ target: {
35457
+ threadId: candidate.target.threadId,
35458
+ receiverId: candidate.target.receiverId
35459
+ }
35460
+ });
35461
+ }
35462
+ }
35463
+ return parsed;
35464
+ }
35465
+ /** Keep live proxy routes on the hibernating bridge socket for restart cleanup. */
35466
+ updateBridgeProxyAttachment(socket) {
35467
+ if (typeof socket.serializeAttachment !== "function") return;
35468
+ let attachment = { type: "bridge" };
35469
+ try {
35470
+ const current = socket.deserializeAttachment();
35471
+ if (current && typeof current === "object") {
35472
+ attachment = { ...current };
35473
+ }
35474
+ } catch {
35475
+ }
35476
+ const proxyStreams = Array.from(this.clientStreams.entries()).filter(([, state]) => state.socket === socket && state.proxyTarget !== null).map(([id, state]) => ({ id, target: state.proxyTarget }));
35477
+ try {
35478
+ socket.serializeAttachment({ ...attachment, proxyStreams });
35479
+ } catch {
35480
+ }
35481
+ }
35482
+ async deliverProxyStreamFrame(target, frame) {
35483
+ try {
35484
+ const namespace = this.env.AGENT_BUILDER_THREAD;
35485
+ const stub = namespace.get(namespace.idFromName(target.threadId));
35486
+ return await stub.proxyStreamReceive(target.receiverId, frame);
35487
+ } catch {
35488
+ return false;
35489
+ }
35490
+ }
35491
+ /** Abort live proxy routes restored without their in-memory stream state. */
35492
+ recoverInterruptedProxyStreams(socket, streams) {
35493
+ const error = "The stream ended because its runtime restarted.";
35494
+ for (const stream of streams) {
35495
+ try {
35496
+ if (socket.readyState === WebSocket.OPEN) {
35497
+ socket.send(JSON.stringify({ type: "stream_abort", id: stream.id, error }));
35498
+ }
35499
+ } catch {
35500
+ }
35501
+ this.ctx.waitUntil(
35502
+ this.deliverProxyStreamFrame(stream.target, { type: "abort", error }).then(() => {
35503
+ })
35504
+ );
35505
+ }
35506
+ this.updateBridgeProxyAttachment(socket);
35507
+ }
35508
+ /** Register the child side before any ancestor opens an owner stream. */
35509
+ createProxyStreamReceiver(onCancel) {
35510
+ const id = crypto.randomUUID();
35511
+ let controller = null;
35512
+ let settleDone;
35513
+ const done = new Promise((resolve3) => {
35514
+ settleDone = resolve3;
35515
+ });
35516
+ const readable = new ReadableStream({
35517
+ start(value) {
35518
+ controller = value;
35519
+ },
35520
+ cancel: (reason) => {
35521
+ const receiver2 = this.proxyStreamReceivers.get(id);
35522
+ if (!receiver2 || receiver2.settled) return;
35523
+ const error = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "The proxy stream reader was canceled.";
35524
+ receiver2.settle({ ok: false, error });
35525
+ receiver2.onCancel(error);
35526
+ }
35527
+ });
35528
+ const receiver = {
35529
+ controller,
35530
+ settle: (result) => {
35531
+ if (receiver.settled) return;
35532
+ receiver.settled = true;
35533
+ this.proxyStreamReceivers.delete(id);
35534
+ settleDone(result);
35535
+ },
35536
+ onCancel,
35537
+ settled: false
35538
+ };
35539
+ this.proxyStreamReceivers.set(id, receiver);
35540
+ return { id, readable, done };
35541
+ }
35542
+ async proxyStreamReceiverExists(receiverId) {
35543
+ return this.proxyStreamReceivers.has(receiverId);
35544
+ }
35545
+ async proxyStreamReceive(receiverId, frame) {
35546
+ const receiver = this.proxyStreamReceivers.get(receiverId);
35547
+ if (!receiver || receiver.settled) return false;
35548
+ if (frame.type === "data") {
35549
+ try {
35550
+ receiver.controller?.enqueue(decodeStreamChunk(frame.data));
35551
+ } catch (error2) {
35552
+ const message = error2 instanceof Error ? error2.message : String(error2);
35553
+ try {
35554
+ receiver.controller?.error(error2 instanceof Error ? error2 : new Error(message));
35555
+ } catch {
35556
+ }
35557
+ receiver.settle({ ok: false, error: message });
35558
+ }
35559
+ return true;
35560
+ }
35561
+ if (frame.type === "end") {
35562
+ try {
35563
+ receiver.controller?.close();
35564
+ } catch {
35565
+ }
35566
+ receiver.settle({ ok: true });
35567
+ return true;
35568
+ }
35569
+ const error = frame.error ?? "The proxied client stream was aborted.";
35570
+ try {
35571
+ receiver.controller?.error(new Error(error));
35572
+ } catch {
35573
+ }
35574
+ receiver.settle({ ok: false, error });
35575
+ return true;
35576
+ }
35465
35577
  /** Open a bidirectional byte stream to the connected owner client. */
35466
- async openClientStream(request) {
35578
+ async openClientStream(request, proxyTarget = null) {
35467
35579
  const owner = await this.getExecutionOwner();
35468
35580
  let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
35469
35581
  if (!socket && owner) {
@@ -35496,9 +35608,12 @@ ${resultContent}${attachmentSummary}`;
35496
35608
  opened: null,
35497
35609
  openTimer: null,
35498
35610
  settleDone,
35499
- settled: false
35611
+ settled: false,
35612
+ proxyTarget,
35613
+ delivery: Promise.resolve()
35500
35614
  };
35501
35615
  this.clientStreams.set(id, state);
35616
+ this.updateBridgeProxyAttachment(socket);
35502
35617
  this.bridgeLastSeen.set(socket, Date.now());
35503
35618
  const openResult = await new Promise((resolve3) => {
35504
35619
  state.opened = resolve3;
@@ -35534,6 +35649,7 @@ ${resultContent}${attachmentSummary}`;
35534
35649
  }
35535
35650
  if (!openResult.ok) {
35536
35651
  this.clientStreams.delete(id);
35652
+ this.updateBridgeProxyAttachment(socket);
35537
35653
  state.settled = true;
35538
35654
  settleDone({ ok: false, error: openResult.error });
35539
35655
  return {
@@ -35575,14 +35691,28 @@ ${resultContent}${attachmentSummary}`;
35575
35691
  };
35576
35692
  return { ok: true, duplex };
35577
35693
  }
35578
- // ─── Subagent stream proxy-up (chunk-pull, no cross-DO ReadableStream) ─────
35579
- // A subagent (child thread, no bridge) proxies its whole stream request UP to
35580
- // this bridge-holding root. We open the client stream locally, then buffer
35581
- // incoming bytes so the child can drain them over plain request/response RPCs
35582
- // — sidestepping the unproven DO-to-DO ReadableStream return.
35583
- proxyStreams = /* @__PURE__ */ new Map();
35584
- async proxyStreamOpen(request) {
35585
- const opened = await this.openClientStream(request);
35694
+ // ─── Event-driven subagent stream proxy ──────────────────────────────────
35695
+ // A child registers a local receiver before the bridge-holding ancestor
35696
+ // opens the client stream. The ancestor invokes that receiver only for real
35697
+ // data/end/abort events; there is no pull loop or permanently outstanding
35698
+ // cross-DO RPC while the stream is idle.
35699
+ async proxyStreamOpen(request, target) {
35700
+ const namespace = this.env.AGENT_BUILDER_THREAD;
35701
+ let receiverExists = false;
35702
+ try {
35703
+ const destination = namespace.get(namespace.idFromName(target.threadId));
35704
+ receiverExists = await destination.proxyStreamReceiverExists(target.receiverId);
35705
+ } catch {
35706
+ receiverExists = false;
35707
+ }
35708
+ if (!receiverExists) {
35709
+ return {
35710
+ ok: false,
35711
+ error: "The destination stream receiver is unavailable.",
35712
+ code: "unavailable"
35713
+ };
35714
+ }
35715
+ const opened = await this.openClientStream(request, target);
35586
35716
  if (!opened.ok) {
35587
35717
  const failureData = opened.data;
35588
35718
  const noClient = failureData?.noClient === true;
@@ -35594,82 +35724,23 @@ ${resultContent}${attachmentSummary}`;
35594
35724
  };
35595
35725
  }
35596
35726
  const id = crypto.randomUUID();
35597
- const duplex = opened.duplex;
35598
- const reader = duplex.readable.getReader();
35599
- const writer = duplex.writable.getWriter();
35600
- const entry = {
35601
- writer,
35602
- reader,
35603
- buffer: [],
35604
- done: false,
35605
- error: void 0,
35606
- pump: Promise.resolve(),
35607
- close: (error) => duplex.close(error),
35608
- waiter: null,
35609
- notify: () => {
35610
- const waiter = entry.waiter;
35611
- entry.waiter = null;
35612
- waiter?.();
35613
- }
35614
- };
35615
- entry.pump = (async () => {
35616
- try {
35617
- while (true) {
35618
- const { value, done } = await reader.read();
35619
- if (done) break;
35620
- if (value) {
35621
- entry.buffer.push(value);
35622
- entry.notify();
35623
- }
35624
- }
35625
- } catch (error) {
35626
- entry.error = error instanceof Error ? error.message : String(error);
35627
- } finally {
35628
- entry.done = true;
35629
- entry.notify();
35630
- }
35631
- })();
35632
- void duplex.done.then((d) => {
35633
- entry.done = true;
35634
- if (!d.ok && !entry.error) entry.error = d.error;
35635
- entry.notify();
35727
+ this.eventProxyStreams.set(id, {
35728
+ writer: opened.duplex.writable.getWriter(),
35729
+ close: (error) => opened.duplex.close(error),
35730
+ target
35636
35731
  });
35637
- this.proxyStreams.set(id, entry);
35638
- return { ok: true, id, parkedPull: true };
35732
+ return { ok: true, id, eventDriven: true };
35639
35733
  }
35640
35734
  async proxyStreamWrite(id, dataB64) {
35641
- const entry = this.proxyStreams.get(id);
35735
+ const entry = this.eventProxyStreams.get(id);
35642
35736
  if (!entry) return;
35643
35737
  try {
35644
35738
  await entry.writer.write(decodeStreamChunk(dataB64));
35645
35739
  } catch {
35646
35740
  }
35647
35741
  }
35648
- async proxyStreamPull(id, maxWaitMs) {
35649
- const entry = this.proxyStreams.get(id);
35650
- if (!entry) return { chunks: [], done: true, error: "unknown proxy stream" };
35651
- if (entry.buffer.length === 0 && !entry.done) {
35652
- await new Promise((resolve3) => {
35653
- const deadline = typeof maxWaitMs === "number" && maxWaitMs > 0 ? setTimeout(() => {
35654
- if (entry.waiter === wake) entry.waiter = null;
35655
- resolve3();
35656
- }, maxWaitMs) : null;
35657
- const wake = () => {
35658
- if (deadline) clearTimeout(deadline);
35659
- resolve3();
35660
- };
35661
- entry.waiter = wake;
35662
- });
35663
- }
35664
- const chunks = entry.buffer.splice(0).map((b) => encodeStreamChunk(b));
35665
- if (entry.done && entry.buffer.length === 0) {
35666
- this.proxyStreams.delete(id);
35667
- return { chunks, done: true, error: entry.error };
35668
- }
35669
- return { chunks, done: false };
35670
- }
35671
35742
  async proxyStreamEnd(id) {
35672
- const entry = this.proxyStreams.get(id);
35743
+ const entry = this.eventProxyStreams.get(id);
35673
35744
  if (!entry) return;
35674
35745
  try {
35675
35746
  await entry.writer.close();
@@ -35677,30 +35748,49 @@ ${resultContent}${attachmentSummary}`;
35677
35748
  }
35678
35749
  }
35679
35750
  async proxyStreamAbort(id) {
35680
- const entry = this.proxyStreams.get(id);
35751
+ const entry = this.eventProxyStreams.get(id);
35681
35752
  if (!entry) return;
35682
- this.proxyStreams.delete(id);
35683
- entry.done = true;
35684
- if (!entry.error) entry.error = "aborted";
35685
- entry.notify();
35753
+ this.eventProxyStreams.delete(id);
35686
35754
  try {
35687
35755
  entry.close("aborted");
35688
35756
  } catch {
35689
35757
  }
35690
35758
  }
35759
+ forgetEventProxyStream(clientStreamId, target) {
35760
+ const matchesTarget = (candidate) => candidate.threadId === target.threadId && candidate.receiverId === target.receiverId;
35761
+ for (const [id, entry] of Array.from(this.eventProxyStreams.entries())) {
35762
+ if (id !== clientStreamId && !matchesTarget(entry.target)) continue;
35763
+ this.eventProxyStreams.delete(id);
35764
+ try {
35765
+ entry.writer.releaseLock();
35766
+ } catch {
35767
+ }
35768
+ }
35769
+ }
35691
35770
  /** Settle and remove a client stream exactly once. */
35692
35771
  settleClientStream(id, result, openCode) {
35693
35772
  const state = this.clientStreams.get(id);
35694
35773
  if (!state) return;
35695
35774
  this.clientStreams.delete(id);
35775
+ this.updateBridgeProxyAttachment(state.socket);
35696
35776
  if (state.openTimer) clearTimeout(state.openTimer);
35697
35777
  if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed.", code: openCode });
35698
35778
  if (!state.settled) {
35699
35779
  state.settled = true;
35700
- try {
35701
- if (result.ok) state.controller?.close();
35702
- else state.controller?.error(new Error(result.error ?? "Client stream aborted."));
35703
- } catch {
35780
+ if (state.proxyTarget) {
35781
+ const target = state.proxyTarget;
35782
+ const frame = result.ok ? { type: "end" } : { type: "abort", error: result.error ?? "Client stream aborted." };
35783
+ state.delivery = state.delivery.then(async () => {
35784
+ await this.deliverProxyStreamFrame(target, frame);
35785
+ });
35786
+ this.ctx.waitUntil(state.delivery);
35787
+ this.forgetEventProxyStream(id, target);
35788
+ } else {
35789
+ try {
35790
+ if (result.ok) state.controller?.close();
35791
+ else state.controller?.error(new Error(result.error ?? "Client stream aborted."));
35792
+ } catch {
35793
+ }
35704
35794
  }
35705
35795
  state.settleDone(result);
35706
35796
  }
@@ -35743,6 +35833,18 @@ ${resultContent}${attachmentSummary}`;
35743
35833
  }
35744
35834
  case "stream_data": {
35745
35835
  if (typeof parsed.data !== "string") return;
35836
+ if (state.proxyTarget) {
35837
+ const target = state.proxyTarget;
35838
+ const frame = { type: "data", data: parsed.data };
35839
+ state.delivery = state.delivery.then(async () => {
35840
+ const delivered = await this.deliverProxyStreamFrame(target, frame);
35841
+ if (!delivered && this.clientStreams.get(id) === state) {
35842
+ this.abortClientStream(id, "The destination stream receiver is unavailable.");
35843
+ }
35844
+ });
35845
+ this.ctx.waitUntil(state.delivery);
35846
+ return;
35847
+ }
35746
35848
  try {
35747
35849
  state.controller?.enqueue(decodeStreamChunk(parsed.data));
35748
35850
  } catch {