zidane 6.5.0 → 6.5.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/acp-cli.js +6 -6
- package/dist/{acp-B-AOovHz.js → acp-o-LQAjFw.js} +4 -5
- package/dist/{acp-B-AOovHz.js.map → acp-o-LQAjFw.js.map} +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-SQ6rxfpT.js → agent-BVNkC1h4.js} +658 -10
- package/dist/agent-BVNkC1h4.js.map +1 -0
- package/dist/agent.js +1 -2
- package/dist/{anthropic-D3lkw5mv.js → anthropic-Dov2mC5e.js} +3 -3
- package/dist/{anthropic-D3lkw5mv.js.map → anthropic-Dov2mC5e.js.map} +1 -1
- package/dist/{auth-BzYbbdN_.js → auth-B8ZuBcKC.js} +5 -5
- package/dist/{auth-BzYbbdN_.js.map → auth-B8ZuBcKC.js.map} +1 -1
- package/dist/chat/pure.js +2 -2
- package/dist/chat.js +5 -5
- package/dist/{edit-utils-EGosADZq.js → context-breakdown-BLF10tOO.js} +154 -2
- package/dist/context-breakdown-BLF10tOO.js.map +1 -0
- package/dist/{context-transport-CDBatvQc.js → context-transport-D-daWOmp.js} +3 -3
- package/dist/{context-transport-CDBatvQc.js.map → context-transport-D-daWOmp.js.map} +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.js +3 -3
- package/dist/index-CWBSrqI6.d.ts.map +1 -1
- package/dist/index.js +9 -10
- package/dist/index.js.map +1 -1
- package/dist/{login-XZDS4K2o.js → login-CR3fDHXh.js} +2 -2
- package/dist/{login-XZDS4K2o.js.map → login-CR3fDHXh.js.map} +1 -1
- package/dist/{mcp-D27rP47I.js → mcp-C6IkXNOS.js} +3 -3
- package/dist/{mcp-D27rP47I.js.map → mcp-C6IkXNOS.js.map} +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-C01kKGj0.js → messages-C_H9soi5.js} +2 -3
- package/dist/{messages-C01kKGj0.js.map → messages-C_H9soi5.js.map} +1 -1
- package/dist/{openai-compat-B3c_hzwf.js → openai-compat-CnHeo4Lz.js} +216 -3
- package/dist/openai-compat-CnHeo4Lz.js.map +1 -0
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-z1vU0b2Q.js → providers-B6mFe7OD.js} +3 -3
- package/dist/{providers-z1vU0b2Q.js.map → providers-B6mFe7OD.js.map} +1 -1
- package/dist/providers.js +4 -4
- package/dist/restate.d.ts +178 -2
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +458 -4
- package/dist/restate.js.map +1 -1
- package/dist/{session-cr9VdoEO.js → session-VpLrNaFQ.js} +2 -2
- package/dist/{session-cr9VdoEO.js.map → session-VpLrNaFQ.js.map} +1 -1
- package/dist/session.js +2 -2
- package/dist/{stdio-O-m9WVkE.js → stdio-DzUUkkhS.js} +2 -2
- package/dist/{stdio-O-m9WVkE.js.map → stdio-DzUUkkhS.js.map} +1 -1
- package/dist/{stdio-loader-BSGQmG4L.js → stdio-loader-BILyUGmi.js} +2 -2
- package/dist/{stdio-loader-BSGQmG4L.js.map → stdio-loader-BILyUGmi.js.map} +1 -1
- package/dist/tools.js +1 -2
- package/dist/{transcript-anchors-D4IxJifV.js → transcript-anchors-DEIr0tJC.js} +5 -5
- package/dist/{transcript-anchors-D4IxJifV.js.map → transcript-anchors-DEIr0tJC.js.map} +1 -1
- package/dist/tui.js +8 -8
- package/dist/{turn-operations-CAvi8NYt.js → turn-operations-C3IijXMW.js} +2 -2
- package/dist/{turn-operations-CAvi8NYt.js.map → turn-operations-C3IijXMW.js.map} +1 -1
- package/dist/{xai-BPwMQSuF.js → xai-Du6Wfnlb.js} +2 -2
- package/dist/{xai-BPwMQSuF.js.map → xai-Du6Wfnlb.js.map} +1 -1
- package/docs/RESTATE.md +102 -3
- package/package.json +1 -1
- package/dist/agent-SQ6rxfpT.js.map +0 -1
- package/dist/aliasing-lVFbm81n.js +0 -217
- package/dist/aliasing-lVFbm81n.js.map +0 -1
- package/dist/context-breakdown-Dzo25N45.js +0 -155
- package/dist/context-breakdown-Dzo25N45.js.map +0 -1
- package/dist/edit-utils-EGosADZq.js.map +0 -1
- package/dist/openai-compat-B3c_hzwf.js.map +0 -1
- package/dist/tool-state-DTs-3ZHH.js +0 -653
- package/dist/tool-state-DTs-3ZHH.js.map +0 -1
package/dist/restate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as coerceToolResultContent } from "./types-BIarq1qE.js";
|
|
3
|
-
import {
|
|
1
|
+
import { L as buildAliasMaps } from "./openai-compat-CnHeo4Lz.js";
|
|
2
|
+
import { c as toolOutputByteLength, l as toolResultToText, r as coerceToolResultContent } from "./types-BIarq1qE.js";
|
|
3
|
+
import { Dt as reserveChildLabel, Ln as hydrateReadStateFromSessionAsync, a as stagedToolState, i as isToolStateOps } from "./agent-BVNkC1h4.js";
|
|
4
|
+
import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
|
|
4
5
|
import { createHash } from "node:crypto";
|
|
5
6
|
//#region src/restate/awakeable.ts
|
|
6
7
|
/**
|
|
@@ -967,6 +968,459 @@ function maxDefined(values) {
|
|
|
967
968
|
return max;
|
|
968
969
|
}
|
|
969
970
|
//#endregion
|
|
971
|
+
//#region src/restate/spawn.ts
|
|
972
|
+
const ZERO_STATS = {
|
|
973
|
+
totalIn: 0,
|
|
974
|
+
totalOut: 0,
|
|
975
|
+
totalCacheRead: 0,
|
|
976
|
+
totalCacheCreation: 0,
|
|
977
|
+
turns: 0,
|
|
978
|
+
elapsed: 0
|
|
979
|
+
};
|
|
980
|
+
const DEFAULT_DESCRIPTION = "Delegate a self-contained task to a sub-agent that runs independently and returns only its final report. Several spawn calls in one turn run in parallel.";
|
|
981
|
+
function restateSpawnTool(options) {
|
|
982
|
+
const persist = options.persist ?? true;
|
|
983
|
+
const emitEvents = options.emitChildEvents ?? true;
|
|
984
|
+
let dispatchSeq = 0;
|
|
985
|
+
let sessionlessLabels = 0;
|
|
986
|
+
let cancelSignalled = false;
|
|
987
|
+
let nestedLabels = 0;
|
|
988
|
+
const pending = [];
|
|
989
|
+
const drainRegistered = /* @__PURE__ */ new WeakSet();
|
|
990
|
+
/**
|
|
991
|
+
* Build the request and the bookkeeping entry. Deliberately side-effect free
|
|
992
|
+
* apart from reserving the label: the fleet path has to be able to reject a
|
|
993
|
+
* misconfigured `dispatch` (below) without leaving a phantom child queued for
|
|
994
|
+
* the merge. `commit` is what actually enrols the child.
|
|
995
|
+
*/
|
|
996
|
+
function prepare(input, ctx) {
|
|
997
|
+
const task = String(input.task ?? "");
|
|
998
|
+
const depth = (ctx.depth ?? 0) + 1;
|
|
999
|
+
const label = ctx.session ? reserveChildLabel(ctx.session) : `child-${++sessionlessLabels}`;
|
|
1000
|
+
return {
|
|
1001
|
+
entry: {
|
|
1002
|
+
order: ++dispatchSeq,
|
|
1003
|
+
label,
|
|
1004
|
+
task,
|
|
1005
|
+
depth,
|
|
1006
|
+
callId: ctx.callId,
|
|
1007
|
+
runId: `${ctx.runId ?? "run"}-${label}`,
|
|
1008
|
+
...ctx.runId !== void 0 ? { parentRunId: ctx.runId } : {},
|
|
1009
|
+
...ctx.session !== void 0 ? { session: ctx.session } : {}
|
|
1010
|
+
},
|
|
1011
|
+
req: {
|
|
1012
|
+
task,
|
|
1013
|
+
label,
|
|
1014
|
+
depth,
|
|
1015
|
+
...ctx.runId !== void 0 ? { parentRunId: ctx.runId } : {},
|
|
1016
|
+
parentCallId: ctx.callId,
|
|
1017
|
+
...ctx.session !== void 0 ? { sessionId: ctx.session.id } : {},
|
|
1018
|
+
...typeof input.model === "string" ? { model: input.model } : {},
|
|
1019
|
+
...typeof input.subagent_type === "string" ? { subagentType: input.subagent_type } : {}
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
/** Enrol a dispatched child for merging, and announce it. */
|
|
1024
|
+
function commit(entry, input, ctx) {
|
|
1025
|
+
pending.push(entry);
|
|
1026
|
+
registerDrain(ctx);
|
|
1027
|
+
watchAbort(entry, ctx);
|
|
1028
|
+
Promise.resolve(ctx.hooks.callHook("spawn:before", {
|
|
1029
|
+
id: entry.label,
|
|
1030
|
+
task: entry.task,
|
|
1031
|
+
callId: entry.callId,
|
|
1032
|
+
depth: entry.depth,
|
|
1033
|
+
...typeof input.model === "string" ? { model: input.model } : {}
|
|
1034
|
+
})).catch(() => {});
|
|
1035
|
+
}
|
|
1036
|
+
function registerDrain(ctx) {
|
|
1037
|
+
if (drainRegistered.has(ctx.hooks)) return;
|
|
1038
|
+
drainRegistered.add(ctx.hooks);
|
|
1039
|
+
ctx.hooks.hook("tool-results:after", async () => {
|
|
1040
|
+
await drain(ctx);
|
|
1041
|
+
});
|
|
1042
|
+
ctx.hooks.hook("turn:after", async () => {
|
|
1043
|
+
await drain(ctx);
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Persist finished children and announce them. Runs where awaiting is legal,
|
|
1048
|
+
* in submission order rather than settlement order, so two children that
|
|
1049
|
+
* complete out of order still land deterministically.
|
|
1050
|
+
*/
|
|
1051
|
+
async function drain(ctx) {
|
|
1052
|
+
if (pending.length === 0) return;
|
|
1053
|
+
const batch = pending.splice(0, pending.length).sort((a, b) => a.order - b.order);
|
|
1054
|
+
let touchedSession;
|
|
1055
|
+
for (const entry of batch) {
|
|
1056
|
+
const result = entry.result;
|
|
1057
|
+
const stats = result?.stats ?? ZERO_STATS;
|
|
1058
|
+
const status = result?.status ?? (entry.cancelRequested ? "aborted" : "error");
|
|
1059
|
+
const session = entry.session;
|
|
1060
|
+
if (persist && session) {
|
|
1061
|
+
touchedSession = session;
|
|
1062
|
+
session.startRun(entry.runId, entry.task, {
|
|
1063
|
+
...entry.parentRunId !== void 0 ? { parentRunId: entry.parentRunId } : {},
|
|
1064
|
+
depth: entry.depth,
|
|
1065
|
+
label: entry.label
|
|
1066
|
+
});
|
|
1067
|
+
if (result && result.turns.length > 0) await session.appendTurns(result.turns.map((turn) => ({
|
|
1068
|
+
...turn,
|
|
1069
|
+
runId: entry.runId
|
|
1070
|
+
})));
|
|
1071
|
+
const rollup = {
|
|
1072
|
+
turns: stats.turns,
|
|
1073
|
+
tokensIn: stats.totalIn,
|
|
1074
|
+
tokensOut: stats.totalOut,
|
|
1075
|
+
...stats.cost !== void 0 ? { cost: stats.cost } : {}
|
|
1076
|
+
};
|
|
1077
|
+
if (status === "completed") session.completeRun(entry.runId, rollup);
|
|
1078
|
+
else if (status === "error") session.errorRun(entry.runId, errorText(entry), rollup);
|
|
1079
|
+
else session.abortRun(entry.runId, rollup);
|
|
1080
|
+
stampJournaledRunTimes(session, entry, result);
|
|
1081
|
+
}
|
|
1082
|
+
if (emitEvents && result) await replayChildEvents(entry, result.turns, ctx);
|
|
1083
|
+
if (result?.children && result.children.length > 0) {
|
|
1084
|
+
const merged = await mergeDescendants(entry, result.children, ctx);
|
|
1085
|
+
if (merged) touchedSession = merged;
|
|
1086
|
+
}
|
|
1087
|
+
options.onChildMerged?.({
|
|
1088
|
+
label: entry.label,
|
|
1089
|
+
task: entry.task,
|
|
1090
|
+
depth: entry.depth,
|
|
1091
|
+
status,
|
|
1092
|
+
stats,
|
|
1093
|
+
turns: result?.turns ?? [],
|
|
1094
|
+
...result?.startedAt !== void 0 ? { startedAt: result.startedAt } : {},
|
|
1095
|
+
...result?.endedAt !== void 0 ? { endedAt: result.endedAt } : {},
|
|
1096
|
+
...result?.children !== void 0 ? { children: result.children } : {}
|
|
1097
|
+
}, ctx);
|
|
1098
|
+
if (status === "error") await ctx.hooks.callHook("spawn:error", {
|
|
1099
|
+
id: entry.label,
|
|
1100
|
+
task: entry.task,
|
|
1101
|
+
error: new Error(errorText(entry)),
|
|
1102
|
+
depth: entry.depth
|
|
1103
|
+
});
|
|
1104
|
+
await ctx.hooks.callHook("spawn:complete", {
|
|
1105
|
+
id: entry.label,
|
|
1106
|
+
task: entry.task,
|
|
1107
|
+
callId: entry.callId,
|
|
1108
|
+
depth: entry.depth,
|
|
1109
|
+
status,
|
|
1110
|
+
stats,
|
|
1111
|
+
...result?.text ? { text: result.text } : {}
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
await touchedSession?.save();
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Merge a child's own descendants under it, depth-first and in the order the
|
|
1118
|
+
* child reported them.
|
|
1119
|
+
*
|
|
1120
|
+
* Each nested child is RE-LABELED from the merging session's counter: the
|
|
1121
|
+
* label it arrived with was minted by its own parent's counter and would
|
|
1122
|
+
* collide (every level starts at `child-1`). Re-labeling keeps the session's
|
|
1123
|
+
* labels unique and makes the synthesized events agree with the persisted
|
|
1124
|
+
* tree.
|
|
1125
|
+
*/
|
|
1126
|
+
async function mergeDescendants(parent, nested, ctx) {
|
|
1127
|
+
let touched;
|
|
1128
|
+
const session = parent.session;
|
|
1129
|
+
for (const child of nested) {
|
|
1130
|
+
const depth = parent.depth + 1;
|
|
1131
|
+
const label = session ? reserveChildLabel(session) : `${parent.runId}-nested-${++nestedLabels}`;
|
|
1132
|
+
const runId = `${parent.runId}-${label}`;
|
|
1133
|
+
if (persist && session) {
|
|
1134
|
+
touched = session;
|
|
1135
|
+
session.startRun(runId, child.task, {
|
|
1136
|
+
parentRunId: parent.runId,
|
|
1137
|
+
depth,
|
|
1138
|
+
label
|
|
1139
|
+
});
|
|
1140
|
+
if (child.turns.length > 0) await session.appendTurns(child.turns.map((turn) => ({
|
|
1141
|
+
...turn,
|
|
1142
|
+
runId
|
|
1143
|
+
})));
|
|
1144
|
+
const rollup = {
|
|
1145
|
+
turns: child.stats.turns,
|
|
1146
|
+
tokensIn: child.stats.totalIn,
|
|
1147
|
+
tokensOut: child.stats.totalOut,
|
|
1148
|
+
...child.stats.cost !== void 0 ? { cost: child.stats.cost } : {}
|
|
1149
|
+
};
|
|
1150
|
+
if (child.status === "completed") session.completeRun(runId, rollup);
|
|
1151
|
+
else if (child.status === "error") session.errorRun(runId, "sub-agent failed", rollup);
|
|
1152
|
+
else session.abortRun(runId, rollup);
|
|
1153
|
+
const record = session.runs.find((run) => run.id === runId);
|
|
1154
|
+
if (record) {
|
|
1155
|
+
const startedAt = child.startedAt ?? child.turns[0]?.createdAt;
|
|
1156
|
+
const endedAt = child.endedAt ?? child.turns[child.turns.length - 1]?.createdAt ?? startedAt;
|
|
1157
|
+
if (startedAt !== void 0) record.startedAt = startedAt;
|
|
1158
|
+
if (endedAt !== void 0) record.endedAt = endedAt;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (emitEvents) await replayChildEvents({
|
|
1162
|
+
label,
|
|
1163
|
+
depth,
|
|
1164
|
+
runId,
|
|
1165
|
+
callId: parent.callId,
|
|
1166
|
+
...parent.runId ? { parentRunId: parent.runId } : {}
|
|
1167
|
+
}, child.turns, ctx);
|
|
1168
|
+
if (child.children && child.children.length > 0) {
|
|
1169
|
+
const deeper = await mergeDescendants({
|
|
1170
|
+
runId,
|
|
1171
|
+
depth,
|
|
1172
|
+
callId: parent.callId,
|
|
1173
|
+
...session ? { session } : {}
|
|
1174
|
+
}, child.children, ctx);
|
|
1175
|
+
touched = touched ?? deeper;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return touched;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Repair the run record's timestamps with journaled values.
|
|
1182
|
+
*
|
|
1183
|
+
* `startRun` / `completeRun` / `errorRun` / `abortRun` stamp `Date.now()`
|
|
1184
|
+
* internally, and these records reach `ctx.set` through `save()` below — so a
|
|
1185
|
+
* live value here changes the state payload on a replay and kills the
|
|
1186
|
+
* invocation with `RestateError 570`. The loop repairs its own runs exactly
|
|
1187
|
+
* this way (`agent.ts`: `sessionRun.startedAt = runStartedAt`, `run.endedAt =
|
|
1188
|
+
* await clock.now()`); the merge path has to do the same.
|
|
1189
|
+
*
|
|
1190
|
+
* Sampling the parent's clock here is not an option: the drain runs inside a
|
|
1191
|
+
* hook the loop may abandon (`settledHook`), and an awaited journal op left in
|
|
1192
|
+
* flight is its own failure mode. Everything below is already journaled by the
|
|
1193
|
+
* time it reaches us — either measured by the child on ITS clock and carried
|
|
1194
|
+
* inside the journaled call result, or read from state the parent's own run
|
|
1195
|
+
* already wrote.
|
|
1196
|
+
*/
|
|
1197
|
+
function stampJournaledRunTimes(session, entry, result) {
|
|
1198
|
+
const merged = session.runs.find((run) => run.id === entry.runId);
|
|
1199
|
+
if (!merged) return;
|
|
1200
|
+
const turns = result?.turns ?? [];
|
|
1201
|
+
const parentStartedAt = entry.parentRunId !== void 0 ? session.runs.find((run) => run.id === entry.parentRunId)?.startedAt : void 0;
|
|
1202
|
+
const startedAt = result?.startedAt ?? turns[0]?.createdAt ?? parentStartedAt;
|
|
1203
|
+
const endedAt = result?.endedAt ?? turns[turns.length - 1]?.createdAt ?? startedAt;
|
|
1204
|
+
if (startedAt !== void 0) merged.startedAt = startedAt;
|
|
1205
|
+
if (endedAt !== void 0) merged.endedAt = endedAt;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Replay a finished child's transcript onto the PARENT's hook bus as `child:*`
|
|
1209
|
+
* events, so hosts that already render subagent activity (`src/output/
|
|
1210
|
+
* terminal.ts`, the GUI engine, any `child:tool:*` listener) light up for
|
|
1211
|
+
* parallel children without a single change.
|
|
1212
|
+
*
|
|
1213
|
+
* Not live \u2014 hook bubbling cannot cross an invocation boundary, so these fire
|
|
1214
|
+
* when the child finishes, in submission order. Everything here is derived
|
|
1215
|
+
* from the journaled call result, so a parent replay produces the identical
|
|
1216
|
+
* sequence; like the in-process tool's `child:tool:*`, they DO re-fire on that
|
|
1217
|
+
* replay, so hosts dedupe by `callId` / `turnId` or gate on
|
|
1218
|
+
* `createReplayTracker`.
|
|
1219
|
+
*
|
|
1220
|
+
* Only events that describe things the child actually did are synthesized.
|
|
1221
|
+
* `tool:gate` / `tool:transform` / `tool:dispatched` / `tool:cancelled` /
|
|
1222
|
+
* `background:*` describe decisions taken inside another process; inventing
|
|
1223
|
+
* them would be fiction, so they stay absent.
|
|
1224
|
+
*/
|
|
1225
|
+
async function replayChildEvents(child, turns, ctx) {
|
|
1226
|
+
if (turns.length === 0) return;
|
|
1227
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
1228
|
+
for (const turn of turns) for (const block of turn.content) if (block.type === "tool_result") outputs.set(block.callId, {
|
|
1229
|
+
output: block.output,
|
|
1230
|
+
...block.isError !== void 0 ? { isError: block.isError } : {}
|
|
1231
|
+
});
|
|
1232
|
+
const childIdentity = {
|
|
1233
|
+
childId: child.label,
|
|
1234
|
+
depth: child.depth
|
|
1235
|
+
};
|
|
1236
|
+
const identity = {
|
|
1237
|
+
...childIdentity,
|
|
1238
|
+
runId: child.runId,
|
|
1239
|
+
...child.parentRunId !== void 0 ? { parentRunId: child.parentRunId } : {},
|
|
1240
|
+
parentCallId: child.callId
|
|
1241
|
+
};
|
|
1242
|
+
const runToolCounts = {};
|
|
1243
|
+
let assistantTurns = 0;
|
|
1244
|
+
for (const turn of turns) {
|
|
1245
|
+
if (turn.role !== "assistant") continue;
|
|
1246
|
+
assistantTurns += 1;
|
|
1247
|
+
const text = turn.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1248
|
+
if (text.length > 0) await ctx.hooks.callHook("child:stream:text", {
|
|
1249
|
+
...childIdentity,
|
|
1250
|
+
turnId: turn.id,
|
|
1251
|
+
delta: text,
|
|
1252
|
+
text
|
|
1253
|
+
});
|
|
1254
|
+
const turnToolCounts = {};
|
|
1255
|
+
for (const block of turn.content) {
|
|
1256
|
+
if (block.type !== "tool_call") continue;
|
|
1257
|
+
runToolCounts[block.name] = (runToolCounts[block.name] ?? 0) + 1;
|
|
1258
|
+
turnToolCounts[block.name] = (turnToolCounts[block.name] ?? 0) + 1;
|
|
1259
|
+
const base = {
|
|
1260
|
+
...identity,
|
|
1261
|
+
turnId: turn.id,
|
|
1262
|
+
callId: block.id,
|
|
1263
|
+
name: block.name,
|
|
1264
|
+
displayName: block.name,
|
|
1265
|
+
input: block.input,
|
|
1266
|
+
runToolCounts: { ...runToolCounts }
|
|
1267
|
+
};
|
|
1268
|
+
await ctx.hooks.callHook("child:tool:before", base);
|
|
1269
|
+
const settled = outputs.get(block.id);
|
|
1270
|
+
if (settled === void 0) continue;
|
|
1271
|
+
if (settled.isError === true) {
|
|
1272
|
+
await ctx.hooks.callHook("child:tool:error", {
|
|
1273
|
+
...base,
|
|
1274
|
+
error: new Error(toolResultToText(settled.output))
|
|
1275
|
+
});
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
await ctx.hooks.callHook("child:tool:after", {
|
|
1279
|
+
...base,
|
|
1280
|
+
result: settled.output,
|
|
1281
|
+
outputBytes: toolOutputByteLength(settled.output)
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
const usage = turn.usage ?? {
|
|
1285
|
+
input: 0,
|
|
1286
|
+
output: 0
|
|
1287
|
+
};
|
|
1288
|
+
await ctx.hooks.callHook("child:turn:after", {
|
|
1289
|
+
...childIdentity,
|
|
1290
|
+
turn: assistantTurns,
|
|
1291
|
+
turnId: turn.id,
|
|
1292
|
+
usage,
|
|
1293
|
+
message: turn,
|
|
1294
|
+
toolCounts: {
|
|
1295
|
+
turn: { ...turnToolCounts },
|
|
1296
|
+
run: { ...runToolCounts }
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
await ctx.hooks.callHook("child:stream:end", {
|
|
1300
|
+
...childIdentity,
|
|
1301
|
+
turnId: turn.id,
|
|
1302
|
+
text
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
function errorText(entry) {
|
|
1307
|
+
if (entry.failure !== void 0) return entry.failure instanceof Error ? entry.failure.message : String(entry.failure);
|
|
1308
|
+
return entry.result?.error ?? "sub-agent failed";
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Shape the model-facing result. Mirrors the in-process tool's wording so a
|
|
1312
|
+
* host or model tuned on one reads the other.
|
|
1313
|
+
*
|
|
1314
|
+
* `stats.elapsed` is safe to print here even though it is a live measurement:
|
|
1315
|
+
* the child measured it inside ITS invocation and it arrives in the journaled
|
|
1316
|
+
* call result, so the parent replays the same bytes. (Sampling it on the
|
|
1317
|
+
* parent side is what corrupts a transcript — see `docs/RESTATE.md`.)
|
|
1318
|
+
*/
|
|
1319
|
+
/**
|
|
1320
|
+
* Arm the abort path for one child. Nothing is read off the dispatch promise:
|
|
1321
|
+
* touching its `invocationId` mid-flight would register a second consumer on
|
|
1322
|
+
* the runtime's progress channel (see `onAbort`'s note), so the hook gets
|
|
1323
|
+
* labels and the host cancels the tree.
|
|
1324
|
+
*/
|
|
1325
|
+
function watchAbort(entry, ctx) {
|
|
1326
|
+
if (!options.onAbort) return;
|
|
1327
|
+
if (ctx.signal.aborted) {
|
|
1328
|
+
requestCancel(ctx);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const onAbort = () => requestCancel(ctx);
|
|
1332
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
1333
|
+
entry.release = () => ctx.signal.removeEventListener("abort", onAbort);
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Fire the host's abort hook once for the whole in-flight set. Called once per
|
|
1337
|
+
* abort, not once per child: cancelling the tree is a single operation, and a
|
|
1338
|
+
* second call would append a redundant journal command.
|
|
1339
|
+
*/
|
|
1340
|
+
function requestCancel(ctx) {
|
|
1341
|
+
const onAbort = options.onAbort;
|
|
1342
|
+
if (!onAbort || cancelSignalled) return;
|
|
1343
|
+
const inFlight = pending.filter((entry) => !entry.settled);
|
|
1344
|
+
if (inFlight.length === 0) return;
|
|
1345
|
+
cancelSignalled = true;
|
|
1346
|
+
for (const entry of inFlight) entry.cancelRequested = true;
|
|
1347
|
+
onAbort(inFlight.map((entry) => ({
|
|
1348
|
+
label: entry.label,
|
|
1349
|
+
task: entry.task,
|
|
1350
|
+
depth: entry.depth
|
|
1351
|
+
})), ctx);
|
|
1352
|
+
}
|
|
1353
|
+
function settle(entry, value, failure) {
|
|
1354
|
+
entry.settled = true;
|
|
1355
|
+
entry.release?.();
|
|
1356
|
+
if (failure !== void 0) {
|
|
1357
|
+
entry.failure = failure;
|
|
1358
|
+
if (entry.cancelRequested) return `[sub-agent ${entry.label}] Aborted (cancelled)`;
|
|
1359
|
+
return `[sub-agent ${entry.label}] Error: ${errorText(entry)}`;
|
|
1360
|
+
}
|
|
1361
|
+
const result = value ?? {
|
|
1362
|
+
status: "error",
|
|
1363
|
+
text: "",
|
|
1364
|
+
turns: [],
|
|
1365
|
+
stats: ZERO_STATS
|
|
1366
|
+
};
|
|
1367
|
+
entry.result = result;
|
|
1368
|
+
const head = `[sub-agent ${entry.label}]`;
|
|
1369
|
+
const usage = `Tokens: ${formatTokenUsage(result.stats)}`;
|
|
1370
|
+
if (result.status === "completed") return [
|
|
1371
|
+
`${head} Completed in ${result.stats.turns} turns (${result.stats.elapsed}ms)`,
|
|
1372
|
+
usage,
|
|
1373
|
+
"",
|
|
1374
|
+
result.text || "(no text response)"
|
|
1375
|
+
].join("\n");
|
|
1376
|
+
if (result.status === "error") return `${head} Error: ${errorText(entry)}`;
|
|
1377
|
+
return [`${head} ${result.status === "timeout" ? "Timed out" : "Aborted"} after ${result.stats.turns} turns (${result.stats.elapsed}ms)`, usage].join("\n");
|
|
1378
|
+
}
|
|
1379
|
+
return {
|
|
1380
|
+
spec: {
|
|
1381
|
+
name: "spawn",
|
|
1382
|
+
description: options.description ?? DEFAULT_DESCRIPTION,
|
|
1383
|
+
inputSchema: {
|
|
1384
|
+
type: "object",
|
|
1385
|
+
properties: {
|
|
1386
|
+
task: {
|
|
1387
|
+
type: "string",
|
|
1388
|
+
description: "The self-contained task for the sub-agent."
|
|
1389
|
+
},
|
|
1390
|
+
model: {
|
|
1391
|
+
type: "string",
|
|
1392
|
+
description: "Optional model id for the sub-agent."
|
|
1393
|
+
},
|
|
1394
|
+
subagent_type: {
|
|
1395
|
+
type: "string",
|
|
1396
|
+
description: "Optional named sub-agent type."
|
|
1397
|
+
}
|
|
1398
|
+
},
|
|
1399
|
+
required: ["task"]
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
isConcurrencySafe: true,
|
|
1403
|
+
runsNestedAgent: true,
|
|
1404
|
+
durableExecute: (input, ctx) => {
|
|
1405
|
+
const { req, entry } = prepare(input, ctx);
|
|
1406
|
+
const promise = options.dispatch(req, ctx);
|
|
1407
|
+
if (typeof promise.map !== "function") throw new TypeError("restateSpawnTool: `dispatch` must return the runtime's durable promise (e.g. `ctx.serviceClient(svc).runChild(req)`), which exposes `map`.");
|
|
1408
|
+
commit(entry, input, ctx);
|
|
1409
|
+
return promise.map((value, failure) => settle(entry, value, failure));
|
|
1410
|
+
},
|
|
1411
|
+
execute: async (input, ctx) => {
|
|
1412
|
+
const { req, entry } = prepare(input, ctx);
|
|
1413
|
+
const promise = options.dispatch(req, ctx);
|
|
1414
|
+
commit(entry, input, ctx);
|
|
1415
|
+
try {
|
|
1416
|
+
return settle(entry, await promise);
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
return settle(entry, void 0, err);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
//#endregion
|
|
970
1424
|
//#region src/restate/tool-state.ts
|
|
971
1425
|
/** Default virtual-object state key holding the tool-state map. */
|
|
972
1426
|
const DEFAULT_TOOL_STATE_KEY = "zidane_tool_state";
|
|
@@ -1101,6 +1555,6 @@ function restateWaitBackground(options) {
|
|
|
1101
1555
|
};
|
|
1102
1556
|
}
|
|
1103
1557
|
//#endregion
|
|
1104
|
-
export { DEFAULT_TOOL_STATE_KEY, awakeableWithTimeout, createReplayTracker, deterministicUuid, replayAwareAbort, restateAwakeableTool, restateBehavior, restateClock, restateMcpToolWrap, restateProvider, restateSessionStore, restateTool, restateToolBatchExecutor, restateToolStateStore, restateWaitBackground, shouldRethrowRestateControlError, wrapAgentTools };
|
|
1558
|
+
export { DEFAULT_TOOL_STATE_KEY, awakeableWithTimeout, createReplayTracker, deterministicUuid, replayAwareAbort, restateAwakeableTool, restateBehavior, restateClock, restateMcpToolWrap, restateProvider, restateSessionStore, restateSpawnTool, restateTool, restateToolBatchExecutor, restateToolStateStore, restateWaitBackground, shouldRethrowRestateControlError, wrapAgentTools };
|
|
1105
1559
|
|
|
1106
1560
|
//# sourceMappingURL=restate.js.map
|