@standardagents/builder 0.28.1 → 0.29.0

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.
@@ -77,15 +77,11 @@ declare class StreamManager {
77
77
  }
78
78
 
79
79
  /**
80
- * Client-tool forwarding — a BUILDER capability, not a spec surface.
80
+ * Client-tool forwarding and the legacy client-stream adapter.
81
81
  *
82
- * The Standard Agent Specification has no "client" concept: agents, prompts,
83
- * hooks, and tools are defined against spec surfaces only. But tools are
84
- * arbitrary code and may call anything — including this builder API, which
85
- * forwards an operation over the thread's `bridge` WebSocket to a connected
86
- * client (a CLI or machine daemon) for local execution. Products built on
87
- * AgentBuilder (e.g. Standard Code) import these types and entry points from
88
- * `@standardagents/builder`.
82
+ * Tool forwarding remains Builder-specific. Live byte streams are exposed to
83
+ * portable agent code through the optional spec `ThreadState.runtimeStreams`
84
+ * surface, which this module adapts onto the existing bridge frames.
89
85
  */
90
86
  /**
91
87
  * A request forwarded from a server-side tool to a connected client (CLI or
@@ -125,10 +121,8 @@ interface ClientToolResponse {
125
121
  /**
126
122
  * A request to open a bidirectional byte stream to the connected client.
127
123
  *
128
- * The client interprets `tool` + `meta` (e.g. `tcp_tunnel` with
129
- * `{ host, port }` opens a raw TCP socket and shuttles bytes both ways).
130
- * Payload bytes are opaque to the transport — for a TLS tunnel the client
131
- * only ever relays ciphertext.
124
+ * The execution host interprets `tool` + `meta`; both fields and all payload
125
+ * bytes are opaque to the bridge transport.
132
126
  */
