@adhdev/daemon-core 0.9.82-rc.354 → 0.9.82-rc.356

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
@@ -316,10 +316,10 @@ function readInjected(value) {
316
316
  }
317
317
  function getDaemonBuildInfo() {
318
318
  if (cached) return cached;
319
- const commit = readInjected(true ? "5d100a177f412ae29a58048f0a211c3928b06910" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "5d100a17" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.354" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-22T11:32:05.389Z" : void 0);
319
+ const commit = readInjected(true ? "91500e054cf2b6258f6041f90c18be03ad05a8ad" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "91500e05" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.356" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-22T22:47:29.082Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -4827,9 +4827,12 @@ function getActiveDirectDispatches(meshId) {
4827
4827
  return [];
4828
4828
  }
4829
4829
  }
4830
- function updateDirectDispatchStatus(meshId, sessionId, status) {
4830
+ function updateDirectDispatchStatus(meshId, sessionId, status, taskId) {
4831
4831
  try {
4832
- MeshRuntimeStore.getInstance().updateDirectDispatchStatus(meshId, sessionId, status);
4832
+ if (!taskId) {
4833
+ LOG.warn("MeshQueue", `updateDirectDispatchStatus(${status}) for mesh ${meshId} session ${sessionId} has no taskId \u2014 falling back to session_id match (may flip a sibling dispatch row)`);
4834
+ }
4835
+ MeshRuntimeStore.getInstance().updateDirectDispatchStatus(meshId, sessionId, status, taskId);
4833
4836
  } catch {
4834
4837
  }
4835
4838
  }
@@ -5662,9 +5665,25 @@ var init_mesh_runtime_store = __esm({
5662
5665
  updatedAt: r.updated_at
5663
5666
  }));
5664
5667
  }
