@mrciphersmith/keryx 0.2.120 → 0.2.121

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 +127 -28
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -29500,6 +29500,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
29500
29500
  const spawnConcurrencyCandidates = calls.filter((call) => call.name === "spawn_subagent" && reservationByCallId.get(call.id)?.ok === true);
29501
29501
  const untrustedGateBlocksSpawns = untrustedContentSeen || batchContainsUntrustedWeb;
29502
29502
  const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns && maxToolCalls === undefined ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps, hasInvocationCapacity, reserveInvocation) : undefined;
29503
+ const answeredToolIds = new Set;
29503
29504
  let anchorsToAnnounce;
29504
29505
  let repeatedFailureHint;
29505
29506
  for (const call of calls) {
@@ -29507,6 +29508,26 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
29507
29508
  system(`
29508
29509
  [stopped] Model turn interrupted by user.
29509
29510
  `);
29511
+ if (concurrentSpawnResults !== undefined) {
29512
+ for (const spawnCall of spawnConcurrencyCandidates) {
29513
+ if (answeredToolIds.has(spawnCall.id)) {
29514
+ continue;
29515
+ }
29516
+ const settled = concurrentSpawnResults.get(spawnCall.id);
29517
+ if (settled === undefined) {
29518
+ continue;
29519
+ }
29520
+ io.onToolResult?.(spawnCall.name, settled);
29521
+ history.push({
29522
+ role: "tool",
29523
+ content: redactSensitiveText(settled.output),
29524
+ provenance: "tool",
29525
+ toolCallId: spawnCall.id,
29526
+ ts: now()
29527
+ });
29528
+ io.onHistoryChange?.("tool");
29529
+ }
29530
+ }
29510
29531
  return {};
29511
29532
  }
29512
29533
  if (deps.unattended === true && call.name === "ask_user") {
@@ -29549,6 +29570,7 @@ ${modelOutput}` : modelOutput,
29549
29570
  ts: now()
29550
29571
  });
29551
29572
  io.onHistoryChange?.("tool");
29573
+ answeredToolIds.add(call.id);
29552
29574
  if (untrusted) {
29553
29575
  untrustedContentSeen = true;
29554
29576
  }
@@ -50252,7 +50274,7 @@ import path47 from "path";
50252
50274
  // package.json
50253
50275
  var package_default = {
50254
50276
  name: "@mrciphersmith/keryx",
50255
- version: "0.2.120",
50277
+ version: "0.2.121",
50256
50278
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
50257
50279
  private: false,
50258
50280
  publishConfig: {
@@ -94358,7 +94380,48 @@ function onKeypress2(r, handler) {
94358
94380
  r._internalKeyInput.onInternal("keypress", handler);
94359
94381
  return () => r._internalKeyInput.offInternal("keypress", handler);
94360
94382
  }
94361
- function showComposerChoice(otui, r, dock, request) {
94383
+ var dockChain = new WeakMap;
94384
+ var dockDepth = new WeakMap;
94385
+ function dockBusy(dock) {
94386
+ return (dockDepth.get(dock) ?? 0) > 0;
94387
+ }
94388
+ function lockDock(dock) {
94389
+ dockDepth.set(dock, (dockDepth.get(dock) ?? 0) + 1);
94390
+ const prev = dockChain.get(dock) ?? Promise.resolve();
94391
+ let release;
94392
+ const mine = new Promise((resolve) => {
94393
+ release = resolve;
94394
+ });
94395
+ dockChain.set(dock, prev.then(() => mine));
94396
+ return prev.then(() => {
94397
+ return () => {
94398
+ const n = (dockDepth.get(dock) ?? 1) - 1;
94399
+ if (n <= 0) {
94400
+ dockDepth.delete(dock);
94401
+ } else {
94402
+ dockDepth.set(dock, n);
94403
+ }
94404
+ release();
94405
+ };
94406
+ });
94407
+ }
94408
+ async function showComposerChoice(otui, r, dock, request) {
94409
+ const enqueue = request.enqueue !== false;
94410
+ if (!enqueue && dockBusy(dock)) {
94411
+ request.onBusy?.();
94412
+ return request.cancelId;
94413
+ }
94414
+ const unlock = await lockDock(dock);
94415
+ try {
94416
+ if (request.signal?.aborted === true) {
94417
+ return request.cancelId;
94418
+ }
94419
+ return await presentComposerChoice(otui, r, dock, request);
94420
+ } finally {
94421
+ unlock();
94422
+ }
94423
+ }
94424
+ function presentComposerChoice(otui, r, dock, request) {
94362
94425
  if (dock.visible === true) {
94363
94426
  request.onBusy?.();
94364
94427
  return Promise.resolve(request.cancelId);
@@ -94371,6 +94434,7 @@ function showComposerChoice(otui, r, dock, request) {
94371
94434
  const recommendedIdx = options.findIndex((o) => o.recommended === true);
94372
94435
  let selected = recommendedIdx >= 0 ? recommendedIdx : 0;
94373
94436
  dock.visible = true;
94437
+ request.onOpen?.();
94374
94438
  const theme = getTheme();
94375
94439
  const rawSubtitle = request.subtitle !== undefined && request.subtitle.length > 0 ? request.subtitle : undefined;
94376
94440
  const subtitleText = rawSubtitle !== undefined && rawSubtitle.length > MAX_SUBTITLE_CHARS ? `${rawSubtitle.slice(0, MAX_SUBTITLE_CHARS)}
@@ -94435,7 +94499,15 @@ function showComposerChoice(otui, r, dock, request) {
94435
94499
  }
94436
94500
  }
