@standardagents/builder 0.40.5-next.3b25e2a → 0.40.6
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 +62 -102
- package/dist/built-in-routes.js.map +1 -1
- package/dist/{index-0NI3m5DR.d.ts → index-DYhbT5Eq.d.ts} +44 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +393 -246
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +90 -19
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +20 -21
- package/dist/runtime.js +580 -495
- package/dist/runtime.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -5083,9 +5083,7 @@ Message one with subagent_message(referenceId, message).`
|
|
|
5083
5083
|
if (resumable) {
|
|
5084
5084
|
await this.settleResumableChildAfterRun(
|
|
5085
5085
|
state,
|
|
5086
|
-
|
|
5087
|
-
childThreadId,
|
|
5088
|
-
childResult
|
|
5086
|
+
childThreadId
|
|
5089
5087
|
);
|
|
5090
5088
|
} else if (stub.terminateThread) {
|
|
5091
5089
|
await stub.terminateThread(childThreadId);
|
|
@@ -5097,7 +5095,7 @@ Message one with subagent_message(referenceId, message).`
|
|
|
5097
5095
|
attachments: copiedAttachments,
|
|
5098
5096
|
subagent_id: childThreadId
|
|
5099
5097
|
};
|
|
5100
|
-
return resumable
|
|
5098
|
+
return resumable ? this.withReferenceInToolResult(toolResult, childThreadId) : toolResult;
|
|
5101
5099
|
}
|
|
5102
5100
|
const backgroundRun = (async () => {
|
|
5103
5101
|
try {
|
|
@@ -5132,9 +5130,7 @@ Message one with subagent_message(referenceId, message).`
|
|
|
5132
5130
|
if (resumable) {
|
|
5133
5131
|
await this.settleResumableChildAfterRun(
|
|
5134
5132
|
state,
|
|
5135
|
-
|
|
5136
|
-
childThreadId,
|
|
5137
|
-
childResult
|
|
5133
|
+
childThreadId
|
|
5138
5134
|
);
|
|
5139
5135
|
} else if (stub.terminateThread) {
|
|
5140
5136
|
await stub.terminateThread(childThreadId);
|
|
@@ -5529,18 +5525,8 @@ Message one with subagent_message(referenceId, message).`
|
|
|
5529
5525
|
JSON.stringify(next)
|
|
5530
5526
|
);
|
|
5531
5527
|
}
|
|
5532
|
-
static
|
|
5533
|
-
|
|
5534
|
-
}
|
|
5535
|
-
static async settleResumableChildAfterRun(state, childThread, reference, result) {
|
|
5536
|
-
if (!this.isTerminalSubagentResult(result)) {
|
|
5537
|
-
await this.updateChildStatus(state, reference, "idle");
|
|
5538
|
-
return;
|
|
5539
|
-
}
|
|
5540
|
-
await this.updateChildStatus(state, reference, "terminated");
|
|
5541
|
-
if (typeof childThread.terminateThread === "function") {
|
|
5542
|
-
await childThread.terminateThread(reference);
|
|
5543
|
-
}
|
|
5528
|
+
static async settleResumableChildAfterRun(state, reference) {
|
|
5529
|
+
await this.updateChildStatus(state, reference, "idle");
|
|
5544
5530
|
}
|
|
5545
5531
|
static async buildChildInvocationMessages(state, childThread, content, receivesMessages, parentAttachments, initialArguments) {
|
|
5546
5532
|
const copiedAttachments = await this.copyAttachmentsBetweenThreads(
|
|
@@ -14736,27 +14722,51 @@ var init_ThreadStateImpl = __esm({
|
|
|
14736
14722
|
const withWake = (threadId) => wakeUserId ? { ...request, wake: { threadId, userId: wakeUserId } } : request;
|
|
14737
14723
|
const local = await instance.openClientStream(withWake(this._metadata.id));
|
|
14738
14724
|
if (local.ok || !this.isNoClient(local)) return local;
|
|
14739
|
-
|
|
14740
|
-
|
|
14725
|
+
if (typeof instance.createProxyStreamReceiver !== "function" || typeof instance.proxyStreamReceive !== "function") {
|
|
14726
|
+
return local;
|
|
14727
|
+
}
|
|
14728
|
+
let abortRemote = null;
|
|
14729
|
+
const receiver = instance.createProxyStreamReceiver((error) => abortRemote?.(error));
|
|
14730
|
+
const discardReceiver = async (error) => {
|
|
14731
|
+
await instance.proxyStreamReceive(receiver.id, { type: "abort", error });
|
|
14732
|
+
};
|
|
14733
|
+
const namespace = instance.env.AGENT_BUILDER_THREAD;
|
|
14741
14734
|
const seen = /* @__PURE__ */ new Set([this._metadata.id]);
|
|
14742
14735
|
let parentId = await this.resolveParentThreadId(this._metadata.id);
|
|
14743
14736
|
while (parentId && !seen.has(parentId)) {
|
|
14744
14737
|
seen.add(parentId);
|
|
14745
14738
|
try {
|
|
14746
|
-
const stub =
|
|
14739
|
+
const stub = namespace.get(namespace.idFromName(parentId));
|
|
14747
14740
|
if (typeof stub.proxyStreamOpen === "function") {
|
|
14748
|
-
const opened = await stub.proxyStreamOpen(withWake(parentId)
|
|
14741
|
+
const opened = await stub.proxyStreamOpen(withWake(parentId), {
|
|
14742
|
+
threadId: this._metadata.id,
|
|
14743
|
+
receiverId: receiver.id
|
|
14744
|
+
});
|
|
14749
14745
|
if (opened.ok && opened.id) {
|
|
14746
|
+
if (opened.eventDriven !== true) {
|
|
14747
|
+
await stub.proxyStreamAbort(opened.id).catch(() => {
|
|
14748
|
+
});
|
|
14749
|
+
await discardReceiver("The ancestor runtime does not support event-driven streams.");
|
|
14750
|
+
return {
|
|
14751
|
+
ok: false,
|
|
14752
|
+
error: "The ancestor runtime does not support event-driven streams.",
|
|
14753
|
+
data: { code: "unsupported" }
|
|
14754
|
+
};
|
|
14755
|
+
}
|
|
14756
|
+
abortRemote = (error) => {
|
|
14757
|
+
void stub.proxyStreamAbort(opened.id).catch(() => {
|
|
14758
|
+
});
|
|
14759
|
+
if (error) {
|
|
14760
|
+
void instance.proxyStreamReceive(receiver.id, { type: "abort", error });
|
|
14761
|
+
}
|
|
14762
|
+
};
|
|
14750
14763
|
return {
|
|
14751
14764
|
ok: true,
|
|
14752
|
-
duplex: this.
|
|
14753
|
-
stub,
|
|
14754
|
-
opened.id,
|
|
14755
|
-
opened.parkedPull === true
|
|
14756
|
-
)
|
|
14765
|
+
duplex: this.buildEventProxyDuplex(stub, opened.id, receiver, instance)
|
|
14757
14766
|
};
|
|
14758
14767
|
}
|
|
14759
14768
|
if (opened.error && !opened.noClient) {
|
|
14769
|
+
await discardReceiver(opened.error);
|
|
14760
14770
|
return {
|
|
14761
14771
|
ok: false,
|
|
14762
14772
|
error: opened.error,
|
|
@@ -14768,69 +14778,21 @@ var init_ThreadStateImpl = __esm({
|
|
|
14768
14778
|
}
|
|
14769
14779
|
parentId = await this.resolveParentThreadId(parentId);
|
|
14770
14780
|
}
|
|
14781
|
+
await discardReceiver(local.error ?? "No execution-owner bridge is available.");
|
|
14771
14782
|
return local;
|
|
14772
14783
|
}
|
|
14773
14784
|
/**
|
|
14774
14785
|
* Build a client duplex that bridges to a stream opened on an ancestor DO
|
|
14775
|
-
* (the bridge holder)
|
|
14776
|
-
*
|
|
14777
|
-
* error, or abort, so an idle open stream performs zero repeated pulls.
|
|
14778
|
-
* Older ancestors use the bounded long-pull compatibility path. `writable`
|
|
14779
|
-
* forwards each chunk with `proxyStreamWrite`; teardown calls
|
|
14780
|
-
* `proxyStreamAbort`.
|
|
14786
|
+
* (the bridge holder). The readable is the receiver registered on this DO;
|
|
14787
|
+
* writes and terminal actions are the only child-to-ancestor RPCs.
|
|
14781
14788
|
*/
|
|
14782
|
-
|
|
14783
|
-
buildProxyDuplex(stub, id, supportsParkedPull = false) {
|
|
14784
|
-
let settleDone;
|
|
14785
|
-
const done = new Promise((r) => settleDone = r);
|
|
14789
|
+
buildEventProxyDuplex(stub, id, receiver, instance) {
|
|
14786
14790
|
let closed = false;
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
settleDone(result);
|
|
14791
|
+
const settleLocal = (error) => {
|
|
14792
|
+
if (typeof instance.proxyStreamReceive !== "function") return;
|
|
14793
|
+
const frame = error ? { type: "abort", error } : { type: "end" };
|
|
14794
|
+
void instance.proxyStreamReceive(receiver.id, frame);
|
|
14792
14795
|
};
|
|
14793
|
-
const readable = new ReadableStream({
|
|
14794
|
-
async pull(controller) {
|
|
14795
|
-
while (!closed) {
|
|
14796
|
-
const pullStartedAt = Date.now();
|
|
14797
|
-
let res;
|
|
14798
|
-
try {
|
|
14799
|
-
res = await stub.proxyStreamPull(
|
|
14800
|
-
id,
|
|
14801
|
-
supportsParkedPull ? void 0 : _ThreadStateImpl.PROXY_PULL_MAX_WAIT_MS
|
|
14802
|
-
);
|
|
14803
|
-
} catch (error) {
|
|
14804
|
-
controller.error(error instanceof Error ? error : new Error(String(error)));
|
|
14805
|
-
settle({ ok: false, error: "proxy stream pull failed" });
|
|
14806
|
-
return;
|
|
14807
|
-
}
|
|
14808
|
-
for (const chunk of res.chunks) {
|
|
14809
|
-
controller.enqueue(decodeStreamChunk(chunk));
|
|
14810
|
-
}
|
|
14811
|
-
if (res.done) {
|
|
14812
|
-
controller.close();
|
|
14813
|
-
settle(res.error ? { ok: false, error: res.error } : { ok: true });
|
|
14814
|
-
return;
|
|
14815
|
-
}
|
|
14816
|
-
if (res.chunks.length > 0) return;
|
|
14817
|
-
if (supportsParkedPull) {
|
|
14818
|
-
controller.error(new Error("parked proxy pull returned without data"));
|
|
14819
|
-
settle({ ok: false, error: "parked proxy pull returned without data" });
|
|
14820
|
-
return;
|
|
14821
|
-
}
|
|
14822
|
-
if (Date.now() - pullStartedAt < 250) {
|
|
14823
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
14824
|
-
}
|
|
14825
|
-
}
|
|
14826
|
-
},
|
|
14827
|
-
cancel() {
|
|
14828
|
-
closed = true;
|
|
14829
|
-
void stub.proxyStreamAbort(id).catch(() => {
|
|
14830
|
-
});
|
|
14831
|
-
settle({ ok: false, error: "proxy stream reader canceled" });
|
|
14832
|
-
}
|
|
14833
|
-
});
|
|
14834
14796
|
const writable = new WritableStream({
|
|
14835
14797
|
async write(chunk) {
|
|
14836
14798
|
await stub.proxyStreamWrite(id, encodeStreamChunk(chunk));
|
|
@@ -14843,20 +14805,20 @@ var init_ThreadStateImpl = __esm({
|
|
|
14843
14805
|
closed = true;
|
|
14844
14806
|
void stub.proxyStreamAbort(id).catch(() => {
|
|
14845
14807
|
});
|
|
14846
|
-
|
|
14808
|
+
settleLocal("The proxy stream writer was aborted.");
|
|
14847
14809
|
}
|
|
14848
14810
|
});
|
|
14849
14811
|
return {
|
|
14850
|
-
readable,
|
|
14812
|
+
readable: receiver.readable,
|
|
14851
14813
|
writable,
|
|
14852
14814
|
close: (error) => {
|
|
14853
14815
|
if (closed) return;
|
|
14854
14816
|
closed = true;
|
|
14855
14817
|
void stub.proxyStreamAbort(id).catch(() => {
|
|
14856
14818
|
});
|
|
14857
|
-
|
|
14819
|
+
settleLocal(error);
|
|
14858
14820
|
},
|
|
14859
|
-
done
|
|
14821
|
+
done: receiver.done
|
|
14860
14822
|
};
|
|
14861
14823
|
}
|
|
14862
14824
|
isNoClient(res) {
|
|
@@ -16819,7 +16781,7 @@ function createHmrReloader() {
|
|
|
16819
16781
|
const isConfigFile = isModelFile || isProviderFile || isPromptFile || isAgentFile || isEffectFile;
|
|
16820
16782
|
if (!isToolFile && !isThreadApiFile && !isHookFile && !isConfigFile && !isUserEndpointFile) return;
|
|
16821
16783
|
reloader.invalidateSourceFileModule(server, file);
|
|
16822
|
-
if (isConfigFile || isToolFile) regenerateTypes();
|
|
16784
|
+
if (isConfigFile || isToolFile || isHookFile) regenerateTypes();
|
|
16823
16785
|
if (isToolFile) await reloader.reloadToolsModule(server);
|
|
16824
16786
|
if (isModelFile) await reloader.reloadModelsModule(server);
|
|
16825
16787
|
if (isProviderFile) await reloader.reloadProvidersModule(server);
|
|
@@ -18947,6 +18909,29 @@ async function scanPromptsDirectory(dir) {
|
|
|
18947
18909
|
async function scanAgentsDirectory(dir) {
|
|
18948
18910
|
return scanConfigDirectory(dir, /export\s+default\s+defineAgent/);
|
|
18949
18911
|
}
|
|
18912
|
+
function findLegacyRootAgentFiles(agentsDir) {
|
|
18913
|
+
const agentsRoot = path9__default.dirname(agentsDir);
|
|
18914
|
+
if (path9__default.basename(agentsDir) !== "agents" || path9__default.basename(agentsRoot) !== "agents") {
|
|
18915
|
+
return [];
|
|
18916
|
+
}
|
|
18917
|
+
if (!fs9__default.existsSync(agentsRoot)) return [];
|
|
18918
|
+
return fs9__default.readdirSync(agentsRoot, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => path9__default.join(agentsRoot, entry.name)).filter((filePath) => {
|
|
18919
|
+
try {
|
|
18920
|
+
return /export\s+default\s+defineAgent/.test(fs9__default.readFileSync(filePath, "utf8"));
|
|
18921
|
+
} catch {
|
|
18922
|
+
return false;
|
|
18923
|
+
}
|
|
18924
|
+
}).sort();
|
|
18925
|
+
}
|
|
18926
|
+
function warnLegacyRootAgentFiles(agentsDir) {
|
|
18927
|
+
const expectedDir = path9__default.relative(process.cwd(), agentsDir).replace(/\\/g, "/");
|
|
18928
|
+
for (const filePath of findLegacyRootAgentFiles(agentsDir)) {
|
|
18929
|
+
const source = path9__default.relative(process.cwd(), filePath).replace(/\\/g, "/");
|
|
18930
|
+
console.warn(
|
|
18931
|
+
`[agentbuilder] Agent definition "${source}" is outside the scanned agent directory. Move it to "${expectedDir}/" so it is registered.`
|
|
18932
|
+
);
|
|
18933
|
+
}
|
|
18934
|
+
}
|
|
18950
18935
|
async function scanEffectsDirectory(dir) {
|
|
18951
18936
|
const effects = [];
|
|
18952
18937
|
if (!fs9__default.existsSync(dir)) {
|
|
@@ -22657,7 +22642,32 @@ function isHostedInstance(env) {
|
|
|
22657
22642
|
}
|
|
22658
22643
|
return Boolean(value);
|
|
22659
22644
|
}
|
|
22660
|
-
function
|
|
22645
|
+
function normalizeTrustedDevHosts(hosts) {
|
|
22646
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
22647
|
+
for (const rawHost of hosts ?? []) {
|
|
22648
|
+
const candidate = rawHost.trim().toLowerCase().replace(/\.$/, "");
|
|
22649
|
+
try {
|
|
22650
|
+
const parsed = new URL(`http://${candidate}`);
|
|
22651
|
+
if (!candidate || candidate.includes("*") || parsed.hostname.toLowerCase().replace(/\.$/, "") !== candidate || parsed.port || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
|
|
22652
|
+
throw new Error("invalid hostname");
|
|
22653
|
+
}
|
|
22654
|
+
} catch {
|
|
22655
|
+
throw new Error(
|
|
22656
|
+
`Invalid trusted development hostname "${rawHost}". Use an exact hostname without a scheme, port, path, or wildcard.`
|
|
22657
|
+
);
|
|
22658
|
+
}
|
|
22659
|
+
normalized.add(candidate);
|
|
22660
|
+
}
|
|
22661
|
+
return [...normalized];
|
|
22662
|
+
}
|
|
22663
|
+
function compiledTrustedDevHosts() {
|
|
22664
|
+
try {
|
|
22665
|
+
return typeof __STANDARD_AGENTS_TRUSTED_DEV_HOSTS__ !== "undefined" && Array.isArray(__STANDARD_AGENTS_TRUSTED_DEV_HOSTS__) ? __STANDARD_AGENTS_TRUSTED_DEV_HOSTS__ : [];
|
|
22666
|
+
} catch {
|
|
22667
|
+
return [];
|
|
22668
|
+
}
|
|
22669
|
+
}
|
|
22670
|
+
function isTrustedLocalDevelopmentRequest(request, env, trustedDevHosts = compiledTrustedDevHosts()) {
|
|
22661
22671
|
if (isHostedInstance(env)) return false;
|
|
22662
22672
|
let hostname;
|
|
22663
22673
|
try {
|
|
@@ -22665,7 +22675,7 @@ function isTrustedLocalDevelopmentRequest(request, env) {
|
|
|
22665
22675
|
} catch {
|
|
22666
22676
|
return false;
|
|
22667
22677
|
}
|
|
22668
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local");
|
|
22678
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local") || trustedDevHosts.some((trusted) => trusted.trim().toLowerCase() === hostname);
|
|
22669
22679
|
}
|
|
22670
22680
|
function isPublicApiRoute(routePath, request, env) {
|
|
22671
22681
|
if (PUBLIC_ROUTES.has(routePath)) return true;
|
|
@@ -22814,7 +22824,8 @@ function createDevMiddleware(server, context) {
|
|
|
22814
22824
|
installedProviderPackageMap,
|
|
22815
22825
|
hmr,
|
|
22816
22826
|
sourceDirs,
|
|
22817
|
-
regenerateTypes
|
|
22827
|
+
regenerateTypes,
|
|
22828
|
+
trustedDevHosts
|
|
22818
22829
|
} = context;
|
|
22819
22830
|
server.watcher.on("add", async (file) => {
|
|
22820
22831
|
await hmr.handleFileChange(file, server, sourceDirs, regenerateTypes);
|
|
@@ -22848,7 +22859,8 @@ function createDevMiddleware(server, context) {
|
|
|
22848
22859
|
try {
|
|
22849
22860
|
trustedLocalHost = isTrustedLocalDevelopmentRequest(
|
|
22850
22861
|
new Request(`http://${req.headers.host || "invalid.remote"}${routePath}`),
|
|
22851
|
-
{}
|
|
22862
|
+
{},
|
|
22863
|
+
trustedDevHosts
|
|
22852
22864
|
);
|
|
22853
22865
|
} catch {
|
|
22854
22866
|
}
|
|
@@ -24082,7 +24094,7 @@ var firstPartyProviderPackages = [
|
|
|
24082
24094
|
"@standardagents/openrouter",
|
|
24083
24095
|
"@standardagents/xai"
|
|
24084
24096
|
];
|
|
24085
|
-
var
|
|
24097
|
+
var baseDepsToInclude = [
|
|
24086
24098
|
"zod",
|
|
24087
24099
|
"zod/v3",
|
|
24088
24100
|
"zod/v4",
|
|
@@ -24093,17 +24105,24 @@ var depsToInclude = [
|
|
|
24093
24105
|
// a root dep). The `parent > child` form pre-bundles it at startup so Vite
|
|
24094
24106
|
// doesn't discover it mid-request and reload the worker (which would destroy
|
|
24095
24107
|
// an in-flight Durable Object execution and drop the client bridge).
|
|
24096
|
-
"@standardagents/openrouter > @openrouter/sdk",
|
|
24097
24108
|
"magic-string",
|
|
24098
24109
|
"@standardagents/spec",
|
|
24099
24110
|
// Incremental JSON reader used by the runtime to surface a tool's progress
|
|
24100
24111
|
// argument from partial streamed tool-call arguments (tool_call_started).
|
|
24101
|
-
"@formkit/jsonreader"
|
|
24102
|
-
// Pre-bundle the provider packages themselves so their whole subtree (SDKs,
|
|
24103
|
-
// pricing tables) lands in the initial optimized bundle and nothing is
|
|
24104
|
-
// discovered lazily by workerd.
|
|
24105
|
-
...firstPartyProviderPackages
|
|
24112
|
+
"@formkit/jsonreader"
|
|
24106
24113
|
];
|
|
24114
|
+
function depsToInclude(options = {}) {
|
|
24115
|
+
const installedProviders = options.isPackageInstalled ? firstPartyProviderPackages.filter(options.isPackageInstalled) : firstPartyProviderPackages;
|
|
24116
|
+
const openRouterTransitive = installedProviders.includes("@standardagents/openrouter") ? ["@standardagents/openrouter > @openrouter/sdk"] : [];
|
|
24117
|
+
return [...baseDepsToInclude, ...openRouterTransitive, ...installedProviders];
|
|
24118
|
+
}
|
|
24119
|
+
function trustedDevHostDefines(options) {
|
|
24120
|
+
return {
|
|
24121
|
+
__STANDARD_AGENTS_TRUSTED_DEV_HOSTS__: JSON.stringify(
|
|
24122
|
+
normalizeTrustedDevHosts(options.trustedDevHosts)
|
|
24123
|
+
)
|
|
24124
|
+
};
|
|
24125
|
+
}
|
|
24107
24126
|
var packingDeps = [
|
|
24108
24127
|
"rollup",
|
|
24109
24128
|
"@rollup/plugin-commonjs",
|
|
@@ -24112,7 +24131,7 @@ var packingDeps = [
|
|
|
24112
24131
|
"fsevents",
|
|
24113
24132
|
"typescript"
|
|
24114
24133
|
];
|
|
24115
|
-
function createPluginViteConfig(pluginModuleUrl) {
|
|
24134
|
+
function createPluginViteConfig(pluginModuleUrl, options = {}) {
|
|
24116
24135
|
const currentDir = path9__default.dirname(fileURLToPath(pluginModuleUrl));
|
|
24117
24136
|
const isInDist = currentDir.endsWith("dist");
|
|
24118
24137
|
const builderClientDir = path9__default.resolve(
|
|
@@ -24120,6 +24139,7 @@ function createPluginViteConfig(pluginModuleUrl) {
|
|
|
24120
24139
|
isInDist ? "./client" : "../dist/client"
|
|
24121
24140
|
);
|
|
24122
24141
|
return {
|
|
24142
|
+
define: trustedDevHostDefines(options),
|
|
24123
24143
|
publicDir: fs9__default.existsSync(builderClientDir) ? builderClientDir : void 0,
|
|
24124
24144
|
build: {
|
|
24125
24145
|
rollupOptions: {
|
|
@@ -24135,7 +24155,7 @@ function createPluginViteConfig(pluginModuleUrl) {
|
|
|
24135
24155
|
// `npm run dev` ("There is a new version of the pre-bundle for X").
|
|
24136
24156
|
holdUntilCrawlEnd: true,
|
|
24137
24157
|
exclude: depsToExclude,
|
|
24138
|
-
include: depsToInclude.filter((dep) => dep !== "zod/v3" && dep !== "zod/v4" && dep !== "zod/v4/core")
|
|
24158
|
+
include: depsToInclude(options).filter((dep) => dep !== "zod/v3" && dep !== "zod/v4" && dep !== "zod/v4/core")
|
|
24139
24159
|
},
|
|
24140
24160
|
ssr: {
|
|
24141
24161
|
noExternal: [
|
|
@@ -24240,7 +24260,11 @@ function createAgentBuilderCloudflarePlugins(options) {
|
|
|
24240
24260
|
}
|
|
24241
24261
|
});
|
|
24242
24262
|
}
|
|
24243
|
-
function applyPluginEnvironmentConfig(config) {
|
|
24263
|
+
function applyPluginEnvironmentConfig(config, options = {}) {
|
|
24264
|
+
config.define = {
|
|
24265
|
+
...config.define || {},
|
|
24266
|
+
...trustedDevHostDefines(options)
|
|
24267
|
+
};
|
|
24244
24268
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
24245
24269
|
config.optimizeDeps.holdUntilCrawlEnd = true;
|
|
24246
24270
|
config.optimizeDeps.exclude = [
|
|
@@ -24249,7 +24273,7 @@ function applyPluginEnvironmentConfig(config) {
|
|
|
24249
24273
|
];
|
|
24250
24274
|
config.optimizeDeps.include = [
|
|
24251
24275
|
...config.optimizeDeps.include || [],
|
|
24252
|
-
...depsToInclude.filter((dep) => !config.optimizeDeps?.include?.includes(dep))
|
|
24276
|
+
...depsToInclude(options).filter((dep) => !config.optimizeDeps?.include?.includes(dep))
|
|
24253
24277
|
];
|
|
24254
24278
|
config.build = config.build || {};
|
|
24255
24279
|
config.build.rollupOptions = config.build.rollupOptions || {};
|
|
@@ -24309,6 +24333,7 @@ function normalizeDeviceApprovalBrand(value) {
|
|
|
24309
24333
|
// src/plugin.ts
|
|
24310
24334
|
function agentbuilder(options = {}) {
|
|
24311
24335
|
const deviceApproval = normalizeDeviceApprovalBrand(options.deviceApproval);
|
|
24336
|
+
const trustedDevHosts = normalizeTrustedDevHosts(options.trustedDevHosts);
|
|
24312
24337
|
const toolsDir = options.toolsDir ? path9__default.resolve(process.cwd(), options.toolsDir) : path9__default.resolve(process.cwd(), "agents/tools");
|
|
24313
24338
|
const hooksDir = options.hooksDir ? path9__default.resolve(process.cwd(), options.hooksDir) : path9__default.resolve(process.cwd(), "agents/hooks");
|
|
24314
24339
|
const threadApiDir = options.apiDir ? path9__default.resolve(process.cwd(), options.apiDir) : path9__default.resolve(process.cwd(), "agents/api");
|
|
@@ -24413,7 +24438,8 @@ export { CodeExecutionBridge, DurableAgentBuilder, DurableThread, ThreadWatchdog
|
|
|
24413
24438
|
installedProviderPackageMap,
|
|
24414
24439
|
hmr,
|
|
24415
24440
|
sourceDirs,
|
|
24416
|
-
regenerateTypes
|
|
24441
|
+
regenerateTypes,
|
|
24442
|
+
trustedDevHosts
|
|
24417
24443
|
};
|
|
24418
24444
|
const workerEntry = path9__default.relative(process.cwd(), generatedWorkerEntry).split(path9__default.sep).join("/");
|
|
24419
24445
|
const corePlugin = {
|
|
@@ -24421,12 +24447,18 @@ export { CodeExecutionBridge, DurableAgentBuilder, DurableThread, ThreadWatchdog
|
|
|
24421
24447
|
enforce: "pre",
|
|
24422
24448
|
config() {
|
|
24423
24449
|
writeGeneratedWorkerEntry();
|
|
24424
|
-
const viteConfig = createPluginViteConfig(import.meta.url
|
|
24450
|
+
const viteConfig = createPluginViteConfig(import.meta.url, {
|
|
24451
|
+
isPackageInstalled: isProviderPackageInstalled,
|
|
24452
|
+
trustedDevHosts
|
|
24453
|
+
});
|
|
24425
24454
|
return viteConfig;
|
|
24426
24455
|
},
|
|
24427
24456
|
// Apply exclusions and inclusions to ALL environments including Cloudflare worker
|
|
24428
24457
|
configEnvironment(_name, config) {
|
|
24429
|
-
applyPluginEnvironmentConfig(config
|
|
24458
|
+
applyPluginEnvironmentConfig(config, {
|
|
24459
|
+
isPackageInstalled: isProviderPackageInstalled,
|
|
24460
|
+
trustedDevHosts
|
|
24461
|
+
});
|
|
24430
24462
|
},
|
|
24431
24463
|
resolveId(id) {
|
|
24432
24464
|
if (id === VIRTUAL_TOOLS_ID) {
|
|
@@ -24471,6 +24503,7 @@ export { CodeExecutionBridge, DurableAgentBuilder, DurableThread, ThreadWatchdog
|
|
|
24471
24503
|
},
|
|
24472
24504
|
buildStart() {
|
|
24473
24505
|
writeGeneratedWorkerEntry();
|
|
24506
|
+
warnLegacyRootAgentFiles(agentsDir);
|
|
24474
24507
|
regenerateTypes();
|
|
24475
24508
|
this.addWatchFile(toolsDir);
|
|
24476
24509
|
this.addWatchFile(threadApiDir);
|
|
@@ -29242,12 +29275,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29242
29275
|
await parentThread.updateChildRegistryStatus(
|
|
29243
29276
|
params.parent_thread_id,
|
|
29244
29277
|
childThread.id,
|
|
29245
|
-
|
|
29278
|
+
"idle"
|
|
29246
29279
|
);
|
|
29247
29280
|
}
|
|
29248
|
-
if (childResult.terminal && typeof childThreadStub.terminateThread === "function") {
|
|
29249
|
-
await childThreadStub.terminateThread(childThread.id);
|
|
29250
|
-
}
|
|
29251
29281
|
} else if (typeof childThreadStub.terminateThread === "function") {
|
|
29252
29282
|
await childThreadStub.terminateThread(childThread.id);
|
|
29253
29283
|
}
|
|
@@ -31042,6 +31072,8 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31042
31072
|
// opened on and dies with it. Streams are in-memory only — they exist solely
|
|
31043
31073
|
// while the client is attached and never park durably.
|
|
31044
31074
|
clientStreams = /* @__PURE__ */ new Map();
|
|
31075
|
+
proxyStreamReceivers = /* @__PURE__ */ new Map();
|
|
31076
|
+
eventProxyStreams = /* @__PURE__ */ new Map();
|
|
31045
31077
|
alarmQueue;
|
|
31046
31078
|
runtimeSafety;
|
|
31047
31079
|
alarmQueueRearmed = false;
|
|
@@ -31153,6 +31185,10 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31153
31185
|
clientName: typeof attachment.clientName === "string" ? attachment.clientName : null,
|
|
31154
31186
|
clientKind: typeof attachment.clientKind === "string" ? attachment.clientKind : null
|
|
31155
31187
|
});
|
|
31188
|
+
const interrupted = this.parseProxyStreamAttachments(attachment.proxyStreams);
|
|
31189
|
+
if (interrupted.length > 0) {
|
|
31190
|
+
this.recoverInterruptedProxyStreams(ws, interrupted);
|
|
31191
|
+
}
|
|
31156
31192
|
}
|
|
31157
31193
|
}
|
|
31158
31194
|
}
|
|
@@ -33243,11 +33279,13 @@ ${paths.join("\n")}`;
|
|
|
33243
33279
|
try {
|
|
33244
33280
|
const parentId = this.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
|
|
33245
33281
|
const parentStub = this.env.AGENT_BUILDER_THREAD.get(parentId);
|
|
33246
|
-
const
|
|
33282
|
+
const childRegistryEntry = await this.getParentRegistryEntryForChild(
|
|
33247
33283
|
parentStub,
|
|
33248
33284
|
parentThreadId,
|
|
33249
33285
|
threadId
|
|
33250
33286
|
);
|
|
33287
|
+
const parentCommunication = childRegistryEntry?.parentCommunication === "explicit" ? "explicit" : "implicit";
|
|
33288
|
+
const resumableChild = childRegistryEntry?.resumable === true;
|
|
33251
33289
|
const copiedRefs = await this.copyAttachmentRefsToThread(
|
|
33252
33290
|
refs,
|
|
33253
33291
|
parentStub,
|
|
@@ -33270,10 +33308,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33270
33308
|
});
|
|
33271
33309
|
}
|
|
33272
33310
|
const terminalChildRun = sessionStopped || hasTerminalSessionTools;
|
|
33311
|
+
const terminateChildThread = terminalChildRun && !resumableChild;
|
|
33273
33312
|
await parentStub.updateChildRegistryStatus(
|
|
33274
33313
|
parentThreadId,
|
|
33275
33314
|
threadId,
|
|
33276
|
-
|
|
33315
|
+
terminateChildThread ? "terminated" : "idle"
|
|
33277
33316
|
);
|
|
33278
33317
|
if (parentCommunication !== "explicit") {
|
|
33279
33318
|
await this.persistParentHandoffStatusMessage(
|
|
@@ -33281,23 +33320,22 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33281
33320
|
isErrorTerminal ? "failure" : "result"
|
|
33282
33321
|
);
|
|
33283
33322
|
}
|
|
33284
|
-
if (
|
|
33323
|
+
if (terminateChildThread) {
|
|
33285
33324
|
await this.terminateThread(threadId);
|
|
33286
33325
|
}
|
|
33287
33326
|
} catch (error) {
|
|
33288
33327
|
console.error("[DurableThread] Failed to deliver subagent completion to parent:", error);
|
|
33289
33328
|
}
|
|
33290
33329
|
}
|
|
33291
|
-
async
|
|
33330
|
+
async getParentRegistryEntryForChild(parentStub, parentThreadId, childThreadId) {
|
|
33292
33331
|
if (typeof parentStub.getChildrenRegistry !== "function") {
|
|
33293
|
-
return
|
|
33332
|
+
return void 0;
|
|
33294
33333
|
}
|
|
33295
33334
|
try {
|
|
33296
33335
|
const entries = await parentStub.getChildrenRegistry(parentThreadId);
|
|
33297
|
-
|
|
33298
|
-
return childEntry?.parentCommunication === "explicit" ? "explicit" : "implicit";
|
|
33336
|
+
return entries.find((entry) => entry.reference === childThreadId);
|
|
33299
33337
|
} catch {
|
|
33300
|
-
return
|
|
33338
|
+
return void 0;
|
|
33301
33339
|
}
|
|
33302
33340
|
}
|
|
33303
33341
|
/**
|
|
@@ -34845,7 +34883,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
34845
34883
|
void 0,
|
|
34846
34884
|
INTERNAL_TOOL_DELIVERY
|
|
34847
34885
|
);
|
|
34848
|
-
if (delivered
|
|
34886
|
+
if (delivered) sides.add(delivered.side);
|
|
34849
34887
|
}
|
|
34850
34888
|
if (threadId) {
|
|
34851
34889
|
for (const side of sides) {
|
|
@@ -35147,17 +35185,14 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35147
35185
|
deliveredResultWaiters = /* @__PURE__ */ new Map();
|
|
35148
35186
|
notifyDeliveredResultWaiters(toolCallId) {
|
|
35149
35187
|
const waiters = this.deliveredResultWaiters?.get(toolCallId);
|
|
35150
|
-
if (!waiters
|
|
35188
|
+
if (!waiters) return;
|
|
35151
35189
|
this.deliveredResultWaiters.delete(toolCallId);
|
|
35152
|
-
let notified = false;
|
|
35153
35190
|
for (const wake of waiters) {
|
|
35154
35191
|
try {
|
|
35155
35192
|
wake();
|
|
35156
|
-
notified = true;
|
|
35157
35193
|
} catch {
|
|
35158
35194
|
}
|
|
35159
35195
|
}
|
|
35160
|
-
return notified;
|
|
35161
35196
|
}
|
|
35162
35197
|
/**
|
|
35163
35198
|
* Await a parked forwarded call's delivered result: one immediate read,
|
|
@@ -35302,46 +35337,37 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35302
35337
|
const side = pending.side === "b" ? "b" : "a";
|
|
35303
35338
|
const content = ok ? result ?? "" : `Error: ${error ?? "tool failed"}`;
|
|
35304
35339
|
const toolStatus = ok ? "success" : "error";
|
|
35305
|
-
|
|
35306
|
-
|
|
35307
|
-
|
|
35308
|
-
|
|
35309
|
-
|
|
35310
|
-
|
|
35311
|
-
|
|
35340
|
+
await this.ctx.storage.sql.exec(
|
|
35341
|
+
`UPDATE messages SET content = ?, tool_status = ? WHERE tool_call_id = ? AND role = 'tool'`,
|
|
35342
|
+
content,
|
|
35343
|
+
toolStatus,
|
|
35344
|
+
toolCallId
|
|
35345
|
+
);
|
|
35346
|
+
const updated = await this.ctx.storage.sql.exec(
|
|
35347
|
+
`SELECT COUNT(*) AS total FROM messages WHERE tool_call_id = ? AND role = 'tool'`,
|
|
35348
|
+
toolCallId
|
|
35349
|
+
);
|
|
35350
|
+
if (Number(updated.toArray()[0]?.total ?? 0) === 0) {
|
|
35351
|
+
const toolRow = await this.ctx.storage.sql.exec(
|
|
35352
|
+
`SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
|
|
35312
35353
|
toolCallId
|
|
35313
35354
|
);
|
|
35314
|
-
|
|
35315
|
-
|
|
35316
|
-
|
|
35355
|
+
await this.ctx.storage.sql.exec(
|
|
35356
|
+
`INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
|
|
35357
|
+
VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
|
|
35358
|
+
crypto.randomUUID(),
|
|
35317
35359
|
content,
|
|
35360
|
+
toolRow.toArray()[0]?.tool ?? null,
|
|
35361
|
+
toolCallId,
|
|
35318
35362
|
toolStatus,
|
|
35319
|
-
|
|
35363
|
+
Date.now() * TIMESTAMP_MULTIPLIER
|
|
35320
35364
|
);
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
|
|
35324
|
-
|
|
35325
|
-
|
|
35326
|
-
|
|
35327
|
-
`SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
|
|
35328
|
-
toolCallId
|
|
35329
|
-
);
|
|
35330
|
-
this.ctx.storage.sql.exec(
|
|
35331
|
-
`INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
|
|
35332
|
-
VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
|
|
35333
|
-
resultMessageId,
|
|
35334
|
-
content,
|
|
35335
|
-
toolRow.toArray()[0]?.tool ?? null,
|
|
35336
|
-
toolCallId,
|
|
35337
|
-
toolStatus,
|
|
35338
|
-
resultCreatedAt
|
|
35339
|
-
);
|
|
35340
|
-
}
|
|
35341
|
-
return true;
|
|
35342
|
-
});
|
|
35343
|
-
if (!accepted) return null;
|
|
35344
|
-
const resumedInline = this.notifyDeliveredResultWaiters(toolCallId);
|
|
35365
|
+
}
|
|
35366
|
+
await this.ctx.storage.sql.exec(
|
|
35367
|
+
`UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
|
|
35368
|
+
toolCallId
|
|
35369
|
+
);
|
|
35370
|
+
this.notifyDeliveredResultWaiters(toolCallId);
|
|
35345
35371
|
try {
|
|
35346
35372
|
const msgCursor = await this.ctx.storage.sql.exec(
|
|
35347
35373
|
`SELECT id, role, content, name, tool_call_id, tool_status, created_at, depth
|
|
@@ -35352,7 +35378,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35352
35378
|
if (msg) this.broadcastMessage(msg);
|
|
35353
35379
|
} catch {
|
|
35354
35380
|
}
|
|
35355
|
-
return { side
|
|
35381
|
+
return { side };
|
|
35356
35382
|
}
|
|
35357
35383
|
// ─── Runtime byte streams ──────────────────────────────────────────────────
|
|
35358
35384
|
// The existing bridge frames carry opaque capability metadata and bytes
|
|
@@ -35418,8 +35444,138 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35418
35444
|
});
|
|
35419
35445
|
}
|
|
35420
35446
|
}
|
|
35447
|
+
parseProxyStreamAttachments(value) {
|
|
35448
|
+
if (!Array.isArray(value)) return [];
|
|
35449
|
+
const parsed = [];
|
|
35450
|
+
for (const item of value) {
|
|
35451
|
+
if (!item || typeof item !== "object") continue;
|
|
35452
|
+
const candidate = item;
|
|
35453
|
+
if (typeof candidate.id === "string" && candidate.target && typeof candidate.target.threadId === "string" && typeof candidate.target.receiverId === "string") {
|
|
35454
|
+
parsed.push({
|
|
35455
|
+
id: candidate.id,
|
|
35456
|
+
target: {
|
|
35457
|
+
threadId: candidate.target.threadId,
|
|
35458
|
+
receiverId: candidate.target.receiverId
|
|
35459
|
+
}
|
|
35460
|
+
});
|
|
35461
|
+
}
|
|
35462
|
+
}
|
|
35463
|
+
return parsed;
|
|
35464
|
+
}
|
|
35465
|
+
/** Keep live proxy routes on the hibernating bridge socket for restart cleanup. */
|
|
35466
|
+
updateBridgeProxyAttachment(socket) {
|
|
35467
|
+
if (typeof socket.serializeAttachment !== "function") return;
|
|
35468
|
+
let attachment = { type: "bridge" };
|
|
35469
|
+
try {
|
|
35470
|
+
const current = socket.deserializeAttachment();
|
|
35471
|
+
if (current && typeof current === "object") {
|
|
35472
|
+
attachment = { ...current };
|
|
35473
|
+
}
|
|
35474
|
+
} catch {
|
|
35475
|
+
}
|
|
35476
|
+
const proxyStreams = Array.from(this.clientStreams.entries()).filter(([, state]) => state.socket === socket && state.proxyTarget !== null).map(([id, state]) => ({ id, target: state.proxyTarget }));
|
|
35477
|
+
try {
|
|
35478
|
+
socket.serializeAttachment({ ...attachment, proxyStreams });
|
|
35479
|
+
} catch {
|
|
35480
|
+
}
|
|
35481
|
+
}
|
|
35482
|
+
async deliverProxyStreamFrame(target, frame) {
|
|
35483
|
+
try {
|
|
35484
|
+
const namespace = this.env.AGENT_BUILDER_THREAD;
|
|
35485
|
+
const stub = namespace.get(namespace.idFromName(target.threadId));
|
|
35486
|
+
return await stub.proxyStreamReceive(target.receiverId, frame);
|
|
35487
|
+
} catch {
|
|
35488
|
+
return false;
|
|
35489
|
+
}
|
|
35490
|
+
}
|
|
35491
|
+
/** Abort live proxy routes restored without their in-memory stream state. */
|
|
35492
|
+
recoverInterruptedProxyStreams(socket, streams) {
|
|
35493
|
+
const error = "The stream ended because its runtime restarted.";
|
|
35494
|
+
for (const stream of streams) {
|
|
35495
|
+
try {
|
|
35496
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
35497
|
+
socket.send(JSON.stringify({ type: "stream_abort", id: stream.id, error }));
|
|
35498
|
+
}
|
|
35499
|
+
} catch {
|
|
35500
|
+
}
|
|
35501
|
+
this.ctx.waitUntil(
|
|
35502
|
+
this.deliverProxyStreamFrame(stream.target, { type: "abort", error }).then(() => {
|
|
35503
|
+
})
|
|
35504
|
+
);
|
|
35505
|
+
}
|
|
35506
|
+
this.updateBridgeProxyAttachment(socket);
|
|
35507
|
+
}
|
|
35508
|
+
/** Register the child side before any ancestor opens an owner stream. */
|
|
35509
|
+
createProxyStreamReceiver(onCancel) {
|
|
35510
|
+
const id = crypto.randomUUID();
|
|
35511
|
+
let controller = null;
|
|
35512
|
+
let settleDone;
|
|
35513
|
+
const done = new Promise((resolve3) => {
|
|
35514
|
+
settleDone = resolve3;
|
|
35515
|
+
});
|
|
35516
|
+
const readable = new ReadableStream({
|
|
35517
|
+
start(value) {
|
|
35518
|
+
controller = value;
|
|
35519
|
+
},
|
|
35520
|
+
cancel: (reason) => {
|
|
35521
|
+
const receiver2 = this.proxyStreamReceivers.get(id);
|
|
35522
|
+
if (!receiver2 || receiver2.settled) return;
|
|
35523
|
+
const error = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "The proxy stream reader was canceled.";
|
|
35524
|
+
receiver2.settle({ ok: false, error });
|
|
35525
|
+
receiver2.onCancel(error);
|
|
35526
|
+
}
|
|
35527
|
+
});
|
|
35528
|
+
const receiver = {
|
|
35529
|
+
controller,
|
|
35530
|
+
settle: (result) => {
|
|
35531
|
+
if (receiver.settled) return;
|
|
35532
|
+
receiver.settled = true;
|
|
35533
|
+
this.proxyStreamReceivers.delete(id);
|
|
35534
|
+
settleDone(result);
|
|
35535
|
+
},
|
|
35536
|
+
onCancel,
|
|
35537
|
+
settled: false
|
|
35538
|
+
};
|
|
35539
|
+
this.proxyStreamReceivers.set(id, receiver);
|
|
35540
|
+
return { id, readable, done };
|
|
35541
|
+
}
|
|
35542
|
+
async proxyStreamReceiverExists(receiverId) {
|
|
35543
|
+
return this.proxyStreamReceivers.has(receiverId);
|
|
35544
|
+
}
|
|
35545
|
+
async proxyStreamReceive(receiverId, frame) {
|
|
35546
|
+
const receiver = this.proxyStreamReceivers.get(receiverId);
|
|
35547
|
+
if (!receiver || receiver.settled) return false;
|
|
35548
|
+
if (frame.type === "data") {
|
|
35549
|
+
try {
|
|
35550
|
+
receiver.controller?.enqueue(decodeStreamChunk(frame.data));
|
|
35551
|
+
} catch (error2) {
|
|
35552
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
35553
|
+
try {
|
|
35554
|
+
receiver.controller?.error(error2 instanceof Error ? error2 : new Error(message));
|
|
35555
|
+
} catch {
|
|
35556
|
+
}
|
|
35557
|
+
receiver.settle({ ok: false, error: message });
|
|
35558
|
+
}
|
|
35559
|
+
return true;
|
|
35560
|
+
}
|
|
35561
|
+
if (frame.type === "end") {
|
|
35562
|
+
try {
|
|
35563
|
+
receiver.controller?.close();
|
|
35564
|
+
} catch {
|
|
35565
|
+
}
|
|
35566
|
+
receiver.settle({ ok: true });
|
|
35567
|
+
return true;
|
|
35568
|
+
}
|
|
35569
|
+
const error = frame.error ?? "The proxied client stream was aborted.";
|
|
35570
|
+
try {
|
|
35571
|
+
receiver.controller?.error(new Error(error));
|
|
35572
|
+
} catch {
|
|
35573
|
+
}
|
|
35574
|
+
receiver.settle({ ok: false, error });
|
|
35575
|
+
return true;
|
|
35576
|
+
}
|
|
35421
35577
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
35422
|
-
async openClientStream(request) {
|
|
35578
|
+
async openClientStream(request, proxyTarget = null) {
|
|
35423
35579
|
const owner = await this.getExecutionOwner();
|
|
35424
35580
|
let socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
35425
35581
|
if (!socket && owner) {
|
|
@@ -35452,9 +35608,12 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35452
35608
|
opened: null,
|
|
35453
35609
|
openTimer: null,
|
|
35454
35610
|
settleDone,
|
|
35455
|
-
settled: false
|
|
35611
|
+
settled: false,
|
|
35612
|
+
proxyTarget,
|
|
35613
|
+
delivery: Promise.resolve()
|
|
35456
35614
|
};
|
|
35457
35615
|
this.clientStreams.set(id, state);
|
|
35616
|
+
this.updateBridgeProxyAttachment(socket);
|
|
35458
35617
|
this.bridgeLastSeen.set(socket, Date.now());
|
|
35459
35618
|
const openResult = await new Promise((resolve3) => {
|
|
35460
35619
|
state.opened = resolve3;
|
|
@@ -35490,6 +35649,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35490
35649
|
}
|
|
35491
35650
|
if (!openResult.ok) {
|
|
35492
35651
|
this.clientStreams.delete(id);
|
|
35652
|
+
this.updateBridgeProxyAttachment(socket);
|
|
35493
35653
|
state.settled = true;
|
|
35494
35654
|
settleDone({ ok: false, error: openResult.error });
|
|
35495
35655
|
return {
|
|
@@ -35531,14 +35691,28 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35531
35691
|
};
|
|
35532
35692
|
return { ok: true, duplex };
|
|
35533
35693
|
}
|
|
35534
|
-
// ───
|
|
35535
|
-
// A
|
|
35536
|
-
//
|
|
35537
|
-
//
|
|
35538
|
-
//
|
|
35539
|
-
|
|
35540
|
-
|
|
35541
|
-
|
|
35694
|
+
// ─── Event-driven subagent stream proxy ──────────────────────────────────
|
|
35695
|
+
// A child registers a local receiver before the bridge-holding ancestor
|
|
35696
|
+
// opens the client stream. The ancestor invokes that receiver only for real
|
|
35697
|
+
// data/end/abort events; there is no pull loop or permanently outstanding
|
|
35698
|
+
// cross-DO RPC while the stream is idle.
|
|
35699
|
+
async proxyStreamOpen(request, target) {
|
|
35700
|
+
const namespace = this.env.AGENT_BUILDER_THREAD;
|
|
35701
|
+
let receiverExists = false;
|
|
35702
|
+
try {
|
|
35703
|
+
const destination = namespace.get(namespace.idFromName(target.threadId));
|
|
35704
|
+
receiverExists = await destination.proxyStreamReceiverExists(target.receiverId);
|
|
35705
|
+
} catch {
|
|
35706
|
+
receiverExists = false;
|
|
35707
|
+
}
|
|
35708
|
+
if (!receiverExists) {
|
|
35709
|
+
return {
|
|
35710
|
+
ok: false,
|
|
35711
|
+
error: "The destination stream receiver is unavailable.",
|
|
35712
|
+
code: "unavailable"
|
|
35713
|
+
};
|
|
35714
|
+
}
|
|
35715
|
+
const opened = await this.openClientStream(request, target);
|
|
35542
35716
|
if (!opened.ok) {
|
|
35543
35717
|
const failureData = opened.data;
|
|
35544
35718
|
const noClient = failureData?.noClient === true;
|
|
@@ -35550,82 +35724,23 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35550
35724
|
};
|
|
35551
35725
|
}
|
|
35552
35726
|
const id = crypto.randomUUID();
|
|
35553
|
-
|
|
35554
|
-
|
|
35555
|
-
|
|
35556
|
-
|
|
35557
|
-
writer,
|
|
35558
|
-
reader,
|
|
35559
|
-
buffer: [],
|
|
35560
|
-
done: false,
|
|
35561
|
-
error: void 0,
|
|
35562
|
-
pump: Promise.resolve(),
|
|
35563
|
-
close: (error) => duplex.close(error),
|
|
35564
|
-
waiter: null,
|
|
35565
|
-
notify: () => {
|
|
35566
|
-
const waiter = entry.waiter;
|
|
35567
|
-
entry.waiter = null;
|
|
35568
|
-
waiter?.();
|
|
35569
|
-
}
|
|
35570
|
-
};
|
|
35571
|
-
entry.pump = (async () => {
|
|
35572
|
-
try {
|
|
35573
|
-
while (true) {
|
|
35574
|
-
const { value, done } = await reader.read();
|
|
35575
|
-
if (done) break;
|
|
35576
|
-
if (value) {
|
|
35577
|
-
entry.buffer.push(value);
|
|
35578
|
-
entry.notify();
|
|
35579
|
-
}
|
|
35580
|
-
}
|
|
35581
|
-
} catch (error) {
|
|
35582
|
-
entry.error = error instanceof Error ? error.message : String(error);
|
|
35583
|
-
} finally {
|
|
35584
|
-
entry.done = true;
|
|
35585
|
-
entry.notify();
|
|
35586
|
-
}
|
|
35587
|
-
})();
|
|
35588
|
-
void duplex.done.then((d) => {
|
|
35589
|
-
entry.done = true;
|
|
35590
|
-
if (!d.ok && !entry.error) entry.error = d.error;
|
|
35591
|
-
entry.notify();
|
|
35727
|
+
this.eventProxyStreams.set(id, {
|
|
35728
|
+
writer: opened.duplex.writable.getWriter(),
|
|
35729
|
+
close: (error) => opened.duplex.close(error),
|
|
35730
|
+
target
|
|
35592
35731
|
});
|
|
35593
|
-
|
|
35594
|
-
return { ok: true, id, parkedPull: true };
|
|
35732
|
+
return { ok: true, id, eventDriven: true };
|
|
35595
35733
|
}
|
|
35596
35734
|
async proxyStreamWrite(id, dataB64) {
|
|
35597
|
-
const entry = this.
|
|
35735
|
+
const entry = this.eventProxyStreams.get(id);
|
|
35598
35736
|
if (!entry) return;
|
|
35599
35737
|
try {
|
|
35600
35738
|
await entry.writer.write(decodeStreamChunk(dataB64));
|
|
35601
35739
|
} catch {
|
|
35602
35740
|
}
|
|
35603
35741
|
}
|
|
35604
|
-
async proxyStreamPull(id, maxWaitMs) {
|
|
35605
|
-
const entry = this.proxyStreams.get(id);
|
|
35606
|
-
if (!entry) return { chunks: [], done: true, error: "unknown proxy stream" };
|
|
35607
|
-
if (entry.buffer.length === 0 && !entry.done) {
|
|
35608
|
-
await new Promise((resolve3) => {
|
|
35609
|
-
const deadline = typeof maxWaitMs === "number" && maxWaitMs > 0 ? setTimeout(() => {
|
|
35610
|
-
if (entry.waiter === wake) entry.waiter = null;
|
|
35611
|
-
resolve3();
|
|
35612
|
-
}, maxWaitMs) : null;
|
|
35613
|
-
const wake = () => {
|
|
35614
|
-
if (deadline) clearTimeout(deadline);
|
|
35615
|
-
resolve3();
|
|
35616
|
-
};
|
|
35617
|
-
entry.waiter = wake;
|
|
35618
|
-
});
|
|
35619
|
-
}
|
|
35620
|
-
const chunks = entry.buffer.splice(0).map((b) => encodeStreamChunk(b));
|
|
35621
|
-
if (entry.done && entry.buffer.length === 0) {
|
|
35622
|
-
this.proxyStreams.delete(id);
|
|
35623
|
-
return { chunks, done: true, error: entry.error };
|
|
35624
|
-
}
|
|
35625
|
-
return { chunks, done: false };
|
|
35626
|
-
}
|
|
35627
35742
|
async proxyStreamEnd(id) {
|
|
35628
|
-
const entry = this.
|
|
35743
|
+
const entry = this.eventProxyStreams.get(id);
|
|
35629
35744
|
if (!entry) return;
|
|
35630
35745
|
try {
|
|
35631
35746
|
await entry.writer.close();
|
|
@@ -35633,30 +35748,49 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35633
35748
|
}
|
|
35634
35749
|
}
|
|
35635
35750
|
async proxyStreamAbort(id) {
|
|
35636
|
-
const entry = this.
|
|
35751
|
+
const entry = this.eventProxyStreams.get(id);
|
|
35637
35752
|
if (!entry) return;
|
|
35638
|
-
this.
|
|
35639
|
-
entry.done = true;
|
|
35640
|
-
if (!entry.error) entry.error = "aborted";
|
|
35641
|
-
entry.notify();
|
|
35753
|
+
this.eventProxyStreams.delete(id);
|
|
35642
35754
|
try {
|
|
35643
35755
|
entry.close("aborted");
|
|
35644
35756
|
} catch {
|
|
35645
35757
|
}
|
|
35646
35758
|
}
|
|
35759
|
+
forgetEventProxyStream(clientStreamId, target) {
|
|
35760
|
+
const matchesTarget = (candidate) => candidate.threadId === target.threadId && candidate.receiverId === target.receiverId;
|
|
35761
|
+
for (const [id, entry] of Array.from(this.eventProxyStreams.entries())) {
|
|
35762
|
+
if (id !== clientStreamId && !matchesTarget(entry.target)) continue;
|
|
35763
|
+
this.eventProxyStreams.delete(id);
|
|
35764
|
+
try {
|
|
35765
|
+
entry.writer.releaseLock();
|
|
35766
|
+
} catch {
|
|
35767
|
+
}
|
|
35768
|
+
}
|
|
35769
|
+
}
|
|
35647
35770
|
/** Settle and remove a client stream exactly once. */
|
|
35648
35771
|
settleClientStream(id, result, openCode) {
|
|
35649
35772
|
const state = this.clientStreams.get(id);
|
|
35650
35773
|
if (!state) return;
|
|
35651
35774
|
this.clientStreams.delete(id);
|
|
35775
|
+
this.updateBridgeProxyAttachment(state.socket);
|
|
35652
35776
|
if (state.openTimer) clearTimeout(state.openTimer);
|
|
35653
35777
|
if (state.opened) state.opened({ ok: false, error: result.error ?? "Stream closed.", code: openCode });
|
|
35654
35778
|
if (!state.settled) {
|
|
35655
35779
|
state.settled = true;
|
|
35656
|
-
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
|
|
35780
|
+
if (state.proxyTarget) {
|
|
35781
|
+
const target = state.proxyTarget;
|
|
35782
|
+
const frame = result.ok ? { type: "end" } : { type: "abort", error: result.error ?? "Client stream aborted." };
|
|
35783
|
+
state.delivery = state.delivery.then(async () => {
|
|
35784
|
+
await this.deliverProxyStreamFrame(target, frame);
|
|
35785
|
+
});
|
|
35786
|
+
this.ctx.waitUntil(state.delivery);
|
|
35787
|
+
this.forgetEventProxyStream(id, target);
|
|
35788
|
+
} else {
|
|
35789
|
+
try {
|
|
35790
|
+
if (result.ok) state.controller?.close();
|
|
35791
|
+
else state.controller?.error(new Error(result.error ?? "Client stream aborted."));
|
|
35792
|
+
} catch {
|
|
35793
|
+
}
|
|
35660
35794
|
}
|
|
35661
35795
|
state.settleDone(result);
|
|
35662
35796
|
}
|
|
@@ -35699,6 +35833,18 @@ ${resultContent}${attachmentSummary}`;
|
|
|
35699
35833
|
}
|
|
35700
35834
|
case "stream_data": {
|
|
35701
35835
|
if (typeof parsed.data !== "string") return;
|
|
35836
|
+
if (state.proxyTarget) {
|
|
35837
|
+
const target = state.proxyTarget;
|
|
35838
|
+
const frame = { type: "data", data: parsed.data };
|
|
35839
|
+
state.delivery = state.delivery.then(async () => {
|
|
35840
|
+
const delivered = await this.deliverProxyStreamFrame(target, frame);
|
|
35841
|
+
if (!delivered && this.clientStreams.get(id) === state) {
|
|
35842
|
+
this.abortClientStream(id, "The destination stream receiver is unavailable.");
|
|
35843
|
+
}
|
|
35844
|
+
});
|
|
35845
|
+
this.ctx.waitUntil(state.delivery);
|
|
35846
|
+
return;
|
|
35847
|
+
}
|
|
35702
35848
|
try {
|
|
35703
35849
|
state.controller?.enqueue(decodeStreamChunk(parsed.data));
|
|
35704
35850
|
} catch {
|
|
@@ -36526,11 +36672,12 @@ ${resultContent}${attachmentSummary}`;
|
|
|
36526
36672
|
if (!parentThreadId) return;
|
|
36527
36673
|
const parentId = this.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
|
|
36528
36674
|
const parentStub = this.env.AGENT_BUILDER_THREAD.get(parentId);
|
|
36529
|
-
const
|
|
36675
|
+
const childRegistryEntry = await this.getParentRegistryEntryForChild(
|
|
36530
36676
|
parentStub,
|
|
36531
36677
|
parentThreadId,
|
|
36532
36678
|
threadId
|
|
36533
36679
|
);
|
|
36680
|
+
const parentCommunication = childRegistryEntry?.parentCommunication === "explicit" ? "explicit" : "implicit";
|
|
36534
36681
|
if (parentCommunication !== "explicit") {
|
|
36535
36682
|
await parentStub.queueMessage(parentThreadId, {
|
|
36536
36683
|
role: "user",
|