@standardagents/builder 0.28.1 → 0.29.1
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 +66 -12
- package/dist/built-in-routes.js.map +1 -1
- package/dist/{index-BzXyIJPA.d.ts → index-BNLDsLsL.d.ts} +22 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +151 -28
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +18 -2
- package/dist/runtime.js +151 -28
- package/dist/runtime.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/package.json +5 -5
|
@@ -77,15 +77,11 @@ declare class StreamManager {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Client-tool forwarding
|
|
80
|
+
* Client-tool forwarding and the legacy client-stream adapter.
|
|
81
81
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
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
|
|
129
|
-
*
|
|
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`). */
|
|
@@ -137,6 +131,18 @@ interface ClientStreamRequest {
|
|
|
137
131
|
meta?: Record<string, unknown>;
|
|
138
132
|
/** Milliseconds to wait for the client to accept the stream. */
|
|
139
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
|
+
};
|
|
140
146
|
}
|
|
141
147
|
/**
|
|
142
148
|
* A live bidirectional byte pipe between the thread runtime and the client.
|
|
@@ -189,9 +195,10 @@ declare function supportsClientForwarding(state: ThreadState): state is ThreadSt
|
|
|
189
195
|
*/
|
|
190
196
|
declare function callClient(state: ThreadState, request: ClientToolRequest): Promise<ClientToolResponse>;
|
|
191
197
|
/**
|
|
192
|
-
* Open a bidirectional byte stream
|
|
193
|
-
*
|
|
194
|
-
*
|
|
198
|
+
* Open a bidirectional byte stream through the legacy Builder-only surface.
|
|
199
|
+
* Requires a live execution-host bridge and never parks durably.
|
|
200
|
+
*
|
|
201
|
+
* @deprecated Portable agent code should use `state.runtimeStreams?.open()`.
|
|
195
202
|
*/
|
|
196
203
|
declare function callClientStream(state: ThreadState, request: ClientStreamRequest): Promise<ClientStreamResponse>;
|
|
197
204
|
/** 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-
|
|
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-BNLDsLsL.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
|
-
*
|
|
13519
|
-
*
|
|
13520
|
-
*
|
|
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;
|
|
@@ -13529,7 +13592,9 @@ var init_ThreadStateImpl = __esm({
|
|
|
13529
13592
|
error: "This thread runtime cannot open client streams. Client streaming requires the DurableThread bridge."
|
|
13530
13593
|
};
|
|
13531
13594
|
}
|
|
13532
|
-
const
|
|
13595
|
+
const wakeUserId = this._metadata.user_id ?? null;
|
|
13596
|
+
const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
|
|
13597
|
+
const local = await instance.openClientStream(withWake(this._metadata.id));
|
|
13533
13598
|
if (local.ok || !this.isNoClient(local)) return local;
|
|
13534
13599
|
const env = this._threadInstance.env;
|
|
13535
13600
|
if (!env?.AGENT_BUILDER_THREAD) return local;
|
|
@@ -13540,12 +13605,16 @@ var init_ThreadStateImpl = __esm({
|
|
|
13540
13605
|
try {
|
|
13541
13606
|
const stub = env.AGENT_BUILDER_THREAD.get(env.AGENT_BUILDER_THREAD.idFromName(parentId));
|
|
13542
13607
|
if (typeof stub.proxyStreamOpen === "function") {
|
|
13543
|
-
const opened = await stub.proxyStreamOpen(
|
|
13608
|
+
const opened = await stub.proxyStreamOpen(withWake(parentId));
|
|
13544
13609
|
if (opened.ok && opened.id) {
|
|
13545
13610
|
return { ok: true, duplex: this.buildProxyDuplex(stub, opened.id) };
|
|
13546
13611
|
}
|
|
13547
13612
|
if (opened.error && !opened.noClient) {
|
|
13548
|
-
return {
|
|
13613
|
+
return {
|
|
13614
|
+
ok: false,
|
|
13615
|
+
error: opened.error,
|
|
13616
|
+
data: opened.code ? { code: opened.code } : void 0
|
|
13617
|
+
};
|
|
13549
13618
|
}
|
|
13550
13619
|
}
|
|
13551
13620
|
} catch {
|
|
@@ -13564,6 +13633,12 @@ var init_ThreadStateImpl = __esm({
|
|
|
13564
13633
|
let settleDone;
|
|
13565
13634
|
const done = new Promise((r) => settleDone = r);
|
|
13566
13635
|
let closed = false;
|
|
13636
|
+
let settled = false;
|
|
13637
|
+
const settle = (result) => {
|
|
13638
|
+
if (settled) return;
|
|
13639
|
+
settled = true;
|
|
13640
|
+
settleDone(result);
|
|
13641
|
+
};
|
|
13567
13642
|
const readable = new ReadableStream({
|
|
13568
13643
|
async pull(controller) {
|
|
13569
13644
|
while (!closed) {
|
|
@@ -13572,7 +13647,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
13572
13647
|
res = await stub.proxyStreamPull(id);
|
|
13573
13648
|
} catch (error) {
|
|
13574
13649
|
controller.error(error instanceof Error ? error : new Error(String(error)));
|
|
13575
|
-
|
|
13650
|
+
settle({ ok: false, error: "proxy stream pull failed" });
|
|
13576
13651
|
return;
|
|
13577
13652
|
}
|
|
13578
13653
|
for (const chunk of res.chunks) {
|
|
@@ -13580,7 +13655,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
13580
13655
|
}
|
|
13581
13656
|
if (res.done) {
|
|
13582
13657
|
controller.close();
|
|
13583
|
-
|
|
13658
|
+
settle(res.error ? { ok: false, error: res.error } : { ok: true });
|
|
13584
13659
|
return;
|
|
13585
13660
|
}
|
|
13586
13661
|
if (res.chunks.length > 0) return;
|
|
@@ -13591,6 +13666,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
13591
13666
|
closed = true;
|
|
13592
13667
|
void stub.proxyStreamAbort(id).catch(() => {
|
|
13593
13668
|
});
|
|
13669
|
+
settle({ ok: false, error: "proxy stream reader canceled" });
|
|
13594
13670
|
}
|
|
13595
13671
|
});
|
|
13596
13672
|
const writable = new WritableStream({
|
|
@@ -13605,16 +13681,18 @@ var init_ThreadStateImpl = __esm({
|
|
|
13605
13681
|
closed = true;
|
|
13606
13682
|
void stub.proxyStreamAbort(id).catch(() => {
|
|
13607
13683
|
});
|
|
13684
|
+
settle({ ok: false, error: "proxy stream writer aborted" });
|
|
13608
13685
|
}
|
|
13609
13686
|
});
|
|
13610
13687
|
return {
|
|
13611
13688
|
readable,
|
|
13612
13689
|
writable,
|
|
13613
13690
|
close: (error) => {
|
|
13691
|
+
if (closed) return;
|
|
13614
13692
|
closed = true;
|
|
13615
13693
|
void stub.proxyStreamAbort(id).catch(() => {
|
|
13616
13694
|
});
|
|
13617
|
-
|
|
13695
|
+
settle(error ? { ok: false, error } : { ok: true });
|
|
13618
13696
|
},
|
|
13619
13697
|
done
|
|
13620
13698
|
};
|
|
@@ -28588,23 +28666,51 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28588
28666
|
}
|
|
28589
28667
|
return { side };
|
|
28590
28668
|
}
|
|
28591
|
-
// ───
|
|
28592
|
-
//
|
|
28593
|
-
//
|
|
28594
|
-
//
|
|
28595
|
-
// duplex), so the client only ever relays ciphertext. Streams are bound to a
|
|
28596
|
-
// live socket and never park.
|
|
28669
|
+
// ─── Runtime byte streams ──────────────────────────────────────────────────
|
|
28670
|
+
// The existing bridge frames carry opaque capability metadata and bytes
|
|
28671
|
+
// between the runtime and its execution owner. Streams are live resources:
|
|
28672
|
+
// they are bound to one bridge socket and never park.
|
|
28597
28673
|
/** Default time to wait for the client to accept a stream open. */
|
|
28598
28674
|
static CLIENT_STREAM_OPEN_TIMEOUT_MS = 3e4;
|
|
28675
|
+
/** Poll cadence while waiting for a disconnected owner's bridge to return. */
|
|
28676
|
+
static OWNER_REATTACH_POLL_MS = 250;
|
|
28677
|
+
/**
|
|
28678
|
+
* The thread HAS an elected execution owner, but its bridge socket is gone
|
|
28679
|
+
* (daemon restart, network blip, DO handoff). Summon the owning machine
|
|
28680
|
+
* through the per-user hub and wait for a bridge to re-attach instead of
|
|
28681
|
+
* failing the stream open instantly — that instant failure is what ended
|
|
28682
|
+
* Sama One turns with "workspace unavailable" whenever a slow tool/approval
|
|
28683
|
+
* outlived the socket. Threads with NO recorded owner (subagent children)
|
|
28684
|
+
* still fail fast so ancestor walk-up routing stays immediate.
|
|
28685
|
+
*/
|
|
28686
|
+
async awaitOwnerBridgeSocket(request) {
|
|
28687
|
+
if (request.wake?.userId) {
|
|
28688
|
+
void this.requestHubWake(request.wake.userId, request.wake.threadId);
|
|
28689
|
+
}
|
|
28690
|
+
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : _DurableThread.CLIENT_STREAM_OPEN_TIMEOUT_MS;
|
|
28691
|
+
const deadline = Date.now() + timeoutMs;
|
|
28692
|
+
while (Date.now() < deadline) {
|
|
28693
|
+
await new Promise(
|
|
28694
|
+
(resolve2) => setTimeout(resolve2, _DurableThread.OWNER_REATTACH_POLL_MS)
|
|
28695
|
+
);
|
|
28696
|
+
const owner = await this.getExecutionOwner();
|
|
28697
|
+
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
28698
|
+
if (socket) return socket;
|
|
28699
|
+
}
|
|
28700
|
+
return null;
|
|
28701
|
+
}
|
|
28599
28702
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
28600
28703
|
async openClientStream(request) {
|
|
28601
28704
|
const owner = await this.getExecutionOwner();
|
|
28602
|
-
|
|
28705
|
+
let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
28706
|
+
if (!socket && owner) {
|
|
28707
|
+
socket = await this.awaitOwnerBridgeSocket(request);
|
|
28708
|
+
}
|
|
28603
28709
|
if (!socket) {
|
|
28604
28710
|
return {
|
|
28605
28711
|
ok: false,
|
|
28606
28712
|
error: "The workspace is not available right now. Try again in a moment.",
|
|
28607
|
-
data: { noClient: true }
|
|
28713
|
+
data: { noClient: true, code: "unavailable" }
|
|
28608
28714
|
};
|
|
28609
28715
|
}
|
|
28610
28716
|
const id = crypto.randomUUID();
|
|
@@ -28637,7 +28743,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28637
28743
|
state.openTimer = setTimeout(() => {
|
|
28638
28744
|
resolve2({
|
|
28639
28745
|
ok: false,
|
|
28640
|
-
error: `The client did not accept the ${request.tool} stream within ${Math.round(timeoutMs / 1e3)}s
|
|
28746
|
+
error: `The client did not accept the ${request.tool} stream within ${Math.round(timeoutMs / 1e3)}s.`,
|
|
28747
|
+
code: "timeout"
|
|
28641
28748
|
});
|
|
28642
28749
|
}, timeoutMs);
|
|
28643
28750
|
try {
|
|
@@ -28652,7 +28759,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28652
28759
|
} catch (error) {
|
|
28653
28760
|
resolve2({
|
|
28654
28761
|
ok: false,
|
|
28655
|
-
error: `Failed to reach the client: ${error instanceof Error ? error.message : String(error)}
|
|
28762
|
+
error: `Failed to reach the client: ${error instanceof Error ? error.message : String(error)}`,
|
|
28763
|
+
code: "unavailable"
|
|
28656
28764
|
});
|
|
28657
28765
|
}
|
|
28658
28766
|
});
|
|
@@ -28665,7 +28773,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28665
28773
|
this.clientStreams.delete(id);
|
|
28666
28774
|
state.settled = true;
|
|
28667
28775
|
settleDone({ ok: false, error: openResult.error });
|
|
28668
|
-
return {
|
|
28776
|
+
return {
|
|
28777
|
+
ok: false,
|
|
28778
|
+
error: openResult.error ?? "Stream open failed.",
|
|
28779
|
+
data: openResult.code ? { code: openResult.code } : void 0
|
|
28780
|
+
};
|
|
28669
28781
|
}
|
|
28670
28782
|
const sendChunked = (bytes) => {
|
|
28671
28783
|
for (let i = 0; i < bytes.length; i += CLIENT_STREAM_CHUNK_BYTES) {
|
|
@@ -28709,8 +28821,14 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28709
28821
|
async proxyStreamOpen(request) {
|
|
28710
28822
|
const opened = await this.openClientStream(request);
|
|
28711
28823
|
if (!opened.ok) {
|
|
28712
|
-
const
|
|
28713
|
-
|
|
28824
|
+
const failureData = opened.data;
|
|
28825
|
+
const noClient = failureData?.noClient === true;
|
|
28826
|
+
return {
|
|
28827
|
+
ok: false,
|
|
28828
|
+
error: opened.error,
|
|
28829
|
+
noClient,
|
|
28830
|
+
code: failureData?.code
|
|
28831
|
+
};
|
|
28714
28832
|
}
|
|
28715
28833
|
const id = crypto.randomUUID();
|
|
28716
28834
|
const duplex = opened.duplex;
|
|
@@ -28781,12 +28899,12 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28781
28899
|
}
|
|
28782
28900
|
}
|
|
28783
28901
|
/** Settle and remove a client stream exactly once. */
|
|
28784
|
-
settleClientStream(id, result) {
|
|
28902
|
+
settleClientStream(id, result, openCode) {
|
|
28785
28903
|
const state = this.clientStreams.get(id);
|
|
28786
28904
|
if (!state) return;
|
|
28787
28905
|
this.clientStreams.delete(id);
|
|
28788
28906
|
if (state.openTimer) clearTimeout(state.openTimer);
|
|
28789
|
-
if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed." });
|
|
28907
|
+
if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed.", code: openCode });
|
|
28790
28908
|
if (!state.settled) {
|
|
28791
28909
|
state.settled = true;
|
|
28792
28910
|
try {
|
|
@@ -28830,7 +28948,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28830
28948
|
}
|
|
28831
28949
|
case "stream_error": {
|
|
28832
28950
|
const error = typeof parsed.error === "string" ? parsed.error : "Client stream error.";
|
|
28833
|
-
this.settleClientStream(id, { ok: false, error });
|
|
28951
|
+
this.settleClientStream(id, { ok: false, error }, "refused");
|
|
28834
28952
|
return;
|
|
28835
28953
|
}
|
|
28836
28954
|
case "stream_data": {
|
|
@@ -28857,7 +28975,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28857
28975
|
failClientStreamsForSocket(ws) {
|
|
28858
28976
|
for (const [id, state] of Array.from(this.clientStreams.entries())) {
|
|
28859
28977
|
if (state.socket === ws) {
|
|
28860
|
-
this.settleClientStream(id, { ok: false, error: "The client connection was lost." });
|
|
28978
|
+
this.settleClientStream(id, { ok: false, error: "The client connection was lost." }, "unavailable");
|
|
28861
28979
|
}
|
|
28862
28980
|
}
|
|
28863
28981
|
}
|
|
@@ -28881,6 +28999,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28881
28999
|
console.error("[DurableThread] Failed to arm park TTL (non-fatal):", error);
|
|
28882
29000
|
}
|
|
28883
29001
|
if (!userId) return;
|
|
29002
|
+
await this.requestHubWake(userId, threadId);
|
|
29003
|
+
}
|
|
29004
|
+
/** Ask the user's hub to summon the owning machine's daemon to a thread.
|
|
29005
|
+
* Best-effort: wake failure must never fail the caller's own path. */
|
|
29006
|
+
async requestHubWake(userId, threadId) {
|
|
28884
29007
|
try {
|
|
28885
29008
|
const namespace = this.env.AGENT_BUILDER_USER_HUB;
|
|
28886
29009
|
if (!namespace) return;
|