94437
94501
  };
94502
+ let settled = false;
94503
+ const onAbort = () => {
94504
+ finish(request.cancelId);
94505
+ };
94438
94506
  const finish = (id) => {
94507
+ if (settled) {
94508
+ return;
94509
+ }
94510
+ settled = true;
94439
94511
  cleanup();
94440
94512
  resolve(id);
94441
94513
  };
@@ -94487,6 +94559,7 @@ function showComposerChoice(otui, r, dock, request) {
94487
94559
  function cleanup() {
94488
94560
  closed = true;
94489
94561
  unsub();
94562
+ request.signal?.removeEventListener("abort", onAbort);
94490
94563
  try {
94491
94564
  dock.remove(title);
94492
94565
  if (subtitleScroll !== undefined) {
@@ -94523,7 +94596,7 @@ function showComposerChoice(otui, r, dock, request) {
94523
94596
  }
94524
94597
  const focused = r.currentFocusedRenderable;
94525
94598
  const inScroll = subtitleScroll !== undefined && focused !== null && containsNode(subtitleScroll, focused);
94526
- if (inScroll) {
94599
+ if (inScroll && key.name !== "return" && key.name !== "linefeed" && key.name !== "kpenter") {
94527
94600
  return;
94528
94601
  }
94529
94602
  if (key.name === "up") {
@@ -94550,6 +94623,9 @@ function showComposerChoice(otui, r, dock, request) {
94550
94623
  }
94551
94624
  };
94552
94625
  const unsub = onKeypress2(r, onKey);
94626
+ if (request.signal !== undefined) {
94627
+ request.signal.addEventListener("abort", onAbort, { once: true });
94628
+ }
94553
94629
  optionsScroll.focus();
94554
94630
  });
94555
94631
  }
@@ -95353,6 +95429,9 @@ async function createShellChrome(otui, renderer, opts) {
95353
95429
  }
95354
95430
  });
95355
95431
  textarea.onSubmit = () => {
95432
+ if (overlayActive()) {
95433
+ return;
95434
+ }
95356
95435
  const line = input.value.trim();
95357
95436
  input.value = "";
95358
95437
  hideMenu();
@@ -98148,7 +98227,7 @@ function createApprovalContextLoader(cwd) {
98148
98227
  return buildApprovalContext(port, command);
98149
98228
  };
98150
98229
  }
98151
- async function pickShellApproval(otui, r, dock, command, loadContext, destructive = false, credentials = false) {
98230
+ async function pickShellApproval(otui, r, dock, command, loadContext, destructive = false, credentials = false, ui = {}) {
98152
98231
  let context;
98153
98232
  try {
98154
98233
  context = loadContext(command);
@@ -98188,7 +98267,9 @@ async function pickShellApproval(otui, r, dock, command, loadContext, destructiv
98188
98267
  subtitle: command,
98189
98268
  ...context !== undefined ? { context } : {},
98190
98269
  cancelId: "deny",
98191
- options
98270
+ options,
98271
+ ...ui.onOpen !== undefined ? { onOpen: ui.onOpen } : {},
98272
+ ...ui.signal !== undefined ? { signal: ui.signal } : {}
98192
98273
  });
98193
98274
  if (id === "once" || id === "always-exact" || id === "always-prefix" || id === "deny") {
98194
98275
  return id;
@@ -99533,10 +99614,12 @@ async function launchTuiAgentShell(opts) {
99533
99614
  chrome.hideMenu();
99534
99615
  setMainAgent("blocked", "approval");
99535
99616
  const preview = request.task.length > 80 ? `${request.task.slice(0, 77)}\u2026` : request.task;
99536
- const id = await showComposerChoice(otui, r, chrome.dock, {
99617
+ const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99537
99618
  title: `Run the external agent ${request.agentId}?`,
99538
99619
  subtitle: preview,
99539
99620
  cancelId: "deny",
99621
+ onOpen: () => chrome.blurComposer(),
99622
+ signal: foregroundOperation.signal,
99540
99623
  options: [
99541
99624
  {
99542
99625
  id: "allow",
@@ -99545,7 +99628,7 @@ async function launchTuiAgentShell(opts) {
99545
99628
  },
99546
99629
  { id: "deny", label: "Deny", description: "Do not start the external agent" }
99547
99630
  ]
99548
- });
99631
+ }));
99549
99632
  input.focus();
99550
99633
  setMainAgent("running", id === "allow" ? "external" : "denied");
99551
99634
  return id === "allow" ? { ok: true } : { ok: false, reason: `you declined the external spawn of ${request.agentId}` };
@@ -99716,10 +99799,12 @@ async function launchTuiAgentShell(opts) {
99716
99799
  }
99717
99800
  chrome.hideMenu();
99718
99801
  setMainAgent("blocked", "approval");
99719
- const id = await showComposerChoice(otui, r, chrome.dock, {
99802
+ const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99720
99803
  title: "Spawn general subagent?",
99721
99804
  subtitle: taskPreview,
99722
99805
  cancelId: "deny",
99806
+ onOpen: () => chrome.blurComposer(),
99807
+ signal: foregroundOperation.signal,
99723
99808
  options: [
99724
99809
  {
99725
99810
  id: "allow",
@@ -99729,7 +99814,7 @@ async function launchTuiAgentShell(opts) {
99729
99814
  },
99730
99815
  { id: "deny", label: "Deny", description: "Do not spawn" }
99731
99816
  ]
99732
- });
99817
+ }));
99733
99818
  input.focus();
