@adhdev/daemon-core 0.9.82-rc.207 → 0.9.82-rc.209

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 (30) hide show
  1. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +1 -3
  2. package/dist/cli-adapters/terminal-backends/types.d.ts +1 -2
  3. package/dist/cli-adapters/terminal-screen.d.ts +2 -11
  4. package/dist/index.js +156 -212
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +141 -197
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/providers/spec/adapter.d.ts +11 -19
  9. package/dist/providers/spec/driver.d.ts +14 -0
  10. package/dist/providers/spec/schema.gen.d.ts +24 -1
  11. package/dist/providers/spec/types.d.ts +8 -1
  12. package/dist/shared-types-extra.d.ts +1 -3
  13. package/package.json +1 -3
  14. package/src/cli-adapters/provider-cli-adapter.ts +3 -2
  15. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +11 -34
  16. package/src/cli-adapters/terminal-backends/types.ts +1 -3
  17. package/src/cli-adapters/terminal-screen.ts +11 -81
  18. package/src/commands/mesh-coordinator.ts +3 -2
  19. package/src/daemon/dev-auto-implement.ts +3 -2
  20. package/src/providers/spec/adapter.ts +40 -78
  21. package/src/providers/spec/cli-adapter.ts +2 -0
  22. package/src/providers/spec/driver.ts +71 -7
  23. package/src/providers/spec/evaluator.ts +44 -14
  24. package/src/providers/spec/loader.ts +6 -1
  25. package/src/providers/spec/schema.gen.ts +17 -1
  26. package/src/providers/spec/types.ts +5 -1
  27. package/src/shared-types-extra.ts +1 -3
  28. package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
  29. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
  30. package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -104
