zidane 2.0.1 → 2.1.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/{agent-D-ZFMbSd.d.ts → agent-DFkSTVKm.d.ts} +447 -310
- package/dist/{chunk-SZA4FKW5.js → chunk-2EQT4EHD.js} +4 -3
- package/dist/{chunk-PASFWG7S.js → chunk-AWDWJ2YJ.js} +260 -48
- package/dist/{chunk-FRNFVKWW.js → chunk-CDRXC7A7.js} +64 -33
- package/dist/{chunk-BCXXXJ3G.js → chunk-DCYJYM3E.js} +99 -37
- package/dist/{chunk-LVC7NQUZ.js → chunk-HJUB63VE.js} +1 -1
- package/dist/{chunk-PJUUYBKF.js → chunk-IJORSHFI.js} +44 -15
- 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-VUVLOTEY.js} +1 -1
- package/dist/contexts.js +1 -1
- package/dist/harnesses.d.ts +1 -1
- package/dist/harnesses.js +6 -6
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -10
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- 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-DWprxufr.d.ts} +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +3 -3
- package/dist/spawn-CW5GEK-T.d.ts +150 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +5 -5
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/package.json +2 -2
- package/dist/spawn-RoqpjYLZ.d.ts +0 -99
|
@@ -165,10 +165,11 @@ function createProcessContext(config) {
|
|
|
165
165
|
});
|
|
166
166
|
return { stdout, stderr, exitCode: 0 };
|
|
167
167
|
} catch (err) {
|
|
168
|
+
const e = err;
|
|
168
169
|
return {
|
|
169
|
-
stdout:
|
|
170
|
-
stderr:
|
|
171
|
-
exitCode:
|
|
170
|
+
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
171
|
+
stderr: typeof e.stderr === "string" ? e.stderr : typeof e.message === "string" ? e.message : String(err),
|
|
172
|
+
exitCode: typeof e.code === "number" ? e.code : 1
|
|
172
173
|
};
|
|
173
174
|
}
|
|
174
175
|
},
|
|
@@ -6,18 +6,18 @@ import {
|
|
|
6
6
|
mergeSkillsConfig,
|
|
7
7
|
resolveSkills,
|
|
8
8
|
validateResourcePath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DCYJYM3E.js";
|
|
10
10
|
import {
|
|
11
11
|
createProcessContext
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-2EQT4EHD.js";
|
|
13
13
|
import {
|
|
14
14
|
connectMcpServers
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-IJORSHFI.js";
|
|
16
16
|
import {
|
|
17
17
|
AgentAbortedError,
|
|
18
18
|
AgentProviderError,
|
|
19
19
|
toTypedError
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-LNN5UTS2.js";
|
|
21
21
|
|
|
22
22
|
// src/tools/glob.ts
|
|
23
23
|
var DEFAULT_LIMIT = 1e3;
|
|
@@ -69,7 +69,8 @@ var glob = {
|
|
|
69
69
|
const entries = ctx.execution.type === "process" ? await globInProcess(pat, ctx.handle.cwd, max) : await globViaShell(pat, ctx, max);
|
|
70
70
|
return entries.length > 0 ? entries.join("\n") : "(no matches)";
|
|
71
71
|
} catch (err) {
|
|
72
|
-
|
|
72
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73
|
+
return `Glob error: ${message}`;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
};
|
|
@@ -211,7 +212,8 @@ function createSkillsReadTool(options) {
|
|
|
211
212
|
try {
|
|
212
213
|
content = await ctx.execution.readFile(ctx.handle, validated.absolutePath);
|
|
213
214
|
} catch (err) {
|
|
214
|
-
|
|
215
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
216
|
+
return `Error reading "${relPath}" in skill "${skillName}": ${message}`;
|
|
215
217
|
}
|
|
216
218
|
if (looksBinary(content)) {
|
|
217
219
|
return JSON.stringify({
|
|
@@ -289,7 +291,8 @@ function createSkillsRunScriptTool(options) {
|
|
|
289
291
|
stderr: result.stderr
|
|
290
292
|
});
|
|
291
293
|
} catch (err) {
|
|
292
|
-
|
|
294
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
295
|
+
return `Error running script "${scriptRel}" for skill "${skillName}": ${message}`;
|
|
293
296
|
}
|
|
294
297
|
}
|
|
295
298
|
};
|
|
@@ -774,19 +777,23 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
774
777
|
hooks: ctx.hooks,
|
|
775
778
|
harness: ctx.harness,
|
|
776
779
|
turnId,
|
|
777
|
-
callId
|
|
780
|
+
callId,
|
|
781
|
+
runId: ctx.runId,
|
|
782
|
+
...ctx.session ? { session: ctx.session } : {},
|
|
783
|
+
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
778
784
|
};
|
|
779
785
|
output = await toolDef.execute(effectiveInput, toolCtx);
|
|
780
786
|
} catch (err) {
|
|
787
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
781
788
|
await ctx.hooks.callHook("tool:error", {
|
|
782
789
|
turnId,
|
|
783
790
|
callId,
|
|
784
791
|
name: call.name,
|
|
785
792
|
displayName,
|
|
786
793
|
input: effectiveInput,
|
|
787
|
-
error
|
|
794
|
+
error
|
|
788
795
|
});
|
|
789
|
-
output = `Tool error: ${
|
|
796
|
+
output = `Tool error: ${error.message}`;
|
|
790
797
|
isError = true;
|
|
791
798
|
}
|
|
792
799
|
const transformCtx = {
|
|
@@ -921,6 +928,57 @@ function buildPromptMessage(provider, parts) {
|
|
|
921
928
|
|
|
922
929
|
// src/agent.ts
|
|
923
930
|
var noTools = { name: "none", system: "You are a helpful assistant.", tools: {} };
|
|
931
|
+
var HOOK_EVENT_NAMES = [
|
|
932
|
+
"system:before",
|
|
933
|
+
"turn:before",
|
|
934
|
+
"turn:after",
|
|
935
|
+
"stream:text",
|
|
936
|
+
"stream:end",
|
|
937
|
+
"stream:thinking",
|
|
938
|
+
"oauth:refresh",
|
|
939
|
+
"tool:gate",
|
|
940
|
+
"tool:before",
|
|
941
|
+
"tool:after",
|
|
942
|
+
"tool:error",
|
|
943
|
+
"tool:transform",
|
|
944
|
+
"context:transform",
|
|
945
|
+
"steer:inject",
|
|
946
|
+
"spawn:before",
|
|
947
|
+
"spawn:complete",
|
|
948
|
+
"spawn:error",
|
|
949
|
+
"child:stream:text",
|
|
950
|
+
"child:stream:thinking",
|
|
951
|
+
"child:stream:end",
|
|
952
|
+
"child:tool:before",
|
|
953
|
+
"child:tool:after",
|
|
954
|
+
"child:tool:error",
|
|
955
|
+
"child:turn:after",
|
|
956
|
+
"mcp:connect",
|
|
957
|
+
"mcp:error",
|
|
958
|
+
"mcp:close",
|
|
959
|
+
"mcp:tool:gate",
|
|
960
|
+
"mcp:tool:before",
|
|
961
|
+
"mcp:tool:after",
|
|
962
|
+
"mcp:tool:transform",
|
|
963
|
+
"mcp:tool:error",
|
|
964
|
+
"skills:resolve",
|
|
965
|
+
"skills:catalog",
|
|
966
|
+
"skills:activate",
|
|
967
|
+
"skills:deactivate",
|
|
968
|
+
"usage",
|
|
969
|
+
"output",
|
|
970
|
+
"agent:abort",
|
|
971
|
+
"agent:done",
|
|
972
|
+
"session:start",
|
|
973
|
+
"session:end",
|
|
974
|
+
"session:turns",
|
|
975
|
+
"session:meta",
|
|
976
|
+
"session:save"
|
|
977
|
+
];
|
|
978
|
+
var HOOK_EVENT_SET = new Set(HOOK_EVENT_NAMES);
|
|
979
|
+
function isKnownHookEvent(event) {
|
|
980
|
+
return HOOK_EVENT_SET.has(event);
|
|
981
|
+
}
|
|
924
982
|
function resolveBehavior(harnessBehavior, agentBehavior, runBehavior) {
|
|
925
983
|
return {
|
|
926
984
|
toolExecution: runBehavior?.toolExecution ?? agentBehavior?.toolExecution ?? harnessBehavior?.toolExecution ?? "parallel",
|
|
@@ -971,7 +1029,10 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
971
1029
|
abortController = new AbortController();
|
|
972
1030
|
const runId = `run_${++runCounter}`;
|
|
973
1031
|
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
|
|
1032
|
+
session?.startRun(runId, promptLabel, {
|
|
1033
|
+
...options.parentRunId ? { parentRunId: options.parentRunId } : {},
|
|
1034
|
+
depth: typeof options.depth === "number" ? options.depth : 0
|
|
1035
|
+
});
|
|
975
1036
|
if (session) {
|
|
976
1037
|
await session.updateStatus("running");
|
|
977
1038
|
await hooks.callHook("session:start", { sessionId: session.id, runId, prompt: promptLabel });
|
|
@@ -994,6 +1055,11 @@ function createAgent({ harness: harnessOption, provider, behavior: agentBehavior
|
|
|
994
1055
|
const perRunUnregisters = [];
|
|
995
1056
|
if (options.hooks) {
|
|
996
1057
|
for (const [event, handler] of Object.entries(options.hooks)) {
|
|
1058
|
+
if (!isKnownHookEvent(event)) {
|
|
1059
|
+
throw new Error(
|
|
1060
|
+
`Unknown hook event "${event}" passed to run(). See AgentHooks for valid events.`
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
997
1063
|
const handlerList = Array.isArray(handler) ? handler : [handler];
|
|
998
1064
|
for (const fn of handlerList) {
|
|
999
1065
|
if (typeof fn !== "function")
|
|
@@ -1140,6 +1206,7 @@ ${skillsCatalog}`;
|
|
|
1140
1206
|
}
|
|
1141
1207
|
const uninstallAllowedToolsGate = installAllowedToolsGate(hooks, skillActivationState);
|
|
1142
1208
|
const runStartMs = Date.now();
|
|
1209
|
+
const runDepth = typeof options.depth === "number" ? options.depth : 0;
|
|
1143
1210
|
try {
|
|
1144
1211
|
const stats = await runLoop({
|
|
1145
1212
|
provider,
|
|
@@ -1162,6 +1229,8 @@ ${skillsCatalog}`;
|
|
|
1162
1229
|
generateTurnId: () => session?.generateTurnId() ?? crypto.randomUUID(),
|
|
1163
1230
|
maxTurns,
|
|
1164
1231
|
maxTokens,
|
|
1232
|
+
...session ? { session } : {},
|
|
1233
|
+
depth: runDepth,
|
|
1165
1234
|
thinkingBudget,
|
|
1166
1235
|
schema,
|
|
1167
1236
|
runStartMs
|
|
@@ -1199,7 +1268,8 @@ ${skillsCatalog}`;
|
|
|
1199
1268
|
await hooks.callHook("agent:done", stats);
|
|
1200
1269
|
return stats;
|
|
1201
1270
|
}
|
|
1202
|
-
|
|
1271
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1272
|
+
session?.errorRun(runId, message);
|
|
1203
1273
|
await finalizeSession("error");
|
|
1204
1274
|
throw err;
|
|
1205
1275
|
} finally {
|
|
@@ -1230,12 +1300,13 @@ ${skillsCatalog}`;
|
|
|
1230
1300
|
function waitForIdle() {
|
|
1231
1301
|
return idlePromise ?? Promise.resolve();
|
|
1232
1302
|
}
|
|
1233
|
-
function reset() {
|
|
1303
|
+
async function reset() {
|
|
1234
1304
|
conversationTurns = [];
|
|
1235
1305
|
steeringQueue.length = 0;
|
|
1236
1306
|
followUpQueue.length = 0;
|
|
1237
|
-
|
|
1238
|
-
|
|
1307
|
+
const cleared = skillActivationState.clear();
|
|
1308
|
+
for (const record of cleared)
|
|
1309
|
+
await hooks.callHook("skills:deactivate", { skill: record.skill, reason: "reset" });
|
|
1239
1310
|
}
|
|
1240
1311
|
async function activateSkill(name) {
|
|
1241
1312
|
if (!resolvedSkills) {
|
|
@@ -1271,10 +1342,16 @@ ${skillsCatalog}`;
|
|
|
1271
1342
|
};
|
|
1272
1343
|
session.setMeta = (key, value) => {
|
|
1273
1344
|
originalSetMeta(key, value);
|
|
1274
|
-
hooks.callHook("session:meta", { sessionId: session.id, key, value })
|
|
1345
|
+
void Promise.resolve(hooks.callHook("session:meta", { sessionId: session.id, key, value })).catch((err) => {
|
|
1346
|
+
console.error("[zidane] session:meta listener rejected:", err);
|
|
1347
|
+
});
|
|
1275
1348
|
};
|
|
1276
1349
|
}
|
|
1350
|
+
let destroyed = false;
|
|
1277
1351
|
async function destroy() {
|
|
1352
|
+
if (destroyed)
|
|
1353
|
+
return;
|
|
1354
|
+
destroyed = true;
|
|
1278
1355
|
if (mcpConnection) {
|
|
1279
1356
|
await mcpConnection.close();
|
|
1280
1357
|
mcpConnection = null;
|
|
@@ -1313,11 +1390,34 @@ ${skillsCatalog}`;
|
|
|
1313
1390
|
get activeSkills() {
|
|
1314
1391
|
return skillActivationState.active();
|
|
1315
1392
|
},
|
|
1316
|
-
|
|
1393
|
+
// Expose a frozen view of provider.meta. Hosts previously could mutate
|
|
1394
|
+
// the underlying provider meta (e.g. via `agent.meta.defaultModel = …`),
|
|
1395
|
+
// which quietly affected every other agent sharing the same provider
|
|
1396
|
+
// instance. Freezing forces callers to construct a new provider when
|
|
1397
|
+
// they want to override model/capabilities.
|
|
1398
|
+
meta: Object.freeze({ ...provider.meta })
|
|
1317
1399
|
};
|
|
1318
1400
|
}
|
|
1319
1401
|
|
|
1320
1402
|
// src/tools/spawn.ts
|
|
1403
|
+
var BUBBLED_EVENTS = [
|
|
1404
|
+
"stream:text",
|
|
1405
|
+
"stream:thinking",
|
|
1406
|
+
"stream:end",
|
|
1407
|
+
"tool:before",
|
|
1408
|
+
"tool:after",
|
|
1409
|
+
"tool:error",
|
|
1410
|
+
"turn:after"
|
|
1411
|
+
];
|
|
1412
|
+
var CHILD_EVENT_NAME = {
|
|
1413
|
+
"stream:text": "child:stream:text",
|
|
1414
|
+
"stream:thinking": "child:stream:thinking",
|
|
1415
|
+
"stream:end": "child:stream:end",
|
|
1416
|
+
"tool:before": "child:tool:before",
|
|
1417
|
+
"tool:after": "child:tool:after",
|
|
1418
|
+
"tool:error": "child:tool:error",
|
|
1419
|
+
"turn:after": "child:turn:after"
|
|
1420
|
+
};
|
|
1321
1421
|
function extractText(message) {
|
|
1322
1422
|
if (!message || typeof message !== "object")
|
|
1323
1423
|
return "";
|
|
@@ -1325,15 +1425,60 @@ function extractText(message) {
|
|
|
1325
1425
|
if (typeof msg.content === "string")
|
|
1326
1426
|
return msg.content;
|
|
1327
1427
|
if (Array.isArray(msg.content)) {
|
|
1328
|
-
return msg.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
1428
|
+
return msg.content.filter((block) => !!block && typeof block === "object" && block.type === "text").map((block) => block.text).join("\n");
|
|
1329
1429
|
}
|
|
1330
1430
|
return "";
|
|
1331
1431
|
}
|
|
1432
|
+
async function raceWithTimeout(task, timeoutMs) {
|
|
1433
|
+
if (!timeoutMs || timeoutMs <= 0)
|
|
1434
|
+
return task;
|
|
1435
|
+
let timer;
|
|
1436
|
+
try {
|
|
1437
|
+
return await new Promise((resolve, reject) => {
|
|
1438
|
+
timer = setTimeout(() => reject(new SpawnTimeoutError(timeoutMs)), timeoutMs);
|
|
1439
|
+
task.then(resolve, reject);
|
|
1440
|
+
});
|
|
1441
|
+
} finally {
|
|
1442
|
+
if (timer)
|
|
1443
|
+
clearTimeout(timer);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
var SpawnTimeoutError = class extends Error {
|
|
1447
|
+
timeoutMs;
|
|
1448
|
+
constructor(timeoutMs) {
|
|
1449
|
+
super(`Child agent timed out after ${timeoutMs}ms`);
|
|
1450
|
+
this.name = "SpawnTimeoutError";
|
|
1451
|
+
this.timeoutMs = timeoutMs;
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
function bubbleHooks(childHooks, parentHooks, childId, depth) {
|
|
1455
|
+
const unregisters = [];
|
|
1456
|
+
const fire = parentHooks.callHook;
|
|
1457
|
+
for (const evt of BUBBLED_EVENTS) {
|
|
1458
|
+
const parentEvt = CHILD_EVENT_NAME[evt];
|
|
1459
|
+
const unregister = childHooks.hook(evt, (ctx) => {
|
|
1460
|
+
void fire(parentEvt, { ...ctx, childId, depth });
|
|
1461
|
+
});
|
|
1462
|
+
unregisters.push(unregister);
|
|
1463
|
+
}
|
|
1464
|
+
for (const evt of BUBBLED_EVENTS) {
|
|
1465
|
+
const parentEvt = CHILD_EVENT_NAME[evt];
|
|
1466
|
+
const unregister = childHooks.hook(parentEvt, (ctx) => {
|
|
1467
|
+
void fire(parentEvt, ctx);
|
|
1468
|
+
});
|
|
1469
|
+
unregisters.push(unregister);
|
|
1470
|
+
}
|
|
1471
|
+
return () => {
|
|
1472
|
+
for (const u of unregisters) u();
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1332
1475
|
function createSpawnTool(options = {}) {
|
|
1333
1476
|
const localChildren = /* @__PURE__ */ new Map();
|
|
1334
1477
|
let localCounter = 0;
|
|
1335
1478
|
let localActiveCount = 0;
|
|
1336
1479
|
const maxConcurrent = options.maxConcurrent ?? 3;
|
|
1480
|
+
const maxDepth = options.maxDepth ?? 3;
|
|
1481
|
+
const forwardHooks = options.forwardHooks ?? true;
|
|
1337
1482
|
const localStats = {
|
|
1338
1483
|
totalIn: 0,
|
|
1339
1484
|
totalOut: 0,
|
|
@@ -1368,51 +1513,118 @@ function createSpawnTool(options = {}) {
|
|
|
1368
1513
|
async execute(input, ctx) {
|
|
1369
1514
|
const task = input.task;
|
|
1370
1515
|
const systemOverride = input.system;
|
|
1516
|
+
const parentDepth = ctx.depth ?? 0;
|
|
1517
|
+
const childDepth = parentDepth + 1;
|
|
1518
|
+
if (childDepth > maxDepth) {
|
|
1519
|
+
return `Cannot spawn: maxDepth=${maxDepth} reached (parent depth=${parentDepth}). Deepen the cap with createSpawnTool({ maxDepth }).`;
|
|
1520
|
+
}
|
|
1371
1521
|
if (localActiveCount >= maxConcurrent) {
|
|
1372
1522
|
return `Cannot spawn: ${localActiveCount}/${maxConcurrent} sub-agents already running. Wait for one to complete.`;
|
|
1373
1523
|
}
|
|
1524
|
+
if (ctx.signal.aborted) {
|
|
1525
|
+
return `[sub-agent pre-aborted] Parent signal was already aborted \u2014 skipped "${task.slice(0, 80)}"`;
|
|
1526
|
+
}
|
|
1374
1527
|
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
1528
|
localActiveCount++;
|
|
1383
|
-
|
|
1384
|
-
|
|
1529
|
+
const child = { id, task, startedAt: Date.now(), depth: childDepth };
|
|
1530
|
+
localChildren.set(id, child);
|
|
1531
|
+
let destroyError;
|
|
1532
|
+
let childRunStatus = "completed";
|
|
1533
|
+
let finalStats;
|
|
1534
|
+
let result = "";
|
|
1535
|
+
let unbubble;
|
|
1385
1536
|
try {
|
|
1386
|
-
const
|
|
1537
|
+
const agent = createAgent({
|
|
1538
|
+
harness: options.harness ?? ctx.harness,
|
|
1539
|
+
provider: ctx.provider,
|
|
1540
|
+
execution: ctx.execution,
|
|
1541
|
+
// Share the parent's session on opt-in. Child turns get appended to
|
|
1542
|
+
// the same session.turns stream with the child's runId; the child
|
|
1543
|
+
// run itself is tagged with parentRunId below, via AgentRunOptions.
|
|
1544
|
+
...options.persist && ctx.session ? { session: ctx.session } : {}
|
|
1545
|
+
});
|
|
1546
|
+
if (forwardHooks)
|
|
1547
|
+
unbubble = bubbleHooks(agent.hooks, ctx.hooks, id, childDepth);
|
|
1548
|
+
options.onSpawn?.(child);
|
|
1549
|
+
await ctx.hooks.callHook("spawn:before", { id, task, depth: childDepth });
|
|
1550
|
+
const runPromise = agent.run({
|
|
1387
1551
|
prompt: task,
|
|
1388
1552
|
model: options.model,
|
|
1389
1553
|
system: systemOverride ?? options.system,
|
|
1390
1554
|
thinking: options.thinking,
|
|
1391
|
-
signal: ctx.signal
|
|
1555
|
+
signal: ctx.signal,
|
|
1556
|
+
depth: childDepth,
|
|
1557
|
+
...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}
|
|
1392
1558
|
});
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1559
|
+
try {
|
|
1560
|
+
finalStats = await raceWithTimeout(runPromise, options.timeoutMs);
|
|
1561
|
+
if (ctx.signal.aborted) {
|
|
1562
|
+
childRunStatus = "aborted";
|
|
1563
|
+
result = [
|
|
1564
|
+
`[sub-agent ${id}] Aborted after ${finalStats.turns} turns (${finalStats.elapsed}ms)`,
|
|
1565
|
+
`Tokens: ${finalStats.totalIn} in / ${finalStats.totalOut} out`
|
|
1566
|
+
].join("\n");
|
|
1567
|
+
} else {
|
|
1568
|
+
const response = extractText(agent.turns.at(-1));
|
|
1569
|
+
result = [
|
|
1570
|
+
`[sub-agent ${id}] Completed in ${finalStats.turns} turns (${finalStats.elapsed}ms)`,
|
|
1571
|
+
`Tokens: ${finalStats.totalIn} in / ${finalStats.totalOut} out`,
|
|
1572
|
+
"",
|
|
1573
|
+
response || "(no text response)"
|
|
1574
|
+
].join("\n");
|
|
1575
|
+
}
|
|
1576
|
+
} catch (err) {
|
|
1577
|
+
if (err instanceof SpawnTimeoutError) {
|
|
1578
|
+
childRunStatus = "timeout";
|
|
1579
|
+
agent.abort();
|
|
1580
|
+
try {
|
|
1581
|
+
finalStats = await runPromise;
|
|
1582
|
+
} catch {
|
|
1583
|
+
finalStats = { totalIn: 0, totalOut: 0, turns: 0, elapsed: err.timeoutMs };
|
|
1584
|
+
}
|
|
1585
|
+
result = `[sub-agent ${id}] Timed out after ${err.timeoutMs}ms`;
|
|
1586
|
+
} else {
|
|
1587
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1588
|
+
childRunStatus = "error";
|
|
1589
|
+
finalStats = { totalIn: 0, totalOut: 0, turns: 0, elapsed: 0 };
|
|
1590
|
+
result = `[sub-agent ${id}] Error: ${error.message}`;
|
|
1591
|
+
await ctx.hooks.callHook("spawn:error", { id, task, depth: childDepth, error });
|
|
1592
|
+
}
|
|
1593
|
+
} finally {
|
|
1594
|
+
try {
|
|
1595
|
+
await agent.destroy();
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
destroyError = err instanceof Error ? err : new Error(String(err));
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if (finalStats) {
|
|
1601
|
+
localStats.totalIn += finalStats.totalIn;
|
|
1602
|
+
localStats.totalOut += finalStats.totalOut;
|
|
1603
|
+
localStats.turns += finalStats.turns;
|
|
1604
|
+
localStats.elapsed += finalStats.elapsed;
|
|
1605
|
+
}
|
|
1606
|
+
const childRunStats = {
|
|
1399
1607
|
id,
|
|
1400
1608
|
task,
|
|
1401
|
-
stats
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1609
|
+
stats: finalStats,
|
|
1610
|
+
depth: childDepth,
|
|
1611
|
+
status: childRunStatus,
|
|
1612
|
+
...finalStats.output ? { output: finalStats.output } : {}
|
|
1613
|
+
};
|
|
1614
|
+
options.onComplete?.(child, finalStats, childRunStatus);
|
|
1615
|
+
await ctx.hooks.callHook("spawn:complete", childRunStats);
|
|
1616
|
+
if (destroyError) {
|
|
1617
|
+
await ctx.hooks.callHook("spawn:error", {
|
|
1618
|
+
id,
|
|
1619
|
+
task,
|
|
1620
|
+
depth: childDepth,
|
|
1621
|
+
error: destroyError
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
return result;
|
|
1413
1625
|
} finally {
|
|
1626
|
+
unbubble?.();
|
|
1414
1627
|
localActiveCount--;
|
|
1415
|
-
await agent.destroy();
|
|
1416
1628
|
localChildren.delete(id);
|
|
1417
1629
|
}
|
|
1418
1630
|
}
|
|
@@ -5,27 +5,39 @@ import {
|
|
|
5
5
|
toAnthropic,
|
|
6
6
|
toolResultsMessage,
|
|
7
7
|
userMessage
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-S3FCOMRI.js";
|
|
9
9
|
import {
|
|
10
10
|
matchesContextExceeded
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
|
|
13
|
-
// src/providers/anthropic.ts
|
|
14
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
15
|
-
import { resolve as resolve2 } from "path";
|
|
11
|
+
} from "./chunk-LNN5UTS2.js";
|
|
16
12
|
|
|
17
13
|
// src/providers/oauth.ts
|
|
18
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
14
|
+
import { existsSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
19
15
|
import { resolve } from "path";
|
|
20
16
|
import { getOAuthApiKey } from "@yaelg/pi-ai/oauth";
|
|
21
|
-
|
|
17
|
+
function credentialsFilePath() {
|
|
18
|
+
return resolve(process.cwd(), ".credentials.json");
|
|
19
|
+
}
|
|
20
|
+
var CREDENTIALS_FILE_MODE = 384;
|
|
21
|
+
var refreshLocks = /* @__PURE__ */ new Map();
|
|
22
22
|
function readOAuthCredentials() {
|
|
23
|
-
|
|
23
|
+
const path = credentialsFilePath();
|
|
24
|
+
if (!existsSync(path))
|
|
24
25
|
return {};
|
|
25
|
-
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(path, "utf-8");
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
30
|
+
return {};
|
|
31
|
+
return parsed;
|
|
32
|
+
} catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
26
35
|
}
|
|
27
36
|
function writeOAuthCredentials(credentials) {
|
|
28
|
-
|
|
37
|
+
const path = credentialsFilePath();
|
|
38
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
39
|
+
writeFileSync(tmp, JSON.stringify(credentials, null, 2), { mode: CREDENTIALS_FILE_MODE });
|
|
40
|
+
renameSync(tmp, path);
|
|
29
41
|
}
|
|
30
42
|
function credentialsFromParams(params, extraKeys = []) {
|
|
31
43
|
if (typeof params?.access !== "string" || typeof params.refresh !== "string" || typeof params.expires !== "number")
|
|
@@ -45,7 +57,10 @@ async function resolveOAuthApiKey(options, callbacks) {
|
|
|
45
57
|
return options.params.apiKey;
|
|
46
58
|
const paramsCredentials = credentialsFromParams(options.params, options.extraCredentialKeys);
|
|
47
59
|
if (paramsCredentials) {
|
|
48
|
-
return await
|
|
60
|
+
return await withRefreshLock(
|
|
61
|
+
`params:${options.providerId}`,
|
|
62
|
+
() => resolveCredentialSource("params", paramsCredentials)
|
|
63
|
+
);
|
|
49
64
|
}
|
|
50
65
|
if (typeof options.params?.access === "string")
|
|
51
66
|
return options.params.access;
|
|
@@ -53,21 +68,23 @@ async function resolveOAuthApiKey(options, callbacks) {
|
|
|
53
68
|
return process.env[options.envKey];
|
|
54
69
|
const readCredentials = options.readCredentials ?? readOAuthCredentials;
|
|
55
70
|
const writeCredentials = options.writeCredentials ?? writeOAuthCredentials;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
return await withRefreshLock(`file:${options.providerId}`, async () => {
|
|
72
|
+
const allCredentials = readCredentials();
|
|
73
|
+
const storedCredentials = allCredentials[options.providerId];
|
|
74
|
+
if (!storedCredentials)
|
|
75
|
+
throw new Error(options.missingError);
|
|
76
|
+
return await resolveCredentialSource("file", storedCredentials, allCredentials, writeCredentials);
|
|
77
|
+
});
|
|
78
|
+
async function resolveCredentialSource(source, current, allCredentials, persistCredentials) {
|
|
62
79
|
try {
|
|
63
80
|
const refreshOAuthApiKey = options.getOAuthApiKey ?? getOAuthApiKey;
|
|
64
81
|
const result = await refreshOAuthApiKey(options.providerId, { [options.providerId]: current });
|
|
65
82
|
if (!result)
|
|
66
83
|
throw new Error(options.missingError);
|
|
67
84
|
if (result.newCredentials !== current) {
|
|
68
|
-
if (source === "file" &&
|
|
69
|
-
|
|
70
|
-
persistCredentials(
|
|
85
|
+
if (source === "file" && allCredentials && persistCredentials) {
|
|
86
|
+
allCredentials[options.providerId] = result.newCredentials;
|
|
87
|
+
persistCredentials(allCredentials);
|
|
71
88
|
}
|
|
72
89
|
await callbacks?.onOAuthRefresh?.({
|
|
73
90
|
provider: options.provider,
|
|
@@ -84,9 +101,22 @@ async function resolveOAuthApiKey(options, callbacks) {
|
|
|
84
101
|
}
|
|
85
102
|
}
|
|
86
103
|
}
|
|
104
|
+
async function withRefreshLock(key, fn) {
|
|
105
|
+
const existing = refreshLocks.get(key);
|
|
106
|
+
if (existing)
|
|
107
|
+
return existing;
|
|
108
|
+
const task = (async () => {
|
|
109
|
+
try {
|
|
110
|
+
return await fn();
|
|
111
|
+
} finally {
|
|
112
|
+
refreshLocks.delete(key);
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
refreshLocks.set(key, task);
|
|
116
|
+
return task;
|
|
117
|
+
}
|
|
87
118
|
|
|
88
119
|
// src/providers/anthropic.ts
|
|
89
|
-
var CREDENTIALS_FILE2 = resolve2(process.cwd(), ".credentials.json");
|
|
90
120
|
var _sdkCtor = null;
|
|
91
121
|
async function loadAnthropicSdk() {
|
|
92
122
|
if (_sdkCtor)
|
|
@@ -109,11 +139,9 @@ function getConfiguredApiKey(anthropicParams) {
|
|
|
109
139
|
return anthropicParams.access;
|
|
110
140
|
if (process.env.ANTHROPIC_API_KEY)
|
|
111
141
|
return process.env.ANTHROPIC_API_KEY;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return creds.anthropic.access;
|
|
116
|
-
}
|
|
142
|
+
const access = readOAuthCredentials().anthropic?.access;
|
|
143
|
+
if (typeof access === "string" && access.length > 0)
|
|
144
|
+
return access;
|
|
117
145
|
throw new Error("No API key found. Run `bun run auth` first.");
|
|
118
146
|
}
|
|
119
147
|
function createClient(SDK, apiKey, isOAuth, baseURL) {
|
|
@@ -183,10 +211,13 @@ function classifyAnthropicError(err) {
|
|
|
183
211
|
message
|
|
184
212
|
};
|
|
185
213
|
}
|
|
214
|
+
const status = anyErr.status;
|
|
215
|
+
const retryable = typeof status === "number" ? status === 429 || status >= 500 && status !== 501 : void 0;
|
|
186
216
|
return {
|
|
187
217
|
kind: "provider_error",
|
|
188
|
-
providerCode: nativeType ?? (
|
|
189
|
-
message
|
|
218
|
+
providerCode: nativeType ?? (status ? String(status) : void 0),
|
|
219
|
+
message,
|
|
220
|
+
...retryable !== void 0 ? { retryable } : {}
|
|
190
221
|
};
|
|
191
222
|
}
|
|
192
223
|
function anthropicPromptMessage(parts) {
|
|
@@ -368,11 +399,11 @@ function anthropic(anthropicParams) {
|
|
|
368
399
|
// src/providers/cerebras.ts
|
|
369
400
|
var BASE_URL = "https://api.cerebras.ai/v1";
|
|
370
401
|
function getApiKey(params) {
|
|
371
|
-
if (params?.apiKey)
|
|
402
|
+
if (typeof params?.apiKey === "string" && params.apiKey.length > 0)
|
|
372
403
|
return params.apiKey;
|
|
373
404
|
if (process.env.CEREBRAS_API_KEY)
|
|
374
405
|
return process.env.CEREBRAS_API_KEY;
|
|
375
|
-
throw new Error("No Cerebras API key found.
|
|
406
|
+
throw new Error("No Cerebras API key found. Pass `apiKey` or set CEREBRAS_API_KEY in your environment.");
|
|
376
407
|
}
|
|
377
408
|
function cerebras(params) {
|
|
378
409
|
const apiKey = getApiKey(params);
|
|
@@ -650,11 +681,11 @@ function openai(params) {
|
|
|
650
681
|
// src/providers/openrouter.ts
|
|
651
682
|
var BASE_URL2 = "https://openrouter.ai/api/v1";
|
|
652
683
|
function getApiKey2(params) {
|
|
653
|
-
if (params?.apiKey)
|
|
684
|
+
if (typeof params?.apiKey === "string" && params.apiKey.length > 0)
|
|
654
685
|
return params.apiKey;
|
|
655
686
|
if (process.env.OPENROUTER_API_KEY)
|
|
656
687
|
return process.env.OPENROUTER_API_KEY;
|
|
657
|
-
throw new Error("No OpenRouter API key found.
|
|
688
|
+
throw new Error("No OpenRouter API key found. Pass `apiKey` or set OPENROUTER_API_KEY in your environment.");
|
|
658
689
|
}
|
|
659
690
|
function openrouter(params) {
|
|
660
691
|
const apiKey = getApiKey2(params);
|