99734
99819
  setMainAgent("running", id === "allow" ? "subagent" : "denied");
99735
99820
  transcript.add(new otui.TextRenderable(r, {
@@ -99758,15 +99843,17 @@ async function launchTuiAgentShell(opts) {
99758
99843
  }
99759
99844
  chrome.hideMenu();
99760
99845
  setMainAgent("blocked", "approval");
99761
- const id = await showComposerChoice(otui, r, chrome.dock, {
99846
+ const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99762
99847
  title: "Approve apply_patch?",
99763
99848
  subtitle: "Diff shown above \xB7 Esc denies",
99764
99849
  cancelId: "deny",
99850
+ onOpen: () => chrome.blurComposer(),
99851
+ signal: foregroundOperation.signal,
99765
99852
  options: [
99766
99853
  { id: "allow", label: "Approve", description: "Write the patch to disk", recommended: true },
99767
99854
  { id: "deny", label: "Deny", description: "Do not write anything" }
99768
99855
  ]
99769
- });
99856
+ }));
99770
99857
  input.focus();
99771
99858
  setMainAgent("running", id === "allow" ? "write" : "denied");
99772
99859
  transcript.add(new otui.TextRenderable(r, {
@@ -99792,15 +99879,17 @@ async function launchTuiAgentShell(opts) {
99792
99879
  }
99793
99880
  chrome.hideMenu();
99794
99881
  setMainAgent("blocked", "approval");
99795
- const id = await showComposerChoice(otui, r, chrome.dock, {
99882
+ const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99796
99883
  title: described.title,
99797
99884
  subtitle: summariseUseToolApproval(described),
99798
99885
  cancelId: "deny",
99886
+ onOpen: () => chrome.blurComposer(),
99887
+ signal: foregroundOperation.signal,
99799
99888
  options: [
99800
99889
  { id: APPROVAL_ALLOW_ID, label: "Approve", description: `run ${described.tool} on ${described.server}` },
99801
99890
  { id: "deny", label: "Deny", description: "the tool call is refused", recommended: true }
99802
99891
  ]
99803
- });
99892
+ }));
99804
99893
  input.focus();
99805
99894
  const allowed = isDockApproval(id);
99806
99895
  setMainAgent("running", allowed ? "write" : "denied");
@@ -99838,15 +99927,17 @@ async function launchTuiAgentShell(opts) {
99838
99927
  chrome.hideMenu();
99839
99928
  setMainAgent("blocked", "approval");
99840
99929
  const elicitationSubtitle = described.message.length > 80 ? `${described.message.slice(0, 77)}\u2026` : described.message;
99841
- const id = await showComposerChoice(otui, r, chrome.dock, {
99930
+ const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99842
99931
  title: "Approve codex elicitation?",
99843
99932
  subtitle: elicitationSubtitle,
99844
99933
  cancelId: "deny",
99934
+ onOpen: () => chrome.blurComposer(),
99935
+ signal: foregroundOperation.signal,
99845
99936
  options: [
99846
99937
  { id: "allow", label: "Approve", description: "codex proceeds with this action", recommended: true },
99847
99938
  { id: "deny", label: "Deny", description: "codex's action is refused" }
99848
99939
  ]
99849
- });
99940
+ }));
99850
99941
  input.focus();
99851
99942
  setMainAgent("running", id === "allow" ? "write" : "denied");
