@adhdev/daemon-core 0.9.82-rc.268 → 0.9.82-rc.269

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.
@@ -7,7 +7,10 @@ export declare class GhosttyVtTerminalBackend implements TerminalViewportBackend
7
7
  constructor(options: TerminalViewportBackendOptions);
8
8
  resize(rows: number, cols: number): void;
9
9
  write(data: string): void;
10
+ private formatLines;
11
+ private static trimBlankEnds;
10
12
  getText(): string;
13
+ getTextWithScrollback(): string;
11
14
  getCursorPosition(): {
12
15
  col: number;
13
16
  row: number;
@@ -9,6 +9,15 @@ export interface TerminalViewportBackend {
9
9
  resize(rows: number, cols: number): void;
10
10
  write(data: string): void;
11
11
  getText(): string;
12
+ /**
13
+ * Like getText() but includes scrollback history (the lines that have
14
+ * scrolled above the visible viewport). Used by content-pattern matching
15
+ * (e.g. approval/modal button extraction) that must stay correct when a
16
+ * tall prompt — a big diff or long explanation — pushes part of the
17
+ * prompt box above the viewport. NOT for cursor-relative / stable_ms
18
+ * conditions, whose row indices are viewport-relative.
19
+ */
20
+ getTextWithScrollback(): string;
12
21
  getCursorPosition(): {
13
22
  col: number;
14
23
  row: number;
@@ -15,6 +15,8 @@ export declare class TerminalScreen {
15
15
  resize(rows: number, cols: number): void;
16
16
  write(data: string): void;
17
17
  getText(): string;
18
+ /** Full buffer including scrollback history (see backend doc). */
19
+ getTextWithScrollback(): string;
18
20
  getCursorPosition(): {
19
21
  col: number;
20
22
  row: number;
package/dist/index.js CHANGED
@@ -265,10 +265,10 @@ function readInjected(value) {
265
265
  }
266
266
  function getDaemonBuildInfo() {
267
267
  if (cached) return cached;
268
- const commit = readInjected(true ? "9da2b92afe11e5b813734c9f389479542e25a41f" : void 0) ?? "unknown";
269
- const commitShort = readInjected(true ? "9da2b92a" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
270
- const version = readInjected(true ? "0.9.82-rc.268" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
271
- const builtAt = readInjected(true ? "2026-06-14T21:47:03.827Z" : void 0);
268
+ const commit = readInjected(true ? "025d8bef81a66e7b44a5ba5d5a04e063db46db4e" : void 0) ?? "unknown";
269
+ const commitShort = readInjected(true ? "025d8bef" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
270
+ const version = readInjected(true ? "0.9.82-rc.269" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
271
+ const builtAt = readInjected(true ? "2026-06-14T22:11:38.134Z" : void 0);
272
272
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
273
273
  return cached;
274
274
  }
@@ -9503,7 +9503,7 @@ var init_ghostty_vt_backend = __esm({
9503
9503
  "@adhdev/ghostty-vt-node"
9504
9504
  ];
9505
9505
  cachedBindingError = null;
9506
- GhosttyVtTerminalBackend = class {
9506
+ GhosttyVtTerminalBackend = class _GhosttyVtTerminalBackend {
9507
9507
  kind = "ghostty-vt";
9508
9508
  terminal;
9509
9509
  rows;
@@ -9525,17 +9525,30 @@ var init_ghostty_vt_backend = __esm({
9525
9525
  if (!data || this.disposed) return;
9526
9526
  this.terminal.write(data);
9527
9527
  }
9528
+ formatLines() {
9529
+ const raw = this.terminal.formatPlainText({ trim: false }) || "";
9530
+ if (!raw) return [];
9531
+ return raw.split("\n").map((row) => row.replace(/\s+$/, ""));
9532
+ }
9533
+ static trimBlankEnds(lines) {
9534
+ let first = 0;
9535
+ let last = lines.length;
9536
+ while (first < last && !lines[first]) first += 1;
9537
+ while (last > first && !lines[last - 1]) last -= 1;
9538
+ return lines.slice(first, last).join("\n");
9539
+ }
9528
9540
  getText() {
9529
9541
  if (this.disposed) return "";
9530
- const raw = this.terminal.formatPlainText({ trim: false }) || "";
9531
- if (!raw) return "";
9532
- const lines = raw.split("\n").map((row) => row.replace(/\s+$/, ""));
9542
+ const lines = this.formatLines();
9543
+ if (lines.length === 0) return "";
9533
9544
  const viewport = lines.length > this.rows ? lines.slice(-this.rows) : lines;
9534
- let first = 0;
9535
- let last = viewport.length;
9536
- while (first < last && !viewport[first]) first += 1;
9537
- while (last > first && !viewport[last - 1]) last -= 1;
9538
- return viewport.slice(first, last).join("\n");
9545
+ return _GhosttyVtTerminalBackend.trimBlankEnds(viewport);
9546
+ }
9547
+ getTextWithScrollback() {
9548
+ if (this.disposed) return "";
9549
+ const lines = this.formatLines();
9550
+ if (lines.length === 0) return "";
9551
+ return _GhosttyVtTerminalBackend.trimBlankEnds(lines);
9539
9552
  }
9540
9553
  getCursorPosition() {
9541
9554
  if (this.disposed) return { col: 0, row: 0 };
@@ -9588,6 +9601,10 @@ var init_terminal_screen = __esm({
9588
9601
  getText() {
9589
9602
  return this.terminal.getText();
9590
9603
  }
9604
+ /** Full buffer including scrollback history (see backend doc). */
9605
+ getTextWithScrollback() {
9606
+ return this.terminal.getTextWithScrollback();
9607
+ }
9591
9608
  getCursorPosition() {
9592
9609
  return this.terminal.getCursorPosition();
9593
9610
  }
@@ -29325,6 +29342,15 @@ var TerminalAdapter = class {
29325
29342
  snapshot() {
29326
29343
  return this.lastScreen || this.computeScreen();
29327
29344
  }
29345
+ /**
29346
+ * Full-buffer snapshot including scrollback history. Unlike snapshot()
29347
+ * (visible viewport only), this survives a tall prompt whose top has
29348
+ * scrolled off-screen — used for content-pattern extraction (modal
29349
+ * buttons / approval anchors), NOT for cursor-relative conditions.
29350
+ */
29351
+ snapshotWithScrollback() {
29352
+ return this.screen.getTextWithScrollback();
29353
+ }
29328
29354
  getCursorPosition() {
29329
29355
  const pos = this.screen.getCursorPosition();
29330
29356
  return { row: pos.row, col: pos.col };
@@ -29499,6 +29525,18 @@ var FsmDriver = class {
29499
29525
  getScreen() {
29500
29526
  return this.adapter.snapshot();
29501
29527
  }
29528
+ /** Scrollback-inclusive screen as line array — used only for modal/button
29529
+ * content extraction so a tall prompt's off-screen anchors stay matchable.
29530
+ * Falls back to the viewport snapshot if scrollback read is unavailable. */
29531
+ scrollbackLines() {
29532
+ let screen = "";
29533
+ try {
29534
+ screen = this.adapter.snapshotWithScrollback();
29535
+ } catch {
29536
+ }
29537
+ if (!screen) screen = this.adapter.snapshot();
29538
+ return screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
29539
+ }
29502
29540
  getSpecPath() {
29503
29541
  return this.opts.specPath;
29504
29542
  }
@@ -29680,9 +29718,12 @@ var FsmDriver = class {
29680
29718
  const screen = this.adapter.snapshot();
29681
29719
  const lines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
29682
29720
  const sections = resolveSections(this.spec.sections ?? {}, lines);
29683
- const modal = this.deriveModal(state, sections, lines.join("\n"));
29721
+ const modalLines = state.modal ? this.scrollbackLines() : lines;
29722
+ const modalSections = state.modal ? resolveSections(this.spec.sections ?? {}, modalLines) : sections;
29723
+ const modalFullScreen = modalLines.join("\n");
29724
+ const modal = this.deriveModal(state, modalSections, modalFullScreen);
29684
29725
  const controls = this.deriveControls(state.id);
29685
- const title = modal?.title ?? this.deriveTitle(state, sections, lines.join("\n"));
29726
+ const title = modal?.title ?? this.deriveTitle(state, modalSections, modalFullScreen);
29686
29727
  const next = {
29687
29728
  // status is derived from the FSM state itself (statusForState), NOT from
29688
29729
  // whether a modal was parsed this frame. A modal state whose buttons briefly
@@ -31553,7 +31594,7 @@ async function waitForCliAdapterReady(adapter, options) {
31553
31594
  }
31554
31595
  throw new Error(`CLI runtime did not become ready within ${timeoutMs}ms`);
31555
31596
  }
31556
- var CliProviderInstance = class {
31597
+ var CliProviderInstance = class _CliProviderInstance {
31557
31598
  constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
31558
31599
  this.provider = provider;
31559
31600
  this.workingDir = workingDir;
@@ -31573,6 +31614,18 @@ var CliProviderInstance = class {
31573
31614
  }
31574
31615
  type;
31575
31616
  category = "cli";
31617
+ /**
31618
+ * Quiet period an approval modal's signature must be stable before
31619
+ * auto-approve sends the approve key. Guards against firing on a prompt
31620
+ * that is still streaming into the PTY (the "resolves too fast" symptom):
31621
+ * while the modal text/buttons are still changing, every frame yields a
31622
+ * new signature and the settle clock restarts. Once the prompt finishes
31623
+ * rendering the signature holds and the key is sent after this window.
31624
+ * Bounded + small so genuine approvals stay timely. The FSM is already
31625
+ * authoritative over the `waiting_approval` state; this only delays the
31626
+ * keystroke until the modal *content* has settled.
31627
+ */
31628
+ static AUTO_APPROVE_SETTLE_MS = 600;
31576
31629
  adapter;
31577
31630
  context = null;
31578
31631
  events = [];
@@ -31586,6 +31639,14 @@ var CliProviderInstance = class {
31586
31639
  autoApproveBusy = false;
31587
31640
  autoApproveBusyTimer = null;
31588
31641
  lastAutoApprovalSignature = "";
31642
+ // Settle gate: the approval modal's signature + the wall-clock when this
31643
+ // exact signature was first observed. Auto-approve only fires once the
31644
+ // SAME signature has been stable for AUTO_APPROVE_SETTLE_MS, so a prompt
31645
+ // still streaming into the PTY (its buttons/message changing frame to
31646
+ // frame) keeps resetting the timer and is never approved half-rendered.
31647
+ pendingAutoApprovalSignature = "";
31648
+ pendingAutoApprovalSince = 0;
31649
+ autoApproveSettleTimer = null;
31589
31650
  controlValues = {};
31590
31651
  summaryMetadata = void 0;
31591
31652
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -32020,6 +32081,14 @@ var CliProviderInstance = class {
32020
32081
  dispose() {
32021
32082
  this.adapter.shutdown();
32022
32083
  this.monitor.reset();
32084
+ if (this.autoApproveSettleTimer) {
32085
+ clearTimeout(this.autoApproveSettleTimer);
32086
+ this.autoApproveSettleTimer = null;
32087
+ }
32088
+ if (this.autoApproveBusyTimer) {
32089
+ clearTimeout(this.autoApproveBusyTimer);
32090
+ this.autoApproveBusyTimer = null;
32091
+ }
32023
32092
  this.appliedEffectKeys.clear();
32024
32093
  try {
32025
32094
  this.cachedSqliteDb?.close();
@@ -32370,6 +32439,12 @@ var CliProviderInstance = class {
32370
32439
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
32371
32440
  if (!autoApproveActive) {
32372
32441
  this.lastAutoApprovalSignature = "";
32442
+ this.pendingAutoApprovalSignature = "";
32443
+ this.pendingAutoApprovalSince = 0;
32444
+ if (this.autoApproveSettleTimer) {
32445
+ clearTimeout(this.autoApproveSettleTimer);
32446
+ this.autoApproveSettleTimer = null;
32447
+ }
32373
32448
  return autoApproveActive;
32374
32449
  }
32375
32450
  const modal = adapterStatus.activeModal;
@@ -32388,22 +32463,57 @@ var CliProviderInstance = class {
32388
32463
  buttons.join("|"),
32389
32464
  buttonIndex
32390
32465
  ].join("::");
32391
- if (!this.autoApproveBusy || signature !== this.lastAutoApprovalSignature) {
32392
- this.autoApproveBusy = true;
32393
- this.lastAutoApprovalSignature = signature;
32394
- if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
32395
- this.autoApproveBusyTimer = setTimeout(() => {
32396
- this.autoApproveBusy = false;
32397
- this.autoApproveBusyTimer = null;
32398
- this.lastAutoApprovalSignature = "";
32399
- }, 5e3);
32400
- this.recordAutoApproval(modal?.message, buttonLabel, now);
32401
- setTimeout(() => {
32402
- this.adapter.resolveModal(buttonIndex);
32403
- }, 0);
32466
+ if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
32467
+ return autoApproveActive;
32404
32468
  }
32469
+ if (signature !== this.pendingAutoApprovalSignature) {
32470
+ this.pendingAutoApprovalSignature = signature;
32471
+ this.pendingAutoApprovalSince = now;
32472
+ }
32473
+ const settledForMs = now - this.pendingAutoApprovalSince;
32474
+ if (settledForMs < _CliProviderInstance.AUTO_APPROVE_SETTLE_MS) {
32475
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
32476
+ this.autoApproveSettleTimer = setTimeout(() => {
32477
+ this.autoApproveSettleTimer = null;
32478
+ this.recheckAutoApproveSettled();
32479
+ }, _CliProviderInstance.AUTO_APPROVE_SETTLE_MS - settledForMs + 20);
32480
+ return autoApproveActive;
32481
+ }
32482
+ if (this.autoApproveSettleTimer) {
32483
+ clearTimeout(this.autoApproveSettleTimer);
32484
+ this.autoApproveSettleTimer = null;
32485
+ }
32486
+ this.autoApproveBusy = true;
32487
+ this.lastAutoApprovalSignature = signature;
32488
+ this.pendingAutoApprovalSignature = "";
32489
+ this.pendingAutoApprovalSince = 0;
32490
+ if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
32491
+ this.autoApproveBusyTimer = setTimeout(() => {
32492
+ this.autoApproveBusy = false;
32493
+ this.autoApproveBusyTimer = null;
32494
+ this.lastAutoApprovalSignature = "";
32495
+ }, 5e3);
32496
+ this.recordAutoApproval(modal?.message, buttonLabel, now);
32497
+ setTimeout(() => {
32498
+ this.adapter.resolveModal(buttonIndex);
32499
+ }, 0);
32405
32500
  return autoApproveActive;
32406
32501
  }
32502
+ /**
32503
+ * Re-drive the auto-approve check after the settle quiet window elapses.
32504
+ * The PTY may have gone silent once the approval prompt finished painting,
32505
+ * so no status-change frame is guaranteed to re-enter maybeAutoApproveStatus
32506
+ * — this timer-driven re-check picks up the now-settled modal and fires.
32507
+ * Deliberately lighter than detectStatusTransition(): it only re-evaluates
32508
+ * the approval decision; the next real PTY frame refreshes visible status.
32509
+ */
32510
+ recheckAutoApproveSettled() {
32511
+ try {
32512
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
32513
+ this.maybeAutoApproveStatus(adapterStatus, Date.now());
32514
+ } catch {
32515
+ }
32516
+ }
32407
32517
  detectStatusTransition() {
32408
32518
  const now = Date.now();
32409
32519
  const adapterStatus = this.adapter.getStatus({ allowParse: false });