@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.
Files changed (2) hide show
  1. package/dist/cli.js +531 -318
  2. 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
- isError: false
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.44",
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
- if (command2?.name === "/exit") {
55530
- (async () => {
55531
- await closeSlateSession(slateSession, mintTimestampAttemptId);
55532
- r.off("theme_mode", onThemeMode);
55533
- r.destroy();
55534
- })();
55535
- return;
55536
- }
55537
- if (command2?.name === "/help") {
55538
- transcript.add(new otui.TextRenderable(r, {
55539
- id: `c${uid++}`,
55540
- content: otui.t`${otui.cyan(`\u276F ${line}`)}`,
55541
- marginTop: 1
55542
- }));
55543
- 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.
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
- return;
55546
- }
55547
- if (command2?.name === "/interrupt") {
55548
- if (mainTurnAbortController !== undefined && !mainTurnAbortController.signal.aborted) {
55549
- mainTurnAbortController.abort();
55550
- io.onSystem?.(`\u25C7 main turn interrupted.
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
- io.onSystem?.(`\u25C7 no active main turn to interrupt.
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
- return;
55557
- }
55558
- if (command2?.name === "/queue") {
55559
- const parsed = parseQueueCommand(line.trim().split(/\s+/).slice(1).join(" "));
55560
- if (parsed === undefined) {
55561
- io.onSystem?.(`\u25C7 usage: /queue <remove|edit|force> [N] (N = qN position, default 1)
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
- const index = parsed.position - 1;
55566
- if (index < 0 || index >= mainQueue.length) {
55567
- io.onSystem?.(`\u25C7 queue: no item q${parsed.position}.
55766
+ case "think": {
55767
+ if (toggleNewestBlock("thought") === undefined) {
55768
+ io.onSystem?.(`No reasoning yet.
55568
55769
  `);
55770
+ }
55569
55771
  return;
55570
55772
  }
55571
- if (parsed.action === "remove") {
55572
- removeMainQueue(index);
55573
- io.onSystem?.(`\u25C7 removed q${parsed.position} from the main queue.
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
- if (parsed.action === "edit") {
55578
- editMainQueue(index);
55579
- io.onSystem?.(`\u25C7 q${parsed.position} moved to the composer \u2014 edit and submit to re-queue at the same position.
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
- forceMainQueue(index);
55584
- return;
55585
- }
55586
- if (isBusyReadonlyCommand && isSessionInfoCommand(line)) {
55587
- showSessionInfo();
55588
- return;
55589
- }
55590
- if (isBusyReadonlyCommand && isFlowsCommand(line)) {
55591
- showFlows();
55592
- return;
55593
- }
55594
- if (command2 !== undefined || line.startsWith("/")) {
55595
- transcript.add(new otui.TextRenderable(r, {
55596
- id: `c${uid++}`,
55597
- content: otui.t`${otui.yellow(`\u25C7 main is busy \u2014 command deferred. Ask a normal question for a side worker, or wait.`)}`,
55598
- marginTop: 1
55599
- }));
55600
- return;
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.44",
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": {