package/dist/index.mjs CHANGED
@@ -1958,6 +1958,7 @@ __export(mesh_coordinator_exports, {
1958
1958
  import { createHash as createHash2 } from "crypto";
1959
1959
  import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1960
1960
  import * as os4 from "os";
1961
+ import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
1961
1962
  import { basename as basename2, isAbsolute as isAbsolute4, join as join7, resolve as resolve7 } from "path";
1962
1963
  function isHermesProvider(provider, cliType) {
1963
1964
  const type = cliType?.trim() || provider?.type?.trim() || "";
@@ -2299,8 +2300,8 @@ async function execUnderPty(command, args, options = {}) {
2299
2300
  try {
2300
2301
  child = ptyLib.spawn(command, args, {
2301
2302
  name: "xterm-256color",
2302
- cols: 120,
2303
- rows: 30,
2303
+ cols: DEFAULT_SESSION_HOST_COLS,
2304
+ rows: DEFAULT_SESSION_HOST_ROWS,
2304
2305
  cwd: options.cwd ?? process.cwd(),
2305
2306
  env
2306
2307
  });
@@ -7043,11 +7044,9 @@ function getBindingCandidates() {
7043
7044
  const explicit = process.env.ADHDEV_GHOSTTY_VT_BINDING?.trim();
7044
7045
  return explicit ? [explicit] : DEFAULT_BINDING_CANDIDATES;
7045
7046
  }
7046
- function loadGhosttyVtBinding(required) {
7047
+ function loadGhosttyVtBinding() {
7047
7048
  if (cachedBinding !== void 0) {
7048
- if (!cachedBinding && required && cachedBindingError) {
7049
- throw cachedBindingError;
7050
- }
7049
+ if (!cachedBinding && cachedBindingError) throw cachedBindingError;
7051
7050
  return cachedBinding;
7052
7051
  }
7053
7052
  const errors = [];
@@ -7068,18 +7067,9 @@ function loadGhosttyVtBinding(required) {
7068
7067
  }
7069
7068
  cachedBinding = null;
7070
7069
  cachedBindingError = new Error(
7071
- `ghostty-vt backend requested but no binding is available (${errors.join("; ") || "no candidates tried"})`
7070
+ `ghostty-vt binding unavailable (${errors.join("; ") || "no candidates tried"})`
7072
7071
  );
7073
- if (required) throw cachedBindingError;
7074
- return null;
7075
- }
7076
- function resolveTerminalBackendPreference() {
7077
- const raw = process.env.ADHDEV_TERMINAL_BACKEND?.trim().toLowerCase();
7078
- if (raw === "ghostty-vt" || raw === "xterm" || raw === "auto") return raw;
7079
- return "auto";
7080
- }
7081
- function isGhosttyVtBackendAvailable() {
7082
- return !!loadGhosttyVtBinding(false);
7072
+ throw cachedBindingError;
7083
7073
  }
7084
7074
  var DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend;
7085
7075
  var init_ghostty_vt_backend = __esm({
@@ -7093,10 +7083,7 @@ var init_ghostty_vt_backend = __esm({
7093
7083
  kind = "ghostty-vt";
7094
7084
  terminal;
7095
7085
  constructor(options) {
7096
- const binding = loadGhosttyVtBinding(true);
7097
- if (!binding) {
7098
- throw new Error("ghostty-vt backend requested but no binding is available");
7099
- }
7086
+ const binding = loadGhosttyVtBinding();
7100
7087
  this.terminal = binding.createTerminal({
7101
7088
  cols: Math.max(1, options.cols | 0),
7102
7089
  rows: Math.max(1, options.rows | 0),
@@ -7130,141 +7117,26 @@ var init_ghostty_vt_backend = __esm({
7130
7117
  }
7131
7118
  });
7132
7119
 
7133
- // src/cli-adapters/terminal-backends/xterm-backend.ts
7134
- function loadTerminalCtor() {
7135
- if (!TerminalCtor) {
7136
- const mod = __require("@xterm/xterm");
7137
- TerminalCtor = mod.Terminal || mod.default?.Terminal || mod.default;
7138
- if (!TerminalCtor) {
7139
- throw new Error("@xterm/xterm Terminal export not found");
7140
- }
7141
- }
7142
- return TerminalCtor;
7143
- }
7144
- var TerminalCtor, XtermTerminalBackend;
7145
- var init_xterm_backend = __esm({
7146
- "src/cli-adapters/terminal-backends/xterm-backend.ts"() {
7147
- "use strict";
7148
- TerminalCtor = null;
7149
- XtermTerminalBackend = class {
7150
- kind = "xterm";
7151
- rows;
7152
- cols;
7153
- terminal;
7154
- constructor(options) {
7155
- this.rows = Math.max(1, options.rows | 0);
7156
- this.cols = Math.max(1, options.cols | 0);
7157
- this.terminal = this.createTerminal(options.scrollback);
7158
- }
7159
- resize(rows, cols) {
7160
- this.rows = Math.max(1, rows | 0);
7161
- this.cols = Math.max(1, cols | 0);
7162
- this.terminal.resize(this.cols, this.rows);
7163
- }
7164
- write(data) {
7165
- if (!data) return;
7166
- this.terminal.write(data);
7167
- }
7168
- getText() {
7169
- const buffer = this.terminal.buffer.active;
7170
- const start = Math.max(0, buffer.viewportY || 0);
7171
- const end = Math.max(start, Math.min(buffer.length || 0, start + this.rows));
7172
- const lines = [];
7173
- for (let i = start; i < end; i++) {
7174
- const line = buffer.getLine(i);
7175
- const raw = line ? line.translateToString(false) : "";
7176
- lines.push(raw.replace(/\s+$/, ""));
7177
- }
7178
- let first = 0;
7179
- let last = lines.length;
7180
- while (first < last && !lines[first]?.trim()) first++;
7181
- while (last > first && !lines[last - 1]?.trim()) last--;
7182
- return lines.slice(first, last).join("\n");
7183
- }
7184
- getCursorPosition() {
7185
- const buffer = this.terminal.buffer.active;
7186
- return {
7187
- col: Math.max(0, buffer.cursorX || 0),
7188
- row: Math.max(0, buffer.cursorY || 0)
7189
- };
7190
- }
7191
- dispose() {
7192
- this.terminal.dispose();
7193
- }
7194
- createTerminal(scrollback) {
7195
- const Terminal2 = loadTerminalCtor();
7196
- return new Terminal2({
7197
- cols: this.cols,
7198
- rows: this.rows,
7199
- scrollback
7200
- });
7201
- }
7202
- };
7203
- }
7204
- });
7205
-
7206
7120
  // src/cli-adapters/terminal-screen.ts
7121
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS2, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS2 } from "@adhdev/session-host-core";
7207
7122
  function getTerminalBackendRuntimeStatus() {
7208
- const preference = resolveTerminalBackendPreference();
7209
- const ghosttyAvailable = isGhosttyVtBackendAvailable();
7210
- const backend = preference === "ghostty-vt" || preference === "auto" && ghosttyAvailable ? "ghostty-vt" : "xterm";
7211
- return { backend, preference, ghosttyAvailable };
7212
- }
7213
- function createTerminalBackend(options, preference) {
7214
- const ghosttyAvailable = isGhosttyVtBackendAvailable();
7215
- if (preference === "ghostty-vt") {
7216
- const backend2 = new GhosttyVtTerminalBackend(options);
7217
- logTerminalBackendSelection(preference, ghosttyAvailable, backend2.kind);
7218
- return backend2;
7219
- }
7220
- if (preference === "auto" && ghosttyAvailable) {
7221
- const backend2 = new GhosttyVtTerminalBackend(options);
7222
- logTerminalBackendSelection(preference, ghosttyAvailable, backend2.kind);
7223
- return backend2;
7224
- }
7225
- const backend = new XtermTerminalBackend(options);
7226
- logTerminalBackendSelection(preference, ghosttyAvailable, backend.kind);
7227
- return backend;
7228
- }
7229
- function logTerminalBackendSelection(preference, ghosttyAvailable, backendKind) {
7230
- const key = `${preference}:${ghosttyAvailable}:${backendKind}`;
7231
- if (loggedTerminalBackends.has(key)) return;
7232
- loggedTerminalBackends.add(key);
7233
- if (backendKind === "xterm" && preference !== "xterm" && !ghosttyAvailable) {
7234
- const message = `[terminal-screen] ghostty-vt unavailable; using xterm fallback (preference=${preference})`;
7235
- if (preference === "auto") {
7236
- LOG.info("Terminal", message);
7237
- } else {
7238
- LOG.warn("Terminal", message);
7239
- }
7240
- return;
7241
- }
7242
- LOG.info(
7243
- "Terminal",
7244
- `[terminal-screen] backend=${backendKind} preference=${preference} ghosttyAvailable=${ghosttyAvailable}`
7245
- );
7123
+ return { backend: "ghostty-vt" };
7246
7124
  }
7247
- var DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen;
7125
+ var DEFAULT_SCROLLBACK, TerminalScreen;
7248
7126
  var init_terminal_screen = __esm({
7249
7127
  "src/cli-adapters/terminal-screen.ts"() {
7250
7128
  "use strict";
7251
- init_logger();
7252
7129
  init_ghostty_vt_backend();
7253
- init_xterm_backend();
7254
7130
  DEFAULT_SCROLLBACK = 2e3;
7255
- loggedTerminalBackends = /* @__PURE__ */ new Set();
7256
7131
  TerminalScreen = class {
7257
- backendKind;
7132
+ backendKind = "ghostty-vt";
7258
7133
  rows;
7259
7134
  cols;
7260
- preference;
7261
7135
  terminal;
7262
- constructor(rows = 40, cols = 120) {
7136
+ constructor(rows = DEFAULT_SESSION_HOST_ROWS2, cols = DEFAULT_SESSION_HOST_COLS2) {
7263
7137
  this.rows = Math.max(1, rows | 0);
7264
7138
  this.cols = Math.max(1, cols | 0);
7265
- this.preference = resolveTerminalBackendPreference();
7266
7139
  this.terminal = this.createBackend();
7267
- this.backendKind = this.terminal.kind;
7268
7140
  }
7269
7141
  reset(rows = this.rows, cols = this.cols) {
7270
7142
  this.rows = Math.max(1, rows | 0);
@@ -7290,11 +7162,11 @@ var init_terminal_screen = __esm({
7290
7162
  this.terminal.dispose();
7291
7163
  }
7292
7164
  createBackend() {
7293
- return createTerminalBackend({
7165
+ return new GhosttyVtTerminalBackend({
7294
7166
  cols: this.cols,
7295
7167
  rows: this.rows,
7296
7168
  scrollback: DEFAULT_SCROLLBACK
7297
- }, this.preference);
7169
+ });
7298
7170
  }
7299
7171
  };
7300
7172
  }
@@ -7313,6 +7185,10 @@ var init_spawn_env = __esm({
7313
7185
  });
7314
7186
 
7315
7187
  // src/cli-adapters/pty-transport.ts
7188
+ var pty_transport_exports = {};
7189
+ __export(pty_transport_exports, {
7190
+ NodePtyTransportFactory: () => NodePtyTransportFactory
7191
+ });
7316
7192
  import * as os11 from "os";
7317
7193
  function loadNodePty() {
7318
7194
  if (cachedPty !== void 0) return cachedPty;
@@ -9503,7 +9379,7 @@ var init_provider_cli_config = __esm({
9503
9379
  // src/cli-adapters/provider-cli-runtime.ts
9504
9380
  import * as os13 from "os";
9505
9381
  import * as path17 from "path";
9506
- import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
9382
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS3, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS3 } from "@adhdev/session-host-core";
9507
9383
  function resolveCliSpawnPlan(options) {
9508
9384
  const { provider, runtimeSettings, workingDir, extraArgs, extraEnv } = options;
9509
9385
  const { spawn: spawnConfig } = provider;
@@ -9541,8 +9417,8 @@ function resolveCliSpawnPlan(options) {
9541
9417
  isWin,
9542
9418
  useShell,
9543
9419
  ptyOptions: {
9544
- cols: DEFAULT_SESSION_HOST_COLS,
9545
- rows: DEFAULT_SESSION_HOST_ROWS,
9420
+ cols: DEFAULT_SESSION_HOST_COLS3,
9421
+ rows: DEFAULT_SESSION_HOST_ROWS3,
9546
9422
  cwd: workingDir,
9547
9423
  env
9548
9424
  }
@@ -9608,6 +9484,7 @@ __export(provider_cli_adapter_exports, {
9608
9484
  normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
9609
9485
  });
9610
9486
  import * as os14 from "os";
9487
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS4, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS4 } from "@adhdev/session-host-core";
9611
9488
  function appendBoundedText(current, chunk, maxChars) {
9612
9489
  if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
9613
9490
  if (maxChars <= 0) return "";
@@ -9774,7 +9651,7 @@ var init_provider_cli_adapter = __esm({
9774
9651
  /** Full accumulated raw PTY output (with ANSI) */
9775
9652
  accumulatedRawBuffer = "";
9776
9653
  /** Current visible terminal screen snapshot */
9777
- terminalScreen = new TerminalScreen(24, 80);
9654
+ terminalScreen = new TerminalScreen(DEFAULT_SESSION_HOST_ROWS4, DEFAULT_SESSION_HOST_COLS4);
9778
9655
  static MAX_RESPONSE_BUFFER = 8e3;
9779
9656
  static MAX_RECENT_OUTPUT_BUFFER = 1e3;
9780
9657
  responseBufferDroppedChars = 0;
@@ -9987,7 +9864,7 @@ ${lastSnapshot}`;
9987
9864
  clearTimeout(this.startupSettleTimer);
9988
9865
  this.startupSettleTimer = null;
9989
9866
  }
9990
- this.resetTerminalScreen(24, 80);
9867
+ this.resetTerminalScreen(DEFAULT_SESSION_HOST_ROWS4, DEFAULT_SESSION_HOST_COLS4);
9991
9868
  this.pendingTerminalQueryTail = "";
9992
9869
  this.ready = false;
9993
9870
  await this.ptyProcess.ready;
@@ -26078,33 +25955,33 @@ import * as os15 from "os";
26078
25955
  import * as path19 from "path";
26079
25956
 
26080
25957
  // src/providers/spec/adapter.ts
26081
- init_pty_transport();
26082
- import * as xtermHeadlessNs from "@xterm/headless";
26083
- var TerminalCtor2 = xtermHeadlessNs.Terminal ?? xtermHeadlessNs.default?.Terminal;
25958
+ init_terminal_screen();
25959
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS5, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS5 } from "@adhdev/session-host-core";
26084
25960
  var TerminalAdapter = class {
26085
25961
  constructor(opts, handlers) {
26086
25962
  this.opts = opts;
26087
25963
  this.handlers = handlers;
26088
- this.cols = opts.cols ?? 100;
26089
- this.rows = opts.rows ?? 30;
25964
+ this.cols = opts.cols ?? DEFAULT_SESSION_HOST_COLS5;
25965
+ this.rows = opts.rows ?? DEFAULT_SESSION_HOST_ROWS5;
26090
25966
  this.screenDebounceMs = opts.screenChangeDebounceMs ?? 80;
26091
25967
  this.tickIntervalMs = opts.tickIntervalMs ?? 0;
26092
- this.factory = opts.transportFactory ?? new NodePtyTransportFactory();
26093
- this.term = new TerminalCtor2({ cols: this.cols, rows: this.rows, allowProposedApi: true, scrollback: 1e3 });
25968
+ const { NodePtyTransportFactory: NodePtyTransportFactory2 } = (init_pty_transport(), __toCommonJS(pty_transport_exports));
25969
+ this.factory = opts.transportFactory ?? new NodePtyTransportFactory2();
25970
+ this.screen = new TerminalScreen(this.rows, this.cols);
26094
25971
  }
26095
- term;
26096
- pty = null;
26097
- factory;
26098
- cols;
26099
25972
  rows;
25973
+ cols;
26100
25974
  screenDebounceMs;
26101
25975
  tickIntervalMs;
25976
+ factory;
25977
+ screen;
25978
+ pty = null;
26102
25979
  screenTimer = null;
26103
25980
  tickTimer = null;
26104
25981
  lastScreen = "";
26105
25982
  start() {
26106
25983
  this.pty = this.factory.spawn(this.opts.binary, this.opts.args ?? [], {
26107
- cwd: this.opts.cwd,
25984
+ cwd: this.opts.cwd ?? process.cwd(),
26108
25985
  env: { ...process.env, ...this.opts.env ?? {} },
26109
25986
  cols: this.cols,
26110
25987
  rows: this.rows
@@ -26120,24 +25997,21 @@ var TerminalAdapter = class {
26120
25997
  this.tickTimer = setInterval(() => this.handlers.tick?.(), this.tickIntervalMs);
26121
25998
  }
26122
25999
  }
26123
- send_keys(s) {
26124
- this.pty?.write(s);
26125
- }
26126
26000
  resize(cols, rows) {
26127
26001
  this.cols = cols;
26128
26002
  this.rows = rows;
26129
26003
  this.pty?.resize(cols, rows);
26130
- this.term.resize(cols, rows);
26004
+ this.screen.resize(rows, cols);
26131
26005
  }
26132
26006
  snapshot() {
26133
26007
  return this.lastScreen || this.computeScreen();
26134
26008
  }
26135
26009
  getCursorPosition() {
26136
- const buf = this.term.buffer.active;
26137
- return {
26138
- row: Math.max(0, buf.cursorY ?? 0),
26139
- col: Math.max(0, buf.cursorX ?? 0)
26140
- };
26010
+ const pos = this.screen.getCursorPosition();
26011
+ return { row: pos.row, col: pos.col };
26012
+ }
26013
+ send_keys(text) {
26014
+ this.pty?.write(text);
26141
26015
  }
26142
26016
  kill() {
26143
26017
  this.stopTimers();
@@ -26146,14 +26020,14 @@ var TerminalAdapter = class {
26146
26020
  } catch {
26147
26021
  }
26148
26022
  this.pty = null;
26149
- this.term.dispose();
26023
+ this.screen.dispose();
26150
26024
  }
26151
26025
  onChunk(chunk) {
26152
26026
  try {
26153
26027
  this.handlers.on_pty_data?.(chunk);
26154
26028
  } catch {
26155
26029
  }
26156
- this.term.write(chunk);
26030
+ this.screen.write(chunk);
26157
26031
  if (this.screenTimer) return;
26158
26032
  this.screenTimer = setTimeout(() => {
26159
26033
  this.screenTimer = null;
@@ -26167,15 +26041,7 @@ var TerminalAdapter = class {
26167
26041
  }, this.screenDebounceMs);
26168
26042
  }
26169
26043
  computeScreen() {
26170
- const buf = this.term.buffer.active;
26171
- const out = [];
26172
- for (let y = 0; y < buf.length; y += 1) {
26173
- const line = buf.getLine(y);
26174
- if (!line) continue;
26175
- out.push(line.translateToString(true));
26176
- }
26177
- while (out.length > 0 && out[out.length - 1].trim() === "") out.pop();
26178
- return out.join("\n");
26044
+ return this.screen.getText();
26179
26045
  }
26180
26046
  stopTimers() {
26181
26047
  if (this.screenTimer) {
@@ -26189,6 +26055,9 @@ var TerminalAdapter = class {
26189
26055
  }
26190
26056
  };
26191
26057
 
26058
+ // src/providers/spec/driver.ts
26059
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS6, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS6 } from "@adhdev/session-host-core";
26060
+
26192
26061
  // src/providers/spec/evaluator.ts
26193
26062
  function resolveSize(size, total) {
26194
26063
  if (size === void 0) return 0;
@@ -26316,12 +26185,10 @@ function matchState(state, sections, fullScreen, trace, cursor) {
26316
26185
  }
26317
26186
  return { matched: true, title };
26318
26187
  }
26319
- function extractModal(state, sections, fullScreen, title, trace) {
26320
- if (!state.modal_buttons) return null;
26321
- const hay = sectionText(sections, state.modal_buttons.section, fullScreen);
26188
+ function extractButtonsWithPattern(rule, hay, keyTemplate, continuationLines) {
26322
26189
  const buttons = [];
26323
- if (state.modal_buttons.continuation_lines) {
26324
- const re = compileLinePattern(state.modal_buttons);
26190
+ if (continuationLines) {
26191
+ const re = compileLinePattern(rule);
26325
26192
  const lines = hay.split("\n");
26326
26193
  for (let i = 0; i < lines.length; i += 1) {
26327
26194
  const m = re.exec(lines[i]);
@@ -26339,24 +26206,40 @@ function extractModal(state, sections, fullScreen, title, trace) {
26339
26206
  j += 1;
26340
26207
  }
26341
26208
  if (buttons.some((b) => b.index === idx)) continue;
26342
- const key = state.modal_buttons.key_for_index.replace(/\{index\}/g, String(idx));
26209
+ const key = keyTemplate.replace(/\{index\}/g, String(idx));
26343
26210
  buttons.push({ index: idx, label, key });
26344
26211
  i = j - 1;
26345
26212
  }
26346
26213
  } else {
26347
- const re = compilePattern(state.modal_buttons);
26214
+ const re = compilePattern(rule);
26348
26215
  let m;
26349
26216
  while ((m = re.exec(hay)) !== null) {
26350
26217
  const idx = Number(m[1]);
26351
26218
  const label = String(m[2] ?? "").trim();
26352
26219
  if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
26353
26220
  if (buttons.some((b) => b.index === idx)) continue;
26354
- const key = state.modal_buttons.key_for_index.replace(/\{index\}/g, String(idx));
26221
+ const key = keyTemplate.replace(/\{index\}/g, String(idx));
26355
26222
  buttons.push({ index: idx, label, key });
26356
26223
  }
26357
26224
  }
26358
26225
  buttons.sort((a, b) => a.index - b.index);
26226
+ return buttons;
26227
+ }
26228
+ function extractModal(state, sections, fullScreen, title, trace) {
26229
+ if (!state.modal_buttons) return null;
26230
+ const hay = sectionText(sections, state.modal_buttons.section, fullScreen);
26359
26231
  const minCount = state.modal_buttons.min_count ?? 2;
26232
+ const keyTemplate = state.modal_buttons.key_for_index;
26233
+ const continuationLines = state.modal_buttons.continuation_lines ?? false;
26234
+ const candidates = state.modal_buttons.patterns?.length ? state.modal_buttons.patterns : state.modal_buttons.pattern ? [{ pattern: state.modal_buttons.pattern, flags: state.modal_buttons.flags }] : [];
26235
+ let buttons = [];
26236
+ for (const candidate of candidates) {
26237
+ const result = extractButtonsWithPattern(candidate, hay, keyTemplate, continuationLines);
26238
+ if (result.length >= minCount) {
26239
+ buttons = result;
26240
+ break;
26241
+ }
26242
+ }
26360
26243
  if (buttons.length < minCount) {
26361
26244
  trace.push({ kind: "modal", text: `modal_buttons matched ${buttons.length}/${minCount} required \u2014 discarded` });
26362
26245
  return null;
@@ -26734,9 +26617,12 @@ var SCHEMA = {
26734
26617
  "type": "object",
26735
26618
  "additionalProperties": false,
26736
26619
  "required": [
26737
- "pattern",
26738
26620
  "key_for_index"
26739
26621
  ],
26622
+ "oneOf": [
26623
+ { "required": ["pattern"] },
26624
+ { "required": ["patterns"] }
26625
+ ],
26740
26626
  "properties": {
26741
26627
  "section": {
26742
26628
  "type": "string"
@@ -26748,6 +26634,19 @@ var SCHEMA = {
26748
26634
  "flags": {
26749
26635
  "type": "string"
26750
26636
  },
26637
+ "patterns": {
26638
+ "type": "array",
26639
+ "minItems": 1,
26640
+ "items": {
26641
+ "type": "object",
26642
+ "additionalProperties": false,
26643
+ "required": ["pattern"],
26644
+ "properties": {
26645
+ "pattern": { "type": "string", "minLength": 1 },
26646
+ "flags": { "type": "string" }
26647
+ }
26648
+ }
26649
+ },
26751
26650
  "key_for_index": {
26752
26651
  "type": "string",
26753
26652
  "minLength": 1
@@ -27035,7 +26934,12 @@ function validateRefs(spec) {
27035
26934
  if (s.modal_buttons.section && !sectionIds.has(s.modal_buttons.section)) {
27036
26935
  errs.push(`states[${s.id}].modal_buttons.section "${s.modal_buttons.section}" unknown`);
27037
26936
  }
27038
- compileRegex2(s.modal_buttons.pattern, s.modal_buttons.flags ?? "m", `states[${s.id}].modal_buttons.pattern`, errs);
26937
+ if (s.modal_buttons.pattern) {
26938
+ compileRegex2(s.modal_buttons.pattern, s.modal_buttons.flags ?? "m", `states[${s.id}].modal_buttons.pattern`, errs);
26939
+ }
26940
+ for (const [pi, p] of (s.modal_buttons.patterns ?? []).entries()) {
26941
+ compileRegex2(p.pattern, p.flags ?? "m", `states[${s.id}].modal_buttons.patterns[${pi}]`, errs);
26942
+ }
27039
26943
  }
27040
26944
  }
27041
26945
  for (const c of spec.control_bar ?? []) {
@@ -27105,8 +27009,8 @@ function matchesCompletionIdleRule(spec, ev, screen) {
27105
27009
  if (!haystack) return null;
27106
27010
  try {
27107
27011
  const regex = new RegExp(rule.regex, rule.flags || "");
27108
- const match = haystack.match(regex);
27109
- return match?.[0] || null;
27012
+ const matched = regex.test(haystack);
27013
+ return matched ? rule.regex : null;
27110
27014
  } catch {
27111
27015
  return null;
27112
27016
  }
@@ -27168,6 +27072,14 @@ var SpecDriver = class {
27168
27072
  * because the evaluator already moved past busy by the time the hold
27169
27073
  * kicks in. */
27170
27074
  lastBusyState = null;
27075
+ /** Timestamp of the last time we entered a modal state (approval/picker or
27076
+ * any non-busy non-idle state). Used to suppress brief busy blips that
27077
+ * appear while the modal is still on screen — Claude Code streams body
27078
+ * text that transiently shows a spinner even while an approval modal is
27079
+ * visible, causing rapid approval→busy→approval flicker on the dashboard. */
27080
+ lastModalAt = 0;
27081
+ /** The modal state snapshot held across busy blips. */
27082
+ lastModalState = null;
27171
27083
  completionIdleFirstSeenAt = 0;
27172
27084
  completionIdleKey = "";
27173
27085
  /** Timer that re-runs evaluate() once the hold window expires. Needed
@@ -27281,6 +27193,17 @@ var SpecDriver = class {
27281
27193
  getSpecPath() {
27282
27194
  return this.opts.specPath;
27283
27195
  }
27196
+ getCompletionIdleDebounceState() {
27197
+ if (!this.completionIdleKey || !this.completionIdleFirstSeenAt) return null;
27198
+ const rule = this.spec.debounce?.completion_idle_after;
27199
+ if (!rule) return null;
27200
+ return {
27201
+ active: true,
27202
+ ageMs: Date.now() - this.completionIdleFirstSeenAt,
27203
+ holdMs: rule.hold_ms ?? 0,
27204
+ forceAfterMs: typeof rule.force_after_ms === "number" ? rule.force_after_ms : 0
27205
+ };
27206
+ }
27284
27207
  getScreen() {
27285
27208
  return this.adapter.snapshot();
27286
27209
  }
@@ -27309,8 +27232,8 @@ var SpecDriver = class {
27309
27232
  args: [...baseArgs, ...extra],
27310
27233
  cwd: this.opts.workingDir,
27311
27234
  env: { ...this.spec.env ?? {}, ...this.opts.extraEnv ?? {} },
27312
- cols: this.opts.cols ?? 100,
27313
- rows: this.opts.rows ?? 30,
27235
+ cols: this.opts.cols ?? DEFAULT_SESSION_HOST_COLS6,
27236
+ rows: this.opts.rows ?? DEFAULT_SESSION_HOST_ROWS6,
27314
27237
  transportFactory: this.opts.transportFactory
27315
27238
  };
27316
27239
  }
@@ -27367,6 +27290,14 @@ var SpecDriver = class {
27367
27290
  evState = this.lastBusyState ?? evState;
27368
27291
  }
27369
27292
  }
27293
+ const idleStateId = this.spec.default_state ?? "idle";
27294
+ const isModalState = (id) => id !== null && id !== "busy" && id !== idleStateId;
27295
+ if (isModalState(this.currentStateId) && evState.id === "busy") {
27296
+ const ageMs = Date.now() - this.lastModalAt;
27297
+ if (ageMs < busyHoldMs && this.lastModalState) {
27298
+ evState = this.lastModalState;
27299
+ }
27300
+ }
27370
27301
  const completionIdleRule = this.spec.debounce?.completion_idle_after;
27371
27302
  let busyWakeMs = busyHoldMs;
27372
27303
  if (evState.id === "busy" && completionIdleRule) {
@@ -27406,11 +27337,21 @@ var SpecDriver = class {
27406
27337
  this.lastBusyAt = Date.now();
27407
27338
  this.lastBusyState = evState;
27408
27339
  this.cancelIdleHold();
27340
+ if (!this.completionIdleKey) {
27341
+ this.completionIdleFirstSeenAt = 0;
27342
+ }
27409
27343
  this.scheduleBusyExpiry(busyWakeMs);
27410
27344
  } else if (evState.id !== this.currentStateId && evState.id !== "busy") {
27411
27345
  if (evState.id !== (this.spec.default_state ?? "idle")) {
27412
27346
  this.cancelIdleHold();
27413
27347
  }
27348
+ if (isModalState(evState.id)) {
27349
+ this.lastModalAt = Date.now();
27350
+ this.lastModalState = evState;
27351
+ } else {
27352
+ this.lastModalAt = 0;
27353
+ this.lastModalState = null;
27354
+ }
27414
27355
  }
27415
27356
  const idleHoldMs = this.spec.debounce?.idle_hold_ms ?? 0;
27416
27357
  const isIdleState = evState.id === (this.spec.default_state ?? "idle");
@@ -27657,7 +27598,7 @@ function guessExt(mime) {
27657
27598
  }
27658
27599
  function extractMatchedRules(ev) {
27659
27600
  if (!Array.isArray(ev.trace)) return [];
27660
- return ev.trace.filter((t) => t.matched === true || t.kind === "state_match").map((t) => t.rule ?? t.stateId ?? t.id ?? String(t)).filter(Boolean);
27601
+ return ev.trace.filter((t) => t.kind === "state_match").map((t) => t.text).filter(Boolean);
27661
27602
  }
27662
27603
 
27663
27604
  // src/providers/spec/native-history-executor.ts
@@ -28638,6 +28579,8 @@ var SpecCliAdapter = class {
28638
28579
  idleHoldPending: this.driver.hasIdleHoldPending(),
28639
28580
  lastBusyAt: this.driver.getLastBusyAt(),
28640
28581
  specPath: this.driver.getSpecPath(),
28582
+ cursorPosition: this.driver.getCursorPosition(),
28583
+ completionIdleDebounce: this.driver.getCompletionIdleDebounceState(),
28641
28584
  // Extended fields
28642
28585
  name: this.cliName,
28643
28586
  status: this.getStatus().status,
@@ -47095,6 +47038,7 @@ async function handleCliRaw(ctx, req, res) {
47095
47038
  import * as fs26 from "fs";
47096
47039
  import * as path38 from "path";
47097
47040
  import * as os28 from "os";
47041
+ import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS7, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS7 } from "@adhdev/session-host-core";
47098
47042
  function getAutoImplPid(ctx) {
47099
47043
  const pid = ctx.autoImplProcess?.pid;
47100
47044
  return typeof pid === "number" && pid > 0 ? pid : null;
@@ -47492,8 +47436,8 @@ async function handleAutoImplement(ctx, type, req, res) {
47492
47436
  const isWin2 = os28.platform() === "win32";
47493
47437
  child = pty.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
47494
47438
  name: "xterm-256color",
47495
- cols: 120,
47496
- rows: 40,
47439
+ cols: DEFAULT_SESSION_HOST_COLS7,
47440
+ rows: DEFAULT_SESSION_HOST_ROWS7,
47497
47441
  cwd: providerDir,
47498
47442
  env: { ...process.env, ...spawn4.env || {} }
47499
47443
  });