133
127
  interface ClientStreamRequest {
134
128
  /** Client stream capability name (e.g. `tcp_tunnel`). */
@@ -189,9 +183,10 @@ declare function supportsClientForwarding(state: ThreadState): state is ThreadSt
189
183
  */
190
184
  declare function callClient(state: ThreadState, request: ClientToolRequest): Promise<ClientToolResponse>;
191
185
  /**
192
- * Open a bidirectional byte stream to the connected client (e.g. a raw TCP
193
- * tunnel for Worker-side TLS termination). Requires a live bridge socket on
194
- * this thread — streams never park durably.
186
+ * Open a bidirectional byte stream through the legacy Builder-only surface.
187
+ * Requires a live execution-host bridge and never parks durably.
188
+ *
189
+ * @deprecated Portable agent code should use `state.runtimeStreams?.open()`.
195
190
  */
196
191
  declare function callClientStream(state: ThreadState, request: ClientStreamRequest): Promise<ClientStreamResponse>;
197
192
  /** Maximum raw bytes per `stream_data` frame (base64 expands ~4/3 on the wire;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ModelDefinition, AgentDefinition } from './runtime.js';
2
2
  export { AgentBuilderEnv, AuthContext, AuthUser, 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, ThreadRegistryEntry, ThreadWatchdog, ThreadWatchdogState, ToolConfig, UpdateThreadParams, User, WatchdogCheckin, WatchdogProbeResult, authenticate, buildImageDescription, cat, createNamespaceContext, emitThreadEvent, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isQualifiedName, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, stat, tail, unlink, updateThread, writeFile, writeImage } from './runtime.js';
3
3
  export { AgentPluginOptions, agentbuilder, builder } from './plugin.js';
4
- export { A as Agent, x as AttachmentRef, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, m as ClientStreamRequest, n as ClientStreamResponse, k as ClientToolRequest, l as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, w as FileRecord, y as FileStats, u as FlowResult, F as FlowState, G as GrepResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, J as MessageContent, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, q as ThreadInstance, r as ThreadMetadata, T as ToolCall, t as ToolResult, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-BzXyIJPA.js';
4
+ export { A as Agent, x as AttachmentRef, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, m as ClientStreamRequest, n as ClientStreamResponse, k as ClientToolRequest, l as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, w as FileRecord, y as FileStats, u as FlowResult, F as FlowState, G as GrepResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, J as MessageContent, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, q as ThreadInstance, r as ThreadMetadata, T as ToolCall, t as ToolResult, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-Cp_Zcrhy.js';
5
5
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, 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, 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-Bpe7IANZ.js';
7
7
  export { D as DiscoveryConfig, P as PackageDiscoveryService, d as discoverPackages } from './discovery-DiMJWisl.js';
package/dist/index.js CHANGED
@@ -56,6 +56,30 @@ function callClient(state, request) {
56
56
  return s.callClient(request);
57
57
  }
58
58
  function callClientStream(state, request) {
59
+ if (state.runtimeStreams) {
60
+ return state.runtimeStreams.open({
61
+ capability: request.tool,
62
+ metadata: request.meta,
63
+ timeoutMs: request.timeoutMs
64
+ }).then(
65
+ (opened) => opened.ok ? {
66
+ ok: true,
67
+ duplex: {
68
+ readable: opened.stream.readable,
69
+ writable: opened.stream.writable,
70
+ close: (reason) => opened.stream.close(reason),
71
+ done: opened.stream.closed
72
+ }
73
+ } : {
74
+ ok: false,
75
+ error: opened.error,
76
+ data: opened.code ? {
77
+ code: opened.code,
78
+ noClient: opened.code === "unavailable" ? true : void 0
79
+ } : void 0
80
+ }
81
+ );
82
+ }
59
83
  const s = asForwarding(state);
60
84
  if (typeof s.callClientStream !== "function") {
61
85
  return Promise.resolve({
@@ -65,6 +89,39 @@ function callClientStream(state, request) {
65
89
  }
66
90
  return s.callClientStream(request);
67
91
  }
92
+ function createRuntimeStreams(openClientStream) {
93
+ return {
94
+ async open(request) {
95
+ const opened = await openClientStream({
96
+ tool: request.capability,
97
+ meta: request.metadata,
98
+ timeoutMs: request.timeoutMs
99
+ });
100
+ if (!opened.ok) {
101
+ const failureData = opened.data;
102
+ return {
103
+ ok: false,
104
+ error: opened.error,
105
+ code: failureData?.code
106
+ };
107
+ }
108
+ let closed = false;
109
+ return {
110
+ ok: true,
111
+ stream: {
112
+ readable: opened.duplex.readable,
113
+ writable: opened.duplex.writable,
114
+ closed: opened.duplex.done,
115
+ close: (reason) => {
116
+ if (closed) return;
117
+ closed = true;
118
+ opened.duplex.close(reason);
119
+ }
120
+ }
121
+ };
122
+ }
123
+ };
124
+ }
68
125
  function encodeStreamChunk(bytes) {
69
126
  let binary = "";
70
127
  const step = 32768;
@@ -12939,6 +12996,14 @@ var init_ThreadStateImpl = __esm({
12939
12996
  }
12940
12997
  return this.getMetadataArguments();
12941
12998
  }
12999
+ /**
13000
+ * Optional spec byte streams backed by the execution owner's existing
13001
+ * bridge. Streams are live resources: they proxy through ancestors when a
13002
+ * subagent opens one, but they never park or outlive a bridge disconnect.
13003
+ */
13004
+ get runtimeStreams() {
13005
+ return createRuntimeStreams((request) => this.callClientStream(request));
13006
+ }
12942
13007
  getMetadataArguments() {
12943
13008
  const value = this._metadata.properties?.[SUBAGENT_INITIAL_ARGUMENTS_THREAD_PROPERTY];
12944
13009
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -13515,11 +13580,9 @@ var init_ThreadStateImpl = __esm({
13515
13580
  return local;
13516
13581
  }
13517
13582
  /**
13518
- * Open a bidirectional byte stream to the connected client (e.g. a raw TCP
13519
- * tunnel for Worker-side TLS termination). Streams require a LIVE bridge on
13520
- * this thread and never park — a stream is only meaningful while the client
13521
- * is attached. Subagents must not call this directly; they proxy whole
13522
- * requests up to the root thread instead (request-up / stream-down).
13583
+ * Legacy bridge implementation used by `runtimeStreams`. Streams require a
13584
+ * live execution owner and never park. A child thread proxies the complete
13585
+ * request to the nearest bridge-holding ancestor (request-up/stream-down).
13523
13586
  */
13524
13587
  async callClientStream(request) {
13525
13588
  const instance = this._threadInstance;
@@ -13545,7 +13608,11 @@ var init_ThreadStateImpl = __esm({
13545
13608
  return { ok: true, duplex: this.buildProxyDuplex(stub, opened.id) };
13546
13609
  }
13547
13610
  if (opened.error && !opened.noClient) {
13548
- return { ok: false, error: opened.error };
13611
+ return {
13612
+ ok: false,
13613
+ error: opened.error,
13614
+ data: opened.code ? { code: opened.code } : void 0
13615
+ };
13549
13616
  }
13550
13617
  }
13551
13618
  } catch {
@@ -13564,6 +13631,12 @@ var init_ThreadStateImpl = __esm({
13564
13631
  let settleDone;
13565
13632
  const done = new Promise((r) => settleDone = r);
13566
13633
  let closed = false;
13634
+ let settled = false;
13635
+ const settle = (result) => {
13636
+ if (settled) return;
13637
+ settled = true;
13638
+ settleDone(result);
13639
+ };
13567
13640
  const readable = new ReadableStream({
13568
13641
  async pull(controller) {
13569
13642
  while (!closed) {
@@ -13572,7 +13645,7 @@ var init_ThreadStateImpl = __esm({
13572
13645
  res = await stub.proxyStreamPull(id);
13573
13646
  } catch (error) {
13574
13647
  controller.error(error instanceof Error ? error : new Error(String(error)));
13575
- settleDone({ ok: false, error: "proxy stream pull failed" });
13648
+ settle({ ok: false, error: "proxy stream pull failed" });
13576
13649
  return;
13577
13650
  }
13578
13651
  for (const chunk of res.chunks) {
@@ -13580,7 +13653,7 @@ var init_ThreadStateImpl = __esm({
13580
13653
  }
13581
13654
  if (res.done) {
13582
13655
  controller.close();
13583
- settleDone(res.error ? { ok: false, error: res.error } : { ok: true });
13656
+ settle(res.error ? { ok: false, error: res.error } : { ok: true });
13584
13657
  return;
13585
13658
  }
13586
13659
  if (res.chunks.length > 0) return;
@@ -13591,6 +13664,7 @@ var init_ThreadStateImpl = __esm({
13591
13664
  closed = true;
13592
13665
  void stub.proxyStreamAbort(id).catch(() => {
13593
13666
  });
13667
+ settle({ ok: false, error: "proxy stream reader canceled" });
13594
13668
  }
13595
13669
  });
13596
13670
  const writable = new WritableStream({
@@ -13605,16 +13679,18 @@ var init_ThreadStateImpl = __esm({
13605
13679
  closed = true;
13606
13680
  void stub.proxyStreamAbort(id).catch(() => {
13607
13681
  });
13682
+ settle({ ok: false, error: "proxy stream writer aborted" });
13608
13683
  }
13609
13684
  });
13610
13685
  return {
13611
13686
  readable,
13612
13687
  writable,
13613
13688
  close: (error) => {
13689
+ if (closed) return;
13614
13690
  closed = true;
13615
13691
  void stub.proxyStreamAbort(id).catch(() => {
13616
13692
  });
13617
- settleDone(error ? { ok: false, error } : { ok: true });
13693
+ settle(error ? { ok: false, error } : { ok: true });
13618
13694
  },
13619
13695
  done
13620
13696
  };
@@ -28588,12 +28664,10 @@ ${resultContent}${attachmentSummary}`;
28588
28664
  }
28589
28665
  return { side };
28590
28666
  }
28591
- // ─── Client byte streams (Worker-side TLS tunnels) ─────────────────────────
28592
- // A stream is a bidirectional byte pipe over the bridge WebSocket: the
28593
- // client opens a raw TCP socket per the stream's `meta` and shuttles bytes
28594
- // both ways; the Worker terminates TLS over those bytes (node:tls over the
28595
- // duplex), so the client only ever relays ciphertext. Streams are bound to a
28596
- // live socket and never park.
28667
+ // ─── Runtime byte streams ──────────────────────────────────────────────────
28668
+ // The existing bridge frames carry opaque capability metadata and bytes
28669
+ // between the runtime and its execution owner. Streams are live resources:
28670
+ // they are bound to one bridge socket and never park.
28597
28671
  /** Default time to wait for the client to accept a stream open. */
28598
28672
  static CLIENT_STREAM_OPEN_TIMEOUT_MS = 3e4;
28599
28673
  /** Open a bidirectional byte stream to the connected owner client. */
@@ -28604,7 +28678,7 @@ ${resultContent}${attachmentSummary}`;
28604
28678
  return {
28605
28679
  ok: false,
28606
28680
  error: "The workspace is not available right now. Try again in a moment.",
28607
- data: { noClient: true }
28681
+ data: { noClient: true, code: "unavailable" }
28608
28682
  };
28609
28683
  }
28610
28684
  const id = crypto.randomUUID();
@@ -28637,7 +28711,8 @@ ${resultContent}${attachmentSummary}`;
28637
28711
  state.openTimer = setTimeout(() => {
28638
28712
  resolve2({
28639
28713
  ok: false,
28640
- error: `The client did not accept the ${request.tool} stream within ${Math.round(timeoutMs / 1e3)}s.`
28714
+ error: `The client did not accept the ${request.tool} stream within ${Math.round(timeoutMs / 1e3)}s.`,
28715
+ code: "timeout"
28641
28716
  });
28642
28717
  }, timeoutMs);
28643
28718
  try {
@@ -28652,7 +28727,8 @@ ${resultContent}${attachmentSummary}`;
28652
28727
  } catch (error) {
28653
28728
  resolve2({
28654
28729
  ok: false,
28655
- error: `Failed to reach the client: ${error instanceof Error ? error.message : String(error)}`
28730
+ error: `Failed to reach the client: ${error instanceof Error ? error.message : String(error)}`,
28731
+ code: "unavailable"
28656
28732
  });
28657
28733
  }
28658
28734
  });
@@ -28665,7 +28741,11 @@ ${resultContent}${attachmentSummary}`;
28665
28741
  this.clientStreams.delete(id);
28666
28742
  state.settled = true;
28667
28743
  settleDone({ ok: false, error: openResult.error });
28668
- return { ok: false, error: openResult.error ?? "Stream open failed." };
28744
+ return {
28745
+ ok: false,
28746
+ error: openResult.error ?? "Stream open failed.",
28747
+ data: openResult.code ? { code: openResult.code } : void 0
28748
+ };
28669
28749
  }
28670
28750
  const sendChunked = (bytes) => {
28671
28751
  for (let i = 0; i < bytes.length; i += CLIENT_STREAM_CHUNK_BYTES) {
@@ -28709,8 +28789,14 @@ ${resultContent}${attachmentSummary}`;
28709
28789
  async proxyStreamOpen(request) {
28710
28790
  const opened = await this.openClientStream(request);
28711
28791
  if (!opened.ok) {
28712
- const noClient = !!(opened.data && opened.data.noClient);
28713
- return { ok: false, error: opened.error, noClient };
28792
+ const failureData = opened.data;
28793
+ const noClient = failureData?.noClient === true;
28794
+ return {
28795
+ ok: false,
28796
+ error: opened.error,
28797
+ noClient,
28798
+ code: failureData?.code
28799
+ };
28714
28800
  }
28715
28801
  const id = crypto.randomUUID();
28716
28802
  const duplex = opened.duplex;
@@ -28781,12 +28867,12 @@ ${resultContent}${attachmentSummary}`;
28781
28867
  }
28782
28868
  }
28783
28869
  /** Settle and remove a client stream exactly once. */
28784
- settleClientStream(id, result) {
28870
+ settleClientStream(id, result, openCode) {
28785
28871
  const state = this.clientStreams.get(id);
28786
28872
  if (!state) return;
28787
28873
  this.clientStreams.delete(id);
28788
28874
  if (state.openTimer) clearTimeout(state.openTimer);
28789
- if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed." });
28875
+ if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed.", code: openCode });
28790
28876
  if (!state.settled) {
28791
28877
  state.settled = true;
28792
28878
  try {
@@ -28830,7 +28916,7 @@ ${resultContent}${attachmentSummary}`;
28830
28916
  }
28831
28917
  case "stream_error": {
28832
28918
  const error = typeof parsed.error === "string" ? parsed.error : "Client stream error.";
28833
- this.settleClientStream(id, { ok: false, error });
28919
+ this.settleClientStream(id, { ok: false, error }, "refused");
28834
28920
  return;
28835
28921
  }
28836
28922
  case "stream_data": {
@@ -28857,7 +28943,7 @@ ${resultContent}${attachmentSummary}`;
28857
28943
  failClientStreamsForSocket(ws) {
28858
28944
  for (const [id, state] of Array.from(this.clientStreams.entries())) {
28859
28945
  if (state.socket === ws) {
28860
- this.settleClientStream(id, { ok: false, error: "The client connection was lost." });
28946
+ this.settleClientStream(id, { ok: false, error: "The client connection was lost." }, "unavailable");
28861
28947
  }
28862
28948
  }
28863
28949
  }