@wrongstack/core 0.2.0 → 0.3.2
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-bridge-DmBiCipY.d.ts → agent-bridge-C3DUGjSb.d.ts} +1 -1
- package/dist/{compactor-DSl2FK7a.d.ts → compactor-DpJBI1YH.d.ts} +8 -2
- package/dist/{config-DXrqb41m.d.ts → config-D2qvAxVd.d.ts} +39 -2
- package/dist/{context-u0bryklF.d.ts → context-IovtuTf8.d.ts} +2 -0
- package/dist/coordination/index.d.ts +11 -11
- package/dist/coordination/index.js +307 -245
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +30 -15
- package/dist/defaults/index.js +1077 -479
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-B6Q03pTu.d.ts → events-BHIQs4o1.d.ts} +34 -1
- package/dist/execution/index.d.ts +17 -14
- package/dist/execution/index.js +166 -18
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.js +241 -0
- package/dist/extension/index.js.map +1 -0
- package/dist/{plugin-CoYYZKdn.d.ts → index-hWNybrNZ.d.ts} +368 -11
- package/dist/index.d.ts +76 -26
- package/dist/index.js +1595 -748
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +191 -20
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +12 -9
- package/dist/kernel/index.js +73 -7
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-BA1Ofmfj.d.ts → mcp-servers-C2OopXOn.d.ts} +21 -5
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +24 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-BDfkxL5C.d.ts → multi-agent-B9a6sflH.d.ts} +2 -2
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-Crkt8wTQ.d.ts → path-resolver--59rCou3.d.ts} +2 -2
- package/dist/provider-runner-B39miKRw.d.ts +36 -0
- package/dist/sdd/index.d.ts +3 -3
- package/dist/{secret-scrubber-3TLUkiCV.d.ts → secret-scrubber-CgG2tV2B.d.ts} +1 -1
- package/dist/{secret-scrubber-CwYliRWd.d.ts → secret-scrubber-Cuy5afaQ.d.ts} +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +24 -1
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BRqzvugb.d.ts → selector-wT2fv9Fg.d.ts} +1 -1
- package/dist/{session-reader-C3x96CDR.d.ts → session-reader-CcPi4BQ8.d.ts} +1 -1
- package/dist/{skill-Bx8jxznf.d.ts → skill-C_7znCIC.d.ts} +2 -2
- package/dist/storage/index.d.ts +7 -6
- package/dist/storage/index.js +204 -14
- package/dist/storage/index.js.map +1 -1
- package/dist/{renderer-0A2ZEtca.d.ts → system-prompt-Dk1qm8ey.d.ts} +30 -2
- package/dist/{tool-executor-CYdZdtno.d.ts → tool-executor-HsBLGRaA.d.ts} +5 -5
- package/dist/types/index.d.ts +16 -16
- package/dist/types/index.js +230 -10
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +23 -2
- package/dist/utils/index.js +117 -2
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/system-prompt-CG9jU5-5.d.ts +0 -31
|
@@ -33,7 +33,7 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
33
33
|
if (mode !== void 0) {
|
|
34
34
|
await fsp4.chmod(tmp, mode);
|
|
35
35
|
}
|
|
36
|
-
await
|
|
36
|
+
await renameWithRetry(tmp, targetPath);
|
|
37
37
|
} catch (err) {
|
|
38
38
|
try {
|
|
39
39
|
await fsp4.unlink(tmp);
|
|
@@ -45,6 +45,29 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
45
45
|
async function ensureDir(dir) {
|
|
46
46
|
await fsp4.mkdir(dir, { recursive: true });
|
|
47
47
|
}
|
|
48
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
49
|
+
async function renameWithRetry(from, to) {
|
|
50
|
+
if (process.platform !== "win32") {
|
|
51
|
+
await fsp4.rename(from, to);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const delays = [10, 25, 60, 120, 250];
|
|
55
|
+
let lastErr;
|
|
56
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
57
|
+
try {
|
|
58
|
+
await fsp4.rename(from, to);
|
|
59
|
+
return;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
lastErr = err;
|
|
62
|
+
const code = err?.code;
|
|
63
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, delays[i]));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw lastErr;
|
|
70
|
+
}
|
|
48
71
|
|
|
49
72
|
// src/storage/director-state.ts
|
|
50
73
|
var DirectorStateCheckpoint = class {
|
|
@@ -267,6 +290,7 @@ var InMemoryAgentBridge = class {
|
|
|
267
290
|
this.stopped = true;
|
|
268
291
|
for (const [, p] of this.pendingRequests) {
|
|
269
292
|
clearTimeout(p.timer);
|
|
293
|
+
p.reject(new Error("Bridge stopped"));
|
|
270
294
|
}
|
|
271
295
|
this.pendingRequests.clear();
|
|
272
296
|
this.inflightGuards.clear();
|
|
@@ -1178,6 +1202,179 @@ function providerErrorToSubagentError(err, message, cause) {
|
|
|
1178
1202
|
}
|
|
1179
1203
|
return { kind: "unknown", message, retryable: err.retryable, cause };
|
|
1180
1204
|
}
|
|
1205
|
+
function makeSpawnTool(director, roster) {
|
|
1206
|
+
const inputSchema = {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
properties: {
|
|
1209
|
+
role: { type: "string", description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields." },
|
|
1210
|
+
name: { type: "string", description: "Display name for the subagent. Required when not using roster." },
|
|
1211
|
+
provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
|
|
1212
|
+
model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
|
|
1213
|
+
systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
|
|
1214
|
+
maxIterations: { type: "number" },
|
|
1215
|
+
maxToolCalls: { type: "number" },
|
|
1216
|
+
maxCostUsd: { type: "number" }
|
|
1217
|
+
},
|
|
1218
|
+
required: []
|
|
1219
|
+
};
|
|
1220
|
+
return {
|
|
1221
|
+
name: "spawn_subagent",
|
|
1222
|
+
description: "Create a new subagent under this director. Returns the subagent id.",
|
|
1223
|
+
usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
|
|
1224
|
+
permission: "auto",
|
|
1225
|
+
mutating: false,
|
|
1226
|
+
inputSchema,
|
|
1227
|
+
async execute(input) {
|
|
1228
|
+
const i = input ?? {};
|
|
1229
|
+
const role = typeof i.role === "string" ? i.role : void 0;
|
|
1230
|
+
const base = role && roster ? roster[role] : void 0;
|
|
1231
|
+
if (role && !base) {
|
|
1232
|
+
return { error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}` };
|
|
1233
|
+
}
|
|
1234
|
+
const cfg = { ...base ?? { name: i.name ?? "subagent" } };
|
|
1235
|
+
if (typeof i.name === "string") cfg.name = i.name;
|
|
1236
|
+
if (typeof i.provider === "string") cfg.provider = i.provider;
|
|
1237
|
+
if (typeof i.model === "string") cfg.model = i.model;
|
|
1238
|
+
if (typeof i.systemPromptOverride === "string") cfg.systemPromptOverride = i.systemPromptOverride;
|
|
1239
|
+
if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
|
|
1240
|
+
if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
|
|
1241
|
+
if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
|
|
1242
|
+
try {
|
|
1243
|
+
const subagentId = await director.spawn(cfg);
|
|
1244
|
+
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
if (err instanceof DirectorBudgetError) {
|
|
1247
|
+
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
1248
|
+
}
|
|
1249
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function makeAssignTool(director) {
|
|
1255
|
+
const inputSchema = {
|
|
1256
|
+
type: "object",
|
|
1257
|
+
properties: {
|
|
1258
|
+
subagentId: { type: "string", description: "Target subagent id. Required." },
|
|
1259
|
+
description: { type: "string", description: "The task in natural language \u2014 what you want this subagent to do." },
|
|
1260
|
+
maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
|
|
1261
|
+
timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
|
|
1262
|
+
},
|
|
1263
|
+
required: ["subagentId", "description"]
|
|
1264
|
+
};
|
|
1265
|
+
return {
|
|
1266
|
+
name: "assign_task",
|
|
1267
|
+
description: "Hand a task to a previously spawned subagent. Returns the task id.",
|
|
1268
|
+
permission: "auto",
|
|
1269
|
+
mutating: false,
|
|
1270
|
+
inputSchema,
|
|
1271
|
+
async execute(input) {
|
|
1272
|
+
const i = input;
|
|
1273
|
+
const task = { id: randomUUID(), description: i.description, subagentId: i.subagentId, maxToolCalls: i.maxToolCalls, timeoutMs: i.timeoutMs };
|
|
1274
|
+
const taskId = await director.assign(task);
|
|
1275
|
+
return { taskId, subagentId: i.subagentId };
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
function makeAwaitTasksTool(director) {
|
|
1280
|
+
return {
|
|
1281
|
+
name: "await_tasks",
|
|
1282
|
+
description: "Block until every named task completes. Returns the array of TaskResult.",
|
|
1283
|
+
permission: "auto",
|
|
1284
|
+
mutating: false,
|
|
1285
|
+
inputSchema: { type: "object", properties: { taskIds: { type: "array", items: { type: "string" }, description: "One or more task ids returned by `assign_task`." } }, required: ["taskIds"] },
|
|
1286
|
+
async execute(input) {
|
|
1287
|
+
const i = input;
|
|
1288
|
+
const results = await director.awaitTasks(i.taskIds);
|
|
1289
|
+
return { results };
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
function makeAskTool(director) {
|
|
1294
|
+
return {
|
|
1295
|
+
name: "ask_subagent",
|
|
1296
|
+
description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge.",
|
|
1297
|
+
permission: "auto",
|
|
1298
|
+
mutating: false,
|
|
1299
|
+
inputSchema: {
|
|
1300
|
+
type: "object",
|
|
1301
|
+
properties: {
|
|
1302
|
+
subagentId: { type: "string", description: "Subagent to ask. Must be a previously spawned id." },
|
|
1303
|
+
question: { type: "string", description: "The question or instruction." },
|
|
1304
|
+
timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
|
|
1305
|
+
},
|
|
1306
|
+
required: ["subagentId", "question"]
|
|
1307
|
+
},
|
|
1308
|
+
async execute(input) {
|
|
1309
|
+
const i = input;
|
|
1310
|
+
try {
|
|
1311
|
+
const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
|
|
1312
|
+
return { ok: true, answer };
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function makeRollUpTool(director) {
|
|
1320
|
+
return {
|
|
1321
|
+
name: "roll_up",
|
|
1322
|
+
description: "Aggregate completed task results into a single formatted summary.",
|
|
1323
|
+
permission: "auto",
|
|
1324
|
+
mutating: false,
|
|
1325
|
+
inputSchema: {
|
|
1326
|
+
type: "object",
|
|
1327
|
+
properties: {
|
|
1328
|
+
taskIds: { type: "array", items: { type: "string" }, description: "Completed task ids to aggregate." },
|
|
1329
|
+
style: { type: "string", enum: ["markdown", "json"], description: "Output flavor \u2014 markdown (default) or json." }
|
|
1330
|
+
},
|
|
1331
|
+
required: ["taskIds"]
|
|
1332
|
+
},
|
|
1333
|
+
async execute(input) {
|
|
1334
|
+
const i = input;
|
|
1335
|
+
const summary = director.rollUp(i.taskIds, i.style ?? "markdown");
|
|
1336
|
+
return { summary, count: i.taskIds.length };
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function makeTerminateTool(director) {
|
|
1341
|
+
return {
|
|
1342
|
+
name: "terminate_subagent",
|
|
1343
|
+
description: "Forcibly abort a subagent.",
|
|
1344
|
+
permission: "auto",
|
|
1345
|
+
mutating: true,
|
|
1346
|
+
inputSchema: { type: "object", properties: { subagentId: { type: "string", description: "Subagent to abort." } }, required: ["subagentId"] },
|
|
1347
|
+
async execute(input) {
|
|
1348
|
+
const i = input;
|
|
1349
|
+
await director.terminate(i.subagentId);
|
|
1350
|
+
return { ok: true };
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
function makeFleetStatusTool(director) {
|
|
1355
|
+
return {
|
|
1356
|
+
name: "fleet_status",
|
|
1357
|
+
description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts.",
|
|
1358
|
+
permission: "auto",
|
|
1359
|
+
mutating: false,
|
|
1360
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1361
|
+
async execute() {
|
|
1362
|
+
return director.status();
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function makeFleetUsageTool(director) {
|
|
1367
|
+
return {
|
|
1368
|
+
name: "fleet_usage",
|
|
1369
|
+
description: "Token + cost breakdown across the fleet, per-subagent and totals.",
|
|
1370
|
+
permission: "auto",
|
|
1371
|
+
mutating: false,
|
|
1372
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1373
|
+
async execute() {
|
|
1374
|
+
return director.snapshot();
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1181
1378
|
|
|
1182
1379
|
// src/coordination/director.ts
|
|
1183
1380
|
var DirectorBudgetError = class extends Error {
|
|
@@ -1736,242 +1933,6 @@ var Director = class {
|
|
|
1736
1933
|
return t;
|
|
1737
1934
|
}
|
|
1738
1935
|
};
|
|
1739
|
-
function makeSpawnTool(director, roster) {
|
|
1740
|
-
const inputSchema = {
|
|
1741
|
-
type: "object",
|
|
1742
|
-
properties: {
|
|
1743
|
-
role: {
|
|
1744
|
-
type: "string",
|
|
1745
|
-
description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields."
|
|
1746
|
-
},
|
|
1747
|
-
name: {
|
|
1748
|
-
type: "string",
|
|
1749
|
-
description: "Display name for the subagent. Required when not using roster."
|
|
1750
|
-
},
|
|
1751
|
-
provider: {
|
|
1752
|
-
type: "string",
|
|
1753
|
-
description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.'
|
|
1754
|
-
},
|
|
1755
|
-
model: {
|
|
1756
|
-
type: "string",
|
|
1757
|
-
description: "Model id within the provider. Defaults to the leader model when omitted."
|
|
1758
|
-
},
|
|
1759
|
-
systemPromptOverride: {
|
|
1760
|
-
type: "string",
|
|
1761
|
-
description: "Extra prompt text appended after the role-base prompt."
|
|
1762
|
-
},
|
|
1763
|
-
maxIterations: { type: "number" },
|
|
1764
|
-
maxToolCalls: { type: "number" },
|
|
1765
|
-
maxCostUsd: { type: "number" }
|
|
1766
|
-
},
|
|
1767
|
-
required: []
|
|
1768
|
-
};
|
|
1769
|
-
return {
|
|
1770
|
-
name: "spawn_subagent",
|
|
1771
|
-
description: "Create a new subagent under this director. Returns the subagent id. Use this when you need a worker with a specific provider, model, or role to handle a piece of the plan.",
|
|
1772
|
-
usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
|
|
1773
|
-
permission: "auto",
|
|
1774
|
-
mutating: false,
|
|
1775
|
-
inputSchema,
|
|
1776
|
-
async execute(input) {
|
|
1777
|
-
const i = input ?? {};
|
|
1778
|
-
const role = typeof i.role === "string" ? i.role : void 0;
|
|
1779
|
-
const base = role && roster ? roster[role] : void 0;
|
|
1780
|
-
if (role && !base) {
|
|
1781
|
-
return {
|
|
1782
|
-
error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}`
|
|
1783
|
-
};
|
|
1784
|
-
}
|
|
1785
|
-
const cfg = {
|
|
1786
|
-
...base ?? { name: i.name ?? "subagent" }
|
|
1787
|
-
};
|
|
1788
|
-
if (typeof i.name === "string") cfg.name = i.name;
|
|
1789
|
-
if (typeof i.provider === "string") cfg.provider = i.provider;
|
|
1790
|
-
if (typeof i.model === "string") cfg.model = i.model;
|
|
1791
|
-
if (typeof i.systemPromptOverride === "string")
|
|
1792
|
-
cfg.systemPromptOverride = i.systemPromptOverride;
|
|
1793
|
-
if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
|
|
1794
|
-
if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
|
|
1795
|
-
if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
|
|
1796
|
-
try {
|
|
1797
|
-
const subagentId = await director.spawn(cfg);
|
|
1798
|
-
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
|
|
1799
|
-
} catch (err) {
|
|
1800
|
-
if (err instanceof DirectorBudgetError) {
|
|
1801
|
-
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
1802
|
-
}
|
|
1803
|
-
return { error: err instanceof Error ? err.message : String(err) };
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
};
|
|
1807
|
-
}
|
|
1808
|
-
function makeAssignTool(director) {
|
|
1809
|
-
const inputSchema = {
|
|
1810
|
-
type: "object",
|
|
1811
|
-
properties: {
|
|
1812
|
-
subagentId: { type: "string", description: "Target subagent id. Required." },
|
|
1813
|
-
description: {
|
|
1814
|
-
type: "string",
|
|
1815
|
-
description: "The task in natural language \u2014 what you want this subagent to do."
|
|
1816
|
-
},
|
|
1817
|
-
maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
|
|
1818
|
-
timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
|
|
1819
|
-
},
|
|
1820
|
-
required: ["subagentId", "description"]
|
|
1821
|
-
};
|
|
1822
|
-
return {
|
|
1823
|
-
name: "assign_task",
|
|
1824
|
-
description: "Hand a task to a previously spawned subagent. Returns the task id \u2014 pass it to `await_tasks` to block on completion.",
|
|
1825
|
-
permission: "auto",
|
|
1826
|
-
mutating: false,
|
|
1827
|
-
inputSchema,
|
|
1828
|
-
async execute(input) {
|
|
1829
|
-
const i = input;
|
|
1830
|
-
const task = {
|
|
1831
|
-
id: randomUUID(),
|
|
1832
|
-
description: i.description,
|
|
1833
|
-
subagentId: i.subagentId,
|
|
1834
|
-
maxToolCalls: i.maxToolCalls,
|
|
1835
|
-
timeoutMs: i.timeoutMs
|
|
1836
|
-
};
|
|
1837
|
-
const taskId = await director.assign(task);
|
|
1838
|
-
return { taskId, subagentId: i.subagentId };
|
|
1839
|
-
}
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
function makeAwaitTasksTool(director) {
|
|
1843
|
-
const inputSchema = {
|
|
1844
|
-
type: "object",
|
|
1845
|
-
properties: {
|
|
1846
|
-
taskIds: {
|
|
1847
|
-
type: "array",
|
|
1848
|
-
items: { type: "string" },
|
|
1849
|
-
description: "One or more task ids returned by `assign_task`. The call blocks until every id resolves."
|
|
1850
|
-
}
|
|
1851
|
-
},
|
|
1852
|
-
required: ["taskIds"]
|
|
1853
|
-
};
|
|
1854
|
-
return {
|
|
1855
|
-
name: "await_tasks",
|
|
1856
|
-
description: "Block until every named task completes. Returns the array of TaskResult \u2014 use this to gather subagent output before deciding the next step.",
|
|
1857
|
-
permission: "auto",
|
|
1858
|
-
mutating: false,
|
|
1859
|
-
inputSchema,
|
|
1860
|
-
async execute(input) {
|
|
1861
|
-
const i = input;
|
|
1862
|
-
const results = await director.awaitTasks(i.taskIds);
|
|
1863
|
-
return { results };
|
|
1864
|
-
}
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
|
-
function makeAskTool(director) {
|
|
1868
|
-
const inputSchema = {
|
|
1869
|
-
type: "object",
|
|
1870
|
-
properties: {
|
|
1871
|
-
subagentId: {
|
|
1872
|
-
type: "string",
|
|
1873
|
-
description: "Subagent to ask. Must be a previously spawned id."
|
|
1874
|
-
},
|
|
1875
|
-
question: {
|
|
1876
|
-
type: "string",
|
|
1877
|
-
description: "The question or instruction. Sent as the bridge message payload."
|
|
1878
|
-
},
|
|
1879
|
-
timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
|
|
1880
|
-
},
|
|
1881
|
-
required: ["subagentId", "question"]
|
|
1882
|
-
};
|
|
1883
|
-
return {
|
|
1884
|
-
name: "ask_subagent",
|
|
1885
|
-
description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge (or the timeout fires). Use this when you need a one-shot answer without spawning a fresh task.",
|
|
1886
|
-
permission: "auto",
|
|
1887
|
-
mutating: false,
|
|
1888
|
-
inputSchema,
|
|
1889
|
-
async execute(input) {
|
|
1890
|
-
const i = input;
|
|
1891
|
-
try {
|
|
1892
|
-
const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
|
|
1893
|
-
return { ok: true, answer };
|
|
1894
|
-
} catch (err) {
|
|
1895
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
function makeRollUpTool(director) {
|
|
1901
|
-
const inputSchema = {
|
|
1902
|
-
type: "object",
|
|
1903
|
-
properties: {
|
|
1904
|
-
taskIds: {
|
|
1905
|
-
type: "array",
|
|
1906
|
-
items: { type: "string" },
|
|
1907
|
-
description: "Completed task ids to aggregate. Pass the ids returned by previous `assign_task` calls."
|
|
1908
|
-
},
|
|
1909
|
-
style: {
|
|
1910
|
-
type: "string",
|
|
1911
|
-
enum: ["markdown", "json"],
|
|
1912
|
-
description: "Output flavor \u2014 markdown (default) for in-prompt summarization, json for structured downstream processing."
|
|
1913
|
-
}
|
|
1914
|
-
},
|
|
1915
|
-
required: ["taskIds"]
|
|
1916
|
-
};
|
|
1917
|
-
return {
|
|
1918
|
-
name: "roll_up",
|
|
1919
|
-
description: "Aggregate completed task results into a single formatted summary. Use this after `await_tasks` to fold subagent outputs back into the director's context before deciding the next step.",
|
|
1920
|
-
permission: "auto",
|
|
1921
|
-
mutating: false,
|
|
1922
|
-
inputSchema,
|
|
1923
|
-
async execute(input) {
|
|
1924
|
-
const i = input;
|
|
1925
|
-
const summary = director.rollUp(i.taskIds, i.style ?? "markdown");
|
|
1926
|
-
return { summary, count: i.taskIds.length };
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
function makeTerminateTool(director) {
|
|
1931
|
-
const inputSchema = {
|
|
1932
|
-
type: "object",
|
|
1933
|
-
properties: {
|
|
1934
|
-
subagentId: { type: "string", description: "Subagent to abort." }
|
|
1935
|
-
},
|
|
1936
|
-
required: ["subagentId"]
|
|
1937
|
-
};
|
|
1938
|
-
return {
|
|
1939
|
-
name: "terminate_subagent",
|
|
1940
|
-
description: 'Forcibly abort a subagent. Use sparingly \u2014 prefer waiting on the natural budget to expire. The current task (if any) ends with status "stopped".',
|
|
1941
|
-
permission: "auto",
|
|
1942
|
-
mutating: true,
|
|
1943
|
-
inputSchema,
|
|
1944
|
-
async execute(input) {
|
|
1945
|
-
const i = input;
|
|
1946
|
-
await director.terminate(i.subagentId);
|
|
1947
|
-
return { ok: true };
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
}
|
|
1951
|
-
function makeFleetStatusTool(director) {
|
|
1952
|
-
return {
|
|
1953
|
-
name: "fleet_status",
|
|
1954
|
-
description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts, and the running total iteration count. Cheap; call freely.",
|
|
1955
|
-
permission: "auto",
|
|
1956
|
-
mutating: false,
|
|
1957
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1958
|
-
async execute() {
|
|
1959
|
-
return director.status();
|
|
1960
|
-
}
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
|
-
function makeFleetUsageTool(director) {
|
|
1964
|
-
return {
|
|
1965
|
-
name: "fleet_usage",
|
|
1966
|
-
description: "Token + cost breakdown across the fleet, per-subagent and totals. Use this to reason about which workers to assign costly tasks to or when to wrap up to stay within budget.",
|
|
1967
|
-
permission: "auto",
|
|
1968
|
-
mutating: false,
|
|
1969
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1970
|
-
async execute() {
|
|
1971
|
-
return director.snapshot();
|
|
1972
|
-
}
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
1936
|
function createDelegateTool(opts) {
|
|
1976
1937
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
1977
1938
|
const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
|
|
@@ -2334,6 +2295,100 @@ function makeAgentSubagentRunner(opts) {
|
|
|
2334
2295
|
function defaultFormatTaskInput(task) {
|
|
2335
2296
|
return task.description ?? "";
|
|
2336
2297
|
}
|
|
2298
|
+
|
|
2299
|
+
// src/utils/message-invariants.ts
|
|
2300
|
+
function repairToolUseAdjacency(messages) {
|
|
2301
|
+
const removedToolUses = [];
|
|
2302
|
+
const removedToolResults = [];
|
|
2303
|
+
let removedMessages = 0;
|
|
2304
|
+
let changed = false;
|
|
2305
|
+
const out = [];
|
|
2306
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2307
|
+
const original = messages[i];
|
|
2308
|
+
let msg = original;
|
|
2309
|
+
if (hasToolUse(msg)) {
|
|
2310
|
+
const nextIds = toolResultIds(messages[i + 1]);
|
|
2311
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
2312
|
+
const next = [];
|
|
2313
|
+
for (const block of blocks) {
|
|
2314
|
+
if (block.type === "tool_use" && !nextIds.has(block.id)) {
|
|
2315
|
+
removedToolUses.push(block.id);
|
|
2316
|
+
changed = true;
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
next.push(block);
|
|
2320
|
+
}
|
|
2321
|
+
return next;
|
|
2322
|
+
});
|
|
2323
|
+
msg = filtered ?? msg;
|
|
2324
|
+
}
|
|
2325
|
+
if (hasToolResult(msg)) {
|
|
2326
|
+
const allowed = toolUseIds(out[out.length - 1]);
|
|
2327
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
2328
|
+
const next = [];
|
|
2329
|
+
for (const block of blocks) {
|
|
2330
|
+
if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
|
|
2331
|
+
removedToolResults.push(block.tool_use_id);
|
|
2332
|
+
changed = true;
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
next.push(block);
|
|
2336
|
+
}
|
|
2337
|
+
return next;
|
|
2338
|
+
});
|
|
2339
|
+
msg = filtered ?? msg;
|
|
2340
|
+
}
|
|
2341
|
+
if (isEmptyMessage(msg)) {
|
|
2342
|
+
removedMessages++;
|
|
2343
|
+
changed = true;
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
out.push(msg);
|
|
2347
|
+
}
|
|
2348
|
+
return {
|
|
2349
|
+
messages: changed ? out : messages,
|
|
2350
|
+
report: { changed, removedToolUses, removedToolResults, removedMessages }
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
function hasToolUse(msg) {
|
|
2354
|
+
return contentBlocks(msg).some((b) => b.type === "tool_use");
|
|
2355
|
+
}
|
|
2356
|
+
function hasToolResult(msg) {
|
|
2357
|
+
return contentBlocks(msg).some((b) => b.type === "tool_result");
|
|
2358
|
+
}
|
|
2359
|
+
function toolUseIds(msg) {
|
|
2360
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2361
|
+
if (!msg || msg.role !== "assistant") return ids;
|
|
2362
|
+
for (const block of contentBlocks(msg)) {
|
|
2363
|
+
if (block.type === "tool_use") ids.add(block.id);
|
|
2364
|
+
}
|
|
2365
|
+
return ids;
|
|
2366
|
+
}
|
|
2367
|
+
function toolResultIds(msg) {
|
|
2368
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2369
|
+
if (!msg || msg.role !== "user") return ids;
|
|
2370
|
+
for (const block of contentBlocks(msg)) {
|
|
2371
|
+
if (block.type === "tool_result") ids.add(block.tool_use_id);
|
|
2372
|
+
}
|
|
2373
|
+
return ids;
|
|
2374
|
+
}
|
|
2375
|
+
function contentBlocks(msg) {
|
|
2376
|
+
return msg && Array.isArray(msg.content) ? msg.content : [];
|
|
2377
|
+
}
|
|
2378
|
+
function mapContent(msg, fn) {
|
|
2379
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
2380
|
+
const next = fn(msg.content);
|
|
2381
|
+
if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
|
|
2382
|
+
return msg;
|
|
2383
|
+
}
|
|
2384
|
+
return { ...msg, content: next };
|
|
2385
|
+
}
|
|
2386
|
+
function isEmptyMessage(msg) {
|
|
2387
|
+
if (typeof msg.content === "string") return msg.content.trim().length === 0;
|
|
2388
|
+
return msg.content.length === 0;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
// src/storage/session-store.ts
|
|
2337
2392
|
var DefaultSessionStore = class {
|
|
2338
2393
|
dir;
|
|
2339
2394
|
events;
|
|
@@ -2535,11 +2590,17 @@ var DefaultSessionStore = class {
|
|
|
2535
2590
|
if (openToolUses.size > 0) {
|
|
2536
2591
|
this.events?.emit("session.damaged", {
|
|
2537
2592
|
sessionId,
|
|
2538
|
-
detail: `${openToolUses.size} tool_use blocks without matching results
|
|
2593
|
+
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
2539
2594
|
});
|
|
2540
|
-
return { messages, usage };
|
|
2541
2595
|
}
|
|
2542
|
-
|
|
2596
|
+
const repaired = repairToolUseAdjacency(messages);
|
|
2597
|
+
if (repaired.report.changed) {
|
|
2598
|
+
this.events?.emit("session.damaged", {
|
|
2599
|
+
sessionId,
|
|
2600
|
+
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
return { messages: repaired.messages, usage };
|
|
2543
2604
|
}
|
|
2544
2605
|
};
|
|
2545
2606
|
var FileSessionWriter = class {
|
|
@@ -2565,6 +2626,7 @@ var FileSessionWriter = class {
|
|
|
2565
2626
|
startedAt;
|
|
2566
2627
|
meta;
|
|
2567
2628
|
closed = false;
|
|
2629
|
+
closing = false;
|
|
2568
2630
|
manifestFile;
|
|
2569
2631
|
summary;
|
|
2570
2632
|
tokenIn = 0;
|
|
@@ -2580,9 +2642,7 @@ var FileSessionWriter = class {
|
|
|
2580
2642
|
resumed;
|
|
2581
2643
|
appendFailCount = 0;
|
|
2582
2644
|
lastAppendWarnAt = 0;
|
|
2583
|
-
async
|
|
2584
|
-
if (this.initDone || this.closed) return;
|
|
2585
|
-
this.initDone = true;
|
|
2645
|
+
async writeSessionStartLazy() {
|
|
2586
2646
|
const record = `${JSON.stringify({
|
|
2587
2647
|
type: this.resumed ? "session_resumed" : "session_start",
|
|
2588
2648
|
ts: this.startedAt,
|
|
@@ -2601,7 +2661,8 @@ var FileSessionWriter = class {
|
|
|
2601
2661
|
async append(event) {
|
|
2602
2662
|
if (this.closed) return;
|
|
2603
2663
|
if (!this.initDone) {
|
|
2604
|
-
|
|
2664
|
+
this.initDone = true;
|
|
2665
|
+
await this.writeSessionStartLazy();
|
|
2605
2666
|
}
|
|
2606
2667
|
this.observeForSummary(event);
|
|
2607
2668
|
try {
|
|
@@ -2642,7 +2703,8 @@ var FileSessionWriter = class {
|
|
|
2642
2703
|
}
|
|
2643
2704
|
}
|
|
2644
2705
|
async close() {
|
|
2645
|
-
if (this.
|
|
2706
|
+
if (this.closing) return;
|
|
2707
|
+
this.closing = true;
|
|
2646
2708
|
this.closed = true;
|
|
2647
2709
|
if (this.manifestFile) {
|
|
2648
2710
|
try {
|