@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
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { j as AuthContext, e as ThreadEnv, w as Agent, y as ThreadMetadata, z as ToolResult, M as Message, o as ClientToolRequest, p as ClientToolResponse, q as ClientStreamRequest, t as ClientStreamResponse, H as FileRecord, V as SkillStorageScope, F as FlowState, N as FileStats, K as GrepResult, J as AttachmentRef, U as MessageContent, G as Env, x as ThreadInstance } from './index-0NI3m5DR.js';
2
- export { A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, u as ClientDuplex, v as ClientForwarding, B as Controller, a as ControllerContext, D as FlowResult, P as ImageContentPart, I as ImageMetadata, L as LLMResponse, Q as MultimodalContent, R as RequestContext, S as StorageBackend, E as TelemetryEvent, O as TextContentPart, b as ThreadEndpointContext, T as ToolCall, 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';
1
+ import { j as AuthContext, e as ThreadEnv, w as Agent, y as ThreadMetadata, z as ToolResult, M as Message, o as ClientToolRequest, p as ClientToolResponse, V as ProxyStreamReceiver, W as ProxyStreamFrame, q as ClientStreamRequest, X as ProxyStreamTarget, t as ClientStreamResponse, Y as ProxyStreamOpenResult, H as FileRecord, Z as SkillStorageScope, F as FlowState, N as FileStats, K as GrepResult, J as AttachmentRef, U as MessageContent, G as Env, x as ThreadInstance } from './index-DYhbT5Eq.js';
2
+ export { A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, u as ClientDuplex, v as ClientForwarding, B as Controller, a as ControllerContext, D as FlowResult, P as ImageContentPart, I as ImageMetadata, L as LLMResponse, Q as MultimodalContent, R as RequestContext, S as StorageBackend, E as TelemetryEvent, O as TextContentPart, b as ThreadEndpointContext, T as ToolCall, 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';
3
3
  import { CodeExecutionOptions, CodeExecutionResult, SubagentRegistryEntry, InjectMessageInput, QueueMessageInput, CredentialGrant, SkillMetadata, SkillFileInput, ModelDefinition as ModelDefinition$1, ToolArgs, PromptTextPart, PromptEnvPart, VariableDefinition, SubpromptConfig as SubpromptConfig$1, PromptToolConfig as PromptToolConfig$1, SubagentToolConfig as SubagentToolConfig$1, ReasoningConfig, SideConfig as SideConfig$1, ParentHookRequirement, LLMProviderInterface, ContentPart, TextPart, ImagePart, FilePart, ThreadState, NamespaceContext, DefinitionLoader } from '@standardagents/spec';
4
4
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookExecutionContext, HookExecutionSource, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, ParentHookRequirement, ParentHookStage, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ModelCapabilities as ProviderModelCapabilities, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, 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';
5
5
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
@@ -234,6 +234,8 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
234
234
  private executionOwnerCache;
235
235
  private pendingClientCalls;
236
236
  private clientStreams;
237
+ private proxyStreamReceivers;
238
+ private eventProxyStreams;
237
239
  private alarmQueue;
238
240
  private runtimeSafety;
239
241
  private alarmQueueRearmed;
@@ -1256,25 +1258,23 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
1256
1258
  * match the CURRENT owner.
1257
1259
  */
1258
1260
  private awaitOwnerBridgeSocket;
1261
+ private parseProxyStreamAttachments;
1262
+ /** Keep live proxy routes on the hibernating bridge socket for restart cleanup. */
1263
+ private updateBridgeProxyAttachment;
1264
+ private deliverProxyStreamFrame;
1265
+ /** Abort live proxy routes restored without their in-memory stream state. */
1266
+ private recoverInterruptedProxyStreams;
1267
+ /** Register the child side before any ancestor opens an owner stream. */
1268
+ createProxyStreamReceiver(onCancel: (error?: string) => void): ProxyStreamReceiver;
1269
+ proxyStreamReceiverExists(receiverId: string): Promise<boolean>;
1270
+ proxyStreamReceive(receiverId: string, frame: ProxyStreamFrame): Promise<boolean>;
1259
1271
  /** Open a bidirectional byte stream to the connected owner client. */
1260
- openClientStream(request: ClientStreamRequest): Promise<ClientStreamResponse>;
1261
- private proxyStreams;
1262
- proxyStreamOpen(request: ClientStreamRequest): Promise<{
1263
- ok: boolean;
1264
- id?: string;
1265
- error?: string;
1266
- noClient?: boolean;
1267
- parkedPull?: boolean;
1268
- code?: 'unsupported' | 'unavailable' | 'refused' | 'timeout';
1269
- }>;
1272
+ openClientStream(request: ClientStreamRequest, proxyTarget?: ProxyStreamTarget | null): Promise<ClientStreamResponse>;
1273
+ proxyStreamOpen(request: ClientStreamRequest, target: ProxyStreamTarget): Promise<ProxyStreamOpenResult>;
1270
1274
  proxyStreamWrite(id: string, dataB64: string): Promise<void>;
1271
- proxyStreamPull(id: string, maxWaitMs?: number): Promise<{
1272
- chunks: string[];
1273
- done: boolean;
1274
- error?: string;
1275
- }>;
1276
1275
  proxyStreamEnd(id: string): Promise<void>;
1277
1276
  proxyStreamAbort(id: string): Promise<void>;
1277
+ private forgetEventProxyStream;
1278
1278
  /** Settle and remove a client stream exactly once. */
1279
1279
  private settleClientStream;
1280
1280
  /** Abort a stream from the Worker side, telling the client to tear down. */
package/dist/runtime.js CHANGED
@@ -14715,27 +14715,51 @@ var init_ThreadStateImpl = __esm({
14715
14715
  const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
14716
14716
  const local = await instance.openClientStream(withWake(this._metadata.id));
14717
14717
  if (local.ok || !this.isNoClient(local)) return local;
14718
- const env = this._threadInstance.env;
14719
- if (!env?.AGENT_BUILDER_THREAD) return local;
14718
+ if (typeof instance.createProxyStreamReceiver !== "function" || typeof instance.proxyStreamReceive !== "function") {
14719
+ return local;
14720
+ }
14721
+ let abortRemote = null;
14722
+ const receiver = instance.createProxyStreamReceiver((error) => abortRemote?.(error));
14723
+ const discardReceiver = async (error) => {
14724
+ await instance.proxyStreamReceive(receiver.id, { type: "abort", error });
14725
+ };
14726
+ const namespace = instance.env.AGENT_BUILDER_THREAD;
14720
14727
  const seen = /* @__PURE__ */ new Set([this._metadata.id]);
14721
14728
  let parentId = await this.resolveParentThreadId(this._metadata.id);
14722
14729
  while (parentId && !seen.has(parentId)) {
14723
14730
  seen.add(parentId);
14724
14731
  try {
14725
- const stub = env.AGENT_BUILDER_THREAD.get(env.AGENT_BUILDER_THREAD.idFromName(parentId));
14732
+ const stub = namespace.get(namespace.idFromName(parentId));
14726
14733
  if (typeof stub.proxyStreamOpen === "function") {
14727
- const opened = await stub.proxyStreamOpen(withWake(parentId));
14734
+ const opened = await stub.proxyStreamOpen(withWake(parentId), {
14735
+ threadId: this._metadata.id,
14736
+ receiverId: receiver.id
14737
+ });
14728
14738
  if (opened.ok && opened.id) {
14739
+ if (opened.eventDriven !== true) {
14740
+ await stub.proxyStreamAbort(opened.id).catch(() => {
14741
+ });
14742
+ await discardReceiver("The ancestor runtime does not support event-driven streams.");
14743
+ return {
14744
+ ok: false,
14745
+ error: "The ancestor runtime does not support event-driven streams.",
14746
+ data: { code: "unsupported" }
14747
+ };
14748
+ }
14749
+ abortRemote = (error) => {
14750
+ void stub.proxyStreamAbort(opened.id).catch(() => {
14751
+ });
14752
+ if (error) {
14753
+ void instance.proxyStreamReceive(receiver.id, { type: "abort", error });
14754
+ }
14755
+ };
14729
14756
  return {
14730
14757
  ok: true,
14731
- duplex: this.buildProxyDuplex(
14732
- stub,
14733
- opened.id,
14734
- opened.parkedPull === true
14735
- )
14758
+ duplex: this.buildEventProxyDuplex(stub, opened.id, receiver, instance)
14736
14759
  };
14737
14760
  }
14738
14761
  if (opened.error && !opened.noClient) {
14762
+ await discardReceiver(opened.error);
14739
14763
  return {
14740
14764
  ok: false,
14741
14765
  error: opened.error,
@@ -14747,69 +14771,21 @@ var init_ThreadStateImpl = __esm({
14747
14771
  }
14748
14772
  parentId = await this.resolveParentThreadId(parentId);
14749
14773
  }
14774
+ await discardReceiver(local.error ?? "No execution-owner bridge is available.");
14750
14775
  return local;
14751
14776
  }
14752
14777
  /**
14753
14778
  * Build a client duplex that bridges to a stream opened on an ancestor DO
14754
- * (the bridge holder) via chunk-pull RPCs. `readable` issues PARKED pulls —
14755
- * a current ancestor holds one `proxyStreamPull` until bytes, completion,
14756
- * error, or abort, so an idle open stream performs zero repeated pulls.
14757
- * Older ancestors use the bounded long-pull compatibility path. `writable`
14758
- * forwards each chunk with `proxyStreamWrite`; teardown calls
14759
- * `proxyStreamAbort`.
14779
+ * (the bridge holder). The readable is the receiver registered on this DO;
14780
+ * writes and terminal actions are the only child-to-ancestor RPCs.
14760
14781
  */
14761
- static PROXY_PULL_MAX_WAIT_MS = 2e4;
14762
- buildProxyDuplex(stub, id, supportsParkedPull = false) {
14763
- let settleDone;
14764
- const done = new Promise((r) => settleDone = r);
14782
+ buildEventProxyDuplex(stub, id, receiver, instance) {
14765
14783
  let closed = false;
14766
- let settled = false;
14767
- const settle = (result) => {
14768
- if (settled) return;
14769
- settled = true;
14770
- settleDone(result);
14784
+ const settleLocal = (error) => {
14785
+ if (typeof instance.proxyStreamReceive !== "function") return;
14786
+ const frame = error ? { type: "abort", error } : { type: "end" };
14787
+ void instance.proxyStreamReceive(receiver.id, frame);
14771
14788
  };
14772
- const readable = new ReadableStream({
14773
- async pull(controller) {
14774
- while (!closed) {
14775
- const pullStartedAt = Date.now();
14776
- let res;
14777
- try {
14778
- res = await stub.proxyStreamPull(
14779
- id,
14780
- supportsParkedPull ? void 0 : _ThreadStateImpl.PROXY_PULL_MAX_WAIT_MS
14781
- );
14782
- } catch (error) {
14783
- controller.error(error instanceof Error ? error : new Error(String(error)));
14784
- settle({ ok: false, error: "proxy stream pull failed" });
14785
- return;
14786
- }
14787
- for (const chunk of res.chunks) {
14788
- controller.enqueue(decodeStreamChunk(chunk));
14789
- }
14790
- if (res.done) {
14791
- controller.close();
14792
- settle(res.error ? { ok: false, error: res.error } : { ok: true });
14793
- return;
14794
- }
14795
- if (res.chunks.length > 0) return;
14796
- if (supportsParkedPull) {
14797
- controller.error(new Error("parked proxy pull returned without data"));
14798
- settle({ ok: false, error: "parked proxy pull returned without data" });
14799
- return;
14800
- }
14801
- if (Date.now() - pullStartedAt < 250) {
14802
- await new Promise((r) => setTimeout(r, 250));
14803
- }
14804
- }
14805
- },
14806
- cancel() {
14807
- closed = true;
14808
- void stub.proxyStreamAbort(id).catch(() => {
14809
- });
14810
- settle({ ok: false, error: "proxy stream reader canceled" });
14811
- }
14812
- });
14813
14789
  const writable = new WritableStream({
14814
14790
  async write(chunk) {
14815
14791
  await stub.proxyStreamWrite(id, encodeStreamChunk(chunk));
@@ -14822,20 +14798,20 @@ var init_ThreadStateImpl = __esm({
14822
14798
  closed = true;
14823
14799
  void stub.proxyStreamAbort(id).catch(() => {
14824
14800
  });
14825
- settle({ ok: false, error: "proxy stream writer aborted" });
14801
+ settleLocal("The proxy stream writer was aborted.");
14826
14802
  }
14827
14803
  });
14828
14804
  return {
14829
- readable,
14805
+ readable: receiver.readable,
14830
14806
  writable,
14831
14807
  close: (error) => {
14832
14808
  if (closed) return;
14833
14809
  closed = true;
14834
14810
  void stub.proxyStreamAbort(id).catch(() => {
14835
14811
  });
14836
- settle(error ? { ok: false, error } : { ok: true });
14812
+ settleLocal(error);
14837
14813
  },
14838
- done
14814
+ done: receiver.done
14839
14815
  };
14840
14816
  }
14841
14817
  isNoClient(res) {
@@ -22266,6 +22242,8 @@ var DurableThread = class _DurableThread extends DurableObject {
22266
22242
  // opened on and dies with it. Streams are in-memory only — they exist solely
22267
22243
  // while the client is attached and never park durably.
22268
22244
  clientStreams = /* @__PURE__ */ new Map();
22245
+ proxyStreamReceivers = /* @__PURE__ */ new Map();
22246
+ eventProxyStreams = /* @__PURE__ */ new Map();
22269
22247
  alarmQueue;
22270
22248
  runtimeSafety;
22271
22249
  alarmQueueRearmed = false;
@@ -22377,6 +22355,10 @@ var DurableThread = class _DurableThread extends DurableObject {
22377
22355
  clientName: typeof attachment.clientName === "string" ? attachment.clientName : null,
22378
22356
  clientKind: typeof attachment.clientKind === "string" ? attachment.clientKind : null
22379
22357
  });
22358
+ const interrupted = this.parseProxyStreamAttachments(attachment.proxyStreams);
22359
+ if (interrupted.length > 0) {
22360
+ this.recoverInterruptedProxyStreams(ws, interrupted);
22361
+ }
22380
22362
  }
22381
22363
  }
22382
22364
  }
@@ -26632,8 +26614,138 @@ ${resultContent}${attachmentSummary}`;
26632
26614
  });
26633
26615
  }
26634
26616
  }
26617
+ parseProxyStreamAttachments(value) {
26618
+ if (!Array.isArray(value)) return [];
26619
+ const parsed = [];
26620
+ for (const item of value) {
26621
+ if (!item || typeof item !== "object") continue;
26622
+ const candidate = item;
26623
+ if (typeof candidate.id === "string" && candidate.target && typeof candidate.target.threadId === "string" && typeof candidate.target.receiverId === "string") {
26624
+ parsed.push({
26625
+ id: candidate.id,
26626
+ target: {
26627
+ threadId: candidate.target.threadId,
26628
+ receiverId: candidate.target.receiverId
26629
+ }
26630
+ });
26631
+ }
26632
+ }
26633
+ return parsed;
26634
+ }
26635
+ /** Keep live proxy routes on the hibernating bridge socket for restart cleanup. */
26636
+ updateBridgeProxyAttachment(socket) {
26637
+ if (typeof socket.serializeAttachment !== "function") return;
26638
+ let attachment = { type: "bridge" };
26639
+ try {
26640
+ const current = socket.deserializeAttachment();
26641
+ if (current && typeof current === "object") {
26642
+ attachment = { ...current };
26643
+ }
26644
+ } catch {
26645
+ }
26646
+ const proxyStreams = Array.from(this.clientStreams.entries()).filter(([, state]) => state.socket === socket && state.proxyTarget !== null).map(([id, state]) => ({ id, target: state.proxyTarget }));
26647
+ try {
26648
+ socket.serializeAttachment({ ...attachment, proxyStreams });
26649
+ } catch {
26650
+ }
26651
+ }
26652
+ async deliverProxyStreamFrame(target, frame) {
26653
+ try {
26654
+ const namespace = this.env.AGENT_BUILDER_THREAD;
26655
+ const stub = namespace.get(namespace.idFromName(target.threadId));
26656
+ return await stub.proxyStreamReceive(target.receiverId, frame);
26657
+ } catch {
26658
+ return false;
26659
+ }
26660
+ }
26661
+ /** Abort live proxy routes restored without their in-memory stream state. */
26662
+ recoverInterruptedProxyStreams(socket, streams) {
26663
+ const error = "The stream ended because its runtime restarted.";
26664
+ for (const stream of streams) {
26665
+ try {
26666
+ if (socket.readyState === WebSocket.OPEN) {
26667
+ socket.send(JSON.stringify({ type: "stream_abort", id: stream.id, error }));
26668
+ }
26669
+ } catch {
26670
+ }
26671
+ this.ctx.waitUntil(
26672
+ this.deliverProxyStreamFrame(stream.target, { type: "abort", error }).then(() => {
26673
+ })
26674
+ );
26675
+ }
26676
+ this.updateBridgeProxyAttachment(socket);
26677
+ }
26678
+ /** Register the child side before any ancestor opens an owner stream. */
26679
+ createProxyStreamReceiver(onCancel) {
26680
+ const id = crypto.randomUUID();
26681
+ let controller = null;
26682
+ let settleDone;
26683
+ const done = new Promise((resolve) => {
26684
+ settleDone = resolve;
26685
+ });
26686
+ const readable = new ReadableStream({
26687
+ start(value) {
26688
+ controller = value;
26689
+ },
26690
+ cancel: (reason) => {
26691
+ const receiver2 = this.proxyStreamReceivers.get(id);
26692
+ if (!receiver2 || receiver2.settled) return;
26693
+ const error = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "The proxy stream reader was canceled.";
26694
+ receiver2.settle({ ok: false, error });
26695
+ receiver2.onCancel(error);
26696
+ }
26697
+ });
26698
+ const receiver = {
26699
+ controller,
26700
+ settle: (result) => {
26701
+ if (receiver.settled) return;
26702
+ receiver.settled = true;
26703
+ this.proxyStreamReceivers.delete(id);
26704
+ settleDone(result);
26705
+ },
26706
+ onCancel,
26707
+ settled: false
26708
+ };
26709
+ this.proxyStreamReceivers.set(id, receiver);
26710
+ return { id, readable, done };
26711
+ }
26712
+ async proxyStreamReceiverExists(receiverId) {
26713
+ return this.proxyStreamReceivers.has(receiverId);
26714
+ }
26715
+ async proxyStreamReceive(receiverId, frame) {
26716
+ const receiver = this.proxyStreamReceivers.get(receiverId);
26717
+ if (!receiver || receiver.settled) return false;
26718
+ if (frame.type === "data") {
26719
+ try {
26720
+ receiver.controller?.enqueue(decodeStreamChunk(frame.data));
26721
+ } catch (error2) {
26722
+ const message = error2 instanceof Error ? error2.message : String(error2);
26723
+ try {
26724
+ receiver.controller?.error(error2 instanceof Error ? error2 : new Error(message));
26725
+ } catch {
26726
+ }
26727
+ receiver.settle({ ok: false, error: message });
26728
+ }
26729
+ return true;
26730
+ }
26731
+ if (frame.type === "end") {
26732
+ try {
26733
+ receiver.controller?.close();
26734
+ } catch {
26735
+ }
26736
+ receiver.settle({ ok: true });
26737
+ return true;
26738
+ }
26739
+ const error = frame.error ?? "The proxied client stream was aborted.";
26740
+ try {
26741
+ receiver.controller?.error(new Error(error));
26742
+ } catch {
26743
+ }
26744
+ receiver.settle({ ok: false, error });
26745
+ return true;
26746
+ }
26635
26747
  /** Open a bidirectional byte stream to the connected owner client. */
26636
- async openClientStream(request) {
26748
+ async openClientStream(request, proxyTarget = null) {
26637
26749
  const owner = await this.getExecutionOwner();
26638
26750
  let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
26639
26751
  if (!socket && owner) {
@@ -26666,9 +26778,12 @@ ${resultContent}${attachmentSummary}`;
26666
26778
  opened: null,
26667
26779
  openTimer: null,
26668
26780
  settleDone,
26669
- settled: false
26781
+ settled: false,
26782
+ proxyTarget,
26783
+ delivery: Promise.resolve()
26670
26784
  };
26671
26785
  this.clientStreams.set(id, state);
26786
+ this.updateBridgeProxyAttachment(socket);
26672
26787
  this.bridgeLastSeen.set(socket, Date.now());
26673
26788
  const openResult = await new Promise((resolve) => {
26674
26789
  state.opened = resolve;
@@ -26704,6 +26819,7 @@ ${resultContent}${attachmentSummary}`;
26704
26819
  }
26705
26820
  if (!openResult.ok) {
26706
26821
  this.clientStreams.delete(id);
26822
+ this.updateBridgeProxyAttachment(socket);
26707
26823
  state.settled = true;
26708
26824
  settleDone({ ok: false, error: openResult.error });
26709
26825
  return {
@@ -26745,14 +26861,28 @@ ${resultContent}${attachmentSummary}`;
26745
26861
  };
26746
26862
  return { ok: true, duplex };
26747
26863
  }
26748
- // ─── Subagent stream proxy-up (chunk-pull, no cross-DO ReadableStream) ─────
26749
- // A subagent (child thread, no bridge) proxies its whole stream request UP to
26750
- // this bridge-holding root. We open the client stream locally, then buffer
26751
- // incoming bytes so the child can drain them over plain request/response RPCs
26752
- // — sidestepping the unproven DO-to-DO ReadableStream return.
26753
- proxyStreams = /* @__PURE__ */ new Map();
26754
- async proxyStreamOpen(request) {
26755
- const opened = await this.openClientStream(request);
26864
+ // ─── Event-driven subagent stream proxy ──────────────────────────────────
26865
+ // A child registers a local receiver before the bridge-holding ancestor
26866
+ // opens the client stream. The ancestor invokes that receiver only for real
26867
+ // data/end/abort events; there is no pull loop or permanently outstanding
26868
+ // cross-DO RPC while the stream is idle.
26869
+ async proxyStreamOpen(request, target) {
26870
+ const namespace = this.env.AGENT_BUILDER_THREAD;
26871
+ let receiverExists = false;
26872
+ try {
26873
+ const destination = namespace.get(namespace.idFromName(target.threadId));
26874
+ receiverExists = await destination.proxyStreamReceiverExists(target.receiverId);
26875
+ } catch {
26876
+ receiverExists = false;
26877
+ }
26878
+ if (!receiverExists) {
26879
+ return {
26880
+ ok: false,
26881
+ error: "The destination stream receiver is unavailable.",
26882
+ code: "unavailable"
26883
+ };
26884
+ }
26885
+ const opened = await this.openClientStream(request, target);
26756
26886
  if (!opened.ok) {
26757
26887
  const failureData = opened.data;
26758
26888
  const noClient = failureData?.noClient === true;
@@ -26764,82 +26894,23 @@ ${resultContent}${attachmentSummary}`;
26764
26894
  };
26765
26895
  }
26766
26896
  const id = crypto.randomUUID();
26767
- const duplex = opened.duplex;
26768
- const reader = duplex.readable.getReader();
26769
- const writer = duplex.writable.getWriter();
26770
- const entry = {
26771
- writer,
26772
- reader,
26773
- buffer: [],
26774
- done: false,
26775
- error: void 0,
26776
- pump: Promise.resolve(),
26777
- close: (error) => duplex.close(error),
26778
- waiter: null,
26779
- notify: () => {
26780
- const waiter = entry.waiter;
26781
- entry.waiter = null;
26782
- waiter?.();
26783
- }
26784
- };
26785
- entry.pump = (async () => {
26786
- try {
26787
- while (true) {
26788
- const { value, done } = await reader.read();
26789
- if (done) break;
26790
- if (value) {
26791
- entry.buffer.push(value);
26792
- entry.notify();
26793
- }
26794
- }
26795
- } catch (error) {
26796
- entry.error = error instanceof Error ? error.message : String(error);
26797
- } finally {
26798
- entry.done = true;
26799
- entry.notify();
26800
- }
26801
- })();
26802
- void duplex.done.then((d) => {
26803
- entry.done = true;
26804
- if (!d.ok && !entry.error) entry.error = d.error;
26805
- entry.notify();
26897
+ this.eventProxyStreams.set(id, {
26898
+ writer: opened.duplex.writable.getWriter(),
26899
+ close: (error) => opened.duplex.close(error),
26900
+ target
26806
26901
  });
26807
- this.proxyStreams.set(id, entry);
26808
- return { ok: true, id, parkedPull: true };
26902
+ return { ok: true, id, eventDriven: true };
26809
26903
  }
26810
26904
  async proxyStreamWrite(id, dataB64) {
26811
- const entry = this.proxyStreams.get(id);
26905
+ const entry = this.eventProxyStreams.get(id);
26812
26906
  if (!entry) return;
26813
26907
  try {
26814
26908
  await entry.writer.write(decodeStreamChunk(dataB64));
26815
26909
  } catch {
26816
26910
  }
26817
26911
  }
26818
- async proxyStreamPull(id, maxWaitMs) {
26819
- const entry = this.proxyStreams.get(id);
26820
- if (!entry) return { chunks: [], done: true, error: "unknown proxy stream" };
26821
- if (entry.buffer.length === 0 && !entry.done) {
26822
- await new Promise((resolve) => {
26823
- const deadline = typeof maxWaitMs === "number" && maxWaitMs > 0 ? setTimeout(() => {
26824
- if (entry.waiter === wake) entry.waiter = null;
26825
- resolve();
26826
- }, maxWaitMs) : null;
26827
- const wake = () => {
26828
- if (deadline) clearTimeout(deadline);
26829
- resolve();
26830
- };
26831
- entry.waiter = wake;
26832
- });
26833
- }
26834
- const chunks = entry.buffer.splice(0).map((b) => encodeStreamChunk(b));
26835
- if (entry.done && entry.buffer.length === 0) {
26836
- this.proxyStreams.delete(id);
26837
- return { chunks, done: true, error: entry.error };
26838
- }
26839
- return { chunks, done: false };
26840
- }
26841
26912
  async proxyStreamEnd(id) {
26842
- const entry = this.proxyStreams.get(id);
26913
+ const entry = this.eventProxyStreams.get(id);
26843
26914
  if (!entry) return;
26844
26915
  try {
26845
26916
  await entry.writer.close();
@@ -26847,30 +26918,49 @@ ${resultContent}${attachmentSummary}`;
26847
26918
  }
26848
26919
  }
26849
26920
  async proxyStreamAbort(id) {
26850
- const entry = this.proxyStreams.get(id);
26921
+ const entry = this.eventProxyStreams.get(id);
26851
26922
  if (!entry) return;
26852
- this.proxyStreams.delete(id);
26853
- entry.done = true;
26854
- if (!entry.error) entry.error = "aborted";
26855
- entry.notify();
26923
+ this.eventProxyStreams.delete(id);
26856
26924
  try {
26857
26925
  entry.close("aborted");
26858
26926
  } catch {
26859
26927
  }
26860
26928
  }
26929
+ forgetEventProxyStream(clientStreamId, target) {
26930
+ const matchesTarget = (candidate) => candidate.threadId === target.threadId && candidate.receiverId === target.receiverId;
26931
+ for (const [id, entry] of Array.from(this.eventProxyStreams.entries())) {
26932
+ if (id !== clientStreamId && !matchesTarget(entry.target)) continue;
26933
+ this.eventProxyStreams.delete(id);
26934
+ try {
26935
+ entry.writer.releaseLock();
26936
+ } catch {
26937
+ }
26938
+ }
26939
+ }
26861
26940
  /** Settle and remove a client stream exactly once. */
26862
26941
  settleClientStream(id, result, openCode) {
26863
26942
  const state = this.clientStreams.get(id);
26864
26943
  if (!state) return;
26865
26944
  this.clientStreams.delete(id);
26945
+ this.updateBridgeProxyAttachment(state.socket);
26866
26946
  if (state.openTimer) clearTimeout(state.openTimer);
26867
26947
  if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed.", code: openCode });
26868
26948
  if (!state.settled) {
26869
26949
  state.settled = true;
26870
- try {
26871
- if (result.ok) state.controller?.close();
26872
- else state.controller?.error(new Error(result.error ?? "Client stream aborted."));
26873
- } catch {
26950
+ if (state.proxyTarget) {
26951
+ const target = state.proxyTarget;
26952
+ const frame = result.ok ? { type: "end" } : { type: "abort", error: result.error ?? "Client stream aborted." };
26953
+ state.delivery = state.delivery.then(async () => {
26954
+ await this.deliverProxyStreamFrame(target, frame);
26955
+ });
26956
+ this.ctx.waitUntil(state.delivery);
26957
+ this.forgetEventProxyStream(id, target);
26958
+ } else {
26959
+ try {
26960
+ if (result.ok) state.controller?.close();
26961
+ else state.controller?.error(new Error(result.error ?? "Client stream aborted."));
26962
+ } catch {
26963
+ }
26874
26964
  }
26875
26965
  state.settleDone(result);
26876
26966
  }
@@ -26913,6 +27003,18 @@ ${resultContent}${attachmentSummary}`;
26913
27003
  }
26914
27004
  case "stream_data": {
26915
27005
  if (typeof parsed.data !== "string") return;
27006
+ if (state.proxyTarget) {
27007
+ const target = state.proxyTarget;
27008
+ const frame = { type: "data", data: parsed.data };
27009
+ state.delivery = state.delivery.then(async () => {
27010
+ const delivered = await this.deliverProxyStreamFrame(target, frame);
27011
+ if (!delivered && this.clientStreams.get(id) === state) {
27012
+ this.abortClientStream(id, "The destination stream receiver is unavailable.");
27013
+ }
27014
+ });
27015
+ this.ctx.waitUntil(state.delivery);
27016
+ return;
27017
+ }
26916
27018
  try {
26917
27019
  state.controller?.enqueue(decodeStreamChunk(parsed.data));
26918
27020
  } catch {