@adhdev/daemon-standalone 1.0.58-rc.51 → 1.0.58-rc.53

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.js CHANGED
@@ -36911,10 +36911,10 @@ var require_dist3 = __commonJS({
36911
36911
  }
36912
36912
  function getDaemonBuildInfo() {
36913
36913
  if (cached2) return cached2;
36914
- const commit = readInjected(true ? "8ae4b622b6ab59e300631f175785f024b2cbecb2" : void 0) ?? "unknown";
36915
- const commitShort = readInjected(true ? "8ae4b622" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36916
- const version2 = readInjected(true ? "1.0.58-rc.51" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36917
- const builtAt = readInjected(true ? "2026-08-30T15:26:29.414Z" : void 0);
36914
+ const commit = readInjected(true ? "7335576b38c7efe574df77aa232271dca8221f88" : void 0) ?? "unknown";
36915
+ const commitShort = readInjected(true ? "7335576b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36916
+ const version2 = readInjected(true ? "1.0.58-rc.53" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36917
+ const builtAt = readInjected(true ? "2026-08-30T19:29:33.228Z" : void 0);
36918
36918
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36919
36919
  return cached2;
36920
36920
  }
@@ -75127,6 +75127,43 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75127
75127
  const reasonText = reason === "probe_disabled" ? "quota probing is disabled for this provider on that node (quotaEnabled: false \u2014 an intended opt-out, not a fault)" : reason === "provider_disabled" ? "this provider is not enabled on that node" : reason === "not_measured" ? "no snapshot has been measured yet" : "that node reported no provider-enablement facts (a daemon predating them, or no loader injected here) \u2014 cannot tell not-yet-measured from an opt-out; check the owning node's config directly";
75128
75128
  LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 no quota entry (${reason}: ${reasonText})`);
75129
75129
  }
75130
+ function antigravityPoolFromLeadingLabel(value) {
75131
+ const label = typeof value === "string" ? value.trim().toLowerCase() : "";
75132
+ if (/^gemini(?:[\s/_-]|$)/.test(label)) return "gemini";
75133
+ if (/^(?:claude|gpt)(?:[\s/_-]|$)/.test(label)) return "claude-gpt";
75134
+ return void 0;
75135
+ }
75136
+ function worstBucketWindow(buckets, targetMinutes) {
75137
+ const tolerance = targetMinutes * 0.1;
75138
+ const matches = buckets.filter((bucket2) => {
75139
+ const windowMinutes3 = Number(bucket2.windowMinutes);
75140
+ return Number.isFinite(windowMinutes3) && windowMinutes3 > 0 && Math.abs(windowMinutes3 - targetMinutes) <= tolerance;
75141
+ });
75142
+ if (!matches.length) return null;
75143
+ return matches.reduce((worst, bucket2) => Number(bucket2.usedPercent) > Number(worst.usedPercent) ? bucket2 : worst);
75144
+ }
75145
+ function quotaWindowsForGateTarget(providerType, quota, target) {
75146
+ if (providerType !== "antigravity-cli" || quota.status !== "ok") return void 0;
75147
+ const selectedPool = antigravityPoolFromLeadingLabel(target?.model);
75148
+ if (!selectedPool || !Array.isArray(quota.buckets) || !quota.buckets.length) return void 0;
75149
+ const classified = [];
75150
+ for (const bucket2 of quota.buckets) {
75151
+ const pool = antigravityPoolFromLeadingLabel(bucket2?.name);
75152
+ const usedPercent = Number(bucket2?.usedPercent);
75153
+ const windowMinutes3 = Number(bucket2?.windowMinutes);
75154
+ if (!pool || !Number.isFinite(usedPercent) || !Number.isFinite(windowMinutes3)) return void 0;
75155
+ classified.push({
75156
+ pool,
75157
+ bucket: { usedPercent, windowMinutes: windowMinutes3, resetsAt: bucket2.resetsAt }
75158
+ });
75159
+ }
75160
+ const selectedBuckets = classified.filter((entry) => entry.pool === selectedPool).map((entry) => entry.bucket);
75161
+ if (!selectedBuckets.length) return void 0;
75162
+ const session = worstBucketWindow(selectedBuckets, DEFAULT_SESSION_WINDOW_MINUTES);
75163
+ const weekly = worstBucketWindow(selectedBuckets, DEFAULT_WEEKLY_WINDOW_MINUTES);
75164
+ if (quota.session && !session || quota.weekly && !weekly) return void 0;
75165
+ return { session, weekly };
75166
+ }
75130
75167
  function isSessionResetImminent(session, facts, quota, imminentMs, now) {
75131
75168
  const resetsAt = Number(session?.resetsAt);
75132
75169
  if (!Number.isFinite(resetsAt) || resetsAt <= 0) return false;
@@ -75154,13 +75191,16 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75154
75191
  if (!Number.isFinite(reporterNowMs)) return false;
75155
75192
  return reporterNowMs >= resetsAt;
75156
75193
  }
75157
- function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context) {
75194
+ function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context, target) {
75158
75195
  const entry = quotaEntryFor(node, providerType, context, now);
75159
75196
  if (!entry) {
75160
75197
  logAbsentQuotaFailOpen(node, providerType, policy, now, context);
75161
75198
  return null;
75162
75199
  }
75163
75200
  const { facts, quota } = entry;
75201
+ const targetWindows = quotaWindowsForGateTarget(providerType, quota, target);
75202
+ const sessionWindow = targetWindows ? targetWindows.session : quota.session;
75203
+ const weeklyWindow = targetWindows ? targetWindows.weekly : quota.weekly;
75164
75204
  let sessionTrustworthy = true;
75165
75205
  let weeklyTrustworthy = true;
75166
75206
  if (quota.status !== "ok") {
@@ -75173,14 +75213,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75173
75213
  };
75174
75214
  }
75175
75215
  if (quota.metadata?.lastGoodWindows !== true) return null;
75176
- sessionTrustworthy = isWindowTrustworthy(quota.session, facts, quota, policy, now);
75177
- weeklyTrustworthy = isWindowTrustworthy(quota.weekly, facts, quota, policy, now);
75216
+ sessionTrustworthy = isWindowTrustworthy(sessionWindow, facts, quota, policy, now);
75217
+ weeklyTrustworthy = isWindowTrustworthy(weeklyWindow, facts, quota, policy, now);
75178
75218
  } else {
75179
- sessionTrustworthy = isWindowTrustworthy(quota.session, facts, quota, policy, now);
75180
- weeklyTrustworthy = isWindowTrustworthy(quota.weekly, facts, quota, policy, now);
75219
+ sessionTrustworthy = isWindowTrustworthy(sessionWindow, facts, quota, policy, now);
75220
+ weeklyTrustworthy = isWindowTrustworthy(weeklyWindow, facts, quota, policy, now);
75181
75221
  }
75182
- if (sessionTrustworthy && isWindowExpired(quota.session, facts, quota, now)) sessionTrustworthy = false;
75183
- if (weeklyTrustworthy && isWindowExpired(quota.weekly, facts, quota, now)) weeklyTrustworthy = false;
75222
+ if (sessionTrustworthy && isWindowExpired(sessionWindow, facts, quota, now)) sessionTrustworthy = false;
75223
+ if (weeklyTrustworthy && isWindowExpired(weeklyWindow, facts, quota, now)) weeklyTrustworthy = false;
75184
75224
  if (quota.status === "ok" && !sessionTrustworthy && !weeklyTrustworthy) {
75185
75225
  if (!isQuotaSnapshotFresh(facts, quota, policy, now)) {
75186
75226
  logStaleQuotaFailOpen(node, providerType, facts, quota, policy, now, context);
@@ -75188,8 +75228,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75188
75228
  return null;
75189
75229
  }
75190
75230
  const resolved = resolveQuotaRoutingPolicy(policy);
75191
- const session = remainingPercent(quota.session);
75192
- if (sessionTrustworthy && session !== void 0 && session < resolved.sessionMinRemainingPercent && !isSessionResetImminent(quota.session, facts, quota, resolved.sessionResetImminentMs, now)) {
75231
+ const session = remainingPercent(sessionWindow);
75232
+ if (sessionTrustworthy && session !== void 0 && session < resolved.sessionMinRemainingPercent && !isSessionResetImminent(sessionWindow, facts, quota, resolved.sessionResetImminentMs, now)) {
75193
75233
  return {
75194
75234
  reason: PROVIDER_QUOTA_SESSION_LOW_SKIP_REASON,
75195
75235
  window: "session",
@@ -75197,7 +75237,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75197
75237
  thresholdPercent: resolved.sessionMinRemainingPercent
75198
75238
  };
75199
75239
  }
75200
- const weekly = remainingPercent(quota.weekly);
75240
+ const weekly = remainingPercent(weeklyWindow);
75201
75241
  if (weeklyTrustworthy && weekly !== void 0 && weekly < resolved.weeklyMinRemainingPercent) {
75202
75242
  return {
75203
75243
  reason: PROVIDER_QUOTA_WEEKLY_LOW_SKIP_REASON,
@@ -75231,11 +75271,18 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
75231
75271
  const timeLeftFraction = Math.min(1, Math.max(0, (resetsAt - reporterNowMs) / windowMs));
75232
75272
  return { remainingPercent: remaining, risk: expiryRiskScore(remaining, timeLeftFraction) };
75233
75273
  }
75234
- function rankProvidersByQuotaGate(node, orderedProviderTypes, policy, now = Date.now(), context) {
75274
+ function rankProvidersByQuotaGate(node, orderedProviderTypes, policy, now = Date.now(), context, targetsByProvider) {
75235
75275
  const clear = [];
75236
75276
  const gated = [];
75237
75277
  for (const providerType of orderedProviderTypes) {
75238
- const block2 = evaluateProviderQuotaGate(node, providerType, policy, now, context);
75278
+ const block2 = evaluateProviderQuotaGate(
75279
+ node,
75280
+ providerType,
75281
+ policy,
75282
+ now,
75283
+ context,
75284
+ targetsByProvider?.get(providerType)
75285
+ );
75239
75286
  if (block2) gated.push({ providerType, block: block2 });
75240
75287
  else clear.push(providerType);
75241
75288
  }
@@ -76388,7 +76435,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
76388
76435
  candidates.map((candidate) => candidate.providerType),
76389
76436
  args.quotaRouting,
76390
76437
  now,
76391
- args.quotaFactsContext
76438
+ args.quotaFactsContext,
76439
+ new Map(candidates.map((candidate) => [
76440
+ candidate.providerType,
76441
+ { model: candidate.slot.model }
76442
+ ]))
76392
76443
  );
76393
76444
  const winner = ranked.clear.length ? candidates.find((candidate) => candidate.providerType === ranked.clear[0]) ?? candidates[0] : void 0;
76394
76445
  const diagnostics = buildProviderSelectionDiagnostics({
@@ -79726,10 +79777,17 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
79726
79777
  return void 0;
79727
79778
  }
79728
79779
  function evaluateQuotaClaimGateForAssignment(args) {
79729
- const { meshId, nodeId, sessionId, providerType, trigger, node, mesh, providerLoader, quotaClaimTrace } = args;
79780
+ const { meshId, nodeId, sessionId, providerType, model, trigger, node, mesh, providerLoader, quotaClaimTrace } = args;
79730
79781
  const pinCandidateTaskId = providerPinOverrideCandidateTaskId(meshId, providerType);
79731
79782
  const isPinOverride = !!pinCandidateTaskId;
79732
- const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, providerLoader));
79783
+ const quotaClaimBlock = evaluateProviderQuotaGate(
79784
+ node,
79785
+ providerType,
79786
+ mesh?.policy?.quotaRouting ?? null,
79787
+ Date.now(),
79788
+ quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, providerLoader),
79789
+ { model }
79790
+ );
79733
79791
  if (quotaClaimTrace) quotaClaimTrace.evaluated += 1;
79734
79792
  if (quotaClaimBlock) {
79735
79793
  const observation = {
@@ -81584,7 +81642,7 @@ ${block2.text}`,
81584
81642
  noteClaimDeferredForNode(meshId, nodeId);
81585
81643
  return false;
81586
81644
  }
81587
- if (evaluateQuotaClaimGateForAssignment({ meshId, nodeId, sessionId, providerType, trigger, node, mesh, providerLoader: components.providerLoader, quotaClaimTrace })) {
81645
+ if (evaluateQuotaClaimGateForAssignment({ meshId, nodeId, sessionId, providerType, model: routingDecision?.selectedSlot?.model, trigger, node, mesh, providerLoader: components.providerLoader, quotaClaimTrace })) {
81588
81646
  return false;
81589
81647
  }
81590
81648
  const inlineBootstrapNode = (() => {
@@ -117192,6 +117250,7 @@ ${marker}`,
117192
117250
  var path38 = __toESM2(require("path"));
117193
117251
  init_terminal_screen();
117194
117252
  var import_session_host_core7 = require_dist();
117253
+ init_logger();
117195
117254
  var MAX_PTY_EVENTS = 300;
117196
117255
  var EVENT_CONTENT_CAP = 240;
117197
117256
  function escapeControl(text) {
@@ -117231,6 +117290,7 @@ ${marker}`,
117231
117290
  /** Debug-only ring buffer of PTY input/output/resize/cursor events. */
117232
117291
  events = [];
117233
117292
  lastCursorKey = "";
117293
+ spawnedAtMs = 0;
117234
117294
  start() {
117235
117295
  if (!this.factory) {
117236
117296
  const { NodePtyTransportFactory: NodePtyTransportFactory2 } = (init_pty_transport(), __toCommonJS2(pty_transport_exports));
@@ -117244,6 +117304,7 @@ ${marker}`,
117244
117304
  cols: this.cols,
117245
117305
  rows: this.rows
117246
117306
  });
117307
+ this.spawnedAtMs = Date.now();
117247
117308
  this.recordEvent("spawn", `${this.opts.binary} (${this.cols}x${this.rows})`);
117248
117309
  this.handlers.init?.({ pid: this.pty.pid });
117249
117310
  this.pty.onData((chunk) => this.onChunk(chunk));
@@ -117286,9 +117347,40 @@ ${marker}`,
117286
117347
  getScreenSize() {
117287
117348
  return { cols: this.cols, rows: this.rows };
117288
117349
  }
117289
- send_keys(text) {
117350
+ send_keys(text, primeContext) {
117290
117351
  this.recordEvent("input", capPreview(escapeControl(text)), text.length);
117291
- this.pty?.write(text);
117352
+ if (!primeContext) {
117353
+ this.pty?.write(text);
117354
+ return;
117355
+ }
117356
+ return this.writeInput(text, primeContext);
117357
+ }
117358
+ async writeInput(text, primeContext) {
117359
+ const bytes = Buffer.byteLength(text, "utf8");
117360
+ const source = primeContext.source;
117361
+ const prefix = `[${primeContext.specTag}] `;
117362
+ const afterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
117363
+ const pty = this.pty;
117364
+ if (!pty) {
117365
+ LOG2.warn("FsmDriver", `${prefix}PTY write skipped source=${source} bytes=${bytes} afterSpawnMs=${afterSpawnMs} outcome=no-active-pty`);
117366
+ return false;
117367
+ }
117368
+ LOG2.debug("FsmDriver", `${prefix}PTY write before source=${source} bytes=${bytes} afterSpawnMs=${afterSpawnMs}`);
117369
+ try {
117370
+ const result = await pty.write(text);
117371
+ const completedAfterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
117372
+ if (result === false) {
117373
+ LOG2.warn("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=false`);
117374
+ return false;
117375
+ }
117376
+ LOG2.debug("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=success`);
117377
+ return true;
117378
+ } catch (error48) {
117379
+ const completedAfterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
117380
+ const message = error48 instanceof Error ? error48.message : String(error48);
117381
+ LOG2.warn("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=error error=${message}`);
117382
+ return false;
117383
+ }
117292
117384
  }
117293
117385
  /** Forward runtime metadata (meshNodeId, workspaceLabel, lifecycle, …) to
117294
117386
  * the underlying transport so it reaches the session registry. The spec
@@ -117636,6 +117728,8 @@ ${marker}`,
117636
117728
  return n;
117637
117729
  }
117638
117730
  var SUBMIT_DELAY_FLOOR_MS = 200;
117731
+ var DEFAULT_SPAWN_PRIME_MAX_WAIT_MS = 2e3;
117732
+ var STALL_REFOCUS_INFO_LIMIT = 3;
117639
117733
  var SEND_IN_FLIGHT_MAX_MS = 3e4;
117640
117734
  var DUPLICATE_RESEND_WINDOW_MS = 6e4;
117641
117735
  function hashSendText(text) {
@@ -117694,6 +117788,7 @@ ${marker}`,
117694
117788
  init: () => this.emitInitialState(),
117695
117789
  on_pty_data: (chunk) => {
117696
117790
  this.lastPtyDataAt = Date.now();
117791
+ this.scheduleSpawnPrimeAfterFirstOutput();
117697
117792
  this.emit({ kind: "pty_data", chunk });
117698
117793
  },
117699
117794
  on_screen_changed: () => this.reevaluate(),
@@ -117735,10 +117830,21 @@ ${marker}`,
117735
117830
  * Re-arms itself while the machine is generating so a re-prime can fire
117736
117831
  * even when the PTY has gone completely quiet. */
117737
117832
  stallTimer = null;
117833
+ /** Spawn-prime delay timer. First PTY output is useful readiness evidence,
117834
+ * not proof that stdin is ready; a separate max-wait timer preserves the
117835
+ * old spawn-relative fallback when no output ever arrives. */
117836
+ spawnPrimeTimer = null;
117837
+ spawnPrimeMaxWaitTimer = null;
117838
+ spawnPrimeAwaitingOutput = false;
117839
+ /** Shared latch for the first-output and max-wait paths. Both timers call
117840
+ * fireSpawnPrimeOnce(), which consumes it before writing. */
117841
+ spawnPrimePending = false;
117738
117842
  /** Wall-clock time the last stall re-prime was injected. The cooldown gate:
117739
117843
  * after a re-prime we don't re-inject until the screen changes (which
117740
117844
  * resets the stall reference) or another full stall window lapses. */
117741
117845
  lastRefocusAt = 0;
117846
+ stallRefocusInfoCount = 0;
117847
+ stallRefocusSuppressedCount = 0;
117742
117848
  /** Timer driving the win32 verification-based submit resend loop (see
117743
117849
  * WIN32_SUBMIT_* and scheduleVerifiedSubmit). Re-arms itself until the FSM
117744
117850
  * leaves idle (submitted) or the resend budget is spent. */
@@ -117825,25 +117931,71 @@ ${marker}`,
117825
117931
  this.startupDismissConfig = normalizeStartupDismissConfig(this.spec.startup_dismiss);
117826
117932
  this.startupDismissState = createStartupDismissState();
117827
117933
  this.startupDismissSpawnAt = now;
117934
+ this.armSpawnPrime();
117828
117935
  this.adapter.start();
117829
- this.scheduleSpawnPrime();
117830
117936
  this.scheduleWakeForState();
117831
117937
  }
117832
- scheduleSpawnPrime() {
117938
+ armSpawnPrime() {
117833
117939
  const seqs = this.spec.send_on_spawn;
117834
117940
  if (!Array.isArray(seqs) || seqs.length === 0) return;
117941
+ this.spawnPrimeAwaitingOutput = true;
117942
+ this.spawnPrimePending = true;
117943
+ const configuredMaxWait = this.spec.send_on_spawn_max_wait_ms;
117944
+ const maxWait = typeof configuredMaxWait === "number" && Number.isFinite(configuredMaxWait) ? Math.max(0, configuredMaxWait) : DEFAULT_SPAWN_PRIME_MAX_WAIT_MS;
117945
+ this.spawnPrimeMaxWaitTimer = setTimeout(() => {
117946
+ this.spawnPrimeMaxWaitTimer = null;
117947
+ this.fireSpawnPrimeOnce("max-wait");
117948
+ }, maxWait);
117949
+ }
117950
+ /** Start the configured prime delay only after the child produces output.
117951
+ * On macOS, writing at a fixed spawn-relative deadline can land while the
117952
+ * slave is still in canonical echo mode: focus-in is echoed as `^[[I` and
117953
+ * lost before the TUI installs its input handler. First output is the
117954
+ * earliest transport-level readiness evidence available to the engine. */
117955
+ scheduleSpawnPrimeAfterFirstOutput() {
117956
+ if (!this.spawnPrimeAwaitingOutput) return;
117957
+ this.spawnPrimeAwaitingOutput = false;
117958
+ if (this.spawnPrimeMaxWaitTimer) {
117959
+ clearTimeout(this.spawnPrimeMaxWaitTimer);
117960
+ this.spawnPrimeMaxWaitTimer = null;
117961
+ }
117835
117962
  const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
117836
- setTimeout(() => this.sendSpawnPrime(), delay2);
117963
+ this.spawnPrimeTimer = setTimeout(() => {
117964
+ this.spawnPrimeTimer = null;
117965
+ this.fireSpawnPrimeOnce("first-output");
117966
+ }, delay2);
117967
+ }
117968
+ /** Consume the one-shot spawn-prime latch and cancel the competing timer
117969
+ * before writing. JavaScript callbacks are serialized, so whichever path
117970
+ * gets here first makes every later/queued callback a no-op. */
117971
+ fireSpawnPrimeOnce(trigger) {
117972
+ if (!this.spawnPrimePending) return;
117973
+ this.spawnPrimePending = false;
117974
+ this.spawnPrimeAwaitingOutput = false;
117975
+ if (this.spawnPrimeTimer) {
117976
+ clearTimeout(this.spawnPrimeTimer);
117977
+ this.spawnPrimeTimer = null;
117978
+ }
117979
+ if (this.spawnPrimeMaxWaitTimer) {
117980
+ clearTimeout(this.spawnPrimeMaxWaitTimer);
117981
+ this.spawnPrimeMaxWaitTimer = null;
117982
+ }
117983
+ this.sendSpawnPrime("spawn-prime", trigger);
117837
117984
  }
117838
117985
  /** Write the declared `send_on_spawn` sequences to the PTY once. Used both
117839
- * at spawn (scheduleSpawnPrime) and, for focus-gated TUIs, by the stall
117986
+ * on the first-output spawn path and, for focus-gated TUIs, by the stall
117840
117987
  * watchdog to re-inject the focus-in wake mid-turn. No-op when the spec
117841
117988
  * declares no prime, so non-focus-gated CLIs are never poked. */
117842
- sendSpawnPrime() {
117989
+ sendSpawnPrime(source, trigger) {
117843
117990
  const seqs = this.spec.send_on_spawn;
117844
117991
  if (!Array.isArray(seqs) || seqs.length === 0) return;
117845
- for (const seq2 of seqs) {
117846
- if (typeof seq2 === "string" && seq2.length > 0) this.adapter.send_keys(seq2);
117992
+ const validSeqs = seqs.filter((seq2) => typeof seq2 === "string" && seq2.length > 0);
117993
+ if (validSeqs.length === 0) return;
117994
+ if (source === "spawn-prime") {
117995
+ LOG2.info("FsmDriver", `[${this.specTag()}] spawn prime firing trigger=${trigger ?? "unknown"} sequences=${validSeqs.length}`);
117996
+ }
117997
+ for (const seq2 of validSeqs) {
117998
+ void this.adapter.send_keys(seq2, { source, specTag: this.specTag() });
117847
117999
  }
117848
118000
  }
117849
118001
  /** Boot-prompt dismissal (CliSpecV4.startup_dismiss). Runs on every
@@ -117950,6 +118102,17 @@ ${marker}`,
117950
118102
  clearTimeout(this.stallTimer);
117951
118103
  this.stallTimer = null;
117952
118104
  }
118105
+ if (this.spawnPrimeTimer) {
118106
+ clearTimeout(this.spawnPrimeTimer);
118107
+ this.spawnPrimeTimer = null;
118108
+ }
118109
+ if (this.spawnPrimeMaxWaitTimer) {
118110
+ clearTimeout(this.spawnPrimeMaxWaitTimer);
118111
+ this.spawnPrimeMaxWaitTimer = null;
118112
+ }
118113
+ this.spawnPrimeAwaitingOutput = false;
118114
+ this.spawnPrimePending = false;
118115
+ this.flushStallRefocusLogSummary();
117953
118116
  if (this.win32SubmitTimer) {
117954
118117
  clearTimeout(this.win32SubmitTimer);
117955
118118
  this.win32SubmitTimer = null;
@@ -118428,12 +118591,26 @@ ${marker}`,
118428
118591
  const stalledFor = now - this.lastScreenChangeAt();
118429
118592
  const sinceLastRefocus = now - this.lastRefocusAt;
118430
118593
  if (stalledFor >= windowMs && sinceLastRefocus >= windowMs) {
118431
- LOG2.info("FsmDriver", `[${this.specTag()}] stall detected (${stalledFor}ms quiet, generating) \u2014 re-injecting focus-in`);
118432
- this.sendSpawnPrime();
118594
+ if (this.stallRefocusInfoCount < STALL_REFOCUS_INFO_LIMIT) {
118595
+ this.stallRefocusInfoCount += 1;
118596
+ LOG2.info("FsmDriver", `[${this.specTag()}] stall detected (${stalledFor}ms quiet, generating) \u2014 re-injecting focus-in (${this.stallRefocusInfoCount}/${STALL_REFOCUS_INFO_LIMIT} detailed)`);
118597
+ } else {
118598
+ this.stallRefocusSuppressedCount += 1;
118599
+ }
118600
+ this.sendSpawnPrime("stall-refocus");
118433
118601
  this.lastRefocusAt = now;
118434
118602
  }
118435
118603
  this.scheduleStallWatchdog();
118436
118604
  }
118605
+ /** Emit one exact session summary, then zero the counter so recursive or
118606
+ * repeated shutdown calls cannot duplicate it. Write failures remain warn
118607
+ * per attempt in TerminalAdapter and are never suppressed here. */
118608
+ flushStallRefocusLogSummary() {
118609
+ const suppressed = this.stallRefocusSuppressedCount;
118610
+ if (suppressed === 0) return;
118611
+ this.stallRefocusSuppressedCount = 0;
118612
+ LOG2.info("FsmDriver", `[${this.specTag()}] stall refocus log summary: ${suppressed} later reinjection(s) suppressed after first ${STALL_REFOCUS_INFO_LIMIT}`);
118613
+ }
118437
118614
  maybeMarkReady() {
118438
118615
  if (this.readySeenOnce) return;
118439
118616
  const st = stateById(this.spec, this.currentStateId);
@@ -122929,21 +123106,64 @@ ${marker}`,
122929
123106
  }
122930
123107
  return attrs;
122931
123108
  }
123109
+ var TAIL_JSONL_CACHE_MAX_ENTRIES = 8;
123110
+ var TAIL_JSONL_CACHE_MAX_SOURCE_BYTES = 16 * 1024 * 1024;
123111
+ var tailJsonlCache = /* @__PURE__ */ new Map();
123112
+ var tailJsonlCacheSourceBytes = 0;
123113
+ var tailJsonlCacheStats = { hits: 0, misses: 0, fileReads: 0, parsePasses: 0 };
123114
+ function tailJsonlSignature(stat2) {
123115
+ return `${stat2.dev}:${stat2.ino}:${stat2.size}:${stat2.mtimeMs}:${stat2.ctimeMs}`;
123116
+ }
123117
+ function storeTailJsonlCache(p, entry) {
123118
+ const prior = tailJsonlCache.get(p);
123119
+ if (prior) tailJsonlCacheSourceBytes -= prior.sourceBytes;
123120
+ tailJsonlCache.delete(p);
123121
+ if (entry.sourceBytes > TAIL_JSONL_CACHE_MAX_SOURCE_BYTES) return;
123122
+ tailJsonlCache.set(p, entry);
123123
+ tailJsonlCacheSourceBytes += entry.sourceBytes;
123124
+ while (tailJsonlCache.size > TAIL_JSONL_CACHE_MAX_ENTRIES || tailJsonlCacheSourceBytes > TAIL_JSONL_CACHE_MAX_SOURCE_BYTES) {
123125
+ const oldestKey = tailJsonlCache.keys().next().value;
123126
+ if (oldestKey === void 0) break;
123127
+ const oldest = tailJsonlCache.get(oldestKey);
123128
+ if (oldest) tailJsonlCacheSourceBytes -= oldest.sourceBytes;
123129
+ tailJsonlCache.delete(oldestKey);
123130
+ }
123131
+ }
122932
123132
  function readTailJsonlLines(filePath, maxBytes) {
122933
- const stat2 = fs41.statSync(filePath);
123133
+ let stat2;
123134
+ try {
123135
+ stat2 = fs41.statSync(filePath);
123136
+ } catch {
123137
+ return [];
123138
+ }
123139
+ const signature = tailJsonlSignature(stat2);
123140
+ const cached5 = tailJsonlCache.get(filePath);
123141
+ if (cached5?.signature === signature && cached5.maxBytes === maxBytes) {
123142
+ tailJsonlCacheStats.hits++;
123143
+ tailJsonlCache.delete(filePath);
123144
+ tailJsonlCache.set(filePath, cached5);
123145
+ return cached5.lines;
123146
+ }
123147
+ tailJsonlCacheStats.misses++;
122934
123148
  const size = stat2.size;
122935
123149
  const start = size > maxBytes ? size - maxBytes : 0;
122936
123150
  const length = size - start;
122937
- if (length <= 0) return [];
122938
- const fd = fs41.openSync(filePath, "r");
123151
+ if (length <= 0) {
123152
+ const out2 = [];
123153
+ storeTailJsonlCache(filePath, { signature, maxBytes, sourceBytes: size, lines: out2 });
123154
+ return out2;
123155
+ }
122939
123156
  let text;
123157
+ const fd = fs41.openSync(filePath, "r");
122940
123158
  try {
122941
123159
  const buf = Buffer.alloc(length);
122942
123160
  const bytes = fs41.readSync(fd, buf, 0, length, start);
122943
123161
  text = buf.subarray(0, bytes).toString("utf8");
123162
+ tailJsonlCacheStats.fileReads++;
122944
123163
  } finally {
122945
123164
  fs41.closeSync(fd);
122946
123165
  }
123166
+ tailJsonlCacheStats.parsePasses++;
122947
123167
  const rawLines = text.split("\n");
122948
123168
  if (start > 0 && rawLines.length > 0) rawLines.shift();
122949
123169
  const out = [];
@@ -122955,6 +123175,7 @@ ${marker}`,
122955
123175
  } catch {
122956
123176
  }
122957
123177
  }
123178
+ storeTailJsonlCache(filePath, { signature, maxBytes, sourceBytes: size, lines: out });
122958
123179
  return out;
122959
123180
  }
122960
123181
  var USER_RE = /^>\s+(\S.*)$/;