@standardagents/builder 0.29.1 → 0.29.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { e as ThreadEnv, A as Agent, r as ThreadMetadata, t as ToolResult, M as Message, k as ClientToolRequest, l as ClientToolResponse, m as ClientStreamRequest, n as ClientStreamResponse, w as FileRecord, F as FlowState, y as FileStats, G as GrepResult, x as AttachmentRef, J as MessageContent, E as Env, q as ThreadInstance } from './index-BNLDsLsL.js';
2
- export { f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, B as Controller, a as ControllerContext, u as FlowResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, T as ToolCall, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-BNLDsLsL.js';
1
+ import { e as ThreadEnv, A as Agent, r as ThreadMetadata, t as ToolResult, M as Message, k as ClientToolRequest, l as ClientToolResponse, m as ClientStreamRequest, n as ClientStreamResponse, w as FileRecord, F as FlowState, y as FileStats, G as GrepResult, x as AttachmentRef, J as MessageContent, E as Env, q as ThreadInstance } from './index-CcnGEkPh.js';
2
+ export { f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, B as Controller, a as ControllerContext, u as FlowResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, T as ToolCall, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-CcnGEkPh.js';
3
3
  import { CodeExecutionOptions, CodeExecutionResult, SubagentRegistryEntry, InjectMessageInput, QueueMessageInput, 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, LLMProviderInterface, ContentPart, TextPart, ImagePart, FilePart, ThreadState, NamespaceContext, DefinitionLoader } from '@standardagents/spec';
4
4
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, 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, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel } from '@standardagents/spec';
5
5
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
@@ -1043,7 +1043,10 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
1043
1043
  * always produce a normal tool error (no client connected, timeout, or user
1044
1044
  * denial) for the model to reason about.
1045
1045
  */
1046
- callClientTool(request: ClientToolRequest): Promise<ClientToolResponse>;
1046
+ callClientTool(request: ClientToolRequest, durability?: {
1047
+ toolCallId: string;
1048
+ side: "a" | "b";
1049
+ }): Promise<ClientToolResponse>;
1047
1050
  /** Settle a pending forwarded call exactly once, clearing its timers. */
1048
1051
  private settleClientCall;
1049
1052
  /** Close and forget a bridge socket we've deemed dead, failing remaining calls. */
package/dist/runtime.js CHANGED
@@ -13536,7 +13536,10 @@ var init_ThreadStateImpl = __esm({
13536
13536
  data: { deferred: true, toolCallId }
13537
13537
  };
13538
13538
  }
13539
- const local = await this._threadInstance.callClientTool(request);
13539
+ const local = await this._threadInstance.callClientTool(
13540
+ request,
13541
+ canPark && toolCallId ? { toolCallId, side } : void 0
13542
+ );
13540
13543
  if (local.ok || !this.isNoClient(local)) return local;
13541
13544
  const env = this._threadInstance.env;
13542
13545
  if (env?.AGENT_BUILDER_THREAD) {
@@ -19938,7 +19941,7 @@ ${resultContent}${attachmentSummary}`;
19938
19941
  }
19939
19942
  /** Whether any forwarded call is mid-flight (blocking await or parked). */
19940
19943
  async hasInFlightClientCalls() {
19941
- if (this.pendingClientCalls.size > 0) return true;
19944
+ if ((this.pendingClientCalls?.size ?? 0) > 0) return true;
19942
19945
  await this.ensureMigrated();
19943
19946
  await this.ensurePendingClientTable();
19944
19947
  const cursor = await this.ctx.storage.sql.exec(
@@ -19997,7 +20000,7 @@ ${resultContent}${attachmentSummary}`;
19997
20000
  * always produce a normal tool error (no client connected, timeout, or user
19998
20001
  * denial) for the model to reason about.
19999
20002
  */
20000
- async callClientTool(request) {
20003
+ async callClientTool(request, durability) {
20001
20004
  const owner = await this.getExecutionOwner();
20002
20005
  const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
20003
20006
  if (!socket) {
@@ -20007,7 +20010,33 @@ ${resultContent}${attachmentSummary}`;
20007
20010
  data: { noClient: true }
20008
20011
  };
20009
20012
  }
20010
- const id = crypto.randomUUID();
20013
+ if (durability) {
20014
+ try {
20015
+ await this.ensureMigrated();
20016
+ await this.ensurePendingClientTable();
20017
+ const ownerId = owner?.client_id ?? null;
20018
+ const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
20019
+ const fenced = owner && socketClientId === owner.client_id;
20020
+ await this.ctx.storage.sql.exec(
20021
+ `INSERT OR REPLACE INTO pending_client_calls
20022
+ (tool_call_id, tool, args, request_permission, risk, side, status, created_at,
20023
+ dispatched_client_id, dispatched_generation)
20024
+ VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
20025
+ durability.toolCallId,
20026
+ request.tool,
20027
+ JSON.stringify(request.args ?? {}),
20028
+ request.requestPermission ?? null,
20029
+ typeof request.risk === "number" ? request.risk : null,
20030
+ durability.side,
20031
+ Date.now(),
20032
+ fenced ? ownerId : null,
20033
+ fenced ? owner.generation : 0
20034
+ );
20035
+ } catch (error) {
20036
+ console.error("[DurableThread] Failed to persist fast-path call (continuing):", error);
20037
+ }
20038
+ }
20039
+ const id = durability?.toolCallId ?? crypto.randomUUID();
20011
20040
  const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : DEFAULT_CLIENT_TOOL_TIMEOUT_MS;
20012
20041
  this.bridgeLastSeen.set(socket, Date.now());
20013
20042
  return new Promise((resolve) => {
@@ -20058,6 +20087,13 @@ ${resultContent}${attachmentSummary}`;
20058
20087
  this.pendingClientCalls.delete(id);
20059
20088
  if (pending.timer) clearTimeout(pending.timer);
20060
20089
  if (pending.liveness) clearInterval(pending.liveness);
20090
+ try {
20091
+ void this.ctx.storage.sql.exec(
20092
+ `UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ? AND status = 'pending'`,
20093
+ id
20094
+ );
20095
+ } catch {
20096
+ }
20061
20097
  pending.resolve(res);
20062
20098
  }
20063
20099
  /** Close and forget a bridge socket we've deemed dead, failing remaining calls. */
@@ -20309,6 +20345,26 @@ ${resultContent}${attachmentSummary}`;
20309
20345
  toolStatus,
20310
20346
  toolCallId
20311
20347
  );
20348
+ const updated = await this.ctx.storage.sql.exec(
20349
+ `SELECT COUNT(*) AS total FROM messages WHERE tool_call_id = ? AND role = 'tool'`,
20350
+ toolCallId
20351
+ );
20352
+ if (Number(updated.toArray()[0]?.total ?? 0) === 0) {
20353
+ const toolRow = await this.ctx.storage.sql.exec(
20354
+ `SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
20355
+ toolCallId
20356
+ );
20357
+ await this.ctx.storage.sql.exec(
20358
+ `INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
20359
+ VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
20360
+ crypto.randomUUID(),
20361
+ content,
20362
+ toolRow.toArray()[0]?.tool ?? null,
20363
+ toolCallId,
20364
+ toolStatus,
20365
+ Date.now() * TIMESTAMP_MULTIPLIER
20366
+ );
20367
+ }
20312
20368
  await this.ctx.storage.sql.exec(
20313
20369
  `UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
20314
20370
  toolCallId
@@ -21246,6 +21302,31 @@ ${resultContent}${attachmentSummary}`;
21246
21302
  `SELECT COUNT(*) AS c FROM message_queue`
21247
21303
  )).toArray()[0]?.c ?? 0;
21248
21304
  if (stopped || finalizedCount === 0 && queued === 0) {
21305
+ let orphanedClientCalls = false;
21306
+ if (!stopped) {
21307
+ try {
21308
+ orphanedClientCalls = await this.hasInFlightClientCalls();
21309
+ } catch {
21310
+ orphanedClientCalls = false;
21311
+ }
21312
+ }
21313
+ if (orphanedClientCalls) {
21314
+ const owner = await this.getExecutionOwner();
21315
+ const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
21316
+ if (socket) {
21317
+ void this.resendPendingClientCalls(socket);
21318
+ } else if (threadId) {
21319
+ try {
21320
+ const meta = await this.getThreadMetadata(threadId);
21321
+ if (meta.user_id) await this.requestHubWake(meta.user_id, threadId);
21322
+ } catch {
21323
+ }
21324
+ }
21325
+ console.warn(
21326
+ `[DurableThread] Watchdog: pending forwarded call with no executor on ${threadId} \u2014 ${socket ? "re-dispatched to the connected bridge" : "requested owner wake"}`
21327
+ );
21328
+ return report("observing", "none", { queuedMessages: queued });
21329
+ }
21249
21330
  if (finalizedCount > 0) {
21250
21331
  return report("parked", "finalized_parked", {
21251
21332
  queuedMessages: queued,