5665
- updateDirectDispatchStatus(meshId, sessionId, status) {
5666
- if (!sessionId) return;
5668
+ // CANON-B (dispatch identity): mesh_direct_dispatches is keyed by task_id (PK), but a
5669
+ // single session can host several sequential direct dispatches (re-dispatch / nudge), so
5670
+ // matching a status flip by session_id alone hits EVERY non-terminal row for that session
5671
+ // — flipping a sibling task's row and stranding the one whose event actually fired (the
5672
+ // assigned-stranded watchdog then requeues a task that is really still generating). When
5673
+ // the firing event carries a taskId, target the single PK row; the session_id match is the
5674
+ // legacy fallback only for events that arrive without a taskId.
5675
+ updateDirectDispatchStatus(meshId, sessionId, status, taskId) {
5667
5676
  const now = (/* @__PURE__ */ new Date()).toISOString();
5677
+ if (taskId) {
5678
+ this.db.prepare(`
5679
+ UPDATE mesh_direct_dispatches
5680
+ SET status = @status, updated_at = @updatedAt
5681
+ WHERE mesh_id = @meshId AND task_id = @taskId
5682
+ AND status NOT IN ('completed', 'failed')
5683
+ `).run({ status, meshId, taskId, updatedAt: now });
5684
+ return;
5685
+ }
5686
+ if (!sessionId) return;
5668
5687
  this.db.prepare(`
5669
5688
  UPDATE mesh_direct_dispatches
5670
5689
  SET status = @status, updated_at = @updatedAt
@@ -7390,7 +7409,7 @@ function resolveWin32Executable(command) {
7390
7409
  windowsHide: true
7391
7410
  }).trim();
7392
7411
  if (out) {
7393
- const matches = out.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
7412
+ const matches = out.split(/\r?\n/).map((s2) => s2.trim()).filter(Boolean);
7394
7413
  const direct = matches.find((m) => DIRECT_EXEC_EXT.has(path10.extname(m).toLowerCase()));
7395
7414
  return direct || matches[0] || command;
7396
7415
  }
@@ -8742,11 +8761,29 @@ function hasPendingRefineTerminalEventDuplicate(event) {
8742
8761
  (pending) => pending.event === event.event && readRefineJobId2(pending) === jobId
8743
8762
  );
8744
8763
  }
8764
+ function isWeakCompletionMetadata(metadata) {
8765
+ const evidenceLevel = readNonEmptyString2(metadata.evidenceLevel);
8766
+ if (evidenceLevel === "insufficient" || evidenceLevel === "weak") return true;
8767
+ if (metadata.reviewRecommended === true) return true;
8768
+ const diag = readRecord4(metadata.completionDiagnostic);
8769
+ return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
8770
+ }
8745
8771
  function buildPendingEventFingerprint(event) {
8746
8772
  const metadata = readRecord4(event.metadataEvent) || {};
8747
8773
  if (event.event === "worktree_bootstrap_complete" || event.event === "worktree_bootstrap_failed") {
8748
8774
  return [event.meshId, event.event, event.nodeId || ""].join("::");
8749
8775
  }
8776
+ if (TERMINAL_COMPLETION_EVENTS.has(event.event)) {
8777
+ const terminalTaskId = readNonEmptyString2(metadata.taskId) || readNonEmptyString2(readRecord4(metadata.payload)?.taskId);
8778
+ if (terminalTaskId) {
8779
+ return [
8780
+ event.meshId,
8781
+ event.event,
8782
+ terminalTaskId,
8783
+ isWeakCompletionMetadata(metadata) ? "weak" : "genuine"
8784
+ ].join("::");
8785
+ }
8786
+ }
8750
8787
  const sessionId = resolveEventSessionId(metadata);
8751
8788
  const providerSessionId = readNonEmptyString2(metadata.providerSessionId);
8752
8789
  const taskId = readNonEmptyString2(metadata.taskId) || readNonEmptyString2(readRecord4(metadata.payload)?.taskId);
@@ -9105,7 +9142,7 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
9105
9142
  }
9106
9143
  }
9107
9144
  }
9108
- var import_fs10, import_path9, import_crypto7, REFINE_TERMINAL_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
9145
+ var import_fs10, import_path9, import_crypto7, REFINE_TERMINAL_EVENTS, TERMINAL_COMPLETION_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
9109
9146
  var init_mesh_events_pending = __esm({
9110
9147
  "src/mesh/mesh-events-pending.ts"() {
9111
9148
  "use strict";
@@ -9118,6 +9155,7 @@ var init_mesh_events_pending = __esm({
9118
9155
  init_mesh_events_utils();
9119
9156
  init_dist();
9120
9157
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9158
+ TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
9121
9159
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9122
9160
  MAX_PENDING_EVENTS_KEEP = 50;
9123
9161
  }
@@ -9447,7 +9485,7 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
9447
9485
  evidence
9448
9486
  }
9449
9487
  });
9450
- updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9488
+ updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed", args.taskId);
9451
9489
  markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9452
9490
  setImmediate(() => cleanupTerminalDirectDispatches());
9453
9491
  queuePendingMeshCoordinatorEvent({
@@ -9737,8 +9775,8 @@ function parsePatternEntry(x) {
9737
9775
  if (x instanceof RegExp) return x;
9738
9776
  if (x && typeof x === "object" && typeof x.source === "string") {
9739
9777
  try {
9740
- const s = x;
9741
- return new RegExp(s.source, s.flags || "");
9778
+ const s2 = x;
9779
+ return new RegExp(s2.source, s2.flags || "");
9742
9780
  } catch {
9743
9781
  return null;
9744
9782
  }
@@ -10309,6 +10347,38 @@ var init_mesh_unresolved_forward_outbox = __esm({
10309
10347
  }
10310
10348
  });
10311
10349
 
10350
+ // src/mesh/mesh-event-trace.ts
10351
+ function s(v) {
10352
+ return typeof v === "string" && v.trim() ? v.trim() : "";
10353
+ }
10354
+ function meshEventTraceKey(ctx) {
10355
+ const segs = [`task=${s(ctx.taskId) || "-"}`];
10356
+ const eventId = s(ctx.eventId);
10357
+ if (eventId) segs.push(`evt=${eventId}`);
10358
+ segs.push(`sess=${s(ctx.sessionId) || "-"}`);
10359
+ const nodeId = s(ctx.nodeId);
10360
+ if (nodeId) segs.push(`node=${nodeId}`);
10361
+ const meshId = s(ctx.meshId);
10362
+ if (meshId) segs.push(`mesh=${meshId}`);
10363
+ const event = s(ctx.event);
10364
+ if (event) segs.push(`event=${event}`);
10365
+ return segs.join(" ");
10366
+ }
10367
+ function traceMeshEventStage(stage, ctx, detail) {
10368
+ LOG.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
10369
+ }
10370
+ function traceMeshEventDrop(reason, ctx, detail) {
10371
+ LOG.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
10372
+ }
10373
+ var CAT;
10374
+ var init_mesh_event_trace = __esm({
10375
+ "src/mesh/mesh-event-trace.ts"() {
10376
+ "use strict";
10377
+ init_logger();
10378
+ CAT = "EvtTrace";
10379
+ }
10380
+ });
10381
+
10312
10382
  // src/config/state-store.ts
10313
10383
  function isPlainObject2(value) {
10314
10384
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -12094,9 +12164,9 @@ function buildAcpSession(state, options) {
12094
12164
  }
12095
12165
  function buildSessionEntries(allStates, cdpManagers, options = {}) {
12096
12166
  const sessions = [];
12097
- const ideStates = allStates.filter((s) => s.category === "ide");
12098
- const cliStates = allStates.filter((s) => s.category === "cli");
12099
- const acpStates = allStates.filter((s) => s.category === "acp");
12167
+ const ideStates = allStates.filter((s2) => s2.category === "ide");
12168
+ const cliStates = allStates.filter((s2) => s2.category === "cli");
12169
+ const acpStates = allStates.filter((s2) => s2.category === "acp");
12100
12170
  for (const state of ideStates) {
12101
12171
  sessions.push(buildWorkspaceSession(state, cdpManagers, options));
12102
12172
  for (const ext of state.extensions) {
@@ -12580,6 +12650,15 @@ function getCachedMeshByWorkspace(workspace) {
12580
12650
  meshByWorkspaceCache.set(workspace, { mesh, cachedAt: now });
12581
12651
  return mesh;
12582
12652
  }
12653
+ function recoverMeshIdByNodeId(nodeId) {
12654
+ if (!nodeId) return "";
12655
+ for (const mesh of listMeshes()) {
12656
+ if (Array.isArray(mesh.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, nodeId))) {
12657
+ return readNonEmptyString2(mesh.id);
12658
+ }
12659
+ }
12660
+ return "";
12661
+ }
12583
12662
  function __resetIdleAutoFastForwardForTests() {
12584
12663
  idleAutoFastForwardLastAttempt.clear();
12585
12664
  }
@@ -13482,6 +13561,13 @@ function shouldForceInjectMeshEvent(eventName) {
13482
13561
  function injectMeshSystemMessage(components, args) {
13483
13562
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
13484
13563
  const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
13564
+ const traceCtx = {
13565
+ taskId: args.metadataEvent.taskId,
13566
+ sessionId: eventSessionId,
13567
+ nodeId: eventNodeId,
13568
+ meshId: args.meshId,
13569
+ event: args.event
13570
+ };
13485
13571
  const sourceSession = args.sourceInstanceId ? components.instanceManager.getInstance(args.sourceInstanceId) : void 0;
13486
13572
  const workerCoordinatorDaemonId = readNonEmptyString2(
13487
13573
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
@@ -13535,6 +13621,7 @@ function injectMeshSystemMessage(components, args) {
13535
13621
  }
13536
13622
  }
13537
13623
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
13624
+ traceMeshEventDrop("intentional_cleanup_stop", traceCtx);
13538
13625
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
13539
13626
  }
13540
13627
  if (args.event === "monitor:no_progress") {
@@ -13555,6 +13642,7 @@ function injectMeshSystemMessage(components, args) {
13555
13642
  }
13556
13643
  if (reconciledCompletion?.source === "no_progress_terminal_ledger_suppression") {
13557
13644
  LOG.info("MeshEvents", `Suppressed no-progress monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
13645
+ traceMeshEventDrop("no_progress_terminal_ledger_suppression", traceCtx, `terminalKind=${reconciledCompletion.terminalLedgerKind}`);
13558
13646
  return {
13559
13647
  success: true,
13560
13648
  forwarded: 0,
@@ -13566,6 +13654,7 @@ function injectMeshSystemMessage(components, args) {
13566
13654
  }
13567
13655
  if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
13568
13656
  LOG.info("MeshEvents", `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
13657
+ traceMeshEventDrop("duplicate_refine_terminal", traceCtx);
13569
13658
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
13570
13659
  }
13571
13660
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
@@ -13580,6 +13669,7 @@ function injectMeshSystemMessage(components, args) {
13580
13669
  });
13581
13670
  if (duplicateApproval) {
13582
13671
  LOG.info("MeshEvents", `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
13672
+ traceMeshEventDrop("duplicate_approval", traceCtx);
13583
13673
  return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
13584
13674
  }
13585
13675
  }
@@ -13599,6 +13689,7 @@ function injectMeshSystemMessage(components, args) {
13599
13689
  const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
13600
13690
  if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "no_progress_reconciliation") {
13601
13691
  LOG.info("MeshEvents", `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
13692
+ traceMeshEventDrop("duplicate_completion_terminal_ledger", traceCtx);
13602
13693
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
13603
13694
  }
13604
13695
  }
@@ -13617,6 +13708,7 @@ function injectMeshSystemMessage(components, args) {
13617
13708
  });
13618
13709
  if (duplicateCompletion) {
13619
13710
  LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
13711
+ traceMeshEventDrop("duplicate_completion", traceCtx);
13620
13712
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
13621
13713
  }
13622
13714
  }
@@ -13635,6 +13727,7 @@ function injectMeshSystemMessage(components, args) {
13635
13727
  });
13636
13728
  if (duplicateStopped) {
13637
13729
  LOG.info("MeshEvents", `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
13730
+ traceMeshEventDrop("duplicate_stopped", traceCtx);
13638
13731
  return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
13639
13732
  }
13640
13733
  }
@@ -13646,7 +13739,7 @@ function injectMeshSystemMessage(components, args) {
13646
13739
  });
13647
13740
  const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
13648
13741
  if (!leaveDirectDispatchActive) {
13649
- updateDirectDispatchStatus(args.meshId, sessionId, outcome);
13742
+ updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
13650
13743
  }
13651
13744
  markSessionDeliveriesTerminal(args.meshId, sessionId, outcome);
13652
13745
  setImmediate(() => cleanupTerminalDirectDispatches());
@@ -13659,7 +13752,7 @@ function injectMeshSystemMessage(components, args) {
13659
13752
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
13660
13753
  const providerType = readNonEmptyString2(args.metadataEvent.providerType);
13661
13754
  if (sessionId) {
13662
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13755
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13663
13756
  const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
13664
13757
  completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
13665
13758
  if (nodeId && providerType) {
@@ -13742,7 +13835,8 @@ function injectMeshSystemMessage(components, args) {
13742
13835
  }
13743
13836
  }
13744
13837
  if (sessionId) {
13745
- updateDirectDispatchStatus(args.meshId, sessionId, "acked");
13838
+ const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
13839
+ updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
13746
13840
  const activeDeliveries = (() => {
13747
13841
  try {
13748
13842
  return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(args.meshId, sessionId);
@@ -13750,7 +13844,8 @@ function injectMeshSystemMessage(components, args) {
13750
13844
  return [];
13751
13845
  }
13752
13846
  })();
13753
- for (const d of activeDeliveries) {
13847
+ const deliveriesToAck = startedTaskId ? activeDeliveries.filter((d) => d.taskId === startedTaskId) : activeDeliveries;
13848
+ for (const d of deliveriesToAck) {
13754
13849
  updateSessionDeliveryStatus(d.id, "acked");
13755
13850
  }
13756
13851
  }
@@ -13764,7 +13859,7 @@ function injectMeshSystemMessage(components, args) {
13764
13859
  }
13765
13860
  }
13766
13861
  if (sessionId) {
13767
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13862
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13768
13863
  completedTaskForLedger = markSessionTerminal(sessionId, "failed");
13769
13864
  }
13770
13865
  }
@@ -13910,6 +14005,9 @@ function injectMeshSystemMessage(components, args) {
13910
14005
  };
13911
14006
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
13912
14007
  LOG.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""}${workerCoordinatorSessionId ? `, coordinator session ${workerCoordinatorSessionId}` : ""})`);
14008
+ traceMeshEventStage("queued", traceCtx, workerCoordinatorDaemonId ? `coordinatorDaemon=${workerCoordinatorDaemonId}` : "broadcast");
14009
+ } else {
14010
+ traceMeshEventDrop("queue_dedup", traceCtx);
13913
14011
  }
13914
14012
  return { success: true, forwarded: 0 };
13915
14013
  }
@@ -13920,8 +14018,23 @@ function handleMeshForwardEvent(components, payload) {
13920
14018
  }
13921
14019
  const nodeId = readNonEmptyString2(payload.nodeId);
13922
14020
  const workspace = readNonEmptyString2(payload.workspace);
13923
- const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "");
13924
- if (!meshId) return { success: false, error: "meshId required" };
14021
+ const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "") || recoverMeshIdByNodeId(nodeId);
14022
+ if (!meshId) {
14023
+ traceMeshEventDrop("meshId_required", {
14024
+ taskId: payload.taskId,
14025
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14026
+ nodeId,
14027
+ event: eventName
14028
+ }, workspace ? `workspace=${workspace} unresolved` : "no workspace/nodeId");
14029
+ return { success: false, error: "meshId required" };
14030
+ }
14031
+ traceMeshEventStage("received", {
14032
+ taskId: payload.taskId,
14033
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14034
+ nodeId,
14035
+ meshId,
14036
+ event: eventName
14037
+ });
13925
14038
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
13926
14039
  const relayModalMessage = readNonEmptyString2(payload.modalMessage);
13927
14040
  const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
@@ -14002,9 +14115,18 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
14002
14115
  workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
14003
14116
  };
14004
14117
  const persisted = enqueueUnresolvedDelegateForward(coordinatorDaemonId, eventName, payload);
14118
+ const fwdTraceCtx = {
14119
+ taskId: payload.taskId,
14120
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14121
+ nodeId: readNonEmptyString2(routing.nodeId) || readNonEmptyString2(event.meshNodeId),
14122
+ event: eventName
14123
+ };
14124
+ traceMeshEventStage("outbox_enqueue", fwdTraceCtx, `coordinatorDaemon=${coordinatorDaemonId} meshId=absent`);
14125
+ traceMeshEventStage("forward_send", fwdTraceCtx, "immediate push");
14005
14126
  Promise.resolve(components.dispatchMeshCommand(coordinatorDaemonId, "mesh_forward_event", payload)).then((result) => {
14006
14127
  if (result && result.success === false) {
14007
14128
  LOG.warn("MeshEvents", `Immediate forward of ${eventName} to coordinator ${coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued for retry`);
14129
+ traceMeshEventDrop("immediate_forward_rejected", fwdTraceCtx, readNonEmptyString2(result.error) || "no reason");
14008
14130
  return;
14009
14131
  }
14010
14132
  if (persisted) ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload);
@@ -14072,6 +14194,14 @@ function setupMeshEventForwarding(components) {
14072
14194
  if (isUnroutableDelegateRejection(routing) && forwardUnresolvedDelegateEvent(components, routing, event)) {
14073
14195
  return;
14074
14196
  }
14197
+ if (isUnroutableDelegateRejection(routing)) {
14198
+ traceMeshEventDrop("unroutable", {
14199
+ taskId: event.meshActiveTaskId ?? event.taskId,
14200
+ sessionId: routing.sessionId,
14201
+ nodeId: routing.nodeId,
14202
+ event: event.event
14203
+ }, "no coordinator anchor / mesh_unresolved");
14204
+ }
14075
14205
  recordUnroutableDelegateEvent(routing, event.event);
14076
14206
  return;
14077
14207
  }
@@ -14102,6 +14232,7 @@ var init_mesh_events_coordinator = __esm({
14102
14232
  init_mesh_events_pending();
14103
14233
  init_mesh_routing();
14104
14234
  init_mesh_unresolved_forward_outbox();
14235
+ init_mesh_event_trace();
14105
14236
  init_snapshot();
14106
14237
  init_repo_mesh_types();
14107
14238
  init_dist();
@@ -14213,6 +14344,13 @@ function findLiveCoordinators(components) {
14213
14344
  function injectPendingIntoCoordinator(coordinator, pending) {
14214
14345
  if (!coordinator || !pending.coordinatorMessage) return;
14215
14346
  const force = shouldForceInjectMeshEvent(pending.event);
14347
+ traceMeshEventStage("surfaced", {
14348
+ taskId: pending.metadataEvent?.taskId,
14349
+ sessionId: pending.metadataEvent?.targetSessionId ?? pending.targetCoordinatorSessionId,
14350
+ nodeId: pending.nodeId,
14351
+ meshId: pending.meshId,
14352
+ event: pending.event
14353
+ }, force ? "force-inject" : "inject");
14216
14354
  coordinator.onEvent("send_message", {
14217
14355
  input: { text: pending.coordinatorMessage, textFallback: pending.coordinatorMessage },
14218
14356
  ...force ? { force: true } : {}
@@ -14271,6 +14409,13 @@ function recoverStrandedAssignedDispatches(meshId, store) {
14271
14409
  });
14272
14410
  if (reclaimed) {
14273
14411
  LOG.warn("MeshReconcile", `Reclaimed stranded assigned task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}, dispatched ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s ago, never confirmed delivered \u2192 ${reclaimed.status})`);
14412
+ traceMeshEventDrop("assigned_stranded_reclaim", {
14413
+ taskId: row.id,
14414
+ sessionId: row.assignedSessionId,
14415
+ nodeId: row.assignedNodeId,
14416
+ meshId,
14417
+ event: "agent:generating_completed"
14418
+ }, `unconfirmed ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s \u2192 ${reclaimed.status}`);
14274
14419
  }
14275
14420
  }
14276
14421
  }
@@ -14430,6 +14575,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14430
14575
  try {
14431
14576
  queuePendingMeshCoordinatorEvent(pending);
14432
14577
  LOG.info("MeshReconcile", `Strict route hold: coordinator session ${wantSession} not live on mesh ${meshId} \u2014 re-queued (${pending.event})`);
14578
+ traceMeshEventDrop("strict_route_hold", {
14579
+ taskId: pending.metadataEvent?.taskId,
14580
+ sessionId: pending.metadataEvent?.targetSessionId ?? wantSession,
14581
+ nodeId: pending.nodeId,
14582
+ meshId,
14583
+ event: pending.event
14584
+ }, `coordinatorSession=${wantSession} not live`);
14433
14585
  } catch (e) {
14434
14586
  LOG.warn("MeshReconcile", `Strict route re-queue failed for ${pending.event} on mesh ${meshId}: ${e?.message || e}`);
14435
14587
  }
@@ -14453,6 +14605,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14453
14605
  }
14454
14606
  });
14455
14607
  LOG.warn("MeshReconcile", `Strict route expire: coordinator session ${wantSession} never returned for mesh ${meshId} \u2014 recorded to ledger (recoverable), dropped (${pending.event})`);
14608
+ traceMeshEventDrop("strict_route_expired", {
14609
+ taskId: pending.metadataEvent?.taskId,
14610
+ sessionId: pending.metadataEvent?.targetSessionId ?? wantSession,
14611
+ nodeId: pending.nodeId,
14612
+ meshId,
14613
+ event: pending.event
14614
+ }, `coordinatorSession=${wantSession} never returned`);
14456
14615
  } catch (e) {
14457
14616
  LOG.warn("MeshReconcile", `Failed to ledger-expire strict-unmatched ${pending.event} for mesh ${meshId}: ${e?.message || e}`);
14458
14617
  }
@@ -14464,15 +14623,24 @@ async function retryUnresolvedDelegateForwards(components) {
14464
14623
  const entries = peekUnresolvedDelegateForwards();
14465
14624
  if (entries.length === 0) return;
14466
14625
  for (const entry of entries) {
14626
+ const entryTraceCtx = {
14627
+ taskId: entry.payload.taskId,
14628
+ sessionId: readNonEmptyString2(entry.payload.targetSessionId) || readNonEmptyString2(entry.payload.sessionId),
14629
+ nodeId: readNonEmptyString2(entry.payload.nodeId),
14630
+ event: readNonEmptyString2(entry.payload.event)
14631
+ };
14467
14632
  let result;
14468
14633
  try {
14634
+ traceMeshEventStage("forward_send", entryTraceCtx, `retry \u2192 ${entry.coordinatorDaemonId}`);
14469
14635
  result = await dispatchMeshCommand(entry.coordinatorDaemonId, "mesh_forward_event", entry.payload);
14470
14636
  } catch (e) {
14471
14637
  LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} failed: ${e?.message || e} \u2014 left queued`);
