zidane 2.0.1 → 2.2.0
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/README.md +40 -26
- package/dist/{agent-D-ZFMbSd.d.ts → agent-vPBFXnu-.d.ts} +389 -274
- package/dist/{chunk-SZA4FKW5.js → chunk-2EQT4EHD.js} +4 -3
- package/dist/{chunk-PJUUYBKF.js → chunk-37GD7NL3.js} +45 -16
- package/dist/{chunk-LVC7NQUZ.js → chunk-BW3WTFIR.js} +1 -1
- package/dist/{chunk-FRNFVKWW.js → chunk-CDRXC7A7.js} +64 -33
- package/dist/{chunk-PASFWG7S.js → chunk-F5UBXERT.js} +309 -77
- package/dist/{chunk-7JTBBZ2U.js → chunk-LNN5UTS2.js} +8 -0
- package/dist/{chunk-VG2E6YK3.js → chunk-PMCQOMV4.js} +4 -2
- package/dist/{chunk-LN4LLLHA.js → chunk-S3FCOMRI.js} +63 -20
- package/dist/{chunk-OVQ4N64O.js → chunk-SP5NA6WF.js} +6 -12
- package/dist/{chunk-BCXXXJ3G.js → chunk-TPXPVEH6.js} +99 -58
- package/dist/contexts.js +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +16 -16
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/presets.d.ts +33 -0
- package/dist/presets.js +15 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -3
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +3 -3
- package/dist/{skills-use-C4KFVla0.d.ts → skills-use-39cCsA7_.d.ts} +4 -4
- package/dist/skills.d.ts +3 -9
- package/dist/skills.js +3 -5
- package/dist/spawn-Czx3owjX.d.ts +152 -0
- package/dist/tools.d.ts +6 -4
- package/dist/tools.js +5 -5
- package/dist/types.d.ts +3 -2
- package/dist/types.js +1 -1
- package/package.json +5 -5
- package/dist/harnesses.d.ts +0 -4
- package/dist/harnesses.js +0 -17
- package/dist/spawn-RoqpjYLZ.d.ts +0 -99
|
@@ -3,21 +3,20 @@ import {
|
|
|
3
3
|
createSkillActivationState,
|
|
4
4
|
installAllowedToolsGate,
|
|
5
5
|
interpolateShellCommands,
|
|
6
|
-
mergeSkillsConfig,
|
|
7
6
|
resolveSkills,
|
|
8
7
|
validateResourcePath
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TPXPVEH6.js";
|
|
10
9
|
import {
|
|
11
10
|
createProcessContext
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2EQT4EHD.js";
|
|
13
12
|
import {
|
|
14
13
|
connectMcpServers
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-37GD7NL3.js";
|
|
16
15
|
import {
|
|
17
16
|
AgentAbortedError,
|
|
18
17
|
AgentProviderError,
|
|
19
18
|
toTypedError
|
|
20
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-LNN5UTS2.js";
|
|
21
20
|
|
|
22
21
|
// src/tools/glob.ts
|
|
23
22
|
var DEFAULT_LIMIT = 1e3;
|
|
@@ -69,7 +68,8 @@ var glob = {
|
|
|
69
68
|
const entries = ctx.execution.type === "process" ? await globInProcess(pat, ctx.handle.cwd, max) : await globViaShell(pat, ctx, max);
|
|
70
69
|
return entries.length > 0 ? entries.join("\n") : "(no matches)";
|
|
71
70
|
} catch (err) {
|
|
72
|
-
|
|
71
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
return `Glob error: ${message}`;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
};
|
|
@@ -211,7 +211,8 @@ function createSkillsReadTool(options) {
|
|
|
211
211
|
try {
|
|
212
212
|
content = await ctx.execution.readFile(ctx.handle, validated.absolutePath);
|
|
213
213
|
} catch (err) {
|
|
214
|
-
|
|
214
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
215
|
+
return `Error reading "${relPath}" in skill "${skillName}": ${message}`;
|
|
215
216
|
}
|
|
216
217
|
if (looksBinary(content)) {
|
|
217
218
|
return JSON.stringify({
|
|
@@ -289,7 +290,8 @@ function createSkillsRunScriptTool(options) {
|
|
|
289
290
|
stderr: result.stderr
|
|
290
291
|
});
|
|
291
292
|
} catch (err) {
|
|
292
|
-
|
|
293
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
294
|
+
return `Error running script "${scriptRel}" for skill "${skillName}": ${message}`;
|
|
293
295
|
}
|
|
294
296
|
}
|
|
295
297
|
};
|
|
@@ -772,21 +774,31 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
772
774
|
execution: ctx.execution,
|
|
773
775
|
handle: ctx.handle,
|
|
774
776
|
hooks: ctx.hooks,
|
|
775
|
-
|
|
777
|
+
tools: ctx.agentTools,
|
|
778
|
+
...ctx.agentName !== void 0 ? { name: ctx.agentName } : {},
|
|
779
|
+
...ctx.agentSystem !== void 0 ? { system: ctx.agentSystem } : {},
|
|
780
|
+
...ctx.agentToolAliases !== void 0 ? { toolAliases: ctx.agentToolAliases } : {},
|
|
781
|
+
...ctx.agentMcpServers !== void 0 ? { mcpServers: ctx.agentMcpServers } : {},
|
|
782
|
+
...ctx.agentSkills !== void 0 ? { skills: ctx.agentSkills } : {},
|
|
783
|
+
...ctx.agentBehavior !== void 0 ? { behavior: ctx.agentBehavior } : {},
|
|
776
784
|
turnId,
|
|
777
|
-
callId
|
|
785
|
+
callId,
|
|
786
|
+
runId: ctx.runId,
|
|
787
|
+
...ctx.session ? { session: ctx.session } : {},
|
|
788
|
+
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
778
789
|
};
|
|
779
790
|
output = await toolDef.execute(effectiveInput, toolCtx);
|
|
780
791
|
} catch (err) {
|
|
792
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
781
793
|
await ctx.hooks.callHook("tool:error", {
|
|
782
794
|
turnId,
|
|
783
795
|
callId,
|
|
784
796
|
name: call.name,
|
|
785
797
|
displayName,
|
|
786
798
|
input: effectiveInput,
|
|
787
|
-
error
|
|
799
|
+
error
|
|
788
800
|
});
|
|
789
|
-
output = `Tool error: ${
|
|
801
|
+
output = `Tool error: ${error.message}`;
|
|
790
802
|
isError = true;
|
|
791
803
|
}
|
|
792
804
|
const transformCtx = {
|
|
@@ -920,38 +932,88 @@ function buildPromptMessage(provider, parts) {
|
|
|
920
932
|
}
|
|
921
933
|
|
|
922
934
|
// src/agent.ts
|
|
923
|
-
var
|
|
924
|
-
|
|
935
|
+
var HOOK_EVENT_NAMES = [
|
|
936
|
+
"system:before",
|
|
937
|
+
"turn:before",
|
|
938
|
+
"turn:after",
|
|
939
|
+
"stream:text",
|
|
940
|
+
"stream:end",
|
|
941
|
+
"stream:thinking",
|
|
942
|
+
"oauth:refresh",
|
|
943
|
+
"tool:gate",
|
|
944
|
+
"tool:before",
|
|
945
|
+
"tool:after",
|
|
946
|
+
"tool:error",
|
|
947
|
+
"tool:transform",
|
|
948
|
+
"context:transform",
|
|
949
|
+
"steer:inject",
|
|
950
|
+
"spawn:before",
|
|
951
|
+
"spawn:complete",
|
|
952
|
+
"spawn:error",
|
|
953
|
+
"child:stream:text",
|
|
954
|
+
"child:stream:thinking",
|
|
955
|
+
"child:stream:end",
|
|
956
|
+
"child:tool:before",
|
|
957
|
+
"child:tool:after",
|
|
958
|
+
"child:tool:error",
|
|
959
|
+
"child:turn:after",
|
|
960
|
+
"mcp:connect",
|
|
961
|
+
"mcp:error",
|
|
962
|
+
"mcp:close",
|
|
963
|
+
"mcp:tool:gate",
|
|
964
|
+
"mcp:tool:before",
|
|
965
|
+
"mcp:tool:after",
|
|
966
|
+
"mcp:tool:transform",
|
|
967
|
+
"mcp:tool:error",
|
|
968
|
+
"skills:resolve",
|
|
969
|
+
"skills:catalog",
|
|
970
|
+
"skills:activate",
|
|
971
|
+
"skills:deactivate",
|
|
972
|
+
"usage",
|
|
973
|
+
"output",
|
|
974
|
+
"agent:abort",
|
|
975
|
+
"agent:done",
|
|
976
|
+
"session:start",
|
|
977
|
+
"session:end",
|
|
978
|
+
"session:turns",
|
|
979
|
+
"session:meta",
|
|
980
|
+
"session:save"
|
|
981
|
+
];
|
|
982
|
+
var HOOK_EVENT_SET = new Set(HOOK_EVENT_NAMES);
|
|
983
|
+
function isKnownHookEvent(event) {
|
|
984
|
+
return HOOK_EVENT_SET.has(event);
|
|
985
|
+
}
|
|
986
|
+
function resolveBehavior(agentBehavior, runBehavior) {
|
|
925
987
|
return {
|
|
926
|
-
toolExecution: runBehavior?.toolExecution ?? agentBehavior?.toolExecution ??
|
|
927
|
-
maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns
|
|
928
|
-
maxTokens: runBehavior?.maxTokens ?? agentBehavior?.maxTokens
|
|
929
|
-
thinkingBudget: runBehavior?.thinkingBudget ?? agentBehavior?.thinkingBudget
|
|
930
|
-
schema: runBehavior?.schema ?? agentBehavior?.schema
|
|
988
|
+
toolExecution: runBehavior?.toolExecution ?? agentBehavior?.toolExecution ?? "parallel",
|
|
989
|
+
maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,
|
|
990
|
+
maxTokens: runBehavior?.maxTokens ?? agentBehavior?.maxTokens,
|
|
991
|
+
thinkingBudget: runBehavior?.thinkingBudget ?? agentBehavior?.thinkingBudget,
|
|
992
|
+
schema: runBehavior?.schema ?? agentBehavior?.schema
|
|
931
993
|
};
|
|
932
994
|
}
|
|
933
|
-
function createAgent({
|
|
995
|
+
function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, skills: agentSkills, _mcpConnector }) {
|
|
934
996
|
const hooks = createHooks();
|
|
935
|
-
const harness = harnessOption ?? noTools;
|
|
936
997
|
const executionContext = execution ?? createProcessContext();
|
|
998
|
+
const sourceTools = agentTools ?? {};
|
|
937
999
|
let abortController;
|
|
938
1000
|
let running = false;
|
|
939
1001
|
let idleResolve;
|
|
940
1002
|
let idlePromise;
|
|
941
1003
|
let executionHandle = null;
|
|
942
1004
|
let mcpConnection = null;
|
|
943
|
-
const allMcpServers =
|
|
1005
|
+
const allMcpServers = mcpServers ?? [];
|
|
944
1006
|
const steeringQueue = [];
|
|
945
1007
|
const followUpQueue = [];
|
|
946
1008
|
let conversationTurns = session?.turns.slice() ?? [];
|
|
947
1009
|
let runCounter = session?.runs.length ?? 0;
|
|
948
|
-
const
|
|
949
|
-
const skillsEnabledValue =
|
|
1010
|
+
const skillsConfig = agentSkills;
|
|
1011
|
+
const skillsEnabledValue = skillsConfig?.enabled;
|
|
950
1012
|
const skillsDisabled = skillsEnabledValue === false || Array.isArray(skillsEnabledValue) && skillsEnabledValue.length === 0;
|
|
951
1013
|
let resolvedSkills = null;
|
|
952
1014
|
let skillsCatalog = null;
|
|
953
1015
|
const skillActivationState = createSkillActivationState({
|
|
954
|
-
maxActive:
|
|
1016
|
+
maxActive: skillsConfig?.maxActive
|
|
955
1017
|
});
|
|
956
1018
|
async function run(options) {
|
|
957
1019
|
if (running) {
|
|
@@ -971,7 +1033,10 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
971
1033
|
abortController = new AbortController();
|
|
972
1034
|
const runId = `run_${++runCounter}`;
|
|
973
1035
|
const promptLabel = typeof options.prompt === "string" ? options.prompt : Array.isArray(options.prompt) ? options.prompt.filter((p) => p.type === "text").map((p) => p.text).join("\n") : "";
|
|
974
|
-
session?.startRun(runId, promptLabel
|
|
1036
|
+
session?.startRun(runId, promptLabel, {
|
|
1037
|
+
...options.parentRunId ? { parentRunId: options.parentRunId } : {},
|
|
1038
|
+
depth: typeof options.depth === "number" ? options.depth : 0
|
|
1039
|
+
});
|
|
975
1040
|
if (session) {
|
|
976
1041
|
await session.updateStatus("running");
|
|
977
1042
|
await hooks.callHook("session:start", { sessionId: session.id, runId, prompt: promptLabel });
|
|
@@ -994,6 +1059,11 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
994
1059
|
const perRunUnregisters = [];
|
|
995
1060
|
if (options.hooks) {
|
|
996
1061
|
for (const [event, handler] of Object.entries(options.hooks)) {
|
|
1062
|
+
if (!isKnownHookEvent(event)) {
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
`Unknown hook event "${event}" passed to run(). See AgentHooks for valid events.`
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
997
1067
|
const handlerList = Array.isArray(handler) ? handler : [handler];
|
|
998
1068
|
for (const fn of handlerList) {
|
|
999
1069
|
if (typeof fn !== "function")
|
|
@@ -1012,10 +1082,10 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
1012
1082
|
mcpConnection = await connectMcpServers(allMcpServers, void 0, hooks);
|
|
1013
1083
|
}
|
|
1014
1084
|
}
|
|
1015
|
-
if (!skillsDisabled &&
|
|
1016
|
-
resolvedSkills = await resolveSkills(
|
|
1085
|
+
if (!skillsDisabled && skillsConfig && !resolvedSkills) {
|
|
1086
|
+
resolvedSkills = await resolveSkills(skillsConfig);
|
|
1017
1087
|
await hooks.callHook("skills:resolve", { skills: resolvedSkills });
|
|
1018
|
-
const skillsToolRegistered =
|
|
1088
|
+
const skillsToolRegistered = skillsConfig?.tool !== false && resolvedSkills.length > 0;
|
|
1019
1089
|
const catalogCtx = {
|
|
1020
1090
|
catalog: buildCatalog(resolvedSkills, { skillsToolRegistered }),
|
|
1021
1091
|
skills: resolvedSkills
|
|
@@ -1045,17 +1115,17 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
1045
1115
|
}
|
|
1046
1116
|
const thinking = options.thinking ?? "off";
|
|
1047
1117
|
const model = options.model ?? provider.meta.defaultModel;
|
|
1048
|
-
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema } = resolveBehavior(
|
|
1049
|
-
let system = options.system ||
|
|
1118
|
+
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema } = resolveBehavior(agentBehavior, options.behavior);
|
|
1119
|
+
let system = options.system || agentSystem || "You are a helpful assistant.";
|
|
1050
1120
|
if (skillsCatalog) {
|
|
1051
1121
|
system = `${system}
|
|
1052
1122
|
|
|
1053
1123
|
${skillsCatalog}`;
|
|
1054
1124
|
}
|
|
1055
|
-
const
|
|
1056
|
-
const shouldInjectSkillTools = options.tools === void 0 && !!resolvedSkills && resolvedSkills.length > 0 &&
|
|
1125
|
+
const runBaseTools = options.tools !== void 0 ? options.tools : mcpConnection ? { ...sourceTools, ...mcpConnection.tools } : sourceTools;
|
|
1126
|
+
const shouldInjectSkillTools = options.tools === void 0 && !!resolvedSkills && resolvedSkills.length > 0 && skillsConfig?.tool !== false;
|
|
1057
1127
|
const mergedWithSkills = shouldInjectSkillTools ? {
|
|
1058
|
-
// Auto-injected first so
|
|
1128
|
+
// Auto-injected first so agent + MCP tools can override by name.
|
|
1059
1129
|
skills_use: createSkillsUseTool({
|
|
1060
1130
|
catalog: resolvedSkills,
|
|
1061
1131
|
state: skillActivationState,
|
|
@@ -1068,15 +1138,15 @@ ${skillsCatalog}`;
|
|
|
1068
1138
|
skills_run_script: createSkillsRunScriptTool({
|
|
1069
1139
|
catalog: resolvedSkills,
|
|
1070
1140
|
state: skillActivationState,
|
|
1071
|
-
scriptTimeoutMs:
|
|
1141
|
+
scriptTimeoutMs: skillsConfig?.scriptTimeoutMs
|
|
1072
1142
|
}),
|
|
1073
|
-
...
|
|
1074
|
-
} :
|
|
1143
|
+
...runBaseTools
|
|
1144
|
+
} : runBaseTools;
|
|
1075
1145
|
const tools = {};
|
|
1076
1146
|
for (const tool of Object.values(mergedWithSkills)) {
|
|
1077
1147
|
tools[tool.spec.name] = tool;
|
|
1078
1148
|
}
|
|
1079
|
-
const aliasMaps = buildAliasMaps(
|
|
1149
|
+
const aliasMaps = buildAliasMaps(toolAliases, Object.keys(tools));
|
|
1080
1150
|
const toolSpecs = Object.values(tools).map(
|
|
1081
1151
|
(t) => ({
|
|
1082
1152
|
name: aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name,
|
|
@@ -1140,11 +1210,18 @@ ${skillsCatalog}`;
|
|
|
1140
1210
|
}
|
|
1141
1211
|
const uninstallAllowedToolsGate = installAllowedToolsGate(hooks, skillActivationState);
|
|
1142
1212
|
const runStartMs = Date.now();
|
|
1213
|
+
const runDepth = typeof options.depth === "number" ? options.depth : 0;
|
|
1143
1214
|
try {
|
|
1144
1215
|
const stats = await runLoop({
|
|
1145
1216
|
provider,
|
|
1146
1217
|
hooks,
|
|
1147
|
-
|
|
1218
|
+
agentName,
|
|
1219
|
+
agentSystem,
|
|
1220
|
+
agentTools: sourceTools,
|
|
1221
|
+
agentToolAliases: toolAliases,
|
|
1222
|
+
agentMcpServers: mcpServers,
|
|
1223
|
+
agentSkills,
|
|
1224
|
+
agentBehavior,
|
|
1148
1225
|
tools,
|
|
1149
1226
|
formattedTools,
|
|
1150
1227
|
aliasMaps,
|
|
@@ -1162,6 +1239,8 @@ ${skillsCatalog}`;
|
|
|
1162
1239
|
generateTurnId: () => session?.generateTurnId() ?? crypto.randomUUID(),
|
|
1163
1240
|
maxTurns,
|
|
1164
1241
|
maxTokens,
|
|
1242
|
+
...session ? { session } : {},
|
|
1243
|
+
depth: runDepth,
|
|
1165
1244
|
thinkingBudget,
|
|
1166
1245
|
schema,
|
|
1167
1246
|
runStartMs
|
|
@@ -1199,7 +1278,8 @@ ${skillsCatalog}`;
|
|
|
1199
1278
|
await hooks.callHook("agent:done", stats);
|
|
1200
1279
|
return stats;
|
|
1201
1280
|
}
|
|
1202
|
-
|
|
1281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1282
|
+
session?.errorRun(runId, message);
|
|
1203
1283
|
await finalizeSession("error");
|
|
1204
1284
|
throw err;
|
|
1205
1285
|
} finally {
|
|
@@ -1230,12 +1310,13 @@ ${skillsCatalog}`;
|
|
|
1230
1310
|
function waitForIdle() {
|
|
1231
1311
|
return idlePromise ?? Promise.resolve();
|
|
1232
1312
|
}
|
|
1233
|
-
function reset() {
|
|
1313
|
+
async function reset() {
|
|
1234
1314
|
conversationTurns = [];
|
|
1235
1315
|
steeringQueue.length = 0;
|
|
1236
1316
|
followUpQueue.length = 0;
|
|
1237
|
-
|
|
1238
|
-
|
|
1317
|
+
const cleared = skillActivationState.clear();
|
|
1318
|
+
for (const record of cleared)
|
|
1319
|
+
await hooks.callHook("skills:deactivate", { skill: record.skill, reason: "reset" });
|
|
1239
1320
|
}
|
|
1240
1321
|
async function activateSkill(name) {
|
|
1241
1322
|
if (!resolvedSkills) {
|
|
@@ -1251,7 +1332,7 @@ ${skillsCatalog}`;
|
|
|
1251
1332
|
const outcome = skillActivationState.activate(skill, "explicit");
|
|
1252
1333
|
if (outcome === "cap-reached") {
|
|
1253
1334
|
throw new Error(
|
|
1254
|
-
`Cannot activate skill "${name}" \u2014 the maxActive cap of ${
|
|
1335
|
+
`Cannot activate skill "${name}" \u2014 the maxActive cap of ${skillsConfig?.maxActive} has been reached.`
|
|
1255
1336
|
);
|
|
1256
1337
|
}
|
|
1257
1338
|
if (outcome === "ok")
|
|
@@ -1271,10 +1352,16 @@ ${skillsCatalog}`;
|
|
|
1271
1352
|
};
|
|
1272
1353
|
session.setMeta = (key, value) => {
|
|
1273
1354
|
originalSetMeta(key, value);
|
|
1274
|
-
hooks.callHook("session:meta", { sessionId: session.id, key, value })
|
|
1355
|
+
void Promise.resolve(hooks.callHook("session:meta", { sessionId: session.id, key, value })).catch((err) => {
|
|
1356
|
+
console.error("[zidane] session:meta listener rejected:", err);
|
|
1357
|
+
});
|
|
1275
1358
|
};
|
|
1276
1359
|
}
|
|
1360
|
+
let destroyed = false;
|
|
1277
1361
|
async function destroy() {
|
|
1362
|
+
if (destroyed)
|
|
1363
|
+
return;
|
|
1364
|
+
destroyed = true;
|
|
1278
1365
|
if (mcpConnection) {
|
|
1279
1366
|
await mcpConnection.close();
|
|
1280
1367
|
mcpConnection = null;
|
|
@@ -1313,11 +1400,34 @@ ${skillsCatalog}`;
|
|
|
1313
1400
|
get activeSkills() {
|
|
1314
1401
|
return skillActivationState.active();
|
|
1315
1402
|
},
|
|
1316
|
-
|
|
1403
|
+
// Expose a frozen view of provider.meta. Hosts previously could mutate
|
|
1404
|
+
// the underlying provider meta (e.g. via `agent.meta.defaultModel = …`),
|
|
1405
|
+
// which quietly affected every other agent sharing the same provider
|
|
1406
|
+
// instance. Freezing forces callers to construct a new provider when
|
|
1407
|
+
// they want to override model/capabilities.
|
|
1408
|
+
meta: Object.freeze({ ...provider.meta })
|
|
1317
1409
|
};
|
|
1318
1410
|
}
|
|
1319
1411
|
|
|
1320
1412
|
// src/tools/spawn.ts
|
|
1413
|
+
var BUBBLED_EVENTS = [
|
|
1414
|
+
"stream:text",
|
|
1415
|
+
"stream:thinking",
|
|
1416
|
+
"stream:end",
|
|
1417
|
+
"tool:before",
|
|
1418
|
+
"tool:after",
|
|
1419
|
+
"tool:error",
|
|
1420
|
+
"turn:after"
|
|
1421
|
+
];
|
|
1422
|
+
var CHILD_EVENT_NAME = {
|
|
1423
|
+
"stream:text": "child:stream:text",
|
|
1424
|
+
"stream:thinking": "child:stream:thinking",
|
|
1425
|
+
"stream:end": "child:stream:end",
|
|
1426
|
+
"tool:before": "child:tool:before",
|
|
1427
|
+
"tool:after": "child:tool:after",
|
|
1428
|
+
"tool:error": "child:tool:error",
|
|
1429
|
+
"turn:after": "child:turn:after"
|
|
1430
|
+
};
|
|
1321
1431
|
function extractText(message) {
|
|
1322
1432
|
if (!message || typeof message !== "object")
|
|
1323
1433
|
return "";
|
|
@@ -1325,15 +1435,60 @@ function extractText(message) {
|
|
|
1325
1435
|
if (typeof msg.content === "string")
|
|
1326
1436
|
return msg.content;
|
|
1327
1437
|
if (Array.isArray(msg.content)) {
|
|
1328
|
-
return msg.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
1438
|
+
return msg.content.filter((block) => !!block && typeof block === "object" && block.type === "text").map((block) => block.text).join("\n");
|
|
1329
1439
|
}
|
|
1330
1440
|
return "";
|
|
1331
1441
|
}
|
|
1442
|
+
async function raceWithTimeout(task, timeoutMs) {
|
|
1443
|
+
if (!timeoutMs || timeoutMs <= 0)
|
|
1444
|
+
return task;
|
|
1445
|
+
let timer;
|
|
1446
|
+
try {
|
|
1447
|
+
return await new Promise((resolve, reject) => {
|
|
1448
|
+
timer = setTimeout(() => reject(new SpawnTimeoutError(timeoutMs)), timeoutMs);
|
|
1449
|
+
task.then(resolve, reject);
|
|
1450
|
+
});
|
|
1451
|
+
} finally {
|
|
1452
|
+
if (timer)
|
|
1453
|
+
clearTimeout(timer);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
var SpawnTimeoutError = class extends Error {
|
|
1457
|
+
timeoutMs;
|
|
1458
|
+
constructor(timeoutMs) {
|
|
1459
|
+
super(`Child agent timed out after ${timeoutMs}ms`);
|
|
1460
|
+
this.name = "SpawnTimeoutError";
|
|
1461
|
+
this.timeoutMs = timeoutMs;
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
function bubbleHooks(childHooks, parentHooks, childId, depth) {
|
|
1465
|
+
const unregisters = [];
|
|
1466
|
+
const fire = parentHooks.callHook;
|
|
1467
|
+
for (const evt of BUBBLED_EVENTS) {
|
|
1468
|
+
const parentEvt = CHILD_EVENT_NAME[evt];
|
|
1469
|
+
const unregister = childHooks.hook(evt, (ctx) => {
|
|
1470
|
+
void fire(parentEvt, { ...ctx, childId, depth });
|
|
1471
|
+
});
|
|
1472
|
+
unregisters.push(unregister);
|
|
1473
|
+
}
|
|
1474
|
+
for (const evt of BUBBLED_EVENTS) {
|
|
1475
|
+
const parentEvt = CHILD_EVENT_NAME[evt];
|
|
1476
|
+
const unregister = childHooks.hook(parentEvt, (ctx) => {
|
|
1477
|
+
void fire(parentEvt, ctx);
|
|
1478
|
+
});
|
|
1479
|
+
unregisters.push(unregister);
|
|
1480
|
+
}
|
|
1481
|
+
return () => {
|
|
1482
|
+
for (const u of unregisters) u();
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1332
1485
|
function createSpawnTool(options = {}) {
|
|
1333
1486
|
const localChildren = /* @__PURE__ */ new Map();
|
|
1334
1487
|
let localCounter = 0;
|
|
1335
1488
|
let localActiveCount = 0;
|
|
1336
1489
|
const maxConcurrent = options.maxConcurrent ?? 3;
|
|
1490
|
+
const maxDepth = options.maxDepth ?? 3;
|
|
1491
|
+
const forwardHooks = options.forwardHooks ?? true;
|
|
1337
1492
|
const localStats = {
|
|
1338
1493
|
totalIn: 0,
|
|
1339
1494
|
totalOut: 0,
|
|
@@ -1368,51 +1523,128 @@ function createSpawnTool(options = {}) {
|
|
|
1368
1523
|
async execute(input, ctx) {
|
|
1369
1524
|
const task = input.task;
|
|
1370
1525
|
const systemOverride = input.system;
|
|
1526
|
+
const parentDepth = ctx.depth ?? 0;
|
|
1527
|
+
const childDepth = parentDepth + 1;
|
|
1528
|
+
if (childDepth > maxDepth) {
|
|
1529
|
+
return `Cannot spawn: maxDepth=${maxDepth} reached (parent depth=${parentDepth}). Deepen the cap with createSpawnTool({ maxDepth }).`;
|
|
1530
|
+
}
|
|
1371
1531
|
if (localActiveCount >= maxConcurrent) {
|
|
1372
1532
|
return `Cannot spawn: ${localActiveCount}/${maxConcurrent} sub-agents already running. Wait for one to complete.`;
|
|
1373
1533
|
}
|
|
1534
|
+
if (ctx.signal.aborted) {
|
|
1535
|
+
return `[sub-agent pre-aborted] Parent signal was already aborted \u2014 skipped "${task.slice(0, 80)}"`;
|
|
1536
|
+
}
|
|
1374
1537
|
const id = `child-${++localCounter}`;
|
|
1375
|
-
const child = { id, task, startedAt: Date.now() };
|
|
1376
|
-
const agent = createAgent({
|
|
1377
|
-
harness: options.harness ?? ctx.harness,
|
|
1378
|
-
provider: ctx.provider,
|
|
1379
|
-
execution: ctx.execution
|
|
1380
|
-
});
|
|
1381
|
-
localChildren.set(id, child);
|
|
1382
1538
|
localActiveCount++;
|
|
1383
|
-
|
|
1384
|
-
|
|
1539
|
+
const child = { id, task, startedAt: Date.now(), depth: childDepth };
|
|
1540
|
+
localChildren.set(id, child);
|
|
1541
|
+
let destroyError;
|
|
1542
|
+
let childRunStatus = "completed";
|
|
1543
|
+
let finalStats;
|
|
1544
|
+
let result = "";
|
|
1545
|
+
let unbubble;
|
|
1385
1546
|
try {
|
|
1386
|
-
const
|
|
1547
|
+
const parentPreset = {
|
|
1548
|
+
...ctx.name !== void 0 ? { name: ctx.name } : {},
|
|
1549
|
+
...ctx.system !== void 0 ? { system: ctx.system } : {},
|
|
1550
|
+
tools: ctx.tools,
|
|
1551
|
+
...ctx.toolAliases !== void 0 ? { toolAliases: ctx.toolAliases } : {},
|
|
1552
|
+
...ctx.mcpServers !== void 0 ? { mcpServers: ctx.mcpServers } : {},
|
|
1553
|
+
...ctx.skills !== void 0 ? { skills: ctx.skills } : {},
|
|
1554
|
+
...ctx.behavior !== void 0 ? { behavior: ctx.behavior } : {}
|
|
1555
|
+
};
|
|
1556
|
+
const agent = createAgent({
|
|
1557
|
+
...parentPreset,
|
|
1558
|
+
...options.preset,
|
|
1559
|
+
provider: ctx.provider,
|
|
1560
|
+
execution: ctx.execution,
|
|
1561
|
+
// Share the parent's session on opt-in. Child turns get appended to
|
|
1562
|
+
// the same session.turns stream with the child's runId; the child
|
|
1563
|
+
// run itself is tagged with parentRunId below, via AgentRunOptions.
|
|
1564
|
+
...options.persist && ctx.session ? { session: ctx.session } : {}
|
|
1565
|
+
});
|
|
1566
|
+
if (forwardHooks)
|
|
1567
|
+
unbubble = bubbleHooks(agent.hooks, ctx.hooks, id, childDepth);
|
|
1568
|
+
options.onSpawn?.(child);
|
|
1569
|
+
await ctx.hooks.callHook("spawn:before", { id, task, depth: childDepth });
|
|
1570
|
+
const runPromise = agent.run({
|
|
1387
1571
|
prompt: task,
|
|
1388
1572
|
model: options.model,
|
|
1389
1573
|
system: systemOverride ?? options.system,
|
|
1390
1574
|
thinking: options.thinking,
|
|
1391
|
-
signal: ctx.signal
|
|
1575
|
+
signal: ctx.signal,
|
|
1576
|
+
depth: childDepth,
|
|
1577
|
+
...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}
|
|
1392
1578
|
});
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1579
|
+
try {
|
|
1580
|
+
finalStats = await raceWithTimeout(runPromise, options.timeoutMs);
|
|
1581
|
+
if (ctx.signal.aborted) {
|
|
1582
|
+
childRunStatus = "aborted";
|
|
1583
|
+
result = [
|
|
1584
|
+
`[sub-agent ${id}] Aborted after ${finalStats.turns} turns (${finalStats.elapsed}ms)`,
|
|
1585
|
+
`Tokens: ${finalStats.totalIn} in / ${finalStats.totalOut} out`
|
|
1586
|
+
].join("\n");
|
|
1587
|
+
} else {
|
|
1588
|
+
const response = extractText(agent.turns.at(-1));
|
|
1589
|
+
result = [
|
|
1590
|
+
`[sub-agent ${id}] Completed in ${finalStats.turns} turns (${finalStats.elapsed}ms)`,
|
|
1591
|
+
`Tokens: ${finalStats.totalIn} in / ${finalStats.totalOut} out`,
|
|
1592
|
+
"",
|
|
1593
|
+
response || "(no text response)"
|
|
1594
|
+
].join("\n");
|
|
1595
|
+
}
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
if (err instanceof SpawnTimeoutError) {
|
|
1598
|
+
childRunStatus = "timeout";
|
|
1599
|
+
agent.abort();
|
|
1600
|
+
try {
|
|
1601
|
+
finalStats = await runPromise;
|
|
1602
|
+
} catch {
|
|
1603
|
+
finalStats = { totalIn: 0, totalOut: 0, turns: 0, elapsed: err.timeoutMs };
|
|
1604
|
+
}
|
|
1605
|
+
result = `[sub-agent ${id}] Timed out after ${err.timeoutMs}ms`;
|
|
1606
|
+
} else {
|
|
1607
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1608
|
+
childRunStatus = "error";
|
|
1609
|
+
finalStats = { totalIn: 0, totalOut: 0, turns: 0, elapsed: 0 };
|
|
1610
|
+
result = `[sub-agent ${id}] Error: ${error.message}`;
|
|
1611
|
+
await ctx.hooks.callHook("spawn:error", { id, task, depth: childDepth, error });
|
|
1612
|
+
}
|
|
1613
|
+
} finally {
|
|
1614
|
+
try {
|
|
1615
|
+
await agent.destroy();
|
|
1616
|
+
} catch (err) {
|
|
1617
|
+
destroyError = err instanceof Error ? err : new Error(String(err));
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
if (finalStats) {
|
|
1621
|
+
localStats.totalIn += finalStats.totalIn;
|
|
1622
|
+
localStats.totalOut += finalStats.totalOut;
|
|
1623
|
+
localStats.turns += finalStats.turns;
|
|
1624
|
+
localStats.elapsed += finalStats.elapsed;
|
|
1625
|
+
}
|
|
1626
|
+
const childRunStats = {
|
|
1399
1627
|
id,
|
|
1400
1628
|
task,
|
|
1401
|
-
stats
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1629
|
+
stats: finalStats,
|
|
1630
|
+
depth: childDepth,
|
|
1631
|
+
status: childRunStatus,
|
|
1632
|
+
...finalStats.output ? { output: finalStats.output } : {}
|
|
1633
|
+
};
|
|
1634
|
+
options.onComplete?.(child, finalStats, childRunStatus);
|
|
1635
|
+
await ctx.hooks.callHook("spawn:complete", childRunStats);
|
|
1636
|
+
if (destroyError) {
|
|
1637
|
+
await ctx.hooks.callHook("spawn:error", {
|
|
1638
|
+
id,
|
|
1639
|
+
task,
|
|
1640
|
+
depth: childDepth,
|
|
1641
|
+
error: destroyError
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
return result;
|
|
1413
1645
|
} finally {
|
|
1646
|
+
unbubble?.();
|
|
1414
1647
|
localActiveCount--;
|
|
1415
|
-
await agent.destroy();
|
|
1416
1648
|
localChildren.delete(id);
|
|
1417
1649
|
}
|
|
1418
1650
|
}
|
|
@@ -14,11 +14,18 @@ var AgentProviderError = class extends Error {
|
|
|
14
14
|
code = "provider_error";
|
|
15
15
|
provider;
|
|
16
16
|
providerCode;
|
|
17
|
+
/**
|
|
18
|
+
* Whether a retry with backoff is likely to succeed. See
|
|
19
|
+
* {@link ClassifiedError.retryable}. Absent when the provider did not
|
|
20
|
+
* classify the error — callers should treat absent as "don't retry".
|
|
21
|
+
*/
|
|
22
|
+
retryable;
|
|
17
23
|
constructor(message, options) {
|
|
18
24
|
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
19
25
|
this.name = "AgentProviderError";
|
|
20
26
|
this.provider = options.provider;
|
|
21
27
|
this.providerCode = options.providerCode;
|
|
28
|
+
this.retryable = options.retryable;
|
|
22
29
|
}
|
|
23
30
|
};
|
|
24
31
|
var AgentAbortedError = class extends Error {
|
|
@@ -74,6 +81,7 @@ function toTypedError(classification, provider, cause) {
|
|
|
74
81
|
return new AgentProviderError(message, {
|
|
75
82
|
provider,
|
|
76
83
|
providerCode: classification.providerCode,
|
|
84
|
+
retryable: classification.retryable,
|
|
77
85
|
cause
|
|
78
86
|
});
|
|
79
87
|
}
|
|
@@ -368,12 +368,14 @@ async function createSession(options = {}) {
|
|
|
368
368
|
get metadata() {
|
|
369
369
|
return data.metadata;
|
|
370
370
|
},
|
|
371
|
-
startRun(runId, prompt) {
|
|
371
|
+
startRun(runId, prompt, extras) {
|
|
372
372
|
data.runs.push({
|
|
373
373
|
id: runId,
|
|
374
374
|
startedAt: Date.now(),
|
|
375
375
|
prompt: prompt ?? "",
|
|
376
|
-
status: "running"
|
|
376
|
+
status: "running",
|
|
377
|
+
...extras?.parentRunId ? { parentRunId: extras.parentRunId } : {},
|
|
378
|
+
...typeof extras?.depth === "number" ? { depth: extras.depth } : {}
|
|
377
379
|
});
|
|
378
380
|
touch();
|
|
379
381
|
},
|