@standardagents/builder 0.29.0 → 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/built-in-routes.js +9 -4
- package/dist/built-in-routes.js.map +1 -1
- package/dist/{index-Cp_Zcrhy.d.ts → index-CcnGEkPh.d.ts} +16 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +125 -7
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +21 -3
- package/dist/runtime.js +125 -7
- package/dist/runtime.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/package.json +5 -5
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-
|
|
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-
|
|
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
|
|
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. */
|
|
@@ -1112,6 +1115,18 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
|
|
|
1112
1115
|
} | null>;
|
|
1113
1116
|
/** Default time to wait for the client to accept a stream open. */
|
|
1114
1117
|
private static readonly CLIENT_STREAM_OPEN_TIMEOUT_MS;
|
|
1118
|
+
/** Poll cadence while waiting for a disconnected owner's bridge to return. */
|
|
1119
|
+
private static readonly OWNER_REATTACH_POLL_MS;
|
|
1120
|
+
/**
|
|
1121
|
+
* The thread HAS an elected execution owner, but its bridge socket is gone
|
|
1122
|
+
* (daemon restart, network blip, DO handoff). Summon the owning machine
|
|
1123
|
+
* through the per-user hub and wait for a bridge to re-attach instead of
|
|
1124
|
+
* failing the stream open instantly — that instant failure is what ended
|
|
1125
|
+
* Sama One turns with "workspace unavailable" whenever a slow tool/approval
|
|
1126
|
+
* outlived the socket. Threads with NO recorded owner (subagent children)
|
|
1127
|
+
* still fail fast so ancestor walk-up routing stays immediate.
|
|
1128
|
+
*/
|
|
1129
|
+
private awaitOwnerBridgeSocket;
|
|
1115
1130
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
1116
1131
|
openClientStream(request: ClientStreamRequest): Promise<ClientStreamResponse>;
|
|
1117
1132
|
private proxyStreams;
|
|
@@ -1147,6 +1162,9 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
|
|
|
1147
1162
|
* instead of a silent hang.
|
|
1148
1163
|
*/
|
|
1149
1164
|
notifyClientWaiting(toolCallId: string, threadId: string, userId: string | null): Promise<void>;
|
|
1165
|
+
/** Ask the user's hub to summon the owning machine's daemon to a thread.
|
|
1166
|
+
* Best-effort: wake failure must never fail the caller's own path. */
|
|
1167
|
+
private requestHubWake;
|
|
1150
1168
|
/**
|
|
1151
1169
|
* Park TTL expired: if the call is still pending, settle it as a durable,
|
|
1152
1170
|
* VISIBLE failure — rewrite the waiting placeholder, mark the pending row
|
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(
|
|
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) {
|
|
@@ -13583,7 +13586,9 @@ var init_ThreadStateImpl = __esm({
|
|
|
13583
13586
|
error: "This thread runtime cannot open client streams. Client streaming requires the DurableThread bridge."
|
|
13584
13587
|
};
|
|
13585
13588
|
}
|
|
13586
|
-
const
|
|
13589
|
+
const wakeUserId = this._metadata.user_id ?? null;
|
|
13590
|
+
const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
|
|
13591
|
+
const local = await instance.openClientStream(withWake(this._metadata.id));
|
|
13587
13592
|
if (local.ok || !this.isNoClient(local)) return local;
|
|
13588
13593
|
const env = this._threadInstance.env;
|
|
13589
13594
|
if (!env?.AGENT_BUILDER_THREAD) return local;
|
|
@@ -13594,7 +13599,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
13594
13599
|
try {
|
|
13595
13600
|
const stub = env.AGENT_BUILDER_THREAD.get(env.AGENT_BUILDER_THREAD.idFromName(parentId));
|
|
13596
13601
|
if (typeof stub.proxyStreamOpen === "function") {
|
|
13597
|
-
const opened = await stub.proxyStreamOpen(
|
|
13602
|
+
const opened = await stub.proxyStreamOpen(withWake(parentId));
|
|
13598
13603
|
if (opened.ok && opened.id) {
|
|
13599
13604
|
return { ok: true, duplex: this.buildProxyDuplex(stub, opened.id) };
|
|
13600
13605
|
}
|
|
@@ -19936,7 +19941,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
19936
19941
|
}
|
|
19937
19942
|
/** Whether any forwarded call is mid-flight (blocking await or parked). */
|
|
19938
19943
|
async hasInFlightClientCalls() {
|
|
19939
|
-
if (this.pendingClientCalls
|
|
19944
|
+
if ((this.pendingClientCalls?.size ?? 0) > 0) return true;
|
|
19940
19945
|
await this.ensureMigrated();
|
|
19941
19946
|
await this.ensurePendingClientTable();
|
|
19942
19947
|
const cursor = await this.ctx.storage.sql.exec(
|
|
@@ -19995,7 +20000,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
19995
20000
|
* always produce a normal tool error (no client connected, timeout, or user
|
|
19996
20001
|
* denial) for the model to reason about.
|
|
19997
20002
|
*/
|
|
19998
|
-
async callClientTool(request) {
|
|
20003
|
+
async callClientTool(request, durability) {
|
|
19999
20004
|
const owner = await this.getExecutionOwner();
|
|
20000
20005
|
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
20001
20006
|
if (!socket) {
|
|
@@ -20005,7 +20010,33 @@ ${resultContent}${attachmentSummary}`;
|
|
|
20005
20010
|
data: { noClient: true }
|
|
20006
20011
|
};
|
|
20007
20012
|
}
|
|
20008
|
-
|
|
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();
|
|
20009
20040
|
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : DEFAULT_CLIENT_TOOL_TIMEOUT_MS;
|
|
20010
20041
|
this.bridgeLastSeen.set(socket, Date.now());
|
|
20011
20042
|
return new Promise((resolve) => {
|
|
@@ -20056,6 +20087,13 @@ ${resultContent}${attachmentSummary}`;
|
|
|
20056
20087
|
this.pendingClientCalls.delete(id);
|
|
20057
20088
|
if (pending.timer) clearTimeout(pending.timer);
|
|
20058
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
|
+
}
|
|
20059
20097
|
pending.resolve(res);
|
|
20060
20098
|
}
|
|
20061
20099
|
/** Close and forget a bridge socket we've deemed dead, failing remaining calls. */
|
|
@@ -20307,6 +20345,26 @@ ${resultContent}${attachmentSummary}`;
|
|
|
20307
20345
|
toolStatus,
|
|
20308
20346
|
toolCallId
|
|
20309
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
|
+
}
|
|
20310
20368
|
await this.ctx.storage.sql.exec(
|
|
20311
20369
|
`UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
|
|
20312
20370
|
toolCallId
|
|
@@ -20329,10 +20387,40 @@ ${resultContent}${attachmentSummary}`;
|
|
|
20329
20387
|
// they are bound to one bridge socket and never park.
|
|
20330
20388
|
/** Default time to wait for the client to accept a stream open. */
|
|
20331
20389
|
static CLIENT_STREAM_OPEN_TIMEOUT_MS = 3e4;
|
|
20390
|
+
/** Poll cadence while waiting for a disconnected owner's bridge to return. */
|
|
20391
|
+
static OWNER_REATTACH_POLL_MS = 250;
|
|
20392
|
+
/**
|
|
20393
|
+
* The thread HAS an elected execution owner, but its bridge socket is gone
|
|
20394
|
+
* (daemon restart, network blip, DO handoff). Summon the owning machine
|
|
20395
|
+
* through the per-user hub and wait for a bridge to re-attach instead of
|
|
20396
|
+
* failing the stream open instantly — that instant failure is what ended
|
|
20397
|
+
* Sama One turns with "workspace unavailable" whenever a slow tool/approval
|
|
20398
|
+
* outlived the socket. Threads with NO recorded owner (subagent children)
|
|
20399
|
+
* still fail fast so ancestor walk-up routing stays immediate.
|
|
20400
|
+
*/
|
|
20401
|
+
async awaitOwnerBridgeSocket(request) {
|
|
20402
|
+
if (request.wake?.userId) {
|
|
20403
|
+
void this.requestHubWake(request.wake.userId, request.wake.threadId);
|
|
20404
|
+
}
|
|
20405
|
+
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : _DurableThread.CLIENT_STREAM_OPEN_TIMEOUT_MS;
|
|
20406
|
+
const deadline = Date.now() + timeoutMs;
|
|
20407
|
+
while (Date.now() < deadline) {
|
|
20408
|
+
await new Promise(
|
|
20409
|
+
(resolve) => setTimeout(resolve, _DurableThread.OWNER_REATTACH_POLL_MS)
|
|
20410
|
+
);
|
|
20411
|
+
const owner = await this.getExecutionOwner();
|
|
20412
|
+
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
20413
|
+
if (socket) return socket;
|
|
20414
|
+
}
|
|
20415
|
+
return null;
|
|
20416
|
+
}
|
|
20332
20417
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
20333
20418
|
async openClientStream(request) {
|
|
20334
20419
|
const owner = await this.getExecutionOwner();
|
|
20335
|
-
|
|
20420
|
+
let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
20421
|
+
if (!socket && owner) {
|
|
20422
|
+
socket = await this.awaitOwnerBridgeSocket(request);
|
|
20423
|
+
}
|
|
20336
20424
|
if (!socket) {
|
|
20337
20425
|
return {
|
|
20338
20426
|
ok: false,
|
|
@@ -20626,6 +20714,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
20626
20714
|
console.error("[DurableThread] Failed to arm park TTL (non-fatal):", error);
|
|
20627
20715
|
}
|
|
20628
20716
|
if (!userId) return;
|
|
20717
|
+
await this.requestHubWake(userId, threadId);
|
|
20718
|
+
}
|
|
20719
|
+
/** Ask the user's hub to summon the owning machine's daemon to a thread.
|
|
20720
|
+
* Best-effort: wake failure must never fail the caller's own path. */
|
|
20721
|
+
async requestHubWake(userId, threadId) {
|
|
20629
20722
|
try {
|
|
20630
20723
|
const namespace = this.env.AGENT_BUILDER_USER_HUB;
|
|
20631
20724
|
if (!namespace) return;
|
|
@@ -21209,6 +21302,31 @@ ${resultContent}${attachmentSummary}`;
|
|
|
21209
21302
|
`SELECT COUNT(*) AS c FROM message_queue`
|
|
21210
21303
|
)).toArray()[0]?.c ?? 0;
|
|
21211
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
|
+
}
|
|
21212
21330
|
if (finalizedCount > 0) {
|
|
21213
21331
|
return report("parked", "finalized_parked", {
|
|
21214
21332
|
queuedMessages: queued,
|