14638
+ traceMeshEventDrop("retry_forward_failed", entryTraceCtx, e?.message || String(e));
14472
14639
  continue;
14473
14640
  }
14474
14641
  if (result && result.success === false) {
14475
14642
  LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued`);
14643
+ traceMeshEventDrop("retry_forward_rejected", entryTraceCtx, readNonEmptyString2(result.error) || "no reason");
14476
14644
  continue;
14477
14645
  }
14478
14646
  ackUnresolvedDelegateForward(entry.id);
@@ -14714,6 +14882,7 @@ var init_mesh_reconcile_loop = __esm({
14714
14882
  init_mesh_events_coordinator();
14715
14883
  init_mesh_unresolved_forward_outbox();
14716
14884
  init_mesh_events_utils();
14885
+ init_mesh_event_trace();
14717
14886
  init_dist();
14718
14887
  init_mesh_work_queue();
14719
14888
  init_mesh_ledger();
@@ -15549,8 +15718,8 @@ function saveProvidersActive(file) {
15549
15718
  }
15550
15719
  function isValidSource(x) {
15551
15720
  if (!x || typeof x !== "object") return false;
15552
- const s = x;
15553
- return typeof s.name === "string" && s.name.length > 0 && typeof s.url === "string" && s.url.length > 0 && typeof s.ref === "string" && s.ref.length > 0 && typeof s.addedAt === "string";
15721
+ const s2 = x;
15722
+ return typeof s2.name === "string" && s2.name.length > 0 && typeof s2.url === "string" && s2.url.length > 0 && typeof s2.ref === "string" && s2.ref.length > 0 && typeof s2.addedAt === "string";
15554
15723
  }
15555
15724
  function deriveSourceName(url) {
15556
15725
  const m = url.match(/[/:]([^/:]+)\/([^/]+?)(?:\.git)?$/);
@@ -15606,7 +15775,7 @@ function inventoryExternalSources() {
15606
15775
  }
15607
15776
  function sourcesProviding(category, type) {
15608
15777
  const inventory = inventoryExternalSources();
15609
- return inventory.filter((s) => (s.providers[category] || []).includes(type)).map((s) => s.sourceName);
15778
+ return inventory.filter((s2) => (s2.providers[category] || []).includes(type)).map((s2) => s2.sourceName);
15610
15779
  }
15611
15780
  function resolveActiveSource(category, type, activeFile) {
15612
15781
  const candidates = sourcesProviding(category, type);
@@ -15813,10 +15982,10 @@ function compileSettledPromptMatchers(spec) {
15813
15982
  const footers = (spec.withFooter ?? []).map((f) => {
15814
15983
  if (f.kind === "regex") {
15815
15984
  const re = compile2(f.pattern, f.flags ?? "i");
15816
- return { test: (s) => re.test(s) };
15985
+ return { test: (s2) => re.test(s2) };
15817
15986
  }
15818
15987
  const needle = f.pattern.toLowerCase();
15819
- return { test: (s) => s.toLowerCase().includes(needle) };
15988
+ return { test: (s2) => s2.toLowerCase().includes(needle) };
15820
15989
  });
15821
15990
  return { prompt, footers };
15822
15991
  }
@@ -16690,7 +16859,7 @@ var init_cli_state_engine = __esm({
16690
16859
  }
16691
16860
  resolveModal(buttonIndex) {
16692
16861
  const snap = this.transport.getSnapshot();
16693
- const parseApproval = typeof this.transport.runParseApproval === "function" ? (s) => this.transport.runParseApproval(s.recentOutputBuffer.slice(-500)) : (s) => this.runParseApproval(s);
16862
+ const parseApproval = typeof this.transport.runParseApproval === "function" ? (s2) => this.transport.runParseApproval(s2.recentOutputBuffer.slice(-500)) : (s2) => this.runParseApproval(s2);
16694
16863
  let modal = this.activeModal ?? parseApproval(snap);
16695
16864
  if (!modal && this.runner.hasParseSession()) {
16696
16865
  try {
@@ -19379,22 +19548,23 @@ function resolveSections(sectionsObj, lines) {
19379
19548
  const matchesCandidate = (c, i) => c.re.test(lines[i]) && (c.prevRe === null || i > 0 && c.prevRe.test(lines[i - 1])) && (c.nextRe === null || i < total - 1 && c.nextRe.test(lines[i + 1]));
19380
19549
  let idx = -1;
19381
19550
  for (const c of candidates) {
19551
+ let candIdx = -1;
19382
19552
  if (sec.anchor_last) {
19383
19553
  for (let i = total - 1; i >= 0; i--) {
19384
19554
  if (matchesCandidate(c, i)) {
19385
- idx = i;
19555
+ candIdx = i;
19386
19556
  break;
19387
19557
  }
19388
19558
  }
19389
19559
  } else {
19390
19560
  for (let i = 0; i < total; i++) {
19391
19561
  if (matchesCandidate(c, i)) {
19392
- idx = i;
19562
+ candIdx = i;
19393
19563
  break;
19394
19564
  }
19395
19565
  }
19396
19566
  }
19397
- if (idx !== -1) break;
19567
+ if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
19398
19568
  }
19399
19569
  if (idx !== -1) {
19400
19570
  from = idx;
@@ -19446,7 +19616,7 @@ function resolveSections(sectionsObj, lines) {
19446
19616
  }
19447
19617
  function sectionText(sections, sectionId, fullScreen) {
19448
19618
  if (!sectionId) return fullScreen;
19449
- const found = sections.find((s) => s.id === sectionId);
19619
+ const found = sections.find((s2) => s2.id === sectionId);
19450
19620
  return found ? found.text : "";
19451
19621
  }
19452
19622
  function isRegexCondition(c) {
@@ -19574,6 +19744,7 @@ function extractButtonsFromRule(rule, hay) {
19574
19744
  const idx = Number(m[1]);
19575
19745
  let label = String(m[2] ?? "").trim();
19576
19746
  if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
19747
+ const current = hasCursorMarker(lines[i]);
19577
19748
  let j = i + 1;
19578
19749
  while (j < lines.length) {
19579
19750
  const next = lines[j];
@@ -19585,7 +19756,7 @@ function extractButtonsFromRule(rule, hay) {
19585
19756
  }
19586
19757
  if (buttons.some((b) => b.index === idx)) continue;
19587
19758
  const key = keyTemplate.replace(/\{index\}/g, String(idx));
19588
- buttons.push({ index: idx, label, key });
19759
+ buttons.push({ index: idx, label, key, current });
19589
19760
  i = j - 1;
19590
19761
  }
19591
19762
  } else {
@@ -19597,12 +19768,15 @@ function extractButtonsFromRule(rule, hay) {
19597
19768
  if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
19598
19769
  if (buttons.some((b) => b.index === idx)) continue;
19599
19770
  const key = keyTemplate.replace(/\{index\}/g, String(idx));
19600
- buttons.push({ index: idx, label, key });
19771
+ buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
19601
19772
  }
19602
19773
  }
19603
19774
  buttons.sort((a, b) => a.index - b.index);
19604
19775
  return buttons;
19605
19776
  }
19777
+ function hasCursorMarker(text) {
19778
+ return /^\s*[❯›>]/.test(text);
19779
+ }
19606
19780
  var init_evaluator = __esm({
19607
19781
  "src/providers/spec/evaluator.ts"() {
19608
19782
  "use strict";
@@ -19614,10 +19788,10 @@ function isV4Spec(raw) {
19614
19788
  return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
19615
19789
  }
19616
19790
  function initialState(spec) {
19617
- return spec.states.find((s) => s.initial) ?? spec.states[0];
19791
+ return spec.states.find((s2) => s2.initial) ?? spec.states[0];
19618
19792
  }
19619
19793
  function stateById(spec, id) {
19620
- return spec.states.find((s) => s.id === id);
19794
+ return spec.states.find((s2) => s2.id === id);
19621
19795
  }
19622
19796
  function outgoingTransitions(spec, stateId) {
19623
19797
  const matches = spec.transitions.filter((t) => {
@@ -19706,7 +19880,17 @@ function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTr
19706
19880
  const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
19707
19881
  const kind = isRegex(cond) ? "regex" : "changed";
19708
19882
  const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
19709
- return { kind, result, detail };
19883
+ let matchedText;
19884
+ if (result && isRegex(cond)) {
19885
+ try {
19886
+ const hay = sectionText(sections, cond.section, fullScreen);
19887
+ const re = new RegExp(cond.matches, cond.flags ?? "i");
19888
+ const m = re.exec(hay);
19889
+ if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
19890
+ } catch {
19891
+ }
19892
+ }
19893
+ return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
19710
19894
  }
19711
19895
  return { kind: "all", result: false, detail: "unknown condition" };
19712
19896
  }
@@ -19821,17 +20005,17 @@ function validateFsmSpec(raw) {
19821
20005
  }
19822
20006
  const ids = /* @__PURE__ */ new Set();
19823
20007
  let initialCount = 0;
19824
- for (const [i, s] of spec.states.entries()) {
19825
- if (!s.id) {
20008
+ for (const [i, s2] of spec.states.entries()) {
20009
+ if (!s2.id) {
19826
20010
  errs.push(`states[${i}].id is required`);
19827
20011
  continue;
19828
20012
  }
19829
- if (ids.has(s.id)) errs.push(`states[${i}].id "${s.id}" is duplicated`);
19830
- ids.add(s.id);
19831
- if (!s.label) errs.push(`states[${i}].label is required`);
19832
- if (s.initial) initialCount += 1;
19833
- if (s.status && !["idle", "generating", "approval"].includes(s.status)) {
19834
- errs.push(`states[${i}].status "${s.status}" must be idle|generating|approval`);
20013
+ if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
20014
+ ids.add(s2.id);
20015
+ if (!s2.label) errs.push(`states[${i}].label is required`);
20016
+ if (s2.initial) initialCount += 1;
20017
+ if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
20018
+ errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
19835
20019
  }
19836
20020
  }
19837
20021
  if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
@@ -19847,10 +20031,10 @@ function validateFsmSpec(raw) {
19847
20031
  else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
19848
20032
  if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
19849
20033
  }
19850
- for (const [i, s] of spec.states.entries()) {
19851
- const sec = s.extract?.title?.section;
20034
+ for (const [i, s2] of spec.states.entries()) {
20035
+ const sec = s2.extract?.title?.section;
19852
20036
  if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
19853
- const bsec = s.extract?.buttons?.section;
20037
+ const bsec = s2.extract?.buttons?.section;
19854
20038
  if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
19855
20039
  }
19856
20040
  return errs;
@@ -32586,10 +32770,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32586
32770
  const path42 = require("path");
32587
32771
  const { spawnSync: spawnSync2 } = require("child_process");
32588
32772
  const file = ext.loadExternalSources();
32589
- if (file.sources.some((s) => s.name === requestedName)) {
32773
+ if (file.sources.some((s2) => s2.name === requestedName)) {
32590
32774
  return { success: false, error: `source name "${requestedName}" is already registered` };
32591
32775
  }
32592
- if (file.sources.some((s) => s.url === url && s.ref === ref)) {
32776
+ if (file.sources.some((s2) => s2.url === url && s2.ref === ref)) {
32593
32777
  return { success: false, error: `source url+ref already registered (use a different name to track another ref)` };
32594
32778
  }
32595
32779
  const sourceDir = path42.join(ext.externalRoot(), requestedName);
@@ -32651,7 +32835,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32651
32835
  const fs32 = require("fs");
32652
32836
  const path42 = require("path");
32653
32837
  const file = ext.loadExternalSources();
32654
- const match = file.sources.find((s) => s.name === name);
32838
+ const match = file.sources.find((s2) => s2.name === name);
32655
32839
  if (!match) return { success: false, error: `source "${name}" not registered` };
32656
32840
  const sourceDir = path42.join(ext.externalRoot(), name);
32657
32841
  if (fs32.existsSync(sourceDir)) {
@@ -32663,7 +32847,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32663
32847
  }
32664
32848
  ext.saveExternalSources({
32665
32849
  schema: 1,
32666
- sources: file.sources.filter((s) => s.name !== name)
32850
+ sources: file.sources.filter((s2) => s2.name !== name)
32667
32851
  });
32668
32852
  const active = ext.loadProvidersActive();
32669
32853
  const filteredActive = {};
@@ -32687,10 +32871,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32687
32871
  const file = ext.loadExternalSources();
32688
32872
  const inventory = ext.inventoryExternalSources();
32689
32873
  const active = ext.loadProvidersActive();
32690
- const sources = file.sources.map((s) => {
32691
- const inv = inventory.find((e) => e.sourceName === s.name);
32874
+ const sources = file.sources.map((s2) => {
32875
+ const inv = inventory.find((e) => e.sourceName === s2.name);
32692
32876
  return {
32693
- ...s,
32877
+ ...s2,
32694
32878
  providers: inv?.providers ?? {}
32695
32879
  };
32696
32880
  });
@@ -32867,6 +33051,21 @@ var path21 = __toESM(require("path"));
32867
33051
  // src/providers/spec/adapter.ts
32868
33052
  init_terminal_screen();
32869
33053
  var import_session_host_core6 = require("@adhdev/session-host-core");
33054
+ var MAX_PTY_EVENTS = 300;
33055
+ var EVENT_CONTENT_CAP = 240;
33056
+ function escapeControl(text) {
33057
+ return String(text).replace(/[\x00-\x1f\x7f]/g, (ch) => {
33058
+ const code = ch.charCodeAt(0);
33059
+ if (ch === "\r") return "\\r";
33060
+ if (ch === "\n") return "\\n";
33061
+ if (ch === " ") return "\\t";
33062
+ if (code === 27) return "\\x1b";
33063
+ return "\\x" + code.toString(16).padStart(2, "0");
33064
+ });
33065
+ }
33066
+ function capPreview(text) {
33067
+ return text.length > EVENT_CONTENT_CAP ? text.slice(0, EVENT_CONTENT_CAP) + `\u2026(+${text.length - EVENT_CONTENT_CAP})` : text;
33068
+ }
32870
33069
  var TerminalAdapter = class {
32871
33070
  constructor(opts, handlers) {
32872
33071
  this.opts = opts;
@@ -32893,6 +33092,9 @@ var TerminalAdapter = class {
32893
33092
  screenTimer = null;
32894
33093
  tickTimer = null;
32895
33094
  lastScreen = "";
33095
+ /** Debug-only ring buffer of PTY input/output/resize/cursor events. */
33096
+ events = [];
33097
+ lastCursorKey = "";
32896
33098
  start() {
32897
33099
  const env = this.opts.envIsComplete ? this.opts.env ?? {} : { ...process.env, ...this.opts.env ?? {} };
32898
33100
  this.pty = this.factory.spawn(this.opts.binary, this.opts.args ?? [], {
@@ -32901,10 +33103,12 @@ var TerminalAdapter = class {
32901
33103
  cols: this.cols,
32902
33104
  rows: this.rows
32903
33105
  });
33106
+ this.recordEvent("spawn", `${this.opts.binary} (${this.cols}x${this.rows})`);
32904
33107
  this.handlers.init?.({ pid: this.pty.pid });
32905
33108
  this.pty.onData((chunk) => this.onChunk(chunk));
32906
33109
  this.pty.onExit((info) => {
32907
33110
  this.stopTimers();
33111
+ this.recordEvent("exit", `exitCode=${typeof info.exitCode === "number" ? info.exitCode : 0}`);
32908
33112
  this.handlers.on_exit?.({ exitCode: typeof info.exitCode === "number" ? info.exitCode : 0 });
32909
33113
  this.pty = null;
32910
33114
  });
@@ -32915,6 +33119,7 @@ var TerminalAdapter = class {
32915
33119
  resize(cols, rows) {
32916
33120
  this.cols = cols;
32917
33121
  this.rows = rows;
33122
+ this.recordEvent("resize", `${cols}x${rows}`);
32918
33123
  this.pty?.resize(cols, rows);
32919
33124
  this.screen.resize(rows, cols);
32920
33125
  }
@@ -32935,8 +33140,21 @@ var TerminalAdapter = class {
32935
33140
  return { row: pos.row, col: pos.col };
32936
33141
  }
32937
33142
  send_keys(text) {
33143
+ this.recordEvent("input", capPreview(escapeControl(text)), text.length);
32938
33144
  this.pty?.write(text);
32939
33145
  }
33146
+ /** Debug-only: most-recent PTY input/output/resize/cursor events, oldest
33147
+ * first. Pure observation — never consulted by the FSM. */
33148
+ getEventTimeline(limit = MAX_PTY_EVENTS) {
33149
+ const n = Math.max(0, Math.min(limit, this.events.length));
33150
+ return this.events.slice(this.events.length - n);
33151
+ }
33152
+ recordEvent(kind, content, bytes) {
33153
+ const ev = { ts: Date.now(), kind, content };
33154
+ if (typeof bytes === "number") ev.bytes = bytes;
33155
+ this.events.push(ev);
33156
+ if (this.events.length > MAX_PTY_EVENTS) this.events.splice(0, this.events.length - MAX_PTY_EVENTS);
33157
+ }
32940
33158
  kill() {
32941
33159
  this.stopTimers();
32942
33160
  try {
@@ -32947,6 +33165,7 @@ var TerminalAdapter = class {
32947
33165
  this.screen.dispose();
32948
33166
  }
32949
33167
  onChunk(chunk) {
33168
+ this.recordEvent("output", capPreview(escapeControl(chunk)), chunk.length);
32950
33169
  try {
32951
33170
  this.handlers.on_pty_data?.(chunk);
32952
33171
  } catch {
@@ -32956,6 +33175,12 @@ var TerminalAdapter = class {
32956
33175
  this.screenTimer = setTimeout(() => {
32957
33176
  this.screenTimer = null;
32958
33177
  const snap = this.computeScreen();
33178
+ const cur = this.screen.getCursorPosition();
33179
+ const curKey = `${cur.row},${cur.col}`;
33180
+ if (curKey !== this.lastCursorKey) {
33181
+ this.lastCursorKey = curKey;
33182
+ this.recordEvent("cursor", `(${cur.row},${cur.col})`);
33183
+ }
32959
33184
  if (snap === this.lastScreen) return;
32960
33185
  this.lastScreen = snap;
32961
33186
  try {
@@ -33040,20 +33265,40 @@ function applyPreLaunchTrust(trust, workingDir) {
33040
33265
 
33041
33266
  // src/providers/spec/fsm-driver.ts
33042
33267
  init_logger();
33043
- function countNewlines(s) {
33268
+ function countNewlines(s2) {
33044
33269
  let n = 0;
33045
- for (let i = 0; i < s.length; i += 1) if (s.charCodeAt(i) === 10) n += 1;
33270
+ for (let i = 0; i < s2.length; i += 1) if (s2.charCodeAt(i) === 10) n += 1;
33046
33271
  return n;
33047
33272
  }
33048
33273
  var SUBMIT_DELAY_FLOOR_MS = 200;
33049
33274
  var WIN32_SUBMIT_RESEND_GAP_MS = 350;
33050
33275
  var WIN32_SUBMIT_MAX_RESENDS = 14;
33276
+ var WIN32_SUBMIT_SETTLE_MS = 500;
33277
+ var WIN32_SUBMIT_MAX_SETTLE_WAIT_MS = 1e4;
33278
+ var WIN32_SUBMIT_SETTLE_POLL_MS = 120;
33279
+ var WIN32_PTY_WRITE_CHUNK_CHARS = 1024;
33280
+ var WIN32_PTY_WRITE_CHUNK_GAP_MS = 8;
33051
33281
  function resolveSubmitDelayMs(specBeforeSubmit, text) {
33052
33282
  const lines = countNewlines(text);
33053
33283
  const linesBonus = Math.min(800, lines * 80);
33054
33284
  const spec = typeof specBeforeSubmit === "number" && specBeforeSubmit > 0 ? specBeforeSubmit : 0;
33055
33285
  return Math.max(spec, SUBMIT_DELAY_FLOOR_MS + linesBonus);
33056
33286
  }
33287
+ function chunkPreservingSurrogates(text, size) {
33288
+ const chunks = [];
33289
+ let offset = 0;
33290
+ while (offset < text.length) {
33291
+ let end = Math.min(text.length, offset + size);
33292
+ if (end < text.length) {
33293
+ const code = text.charCodeAt(end - 1);
33294
+ if (code >= 55296 && code <= 56319) end -= 1;
33295
+ }
33296
+ if (end <= offset) end = Math.min(text.length, offset + size);
33297
+ chunks.push(text.slice(offset, end));
33298
+ offset = end;
33299
+ }
33300
+ return chunks;
33301
+ }
33057
33302
  function guessExt(mime) {
33058
33303
  if (/png/i.test(mime)) return ".png";
33059
33304
  if (/jpe?g/i.test(mime)) return ".jpg";
@@ -33069,7 +33314,10 @@ var FsmDriver = class {
33069
33314
  this.buildAdapterOpts(),
33070
33315
  {
33071
33316
  init: () => this.emitInitialState(),
33072
- on_pty_data: (chunk) => this.emit({ kind: "pty_data", chunk }),
33317
+ on_pty_data: (chunk) => {
33318
+ this.lastPtyDataAt = Date.now();
33319
+ this.emit({ kind: "pty_data", chunk });
33320
+ },
33073
33321
  on_screen_changed: () => this.reevaluate(),
33074
33322
  on_exit: ({ exitCode }) => this.handleExit(exitCode)
33075
33323
  }
@@ -33103,6 +33351,16 @@ var FsmDriver = class {
33103
33351
  * WIN32_SUBMIT_* and scheduleWin32Submit). Re-arms itself until the FSM
33104
33352
  * leaves idle (submitted) or the resend budget is spent. */
33105
33353
  win32SubmitTimer = null;
33354
+ /** Wall-clock (ms) of the most recent raw PTY output chunk. Advances on every
33355
+ * on_pty_data — including the echo of text written into the composer — so the
33356
+ * win32 submit settle-gate can tell when input has finished landing. */
33357
+ lastPtyDataAt = 0;
33358
+ /** Wall-clock (ms) of the most recent win32 message-body input write. Bridges
33359
+ * the gap between writing a chunk and its echo so the settle-gate does not
33360
+ * declare "quiet" mid-write. */
33361
+ lastWin32WriteAt = 0;
33362
+ /** Pending paced chunk-write timer for a large win32 body (see writeWin32Body). */
33363
+ win32WriteTimer = null;
33106
33364
  currentEval = null;
33107
33365
  stateHistory = [];
33108
33366
  prevStateAt = 0;
@@ -33223,6 +33481,10 @@ var FsmDriver = class {
33223
33481
  clearTimeout(this.win32SubmitTimer);
33224
33482
  this.win32SubmitTimer = null;
33225
33483
  }
33484
+ if (this.win32WriteTimer) {
33485
+ clearTimeout(this.win32WriteTimer);
33486
+ this.win32WriteTimer = null;
33487
+ }
33226
33488
  this.specWatcher?.close();
33227
33489
  this.adapter.kill();
33228
33490
  }
@@ -33253,11 +33515,15 @@ var FsmDriver = class {
33253
33515
  getFsmSnapshotHistory() {
33254
33516
  return this.fsmSnapshotHistory;
33255
33517
  }
33518
+ /** Debug-only PTY input/output/resize/cursor timeline from the adapter. */
33519
+ getEventTimeline(limit) {
33520
+ return this.adapter.getEventTimeline(limit);
33521
+ }
33256
33522
  getSections() {
33257
33523
  try {
33258
33524
  const screen = this.adapter.snapshot();
33259
33525
  const lines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
33260
- return resolveSections(this.spec.sections ?? {}, lines).map((s) => ({ id: s.id, text: s.text }));
33526
+ return resolveSections(this.spec.sections ?? {}, lines).map((s2) => ({ id: s2.id, text: s2.text }));
33261
33527
  } catch {
33262
33528
  return null;
33263
33529
  }
@@ -33635,7 +33901,7 @@ var FsmDriver = class {
33635
33901
  const perChar = sm.delay_ms_per_char ?? 0;
33636
33902
  const beforeSubmit = resolveSubmitDelayMs(sm.delay_ms_before_submit, text);
33637
33903
  if (process.platform === "win32") {
33638
- this.adapter.send_keys(text);
33904
+ this.writeWin32Body(text);
33639
33905
  this.scheduleWin32Submit(sm.submit_key, beforeSubmit);
33640
33906
  return;
33641
33907
  }
@@ -33661,20 +33927,72 @@ var FsmDriver = class {
33661
33927
  const st = stateById(this.spec, this.currentStateId);
33662
33928
  return st ? statusForState(st) : "idle";
33663
33929
  }
33930
+ /** Record a win32 body write so the settle-gate counts it as input activity
33931
+ * even before the echo arrives. */
33932
+ markWin32Write() {
33933
+ this.lastWin32WriteAt = Date.now();
33934
+ }
33935
+ /** Most recent win32 input activity — a write we issued OR a PTY output chunk
33936
+ * (echo). The submit settle-gate waits for this to go quiet. */
33937
+ lastWin32InputActivityAt() {
33938
+ return Math.max(this.lastPtyDataAt, this.lastWin32WriteAt);
33939
+ }
33664
33940
  /**
33665
- * win32 verification-based submit. Sends the submit key, waits a gap, and if
33666
- * the FSM is still 'idle' (the prompt did not submit — the CR was absorbed as
33667
- * a multiline-paste newline) resends, up to WIN32_SUBMIT_MAX_RESENDS. The
33668
- * first CR always fires (so a stale/edge status never suppresses the submit);
33669
- * subsequent resends are gated on still being idle, and stop the instant the
33670
- * agent leaves idle (submitted generating / approval). This converges the
33671
- * nondeterministic multiline window without spamming Enter into the next turn.
33941
+ * Write the message body to the PTY for win32, paced into bounded chunks. A
33942
+ * single unbounded ConPTY write can overflow the input pipe and drop leading
33943
+ * bytes; splitting it with a short inter-chunk gap keeps the console input
33944
+ * buffer from overflowing. Small bodies still go out in a single write. Each
33945
+ * chunk advances lastWin32WriteAt so the submit settle-gate keeps waiting until
33946
+ * the final chunk is out and echoed.
33947
+ */
33948
+ writeWin32Body(text) {
33949
+ if (this.win32WriteTimer) {
33950
+ clearTimeout(this.win32WriteTimer);
33951
+ this.win32WriteTimer = null;
33952
+ }
33953
+ if (text.length <= WIN32_PTY_WRITE_CHUNK_CHARS) {
33954
+ this.markWin32Write();
33955
+ this.adapter.send_keys(text);
33956
+ return;
33957
+ }
33958
+ const chunks = chunkPreservingSurrogates(text, WIN32_PTY_WRITE_CHUNK_CHARS);
33959
+ let idx = 0;
33960
+ const writeNext = () => {
33961
+ this.win32WriteTimer = null;
33962
+ if (idx >= chunks.length) return;
33963
+ this.markWin32Write();
33964
+ this.adapter.send_keys(chunks[idx]);
33965
+ idx += 1;
33966
+ if (idx < chunks.length) {
33967
+ this.win32WriteTimer = setTimeout(writeNext, WIN32_PTY_WRITE_CHUNK_GAP_MS);
33968
+ }
33969
+ };
33970
+ writeNext();
33971
+ }
33972
+ /**
33973
+ * win32 submit. Two phases:
33974
+ *
33975
+ * Phase 1 (settle-gate): hold the first CR until the PTY output has been quiet
33976
+ * for WIN32_SUBMIT_SETTLE_MS after the last input write — i.e. the full
33977
+ * (possibly multi-KB / multiline) body has finished arriving in the composer
33978
+ * and echoing. Honors an initial minimum delay and is bounded by
33979
+ * WIN32_SUBMIT_MAX_SETTLE_WAIT_MS so a noisy screen can never hang the submit.
33980
+ * This is what stops a long message from being submitted half-arrived (its
33981
+ * leading lines lost). A short message settles almost immediately.
33982
+ *
33983
+ * Phase 2 (verified resend — unchanged): send the submit key, wait a gap, and
33984
+ * if the FSM is still 'idle' (the CR was absorbed as a multiline-paste
33985
+ * newline) resend, up to WIN32_SUBMIT_MAX_RESENDS. The first CR always fires
33986
+ * (a stale/edge status never suppresses it); resends are gated on still being
33987
+ * idle and stop the instant the agent leaves idle (submitted → generating /
33988
+ * approval). This preserves the win32 lone-CR-swallow handling.
33672
33989
  */
33673
33990
  scheduleWin32Submit(submitKey, initialDelayMs) {
33674
33991
  if (this.win32SubmitTimer) {
33675
33992
  clearTimeout(this.win32SubmitTimer);
33676
33993
  this.win32SubmitTimer = null;
33677
33994
  }
33995
+ const startedAt = Date.now();
33678
33996
  const fire = (attempt) => {
33679
33997
  this.win32SubmitTimer = null;
33680
33998
  this.adapter.send_keys(submitKey);
@@ -33687,8 +34005,20 @@ var FsmDriver = class {
33687
34005
  fire(attempt + 1);
33688
34006
  }, WIN32_SUBMIT_RESEND_GAP_MS);
33689
34007
  };
33690
- if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(() => fire(0), initialDelayMs);
33691
- else fire(0);
34008
+ const waitForSettle = () => {
34009
+ this.win32SubmitTimer = null;
34010
+ const now = Date.now();
34011
+ const quietFor = now - this.lastWin32InputActivityAt();
34012
+ const waited = now - startedAt;
34013
+ if (quietFor >= WIN32_SUBMIT_SETTLE_MS || waited >= WIN32_SUBMIT_MAX_SETTLE_WAIT_MS) {
34014
+ fire(0);
34015
+ return;
34016
+ }
34017
+ const recheckIn = Math.min(WIN32_SUBMIT_SETTLE_MS - quietFor, WIN32_SUBMIT_SETTLE_POLL_MS);
34018
+ this.win32SubmitTimer = setTimeout(waitForSettle, Math.max(recheckIn, 30));
34019
+ };
34020
+ if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(waitForSettle, initialDelayMs);
34021
+ else waitForSettle();
33692
34022
  }
33693
34023
  handleClickControl(controlId, payload) {
33694
34024
  const ctl = (this.spec.control_bar ?? []).find((c) => c.id === controlId);
@@ -33723,6 +34053,19 @@ var FsmDriver = class {
33723
34053
  if (!m) return;
33724
34054
  const btn = m.buttons.find((b) => b.index === index);
33725
34055
  if (!btn) return;
34056
+ const rule = stateById(this.spec, this.currentStateId)?.extract?.buttons;
34057
+ if (rule?.select_mode === "arrow_keys") {
34058
+ const from = m.buttons.find((b) => b.current)?.index ?? 1;
34059
+ const up = rule.cursor_keys?.up ?? "\x1B[A";
34060
+ const down = rule.cursor_keys?.down ?? "\x1B[B";
34061
+ const delta = btn.index - from;
34062
+ const step = delta >= 0 ? down : up;
34063
+ const nav = step.repeat(Math.abs(delta));
34064
+ const confirm = (rule.key_for_index || "\r").replace(/\{index\}/g, "") || "\r";
34065
+ if (nav) this.adapter.send_keys(nav);
34066
+ this.adapter.send_keys(confirm);
34067
+ return;
34068
+ }
33726
34069
  this.adapter.send_keys(btn.key);
33727
34070
  }
33728
34071
  handleAttachImage(blob, mime) {
@@ -33801,7 +34144,8 @@ function summarizeTransition(t) {
33801
34144
  return out;
33802
34145
  }
33803
34146
  function flattenCond(c, out, depth) {
33804
- out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}`);
34147
+ const matched = c.matchedText ? ` matched=${JSON.stringify(c.matchedText)}` : "";
34148
+ out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}${matched}`);
33805
34149
  for (const child of c.children ?? []) flattenCond(child, out, depth + 1);
33806
34150
  }
33807
34151
  function findStable(c) {
@@ -34519,8 +34863,8 @@ function projectToolBlock(block2, role, tmap) {
34519
34863
  }
34520
34864
  return null;
34521
34865
  }
34522
- function oneLine(s, max) {
34523
- const flat = s.replace(/\s+/g, " ").trim();
34866
+ function oneLine(s2, max) {
34867
+ const flat = s2.replace(/\s+/g, " ").trim();
34524
34868
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
34525
34869
  }
34526
34870
  function parseTimestamp(v) {
@@ -34540,10 +34884,10 @@ function parseTimestamp(v) {
34540
34884
  return null;
34541
34885
  }
34542
34886
  function normalizeRole(r) {
34543
- const s = String(r ?? "").toLowerCase();
34544
- if (s === "user" || s === "human" || s === "user_explicit") return "user";
34545
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
34546
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
34887
+ const s2 = String(r ?? "").toLowerCase();
34888
+ if (s2 === "user" || s2 === "human" || s2 === "user_explicit") return "user";
34889
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
34890
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
34547
34891
  return "system";
34548
34892
  }
34549
34893
  function stringifyContent(v) {
@@ -34591,18 +34935,18 @@ function compileWhere(src) {
34591
34935
  return (record) => ors.some((ands) => ands.every((t) => evalTerm(t, record)));
34592
34936
  }
34593
34937
  function parseTerm(src) {
34594
- let s = src.trim();
34938
+ let s2 = src.trim();
34595
34939
  let negate = false;
34596
- if (s.startsWith("!")) {
34940
+ if (s2.startsWith("!")) {
34597
34941
  negate = true;
34598
- s = s.slice(1).trim();
34942
+ s2 = s2.slice(1).trim();
34599
34943
  }
34600
- const fnMatch = s.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34944
+ const fnMatch = s2.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34601
34945
  if (fnMatch) {
34602
34946
  const [, op2, pathExpr, litExpr] = fnMatch;
34603
34947
  return { path: pathExpr, op: op2, lit: parseLiteral(litExpr), negate };
34604
34948
  }
34605
- const opMatch = s.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34949
+ const opMatch = s2.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34606
34950
  if (!opMatch) return null;
34607
34951
  const [, lhs, op, rhsRaw] = opMatch;
34608
34952
  return { path: lhs.trim(), op, lit: parseLiteral(rhsRaw.trim()), negate };
@@ -34959,9 +35303,12 @@ var SpecCliAdapter = class _SpecCliAdapter {
34959
35303
  * — not this code — decides how a selection is keyed for each CLI.
34960
35304
  */
34961
35305
  async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
34962
- this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
34963
- await this.waitForPickerRendered(action);
34964
- const options = this.extractPickerChoices(action);
35306
+ let options = this.extractPickerChoicesIfRendered(action);
35307
+ if (!options) {
35308
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
35309
+ await this.waitForPickerRendered(action);
35310
+ options = this.extractPickerChoices(action);
35311
+ }
34965
35312
  let index = choiceIndex;
34966
35313
  if ((index == null || !Number.isFinite(index)) && choiceLabel) {
34967
35314
  const needle = choiceLabel.trim().toLowerCase();
@@ -34974,8 +35321,27 @@ var SpecCliAdapter = class _SpecCliAdapter {
34974
35321
  if (index == null || !Number.isFinite(index)) {
34975
35322
  return { ok: false, error: "choiceIndex or choiceLabel required to select" };
34976
35323
  }
34977
- const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
34978
- this.driver.dispatch({ kind: "pty_write", data: keys });
35324
+ if (action.select_mode === "arrow_keys") {
35325
+ const current = options.find((o) => o.current);
35326
+ if (current == null) {
35327
+ return {
35328
+ ok: false,
35329
+ error: "arrow-nav picker: current cursor row not detected on screen",
35330
+ controlResult: { options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })) }
35331
+ };
35332
+ }
35333
+ const up = action.cursor_keys?.up ?? "\x1B[A";
35334
+ const down = action.cursor_keys?.down ?? "\x1B[B";
35335
+ const delta = index - current.index;
35336
+ const step = delta >= 0 ? down : up;
35337
+ const nav = step.repeat(Math.abs(delta));
35338
+ const confirm = (action.submit_key || "\r").replace(/\{index\}/g, "") || "\r";
35339
+ if (nav) this.driver.dispatch({ kind: "pty_write", data: nav });
35340
+ this.driver.dispatch({ kind: "pty_write", data: confirm });
35341
+ } else {
35342
+ const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
35343
+ this.driver.dispatch({ kind: "pty_write", data: keys });
35344
+ }
34979
35345
  const selected = options.find((o) => o.index === index);
34980
35346
  return {
34981
35347
  ok: true,
@@ -34987,6 +35353,20 @@ var SpecCliAdapter = class _SpecCliAdapter {
34987
35353
  }
34988
35354
  };
34989
35355
  }
35356
+ /** Parse the picker choices only if the picker already appears rendered on
35357
+ * the live screen (its `wait_for` condition currently matches and at least
35358
+ * one choice parses). Returns the parsed choices when open, else null so
35359
+ * the caller knows it must send the trigger to open it. Used to de-dup the
35360
+ * picker open in {@link selectPickerChoice}. */
35361
+ extractPickerChoicesIfRendered(action) {
35362
+ const wf = action.wait_for;
35363
+ if (wf?.regex) {
35364
+ const re = new RegExp(wf.regex, wf.flags ?? "i");
35365
+ if (!re.test(this.readScreenSectionText(wf.section))) return null;
35366
+ }
35367
+ const options = this.extractPickerChoices(action);
35368
+ return options.length > 0 ? options : null;
35369
+ }
34990
35370
  /** Poll the live screen until the picker's `wait_for` condition matches,
34991
35371
  * up to a short budget. Returns true if it rendered, false on timeout. */
34992
35372
  async waitForPickerRendered(action) {
@@ -35034,7 +35414,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
35034
35414
  try {
35035
35415
  const sections = this.driver.getSections();
35036
35416
  if (sectionId && sections) {
35037
- const hit = sections.find((s) => s.id === sectionId);
35417
+ const hit = sections.find((s2) => s2.id === sectionId);
35038
35418
  if (hit) return hit.text;
35039
35419
  }
35040
35420
  return this.driver.getScreen();
@@ -35049,7 +35429,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
35049
35429
  screen = this.driver.snapshot();
35050
35430
  const driverSections = this.driver.getSections?.();
35051
35431
  if (driverSections) {
35052
- sections = Object.fromEntries(driverSections.map((s) => [s.id, s.text]));
35432
+ sections = Object.fromEntries(driverSections.map((s2) => [s2.id, s2.text]));
35053
35433
  } else {
35054
35434
  sections = this.readCurrentScreenSections(screen);
35055
35435
  }
@@ -35095,6 +35475,10 @@ var SpecCliAdapter = class _SpecCliAdapter {
35095
35475
  // answers "why did this rule fire" after the fact, unlike the live
35096
35476
  // `fsm` field which only reflects the current instant.
35097
35477
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35478
+ // PTY input/output/resize/cursor event timeline (debug-only) so the
35479
+ // snapshot shows what we typed / what the PTY printed around each
35480
+ // status transition. Null for drivers without the timeline.
35481
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
35098
35482
  // Extended fields
35099
35483
  name: this.cliName,
35100
35484
  status: this.getStatus().status,
@@ -35459,6 +35843,8 @@ var SpecCliAdapter = class _SpecCliAdapter {
35459
35843
  // v4 FSM transition snapshot history — the captured pre-transition
35460
35844
  // evaluation table at each transition (null for v3 specs).
35461
35845
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35846
+ // PTY input/output/resize/cursor event timeline (debug-only).
35847
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
35462
35848
  messages,
35463
35849
  committedMessages: messages
35464
35850
  };
@@ -35503,6 +35889,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
35503
35889
 
35504
35890
  // src/providers/cli-provider-instance.ts
35505
35891
  init_logger();
35892
+ init_mesh_event_trace();
35506
35893
  init_control_effects();
35507
35894
  init_approval_utils();
35508
35895
  init_provider_patch_state();
@@ -36554,6 +36941,23 @@ var CliProviderInstance = class _CliProviderInstance {
36554
36941
  if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
36555
36942
  this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
36556
36943
  }
36944
+ // EVTTRACE (observation-only): is this a mesh worker session whose completion
36945
+ // events must route to a coordinator? Used purely to gate trace logging so a
36946
+ // non-mesh CLI session's completions don't add EvtTrace noise. No decision logic.
36947
+ isMeshWorkerSession() {
36948
+ return !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.meshNodeId || this.settings.launchedByCoordinator);
36949
+ }
36950
+ // EVTTRACE correlation context for this session's completion lifecycle. taskId is
36951
+ // the primary grep anchor; instanceId is the session fallback.
36952
+ meshTraceCtx(event = "agent:generating_completed") {
36953
+ return {
36954
+ taskId: this.settings.meshActiveTaskId,
36955
+ sessionId: this.instanceId,
36956
+ nodeId: this.settings.meshNodeId,
36957
+ meshId: this.settings.meshNodeFor,
36958
+ event
36959
+ };
36960
+ }
36557
36961
  flushCompletedDebounceIfFinalized() {
36558
36962
  const pending = this.completedDebouncePending;
36559
36963
  if (!pending) {
@@ -36574,24 +36978,33 @@ var CliProviderInstance = class _CliProviderInstance {
36574
36978
  if (block2) {
36575
36979
  const blockReason = block2.reason;
36576
36980
  const waitedMs = Date.now() - pending.firstObservedAt;
36577
- LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36578
- if (block2.terminal && !block2.allowTimeout || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
36981
+ const isTranscriptEvidenceGate = block2.allowTimeout === true;
36982
+ LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} allowTimeout=${isTranscriptEvidenceGate} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36983
+ if (!isTranscriptEvidenceGate && (block2.terminal || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS)) {
36579
36984
  if (pending.loggedBlockReason !== blockReason) {
36580
36985
  LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
36986
+ if (this.isMeshWorkerSession()) {
36987
+ traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
36988
+ }
36581
36989
  pending.loggedBlockReason = blockReason;
36582
36990
  }
36583
36991
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
36584
36992
  return;
36585
36993
  }
36994
+ const emittedAfterFinalizationTimeout = waitedMs >= COMPLETED_FINALIZATION_MAX_WAIT_MS;
36586
36995
  const completionDiagnostic = this.buildCompletedFinalizationDiagnostic({
36587
36996
  blockReason,
36588
36997
  latestStatus,
36589
36998
  latestVisibleStatus,
36590
36999
  waitedMs,
36591
37000
  pending,
36592
- emittedAfterFinalizationTimeout: true
37001
+ emittedAfterFinalizationTimeout
36593
37002
  });
36594
- LOG.warn("CLI", `[${this.type}] emitting completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
37003
+ completionDiagnostic.decoupledImmediateEmit = isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout;
37004
+ LOG.warn("CLI", `[${this.type}] emitting completed event (${isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "CANON-C decoupled-immediate, transcript pending" : `after ${waitedMs}ms`}) without finalized assistant turn (${blockReason})`);
37005
+ if (this.isMeshWorkerSession()) {
37006
+ traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
37007
+ }
36595
37008
  this.pushEvent({
36596
37009
  event: "agent:generating_completed",
36597
37010
  chatTitle: pending.chatTitle,
@@ -36614,6 +37027,9 @@ var CliProviderInstance = class _CliProviderInstance {
36614
37027
  return;
36615
37028
  }
36616
37029
  LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
37030
+ if (this.isMeshWorkerSession()) {
37031
+ traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
37032
+ }
36617
37033
  this.pushEvent({
36618
37034
  event: "agent:generating_completed",
36619
37035
  chatTitle: pending.chatTitle,
@@ -36852,6 +37268,9 @@ var CliProviderInstance = class _CliProviderInstance {
36852
37268
  if (missingEvidence && !hasMeshContext) {
36853
37269
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, no mesh context (source=${shortEvidenceSource})`);
36854
37270
  } else {
37271
+ if (this.isMeshWorkerSession()) {
37272
+ traceMeshEventStage("fired", this.meshTraceCtx(), `short-generating idle (source=${shortEvidenceSource})`);
37273
+ }
36855
37274
  this.pushEvent({
36856
37275
  event: "agent:generating_completed",
36857
37276
  chatTitle,
@@ -36928,6 +37347,9 @@ var CliProviderInstance = class _CliProviderInstance {
36928
37347
  const monitorParsedStatus = parsedStatus;
36929
37348
  for (const me of monitorEvents) {
36930
37349
  if (me.type === "monitor:no_progress" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
37350
+ if (this.isMeshWorkerSession()) {
37351
+ traceMeshEventStage("fired", this.meshTraceCtx(), "no_progress_monitor_final_summary");
37352
+ }
36931
37353
  this.pushEvent({
36932
37354
  event: "agent:generating_completed",
36933
37355
  chatTitle,
@@ -36968,6 +37390,12 @@ var CliProviderInstance = class _CliProviderInstance {
36968
37390
  workspace: typeof event.workspace === "string" && event.workspace.trim() ? event.workspace : this.workingDir,
36969
37391
  providerSessionId: typeof event.providerSessionId === "string" && event.providerSessionId.trim() ? event.providerSessionId : this.providerSessionId
36970
37392
  };
37393
+ if (this.isMeshWorkerSession() && this.settings.meshActiveTaskId) {
37394
+ const existingTaskId = typeof enrichedEvent.taskId === "string" && enrichedEvent.taskId.trim() ? enrichedEvent.taskId : void 0;
37395
+ if (!existingTaskId) {
37396
+ enrichedEvent.taskId = this.settings.meshActiveTaskId;
37397
+ }
37398
+ }
36971
37399
  if (this.context?.emitProviderEvent) {
36972
37400
  this.context.emitProviderEvent(enrichedEvent);
36973
37401
  } else {
@@ -40791,7 +41219,7 @@ function parsePbFile(filePath, sessionId) {
40791
41219
  }
40792
41220
  if (buf.length === 0) return null;
40793
41221
  const strings = extractStringsFromBuffer(buf);
40794
- const meaningful = strings.filter((s) => s.length >= MIN_PRINTABLE_RUN && /\w/.test(s));
41222
+ const meaningful = strings.filter((s2) => s2.length >= MIN_PRINTABLE_RUN && /\w/.test(s2));
40795
41223
  if (meaningful.length === 0) return null;
40796
41224
  const content = meaningful.join("\n");
40797
41225
  const sourceMtimeMs = statMtimeMs3(filePath);
@@ -40999,10 +41427,10 @@ function readSession4(sessionPath) {
40999
41427
  };
41000
41428
  }
41001
41429
  function normalizeHermesRole(r) {
41002
- const s = String(r ?? "").toLowerCase();
41003
- if (s === "user" || s === "human") return "user";
41004
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
41005
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41430
+ const s2 = String(r ?? "").toLowerCase();
41431
+ if (s2 === "user" || s2 === "human") return "user";
41432
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41433
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
41006
41434
  return "system";
41007
41435
  }
41008
41436
 
@@ -41228,10 +41656,10 @@ function safeMtime(p) {
41228
41656
  }
41229
41657
  }
41230
41658
  function normalizeRole2(r) {
41231
- const s = String(r ?? "").toLowerCase();
41232
- if (s === "user" || s === "human") return "user";
41233
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
41234
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41659
+ const s2 = String(r ?? "").toLowerCase();
41660
+ if (s2 === "user" || s2 === "human") return "user";
41661
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41662
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
41235
41663
  return "system";
41236
41664
  }
41237
41665
 
@@ -41251,7 +41679,7 @@ function synthesizeControlsFromControlBar(specControls) {
41251
41679
  const actionType = ctl?.action?.type;
41252
41680
  if (!id || !actionType) return;
41253
41681
  const label = typeof ctl?.label === "string" && ctl.label.trim() ? ctl.label : id;
41254
- const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s) => typeof s === "string") : void 0;
41682
+ const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s2) => typeof s2 === "string") : void 0;
41255
41683
  if (actionType === "open_picker") {
41256
41684
  out.push({
41257
41685
  id,
@@ -48167,7 +48595,7 @@ var DaemonCommandRouter = class {
48167
48595
  const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing; merge/refine was not attempted. Configure validation.bootstrapCommands if Refinery should bootstrap dependencies before validation." : validationSummary.failureCode === "dependency_bootstrap_failed" ? "Refinery dependency/bootstrap command failed; merge/refine was not attempted." : validationSummary.failureCode === "spawn_resolution_failed" ? validationSummary.spawnResolutionError || "Refinery validation command could not be spawned (executable not found); merge/refine was not attempted." : "Refinery validation gate failed; merge/refine was not attempted.";
48168
48596
  if (!firstFailedCmd) return base;
48169
48597
  const cmdName = typeof firstFailedCmd.displayCommand === "string" ? firstFailedCmd.displayCommand : typeof firstFailedCmd.command === "string" ? [firstFailedCmd.command, ...Array.isArray(firstFailedCmd.args) ? firstFailedCmd.args : []].join(" ").trim() : typeof firstFailedCmd.cmd === "string" ? firstFailedCmd.cmd : "";
48170
- const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s) => typeof s === "string" && s.length > 0).join("\n");
48598
+ const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
48171
48599
  const tail = rawOutput.length > 800 ? rawOutput.slice(-800) : rawOutput;
48172
48600
  return [
48173
48601
  base,
@@ -48918,7 +49346,7 @@ ${hintLines.join("\n")}` : "",
48918
49346
  convergence = "blocked_review";
48919
49347
  }
48920
49348
  const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
48921
- const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
49349
+ const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s2) => s2.status === "failed").map((s2) => s2.stage).filter(Boolean).pop() : void 0;
48922
49350
  results.push({
48923
49351
  nodeId: node.id,
48924
49352
  workspace: node.workspace,
@@ -49915,7 +50343,7 @@ ${hintLines.join("\n")}` : "",
49915
50343
  return {
49916
50344
  success: true,
49917
50345
  screenLineCount: lines.length,
49918
- sections: resolved.map((s) => ({ id: s.id, fromLine: s.fromLine, toLine: s.toLine, text: s.text }))
50346
+ sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
49919
50347
  };
49920
50348
  } catch (e) {
49921
50349
  return { success: false, error: `resolve failed: ${e.message}` };
@@ -50484,7 +50912,7 @@ ${hintLines.join("\n")}` : "",
50484
50912
  if (!meshId) return { success: false, error: "meshId required" };
50485
50913
  try {
50486
50914
  const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
50487
- const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
50915
+ const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
50488
50916
  const rawQueue = getQueue2(meshId, { status });
50489
50917
  const statusById = new Map(getQueue2(meshId).map((task) => [task.id, task.status]));
50490
50918
  const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById) } : task);
@@ -50765,7 +51193,7 @@ ${hintLines.join("\n")}` : "",
50765
51193
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50766
51194
  }
50767
51195
  const selfDaemonId = this.deps.statusInstanceId;
50768
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51196
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50769
51197
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50770
51198
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
50771
51199
  ...typeof args === "object" && args !== null ? args : {},
@@ -50799,7 +51227,7 @@ ${hintLines.join("\n")}` : "",
50799
51227
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50800
51228
  }
50801
51229
  const selfDaemonId = this.deps.statusInstanceId;
50802
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51230
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50803
51231
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50804
51232
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "get_mesh_node_logs", {
50805
51233
  ...typeof args === "object" && args !== null ? args : {},
@@ -50847,7 +51275,7 @@ ${hintLines.join("\n")}` : "",
50847
51275
  const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
50848
51276
  const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
50849
51277
  const selfDaemonId = this.deps.statusInstanceId;
50850
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51278
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50851
51279
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50852
51280
  const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
50853
51281
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
@@ -50934,7 +51362,7 @@ ${hintLines.join("\n")}` : "",
50934
51362
  let worktreeCleanup;
50935
51363
  if (node?.isLocalWorktree) {
50936
51364
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
50937
- const isRemoteWorktree = nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
51365
+ const isRemoteWorktree = nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
50938
51366
  if (isRemoteWorktree) {
50939
51367
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "remove_mesh_node", {
50940
51368
  ...typeof args === "object" && args !== null ? args : {},
@@ -51016,7 +51444,7 @@ ${hintLines.join("\n")}` : "",
51016
51444
  const sourceNode = mesh.nodes?.find((n) => meshNodeIdMatches(n, sourceNodeId));
51017
51445
  if (!sourceNode) return { success: false, error: `Source node '${sourceNodeId}' not found in mesh` };
51018
51446
  const sourceDaemonId = typeof sourceNode.daemonId === "string" ? sourceNode.daemonId.trim() : void 0;
51019
- if (sourceDaemonId && sourceDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51447
+ if (sourceDaemonId && !daemonIdsEquivalent(sourceDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51020
51448
  const forwarded = await this.deps.dispatchMeshCommand(sourceDaemonId, "clone_mesh_node", {
51021
51449
  ...typeof args === "object" && args !== null ? args : {},
51022
51450
  _meshDirectDispatch: true
@@ -51258,7 +51686,7 @@ ${hintLines.join("\n")}` : "",
51258
51686
  if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
51259
51687
  if (!node.isLocalWorktree) return { success: false, error: "Node is not a local worktree node" };
51260
51688
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
51261
- if (nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51689
+ if (nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51262
51690
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "retry_mesh_node_bootstrap", {
51263
51691
  ...typeof args === "object" && args !== null ? args : {},
51264
51692
  _meshDirectDispatch: true
@@ -52468,16 +52896,16 @@ var DaemonStatusReporter = class {
52468
52896
  const now = this.lastStatusSentAt;
52469
52897
  const target = opts?.p2pOnly ? "P2P" : serverConnected ? "P2P+Server" : "P2P";
52470
52898
  const allStates = this.deps.instanceManager.collectAllStates();
52471
- const ideStates = allStates.filter((s) => s.category === "ide");
52472
- const cliStates = allStates.filter((s) => s.category === "cli");
52473
- const acpStates = allStates.filter((s) => s.category === "acp");
52474
- const ideSummary = ideStates.map((s) => {
52475
- const msgs = s.activeChat?.messages?.length || 0;
52476
- const exts = s.extensions.length;
52477
- return `${s.type}(${s.status},${msgs}msg,${exts}ext)`;
52899
+ const ideStates = allStates.filter((s2) => s2.category === "ide");
52900
+ const cliStates = allStates.filter((s2) => s2.category === "cli");
52901
+ const acpStates = allStates.filter((s2) => s2.category === "acp");
52902
+ const ideSummary = ideStates.map((s2) => {
52903
+ const msgs = s2.activeChat?.messages?.length || 0;
52904
+ const exts = s2.extensions.length;
52905
+ return `${s2.type}(${s2.status},${msgs}msg,${exts}ext)`;
52478
52906
  }).join(", ");
52479
- const cliSummary = cliStates.map((s) => `${s.type}(${s.status})`).join(", ");
52480
- const acpSummary = acpStates.map((s) => `${s.type}(${s.status})`).join(", ");
52907
+ const cliSummary = cliStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52908
+ const acpSummary = acpStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52481
52909
  const logLevel = opts?.p2pOnly ? "debug" : "info";
52482
52910
  const baseSummary = `IDE: ${ideStates.length} [${ideSummary}] CLI: ${cliStates.length} [${cliSummary}] ACP: ${acpStates.length} [${acpSummary}]`;
52483
52911
  const summaryChanged = baseSummary !== this.lastStatusSummary;
@@ -52588,10 +53016,10 @@ var DaemonStatusReporter = class {
52588
53016
  }
52589
53017
  return false;
52590
53018
  }
52591
- simpleHash(s) {
53019
+ simpleHash(s2) {
52592
53020
  let h = 2166136261;
52593
- for (let i = 0; i < s.length; i++) {
52594
- h ^= s.charCodeAt(i);
53021
+ for (let i = 0; i < s2.length; i++) {
53022
+ h ^= s2.charCodeAt(i);
52595
53023
  h = h * 16777619 >>> 0;
52596
53024
  }
52597
53025
  return h.toString(36);
@@ -53817,7 +54245,7 @@ var ProviderInstanceManager = class {
53817
54245
  * Per-category status collect
53818
54246
  */
53819
54247
  collectStatesByCategory(category) {
53820
- return this.collectAllStates().filter((s) => s.category === category);
54248
+ return this.collectAllStates().filter((s2) => s2.category === category);
53821
54249
  }
53822
54250
  // ─── Tick engine ─────────────────────────────────
53823
54251
  /**
@@ -55717,9 +56145,9 @@ function getCliProviderResolutionMeta(ctx, type, adapter) {
55717
56145
  function findCliTarget(ctx, type, instanceId) {
55718
56146
  if (!ctx.instanceManager) return null;
55719
56147
  const cliStates = ctx.instanceManager.collectAllStates().filter(isCliTargetState);
55720
- if (instanceId) return cliStates.find((s) => s.instanceId === instanceId) || null;
56148
+ if (instanceId) return cliStates.find((s2) => s2.instanceId === instanceId) || null;
55721
56149
  if (!type) return cliStates[cliStates.length - 1] || null;
55722
- const matches = cliStates.filter((s) => s.type === type);
56150
+ const matches = cliStates.filter((s2) => s2.type === type);
55723
56151
  return matches[matches.length - 1] || null;
55724
56152
  }
55725
56153
  function getCliTargetBundle(ctx, type, instanceId) {
@@ -56082,20 +56510,20 @@ async function handleCliStatus(ctx, _req, res) {
56082
56510
  return;
56083
56511
  }
56084
56512
  const allStates = ctx.instanceManager.collectAllStates();
56085
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
56086
- const result = cliStates.map((s) => ({
56087
- instanceId: s.instanceId,
56088
- type: s.type,
56089
- name: s.name,
56090
- category: s.category,
56091
- status: s.status,
56092
- mode: s.mode,
56093
- workspace: s.workspace,
56094
- messageCount: s.activeChat?.messages?.length || 0,
56095
- lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
56096
- activeModal: s.activeChat?.activeModal || null,
56097
- pendingEvents: s.pendingEvents || [],
56098
- settings: s.settings
56513
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56514
+ const result = cliStates.map((s2) => ({
56515
+ instanceId: s2.instanceId,
56516
+ type: s2.type,
56517
+ name: s2.name,
56518
+ category: s2.category,
56519
+ status: s2.status,
56520
+ mode: s2.mode,
56521
+ workspace: s2.workspace,
56522
+ messageCount: s2.activeChat?.messages?.length || 0,
56523
+ lastMessage: s2.activeChat?.messages?.slice(-1)[0] || null,
56524
+ activeModal: s2.activeChat?.activeModal || null,
56525
+ pendingEvents: s2.pendingEvents || [],
56526
+ settings: s2.settings
56099
56527
  }));
56100
56528
  ctx.json(res, 200, { instances: result, count: result.length });
56101
56529
  }
@@ -56184,9 +56612,9 @@ function handleCliSSE(ctx, cliSSEClients, _req, res) {
56184
56612
  }
56185
56613
  if (ctx.instanceManager) {
56186
56614
  const allStates = ctx.instanceManager.collectAllStates();
56187
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
56188
- for (const s of cliStates) {
56189
- ctx.sendCliSSE({ event: "snapshot", providerType: s.type, status: s.status, instanceId: s.instanceId });
56615
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56616
+ for (const s2 of cliStates) {
56617
+ ctx.sendCliSSE({ event: "snapshot", providerType: s2.type, status: s2.status, instanceId: s2.instanceId });
56190
56618
  }
56191
56619
  }
56192
56620
  _req.on("close", () => {
@@ -56202,7 +56630,7 @@ async function handleCliDebug(ctx, type, _req, res) {
56202
56630
  const target = findCliTarget(ctx, type);
56203
56631
  if (!target) {
56204
56632
  const allStates = ctx.instanceManager.collectAllStates();
56205
- ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type) });
56633
+ ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type) });
56206
56634
  return;
56207
56635
  }
56208
56636
  const instance = ctx.instanceManager.getInstance(target.instanceId);
@@ -56248,7 +56676,7 @@ async function handleCliTrace(ctx, type, req, res) {
56248
56676
  const allStates = ctx.instanceManager.collectAllStates();
56249
56677
  ctx.json(res, 404, {
56250
56678
  error: `No running instance for: ${type}`,
56251
- available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type)
56679
+ available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type)
56252
56680
  });
56253
56681
  return;
56254
56682
  }
@@ -57053,7 +57481,7 @@ async function handleAutoImplement(ctx, type, req, res) {
57053
57481
  child.write("\x1B[12;1R");
57054
57482
  ctx.log("Terminal CPR request (\\x1b[6n) intercepted in PTY, responding with dummy coordinates [12;1R]");
57055
57483
  }
57056
- checkAutoApproval(data, (s) => child.write(s));
57484
+ checkAutoApproval(data, (s2) => child.write(s2));
57057
57485
  sendAutoImplSSE(ctx, { event: "output", data: { chunk: data, stream: "stdout" } });
57058
57486
  scheduleAutoStopForVerification();
57059
57487
  });
@@ -57066,7 +57494,7 @@ async function handleAutoImplement(ctx, type, req, res) {
57066
57494
  stdout += chunk;
57067
57495
  clearAutoStopTimer();
57068
57496
  if (chunk.includes("\x1B[6n")) child.stdin?.write("\x1B[1;1R");
57069
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57497
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
57070
57498
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stdout" } });
57071
57499
  scheduleAutoStopForVerification();
57072
57500
  });
@@ -57074,7 +57502,7 @@ async function handleAutoImplement(ctx, type, req, res) {
57074
57502
  const chunk = d.toString();
57075
57503
  stderr += chunk;
57076
57504
  clearAutoStopTimer();
57077
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57505
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
57078
57506
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stderr" } });
57079
57507
  scheduleAutoStopForVerification();
57080
57508
  });
@@ -57889,59 +58317,59 @@ var DevServer = class _DevServer {
57889
58317
  // ─── Route Table ─────────────────────────────────────
57890
58318
  routes = [
57891
58319
  // Static routes
57892
- { method: "GET", pattern: "/api/providers", handler: (q, s) => this.handleListProviders(q, s) },
57893
- { method: "GET", pattern: "/api/providers/source-config", handler: (q, s) => this.handleGetProviderSourceConfig(q, s) },
57894
- { method: "POST", pattern: "/api/providers/source-config", handler: (q, s) => this.handleSetProviderSourceConfig(q, s) },
57895
- { method: "GET", pattern: "/api/providers/versions", handler: (q, s) => this.handleDetectVersions(q, s) },
57896
- { method: "POST", pattern: "/api/providers/reload", handler: (q, s) => this.handleReload(q, s) },
57897
- { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s) => this.handleCdpEvaluate(q, s) },
57898
- { method: "POST", pattern: "/api/cdp/click", handler: (q, s) => this.handleCdpClick(q, s) },
57899
- { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s) => this.handleCdpDomQuery(q, s) },
57900
- { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s) => this.handleDomInspect(q, s) },
57901
- { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s) => this.handleDomChildren(q, s) },
57902
- { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s) => this.handleDomAnalyze(q, s) },
57903
- { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s) => this.handleFindByText(q, s) },
57904
- { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s) => this.handleFindCommon(q, s) },
57905
- { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s) => this.handleScreenshot(q, s) },
57906
- { method: "GET", pattern: "/api/cdp/targets", handler: (q, s) => this.handleCdpTargets(q, s) },
57907
- { method: "POST", pattern: "/api/scripts/run", handler: (q, s) => this.handleScriptsRun(q, s) },
57908
- { method: "GET", pattern: "/api/status", handler: (q, s) => this.handleStatus(q, s) },
57909
- { method: "POST", pattern: "/api/watch/start", handler: (q, s) => this.handleWatchStart(q, s) },
57910
- { method: "POST", pattern: "/api/watch/stop", handler: (q, s) => this.handleWatchStop(q, s) },
57911
- { method: "GET", pattern: "/api/watch/events", handler: (q, s) => this.handleSSE(q, s) },
57912
- { method: "POST", pattern: "/api/scaffold", handler: (q, s) => this.handleScaffold(q, s) },
58320
+ { method: "GET", pattern: "/api/providers", handler: (q, s2) => this.handleListProviders(q, s2) },
58321
+ { method: "GET", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleGetProviderSourceConfig(q, s2) },
58322
+ { method: "POST", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleSetProviderSourceConfig(q, s2) },
58323
+ { method: "GET", pattern: "/api/providers/versions", handler: (q, s2) => this.handleDetectVersions(q, s2) },
58324
+ { method: "POST", pattern: "/api/providers/reload", handler: (q, s2) => this.handleReload(q, s2) },
58325
+ { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s2) => this.handleCdpEvaluate(q, s2) },
58326
+ { method: "POST", pattern: "/api/cdp/click", handler: (q, s2) => this.handleCdpClick(q, s2) },
58327
+ { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s2) => this.handleCdpDomQuery(q, s2) },
58328
+ { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s2) => this.handleDomInspect(q, s2) },
58329
+ { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s2) => this.handleDomChildren(q, s2) },
58330
+ { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s2) => this.handleDomAnalyze(q, s2) },
58331
+ { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s2) => this.handleFindByText(q, s2) },
58332
+ { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s2) => this.handleFindCommon(q, s2) },
58333
+ { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s2) => this.handleScreenshot(q, s2) },
58334
+ { method: "GET", pattern: "/api/cdp/targets", handler: (q, s2) => this.handleCdpTargets(q, s2) },
58335
+ { method: "POST", pattern: "/api/scripts/run", handler: (q, s2) => this.handleScriptsRun(q, s2) },
58336
+ { method: "GET", pattern: "/api/status", handler: (q, s2) => this.handleStatus(q, s2) },
58337
+ { method: "POST", pattern: "/api/watch/start", handler: (q, s2) => this.handleWatchStart(q, s2) },
58338
+ { method: "POST", pattern: "/api/watch/stop", handler: (q, s2) => this.handleWatchStop(q, s2) },
58339
+ { method: "GET", pattern: "/api/watch/events", handler: (q, s2) => this.handleSSE(q, s2) },
58340
+ { method: "POST", pattern: "/api/scaffold", handler: (q, s2) => this.handleScaffold(q, s2) },
57913
58341
  // CLI Debug routes
57914
- { method: "GET", pattern: "/api/cli/status", handler: (q, s) => this.handleCliStatus(q, s) },
57915
- { method: "POST", pattern: "/api/cli/launch", handler: (q, s) => this.handleCliLaunch(q, s) },
57916
- { method: "POST", pattern: "/api/cli/send", handler: (q, s) => this.handleCliSend(q, s) },
57917
- { method: "POST", pattern: "/api/cli/exercise", handler: (q, s) => this.handleCliExercise(q, s) },
57918
- { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s) => this.handleCliFixtureCapture(q, s) },
57919
- { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s) => this.handleCliFixtureReplay(q, s) },
57920
- { method: "POST", pattern: "/api/cli/resolve", handler: (q, s) => this.handleCliResolve(q, s) },
57921
- { method: "POST", pattern: "/api/cli/raw", handler: (q, s) => this.handleCliRaw(q, s) },
57922
- { method: "POST", pattern: "/api/cli/stop", handler: (q, s) => this.handleCliStop(q, s) },
57923
- { method: "GET", pattern: "/api/cli/events", handler: (q, s) => this.handleCliSSE(q, s) },
57924
- { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s, p) => this.handleCliDebug(p[0], q, s) },
57925
- { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s, p) => this.handleCliTrace(p[0], q, s) },
57926
- { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s, p) => this.handleCliFixtureList(p[0], q, s) },
58342
+ { method: "GET", pattern: "/api/cli/status", handler: (q, s2) => this.handleCliStatus(q, s2) },
58343
+ { method: "POST", pattern: "/api/cli/launch", handler: (q, s2) => this.handleCliLaunch(q, s2) },
58344
+ { method: "POST", pattern: "/api/cli/send", handler: (q, s2) => this.handleCliSend(q, s2) },
58345
+ { method: "POST", pattern: "/api/cli/exercise", handler: (q, s2) => this.handleCliExercise(q, s2) },
58346
+ { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s2) => this.handleCliFixtureCapture(q, s2) },
58347
+ { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s2) => this.handleCliFixtureReplay(q, s2) },
58348
+ { method: "POST", pattern: "/api/cli/resolve", handler: (q, s2) => this.handleCliResolve(q, s2) },
58349
+ { method: "POST", pattern: "/api/cli/raw", handler: (q, s2) => this.handleCliRaw(q, s2) },
58350
+ { method: "POST", pattern: "/api/cli/stop", handler: (q, s2) => this.handleCliStop(q, s2) },
58351
+ { method: "GET", pattern: "/api/cli/events", handler: (q, s2) => this.handleCliSSE(q, s2) },
58352
+ { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s2, p) => this.handleCliDebug(p[0], q, s2) },
58353
+ { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s2, p) => this.handleCliTrace(p[0], q, s2) },
58354
+ { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s2, p) => this.handleCliFixtureList(p[0], q, s2) },
57927
58355
  // Dynamic routes (provider :type param)
57928
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s, p) => this.handleRunScript(p[0], q, s) },
57929
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s, p) => this.handleListFiles(p[0], q, s) },
57930
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleReadFile(p[0], q, s) },
57931
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleWriteFile(p[0], q, s) },
57932
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s, p) => this.handleSource(p[0], q, s) },
57933
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s, p) => this.handleSave(p[0], q, s) },
57934
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s, p) => this.handleTypeAndSend(p[0], q, s) },
57935
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s, p) => this.handleTypeAndSendAt(p[0], q, s) },
57936
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s, p) => this.handleProviderConfig(p[0], q, s) },
57937
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s, p) => this.handleDomContext(p[0], q, s) },
57938
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s, p) => this.handleAutoImplement(p[0], q, s) },
57939
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s, p) => this.handleAutoImplCancel(p[0], q, s) },
57940
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s, p) => this.handleAutoImplSSE(p[0], q, s) },
57941
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s, p) => this.handleSpawnTest(p[0], q, s) },
57942
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s, p) => this.handleValidate(p[0], q, s) },
57943
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s, p) => this.handleAcpChat(p[0], q, s) },
57944
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s, p) => this.handleScriptHints(p[0], q, s) }
58356
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s2, p) => this.handleRunScript(p[0], q, s2) },
58357
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s2, p) => this.handleListFiles(p[0], q, s2) },
58358
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleReadFile(p[0], q, s2) },
58359
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleWriteFile(p[0], q, s2) },
58360
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s2, p) => this.handleSource(p[0], q, s2) },
58361
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s2, p) => this.handleSave(p[0], q, s2) },
58362
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s2, p) => this.handleTypeAndSend(p[0], q, s2) },
58363
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s2, p) => this.handleTypeAndSendAt(p[0], q, s2) },
58364
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s2, p) => this.handleProviderConfig(p[0], q, s2) },
58365
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s2, p) => this.handleDomContext(p[0], q, s2) },
58366
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s2, p) => this.handleAutoImplement(p[0], q, s2) },
58367
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s2, p) => this.handleAutoImplCancel(p[0], q, s2) },
58368
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s2, p) => this.handleAutoImplSSE(p[0], q, s2) },
58369
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s2, p) => this.handleSpawnTest(p[0], q, s2) },
58370
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s2, p) => this.handleValidate(p[0], q, s2) },
58371
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s2, p) => this.handleAcpChat(p[0], q, s2) },
58372
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s2, p) => this.handleScriptHints(p[0], q, s2) }
57945
58373
  ];
57946
58374
  matchRoute(method, pathname) {
57947
58375
  for (const route of this.routes) {
@@ -58536,14 +58964,14 @@ var DevServer = class _DevServer {
58536
58964
  warnings.push(...validation.warnings);
58537
58965
  if (config.settings) {
58538
58966
  for (const [key, val] of Object.entries(config.settings)) {
58539
- const s = val;
58540
- if (!s.type) errors.push(`settings.${key}: missing type`);
58541
- else if (!["boolean", "number", "string", "select"].includes(s.type))
58542
- errors.push(`settings.${key}: invalid type '${s.type}'`);
58543
- if (s.default === void 0) warnings.push(`settings.${key}: no default value`);
58544
- if (s.type === "number" && s.min !== void 0 && s.max !== void 0 && s.min > s.max)
58545
- errors.push(`settings.${key}: min (${s.min}) > max (${s.max})`);
58546
- if (s.type === "select" && (!s.options || !Array.isArray(s.options) || s.options.length === 0))
58967
+ const s2 = val;
58968
+ if (!s2.type) errors.push(`settings.${key}: missing type`);
58969
+ else if (!["boolean", "number", "string", "select"].includes(s2.type))
58970
+ errors.push(`settings.${key}: invalid type '${s2.type}'`);
58971
+ if (s2.default === void 0) warnings.push(`settings.${key}: no default value`);
58972
+ if (s2.type === "number" && s2.min !== void 0 && s2.max !== void 0 && s2.min > s2.max)
58973
+ errors.push(`settings.${key}: min (${s2.min}) > max (${s2.max})`);
58974
+ if (s2.type === "select" && (!s2.options || !Array.isArray(s2.options) || s2.options.length === 0))
58547
58975
  errors.push(`settings.${key}: select type requires options[]`);
58548
58976
  }
58549
58977
  }