@adhdev/daemon-standalone 0.9.82-rc.194 → 0.9.82-rc.196

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
@@ -38106,14 +38106,32 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38106
38106
  function legacyQueuePath(meshId) {
38107
38107
  return (0, import_path7.join)(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
38108
38108
  }
38109
+ function meshRuntimeStorePath() {
38110
+ const dir = getLedgerDir();
38111
+ const nextPath = (0, import_path7.join)(dir, "mesh-runtime.db");
38112
+ if ((0, import_fs7.existsSync)(nextPath)) return nextPath;
38113
+ const legacyPath = (0, import_path7.join)(dir, "beads.db");
38114
+ if (!(0, import_fs7.existsSync)(legacyPath)) return nextPath;
38115
+ try {
38116
+ (0, import_fs7.renameSync)(legacyPath, nextPath);
38117
+ for (const suffix of ["-wal", "-shm"]) {
38118
+ const legacyCompanion = `${legacyPath}${suffix}`;
38119
+ if ((0, import_fs7.existsSync)(legacyCompanion)) {
38120
+ (0, import_fs7.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
38121
+ }
38122
+ }
38123
+ } catch {
38124
+ }
38125
+ return nextPath;
38126
+ }
38109
38127
  var import_fs7;
38110
38128
  var import_path7;
38111
38129
  var import_module;
38112
38130
  var import_meta;
38113
38131
  var DatabaseCtor;
38114
- var BeadsDB;
38115
- var init_beads_db = __esm2({
38116
- "src/mesh/beads-db.ts"() {
38132
+ var MeshRuntimeStore;
38133
+ var init_mesh_runtime_store = __esm2({
38134
+ "src/mesh/mesh-runtime-store.ts"() {
38117
38135
  "use strict";
38118
38136
  import_fs7 = require("fs");
38119
38137
  import_path7 = require("path");
@@ -38121,7 +38139,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38121
38139
  init_mesh_ledger();
38122
38140
  init_mesh_work_queue();
38123
38141
  import_meta = {};
38124
- BeadsDB = class _BeadsDB {
38142
+ MeshRuntimeStore = class _MeshRuntimeStore {
38125
38143
  static instance;
38126
38144
  db;
38127
38145
  dbPath;
@@ -38144,7 +38162,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38144
38162
  }
38145
38163
  static getInstance() {
38146
38164
  if (!this.instance) {
38147
- this.instance = new _BeadsDB((0, import_path7.join)(getLedgerDir(), "beads.db"));
38165
+ this.instance = new _MeshRuntimeStore(meshRuntimeStorePath());
38148
38166
  }
38149
38167
  return this.instance;
38150
38168
  }
@@ -38229,13 +38247,13 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38229
38247
  this.db.prepare("DELETE FROM mesh_completion_fingerprints WHERE expires_at <= ?").run(Date.now());
38230
38248
  }
38231
38249
  maybeCheckpointWal() {
38232
- if (++this.walWriteCounter < _BeadsDB.WAL_CHECK_INTERVAL) return;
38250
+ if (++this.walWriteCounter < _MeshRuntimeStore.WAL_CHECK_INTERVAL) return;
38233
38251
  this.walWriteCounter = 0;
38234
38252
  try {
38235
38253
  const walPath = `${this.dbPath}-wal`;
38236
38254
  if (!(0, import_fs7.existsSync)(walPath)) return;
38237
38255
  const size = (0, import_fs7.statSync)(walPath).size;
38238
- if (size < _BeadsDB.WAL_MAX_BYTES) return;
38256
+ if (size < _MeshRuntimeStore.WAL_MAX_BYTES) return;
38239
38257
  process.stderr.write(
38240
38258
  `[adhdev-mesh] WAL file ${Math.round(size / 1024 / 1024)}MB exceeds threshold; forcing checkpoint
38241
38259
  `
@@ -38544,7 +38562,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38544
38562
  __clearDirectDispatchesForTests: () => __clearDirectDispatchesForTests,
38545
38563
  __clearMeshQueueForTests: () => __clearMeshQueueForTests,
38546
38564
  __replaceMeshQueueForTests: () => __replaceMeshQueueForTests,
38547
- __resetBeadsDBForTests: () => __resetBeadsDBForTests,
38565
+ __resetMeshRuntimeStoreForTests: () => __resetMeshRuntimeStoreForTests,
38548
38566
  buildMeshNodeCapabilityTags: () => buildMeshNodeCapabilityTags,
38549
38567
  cancelTask: () => cancelTask,
38550
38568
  claimNextTask: () => claimNextTask,
@@ -38622,7 +38640,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38622
38640
  return required2.every((tag) => available.has(tag));
38623
38641
  }
38624
38642
  function withQueueLock(_meshId, fn) {
38625
- return BeadsDB.getInstance().transaction(fn);
38643
+ return MeshRuntimeStore.getInstance().transaction(fn);
38626
38644
  }
38627
38645
  function enqueueTask(meshId, message, opts) {
38628
38646
  requireMeshHostQueueOwner(opts);
@@ -38642,55 +38660,55 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38642
38660
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
38643
38661
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
38644
38662
  };
38645
- BeadsDB.getInstance().insertQueueEntry(entry);
38663
+ MeshRuntimeStore.getInstance().insertQueueEntry(entry);
38646
38664
  return entry;
38647
38665
  }
38648
38666
  function getQueue(meshId, opts) {
38649
- return BeadsDB.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
38667
+ return MeshRuntimeStore.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
38650
38668
  }
38651
38669
  function getMeshQueueRevision(meshId) {
38652
- return BeadsDB.getInstance().getQueueRevision(meshId);
38670
+ return MeshRuntimeStore.getInstance().getQueueRevision(meshId);
38653
38671
  }
38654
38672
  function claimNextTask(meshId, nodeId, sessionId, capabilityTags) {
38655
- return BeadsDB.getInstance().claimNextQueueTask(meshId, nodeId, sessionId, capabilityTags);
38673
+ return MeshRuntimeStore.getInstance().claimNextQueueTask(meshId, nodeId, sessionId, capabilityTags);
38656
38674
  }
38657
38675
  function updateTaskStatus(meshId, taskId, status, opts) {
38658
38676
  requireMeshHostQueueOwner(opts);
38659
38677
  return withQueueLock(meshId, () => {
38660
- const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
38678
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
38661
38679
  if (!entry) return null;
38662
38680
  entry.status = status;
38663
- BeadsDB.getInstance().updateQueueEntry(entry);
38681
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
38664
38682
  return entry;
38665
38683
  });
38666
38684
  }
38667
38685
  function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
38668
38686
  return withQueueLock(meshId, () => {
38669
- const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
38687
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
38670
38688
  if (!entry) return null;
38671
38689
  const now = (/* @__PURE__ */ new Date()).toISOString();
38672
38690
  entry.autoLaunch = { ...autoLaunch, updatedAt: now };
38673
- BeadsDB.getInstance().updateQueueEntry(entry);
38691
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
38674
38692
  return entry;
38675
38693
  });
38676
38694
  }
38677
38695
  function cancelTask(meshId, taskId, opts) {
38678
38696
  requireMeshHostQueueOwner(opts);
38679
38697
  return withQueueLock(meshId, () => {
38680
- const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
38698
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
38681
38699
  if (!entry) return null;
38682
38700
  const now = (/* @__PURE__ */ new Date()).toISOString();
38683
38701
  entry.status = "cancelled";
38684
38702
  entry.cancelledAt = now;
38685
38703
  if (opts?.reason) entry.cancelReason = opts.reason;
38686
- BeadsDB.getInstance().updateQueueEntry(entry);
38704
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
38687
38705
  return entry;
38688
38706
  });
38689
38707
  }
38690
38708
  function requeueTask(meshId, taskId, opts) {
38691
38709
  requireMeshHostQueueOwner(opts);
38692
38710
  return withQueueLock(meshId, () => {
38693
- const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
38711
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
38694
38712
  if (!entry) return null;
38695
38713
  entry.status = "pending";
38696
38714
  delete entry.assignedNodeId;
@@ -38704,22 +38722,22 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38704
38722
  entry.requeuedAt = (/* @__PURE__ */ new Date()).toISOString();
38705
38723
  entry.requeueCount = (entry.requeueCount || 0) + 1;
38706
38724
  if (opts?.reason) entry.requeueReason = opts.reason;
38707
- BeadsDB.getInstance().updateQueueEntry(entry);
38725
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
38708
38726
  return entry;
38709
38727
  });
38710
38728
  }
38711
38729
  function updateSessionTaskStatus(meshId, sessionId, status, opts) {
38712
38730
  return withQueueLock(meshId, () => {
38713
38731
  const occurredAtIso = opts?.occurredAt ? new Date(opts.occurredAt).toISOString() : void 0;
38714
- const entry = BeadsDB.getInstance().findAssignedBySession(meshId, sessionId, occurredAtIso);
38732
+ const entry = MeshRuntimeStore.getInstance().findAssignedBySession(meshId, sessionId, occurredAtIso);
38715
38733
  if (!entry) return null;
38716
38734
  entry.status = status;
38717
- BeadsDB.getInstance().updateQueueEntry(entry);
38735
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
38718
38736
  return entry;
38719
38737
  });
38720
38738
  }
38721
38739
  function getMeshQueueStats(meshId) {
38722
- const rows = BeadsDB.getInstance().getQueueStatsByStatus(meshId);
38740
+ const rows = MeshRuntimeStore.getInstance().getQueueStatsByStatus(meshId);
38723
38741
  const counts = {};
38724
38742
  for (const r of rows) counts[r.status] = r.count;
38725
38743
  const pending = counts["pending"] ?? 0;
@@ -38738,26 +38756,26 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38738
38756
  cancelled,
38739
38757
  activeCounts: { pending, assigned },
38740
38758
  historicalCounts: { completed, failed, cancelled },
38741
- activeAssignments: BeadsDB.getInstance().getActiveAssignmentDetails(meshId)
38759
+ activeAssignments: MeshRuntimeStore.getInstance().getActiveAssignmentDetails(meshId)
38742
38760
  };
38743
38761
  }
38744
38762
  function __replaceMeshQueueForTests(meshId, queue) {
38745
- BeadsDB.getInstance().transaction(() => {
38746
- BeadsDB.getInstance().replaceQueue(meshId, queue);
38763
+ MeshRuntimeStore.getInstance().transaction(() => {
38764
+ MeshRuntimeStore.getInstance().replaceQueue(meshId, queue);
38747
38765
  });
38748
38766
  }
38749
38767
  function __clearMeshQueueForTests(meshId) {
38750
- BeadsDB.getInstance().deleteQueue(meshId);
38768
+ MeshRuntimeStore.getInstance().deleteQueue(meshId);
38751
38769
  }
38752
38770
  function __clearDirectDispatchesForTests(meshId) {
38753
- BeadsDB.getInstance().deleteDirectDispatches(meshId);
38771
+ MeshRuntimeStore.getInstance().deleteDirectDispatches(meshId);
38754
38772
  }
38755
- function __resetBeadsDBForTests() {
38756
- BeadsDB.resetForTests();
38773
+ function __resetMeshRuntimeStoreForTests() {
38774
+ MeshRuntimeStore.resetForTests();
38757
38775
  }
38758
38776
  function insertDirectDispatch(meshId, data) {
38759
38777
  try {
38760
- BeadsDB.getInstance().insertDirectDispatch({ ...data, meshId });
38778
+ MeshRuntimeStore.getInstance().insertDirectDispatch({ ...data, meshId });
38761
38779
  } catch (e) {
38762
38780
  process.stderr.write(`[adhdev-mesh] insertDirectDispatch failed for task ${data.taskId}: ${e?.message || e}
38763
38781
  `);
@@ -38765,26 +38783,26 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38765
38783
  }
38766
38784
  function getActiveDirectDispatches(meshId) {
38767
38785
  try {
38768
- return BeadsDB.getInstance().getActiveDirectDispatches(meshId);
38786
+ return MeshRuntimeStore.getInstance().getActiveDirectDispatches(meshId);
38769
38787
  } catch {
38770
38788
  return [];
38771
38789
  }
38772
38790
  }
38773
38791
  function updateDirectDispatchStatus(meshId, sessionId, status) {
38774
38792
  try {
38775
- BeadsDB.getInstance().updateDirectDispatchStatus(meshId, sessionId, status);
38793
+ MeshRuntimeStore.getInstance().updateDirectDispatchStatus(meshId, sessionId, status);
38776
38794
  } catch {
38777
38795
  }
38778
38796
  }
38779
38797
  function cleanupTerminalDirectDispatches(olderThanMs = 7 * 24 * 60 * 6e4) {
38780
38798
  try {
38781
- BeadsDB.getInstance().cleanupTerminalDirectDispatches(olderThanMs);
38799
+ MeshRuntimeStore.getInstance().cleanupTerminalDirectDispatches(olderThanMs);
38782
38800
  } catch {
38783
38801
  }
38784
38802
  }
38785
38803
  function markStaleDirectDispatches(meshId, olderThanMs = 60 * 6e4) {
38786
38804
  try {
38787
- BeadsDB.getInstance().markStaleDirectDispatches(meshId, olderThanMs);
38805
+ MeshRuntimeStore.getInstance().markStaleDirectDispatches(meshId, olderThanMs);
38788
38806
  } catch {
38789
38807
  }
38790
38808
  }
@@ -38798,7 +38816,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38798
38816
  "use strict";
38799
38817
  import_crypto5 = require("crypto");
38800
38818
  init_mesh_host_ownership();
38801
- init_beads_db();
38819
+ init_mesh_runtime_store();
38802
38820
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
38803
38821
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
38804
38822
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -38978,7 +38996,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38978
38996
  }
38979
38997
  function sweepExpiredRemoteIdleSessions() {
38980
38998
  try {
38981
- BeadsDB.getInstance().pruneExpiredRemoteIdleSessions();
38999
+ MeshRuntimeStore.getInstance().pruneExpiredRemoteIdleSessions();
38982
39000
  } catch {
38983
39001
  }
38984
39002
  }
@@ -39254,14 +39272,14 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
39254
39272
  }
39255
39273
  function hasFingerprintSeen(fingerprint) {
39256
39274
  try {
39257
- return BeadsDB.getInstance().hasCompletionFingerprint(fingerprint);
39275
+ return MeshRuntimeStore.getInstance().hasCompletionFingerprint(fingerprint);
39258
39276
  } catch {
39259
39277
  return false;
39260
39278
  }
39261
39279
  }
39262
39280
  function recordFingerprintSeen(fingerprint) {
39263
39281
  try {
39264
- const db = BeadsDB.getInstance();
39282
+ const db = MeshRuntimeStore.getInstance();
39265
39283
  db.recordCompletionFingerprint(fingerprint, RECENT_COMPLETION_FINGERPRINT_TTL_MS);
39266
39284
  db.sweepExpiredFingerprints();
39267
39285
  } catch {
@@ -39881,7 +39899,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
39881
39899
  }
39882
39900
  let remoteSessions = [];
39883
39901
  try {
39884
- remoteSessions = BeadsDB.getInstance().getRemoteIdleSessions();
39902
+ remoteSessions = MeshRuntimeStore.getInstance().getRemoteIdleSessions();
39885
39903
  } catch {
39886
39904
  }
39887
39905
  for (const idle of remoteSessions) {
@@ -39891,7 +39909,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
39891
39909
  const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
39892
39910
  if (assigned) {
39893
39911
  try {
39894
- BeadsDB.getInstance().deleteRemoteIdleSession(idle.nodeId, idle.sessionId);
39912
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(idle.nodeId, idle.sessionId);
39895
39913
  } catch {
39896
39914
  }
39897
39915
  }
@@ -40093,7 +40111,7 @@ Next step: ${nextStep}`;
40093
40111
  if (intentionalCleanupStop) {
40094
40112
  if (eventSessionId && eventNodeId) {
40095
40113
  try {
40096
- BeadsDB.getInstance().deleteRemoteIdleSession(eventNodeId, eventSessionId);
40114
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(eventNodeId, eventSessionId);
40097
40115
  } catch {
40098
40116
  }
40099
40117
  }
@@ -40261,14 +40279,14 @@ Next step: ${nextStep}`;
40261
40279
  if (sessionId && nodeId && providerType) {
40262
40280
  sweepExpiredRemoteIdleSessions();
40263
40281
  try {
40264
- BeadsDB.getInstance().setRemoteIdleSession(nodeId, sessionId, providerType, Date.now() + REMOTE_IDLE_SESSION_TTL_MS);
40282
+ MeshRuntimeStore.getInstance().setRemoteIdleSession(nodeId, sessionId, providerType, Date.now() + REMOTE_IDLE_SESSION_TTL_MS);
40265
40283
  } catch {
40266
40284
  }
40267
40285
  setImmediate(() => {
40268
40286
  maybeAutoFastForwardIdleNode(components, { meshId: args.meshId, nodeId, sessionId, providerType }).finally(() => {
40269
40287
  try {
40270
40288
  const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
40271
- if (assigned) BeadsDB.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40289
+ if (assigned) MeshRuntimeStore.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40272
40290
  } catch (e) {
40273
40291
  LOG2.warn("MeshQueue", `Failed to assign idle queue task after maintenance for ${nodeId}: ${e?.message || e}`);
40274
40292
  }
@@ -40280,7 +40298,7 @@ Next step: ${nextStep}`;
40280
40298
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
40281
40299
  if (sessionId && nodeId) {
40282
40300
  try {
40283
- BeadsDB.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40301
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40284
40302
  } catch {
40285
40303
  }
40286
40304
  }
@@ -40292,7 +40310,7 @@ Next step: ${nextStep}`;
40292
40310
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
40293
40311
  if (sessionId && nodeId) {
40294
40312
  try {
40295
- BeadsDB.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40313
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(nodeId, sessionId);
40296
40314
  } catch {
40297
40315
  }
40298
40316
  }
@@ -40582,7 +40600,7 @@ Next step: ${nextStep}`;
40582
40600
  init_logger();
40583
40601
  init_mesh_ledger();
40584
40602
  init_mesh_work_queue();
40585
- init_beads_db();
40603
+ init_mesh_runtime_store();
40586
40604
  init_mesh_fast_forward();
40587
40605
  REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
40588
40606
  meshByWorkspaceCache = /* @__PURE__ */ new Map();
@@ -43527,11 +43545,12 @@ ${cont}` : cont;
43527
43545
  }
43528
43546
  applyGenerating(ctx) {
43529
43547
  const { modal, parsedMessages, lastParsedAssistant, parsedStatus, prevStatus } = ctx;
43548
+ const noActiveTurn = !this.currentTurnScope;
43549
+ if (!this.isWaitingForResponse && noActiveTurn && !modal) return;
43530
43550
  this.clearIdleFinishCandidate("generating");
43531
43551
  this.cancelPendingIdleFinish("generating_signal_returned");
43532
43552
  const snap = this.transport.getSnapshot();
43533
43553
  const effectiveScreenText = snap.screenText || snap.accumulatedBuffer;
43534
- const noActiveTurn = !this.currentTurnScope;
43535
43554
  const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText);
43536
43555
  const parsedShowsLiveProgress = parsedStatus === "generating" && !!lastParsedAssistant;
43537
43556
  if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveProgress) return;
@@ -43791,7 +43810,25 @@ ${cont}` : cont;
43791
43810
  const parsedStatus = typeof parsed?.status === "string" ? parsed.status.trim() : "";
43792
43811
  if (parsedStatus !== "idle") return true;
43793
43812
  if (parsed?.activeModal || parsed?.modal) return true;
43794
- return !this.parsedStatusHasFinalStandardAssistantMessage(parsed);
43813
+ const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
43814
+ let lastUserIdx = -1;
43815
+ for (let i = messages.length - 1; i >= 0; i--) {
43816
+ if (messages[i]?.role === "user") {
43817
+ lastUserIdx = i;
43818
+ break;
43819
+ }
43820
+ }
43821
+ if (lastUserIdx < 0) {
43822
+ if (messages.length === 0) return false;
43823
+ return !this.parsedStatusHasFinalStandardAssistantMessage(parsed);
43824
+ }
43825
+ const hasCurrentTurnAssistant = messages.slice(lastUserIdx + 1).some((m) => {
43826
+ if (!m || m.role !== "assistant") return false;
43827
+ if (typeof m.content !== "string" || !m.content.trim()) return false;
43828
+ const kind = typeof m.kind === "string" && m.kind.trim() ? m.kind.trim() : "standard";
43829
+ return kind === "standard" && m.meta?.streaming !== true;
43830
+ });
43831
+ return !hasCurrentTurnAssistant;
43795
43832
  }
43796
43833
  rescheduleTranscriptFinishCheck(reason) {
43797
43834
  this.clearIdleFinishCandidate(reason);
@@ -46567,14 +46604,19 @@ ${lastSnapshot}`;
46567
46604
  if (question.multiSelect) throw new Error("Claude TUI multi-select prompts are not supported yet");
46568
46605
  const answer = response.answers[question.questionId];
46569
46606
  if (!answer) throw new Error(`Missing answer for ${question.questionId}`);
46570
- if (answer.freeformText) throw new Error("Claude TUI freeform answers are not supported yet");
46571
- if (answer.selectedLabels.length !== 1) throw new Error(`Expected one selected label for ${question.questionId}`);
46572
- const selectedIndex = question.options.findIndex((option) => option.label === answer.selectedLabels[0]);
46573
- if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${answer.selectedLabels[0]}`);
46574
- for (let i = 0; i < selectedIndex; i += 1) {
46575
- steps.push("\x1B[B");
46607
+ const freeformText = answer.freeformText?.trim() ?? "";
46608
+ if (freeformText) {
46609
+ const typeOptionIndex = question.options.findIndex((o) => /^Type something\.?$/i.test(o.label));
46610
+ const optionNumber = typeOptionIndex >= 0 ? typeOptionIndex + 1 : question.options.length;
46611
+ steps.push(String(optionNumber));
46612
+ for (const ch of freeformText) steps.push(ch);
46613
+ steps.push("\r");
46614
+ } else {
46615
+ if (answer.selectedLabels.length !== 1) throw new Error(`Expected one selected label for ${question.questionId}`);
46616
+ const selectedIndex = question.options.findIndex((option) => option.label === answer.selectedLabels[0]);
46617
+ if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${answer.selectedLabels[0]}`);
46618
+ steps.push(String(selectedIndex + 1));
46576
46619
  }
46577
- steps.push("\r");
46578
46620
  }
46579
46621
  steps.push("\r");
46580
46622
  return steps;
@@ -54797,6 +54839,7 @@ ${effect.notification.body || ""}`.trim();
54797
54839
  if (hasApprovalButtons(opts?.activeModal)) return "waiting_approval";
54798
54840
  const normalized = String(status || "idle").trim().toLowerCase();
54799
54841
  if (normalized === "waiting_approval") return "waiting_approval";
54842
+ if (normalized === "waiting_choice") return "waiting_choice";
54800
54843
  if (WORKING_STATUSES.has(normalized)) return "generating";
54801
54844
  if (normalized === "error") return "error";
54802
54845
  if (normalized === "stopped") return "stopped";
@@ -60765,10 +60808,47 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60765
60808
  for (const sec of spec.layout.sections) {
60766
60809
  let from = 0;
60767
60810
  let to = total;
60768
- if (sec.from_top !== void 0) {
60811
+ if (sec.anchor_regex !== void 0) {
60812
+ try {
60813
+ const re = new RegExp(sec.anchor_regex, sec.anchor_flags ?? "");
60814
+ const prevRe = sec.anchor_context?.prev !== void 0 ? new RegExp(sec.anchor_context.prev, sec.anchor_context.prev_flags ?? "") : null;
60815
+ const nextRe = sec.anchor_context?.next !== void 0 ? new RegExp(sec.anchor_context.next, sec.anchor_context.next_flags ?? "") : null;
60816
+ const matches = (i) => re.test(lines[i]) && (prevRe === null || i > 0 && prevRe.test(lines[i - 1])) && (nextRe === null || i < total - 1 && nextRe.test(lines[i + 1]));
60817
+ let idx = -1;
60818
+ if (sec.anchor_last) {
60819
+ for (let i = total - 1; i >= 0; i--) {
60820
+ if (matches(i)) {
60821
+ idx = i;
60822
+ break;
60823
+ }
60824
+ }
60825
+ } else {
60826
+ for (let i = 0; i < total; i++) {
60827
+ if (matches(i)) {
60828
+ idx = i;
60829
+ break;
60830
+ }
60831
+ }
60832
+ }
60833
+ if (idx !== -1) {
60834
+ from = idx;
60835
+ to = total;
60836
+ if (sec.until_regex !== void 0) {
60837
+ try {
60838
+ const ure = new RegExp(sec.until_regex, sec.until_regex_flags ?? "");
60839
+ const end = lines.findIndex((l, i) => i > idx && ure.test(l));
60840
+ if (end !== -1) to = end;
60841
+ } catch {
60842
+ }
60843
+ } else if (sec.lines !== void 0) {
60844
+ to = Math.min(total, from + sec.lines);
60845
+ }
60846
+ }
60847
+ } catch {
60848
+ }
60849
+ } else if (sec.from_top !== void 0) {
60769
60850
  from = resolveSize(sec.from_top, total);
60770
- }
60771
- if (sec.from_bottom !== void 0) {
60851
+ } else if (sec.from_bottom !== void 0) {
60772
60852
  const sz = resolveSize(sec.from_bottom, total);
60773
60853
  from = total - sz;
60774
60854
  to = total;
@@ -61082,6 +61162,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61082
61162
  "type": "string",
61083
61163
  "minLength": 1
61084
61164
  },
61165
+ "requiresFinalAssistantBeforeIdle": {
61166
+ "type": "boolean"
61167
+ },
61085
61168
  "debounce": {
61086
61169
  "type": "object",
61087
61170
  "additionalProperties": false,
@@ -61096,7 +61179,8 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61096
61179
  "section": { "type": "string", "minLength": 1 },
61097
61180
  "regex": { "type": "string", "minLength": 1 },
61098
61181
  "flags": { "type": "string" },
61099
- "hold_ms": { "type": "integer", "minimum": 0 }
61182
+ "hold_ms": { "type": "integer", "minimum": 0 },
61183
+ "force_after_ms": { "type": "integer", "minimum": 0 }
61100
61184
  }
61101
61185
  }
61102
61186
  }
@@ -61144,7 +61228,23 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61144
61228
  "type": "string"
61145
61229
  }
61146
61230
  }
61147
- }
61231
+ },
61232
+ "anchor_regex": { "type": "string", "minLength": 1 },
61233
+ "anchor_flags": { "type": "string" },
61234
+ "anchor_last": { "type": "boolean" },
61235
+ "anchor_context": {
61236
+ "type": "object",
61237
+ "additionalProperties": false,
61238
+ "properties": {
61239
+ "prev": { "type": "string" },
61240
+ "prev_flags": { "type": "string" },
61241
+ "next": { "type": "string" },
61242
+ "next_flags": { "type": "string" }
61243
+ }
61244
+ },
61245
+ "lines": { "type": "integer", "minimum": 1 },
61246
+ "until_regex": { "type": "string", "minLength": 1 },
61247
+ "until_regex_flags": { "type": "string" }
61148
61248
  }
61149
61249
  },
61150
61250
  "sectionRegex": {
@@ -61576,6 +61676,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61576
61676
  function resolveSpecPath(providerDir) {
61577
61677
  return path18.join(providerDir, "spec.json");
61578
61678
  }
61679
+ init_logger();
61579
61680
  var STARTUP_GRACE_MS = 2500;
61580
61681
  var BUSY_HOLD_MS = 6e3;
61581
61682
  var SUBMIT_DELAY_FLOOR_MS = 200;
@@ -61603,9 +61704,16 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61603
61704
  return null;
61604
61705
  }
61605
61706
  }
61606
- function matchesCompletionIdleTargetState(spec, ev, screen) {
61707
+ function matchesCompletionIdleTargetState(spec, ev, screen, cursor) {
61607
61708
  const target = spec.states.find((state) => state.id === spec.default_state) ?? spec.states.find((state) => state.id === "idle");
61608
- if (!target?.when?.regex) return false;
61709
+ if (!target?.when) return false;
61710
+ const hasCursorGuard = target.when.cursor_row_min !== void 0 || target.when.cursor_row_max !== void 0 || target.when.cursor_col_min !== void 0 || target.when.cursor_col_max !== void 0;
61711
+ if (hasCursorGuard && cursor !== void 0) {
61712
+ const { cursor_row_min, cursor_row_max, cursor_col_min, cursor_col_max } = target.when;
61713
+ const cursorOk = (cursor_row_min === void 0 || cursor.row >= cursor_row_min) && (cursor_row_max === void 0 || cursor.row <= cursor_row_max) && (cursor_col_min === void 0 || cursor.col >= cursor_col_min) && (cursor_col_max === void 0 || cursor.col <= cursor_col_max);
61714
+ if (cursorOk) return true;
61715
+ }
61716
+ if (!target.when.regex) return false;
61609
61717
  const haystack = target.when.section ? ev.sections.find((section) => section.id === target.when.section)?.text ?? "" : screen;
61610
61718
  if (!haystack) return false;
61611
61719
  try {
@@ -61767,6 +61875,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61767
61875
  if (this.busyExpiryTimer) clearTimeout(this.busyExpiryTimer);
61768
61876
  this.busyExpiryTimer = setTimeout(() => {
61769
61877
  this.busyExpiryTimer = null;
61878
+ LOG2.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] busyExpiry fired holdMs=${holdMs}`);
61770
61879
  this.reevaluate();
61771
61880
  }, Math.max(holdMs + 50, 100));
61772
61881
  }
@@ -61791,11 +61900,16 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61791
61900
  if (completionKey !== this.completionIdleKey) {
61792
61901
  this.completionIdleKey = completionKey;
61793
61902
  this.completionIdleFirstSeenAt = now;
61903
+ LOG2.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] completion_idle_after matched: key="${completionKey}"`);
61794
61904
  }
61795
61905
  const holdMs = Math.max(0, completionIdleRule.hold_ms || 0);
61906
+ const forceAfterMs = typeof completionIdleRule.force_after_ms === "number" ? completionIdleRule.force_after_ms : null;
61796
61907
  const ageMs = now - this.completionIdleFirstSeenAt;
61797
61908
  if (ageMs >= holdMs) {
61798
- if (matchesCompletionIdleTargetState(this.spec, ev, screen)) {
61909
+ const targetMatches = matchesCompletionIdleTargetState(this.spec, ev, screen, cursor);
61910
+ const forced = !targetMatches && forceAfterMs !== null && ageMs >= holdMs + forceAfterMs;
61911
+ LOG2.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] completion_idle_after hold expired ageMs=${ageMs} targetState=${targetMatches} forced=${forced} screenTail="${screen.split(/\r?\n/).slice(-3).join("\\n").slice(-200)}"`);
61912
+ if (targetMatches || forced) {
61799
61913
  const idle = this.spec.states.find((state) => state.id === this.spec.default_state) ?? this.spec.states.find((state) => state.id === "idle");
61800
61914
  evState = idle ? { id: idle.id, label: idle.label, title: null } : { id: "idle", label: "Ready", title: null };
61801
61915
  } else {
@@ -62926,16 +63040,19 @@ ${formatManifestValidationIssues2(validation.issues)}`,
62926
63040
  controlValues: this.controlValues
62927
63041
  });
62928
63042
  const activeChatStatus = parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : suppressStaleParsedBusyStatus ? visibleStatus : parsedChatStatus || visibleStatus;
63043
+ const hasInteractivePrompt = !!this.activeInteractivePrompt;
63044
+ const finalStatus = hasInteractivePrompt ? "waiting_choice" : visibleStatus;
63045
+ const finalChatStatus = hasInteractivePrompt ? "waiting_choice" : activeChatStatus;
62929
63046
  return {
62930
63047
  type: this.type,
62931
63048
  name: this.provider.name,
62932
63049
  category: "cli",
62933
- status: visibleStatus,
63050
+ status: finalStatus,
62934
63051
  mode: this.presentationMode,
62935
63052
  activeChat: {
62936
63053
  id: activeChatId,
62937
63054
  title: parsedStatus?.title || dirName,
62938
- status: activeChatStatus,
63055
+ status: finalChatStatus,
62939
63056
  messages: statusMessages,
62940
63057
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
62941
63058
  activeInteractivePrompt: this.activeInteractivePrompt,
@@ -63406,6 +63523,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63406
63523
  const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
63407
63524
  const finalAssistantEvidence = this.completionFinalAssistantEvidence(parsed?.messages);
63408
63525
  const allowMissingAssistantTimeout = !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.launchedByCoordinator);
63526
+ LOG2.debug("CLI", `[${this.type}] finalAssistantEvidence: present=${finalAssistantEvidence.present} source=${finalAssistantEvidence.source} adapterOwnsMessagesElsewhere=${adapterOwnsMessagesElsewhere} parsedStatus=${parsedStatus}`);
63409
63527
  if (!finalAssistantEvidence.present) {
63410
63528
  if (adapterOwnsMessagesElsewhere) {
63411
63529
  if (finalAssistantEvidence.source === "external-native") {
@@ -63414,6 +63532,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63414
63532
  LOG2.info("CLI", `[${this.type}] external transcript probe: msgCount=${probe.msgCount} lastRole=${probe.lastRole || "none"} lastKind=${probe.lastKind || "none"} contentLen=${probe.contentLen} sourceMtime=${probe.sourceMtimeMs ?? "unknown"} mtimeAge=${probe.mtimeAgeMs ?? "unknown"}ms`);
63415
63533
  pending.loggedTranscriptProbe = true;
63416
63534
  }
63535
+ LOG2.debug("CLI", `[${this.type}] external-native probe result: lastRole=${probe?.lastRole} contentLen=${probe?.contentLen}`);
63536
+ if (probe?.lastRole === "assistant" && (probe.contentLen ?? 0) > 0) {
63537
+ return null;
63538
+ }
63417
63539
  if (this.type === "antigravity-cli") {
63418
63540
  return null;
63419
63541
  }
@@ -63423,6 +63545,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63423
63545
  return { reason: "missing_final_assistant", terminal: true, allowTimeout: allowMissingAssistantTimeout };
63424
63546
  }
63425
63547
  } else {
63548
+ LOG2.debug("CLI", `[${this.type}] missing_final_assistant (not ownsExternal) requiresFinalAssistant=${!!this.provider.requiresFinalAssistantBeforeIdle}`);
63426
63549
  return {
63427
63550
  reason: "missing_final_assistant",
63428
63551
  terminal: this.provider.requiresFinalAssistantBeforeIdle === true,
@@ -63456,6 +63579,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63456
63579
  const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
63457
63580
  const externalNativeFinal = this.getExternalNativeFinalReconciliation(void 0, latestStatus);
63458
63581
  const latestVisibleStatus = externalNativeFinal && isCliGeneratingLikeStatus(latestStatus.status) ? "idle" : latestAutoApproveActive ? "generating" : latestStatus.status;
63582
+ LOG2.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} externalNativeFinal=${!!externalNativeFinal} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
63459
63583
  if (latestVisibleStatus !== "idle") {
63460
63584
  LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
63461
63585
  this.completedDebouncePending = null;
@@ -63466,6 +63590,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63466
63590
  if (block2) {
63467
63591
  const blockReason = block2.reason;
63468
63592
  const waitedMs = Date.now() - pending.firstObservedAt;
63593
+ LOG2.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
63469
63594
  if (block2.terminal && !block2.allowTimeout || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
63470
63595
  if (pending.loggedBlockReason !== blockReason) {
63471
63596
  LOG2.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
@@ -63580,9 +63705,13 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63580
63705
  if (newStatus !== this.lastStatus) {
63581
63706
  LOG2.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
63582
63707
  if (this.lastStatus === "idle" && newStatus === "generating") {
63708
+ if (this.completedDebouncePending && this.generatingStartedAt === 0) {
63709
+ LOG2.debug("CLI", `[${this.type}] ignoring post-completion PTY generating blip (generatingStartedAt=0)`);
63710
+ return;
63711
+ }
63583
63712
  this.suppressIdleHistoryReplay = false;
63584
63713
  if (this.completedDebouncePending) {
63585
- LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
63714
+ LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed generating) generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse}`);
63586
63715
  if (this.completedDebounceTimer) {
63587
63716
  clearTimeout(this.completedDebounceTimer);
63588
63717
  this.completedDebounceTimer = null;
@@ -63680,7 +63809,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63680
63809
  firstObservedAt: now,
63681
63810
  previousStatus: this.lastStatus
63682
63811
  };
63683
- this.scheduleCompletedDebounceFlush(3e3);
63812
+ const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
63813
+ const flushDelay = ownsExternalHistory ? 0 : 3e3;
63814
+ LOG2.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration3}s ownsExternalHistory=${ownsExternalHistory} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
63815
+ this.scheduleCompletedDebounceFlush(flushDelay);
63684
63816
  }
63685
63817
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
63686
63818
  this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });