@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
|
@@ -131,6 +131,18 @@ interface ClientStreamRequest {
|
|
|
131
131
|
meta?: Record<string, unknown>;
|
|
132
132
|
/** Milliseconds to wait for the client to accept the stream. */
|
|
133
133
|
timeoutMs?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Builder-internal reconnect hint, never sent to the client: when the
|
|
136
|
+
* thread's elected execution owner has no live socket, ask this user's hub
|
|
137
|
+
* to summon the owning machine's daemon to `threadId` and wait for the
|
|
138
|
+
* bridge to re-attach before failing the open. Without it a transient
|
|
139
|
+
* disconnect (daemon restart, network blip) surfaces instantly as
|
|
140
|
+
* "workspace unavailable" mid-turn.
|
|
141
|
+
*/
|
|
142
|
+
wake?: {
|
|
143
|
+
threadId: string;
|
|
144
|
+
userId: string;
|
|
145
|
+
};
|
|
134
146
|
}
|
|
135
147
|
/**
|
|
136
148
|
* A live bidirectional byte pipe between the thread runtime and the client.
|
|
@@ -384,7 +396,10 @@ interface ThreadInstance {
|
|
|
384
396
|
getValue<T = unknown>(key: string): Promise<T | null>;
|
|
385
397
|
setValue(key: string, value: unknown): Promise<void>;
|
|
386
398
|
emitThreadEvent?(threadId: string, event: string, data: unknown): void | Promise<void>;
|
|
387
|
-
callClientTool?(request: ClientToolRequest
|
|
399
|
+
callClientTool?(request: ClientToolRequest, durability?: {
|
|
400
|
+
toolCallId: string;
|
|
401
|
+
side: "a" | "b";
|
|
402
|
+
}): Promise<ClientToolResponse>;
|
|
388
403
|
openClientStream?(request: ClientStreamRequest): Promise<ClientStreamResponse>;
|
|
389
404
|
dispatchClientTool?(request: ClientToolRequest, toolCallId: string, side: "a" | "b"): Promise<{
|
|
390
405
|
dispatched: boolean;
|
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-
|
|
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-CcnGEkPh.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
|
@@ -13545,7 +13545,10 @@ var init_ThreadStateImpl = __esm({
|
|
|
13545
13545
|
data: { deferred: true, toolCallId }
|
|
13546
13546
|
};
|
|
13547
13547
|
}
|
|
13548
|
-
const local = await this._threadInstance.callClientTool(
|
|
13548
|
+
const local = await this._threadInstance.callClientTool(
|
|
13549
|
+
request,
|
|
13550
|
+
canPark && toolCallId ? { toolCallId, side } : void 0
|
|
13551
|
+
);
|
|
13549
13552
|
if (local.ok || !this.isNoClient(local)) return local;
|
|
13550
13553
|
const env = this._threadInstance.env;
|
|
13551
13554
|
if (env?.AGENT_BUILDER_THREAD) {
|
|
@@ -13592,7 +13595,9 @@ var init_ThreadStateImpl = __esm({
|
|
|
13592
13595
|
error: "This thread runtime cannot open client streams. Client streaming requires the DurableThread bridge."
|
|
13593
13596
|
};
|
|
13594
13597
|
}
|
|
13595
|
-
const
|
|
13598
|
+
const wakeUserId = this._metadata.user_id ?? null;
|
|
13599
|
+
const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
|
|
13600
|
+
const local = await instance.openClientStream(withWake(this._metadata.id));
|
|
13596
13601
|
if (local.ok || !this.isNoClient(local)) return local;
|
|
13597
13602
|
const env = this._threadInstance.env;
|
|
13598
13603
|
if (!env?.AGENT_BUILDER_THREAD) return local;
|
|
@@ -13603,7 +13608,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
13603
13608
|
try {
|
|
13604
13609
|
const stub = env.AGENT_BUILDER_THREAD.get(env.AGENT_BUILDER_THREAD.idFromName(parentId));
|
|
13605
13610
|
if (typeof stub.proxyStreamOpen === "function") {
|
|
13606
|
-
const opened = await stub.proxyStreamOpen(
|
|
13611
|
+
const opened = await stub.proxyStreamOpen(withWake(parentId));
|
|
13607
13612
|
if (opened.ok && opened.id) {
|
|
13608
13613
|
return { ok: true, duplex: this.buildProxyDuplex(stub, opened.id) };
|
|
13609
13614
|
}
|
|
@@ -28277,7 +28282,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28277
28282
|
}
|
|
28278
28283
|
/** Whether any forwarded call is mid-flight (blocking await or parked). */
|
|
28279
28284
|
async hasInFlightClientCalls() {
|
|
28280
|
-
if (this.pendingClientCalls
|
|
28285
|
+
if ((this.pendingClientCalls?.size ?? 0) > 0) return true;
|
|
28281
28286
|
await this.ensureMigrated();
|
|
28282
28287
|
await this.ensurePendingClientTable();
|
|
28283
28288
|
const cursor = await this.ctx.storage.sql.exec(
|
|
@@ -28336,7 +28341,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28336
28341
|
* always produce a normal tool error (no client connected, timeout, or user
|
|
28337
28342
|
* denial) for the model to reason about.
|
|
28338
28343
|
*/
|
|
28339
|
-
async callClientTool(request) {
|
|
28344
|
+
async callClientTool(request, durability) {
|
|
28340
28345
|
const owner = await this.getExecutionOwner();
|
|
28341
28346
|
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
28342
28347
|
if (!socket) {
|
|
@@ -28346,7 +28351,33 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28346
28351
|
data: { noClient: true }
|
|
28347
28352
|
};
|
|
28348
28353
|
}
|
|
28349
|
-
|
|
28354
|
+
if (durability) {
|
|
28355
|
+
try {
|
|
28356
|
+
await this.ensureMigrated();
|
|
28357
|
+
await this.ensurePendingClientTable();
|
|
28358
|
+
const ownerId = owner?.client_id ?? null;
|
|
28359
|
+
const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
|
|
28360
|
+
const fenced = owner && socketClientId === owner.client_id;
|
|
28361
|
+
await this.ctx.storage.sql.exec(
|
|
28362
|
+
`INSERT OR REPLACE INTO pending_client_calls
|
|
28363
|
+
(tool_call_id, tool, args, request_permission, risk, side, status, created_at,
|
|
28364
|
+
dispatched_client_id, dispatched_generation)
|
|
28365
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
|
|
28366
|
+
durability.toolCallId,
|
|
28367
|
+
request.tool,
|
|
28368
|
+
JSON.stringify(request.args ?? {}),
|
|
28369
|
+
request.requestPermission ?? null,
|
|
28370
|
+
typeof request.risk === "number" ? request.risk : null,
|
|
28371
|
+
durability.side,
|
|
28372
|
+
Date.now(),
|
|
28373
|
+
fenced ? ownerId : null,
|
|
28374
|
+
fenced ? owner.generation : 0
|
|
28375
|
+
);
|
|
28376
|
+
} catch (error) {
|
|
28377
|
+
console.error("[DurableThread] Failed to persist fast-path call (continuing):", error);
|
|
28378
|
+
}
|
|
28379
|
+
}
|
|
28380
|
+
const id = durability?.toolCallId ?? crypto.randomUUID();
|
|
28350
28381
|
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : DEFAULT_CLIENT_TOOL_TIMEOUT_MS;
|
|
28351
28382
|
this.bridgeLastSeen.set(socket, Date.now());
|
|
28352
28383
|
return new Promise((resolve2) => {
|
|
@@ -28397,6 +28428,13 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28397
28428
|
this.pendingClientCalls.delete(id);
|
|
28398
28429
|
if (pending.timer) clearTimeout(pending.timer);
|
|
28399
28430
|
if (pending.liveness) clearInterval(pending.liveness);
|
|
28431
|
+
try {
|
|
28432
|
+
void this.ctx.storage.sql.exec(
|
|
28433
|
+
`UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ? AND status = 'pending'`,
|
|
28434
|
+
id
|
|
28435
|
+
);
|
|
28436
|
+
} catch {
|
|
28437
|
+
}
|
|
28400
28438
|
pending.resolve(res);
|
|
28401
28439
|
}
|
|
28402
28440
|
/** Close and forget a bridge socket we've deemed dead, failing remaining calls. */
|
|
@@ -28648,6 +28686,26 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28648
28686
|
toolStatus,
|
|
28649
28687
|
toolCallId
|
|
28650
28688
|
);
|
|
28689
|
+
const updated = await this.ctx.storage.sql.exec(
|
|
28690
|
+
`SELECT COUNT(*) AS total FROM messages WHERE tool_call_id = ? AND role = 'tool'`,
|
|
28691
|
+
toolCallId
|
|
28692
|
+
);
|
|
28693
|
+
if (Number(updated.toArray()[0]?.total ?? 0) === 0) {
|
|
28694
|
+
const toolRow = await this.ctx.storage.sql.exec(
|
|
28695
|
+
`SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
|
|
28696
|
+
toolCallId
|
|
28697
|
+
);
|
|
28698
|
+
await this.ctx.storage.sql.exec(
|
|
28699
|
+
`INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
|
|
28700
|
+
VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
|
|
28701
|
+
crypto.randomUUID(),
|
|
28702
|
+
content,
|
|
28703
|
+
toolRow.toArray()[0]?.tool ?? null,
|
|
28704
|
+
toolCallId,
|
|
28705
|
+
toolStatus,
|
|
28706
|
+
Date.now() * TIMESTAMP_MULTIPLIER
|
|
28707
|
+
);
|
|
28708
|
+
}
|
|
28651
28709
|
await this.ctx.storage.sql.exec(
|
|
28652
28710
|
`UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
|
|
28653
28711
|
toolCallId
|
|
@@ -28670,10 +28728,40 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28670
28728
|
// they are bound to one bridge socket and never park.
|
|
28671
28729
|
/** Default time to wait for the client to accept a stream open. */
|
|
28672
28730
|
static CLIENT_STREAM_OPEN_TIMEOUT_MS = 3e4;
|
|
28731
|
+
/** Poll cadence while waiting for a disconnected owner's bridge to return. */
|
|
28732
|
+
static OWNER_REATTACH_POLL_MS = 250;
|
|
28733
|
+
/**
|
|
28734
|
+
* The thread HAS an elected execution owner, but its bridge socket is gone
|
|
28735
|
+
* (daemon restart, network blip, DO handoff). Summon the owning machine
|
|
28736
|
+
* through the per-user hub and wait for a bridge to re-attach instead of
|
|
28737
|
+
* failing the stream open instantly — that instant failure is what ended
|
|
28738
|
+
* Sama One turns with "workspace unavailable" whenever a slow tool/approval
|
|
28739
|
+
* outlived the socket. Threads with NO recorded owner (subagent children)
|
|
28740
|
+
* still fail fast so ancestor walk-up routing stays immediate.
|
|
28741
|
+
*/
|
|
28742
|
+
async awaitOwnerBridgeSocket(request) {
|
|
28743
|
+
if (request.wake?.userId) {
|
|
28744
|
+
void this.requestHubWake(request.wake.userId, request.wake.threadId);
|
|
28745
|
+
}
|
|
28746
|
+
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : _DurableThread.CLIENT_STREAM_OPEN_TIMEOUT_MS;
|
|
28747
|
+
const deadline = Date.now() + timeoutMs;
|
|
28748
|
+
while (Date.now() < deadline) {
|
|
28749
|
+
await new Promise(
|
|
28750
|
+
(resolve2) => setTimeout(resolve2, _DurableThread.OWNER_REATTACH_POLL_MS)
|
|
28751
|
+
);
|
|
28752
|
+
const owner = await this.getExecutionOwner();
|
|
28753
|
+
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
28754
|
+
if (socket) return socket;
|
|
28755
|
+
}
|
|
28756
|
+
return null;
|
|
28757
|
+
}
|
|
28673
28758
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
28674
28759
|
async openClientStream(request) {
|
|
28675
28760
|
const owner = await this.getExecutionOwner();
|
|
28676
|
-
|
|
28761
|
+
let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
28762
|
+
if (!socket && owner) {
|
|
28763
|
+
socket = await this.awaitOwnerBridgeSocket(request);
|
|
28764
|
+
}
|
|
28677
28765
|
if (!socket) {
|
|
28678
28766
|
return {
|
|
28679
28767
|
ok: false,
|
|
@@ -28967,6 +29055,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28967
29055
|
console.error("[DurableThread] Failed to arm park TTL (non-fatal):", error);
|
|
28968
29056
|
}
|
|
28969
29057
|
if (!userId) return;
|
|
29058
|
+
await this.requestHubWake(userId, threadId);
|
|
29059
|
+
}
|
|
29060
|
+
/** Ask the user's hub to summon the owning machine's daemon to a thread.
|
|
29061
|
+
* Best-effort: wake failure must never fail the caller's own path. */
|
|
29062
|
+
async requestHubWake(userId, threadId) {
|
|
28970
29063
|
try {
|
|
28971
29064
|
const namespace = this.env.AGENT_BUILDER_USER_HUB;
|
|
28972
29065
|
if (!namespace) return;
|
|
@@ -29550,6 +29643,31 @@ ${resultContent}${attachmentSummary}`;
|
|
|
29550
29643
|
`SELECT COUNT(*) AS c FROM message_queue`
|
|
29551
29644
|
)).toArray()[0]?.c ?? 0;
|
|
29552
29645
|
if (stopped || finalizedCount === 0 && queued === 0) {
|
|
29646
|
+
let orphanedClientCalls = false;
|
|
29647
|
+
if (!stopped) {
|
|
29648
|
+
try {
|
|
29649
|
+
orphanedClientCalls = await this.hasInFlightClientCalls();
|
|
29650
|
+
} catch {
|
|
29651
|
+
orphanedClientCalls = false;
|
|
29652
|
+
}
|
|
29653
|
+
}
|
|
29654
|
+
if (orphanedClientCalls) {
|
|
29655
|
+
const owner = await this.getExecutionOwner();
|
|
29656
|
+
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
29657
|
+
if (socket) {
|
|
29658
|
+
void this.resendPendingClientCalls(socket);
|
|
29659
|
+
} else if (threadId) {
|
|
29660
|
+
try {
|
|
29661
|
+
const meta = await this.getThreadMetadata(threadId);
|
|
29662
|
+
if (meta.user_id) await this.requestHubWake(meta.user_id, threadId);
|
|
29663
|
+
} catch {
|
|
29664
|
+
}
|
|
29665
|
+
}
|
|
29666
|
+
console.warn(
|
|
29667
|
+
`[DurableThread] Watchdog: pending forwarded call with no executor on ${threadId} \u2014 ${socket ? "re-dispatched to the connected bridge" : "requested owner wake"}`
|
|
29668
|
+
);
|
|
29669
|
+
return report("observing", "none", { queuedMessages: queued });
|
|
29670
|
+
}
|
|
29553
29671
|
if (finalizedCount > 0) {
|
|
29554
29672
|
return report("parked", "finalized_parked", {
|
|
29555
29673
|
queuedMessages: queued,
|