@adhdev/daemon-core 0.5.5 → 0.5.7

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/index.d.ts CHANGED
@@ -2367,6 +2367,9 @@ declare class ProviderCliAdapter implements CliAdapter {
2367
2367
  private serverConn;
2368
2368
  private logBuffer;
2369
2369
  private lastApprovalResolvedAt;
2370
+ private approvalTransitionBuffer;
2371
+ private approvalExitTimeout;
2372
+ private resizeSuppressUntil;
2370
2373
  private readonly timeouts;
2371
2374
  constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[]);
2372
2375
  setServerConn(serverConn: any): void;
@@ -2384,6 +2387,12 @@ declare class ProviderCliAdapter implements CliAdapter {
2384
2387
  isProcessing(): boolean;
2385
2388
  isReady(): boolean;
2386
2389
  writeRaw(data: string): void;
2390
+ /**
2391
+ * Resolve an approval modal by navigating to the button at `buttonIndex` and pressing Enter.
2392
+ * Index 0 = first option (already selected by default — just Enter).
2393
+ * Index N = press Arrow Down N times, then Enter.
2394
+ */
2395
+ resolveModal(buttonIndex: number): void;
2387
2396
  resize(cols: number, rows: number): void;
2388
2397
  }
2389
2398
 
package/dist/index.js CHANGED
@@ -3568,6 +3568,39 @@ async function handleResolveAction(h, args) {
3568
3568
  const action = args?.action || "approve";
3569
3569
  const button = args?.button || args?.buttonText || (action === "approve" ? "Accept" : action === "reject" ? "Reject" : "Accept");
3570
3570
  LOG.info("Command", `[resolveAction] action=${action} button="${button}" provider=${provider?.type}`);
3571
+ if (provider?.category === "cli") {
3572
+ const adapter = h.getCliAdapter(provider.type);
3573
+ if (!adapter) return { success: false, error: "CLI adapter not running" };
3574
+ const status = adapter.getStatus?.();
3575
+ if (status?.status !== "waiting_approval") {
3576
+ return { success: false, error: "Not in approval state" };
3577
+ }
3578
+ const buttons = status.activeModal?.buttons || ["Allow once", "Always allow", "Deny"];
3579
+ let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
3580
+ if (buttonIndex < 0) {
3581
+ const btnLower = button.toLowerCase();
3582
+ buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
3583
+ }
3584
+ if (buttonIndex < 0) {
3585
+ if (action === "reject" || action === "deny") {
3586
+ buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
3587
+ if (buttonIndex < 0) buttonIndex = buttons.length - 1;
3588
+ } else if (action === "always" || /always/i.test(button)) {
3589
+ buttonIndex = buttons.findIndex((b) => /always/i.test(b));
3590
+ if (buttonIndex < 0) buttonIndex = 1;
3591
+ } else {
3592
+ buttonIndex = 0;
3593
+ }
3594
+ }
3595
+ if (typeof adapter.resolveModal === "function") {
3596
+ adapter.resolveModal(buttonIndex);
3597
+ } else {
3598
+ const keys = "\x1B[B".repeat(Math.max(0, buttonIndex)) + "\r";
3599
+ adapter.writeRaw?.(keys);
3600
+ }
3601
+ LOG.info("Command", `[resolveAction] CLI PTY \u2192 buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}"`);
3602
+ return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
3603
+ }
3571
3604
  if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
3572
3605
  const ok = await h.agentStream.resolveAgentAction(
3573
3606
  h.getCdp(),
@@ -6472,28 +6505,32 @@ function coercePatternArray(raw, fallbacks) {
6472
6505
  }
6473
6506
  var FALLBACK_PROMPT = [
6474
6507
  /Type your message/i,
6475
- />\s*$/,
6508
+ /^>\s*$/m,
6509
+ // '>' alone on its own line
6476
6510
  /[›➤]\s*$/,
6477
6511
  /for shortcuts/i,
6478
6512
  /\?\s*for help/i,
6479
- /Press enter/i,
6480
- /^[\s\u2500-\u257f]*>\s*$/m
6513
+ /Press enter/i
6514
+ // NOTE: removed /^[\s\u2500-\u257f]*>\s*$/m — the box-drawing char range is too wide and
6515
+ // can match dialog-clearing ANSI output, causing false prompt detection in approval state.
6481
6516
  ];
6482
6517
  var FALLBACK_GENERATING = [
6483
- /thinking/i,
6484
- /writing/i,
6485
- /Claude is/i,
6486
- /Opus|Sonnet|Haiku/i,
6487
- /[\u2800-\u28ff]/
6488
- // Braille spinner blocks
6518
+ /[\u2800-\u28ff]/,
6519
+ // Braille spinner blocks (universal TUI)
6520
+ /esc to (cancel|interrupt|stop)/i,
6521
+ // Common TUI generation status line
6522
+ /generating\.\.\./i,
6523
+ /Claude is (?:thinking|processing|working)/i
6524
+ // Specific Claude Code status
6489
6525
  ];
6490
6526
  var FALLBACK_APPROVAL = [
6491
- /approve/i,
6492
- /Allow( once)?/i,
6527
+ /Allow\s+once/i,
6528
+ /Always\s+allow/i,
6493
6529
  /\(y\/n\)/i,
6494
6530
  /\[Y\/n\]/i,
6495
- /continue\?/i,
6496
- /Run this command/i
6531
+ /Run\s+this\s+command/i
6532
+ // NOTE: removed /Do you want to (?:run|execute|allow)/i — too broad, matches AI explanation
6533
+ // text like "Do you want to allow this feature?" causing false approval notifications.
6497
6534
  ];
6498
6535
  function defaultCleanOutput(raw, _lastUserInput) {
6499
6536
  return stripAnsi(raw).trim();
@@ -6554,6 +6591,11 @@ var ProviderCliAdapter = class {
6554
6591
  logBuffer = [];
6555
6592
  // Approval cooldown
6556
6593
  lastApprovalResolvedAt = 0;
6594
+ // Approval state machine
6595
+ approvalTransitionBuffer = "";
6596
+ approvalExitTimeout = null;
6597
+ // Resize redraw suppression
6598
+ resizeSuppressUntil = 0;
6557
6599
  // Resolved timeouts (provider defaults + overrides)
6558
6600
  timeouts;
6559
6601
  // ─── Lifecycle ─────────────────────────────────
@@ -6644,6 +6686,7 @@ var ProviderCliAdapter = class {
6644
6686
  }
6645
6687
  // ─── Output state machine ────────────────────────────
6646
6688
  handleOutput(rawData) {
6689
+ if (Date.now() < this.resizeSuppressUntil) return;
6647
6690
  const cleanData = stripAnsi(rawData);
6648
6691
  const { patterns } = this.provider;
6649
6692
  if (cleanData.trim()) {
@@ -6676,30 +6719,55 @@ var ProviderCliAdapter = class {
6676
6719
  const hasApproval = patterns.approval.some((p) => p.test(this.recentOutputBuffer));
6677
6720
  if (hasApproval && this.currentStatus !== "waiting_approval") {
6678
6721
  if (this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown) return;
6722
+ const ctxLines = this.recentOutputBuffer.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
6679
6723
  this.isWaitingForResponse = true;
6680
6724
  this.currentStatus = "waiting_approval";
6681
6725
  this.recentOutputBuffer = "";
6682
- const ctxLines = cleanData.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
6726
+ this.approvalTransitionBuffer = "";
6683
6727
  this.activeModal = {
6684
6728
  message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
6685
6729
  buttons: ["Allow once", "Always allow", "Deny"]
6686
6730
  };
6687
6731
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
6732
+ if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
6733
+ this.approvalExitTimeout = setTimeout(() => {
6734
+ if (this.currentStatus === "waiting_approval") {
6735
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
6736
+ this.activeModal = null;
6737
+ this.lastApprovalResolvedAt = Date.now();
6738
+ this.recentOutputBuffer = "";
6739
+ this.approvalTransitionBuffer = "";
6740
+ this.approvalExitTimeout = null;
6741
+ this.currentStatus = this.isWaitingForResponse ? "generating" : "idle";
6742
+ this.onStatusChange?.();
6743
+ }
6744
+ }, 6e4);
6688
6745
  this.onStatusChange?.();
6689
6746
  return;
6690
6747
  }
6691
6748
  if (this.currentStatus === "waiting_approval") {
6692
- const genResume = patterns.generating.some((p) => p.test(cleanData));
6693
- const promptResume = patterns.prompt.some((p) => p.test(cleanData));
6749
+ this.approvalTransitionBuffer = (this.approvalTransitionBuffer + cleanData).slice(-500);
6750
+ const genResume = patterns.generating.some((p) => p.test(this.approvalTransitionBuffer));
6751
+ const promptResume = patterns.prompt.some((p) => p.test(this.approvalTransitionBuffer));
6694
6752
  if (genResume) {
6753
+ if (this.approvalExitTimeout) {
6754
+ clearTimeout(this.approvalExitTimeout);
6755
+ this.approvalExitTimeout = null;
6756
+ }
6695
6757
  this.currentStatus = "generating";
6696
6758
  this.activeModal = null;
6697
6759
  this.recentOutputBuffer = "";
6760
+ this.approvalTransitionBuffer = "";
6698
6761
  this.lastApprovalResolvedAt = Date.now();
6699
6762
  this.onStatusChange?.();
6700
6763
  } else if (promptResume) {
6764
+ if (this.approvalExitTimeout) {
6765
+ clearTimeout(this.approvalExitTimeout);
6766
+ this.approvalExitTimeout = null;
6767
+ }
6701
6768
  this.activeModal = null;
6702
6769
  this.recentOutputBuffer = "";
6770
+ this.approvalTransitionBuffer = "";
6703
6771
  this.lastApprovalResolvedAt = Date.now();
6704
6772
  this.finishResponse();
6705
6773
  }
@@ -6725,7 +6793,8 @@ var ProviderCliAdapter = class {
6725
6793
  this.onStatusChange?.();
6726
6794
  return;
6727
6795
  }
6728
- if (patterns.prompt.some((p) => p.test(this.responseBuffer))) {
6796
+ const trailingLines = cleanData.split("\n").slice(-2).join("\n");
6797
+ if (patterns.prompt.some((p) => p.test(trailingLines))) {
6729
6798
  this.finishResponse();
6730
6799
  } else {
6731
6800
  this.idleTimeout = setTimeout(() => {
@@ -6746,6 +6815,10 @@ var ProviderCliAdapter = class {
6746
6815
  clearTimeout(this.idleTimeout);
6747
6816
  this.idleTimeout = null;
6748
6817
  }
6818
+ if (this.approvalExitTimeout) {
6819
+ clearTimeout(this.approvalExitTimeout);
6820
+ this.approvalExitTimeout = null;
6821
+ }
6749
6822
  const lastUserText = this.messages.filter((m) => m.role === "user").pop()?.content;
6750
6823
  let response = this.provider.cleanOutput(this.responseBuffer, lastUserText);
6751
6824
  if (lastUserText && response) {
@@ -6795,6 +6868,10 @@ var ProviderCliAdapter = class {
6795
6868
  this.shutdown();
6796
6869
  }
6797
6870
  shutdown() {
6871
+ if (this.approvalExitTimeout) {
6872
+ clearTimeout(this.approvalExitTimeout);
6873
+ this.approvalExitTimeout = null;
6874
+ }
6798
6875
  if (this.ptyProcess) {
6799
6876
  this.ptyProcess.write("");
6800
6877
  setTimeout(() => {
@@ -6822,10 +6899,22 @@ var ProviderCliAdapter = class {
6822
6899
  writeRaw(data) {
6823
6900
  this.ptyProcess?.write(data);
6824
6901
  }
6902
+ /**
6903
+ * Resolve an approval modal by navigating to the button at `buttonIndex` and pressing Enter.
6904
+ * Index 0 = first option (already selected by default — just Enter).
6905
+ * Index N = press Arrow Down N times, then Enter.
6906
+ */
6907
+ resolveModal(buttonIndex) {
6908
+ if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
6909
+ const DOWN = "\x1B[B";
6910
+ const keys = DOWN.repeat(Math.max(0, buttonIndex)) + "\r";
6911
+ this.ptyProcess.write(keys);
6912
+ }
6825
6913
  resize(cols, rows) {
6826
6914
  if (this.ptyProcess) {
6827
6915
  try {
6828
6916
  this.ptyProcess.resize(cols, rows);
6917
+ this.resizeSuppressUntil = Date.now() + 300;
6829
6918
  } catch {
6830
6919
  }
6831
6920
  }
@@ -6950,20 +7039,24 @@ var CliProviderInstance = class {
6950
7039
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
6951
7040
  const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
6952
7041
  if (newStatus !== this.lastStatus) {
7042
+ LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
6953
7043
  if (this.lastStatus === "idle" && newStatus === "generating") {
6954
7044
  this.generatingStartedAt = now;
6955
7045
  this.pushEvent({ event: "agent:generating_started", chatTitle, timestamp: now });
6956
7046
  } else if (newStatus === "waiting_approval") {
6957
7047
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
7048
+ const modal = adapterStatus.activeModal;
7049
+ LOG.info("CLI", `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? "none"}"`);
6958
7050
  this.pushEvent({
6959
7051
  event: "agent:waiting_approval",
6960
7052
  chatTitle,
6961
7053
  timestamp: now,
6962
- modalMessage: adapterStatus.activeModal?.message,
6963
- modalButtons: adapterStatus.activeModal?.buttons
7054
+ modalMessage: modal?.message,
7055
+ modalButtons: modal?.buttons
6964
7056
  });
6965
7057
  } else if (newStatus === "idle" && (this.lastStatus === "generating" || this.lastStatus === "waiting_approval")) {
6966
7058
  const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
7059
+ LOG.info("CLI", `[${this.type}] completed in ${duration}s`);
6967
7060
  this.pushEvent({ event: "agent:generating_completed", chatTitle, duration, timestamp: now });
6968
7061
  this.generatingStartedAt = 0;
6969
7062
  } else if (newStatus === "stopped") {