@mrciphersmith/keryx 0.2.44 → 0.2.46
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/cli.js +531 -318
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12213,6 +12213,304 @@ var init_permission_mode = __esm(() => {
|
|
|
12213
12213
|
PERMISSION_MODES = ["ask", "trust", "auto"];
|
|
12214
12214
|
});
|
|
12215
12215
|
|
|
12216
|
+
// src/harness/policy/ranks.ts
|
|
12217
|
+
function axesOf(trustMode) {
|
|
12218
|
+
switch (trustMode) {
|
|
12219
|
+
case "read-only":
|
|
12220
|
+
return { authority: "read-only", inputTrust: "vetted" };
|
|
12221
|
+
case "trusted-local":
|
|
12222
|
+
return { authority: "acting", inputTrust: "vetted" };
|
|
12223
|
+
case "untrusted":
|
|
12224
|
+
return { authority: "acting", inputTrust: "unvetted" };
|
|
12225
|
+
default:
|
|
12226
|
+
return;
|
|
12227
|
+
}
|
|
12228
|
+
}
|
|
12229
|
+
function rankOf(map, value) {
|
|
12230
|
+
return Object.prototype.hasOwnProperty.call(map, value) ? map[value] : undefined;
|
|
12231
|
+
}
|
|
12232
|
+
function exceedingAxes(ceiling, candidate) {
|
|
12233
|
+
const low = axesOf(ceiling);
|
|
12234
|
+
const high = axesOf(candidate);
|
|
12235
|
+
if (low === undefined || high === undefined) {
|
|
12236
|
+
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
12237
|
+
}
|
|
12238
|
+
const exceeded = [];
|
|
12239
|
+
if (AUTHORITY_RANK[high.authority] > AUTHORITY_RANK[low.authority]) {
|
|
12240
|
+
exceeded.push(AUTHORITY_AXIS);
|
|
12241
|
+
}
|
|
12242
|
+
if (INPUT_TRUST_RANK[high.inputTrust] > INPUT_TRUST_RANK[low.inputTrust]) {
|
|
12243
|
+
exceeded.push(INPUT_TRUST_AXIS);
|
|
12244
|
+
}
|
|
12245
|
+
return exceeded;
|
|
12246
|
+
}
|
|
12247
|
+
function broadeningAxes(parent, child) {
|
|
12248
|
+
const above = axesOf(parent);
|
|
12249
|
+
const below = axesOf(child);
|
|
12250
|
+
if (above === undefined || below === undefined) {
|
|
12251
|
+
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
12252
|
+
}
|
|
12253
|
+
const broadened = [];
|
|
12254
|
+
if (AUTHORITY_RANK[below.authority] > AUTHORITY_RANK[above.authority]) {
|
|
12255
|
+
broadened.push(AUTHORITY_AXIS);
|
|
12256
|
+
}
|
|
12257
|
+
if (INPUT_TRUST_RANK[below.inputTrust] < INPUT_TRUST_RANK[above.inputTrust]) {
|
|
12258
|
+
broadened.push(INPUT_TRUST_AXIS);
|
|
12259
|
+
}
|
|
12260
|
+
return broadened;
|
|
12261
|
+
}
|
|
12262
|
+
var AUTHORITY_RANK, INPUT_TRUST_RANK, OUTCOME_RANK, ISOLATION_RANK, AUTHORITY_AXIS = "trustMode.authority", INPUT_TRUST_AXIS = "trustMode.inputTrust";
|
|
12263
|
+
var init_ranks = __esm(() => {
|
|
12264
|
+
AUTHORITY_RANK = {
|
|
12265
|
+
"read-only": 0,
|
|
12266
|
+
acting: 1
|
|
12267
|
+
};
|
|
12268
|
+
INPUT_TRUST_RANK = {
|
|
12269
|
+
unvetted: 0,
|
|
12270
|
+
vetted: 1
|
|
12271
|
+
};
|
|
12272
|
+
OUTCOME_RANK = {
|
|
12273
|
+
deny: 0,
|
|
12274
|
+
ask: 1,
|
|
12275
|
+
allow: 2
|
|
12276
|
+
};
|
|
12277
|
+
ISOLATION_RANK = {
|
|
12278
|
+
"not-required": 0,
|
|
12279
|
+
"required-fail-closed": 1
|
|
12280
|
+
};
|
|
12281
|
+
});
|
|
12282
|
+
|
|
12283
|
+
// src/harness/child/isolation.ts
|
|
12284
|
+
function inheritBudget(parentRemaining, childRequest) {
|
|
12285
|
+
if (childRequest.maxRuntimeMs > parentRemaining.maxRuntimeMs) {
|
|
12286
|
+
return {
|
|
12287
|
+
ok: false,
|
|
12288
|
+
reason: `child maxRuntimeMs ${childRequest.maxRuntimeMs} exceeds parent remaining ${parentRemaining.maxRuntimeMs}`
|
|
12289
|
+
};
|
|
12290
|
+
}
|
|
12291
|
+
if (childRequest.maxToolCalls !== undefined) {
|
|
12292
|
+
if (parentRemaining.maxToolCalls === undefined) {
|
|
12293
|
+
return {
|
|
12294
|
+
ok: false,
|
|
12295
|
+
reason: `child requests ${childRequest.maxToolCalls} tool calls but the parent exposes no tool-call budget to inherit`
|
|
12296
|
+
};
|
|
12297
|
+
}
|
|
12298
|
+
if (childRequest.maxToolCalls > parentRemaining.maxToolCalls) {
|
|
12299
|
+
return {
|
|
12300
|
+
ok: false,
|
|
12301
|
+
reason: `child maxToolCalls ${childRequest.maxToolCalls} exceeds parent remaining ${parentRemaining.maxToolCalls}`
|
|
12302
|
+
};
|
|
12303
|
+
}
|
|
12304
|
+
}
|
|
12305
|
+
const reservation = {
|
|
12306
|
+
reservationId: childRequest.reservationId,
|
|
12307
|
+
maxRuntimeMs: childRequest.maxRuntimeMs,
|
|
12308
|
+
...childRequest.maxToolCalls !== undefined ? { maxToolCalls: childRequest.maxToolCalls } : {}
|
|
12309
|
+
};
|
|
12310
|
+
return { ok: true, reservation };
|
|
12311
|
+
}
|
|
12312
|
+
function isKnownCapability(value) {
|
|
12313
|
+
return CAPABILITY_KEYS.includes(value);
|
|
12314
|
+
}
|
|
12315
|
+
function inheritPolicy(parent, childRequest) {
|
|
12316
|
+
if (axesOf(childRequest.trustMode) === undefined || axesOf(parent.trustMode) === undefined) {
|
|
12317
|
+
return {
|
|
12318
|
+
ok: false,
|
|
12319
|
+
reason: `unrecognized trustMode (child "${childRequest.trustMode}", parent "${parent.trustMode}")`
|
|
12320
|
+
};
|
|
12321
|
+
}
|
|
12322
|
+
const broadened = broadeningAxes(parent.trustMode, childRequest.trustMode);
|
|
12323
|
+
if (broadened.length > 0) {
|
|
12324
|
+
return {
|
|
12325
|
+
ok: false,
|
|
12326
|
+
reason: `child trustMode "${childRequest.trustMode}" is broader than parent "${parent.trustMode}" on ${broadened.join(", ")}`
|
|
12327
|
+
};
|
|
12328
|
+
}
|
|
12329
|
+
for (const capability of CAPABILITY_KEYS) {
|
|
12330
|
+
const childOutcome = childRequest.defaults[capability];
|
|
12331
|
+
const parentOutcome = parent.defaults[capability];
|
|
12332
|
+
const childRank = rankOf(OUTCOME_RANK, childOutcome);
|
|
12333
|
+
const parentRank = rankOf(OUTCOME_RANK, parentOutcome);
|
|
12334
|
+
if (childRank === undefined || parentRank === undefined) {
|
|
12335
|
+
return {
|
|
12336
|
+
ok: false,
|
|
12337
|
+
reason: `unrecognized capability outcome for "${capability}" (child "${childOutcome}", parent "${parentOutcome}")`
|
|
12338
|
+
};
|
|
12339
|
+
}
|
|
12340
|
+
if (childRank > parentRank) {
|
|
12341
|
+
return {
|
|
12342
|
+
ok: false,
|
|
12343
|
+
reason: `child capability "${capability}" default "${childOutcome}" is more permissive than parent "${parentOutcome}"`
|
|
12344
|
+
};
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12347
|
+
const childIsolation = rankOf(ISOLATION_RANK, childRequest.requiredControls.isolation);
|
|
12348
|
+
const parentIsolation = rankOf(ISOLATION_RANK, parent.requiredControls.isolation);
|
|
12349
|
+
if (childIsolation === undefined || parentIsolation === undefined) {
|
|
12350
|
+
return {
|
|
12351
|
+
ok: false,
|
|
12352
|
+
reason: `unrecognized isolation control (child "${childRequest.requiredControls.isolation}", parent "${parent.requiredControls.isolation}")`
|
|
12353
|
+
};
|
|
12354
|
+
}
|
|
12355
|
+
if (childIsolation < parentIsolation) {
|
|
12356
|
+
return {
|
|
12357
|
+
ok: false,
|
|
12358
|
+
reason: `child isolation "${childRequest.requiredControls.isolation}" is weaker than parent "${parent.requiredControls.isolation}"`
|
|
12359
|
+
};
|
|
12360
|
+
}
|
|
12361
|
+
return { ok: true, policy: childRequest };
|
|
12362
|
+
}
|
|
12363
|
+
function childProvenance(parent, deps) {
|
|
12364
|
+
const taintIds = [...parent.taintIds ?? [], parent.provenanceId];
|
|
12365
|
+
const provenance = {
|
|
12366
|
+
provenanceId: deps.idSeq(),
|
|
12367
|
+
trustLevel: "derived",
|
|
12368
|
+
sourceKind: parent.sourceKind,
|
|
12369
|
+
taintIds
|
|
12370
|
+
};
|
|
12371
|
+
if (parent.sourceHash !== undefined) {
|
|
12372
|
+
provenance.sourceHash = parent.sourceHash;
|
|
12373
|
+
}
|
|
12374
|
+
return provenance;
|
|
12375
|
+
}
|
|
12376
|
+
var CAPABILITY_KEYS;
|
|
12377
|
+
var init_isolation = __esm(() => {
|
|
12378
|
+
init_ranks();
|
|
12379
|
+
CAPABILITY_KEYS = [
|
|
12380
|
+
"read",
|
|
12381
|
+
"write",
|
|
12382
|
+
"shell",
|
|
12383
|
+
"network",
|
|
12384
|
+
"delegate"
|
|
12385
|
+
];
|
|
12386
|
+
});
|
|
12387
|
+
|
|
12388
|
+
// src/harness/parallel/scheduler.ts
|
|
12389
|
+
function byTaskId(a, b) {
|
|
12390
|
+
return a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;
|
|
12391
|
+
}
|
|
12392
|
+
function computeExcluded(tasks) {
|
|
12393
|
+
const excluded = new Set;
|
|
12394
|
+
for (const t of tasks) {
|
|
12395
|
+
if (t.cancelled === true)
|
|
12396
|
+
excluded.add(t.taskId);
|
|
12397
|
+
}
|
|
12398
|
+
let changed = true;
|
|
12399
|
+
while (changed) {
|
|
12400
|
+
changed = false;
|
|
12401
|
+
for (const t of tasks) {
|
|
12402
|
+
if (excluded.has(t.taskId))
|
|
12403
|
+
continue;
|
|
12404
|
+
if (t.dependsOn.some((dep) => excluded.has(dep))) {
|
|
12405
|
+
excluded.add(t.taskId);
|
|
12406
|
+
changed = true;
|
|
12407
|
+
}
|
|
12408
|
+
}
|
|
12409
|
+
}
|
|
12410
|
+
return excluded;
|
|
12411
|
+
}
|
|
12412
|
+
function decrementRemaining(remaining, reservation) {
|
|
12413
|
+
const maxRuntimeMs = remaining.maxRuntimeMs - reservation.maxRuntimeMs;
|
|
12414
|
+
if (remaining.maxToolCalls !== undefined && reservation.maxToolCalls !== undefined) {
|
|
12415
|
+
return { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls - reservation.maxToolCalls };
|
|
12416
|
+
}
|
|
12417
|
+
return remaining.maxToolCalls !== undefined ? { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls } : { maxRuntimeMs };
|
|
12418
|
+
}
|
|
12419
|
+
function planWaves(tasks, config, _deps) {
|
|
12420
|
+
if (!Number.isInteger(config.maxConcurrency) || config.maxConcurrency < 1) {
|
|
12421
|
+
return { ok: false, reason: `maxConcurrency must be a positive integer, got ${config.maxConcurrency}` };
|
|
12422
|
+
}
|
|
12423
|
+
const excluded = computeExcluded(tasks);
|
|
12424
|
+
const universe = tasks.filter((t) => !excluded.has(t.taskId));
|
|
12425
|
+
const scheduled = new Set;
|
|
12426
|
+
const waveTaskLists = [];
|
|
12427
|
+
while (scheduled.size < universe.length) {
|
|
12428
|
+
const ready = universe.filter((t) => !scheduled.has(t.taskId) && t.dependsOn.every((dep) => scheduled.has(dep))).sort(byTaskId);
|
|
12429
|
+
if (ready.length === 0) {
|
|
12430
|
+
return { ok: false, reason: "dependency cycle detected: no ready task set could be formed" };
|
|
12431
|
+
}
|
|
12432
|
+
const waveTasks = ready.slice(0, config.maxConcurrency);
|
|
12433
|
+
for (const t of waveTasks)
|
|
12434
|
+
scheduled.add(t.taskId);
|
|
12435
|
+
waveTaskLists.push(waveTasks);
|
|
12436
|
+
}
|
|
12437
|
+
let remaining = config.parentRemaining;
|
|
12438
|
+
const waves = [];
|
|
12439
|
+
for (const waveTasks of waveTaskLists) {
|
|
12440
|
+
const taskIds = [];
|
|
12441
|
+
const reservations = [];
|
|
12442
|
+
for (const t of waveTasks) {
|
|
12443
|
+
const granted = inheritBudget(remaining, t.budgetRequest);
|
|
12444
|
+
if (!granted.ok) {
|
|
12445
|
+
return { ok: false, reason: granted.reason };
|
|
12446
|
+
}
|
|
12447
|
+
taskIds.push(t.taskId);
|
|
12448
|
+
reservations.push(granted.reservation);
|
|
12449
|
+
remaining = decrementRemaining(remaining, granted.reservation);
|
|
12450
|
+
}
|
|
12451
|
+
waves.push({ taskIds, reservations });
|
|
12452
|
+
}
|
|
12453
|
+
return { ok: true, waves };
|
|
12454
|
+
}
|
|
12455
|
+
async function executeWaves(tasks, waves, deps) {
|
|
12456
|
+
const byTaskId2 = new Map;
|
|
12457
|
+
for (const t of tasks)
|
|
12458
|
+
byTaskId2.set(t.taskId, t);
|
|
12459
|
+
const results = new Map;
|
|
12460
|
+
for (let waveIndex = 0;waveIndex < waves.length; waveIndex++) {
|
|
12461
|
+
const wave = waves[waveIndex];
|
|
12462
|
+
if (wave === undefined)
|
|
12463
|
+
continue;
|
|
12464
|
+
const settled = await Promise.allSettled(wave.taskIds.map((taskId, i) => {
|
|
12465
|
+
const task = byTaskId2.get(taskId);
|
|
12466
|
+
if (task === undefined) {
|
|
12467
|
+
return Promise.reject(new Error(`executeWaves: wave ${waveIndex} references unknown taskId "${taskId}" (not in \`tasks\`)`));
|
|
12468
|
+
}
|
|
12469
|
+
const reservation = wave.reservations[i];
|
|
12470
|
+
if (reservation === undefined) {
|
|
12471
|
+
return Promise.reject(new Error(`executeWaves: wave ${waveIndex} taskIds/reservations length mismatch at index ${i} (malformed Wave)`));
|
|
12472
|
+
}
|
|
12473
|
+
return deps.run(task, reservation);
|
|
12474
|
+
}));
|
|
12475
|
+
const failedTaskIds = [];
|
|
12476
|
+
const causes = [];
|
|
12477
|
+
for (let i = 0;i < settled.length; i++) {
|
|
12478
|
+
const outcome = settled[i];
|
|
12479
|
+
const taskId = wave.taskIds[i];
|
|
12480
|
+
if (outcome === undefined || taskId === undefined)
|
|
12481
|
+
continue;
|
|
12482
|
+
if (outcome.status === "fulfilled") {
|
|
12483
|
+
results.set(taskId, outcome.value);
|
|
12484
|
+
} else {
|
|
12485
|
+
failedTaskIds.push(taskId);
|
|
12486
|
+
causes.push(outcome.reason);
|
|
12487
|
+
}
|
|
12488
|
+
}
|
|
12489
|
+
if (failedTaskIds.length > 0) {
|
|
12490
|
+
throw new WaveExecutionError(waveIndex, failedTaskIds, causes, new Map(results));
|
|
12491
|
+
}
|
|
12492
|
+
}
|
|
12493
|
+
return results;
|
|
12494
|
+
}
|
|
12495
|
+
var WaveExecutionError;
|
|
12496
|
+
var init_scheduler = __esm(() => {
|
|
12497
|
+
init_isolation();
|
|
12498
|
+
WaveExecutionError = class WaveExecutionError extends Error {
|
|
12499
|
+
waveIndex;
|
|
12500
|
+
failedTaskIds;
|
|
12501
|
+
causes;
|
|
12502
|
+
partialResults;
|
|
12503
|
+
constructor(waveIndex, failedTaskIds, causes, partialResults) {
|
|
12504
|
+
super(`executeWaves: wave ${waveIndex} had ${failedTaskIds.length} rejected task(s): ${failedTaskIds.join(", ")}`);
|
|
12505
|
+
this.name = "WaveExecutionError";
|
|
12506
|
+
this.waveIndex = waveIndex;
|
|
12507
|
+
this.failedTaskIds = failedTaskIds;
|
|
12508
|
+
this.causes = causes;
|
|
12509
|
+
this.partialResults = partialResults;
|
|
12510
|
+
}
|
|
12511
|
+
};
|
|
12512
|
+
});
|
|
12513
|
+
|
|
12216
12514
|
// src/ctx/assembly.ts
|
|
12217
12515
|
import { createHash as createHash6 } from "crypto";
|
|
12218
12516
|
import { mkdir as mkdir24, rename as rename2, writeFile as writeFile26 } from "fs/promises";
|
|
@@ -17203,7 +17501,7 @@ ${block}
|
|
|
17203
17501
|
}
|
|
17204
17502
|
async function runAgentTurn(io, deps, history, userLine, options = {}) {
|
|
17205
17503
|
try {
|
|
17206
|
-
await runAgentTurnCore(io, deps, history, userLine, options);
|
|
17504
|
+
return await runAgentTurnCore(io, deps, history, userLine, options);
|
|
17207
17505
|
} finally {
|
|
17208
17506
|
await closeSlateOnFlowDone(io, deps, options);
|
|
17209
17507
|
}
|
|
@@ -17245,7 +17543,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17245
17543
|
io.onSystem?.(`
|
|
17246
17544
|
[stopped] Model turn interrupted by user.
|
|
17247
17545
|
`);
|
|
17248
|
-
return;
|
|
17546
|
+
return {};
|
|
17249
17547
|
}
|
|
17250
17548
|
const toolByName = new Map(deps.tools.map((t) => [t.definition.name, t]));
|
|
17251
17549
|
const toolDefs = deps.tools.map((t) => t.definition);
|
|
@@ -17354,7 +17652,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17354
17652
|
system(`
|
|
17355
17653
|
[stopped] Model turn interrupted by user.
|
|
17356
17654
|
`);
|
|
17357
|
-
return;
|
|
17655
|
+
return {};
|
|
17358
17656
|
}
|
|
17359
17657
|
if (event.kind === "reasoning_delta") {
|
|
17360
17658
|
reasoningText += event.text ?? "";
|
|
@@ -17401,7 +17699,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17401
17699
|
system(`
|
|
17402
17700
|
[stopped] Model turn interrupted by user.
|
|
17403
17701
|
`);
|
|
17404
|
-
return;
|
|
17702
|
+
return {};
|
|
17405
17703
|
}
|
|
17406
17704
|
system(`
|
|
17407
17705
|
[error] ${cause instanceof Error ? cause.message : String(cause)}
|
|
@@ -17417,10 +17715,10 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17417
17715
|
system(`
|
|
17418
17716
|
[stopped] Model turn interrupted by user.
|
|
17419
17717
|
`);
|
|
17420
|
-
return;
|
|
17718
|
+
return {};
|
|
17421
17719
|
}
|
|
17422
17720
|
if (errored) {
|
|
17423
|
-
return;
|
|
17721
|
+
return {};
|
|
17424
17722
|
}
|
|
17425
17723
|
if (calls.length === 0) {
|
|
17426
17724
|
const shouldReprompt = actionRequest && (assistantText.length === 0 || modelClaimedAction(assistantText));
|
|
@@ -17441,27 +17739,35 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17441
17739
|
system(`
|
|
17442
17740
|
[warning] The provider/model did not emit a tool call for an explicit action request. ` + "Use a chat-safe fallback (`keryx shell --chat`) or switch to a tool-capable model.\n");
|
|
17443
17741
|
}
|
|
17444
|
-
return;
|
|
17742
|
+
return {};
|
|
17445
17743
|
}
|
|
17446
17744
|
if (isAborted()) {
|
|
17447
17745
|
system(`
|
|
17448
17746
|
[stopped] Model turn interrupted by user.
|
|
17449
17747
|
`);
|
|
17450
|
-
return;
|
|
17748
|
+
return {};
|
|
17451
17749
|
}
|
|
17452
17750
|
let exhaustedBudget;
|
|
17453
17751
|
let executedAny = false;
|
|
17454
17752
|
const batchContainsUntrustedWeb = calls.some((call) => call.name === "web_fetch" || call.name === "web_search");
|
|
17753
|
+
const reservationByCallId = new Map;
|
|
17754
|
+
for (const call of calls) {
|
|
17755
|
+
const callRisk = toolByName.get(call.name)?.definition.risk;
|
|
17756
|
+
reservationByCallId.set(call.id, reserveToolAttempt(budget, call.name, call.input, callRisk));
|
|
17757
|
+
}
|
|
17758
|
+
const spawnConcurrencyCandidates = calls.filter((call) => call.name === "spawn_subagent" && reservationByCallId.get(call.id)?.ok === true);
|
|
17759
|
+
const untrustedGateBlocksSpawns = untrustedContentSeen || batchContainsUntrustedWeb;
|
|
17760
|
+
const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps) : undefined;
|
|
17455
17761
|
for (const call of calls) {
|
|
17456
17762
|
if (isAborted()) {
|
|
17457
17763
|
system(`
|
|
17458
17764
|
[stopped] Model turn interrupted by user.
|
|
17459
17765
|
`);
|
|
17460
|
-
return;
|
|
17766
|
+
return {};
|
|
17461
17767
|
}
|
|
17462
17768
|
if (deps.unattended === true && call.name === "ask_user") {
|
|
17463
17769
|
await emitTerminalState(io, deps, options, "ask_user_unanswerable");
|
|
17464
|
-
return;
|
|
17770
|
+
return {};
|
|
17465
17771
|
}
|
|
17466
17772
|
if (untrustedContentSeen || batchContainsUntrustedWeb && call.name !== "web_fetch" && call.name !== "web_search") {
|
|
17467
17773
|
const result2 = {
|
|
@@ -17475,7 +17781,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17475
17781
|
}
|
|
17476
17782
|
io.onToolCall?.(call.name, call.input);
|
|
17477
17783
|
const risk = toolByName.get(call.name)?.definition.risk;
|
|
17478
|
-
const reservation = reserveToolAttempt(budget, call.name, call.input, risk);
|
|
17784
|
+
const reservation = reservationByCallId.get(call.id) ?? reserveToolAttempt(budget, call.name, call.input, risk);
|
|
17479
17785
|
if (!reservation.ok) {
|
|
17480
17786
|
const result2 = { output: reservation.reason, isError: true };
|
|
17481
17787
|
io.onToolResult?.(call.name, result2);
|
|
@@ -17492,7 +17798,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17492
17798
|
continue;
|
|
17493
17799
|
}
|
|
17494
17800
|
executedAny = true;
|
|
17495
|
-
const result = await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17801
|
+
const result = concurrentSpawnResults?.get(call.id) ?? await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17496
17802
|
io.onToolResult?.(call.name, result);
|
|
17497
17803
|
const modelOutput = redactSensitiveText(result.output);
|
|
17498
17804
|
history.push({
|
|
@@ -17544,9 +17850,10 @@ ${hint}
|
|
|
17544
17850
|
}
|
|
17545
17851
|
const noProgress = !executedAny && calls.length > 0;
|
|
17546
17852
|
if (exhaustedBudget !== undefined || noProgress) {
|
|
17853
|
+
const finishReason = exhaustedBudget !== undefined ? "budget" : "no-progress";
|
|
17547
17854
|
if (deps.unattended === true) {
|
|
17548
17855
|
await emitTerminalState(io, deps, options, "budget_exhausted");
|
|
17549
|
-
return;
|
|
17856
|
+
return { finishReason };
|
|
17550
17857
|
}
|
|
17551
17858
|
await finishWithBudgetSummary(io, deps, history, parentRunId, {
|
|
17552
17859
|
maxUnique: maxToolCalls,
|
|
@@ -17560,7 +17867,7 @@ ${hint}
|
|
|
17560
17867
|
...exhaustedBudget !== undefined ? { exhaustedBudget } : {},
|
|
17561
17868
|
noProgress
|
|
17562
17869
|
});
|
|
17563
|
-
return;
|
|
17870
|
+
return { finishReason };
|
|
17564
17871
|
}
|
|
17565
17872
|
}
|
|
17566
17873
|
}
|
|
@@ -17652,6 +17959,68 @@ function isApprovalFor(response, fingerprint) {
|
|
|
17652
17959
|
}
|
|
17653
17960
|
return response.fingerprint === undefined || response.fingerprint === fingerprint;
|
|
17654
17961
|
}
|
|
17962
|
+
async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps) {
|
|
17963
|
+
const maxConcurrency = deps.maxSubagentConcurrency ?? DEFAULT_MAX_SUBAGENT_CONCURRENCY;
|
|
17964
|
+
const perTaskRuntimeMs = NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS;
|
|
17965
|
+
const tasks = spawnCalls.map((call) => ({
|
|
17966
|
+
taskId: call.id,
|
|
17967
|
+
dependsOn: [],
|
|
17968
|
+
budgetRequest: { reservationId: call.id, maxRuntimeMs: perTaskRuntimeMs }
|
|
17969
|
+
}));
|
|
17970
|
+
const runOne = (call) => executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17971
|
+
const plan = planWaves(tasks, {
|
|
17972
|
+
maxConcurrency,
|
|
17973
|
+
parentRemaining: { maxRuntimeMs: perTaskRuntimeMs * tasks.length }
|
|
17974
|
+
});
|
|
17975
|
+
if (!plan.ok) {
|
|
17976
|
+
io.onSystem?.(`
|
|
17977
|
+
[warning] concurrent subagent wave planning denied (${plan.reason}); running sequentially.
|
|
17978
|
+
`);
|
|
17979
|
+
const results = new Map;
|
|
17980
|
+
for (const call of spawnCalls) {
|
|
17981
|
+
try {
|
|
17982
|
+
results.set(call.id, await runOne(call));
|
|
17983
|
+
} catch (cause) {
|
|
17984
|
+
const message2 = cause instanceof Error ? cause.message : String(cause);
|
|
17985
|
+
results.set(call.id, {
|
|
17986
|
+
output: `subagent call ${call.id} failed: sequential fallback error: ${message2}`,
|
|
17987
|
+
isError: true
|
|
17988
|
+
});
|
|
17989
|
+
}
|
|
17990
|
+
}
|
|
17991
|
+
return results;
|
|
17992
|
+
}
|
|
17993
|
+
try {
|
|
17994
|
+
return await executeWaves(tasks, plan.waves, {
|
|
17995
|
+
run: (task) => {
|
|
17996
|
+
const call = spawnCalls.find((c) => c.id === task.taskId);
|
|
17997
|
+
if (call === undefined) {
|
|
17998
|
+
return Promise.resolve({
|
|
17999
|
+
output: `internal error: unknown concurrent spawn taskId ${task.taskId}`,
|
|
18000
|
+
isError: true
|
|
18001
|
+
});
|
|
18002
|
+
}
|
|
18003
|
+
return runOne(call);
|
|
18004
|
+
}
|
|
18005
|
+
});
|
|
18006
|
+
} catch (cause) {
|
|
18007
|
+
const message2 = cause instanceof Error ? cause.message : String(cause);
|
|
18008
|
+
const isWaveError = cause instanceof WaveExecutionError;
|
|
18009
|
+
io.onSystem?.(`
|
|
18010
|
+
[warning] concurrent subagent wave failed${isWaveError ? "" : " (unexpected)"} (degraded): ${message2}
|
|
18011
|
+
`);
|
|
18012
|
+
const partialResults = cause instanceof WaveExecutionError ? cause.partialResults : undefined;
|
|
18013
|
+
const results = new Map;
|
|
18014
|
+
for (const call of spawnCalls) {
|
|
18015
|
+
const settled = partialResults?.get(call.id);
|
|
18016
|
+
results.set(call.id, settled ?? {
|
|
18017
|
+
output: `subagent call ${call.id} failed: concurrent wave error: ${message2}`,
|
|
18018
|
+
isError: true
|
|
18019
|
+
});
|
|
18020
|
+
}
|
|
18021
|
+
return results;
|
|
18022
|
+
}
|
|
18023
|
+
}
|
|
17655
18024
|
async function executeCall(call, toolByName, requestApproval, permissionMode, onAutoApproved) {
|
|
17656
18025
|
const tool = toolByName.get(call.name);
|
|
17657
18026
|
if (tool === undefined) {
|
|
@@ -17703,190 +18072,20 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17703
18072
|
}
|
|
17704
18073
|
return tool.invoke(input2);
|
|
17705
18074
|
}
|
|
17706
|
-
var DEFAULT_MAX_TOOL_CALLS = 48, ENV_AGENT_MAX_TOOL_CALLS = "KERYX_AGENT_MAX_TOOL_CALLS", MAX_AGENT_MAX_TOOL_CALLS = 256, DEFAULT_MAX_READ_TOOL_CALLS = 40, DEFAULT_MAX_NON_READ_TOOL_CALLS = 8, MAX_ATTEMPTS_PER_HASH = 3, ENV_AGENT_MAX_ATTEMPTS_PER_HASH = "KERYX_AGENT_MAX_ATTEMPTS_PER_HASH", MAX_AGENT_MAX_ATTEMPTS_PER_HASH = 10, REPEAT_FAILURE_HINT_THRESHOLD = 2, MAX_TOOLLESS_REPROMPTS = 1;
|
|
18075
|
+
var DEFAULT_MAX_TOOL_CALLS = 48, ENV_AGENT_MAX_TOOL_CALLS = "KERYX_AGENT_MAX_TOOL_CALLS", MAX_AGENT_MAX_TOOL_CALLS = 256, DEFAULT_MAX_READ_TOOL_CALLS = 40, DEFAULT_MAX_NON_READ_TOOL_CALLS = 8, DEFAULT_MAX_SUBAGENT_CONCURRENCY = 3, MAX_ATTEMPTS_PER_HASH = 3, ENV_AGENT_MAX_ATTEMPTS_PER_HASH = "KERYX_AGENT_MAX_ATTEMPTS_PER_HASH", MAX_AGENT_MAX_ATTEMPTS_PER_HASH = 10, REPEAT_FAILURE_HINT_THRESHOLD = 2, MAX_TOOLLESS_REPROMPTS = 1, NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS;
|
|
17707
18076
|
var init_agent = __esm(() => {
|
|
17708
18077
|
init_validator();
|
|
17709
18078
|
init_command_risk();
|
|
17710
18079
|
init_permission_mode();
|
|
17711
18080
|
init_redact();
|
|
18081
|
+
init_scheduler();
|
|
17712
18082
|
init_slate();
|
|
17713
18083
|
init_slate_course();
|
|
17714
18084
|
init_workspace_resolve();
|
|
17715
18085
|
init_machine_wrap_up();
|
|
17716
18086
|
init_slate_lifecycle();
|
|
17717
18087
|
init_slate_terminal_state();
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
// src/harness/policy/ranks.ts
|
|
17721
|
-
function axesOf(trustMode) {
|
|
17722
|
-
switch (trustMode) {
|
|
17723
|
-
case "read-only":
|
|
17724
|
-
return { authority: "read-only", inputTrust: "vetted" };
|
|
17725
|
-
case "trusted-local":
|
|
17726
|
-
return { authority: "acting", inputTrust: "vetted" };
|
|
17727
|
-
case "untrusted":
|
|
17728
|
-
return { authority: "acting", inputTrust: "unvetted" };
|
|
17729
|
-
default:
|
|
17730
|
-
return;
|
|
17731
|
-
}
|
|
17732
|
-
}
|
|
17733
|
-
function rankOf(map, value) {
|
|
17734
|
-
return Object.prototype.hasOwnProperty.call(map, value) ? map[value] : undefined;
|
|
17735
|
-
}
|
|
17736
|
-
function exceedingAxes(ceiling, candidate) {
|
|
17737
|
-
const low = axesOf(ceiling);
|
|
17738
|
-
const high = axesOf(candidate);
|
|
17739
|
-
if (low === undefined || high === undefined) {
|
|
17740
|
-
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
17741
|
-
}
|
|
17742
|
-
const exceeded = [];
|
|
17743
|
-
if (AUTHORITY_RANK[high.authority] > AUTHORITY_RANK[low.authority]) {
|
|
17744
|
-
exceeded.push(AUTHORITY_AXIS);
|
|
17745
|
-
}
|
|
17746
|
-
if (INPUT_TRUST_RANK[high.inputTrust] > INPUT_TRUST_RANK[low.inputTrust]) {
|
|
17747
|
-
exceeded.push(INPUT_TRUST_AXIS);
|
|
17748
|
-
}
|
|
17749
|
-
return exceeded;
|
|
17750
|
-
}
|
|
17751
|
-
function broadeningAxes(parent, child) {
|
|
17752
|
-
const above = axesOf(parent);
|
|
17753
|
-
const below = axesOf(child);
|
|
17754
|
-
if (above === undefined || below === undefined) {
|
|
17755
|
-
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
17756
|
-
}
|
|
17757
|
-
const broadened = [];
|
|
17758
|
-
if (AUTHORITY_RANK[below.authority] > AUTHORITY_RANK[above.authority]) {
|
|
17759
|
-
broadened.push(AUTHORITY_AXIS);
|
|
17760
|
-
}
|
|
17761
|
-
if (INPUT_TRUST_RANK[below.inputTrust] < INPUT_TRUST_RANK[above.inputTrust]) {
|
|
17762
|
-
broadened.push(INPUT_TRUST_AXIS);
|
|
17763
|
-
}
|
|
17764
|
-
return broadened;
|
|
17765
|
-
}
|
|
17766
|
-
var AUTHORITY_RANK, INPUT_TRUST_RANK, OUTCOME_RANK, ISOLATION_RANK, AUTHORITY_AXIS = "trustMode.authority", INPUT_TRUST_AXIS = "trustMode.inputTrust";
|
|
17767
|
-
var init_ranks = __esm(() => {
|
|
17768
|
-
AUTHORITY_RANK = {
|
|
17769
|
-
"read-only": 0,
|
|
17770
|
-
acting: 1
|
|
17771
|
-
};
|
|
17772
|
-
INPUT_TRUST_RANK = {
|
|
17773
|
-
unvetted: 0,
|
|
17774
|
-
vetted: 1
|
|
17775
|
-
};
|
|
17776
|
-
OUTCOME_RANK = {
|
|
17777
|
-
deny: 0,
|
|
17778
|
-
ask: 1,
|
|
17779
|
-
allow: 2
|
|
17780
|
-
};
|
|
17781
|
-
ISOLATION_RANK = {
|
|
17782
|
-
"not-required": 0,
|
|
17783
|
-
"required-fail-closed": 1
|
|
17784
|
-
};
|
|
17785
|
-
});
|
|
17786
|
-
|
|
17787
|
-
// src/harness/child/isolation.ts
|
|
17788
|
-
function inheritBudget(parentRemaining, childRequest) {
|
|
17789
|
-
if (childRequest.maxRuntimeMs > parentRemaining.maxRuntimeMs) {
|
|
17790
|
-
return {
|
|
17791
|
-
ok: false,
|
|
17792
|
-
reason: `child maxRuntimeMs ${childRequest.maxRuntimeMs} exceeds parent remaining ${parentRemaining.maxRuntimeMs}`
|
|
17793
|
-
};
|
|
17794
|
-
}
|
|
17795
|
-
if (childRequest.maxToolCalls !== undefined) {
|
|
17796
|
-
if (parentRemaining.maxToolCalls === undefined) {
|
|
17797
|
-
return {
|
|
17798
|
-
ok: false,
|
|
17799
|
-
reason: `child requests ${childRequest.maxToolCalls} tool calls but the parent exposes no tool-call budget to inherit`
|
|
17800
|
-
};
|
|
17801
|
-
}
|
|
17802
|
-
if (childRequest.maxToolCalls > parentRemaining.maxToolCalls) {
|
|
17803
|
-
return {
|
|
17804
|
-
ok: false,
|
|
17805
|
-
reason: `child maxToolCalls ${childRequest.maxToolCalls} exceeds parent remaining ${parentRemaining.maxToolCalls}`
|
|
17806
|
-
};
|
|
17807
|
-
}
|
|
17808
|
-
}
|
|
17809
|
-
const reservation = {
|
|
17810
|
-
reservationId: childRequest.reservationId,
|
|
17811
|
-
maxRuntimeMs: childRequest.maxRuntimeMs,
|
|
17812
|
-
...childRequest.maxToolCalls !== undefined ? { maxToolCalls: childRequest.maxToolCalls } : {}
|
|
17813
|
-
};
|
|
17814
|
-
return { ok: true, reservation };
|
|
17815
|
-
}
|
|
17816
|
-
function isKnownCapability(value) {
|
|
17817
|
-
return CAPABILITY_KEYS.includes(value);
|
|
17818
|
-
}
|
|
17819
|
-
function inheritPolicy(parent, childRequest) {
|
|
17820
|
-
if (axesOf(childRequest.trustMode) === undefined || axesOf(parent.trustMode) === undefined) {
|
|
17821
|
-
return {
|
|
17822
|
-
ok: false,
|
|
17823
|
-
reason: `unrecognized trustMode (child "${childRequest.trustMode}", parent "${parent.trustMode}")`
|
|
17824
|
-
};
|
|
17825
|
-
}
|
|
17826
|
-
const broadened = broadeningAxes(parent.trustMode, childRequest.trustMode);
|
|
17827
|
-
if (broadened.length > 0) {
|
|
17828
|
-
return {
|
|
17829
|
-
ok: false,
|
|
17830
|
-
reason: `child trustMode "${childRequest.trustMode}" is broader than parent "${parent.trustMode}" on ${broadened.join(", ")}`
|
|
17831
|
-
};
|
|
17832
|
-
}
|
|
17833
|
-
for (const capability of CAPABILITY_KEYS) {
|
|
17834
|
-
const childOutcome = childRequest.defaults[capability];
|
|
17835
|
-
const parentOutcome = parent.defaults[capability];
|
|
17836
|
-
const childRank = rankOf(OUTCOME_RANK, childOutcome);
|
|
17837
|
-
const parentRank = rankOf(OUTCOME_RANK, parentOutcome);
|
|
17838
|
-
if (childRank === undefined || parentRank === undefined) {
|
|
17839
|
-
return {
|
|
17840
|
-
ok: false,
|
|
17841
|
-
reason: `unrecognized capability outcome for "${capability}" (child "${childOutcome}", parent "${parentOutcome}")`
|
|
17842
|
-
};
|
|
17843
|
-
}
|
|
17844
|
-
if (childRank > parentRank) {
|
|
17845
|
-
return {
|
|
17846
|
-
ok: false,
|
|
17847
|
-
reason: `child capability "${capability}" default "${childOutcome}" is more permissive than parent "${parentOutcome}"`
|
|
17848
|
-
};
|
|
17849
|
-
}
|
|
17850
|
-
}
|
|
17851
|
-
const childIsolation = rankOf(ISOLATION_RANK, childRequest.requiredControls.isolation);
|
|
17852
|
-
const parentIsolation = rankOf(ISOLATION_RANK, parent.requiredControls.isolation);
|
|
17853
|
-
if (childIsolation === undefined || parentIsolation === undefined) {
|
|
17854
|
-
return {
|
|
17855
|
-
ok: false,
|
|
17856
|
-
reason: `unrecognized isolation control (child "${childRequest.requiredControls.isolation}", parent "${parent.requiredControls.isolation}")`
|
|
17857
|
-
};
|
|
17858
|
-
}
|
|
17859
|
-
if (childIsolation < parentIsolation) {
|
|
17860
|
-
return {
|
|
17861
|
-
ok: false,
|
|
17862
|
-
reason: `child isolation "${childRequest.requiredControls.isolation}" is weaker than parent "${parent.requiredControls.isolation}"`
|
|
17863
|
-
};
|
|
17864
|
-
}
|
|
17865
|
-
return { ok: true, policy: childRequest };
|
|
17866
|
-
}
|
|
17867
|
-
function childProvenance(parent, deps) {
|
|
17868
|
-
const taintIds = [...parent.taintIds ?? [], parent.provenanceId];
|
|
17869
|
-
const provenance = {
|
|
17870
|
-
provenanceId: deps.idSeq(),
|
|
17871
|
-
trustLevel: "derived",
|
|
17872
|
-
sourceKind: parent.sourceKind,
|
|
17873
|
-
taintIds
|
|
17874
|
-
};
|
|
17875
|
-
if (parent.sourceHash !== undefined) {
|
|
17876
|
-
provenance.sourceHash = parent.sourceHash;
|
|
17877
|
-
}
|
|
17878
|
-
return provenance;
|
|
17879
|
-
}
|
|
17880
|
-
var CAPABILITY_KEYS;
|
|
17881
|
-
var init_isolation = __esm(() => {
|
|
17882
|
-
init_ranks();
|
|
17883
|
-
CAPABILITY_KEYS = [
|
|
17884
|
-
"read",
|
|
17885
|
-
"write",
|
|
17886
|
-
"shell",
|
|
17887
|
-
"network",
|
|
17888
|
-
"delegate"
|
|
17889
|
-
];
|
|
18088
|
+
NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS = 5 * 60000;
|
|
17890
18089
|
});
|
|
17891
18090
|
|
|
17892
18091
|
// src/harness/child/ledger.ts
|
|
@@ -44896,76 +45095,8 @@ function evaluateExtensionGrant(input2, deps) {
|
|
|
44896
45095
|
return { ok: true };
|
|
44897
45096
|
}
|
|
44898
45097
|
|
|
44899
|
-
// src/harness/parallel/scheduler.ts
|
|
44900
|
-
init_isolation();
|
|
44901
|
-
function byTaskId(a, b) {
|
|
44902
|
-
return a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;
|
|
44903
|
-
}
|
|
44904
|
-
function computeExcluded(tasks) {
|
|
44905
|
-
const excluded = new Set;
|
|
44906
|
-
for (const t of tasks) {
|
|
44907
|
-
if (t.cancelled === true)
|
|
44908
|
-
excluded.add(t.taskId);
|
|
44909
|
-
}
|
|
44910
|
-
let changed = true;
|
|
44911
|
-
while (changed) {
|
|
44912
|
-
changed = false;
|
|
44913
|
-
for (const t of tasks) {
|
|
44914
|
-
if (excluded.has(t.taskId))
|
|
44915
|
-
continue;
|
|
44916
|
-
if (t.dependsOn.some((dep) => excluded.has(dep))) {
|
|
44917
|
-
excluded.add(t.taskId);
|
|
44918
|
-
changed = true;
|
|
44919
|
-
}
|
|
44920
|
-
}
|
|
44921
|
-
}
|
|
44922
|
-
return excluded;
|
|
44923
|
-
}
|
|
44924
|
-
function decrementRemaining(remaining, reservation) {
|
|
44925
|
-
const maxRuntimeMs = remaining.maxRuntimeMs - reservation.maxRuntimeMs;
|
|
44926
|
-
if (remaining.maxToolCalls !== undefined && reservation.maxToolCalls !== undefined) {
|
|
44927
|
-
return { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls - reservation.maxToolCalls };
|
|
44928
|
-
}
|
|
44929
|
-
return remaining.maxToolCalls !== undefined ? { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls } : { maxRuntimeMs };
|
|
44930
|
-
}
|
|
44931
|
-
function planWaves(tasks, config, _deps) {
|
|
44932
|
-
if (!Number.isInteger(config.maxConcurrency) || config.maxConcurrency < 1) {
|
|
44933
|
-
return { ok: false, reason: `maxConcurrency must be a positive integer, got ${config.maxConcurrency}` };
|
|
44934
|
-
}
|
|
44935
|
-
const excluded = computeExcluded(tasks);
|
|
44936
|
-
const universe = tasks.filter((t) => !excluded.has(t.taskId));
|
|
44937
|
-
const scheduled = new Set;
|
|
44938
|
-
const waveTaskLists = [];
|
|
44939
|
-
while (scheduled.size < universe.length) {
|
|
44940
|
-
const ready = universe.filter((t) => !scheduled.has(t.taskId) && t.dependsOn.every((dep) => scheduled.has(dep))).sort(byTaskId);
|
|
44941
|
-
if (ready.length === 0) {
|
|
44942
|
-
return { ok: false, reason: "dependency cycle detected: no ready task set could be formed" };
|
|
44943
|
-
}
|
|
44944
|
-
const waveTasks = ready.slice(0, config.maxConcurrency);
|
|
44945
|
-
for (const t of waveTasks)
|
|
44946
|
-
scheduled.add(t.taskId);
|
|
44947
|
-
waveTaskLists.push(waveTasks);
|
|
44948
|
-
}
|
|
44949
|
-
let remaining = config.parentRemaining;
|
|
44950
|
-
const waves = [];
|
|
44951
|
-
for (const waveTasks of waveTaskLists) {
|
|
44952
|
-
const taskIds = [];
|
|
44953
|
-
const reservations = [];
|
|
44954
|
-
for (const t of waveTasks) {
|
|
44955
|
-
const granted = inheritBudget(remaining, t.budgetRequest);
|
|
44956
|
-
if (!granted.ok) {
|
|
44957
|
-
return { ok: false, reason: granted.reason };
|
|
44958
|
-
}
|
|
44959
|
-
taskIds.push(t.taskId);
|
|
44960
|
-
reservations.push(granted.reservation);
|
|
44961
|
-
remaining = decrementRemaining(remaining, granted.reservation);
|
|
44962
|
-
}
|
|
44963
|
-
waves.push({ taskIds, reservations });
|
|
44964
|
-
}
|
|
44965
|
-
return { ok: true, waves };
|
|
44966
|
-
}
|
|
44967
|
-
|
|
44968
45098
|
// src/harness/extension/bound-wave.ts
|
|
45099
|
+
init_scheduler();
|
|
44969
45100
|
function buildPlannedAttemptEvidence(extension, deps) {
|
|
44970
45101
|
const causal = {
|
|
44971
45102
|
runId: extension.parentRunId,
|
|
@@ -47903,7 +48034,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
47903
48034
|
invoke: async (input2) => {
|
|
47904
48035
|
const task = typeof input2.task === "string" ? input2.task.trim() : "";
|
|
47905
48036
|
if (task.length === 0) {
|
|
47906
|
-
return { output: "spawn_subagent requires a non-empty 'task'", isError: true };
|
|
48037
|
+
return { status: "Error", output: "spawn_subagent requires a non-empty 'task'", isError: true };
|
|
47907
48038
|
}
|
|
47908
48039
|
const mode = input2.mode === "general" ? "general" : "read_only";
|
|
47909
48040
|
const maxToolCalls = typeof input2.max_tool_calls === "number" && input2.max_tool_calls > 0 ? Math.min(MAX_SUBAGENT_MAX_TOOL_CALLS, Math.floor(input2.max_tool_calls)) : DEFAULT_SUBAGENT_MAX_TOOL_CALLS;
|
|
@@ -47954,6 +48085,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
47954
48085
|
task
|
|
47955
48086
|
});
|
|
47956
48087
|
return {
|
|
48088
|
+
status: "Denied",
|
|
47957
48089
|
output: `spawn_subagent denied by MAE: ${spawned.reason}`,
|
|
47958
48090
|
isError: true
|
|
47959
48091
|
};
|
|
@@ -48143,6 +48275,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
48143
48275
|
|
|
48144
48276
|
` + "Return a concise summary of findings and any recommended next steps for the parent agent.";
|
|
48145
48277
|
const turn = runAgentTurn(io, childDeps, history, userLine, { signal: childAbort.signal });
|
|
48278
|
+
let turnResult;
|
|
48146
48279
|
if (deadlineMs > 0) {
|
|
48147
48280
|
let timer;
|
|
48148
48281
|
const expired = new Promise((resolve3) => {
|
|
@@ -48164,20 +48297,26 @@ function createSpawnSubagentTool(deps) {
|
|
|
48164
48297
|
await foldChildSlateAndCleanup("incomplete");
|
|
48165
48298
|
const partial = assistant.trim();
|
|
48166
48299
|
return {
|
|
48300
|
+
status: "Timeout",
|
|
48167
48301
|
output: `subagent ${label} (${workerId}) timed out after ${deadlineMs}ms and was abandoned ` + `(tighten or disable with ${ENV_SUBAGENT_TIMEOUT_MS})` + (partial.length > 0 ? `
|
|
48168
48302
|
--- partial output ---
|
|
48169
48303
|
${boundSummary(partial)}` : ""),
|
|
48170
|
-
isError: true
|
|
48304
|
+
isError: true,
|
|
48305
|
+
...partial.length > 0 ? { partial: boundSummary(partial) } : {}
|
|
48171
48306
|
};
|
|
48172
48307
|
}
|
|
48308
|
+
turnResult = await turn;
|
|
48173
48309
|
} else {
|
|
48174
|
-
await turn;
|
|
48310
|
+
turnResult = await turn;
|
|
48175
48311
|
}
|
|
48176
48312
|
closed = true;
|
|
48177
48313
|
releaseBudget();
|
|
48178
48314
|
const raw = assistant.trim().length > 0 ? assistant.trim() : history.filter((m) => m.role === "assistant").map((m) => m.content).join(`
|
|
48179
48315
|
`).trim() || "(subagent produced no text)";
|
|
48180
48316
|
const folded = foldChildSummary(raw);
|
|
48317
|
+
const finishReason = turnResult?.finishReason;
|
|
48318
|
+
const status = finishReason === "budget" ? "BudgetExhausted" : finishReason === "no-progress" ? "NoProgress" : "Completed";
|
|
48319
|
+
const isError = status !== "Completed";
|
|
48181
48320
|
emitSubagentFleet({
|
|
48182
48321
|
kind: "upsert",
|
|
48183
48322
|
id: workerId,
|
|
@@ -48189,11 +48328,13 @@ ${boundSummary(partial)}` : ""),
|
|
|
48189
48328
|
});
|
|
48190
48329
|
await foldChildSlateAndCleanup("completed");
|
|
48191
48330
|
return {
|
|
48331
|
+
status,
|
|
48332
|
+
isError,
|
|
48192
48333
|
output: `subagent ${label} (${workerId}) ${mode} via ${runModel.provider}/${runModel.model}
|
|
48193
48334
|
` + `MAE reservation: tools\u2264${spawned.reservation.maxToolCalls ?? maxToolCalls} ` + `runtime\u2264${spawned.reservation.maxRuntimeMs}ms children=${ledger.childCount}
|
|
48194
48335
|
` + `--- summary ---
|
|
48195
48336
|
${boundSummary(folded.text)}`,
|
|
48196
|
-
|
|
48337
|
+
...status !== "Completed" ? { partial: boundSummary(folded.text) } : {}
|
|
48197
48338
|
};
|
|
48198
48339
|
} catch (cause) {
|
|
48199
48340
|
closed = true;
|
|
@@ -48209,7 +48350,7 @@ ${boundSummary(folded.text)}`,
|
|
|
48209
48350
|
task
|
|
48210
48351
|
});
|
|
48211
48352
|
await foldChildSlateAndCleanup("incomplete");
|
|
48212
|
-
return { output: `subagent ${label} failed: ${msg}`, isError: true };
|
|
48353
|
+
return { status: "Error", output: `subagent ${label} failed: ${msg}`, isError: true };
|
|
48213
48354
|
}
|
|
48214
48355
|
}
|
|
48215
48356
|
};
|
|
@@ -48605,7 +48746,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
48605
48746
|
// package.json
|
|
48606
48747
|
var package_default = {
|
|
48607
48748
|
name: "@mrciphersmith/keryx",
|
|
48608
|
-
version: "0.2.
|
|
48749
|
+
version: "0.2.46",
|
|
48609
48750
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
48610
48751
|
private: false,
|
|
48611
48752
|
publishConfig: {
|
|
@@ -50460,6 +50601,37 @@ function openFlows(otui, chrome, options) {
|
|
|
50460
50601
|
return presentFlows((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
50461
50602
|
}
|
|
50462
50603
|
|
|
50604
|
+
// src/tui/busy-dispatch.ts
|
|
50605
|
+
function classifyBusyDispatch(params) {
|
|
50606
|
+
const { line, commandName, isSessionInfo, isFlows, isWorkspace, isReview } = params;
|
|
50607
|
+
if (commandName === "/exit")
|
|
50608
|
+
return "exit";
|
|
50609
|
+
if (commandName === "/help")
|
|
50610
|
+
return "help";
|
|
50611
|
+
if (commandName === "/interrupt")
|
|
50612
|
+
return "interrupt";
|
|
50613
|
+
if (commandName === "/queue")
|
|
50614
|
+
return "queue";
|
|
50615
|
+
if (commandName === "/think")
|
|
50616
|
+
return "think";
|
|
50617
|
+
if (commandName === "/expand")
|
|
50618
|
+
return "expand";
|
|
50619
|
+
if (commandName === "/copy")
|
|
50620
|
+
return "copy";
|
|
50621
|
+
const isBusyReadonlyCommand = isSessionInfo || isFlows || isWorkspace || isReview;
|
|
50622
|
+
if (isBusyReadonlyCommand && isSessionInfo)
|
|
50623
|
+
return "session-info";
|
|
50624
|
+
if (isBusyReadonlyCommand && isFlows)
|
|
50625
|
+
return "flows";
|
|
50626
|
+
if (isBusyReadonlyCommand && isWorkspace)
|
|
50627
|
+
return "workspace";
|
|
50628
|
+
if (isBusyReadonlyCommand && isReview)
|
|
50629
|
+
return "review";
|
|
50630
|
+
if (commandName !== undefined || line.startsWith("/"))
|
|
50631
|
+
return "deferred";
|
|
50632
|
+
return "not-a-command";
|
|
50633
|
+
}
|
|
50634
|
+
|
|
50463
50635
|
// src/tui/workspace-inspector.ts
|
|
50464
50636
|
var WORKSPACE_COMMAND = "/workspace";
|
|
50465
50637
|
var WORKSPACE_FOOTER = [
|
|
@@ -55523,81 +55695,122 @@ Staying in the current session.
|
|
|
55523
55695
|
return;
|
|
55524
55696
|
}
|
|
55525
55697
|
const displayLine = summarizeSubmittedLine(line);
|
|
55526
|
-
const isBusyReadonlyCommand = isSessionInfoCommand(line) || isFlowsCommand(line);
|
|
55527
55698
|
if (chrome.isBusy()) {
|
|
55528
55699
|
const command2 = findAgentCommand(line, "agent");
|
|
55529
|
-
|
|
55530
|
-
|
|
55531
|
-
|
|
55532
|
-
|
|
55533
|
-
|
|
55534
|
-
|
|
55535
|
-
|
|
55536
|
-
}
|
|
55537
|
-
|
|
55538
|
-
|
|
55539
|
-
|
|
55540
|
-
|
|
55541
|
-
|
|
55542
|
-
|
|
55543
|
-
|
|
55700
|
+
const decision = classifyBusyDispatch({
|
|
55701
|
+
line,
|
|
55702
|
+
commandName: command2?.name,
|
|
55703
|
+
isSessionInfo: isSessionInfoCommand(line),
|
|
55704
|
+
isFlows: isFlowsCommand(line),
|
|
55705
|
+
isWorkspace: isWorkspaceCommand(line),
|
|
55706
|
+
isReview: isReviewCommand(line)
|
|
55707
|
+
});
|
|
55708
|
+
switch (decision) {
|
|
55709
|
+
case "exit": {
|
|
55710
|
+
(async () => {
|
|
55711
|
+
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
55712
|
+
r.off("theme_mode", onThemeMode);
|
|
55713
|
+
r.destroy();
|
|
55714
|
+
})();
|
|
55715
|
+
return;
|
|
55716
|
+
}
|
|
55717
|
+
case "help": {
|
|
55718
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
55719
|
+
id: `c${uid++}`,
|
|
55720
|
+
content: otui.t`${otui.cyan(`\u276F ${line}`)}`,
|
|
55721
|
+
marginTop: 1
|
|
55722
|
+
}));
|
|
55723
|
+
io.onSystem?.("Main agent is busy. Type a normal question to spawn a side worker " + `(sees main status + recent context; read-only). /status \u0438 /flows still open info panels. /exit still works.
|
|
55544
55724
|
`);
|
|
55545
|
-
|
|
55546
|
-
|
|
55547
|
-
|
|
55548
|
-
|
|
55549
|
-
|
|
55550
|
-
|
|
55725
|
+
return;
|
|
55726
|
+
}
|
|
55727
|
+
case "interrupt": {
|
|
55728
|
+
if (mainTurnAbortController !== undefined && !mainTurnAbortController.signal.aborted) {
|
|
55729
|
+
mainTurnAbortController.abort();
|
|
55730
|
+
io.onSystem?.(`\u25C7 main turn interrupted.
|
|
55731
|
+
`);
|
|
55732
|
+
return;
|
|
55733
|
+
}
|
|
55734
|
+
io.onSystem?.(`\u25C7 no active main turn to interrupt.
|
|
55551
55735
|
`);
|
|
55552
55736
|
return;
|
|
55553
55737
|
}
|
|
55554
|
-
|
|
55738
|
+
case "queue": {
|
|
55739
|
+
const parsed = parseQueueCommand(line.trim().split(/\s+/).slice(1).join(" "));
|
|
55740
|
+
if (parsed === undefined) {
|
|
55741
|
+
io.onSystem?.(`\u25C7 usage: /queue <remove|edit|force> [N] (N = qN position, default 1)
|
|
55555
55742
|
`);
|
|
55556
|
-
|
|
55557
|
-
|
|
55558
|
-
|
|
55559
|
-
|
|
55560
|
-
|
|
55561
|
-
|
|
55743
|
+
return;
|
|
55744
|
+
}
|
|
55745
|
+
const index = parsed.position - 1;
|
|
55746
|
+
if (index < 0 || index >= mainQueue.length) {
|
|
55747
|
+
io.onSystem?.(`\u25C7 queue: no item q${parsed.position}.
|
|
55748
|
+
`);
|
|
55749
|
+
return;
|
|
55750
|
+
}
|
|
55751
|
+
if (parsed.action === "remove") {
|
|
55752
|
+
removeMainQueue(index);
|
|
55753
|
+
io.onSystem?.(`\u25C7 removed q${parsed.position} from the main queue.
|
|
55754
|
+
`);
|
|
55755
|
+
return;
|
|
55756
|
+
}
|
|
55757
|
+
if (parsed.action === "edit") {
|
|
55758
|
+
editMainQueue(index);
|
|
55759
|
+
io.onSystem?.(`\u25C7 q${parsed.position} moved to the composer \u2014 edit and submit to re-queue at the same position.
|
|
55562
55760
|
`);
|
|
55761
|
+
return;
|
|
55762
|
+
}
|
|
55763
|
+
forceMainQueue(index);
|
|
55563
55764
|
return;
|
|
55564
55765
|
}
|
|
55565
|
-
|
|
55566
|
-
|
|
55567
|
-
|
|
55766
|
+
case "think": {
|
|
55767
|
+
if (toggleNewestBlock("thought") === undefined) {
|
|
55768
|
+
io.onSystem?.(`No reasoning yet.
|
|
55568
55769
|
`);
|
|
55770
|
+
}
|
|
55569
55771
|
return;
|
|
55570
55772
|
}
|
|
55571
|
-
|
|
55572
|
-
|
|
55573
|
-
|
|
55773
|
+
case "expand": {
|
|
55774
|
+
if (toggleNewestBlock("output") === undefined && toggleNewestBlock() === undefined) {
|
|
55775
|
+
io.onSystem?.(`Nothing to expand \u2014 no tool output yet.
|
|
55574
55776
|
`);
|
|
55777
|
+
}
|
|
55575
55778
|
return;
|
|
55576
55779
|
}
|
|
55577
|
-
|
|
55578
|
-
|
|
55579
|
-
|
|
55780
|
+
case "copy": {
|
|
55781
|
+
const target = newestBlock();
|
|
55782
|
+
if (target === undefined || !copyBlock(target.id)) {
|
|
55783
|
+
io.onSystem?.(`Nothing to copy yet.
|
|
55580
55784
|
`);
|
|
55785
|
+
}
|
|
55581
55786
|
return;
|
|
55582
55787
|
}
|
|
55583
|
-
|
|
55584
|
-
|
|
55585
|
-
|
|
55586
|
-
|
|
55587
|
-
|
|
55588
|
-
|
|
55589
|
-
|
|
55590
|
-
|
|
55591
|
-
|
|
55592
|
-
|
|
55593
|
-
|
|
55594
|
-
|
|
55595
|
-
|
|
55596
|
-
|
|
55597
|
-
|
|
55598
|
-
|
|
55599
|
-
|
|
55600
|
-
|
|
55788
|
+
case "session-info": {
|
|
55789
|
+
showSessionInfo();
|
|
55790
|
+
return;
|
|
55791
|
+
}
|
|
55792
|
+
case "flows": {
|
|
55793
|
+
showFlows();
|
|
55794
|
+
return;
|
|
55795
|
+
}
|
|
55796
|
+
case "workspace": {
|
|
55797
|
+
showWorkspace();
|
|
55798
|
+
return;
|
|
55799
|
+
}
|
|
55800
|
+
case "review": {
|
|
55801
|
+
showReview();
|
|
55802
|
+
return;
|
|
55803
|
+
}
|
|
55804
|
+
case "deferred": {
|
|
55805
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
55806
|
+
id: `c${uid++}`,
|
|
55807
|
+
content: otui.t`${otui.yellow(`\u25C7 main is busy \u2014 command deferred. Ask a normal question for a side worker, or wait.`)}`,
|
|
55808
|
+
marginTop: 1
|
|
55809
|
+
}));
|
|
55810
|
+
return;
|
|
55811
|
+
}
|
|
55812
|
+
case "not-a-command":
|
|
55813
|
+
break;
|
|
55601
55814
|
}
|
|
55602
55815
|
if (pendingQueueEdit !== undefined) {
|
|
55603
55816
|
const edit = pendingQueueEdit;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.46",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|