99852
99943
  transcript.add(new otui.TextRenderable(r, {
@@ -99901,7 +99992,10 @@ async function launchTuiAgentShell(opts) {
99901
99992
  setMainAgent("blocked", "approval");
99902
99993
  setBusyPhase("waiting for your approval (menu above input)");
99903
99994
  chrome.hideMenu();
99904
- const choice = await pickShellApproval(otui, r, chrome.dock, cmd, approvalContext, destructive, meta?.credentials === true);
99995
+ const choice = await chrome.withOverlay(() => pickShellApproval(otui, r, chrome.dock, cmd, approvalContext, destructive, meta?.credentials === true, {
99996
+ onOpen: () => chrome.blurComposer(),
99997
+ signal: foregroundOperation.signal
99998
+ }));
99905
99999
  input.focus();
99906
100000
  if (choice === "deny") {
99907
100001
  transcript.add(new otui.TextRenderable(r, {
@@ -99945,17 +100039,19 @@ async function launchTuiAgentShell(opts) {
99945
100039
  id: `ask${uid++}`,
99946
100040
  content: otui.t`${otui.yellow("? ")} ${otui.dim(qShort)}`
99947
100041
  }));
99948
- const chosen = await showComposerChoice(otui, r, chrome.dock, {
100042
+ const chosen = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
99949
100043
  title: req.question.length > 72 ? `${req.question.slice(0, 69)}\u2026` : req.question,
99950
100044
  subtitle: "Pick an option \xB7 Esc cancels",
99951
100045
  cancelId: "__cancel__",
100046
+ onOpen: () => chrome.blurComposer(),
100047
+ signal: foregroundOperation.signal,
99952
100048
  options: req.options.map((o) => ({
99953
100049
  id: o.id,
99954
100050
  label: o.label,
99955
100051
  description: o.description.length > 0 ? o.description : " ",
99956
100052
  ...o.recommended === true ? { recommended: true } : {}
99957
100053
  }))
99958
- });
100054
+ }));
99959
100055
  input.focus();
99960
100056
  if (chosen !== "__cancel__") {
99961
100057
  const picked = req.options.find((o) => o.id === chosen);
@@ -100417,14 +100513,15 @@ Staying in the current session.
100417
100513
  title: "Switch to auto mode?",
100418
100514
  subtitle: "Skips confirmation for EVERY action, including destructive commands. Only credential-touching commands still ask.",
100419
100515
  cancelId: "cancel",
100420
- options: [
100421
- { id: "confirm", label: "Confirm", description: "I understand the risk" },
100422
- { id: "cancel", label: "Cancel", description: "Keep the current mode", recommended: true }
100423
- ],
100516
+ enqueue: false,
100424
100517
  onBusy: () => {
100425
100518
  blockedByOpenDialog = true;
100426
100519
  chrome.showToast("Answer the open approval first, then retry /mode.");
100427
- }
100520
+ },
100521
+ options: [
100522
+ { id: "confirm", label: "Confirm", description: "I understand the risk" },
100523
+ { id: "cancel", label: "Cancel", description: "Keep the current mode", recommended: true }
100524
+ ]
100428
100525
  }));
100429
100526
  input.focus();
100430
100527
  if (blockedByOpenDialog) {
@@ -100465,16 +100562,17 @@ Staying in the current session.
100465
100562
  title: `Permission mode (current: ${permissionMode})`,
100466
100563
  subtitle: stored !== undefined ? `Project default: ${stored}` : "No project default set.",
100467
100564
  cancelId: permissionMode,
100565
+ enqueue: false,
100566
+ onBusy: () => {
100567
+ blockedByOpenDialog = true;
100568
+ chrome.showToast("Answer the open approval first, then retry /mode.");
100569
+ },
100468
100570
  options: PERMISSION_MODES.map((m) => ({
100469
100571
  id: m,
100470
100572
  label: m,
100471
100573
  description: MODE_PICKER_DESCRIPTIONS[m],
100472
100574
  recommended: m === permissionMode
100473
- })),
100474
- onBusy: () => {
100475
- blockedByOpenDialog = true;
100476
- chrome.showToast("Answer the open approval first, then retry /mode.");
100477
- }
100575
+ }))
100478
100576
  }));
100479
100577
  input.focus();
100480
100578
  if (blockedByOpenDialog) {
@@ -101067,6 +101165,7 @@ Staying in the current session.
101067
101165
  { id: "side", label: "Side-1", description: "read-only answer, outside main history (as before)" }
101068
101166
  ],
101069
101167
  cancelId: "side",
101168
+ enqueue: false,
101070
101169
  onBusy: () => {
101071
101170
  blockedByOpenDialog = true;
101072
101171
  chrome.showToast("Answer the open approval first, then resend.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.120",
3
+ "version": "0.2.121",
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": {