@adhdev/daemon-core 0.9.82-rc.353 → 0.9.82-rc.355

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/commands/handler.d.ts +15 -0
  2. package/dist/index.js +703 -220
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +703 -220
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/mesh/mesh-event-trace.d.ts +21 -0
  7. package/dist/mesh/mesh-runtime-store.d.ts +1 -1
  8. package/dist/mesh/mesh-work-queue.d.ts +1 -1
  9. package/dist/providers/acp-provider-instance.d.ts +3 -0
  10. package/dist/providers/cli-provider-instance.d.ts +14 -0
  11. package/dist/providers/manual-attendance.d.ts +63 -0
  12. package/dist/providers/provider-instance.d.ts +8 -0
  13. package/dist/providers/spec/adapter.d.ts +22 -0
  14. package/dist/providers/spec/fsm-driver.d.ts +49 -7
  15. package/dist/providers/spec/fsm-evaluator.d.ts +4 -0
  16. package/dist/providers/spec/types.d.ts +9 -5
  17. package/package.json +2 -2
  18. package/src/commands/cli-manager.ts +20 -2
  19. package/src/commands/handler.ts +32 -0
  20. package/src/commands/router.ts +19 -6
  21. package/src/git/git-diff.ts +31 -14
  22. package/src/mesh/mesh-event-trace.ts +67 -0
  23. package/src/mesh/mesh-events-coordinator.ts +117 -12
  24. package/src/mesh/mesh-events-pending.ts +33 -0
  25. package/src/mesh/mesh-events-stale.ts +3 -1
  26. package/src/mesh/mesh-reconcile-loop.ts +47 -0
  27. package/src/mesh/mesh-runtime-store.ts +18 -2
  28. package/src/mesh/mesh-work-queue.ts +8 -1
  29. package/src/providers/acp-provider-instance.ts +18 -1
  30. package/src/providers/cli-provider-instance.ts +123 -7
  31. package/src/providers/manual-attendance.ts +85 -0
  32. package/src/providers/provider-instance.ts +9 -0
  33. package/src/providers/spec/adapter.ts +67 -0
  34. package/src/providers/spec/cli-adapter.ts +6 -0
  35. package/src/providers/spec/evaluator.ts +24 -9
  36. package/src/providers/spec/fsm-driver.ts +135 -13
  37. package/src/providers/spec/fsm-evaluator.ts +19 -2
  38. package/src/providers/spec/types.ts +9 -5
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 ? "a680b74d9b940b82f691ebb85a725a1a72445bfc" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "a680b74d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.353" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-22T09:11:13.499Z" : void 0);
319
+ const commit = readInjected(true ? "a45106605e2ae1c10c0bc6cbe48c2cac4e862ded" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "a4510660" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.355" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-22T15:21:19.981Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -1070,6 +1070,9 @@ __export(git_diff_exports, {
1070
1070
  getGitDiffSummary: () => getGitDiffSummary,
1071
1071
  getGitFileDiff: () => getGitFileDiff
1072
1072
  });
1073
+ function withCollectionTimeout(options) {
1074
+ return options.timeoutMs === void 0 ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
1075
+ }
1073
1076
  function validateBaseRef(ref) {
1074
1077
  const trimmed = ref.trim();
1075
1078
  if (!trimmed || trimmed.startsWith("-") || trimmed.includes("..") || !/^[A-Za-z0-9][A-Za-z0-9._/@-]*$/.test(trimmed)) {
@@ -1079,14 +1082,15 @@ function validateBaseRef(ref) {
1079
1082
  }
1080
1083
  async function getGitDiffSummary(workspace, options = {}) {
1081
1084
  const lastCheckedAt = Date.now();
1085
+ const effectiveOptions = withCollectionTimeout(options);
1082
1086
  try {
1083
- const repo = await resolveGitRepository(workspace, options);
1087
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1084
1088
  const repoRoot = repo.repoRoot;
1085
1089
  if (options.baseRef) {
1086
1090
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1087
1091
  const [nameStatus, numstat] = await Promise.all([
1088
- runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...options, cwd: repoRoot }),
1089
- runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...options, cwd: repoRoot })
1092
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...effectiveOptions, cwd: repoRoot }),
1093
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...effectiveOptions, cwd: repoRoot })
1090
1094
  ]);
1091
1095
  const outputBytes2 = byteLength(nameStatus.stdout + numstat.stdout);
1092
1096
  const changes2 = combineDiffEntries(nameStatus.stdout, numstat.stdout, false);
@@ -1105,11 +1109,11 @@ async function getGitDiffSummary(workspace, options = {}) {
1105
1109
  };
1106
1110
  }
1107
1111
  const [unstagedNameStatus, unstagedNumstat, stagedNameStatus, stagedNumstat, untracked] = await Promise.all([
1108
- runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1109
- runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1110
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1111
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1112
- runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...options, cwd: repoRoot })
1112
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1113
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1114
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1115
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1116
+ runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...effectiveOptions, cwd: repoRoot })
1113
1117
  ]);
1114
1118
  const outputBytes = byteLength(
1115
1119
  unstagedNameStatus.stdout + unstagedNumstat.stdout + stagedNameStatus.stdout + stagedNumstat.stdout + untracked.stdout
@@ -1151,13 +1155,14 @@ async function getGitDiffSummary(workspace, options = {}) {
1151
1155
  }
1152
1156
  async function getGitFileDiff(workspace, filePath, options = {}) {
1153
1157
  const lastCheckedAt = Date.now();
1154
- const repo = await resolveGitRepository(workspace, options);
1158
+ const effectiveOptions = withCollectionTimeout(options);
1159
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1155
1160
  const repoRoot = repo.repoRoot;
1156
1161
  const selected = await resolveRepoFilePath(repoRoot, filePath);
1157
1162
  const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
1158
1163
  if (options.baseRef) {
1159
1164
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1160
- const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...options, cwd: repoRoot });
1165
+ const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot });
1161
1166
  const bounded2 = truncateText(result.stdout, maxBytes);
1162
1167
  return {
1163
1168
  workspace: repo.workspace,
@@ -1170,13 +1175,13 @@ async function getGitFileDiff(workspace, filePath, options = {}) {
1170
1175
  };
1171
1176
  }
1172
1177
  const [unstaged, staged] = await Promise.all([
1173
- runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot }),
1174
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot })
1178
+ runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot }),
1179
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot })
1175
1180
  ]);
1176
1181
  let diff = [unstaged.stdout, staged.stdout].filter((part) => part.length > 0).join("\n");
1177
1182
  if (!diff) {
1178
1183
  const untracked = await runGit(repo, ["ls-files", "--others", "--exclude-standard", "--", selected.relativePath], {
1179
- ...options,
1184
+ ...effectiveOptions,
1180
1185
  cwd: repoRoot
1181
1186
  });
1182
1187
  const untrackedFiles = untracked.stdout.split("\n").filter(Boolean);
@@ -4822,9 +4827,12 @@ function getActiveDirectDispatches(meshId) {
4822
4827
  return [];
4823
4828
  }
4824
4829
  }
4825
- function updateDirectDispatchStatus(meshId, sessionId, status) {
4830
+ function updateDirectDispatchStatus(meshId, sessionId, status, taskId) {
4826
4831
  try {
4827
- 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);
4828
4836
  } catch {
4829
4837
  }
4830
4838
  }
@@ -5657,9 +5665,25 @@ var init_mesh_runtime_store = __esm({
5657
5665
  updatedAt: r.updated_at
5658
5666
  }));
5659
5667
  }
5660
- updateDirectDispatchStatus(meshId, sessionId, status) {
5661
- 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) {
5662
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;
5663
5687
  this.db.prepare(`
5664
5688
  UPDATE mesh_direct_dispatches
5665
5689
  SET status = @status, updated_at = @updatedAt
@@ -7385,7 +7409,7 @@ function resolveWin32Executable(command) {
7385
7409
  windowsHide: true
7386
7410
  }).trim();
7387
7411
  if (out) {
7388
- 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);
7389
7413
  const direct = matches.find((m) => DIRECT_EXEC_EXT.has(path10.extname(m).toLowerCase()));
7390
7414
  return direct || matches[0] || command;
7391
7415
  }
@@ -8737,11 +8761,29 @@ function hasPendingRefineTerminalEventDuplicate(event) {
8737
8761
  (pending) => pending.event === event.event && readRefineJobId2(pending) === jobId
8738
8762
  );
8739
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
+ }
8740
8771
  function buildPendingEventFingerprint(event) {
8741
8772
  const metadata = readRecord4(event.metadataEvent) || {};
8742
8773
  if (event.event === "worktree_bootstrap_complete" || event.event === "worktree_bootstrap_failed") {
8743
8774
  return [event.meshId, event.event, event.nodeId || ""].join("::");
8744
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
+ }
8745
8787
  const sessionId = resolveEventSessionId(metadata);
8746
8788
  const providerSessionId = readNonEmptyString2(metadata.providerSessionId);
8747
8789
  const taskId = readNonEmptyString2(metadata.taskId) || readNonEmptyString2(readRecord4(metadata.payload)?.taskId);
@@ -9100,7 +9142,7 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
9100
9142
  }
9101
9143
  }
9102
9144
  }
9103
- 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;
9104
9146
  var init_mesh_events_pending = __esm({
9105
9147
  "src/mesh/mesh-events-pending.ts"() {
9106
9148
  "use strict";
@@ -9113,6 +9155,7 @@ var init_mesh_events_pending = __esm({
9113
9155
  init_mesh_events_utils();
9114
9156
  init_dist();
9115
9157
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9158
+ TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
9116
9159
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9117
9160
  MAX_PENDING_EVENTS_KEEP = 50;
9118
9161
  }
@@ -9442,7 +9485,7 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
9442
9485
  evidence
9443
9486
  }
9444
9487
  });
9445
- updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9488
+ updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed", args.taskId);
9446
9489
  markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9447
9490
  setImmediate(() => cleanupTerminalDirectDispatches());
9448
9491
  queuePendingMeshCoordinatorEvent({
@@ -9732,8 +9775,8 @@ function parsePatternEntry(x) {
9732
9775
  if (x instanceof RegExp) return x;
9733
9776
  if (x && typeof x === "object" && typeof x.source === "string") {
9734
9777
  try {
9735
- const s = x;
9736
- return new RegExp(s.source, s.flags || "");
9778
+ const s2 = x;
9779
+ return new RegExp(s2.source, s2.flags || "");
9737
9780
  } catch {
9738
9781
  return null;
9739
9782
  }
@@ -10304,6 +10347,38 @@ var init_mesh_unresolved_forward_outbox = __esm({
10304
10347
  }
10305
10348
  });
10306
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
+
10307
10382
  // src/config/state-store.ts
10308
10383
  function isPlainObject2(value) {
10309
10384
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -12089,9 +12164,9 @@ function buildAcpSession(state, options) {
12089
12164
  }
12090
12165
  function buildSessionEntries(allStates, cdpManagers, options = {}) {
12091
12166
  const sessions = [];
12092
- const ideStates = allStates.filter((s) => s.category === "ide");
12093
- const cliStates = allStates.filter((s) => s.category === "cli");
12094
- 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");
12095
12170
  for (const state of ideStates) {
12096
12171
  sessions.push(buildWorkspaceSession(state, cdpManagers, options));
12097
12172
  for (const ext of state.extensions) {
@@ -12575,6 +12650,15 @@ function getCachedMeshByWorkspace(workspace) {
12575
12650
  meshByWorkspaceCache.set(workspace, { mesh, cachedAt: now });
12576
12651
  return mesh;
12577
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
+ }
12578
12662
  function __resetIdleAutoFastForwardForTests() {
12579
12663
  idleAutoFastForwardLastAttempt.clear();
12580
12664
  }
@@ -13477,6 +13561,13 @@ function shouldForceInjectMeshEvent(eventName) {
13477
13561
  function injectMeshSystemMessage(components, args) {
13478
13562
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
13479
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
+ };
13480
13571
  const sourceSession = args.sourceInstanceId ? components.instanceManager.getInstance(args.sourceInstanceId) : void 0;
13481
13572
  const workerCoordinatorDaemonId = readNonEmptyString2(
13482
13573
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
@@ -13530,6 +13621,7 @@ function injectMeshSystemMessage(components, args) {
13530
13621
  }
13531
13622
  }
13532
13623
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
13624
+ traceMeshEventDrop("intentional_cleanup_stop", traceCtx);
13533
13625
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
13534
13626
  }
13535
13627
  if (args.event === "monitor:no_progress") {
@@ -13550,6 +13642,7 @@ function injectMeshSystemMessage(components, args) {
13550
13642
  }
13551
13643
  if (reconciledCompletion?.source === "no_progress_terminal_ledger_suppression") {
13552
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}`);
13553
13646
  return {
13554
13647
  success: true,
13555
13648
  forwarded: 0,
@@ -13561,6 +13654,7 @@ function injectMeshSystemMessage(components, args) {
13561
13654
  }
13562
13655
  if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
13563
13656
  LOG.info("MeshEvents", `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
13657
+ traceMeshEventDrop("duplicate_refine_terminal", traceCtx);
13564
13658
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
13565
13659
  }
13566
13660
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
@@ -13575,6 +13669,7 @@ function injectMeshSystemMessage(components, args) {
13575
13669
  });
13576
13670
  if (duplicateApproval) {
13577
13671
  LOG.info("MeshEvents", `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
13672
+ traceMeshEventDrop("duplicate_approval", traceCtx);
13578
13673
  return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
13579
13674
  }
13580
13675
  }
@@ -13594,6 +13689,7 @@ function injectMeshSystemMessage(components, args) {
13594
13689
  const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
13595
13690
  if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "no_progress_reconciliation") {
13596
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);
13597
13693
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
13598
13694
  }
13599
13695
  }
@@ -13612,6 +13708,7 @@ function injectMeshSystemMessage(components, args) {
13612
13708
  });
13613
13709
  if (duplicateCompletion) {
13614
13710
  LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
13711
+ traceMeshEventDrop("duplicate_completion", traceCtx);
13615
13712
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
13616
13713
  }
13617
13714
  }
@@ -13630,6 +13727,7 @@ function injectMeshSystemMessage(components, args) {
13630
13727
  });
13631
13728
  if (duplicateStopped) {
13632
13729
  LOG.info("MeshEvents", `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
13730
+ traceMeshEventDrop("duplicate_stopped", traceCtx);
13633
13731
  return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
13634
13732
  }
13635
13733
  }
@@ -13641,7 +13739,7 @@ function injectMeshSystemMessage(components, args) {
13641
13739
  });
13642
13740
  const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
13643
13741
  if (!leaveDirectDispatchActive) {
13644
- updateDirectDispatchStatus(args.meshId, sessionId, outcome);
13742
+ updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
13645
13743
  }
13646
13744
  markSessionDeliveriesTerminal(args.meshId, sessionId, outcome);
13647
13745
  setImmediate(() => cleanupTerminalDirectDispatches());
@@ -13654,7 +13752,7 @@ function injectMeshSystemMessage(components, args) {
13654
13752
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
13655
13753
  const providerType = readNonEmptyString2(args.metadataEvent.providerType);
13656
13754
  if (sessionId) {
13657
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13755
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13658
13756
  const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
13659
13757
  completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
13660
13758
  if (nodeId && providerType) {
@@ -13737,7 +13835,8 @@ function injectMeshSystemMessage(components, args) {
13737
13835
  }
13738
13836
  }
13739
13837
  if (sessionId) {
13740
- updateDirectDispatchStatus(args.meshId, sessionId, "acked");
13838
+ const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
13839
+ updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
13741
13840
  const activeDeliveries = (() => {
13742
13841
  try {
13743
13842
  return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(args.meshId, sessionId);
@@ -13745,7 +13844,8 @@ function injectMeshSystemMessage(components, args) {
13745
13844
  return [];
13746
13845
  }
13747
13846
  })();
13748
- for (const d of activeDeliveries) {
13847
+ const deliveriesToAck = startedTaskId ? activeDeliveries.filter((d) => d.taskId === startedTaskId) : activeDeliveries;
13848
+ for (const d of deliveriesToAck) {
13749
13849
  updateSessionDeliveryStatus(d.id, "acked");
13750
13850
  }
13751
13851
  }
@@ -13759,7 +13859,7 @@ function injectMeshSystemMessage(components, args) {
13759
13859
  }
13760
13860
  }
13761
13861
  if (sessionId) {
13762
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13862
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13763
13863
  completedTaskForLedger = markSessionTerminal(sessionId, "failed");
13764
13864
  }
13765
13865
  }
@@ -13905,6 +14005,9 @@ function injectMeshSystemMessage(components, args) {
13905
14005
  };
13906
14006
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
13907
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);
13908
14011
  }
13909
14012
  return { success: true, forwarded: 0 };
13910
14013
  }
@@ -13915,8 +14018,23 @@ function handleMeshForwardEvent(components, payload) {
13915
14018
  }
13916
14019
  const nodeId = readNonEmptyString2(payload.nodeId);
13917
14020
  const workspace = readNonEmptyString2(payload.workspace);
13918
- const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "");
13919
- 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
+ });
13920
14038
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
13921
14039
  const relayModalMessage = readNonEmptyString2(payload.modalMessage);
13922
14040
  const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
@@ -13997,9 +14115,18 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
13997
14115
  workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
13998
14116
  };
13999
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");
14000
14126
  Promise.resolve(components.dispatchMeshCommand(coordinatorDaemonId, "mesh_forward_event", payload)).then((result) => {
14001
14127
  if (result && result.success === false) {
14002
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");
14003
14130
  return;
14004
14131
  }
14005
14132
  if (persisted) ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload);
@@ -14067,6 +14194,14 @@ function setupMeshEventForwarding(components) {
14067
14194
  if (isUnroutableDelegateRejection(routing) && forwardUnresolvedDelegateEvent(components, routing, event)) {
14068
14195
  return;
14069
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
+ }
14070
14205
  recordUnroutableDelegateEvent(routing, event.event);
14071
14206
  return;
14072
14207
  }
@@ -14097,6 +14232,7 @@ var init_mesh_events_coordinator = __esm({
14097
14232
  init_mesh_events_pending();
14098
14233
  init_mesh_routing();
14099
14234
  init_mesh_unresolved_forward_outbox();
14235
+ init_mesh_event_trace();
14100
14236
  init_snapshot();
14101
14237
  init_repo_mesh_types();
14102
14238
  init_dist();
@@ -14208,6 +14344,13 @@ function findLiveCoordinators(components) {
14208
14344
  function injectPendingIntoCoordinator(coordinator, pending) {
14209
14345
  if (!coordinator || !pending.coordinatorMessage) return;
14210
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");
14211
14354
  coordinator.onEvent("send_message", {
14212
14355
  input: { text: pending.coordinatorMessage, textFallback: pending.coordinatorMessage },
14213
14356
  ...force ? { force: true } : {}
@@ -14266,6 +14409,13 @@ function recoverStrandedAssignedDispatches(meshId, store) {
14266
14409
  });
14267
14410
  if (reclaimed) {
14268
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}`);
14269
14419
  }
14270
14420
  }
14271
14421
  }
@@ -14425,6 +14575,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14425
14575
  try {
14426
14576
  queuePendingMeshCoordinatorEvent(pending);
14427
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`);
14428
14585
  } catch (e) {
14429
14586
  LOG.warn("MeshReconcile", `Strict route re-queue failed for ${pending.event} on mesh ${meshId}: ${e?.message || e}`);
14430
14587
  }
@@ -14448,6 +14605,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14448
14605
  }
14449
14606
  });
14450
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`);
14451
14615
  } catch (e) {
14452
14616
  LOG.warn("MeshReconcile", `Failed to ledger-expire strict-unmatched ${pending.event} for mesh ${meshId}: ${e?.message || e}`);
14453
14617
  }
@@ -14459,15 +14623,24 @@ async function retryUnresolvedDelegateForwards(components) {
14459
14623
  const entries = peekUnresolvedDelegateForwards();
14460
14624
  if (entries.length === 0) return;
14461
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
+ };
14462
14632
  let result;
14463
14633
  try {
14634
+ traceMeshEventStage("forward_send", entryTraceCtx, `retry \u2192 ${entry.coordinatorDaemonId}`);
14464
14635
  result = await dispatchMeshCommand(entry.coordinatorDaemonId, "mesh_forward_event", entry.payload);
14465
14636
  } catch (e) {
14466
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));
14467
14639
  continue;
14468
14640
  }
14469
14641
  if (result && result.success === false) {
14470
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");
14471
14644
  continue;
14472
14645
  }
14473
14646
  ackUnresolvedDelegateForward(entry.id);
@@ -14709,6 +14882,7 @@ var init_mesh_reconcile_loop = __esm({
14709
14882
  init_mesh_events_coordinator();
14710
14883
  init_mesh_unresolved_forward_outbox();
14711
14884
  init_mesh_events_utils();
14885
+ init_mesh_event_trace();
14712
14886
  init_dist();
14713
14887
  init_mesh_work_queue();
14714
14888
  init_mesh_ledger();
@@ -15544,8 +15718,8 @@ function saveProvidersActive(file) {
15544
15718
  }
15545
15719
  function isValidSource(x) {
15546
15720
  if (!x || typeof x !== "object") return false;
15547
- const s = x;
15548
- 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";
15549
15723
  }
15550
15724
  function deriveSourceName(url) {
15551
15725
  const m = url.match(/[/:]([^/:]+)\/([^/]+?)(?:\.git)?$/);
@@ -15601,7 +15775,7 @@ function inventoryExternalSources() {
15601
15775
  }
15602
15776
  function sourcesProviding(category, type) {
15603
15777
  const inventory = inventoryExternalSources();
15604
- 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);
15605
15779
  }
15606
15780
  function resolveActiveSource(category, type, activeFile) {
15607
15781
  const candidates = sourcesProviding(category, type);
@@ -15808,10 +15982,10 @@ function compileSettledPromptMatchers(spec) {
15808
15982
  const footers = (spec.withFooter ?? []).map((f) => {
15809
15983
  if (f.kind === "regex") {
15810
15984
  const re = compile2(f.pattern, f.flags ?? "i");
15811
- return { test: (s) => re.test(s) };
15985
+ return { test: (s2) => re.test(s2) };
15812
15986
  }
15813
15987
  const needle = f.pattern.toLowerCase();
15814
- return { test: (s) => s.toLowerCase().includes(needle) };
15988
+ return { test: (s2) => s2.toLowerCase().includes(needle) };
15815
15989
  });
15816
15990
  return { prompt, footers };
15817
15991
  }
@@ -16685,7 +16859,7 @@ var init_cli_state_engine = __esm({
16685
16859
  }
16686
16860
  resolveModal(buttonIndex) {
16687
16861
  const snap = this.transport.getSnapshot();
16688
- 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);
16689
16863
  let modal = this.activeModal ?? parseApproval(snap);
16690
16864
  if (!modal && this.runner.hasParseSession()) {
16691
16865
  try {
@@ -19374,22 +19548,23 @@ function resolveSections(sectionsObj, lines) {
19374
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]));
19375
19549
  let idx = -1;
19376
19550
  for (const c of candidates) {
19551
+ let candIdx = -1;
19377
19552
  if (sec.anchor_last) {
19378
19553
  for (let i = total - 1; i >= 0; i--) {
19379
19554
  if (matchesCandidate(c, i)) {
19380
- idx = i;
19555
+ candIdx = i;
19381
19556
  break;
19382
19557
  }
19383
19558
  }
19384
19559
  } else {
19385
19560
  for (let i = 0; i < total; i++) {
19386
19561
  if (matchesCandidate(c, i)) {
19387
- idx = i;
19562
+ candIdx = i;
19388
19563
  break;
19389
19564
  }
19390
19565
  }
19391
19566
  }
19392
- if (idx !== -1) break;
19567
+ if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
19393
19568
  }
19394
19569
  if (idx !== -1) {
19395
19570
  from = idx;
@@ -19441,7 +19616,7 @@ function resolveSections(sectionsObj, lines) {
19441
19616
  }
19442
19617
  function sectionText(sections, sectionId, fullScreen) {
19443
19618
  if (!sectionId) return fullScreen;
19444
- const found = sections.find((s) => s.id === sectionId);
19619
+ const found = sections.find((s2) => s2.id === sectionId);
19445
19620
  return found ? found.text : "";
19446
19621
  }
19447
19622
  function isRegexCondition(c) {
@@ -19609,10 +19784,10 @@ function isV4Spec(raw) {
19609
19784
  return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
19610
19785
  }
19611
19786
  function initialState(spec) {
19612
- return spec.states.find((s) => s.initial) ?? spec.states[0];
19787
+ return spec.states.find((s2) => s2.initial) ?? spec.states[0];
19613
19788
  }
19614
19789
  function stateById(spec, id) {
19615
- return spec.states.find((s) => s.id === id);
19790
+ return spec.states.find((s2) => s2.id === id);
19616
19791
  }
19617
19792
  function outgoingTransitions(spec, stateId) {
19618
19793
  const matches = spec.transitions.filter((t) => {
@@ -19701,7 +19876,17 @@ function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTr
19701
19876
  const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
19702
19877
  const kind = isRegex(cond) ? "regex" : "changed";
19703
19878
  const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
19704
- return { kind, result, detail };
19879
+ let matchedText;
19880
+ if (result && isRegex(cond)) {
19881
+ try {
19882
+ const hay = sectionText(sections, cond.section, fullScreen);
19883
+ const re = new RegExp(cond.matches, cond.flags ?? "i");
19884
+ const m = re.exec(hay);
19885
+ if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
19886
+ } catch {
19887
+ }
19888
+ }
19889
+ return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
19705
19890
  }
19706
19891
  return { kind: "all", result: false, detail: "unknown condition" };
19707
19892
  }
@@ -19816,17 +20001,17 @@ function validateFsmSpec(raw) {
19816
20001
  }
19817
20002
  const ids = /* @__PURE__ */ new Set();
19818
20003
  let initialCount = 0;
19819
- for (const [i, s] of spec.states.entries()) {
19820
- if (!s.id) {
20004
+ for (const [i, s2] of spec.states.entries()) {
20005
+ if (!s2.id) {
19821
20006
  errs.push(`states[${i}].id is required`);
19822
20007
  continue;
19823
20008
  }
19824
- if (ids.has(s.id)) errs.push(`states[${i}].id "${s.id}" is duplicated`);
19825
- ids.add(s.id);
19826
- if (!s.label) errs.push(`states[${i}].label is required`);
19827
- if (s.initial) initialCount += 1;
19828
- if (s.status && !["idle", "generating", "approval"].includes(s.status)) {
19829
- errs.push(`states[${i}].status "${s.status}" must be idle|generating|approval`);
20009
+ if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
20010
+ ids.add(s2.id);
20011
+ if (!s2.label) errs.push(`states[${i}].label is required`);
20012
+ if (s2.initial) initialCount += 1;
20013
+ if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
20014
+ errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
19830
20015
  }
19831
20016
  }
19832
20017
  if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
@@ -19842,10 +20027,10 @@ function validateFsmSpec(raw) {
19842
20027
  else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
19843
20028
  if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
19844
20029
  }
19845
- for (const [i, s] of spec.states.entries()) {
19846
- const sec = s.extract?.title?.section;
20030
+ for (const [i, s2] of spec.states.entries()) {
20031
+ const sec = s2.extract?.title?.section;
19847
20032
  if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
19848
- const bsec = s.extract?.buttons?.section;
20033
+ const bsec = s2.extract?.buttons?.section;
19849
20034
  if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
19850
20035
  }
19851
20036
  return errs;
@@ -27440,6 +27625,42 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
27440
27625
  return fn() || null;
27441
27626
  }
27442
27627
 
27628
+ // src/providers/manual-attendance.ts
27629
+ var AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS = 6e4;
27630
+ var ManualAttendanceTracker = class {
27631
+ constructor(suppressMs = AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS) {
27632
+ this.suppressMs = suppressMs;
27633
+ }
27634
+ lastInteractionAt = 0;
27635
+ /** Record that a human just drove this session by hand. */
27636
+ note(now = Date.now()) {
27637
+ this.lastInteractionAt = now;
27638
+ }
27639
+ /** True while a manual interaction is recent enough to suppress auto-approve. */
27640
+ isAttended(now = Date.now()) {
27641
+ return this.lastInteractionAt > 0 && now - this.lastInteractionAt < this.suppressMs;
27642
+ }
27643
+ /**
27644
+ * Milliseconds remaining in the current suppression window, or 0 when not
27645
+ * attended. Used to re-arm a re-check timer so auto-approve fires the moment
27646
+ * the window lapses even if the PTY/agent has since gone silent.
27647
+ */
27648
+ remainingMs(now = Date.now()) {
27649
+ if (this.lastInteractionAt <= 0) return 0;
27650
+ return Math.max(0, this.suppressMs - (now - this.lastInteractionAt));
27651
+ }
27652
+ };
27653
+ var MANUAL_ATTENDANCE_COMMANDS = /* @__PURE__ */ new Set([
27654
+ "select_session",
27655
+ "open_panel",
27656
+ "invoke_provider_script",
27657
+ "set_mode",
27658
+ "change_model",
27659
+ "set_thought_level",
27660
+ "resolve_action",
27661
+ "pty_input"
27662
+ ]);
27663
+
27443
27664
  // src/commands/chat-commands.ts
27444
27665
  var fs7 = __toESM(require("fs"));
27445
27666
  var os10 = __toESM(require("os"));
@@ -31779,11 +32000,38 @@ var DaemonCommandHandler = class {
31779
32000
  setAgentStreamManager(manager) {
31780
32001
  this._agentStream = manager;
31781
32002
  }
32003
+ /**
32004
+ * When a command in the manual-attendance set arrives for a session this
32005
+ * daemon hosts, stamp the live instance so auto-approve holds while the user
32006
+ * drives the session by hand. Provider-common: the signal is the command
32007
+ * (foreground select_session / open_panel, controlbar invoke_provider_script
32008
+ * / set_mode / change_model / set_thought_level, manual resolve_action,
32009
+ * pty_input), never any CLI-specific modal text — so it works identically for
32010
+ * every CLI/ACP provider. send_chat is deliberately excluded because a
32011
+ * coordinator delegating a task to a worker also uses send_chat; counting it
32012
+ * would wrongly suppress the worker's delegated auto-approve. For a remote
32013
+ * mesh worker session the controlbar commands are forwarded to the owning
32014
+ * worker daemon, which runs this same hook there, so attendance is recorded
32015
+ * on the daemon that actually hosts the instance.
32016
+ */
32017
+ noteManualAttendanceIfApplicable(cmd, args) {
32018
+ if (!MANUAL_ATTENDANCE_COMMANDS.has(cmd)) return;
32019
+ const sessionId = this._currentRoute.session?.sessionId || (typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "");
32020
+ if (!sessionId) return;
32021
+ const session = this._ctx.sessionRegistry?.get(sessionId);
32022
+ const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
32023
+ const instance = this._ctx.instanceManager?.getInstance(instanceKey);
32024
+ try {
32025
+ instance?.noteManualInteraction?.();
32026
+ } catch {
32027
+ }
32028
+ }
31782
32029
  // ─── Command Dispatcher ──────────────────────────
31783
32030
  async handle(cmd, args) {
31784
32031
  this._currentRoute = this.resolveRoute(args);
31785
32032
  const startedAt = Date.now();
31786
32033
  this.logCommandStart(cmd, args);
32034
+ this.noteManualAttendanceIfApplicable(cmd, args);
31787
32035
  let result;
31788
32036
  if (isGitCommandName(cmd)) {
31789
32037
  result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
@@ -32518,10 +32766,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32518
32766
  const path42 = require("path");
32519
32767
  const { spawnSync: spawnSync2 } = require("child_process");
32520
32768
  const file = ext.loadExternalSources();
32521
- if (file.sources.some((s) => s.name === requestedName)) {
32769
+ if (file.sources.some((s2) => s2.name === requestedName)) {
32522
32770
  return { success: false, error: `source name "${requestedName}" is already registered` };
32523
32771
  }
32524
- if (file.sources.some((s) => s.url === url && s.ref === ref)) {
32772
+ if (file.sources.some((s2) => s2.url === url && s2.ref === ref)) {
32525
32773
  return { success: false, error: `source url+ref already registered (use a different name to track another ref)` };
32526
32774
  }
32527
32775
  const sourceDir = path42.join(ext.externalRoot(), requestedName);
@@ -32583,7 +32831,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32583
32831
  const fs32 = require("fs");
32584
32832
  const path42 = require("path");
32585
32833
  const file = ext.loadExternalSources();
32586
- const match = file.sources.find((s) => s.name === name);
32834
+ const match = file.sources.find((s2) => s2.name === name);
32587
32835
  if (!match) return { success: false, error: `source "${name}" not registered` };
32588
32836
  const sourceDir = path42.join(ext.externalRoot(), name);
32589
32837
  if (fs32.existsSync(sourceDir)) {
@@ -32595,7 +32843,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32595
32843
  }
32596
32844
  ext.saveExternalSources({
32597
32845
  schema: 1,
32598
- sources: file.sources.filter((s) => s.name !== name)
32846
+ sources: file.sources.filter((s2) => s2.name !== name)
32599
32847
  });
32600
32848
  const active = ext.loadProvidersActive();
32601
32849
  const filteredActive = {};
@@ -32619,10 +32867,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32619
32867
  const file = ext.loadExternalSources();
32620
32868
  const inventory = ext.inventoryExternalSources();
32621
32869
  const active = ext.loadProvidersActive();
32622
- const sources = file.sources.map((s) => {
32623
- const inv = inventory.find((e) => e.sourceName === s.name);
32870
+ const sources = file.sources.map((s2) => {
32871
+ const inv = inventory.find((e) => e.sourceName === s2.name);
32624
32872
  return {
32625
- ...s,
32873
+ ...s2,
32626
32874
  providers: inv?.providers ?? {}
32627
32875
  };
32628
32876
  });
@@ -32799,6 +33047,21 @@ var path21 = __toESM(require("path"));
32799
33047
  // src/providers/spec/adapter.ts
32800
33048
  init_terminal_screen();
32801
33049
  var import_session_host_core6 = require("@adhdev/session-host-core");
33050
+ var MAX_PTY_EVENTS = 300;
33051
+ var EVENT_CONTENT_CAP = 240;
33052
+ function escapeControl(text) {
33053
+ return String(text).replace(/[\x00-\x1f\x7f]/g, (ch) => {
33054
+ const code = ch.charCodeAt(0);
33055
+ if (ch === "\r") return "\\r";
33056
+ if (ch === "\n") return "\\n";
33057
+ if (ch === " ") return "\\t";
33058
+ if (code === 27) return "\\x1b";
33059
+ return "\\x" + code.toString(16).padStart(2, "0");
33060
+ });
33061
+ }
33062
+ function capPreview(text) {
33063
+ return text.length > EVENT_CONTENT_CAP ? text.slice(0, EVENT_CONTENT_CAP) + `\u2026(+${text.length - EVENT_CONTENT_CAP})` : text;
33064
+ }
32802
33065
  var TerminalAdapter = class {
32803
33066
  constructor(opts, handlers) {
32804
33067
  this.opts = opts;
@@ -32825,6 +33088,9 @@ var TerminalAdapter = class {
32825
33088
  screenTimer = null;
32826
33089
  tickTimer = null;
32827
33090
  lastScreen = "";
33091
+ /** Debug-only ring buffer of PTY input/output/resize/cursor events. */
33092
+ events = [];
33093
+ lastCursorKey = "";
32828
33094
  start() {
32829
33095
  const env = this.opts.envIsComplete ? this.opts.env ?? {} : { ...process.env, ...this.opts.env ?? {} };
32830
33096
  this.pty = this.factory.spawn(this.opts.binary, this.opts.args ?? [], {
@@ -32833,10 +33099,12 @@ var TerminalAdapter = class {
32833
33099
  cols: this.cols,
32834
33100
  rows: this.rows
32835
33101
  });
33102
+ this.recordEvent("spawn", `${this.opts.binary} (${this.cols}x${this.rows})`);
32836
33103
  this.handlers.init?.({ pid: this.pty.pid });
32837
33104
  this.pty.onData((chunk) => this.onChunk(chunk));
32838
33105
  this.pty.onExit((info) => {
32839
33106
  this.stopTimers();
33107
+ this.recordEvent("exit", `exitCode=${typeof info.exitCode === "number" ? info.exitCode : 0}`);
32840
33108
  this.handlers.on_exit?.({ exitCode: typeof info.exitCode === "number" ? info.exitCode : 0 });
32841
33109
  this.pty = null;
32842
33110
  });
@@ -32847,6 +33115,7 @@ var TerminalAdapter = class {
32847
33115
  resize(cols, rows) {
32848
33116
  this.cols = cols;
32849
33117
  this.rows = rows;
33118
+ this.recordEvent("resize", `${cols}x${rows}`);
32850
33119
  this.pty?.resize(cols, rows);
32851
33120
  this.screen.resize(rows, cols);
32852
33121
  }
@@ -32867,8 +33136,21 @@ var TerminalAdapter = class {
32867
33136
  return { row: pos.row, col: pos.col };
32868
33137
  }
32869
33138
  send_keys(text) {
33139
+ this.recordEvent("input", capPreview(escapeControl(text)), text.length);
32870
33140
  this.pty?.write(text);
32871
33141
  }
33142
+ /** Debug-only: most-recent PTY input/output/resize/cursor events, oldest
33143
+ * first. Pure observation — never consulted by the FSM. */
33144
+ getEventTimeline(limit = MAX_PTY_EVENTS) {
33145
+ const n = Math.max(0, Math.min(limit, this.events.length));
33146
+ return this.events.slice(this.events.length - n);
33147
+ }
33148
+ recordEvent(kind, content, bytes) {
33149
+ const ev = { ts: Date.now(), kind, content };
33150
+ if (typeof bytes === "number") ev.bytes = bytes;
33151
+ this.events.push(ev);
33152
+ if (this.events.length > MAX_PTY_EVENTS) this.events.splice(0, this.events.length - MAX_PTY_EVENTS);
33153
+ }
32872
33154
  kill() {
32873
33155
  this.stopTimers();
32874
33156
  try {
@@ -32879,6 +33161,7 @@ var TerminalAdapter = class {
32879
33161
  this.screen.dispose();
32880
33162
  }
32881
33163
  onChunk(chunk) {
33164
+ this.recordEvent("output", capPreview(escapeControl(chunk)), chunk.length);
32882
33165
  try {
32883
33166
  this.handlers.on_pty_data?.(chunk);
32884
33167
  } catch {
@@ -32888,6 +33171,12 @@ var TerminalAdapter = class {
32888
33171
  this.screenTimer = setTimeout(() => {
32889
33172
  this.screenTimer = null;
32890
33173
  const snap = this.computeScreen();
33174
+ const cur = this.screen.getCursorPosition();
33175
+ const curKey = `${cur.row},${cur.col}`;
33176
+ if (curKey !== this.lastCursorKey) {
33177
+ this.lastCursorKey = curKey;
33178
+ this.recordEvent("cursor", `(${cur.row},${cur.col})`);
33179
+ }
32891
33180
  if (snap === this.lastScreen) return;
32892
33181
  this.lastScreen = snap;
32893
33182
  try {
@@ -32972,20 +33261,40 @@ function applyPreLaunchTrust(trust, workingDir) {
32972
33261
 
32973
33262
  // src/providers/spec/fsm-driver.ts
32974
33263
  init_logger();
32975
- function countNewlines(s) {
33264
+ function countNewlines(s2) {
32976
33265
  let n = 0;
32977
- for (let i = 0; i < s.length; i += 1) if (s.charCodeAt(i) === 10) n += 1;
33266
+ for (let i = 0; i < s2.length; i += 1) if (s2.charCodeAt(i) === 10) n += 1;
32978
33267
  return n;
32979
33268
  }
32980
33269
  var SUBMIT_DELAY_FLOOR_MS = 200;
32981
33270
  var WIN32_SUBMIT_RESEND_GAP_MS = 350;
32982
33271
  var WIN32_SUBMIT_MAX_RESENDS = 14;
33272
+ var WIN32_SUBMIT_SETTLE_MS = 500;
33273
+ var WIN32_SUBMIT_MAX_SETTLE_WAIT_MS = 1e4;
33274
+ var WIN32_SUBMIT_SETTLE_POLL_MS = 120;
33275
+ var WIN32_PTY_WRITE_CHUNK_CHARS = 1024;
33276
+ var WIN32_PTY_WRITE_CHUNK_GAP_MS = 8;
32983
33277
  function resolveSubmitDelayMs(specBeforeSubmit, text) {
32984
33278
  const lines = countNewlines(text);
32985
33279
  const linesBonus = Math.min(800, lines * 80);
32986
33280
  const spec = typeof specBeforeSubmit === "number" && specBeforeSubmit > 0 ? specBeforeSubmit : 0;
32987
33281
  return Math.max(spec, SUBMIT_DELAY_FLOOR_MS + linesBonus);
32988
33282
  }
33283
+ function chunkPreservingSurrogates(text, size) {
33284
+ const chunks = [];
33285
+ let offset = 0;
33286
+ while (offset < text.length) {
33287
+ let end = Math.min(text.length, offset + size);
33288
+ if (end < text.length) {
33289
+ const code = text.charCodeAt(end - 1);
33290
+ if (code >= 55296 && code <= 56319) end -= 1;
33291
+ }
33292
+ if (end <= offset) end = Math.min(text.length, offset + size);
33293
+ chunks.push(text.slice(offset, end));
33294
+ offset = end;
33295
+ }
33296
+ return chunks;
33297
+ }
32989
33298
  function guessExt(mime) {
32990
33299
  if (/png/i.test(mime)) return ".png";
32991
33300
  if (/jpe?g/i.test(mime)) return ".jpg";
@@ -33001,7 +33310,10 @@ var FsmDriver = class {
33001
33310
  this.buildAdapterOpts(),
33002
33311
  {
33003
33312
  init: () => this.emitInitialState(),
33004
- on_pty_data: (chunk) => this.emit({ kind: "pty_data", chunk }),
33313
+ on_pty_data: (chunk) => {
33314
+ this.lastPtyDataAt = Date.now();
33315
+ this.emit({ kind: "pty_data", chunk });
33316
+ },
33005
33317
  on_screen_changed: () => this.reevaluate(),
33006
33318
  on_exit: ({ exitCode }) => this.handleExit(exitCode)
33007
33319
  }
@@ -33035,6 +33347,16 @@ var FsmDriver = class {
33035
33347
  * WIN32_SUBMIT_* and scheduleWin32Submit). Re-arms itself until the FSM
33036
33348
  * leaves idle (submitted) or the resend budget is spent. */
33037
33349
  win32SubmitTimer = null;
33350
+ /** Wall-clock (ms) of the most recent raw PTY output chunk. Advances on every
33351
+ * on_pty_data — including the echo of text written into the composer — so the
33352
+ * win32 submit settle-gate can tell when input has finished landing. */
33353
+ lastPtyDataAt = 0;
33354
+ /** Wall-clock (ms) of the most recent win32 message-body input write. Bridges
33355
+ * the gap between writing a chunk and its echo so the settle-gate does not
33356
+ * declare "quiet" mid-write. */
33357
+ lastWin32WriteAt = 0;
33358
+ /** Pending paced chunk-write timer for a large win32 body (see writeWin32Body). */
33359
+ win32WriteTimer = null;
33038
33360
  currentEval = null;
33039
33361
  stateHistory = [];
33040
33362
  prevStateAt = 0;
@@ -33155,6 +33477,10 @@ var FsmDriver = class {
33155
33477
  clearTimeout(this.win32SubmitTimer);
33156
33478
  this.win32SubmitTimer = null;
33157
33479
  }
33480
+ if (this.win32WriteTimer) {
33481
+ clearTimeout(this.win32WriteTimer);
33482
+ this.win32WriteTimer = null;
33483
+ }
33158
33484
  this.specWatcher?.close();
33159
33485
  this.adapter.kill();
33160
33486
  }
@@ -33185,11 +33511,15 @@ var FsmDriver = class {
33185
33511
  getFsmSnapshotHistory() {
33186
33512
  return this.fsmSnapshotHistory;
33187
33513
  }
33514
+ /** Debug-only PTY input/output/resize/cursor timeline from the adapter. */
33515
+ getEventTimeline(limit) {
33516
+ return this.adapter.getEventTimeline(limit);
33517
+ }
33188
33518
  getSections() {
33189
33519
  try {
33190
33520
  const screen = this.adapter.snapshot();
33191
33521
  const lines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
33192
- return resolveSections(this.spec.sections ?? {}, lines).map((s) => ({ id: s.id, text: s.text }));
33522
+ return resolveSections(this.spec.sections ?? {}, lines).map((s2) => ({ id: s2.id, text: s2.text }));
33193
33523
  } catch {
33194
33524
  return null;
33195
33525
  }
@@ -33567,7 +33897,7 @@ var FsmDriver = class {
33567
33897
  const perChar = sm.delay_ms_per_char ?? 0;
33568
33898
  const beforeSubmit = resolveSubmitDelayMs(sm.delay_ms_before_submit, text);
33569
33899
  if (process.platform === "win32") {
33570
- this.adapter.send_keys(text);
33900
+ this.writeWin32Body(text);
33571
33901
  this.scheduleWin32Submit(sm.submit_key, beforeSubmit);
33572
33902
  return;
33573
33903
  }
@@ -33593,20 +33923,72 @@ var FsmDriver = class {
33593
33923
  const st = stateById(this.spec, this.currentStateId);
33594
33924
  return st ? statusForState(st) : "idle";
33595
33925
  }
33926
+ /** Record a win32 body write so the settle-gate counts it as input activity
33927
+ * even before the echo arrives. */
33928
+ markWin32Write() {
33929
+ this.lastWin32WriteAt = Date.now();
33930
+ }
33931
+ /** Most recent win32 input activity — a write we issued OR a PTY output chunk
33932
+ * (echo). The submit settle-gate waits for this to go quiet. */
33933
+ lastWin32InputActivityAt() {
33934
+ return Math.max(this.lastPtyDataAt, this.lastWin32WriteAt);
33935
+ }
33596
33936
  /**
33597
- * win32 verification-based submit. Sends the submit key, waits a gap, and if
33598
- * the FSM is still 'idle' (the prompt did not submit — the CR was absorbed as
33599
- * a multiline-paste newline) resends, up to WIN32_SUBMIT_MAX_RESENDS. The
33600
- * first CR always fires (so a stale/edge status never suppresses the submit);
33601
- * subsequent resends are gated on still being idle, and stop the instant the
33602
- * agent leaves idle (submitted generating / approval). This converges the
33603
- * nondeterministic multiline window without spamming Enter into the next turn.
33937
+ * Write the message body to the PTY for win32, paced into bounded chunks. A
33938
+ * single unbounded ConPTY write can overflow the input pipe and drop leading
33939
+ * bytes; splitting it with a short inter-chunk gap keeps the console input
33940
+ * buffer from overflowing. Small bodies still go out in a single write. Each
33941
+ * chunk advances lastWin32WriteAt so the submit settle-gate keeps waiting until
33942
+ * the final chunk is out and echoed.
33943
+ */
33944
+ writeWin32Body(text) {
33945
+ if (this.win32WriteTimer) {
33946
+ clearTimeout(this.win32WriteTimer);
33947
+ this.win32WriteTimer = null;
33948
+ }
33949
+ if (text.length <= WIN32_PTY_WRITE_CHUNK_CHARS) {
33950
+ this.markWin32Write();
33951
+ this.adapter.send_keys(text);
33952
+ return;
33953
+ }
33954
+ const chunks = chunkPreservingSurrogates(text, WIN32_PTY_WRITE_CHUNK_CHARS);
33955
+ let idx = 0;
33956
+ const writeNext = () => {
33957
+ this.win32WriteTimer = null;
33958
+ if (idx >= chunks.length) return;
33959
+ this.markWin32Write();
33960
+ this.adapter.send_keys(chunks[idx]);
33961
+ idx += 1;
33962
+ if (idx < chunks.length) {
33963
+ this.win32WriteTimer = setTimeout(writeNext, WIN32_PTY_WRITE_CHUNK_GAP_MS);
33964
+ }
33965
+ };
33966
+ writeNext();
33967
+ }
33968
+ /**
33969
+ * win32 submit. Two phases:
33970
+ *
33971
+ * Phase 1 (settle-gate): hold the first CR until the PTY output has been quiet
33972
+ * for WIN32_SUBMIT_SETTLE_MS after the last input write — i.e. the full
33973
+ * (possibly multi-KB / multiline) body has finished arriving in the composer
33974
+ * and echoing. Honors an initial minimum delay and is bounded by
33975
+ * WIN32_SUBMIT_MAX_SETTLE_WAIT_MS so a noisy screen can never hang the submit.
33976
+ * This is what stops a long message from being submitted half-arrived (its
33977
+ * leading lines lost). A short message settles almost immediately.
33978
+ *
33979
+ * Phase 2 (verified resend — unchanged): send the submit key, wait a gap, and
33980
+ * if the FSM is still 'idle' (the CR was absorbed as a multiline-paste
33981
+ * newline) resend, up to WIN32_SUBMIT_MAX_RESENDS. The first CR always fires
33982
+ * (a stale/edge status never suppresses it); resends are gated on still being
33983
+ * idle and stop the instant the agent leaves idle (submitted → generating /
33984
+ * approval). This preserves the win32 lone-CR-swallow handling.
33604
33985
  */
33605
33986
  scheduleWin32Submit(submitKey, initialDelayMs) {
33606
33987
  if (this.win32SubmitTimer) {
33607
33988
  clearTimeout(this.win32SubmitTimer);
33608
33989
  this.win32SubmitTimer = null;
33609
33990
  }
33991
+ const startedAt = Date.now();
33610
33992
  const fire = (attempt) => {
33611
33993
  this.win32SubmitTimer = null;
33612
33994
  this.adapter.send_keys(submitKey);
@@ -33619,8 +34001,20 @@ var FsmDriver = class {
33619
34001
  fire(attempt + 1);
33620
34002
  }, WIN32_SUBMIT_RESEND_GAP_MS);
33621
34003
  };
33622
- if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(() => fire(0), initialDelayMs);
33623
- else fire(0);
34004
+ const waitForSettle = () => {
34005
+ this.win32SubmitTimer = null;
34006
+ const now = Date.now();
34007
+ const quietFor = now - this.lastWin32InputActivityAt();
34008
+ const waited = now - startedAt;
34009
+ if (quietFor >= WIN32_SUBMIT_SETTLE_MS || waited >= WIN32_SUBMIT_MAX_SETTLE_WAIT_MS) {
34010
+ fire(0);
34011
+ return;
34012
+ }
34013
+ const recheckIn = Math.min(WIN32_SUBMIT_SETTLE_MS - quietFor, WIN32_SUBMIT_SETTLE_POLL_MS);
34014
+ this.win32SubmitTimer = setTimeout(waitForSettle, Math.max(recheckIn, 30));
34015
+ };
34016
+ if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(waitForSettle, initialDelayMs);
34017
+ else waitForSettle();
33624
34018
  }
33625
34019
  handleClickControl(controlId, payload) {
33626
34020
  const ctl = (this.spec.control_bar ?? []).find((c) => c.id === controlId);
@@ -33733,7 +34127,8 @@ function summarizeTransition(t) {
33733
34127
  return out;
33734
34128
  }
33735
34129
  function flattenCond(c, out, depth) {
33736
- out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}`);
34130
+ const matched = c.matchedText ? ` matched=${JSON.stringify(c.matchedText)}` : "";
34131
+ out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}${matched}`);
33737
34132
  for (const child of c.children ?? []) flattenCond(child, out, depth + 1);
33738
34133
  }
33739
34134
  function findStable(c) {
@@ -34451,8 +34846,8 @@ function projectToolBlock(block2, role, tmap) {
34451
34846
  }
34452
34847
  return null;
34453
34848
  }
34454
- function oneLine(s, max) {
34455
- const flat = s.replace(/\s+/g, " ").trim();
34849
+ function oneLine(s2, max) {
34850
+ const flat = s2.replace(/\s+/g, " ").trim();
34456
34851
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
34457
34852
  }
34458
34853
  function parseTimestamp(v) {
@@ -34472,10 +34867,10 @@ function parseTimestamp(v) {
34472
34867
  return null;
34473
34868
  }
34474
34869
  function normalizeRole(r) {
34475
- const s = String(r ?? "").toLowerCase();
34476
- if (s === "user" || s === "human" || s === "user_explicit") return "user";
34477
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
34478
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
34870
+ const s2 = String(r ?? "").toLowerCase();
34871
+ if (s2 === "user" || s2 === "human" || s2 === "user_explicit") return "user";
34872
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
34873
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
34479
34874
  return "system";
34480
34875
  }
34481
34876
  function stringifyContent(v) {
@@ -34523,18 +34918,18 @@ function compileWhere(src) {
34523
34918
  return (record) => ors.some((ands) => ands.every((t) => evalTerm(t, record)));
34524
34919
  }
34525
34920
  function parseTerm(src) {
34526
- let s = src.trim();
34921
+ let s2 = src.trim();
34527
34922
  let negate = false;
34528
- if (s.startsWith("!")) {
34923
+ if (s2.startsWith("!")) {
34529
34924
  negate = true;
34530
- s = s.slice(1).trim();
34925
+ s2 = s2.slice(1).trim();
34531
34926
  }
34532
- const fnMatch = s.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34927
+ const fnMatch = s2.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34533
34928
  if (fnMatch) {
34534
34929
  const [, op2, pathExpr, litExpr] = fnMatch;
34535
34930
  return { path: pathExpr, op: op2, lit: parseLiteral(litExpr), negate };
34536
34931
  }
34537
- const opMatch = s.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34932
+ const opMatch = s2.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34538
34933
  if (!opMatch) return null;
34539
34934
  const [, lhs, op, rhsRaw] = opMatch;
34540
34935
  return { path: lhs.trim(), op, lit: parseLiteral(rhsRaw.trim()), negate };
@@ -34966,7 +35361,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
34966
35361
  try {
34967
35362
  const sections = this.driver.getSections();
34968
35363
  if (sectionId && sections) {
34969
- const hit = sections.find((s) => s.id === sectionId);
35364
+ const hit = sections.find((s2) => s2.id === sectionId);
34970
35365
  if (hit) return hit.text;
34971
35366
  }
34972
35367
  return this.driver.getScreen();
@@ -34981,7 +35376,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
34981
35376
  screen = this.driver.snapshot();
34982
35377
  const driverSections = this.driver.getSections?.();
34983
35378
  if (driverSections) {
34984
- sections = Object.fromEntries(driverSections.map((s) => [s.id, s.text]));
35379
+ sections = Object.fromEntries(driverSections.map((s2) => [s2.id, s2.text]));
34985
35380
  } else {
34986
35381
  sections = this.readCurrentScreenSections(screen);
34987
35382
  }
@@ -35027,6 +35422,10 @@ var SpecCliAdapter = class _SpecCliAdapter {
35027
35422
  // answers "why did this rule fire" after the fact, unlike the live
35028
35423
  // `fsm` field which only reflects the current instant.
35029
35424
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35425
+ // PTY input/output/resize/cursor event timeline (debug-only) so the
35426
+ // snapshot shows what we typed / what the PTY printed around each
35427
+ // status transition. Null for drivers without the timeline.
35428
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
35030
35429
  // Extended fields
35031
35430
  name: this.cliName,
35032
35431
  status: this.getStatus().status,
@@ -35391,6 +35790,8 @@ var SpecCliAdapter = class _SpecCliAdapter {
35391
35790
  // v4 FSM transition snapshot history — the captured pre-transition
35392
35791
  // evaluation table at each transition (null for v3 specs).
35393
35792
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35793
+ // PTY input/output/resize/cursor event timeline (debug-only).
35794
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
35394
35795
  messages,
35395
35796
  committedMessages: messages
35396
35797
  };
@@ -35435,6 +35836,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
35435
35836
 
35436
35837
  // src/providers/cli-provider-instance.ts
35437
35838
  init_logger();
35839
+ init_mesh_event_trace();
35438
35840
  init_control_effects();
35439
35841
  init_approval_utils();
35440
35842
  init_provider_patch_state();
@@ -35737,6 +36139,11 @@ var CliProviderInstance = class _CliProviderInstance {
35737
36139
  // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35738
36140
  // brief generating flip does not immediately wipe the settle clock.
35739
36141
  autoApproveInactiveSince = 0;
36142
+ // Provider-common manual-attendance signal: while a human is actively driving
36143
+ // this session from the dashboard, auto-approve holds so they can take manual
36144
+ // control. Background mesh workers are never attended → delegated auto-approve
36145
+ // is unaffected.
36146
+ manualAttendance = new ManualAttendanceTracker();
35740
36147
  controlValues = {};
35741
36148
  summaryMetadata = void 0;
35742
36149
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -36012,7 +36419,7 @@ var CliProviderInstance = class _CliProviderInstance {
36012
36419
  }
36013
36420
  getHotChatSessionState() {
36014
36421
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
36015
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36422
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
36016
36423
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
36017
36424
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
36018
36425
  const runtime = this.adapter.getRuntimeMetadata();
@@ -36027,7 +36434,7 @@ var CliProviderInstance = class _CliProviderInstance {
36027
36434
  }
36028
36435
  getSessionModalState(sessionId) {
36029
36436
  const adapterStatus = this.adapter.getStatus({ allowParse: true });
36030
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36437
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
36031
36438
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
36032
36439
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
36033
36440
  const dirName = workingDirBasename(this.workingDir);
@@ -36108,7 +36515,7 @@ var CliProviderInstance = class _CliProviderInstance {
36108
36515
  } catch {
36109
36516
  return null;
36110
36517
  }
36111
- if (adapterStatus.status === "waiting_approval" && !this.shouldAutoApprove()) {
36518
+ if (adapterStatus.status === "waiting_approval" && !this.autoApproveEffectivelyActive(adapterStatus.status)) {
36112
36519
  return "waiting_approval";
36113
36520
  }
36114
36521
  return null;
@@ -36481,6 +36888,23 @@ var CliProviderInstance = class _CliProviderInstance {
36481
36888
  if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
36482
36889
  this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
36483
36890
  }
36891
+ // EVTTRACE (observation-only): is this a mesh worker session whose completion
36892
+ // events must route to a coordinator? Used purely to gate trace logging so a
36893
+ // non-mesh CLI session's completions don't add EvtTrace noise. No decision logic.
36894
+ isMeshWorkerSession() {
36895
+ return !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.meshNodeId || this.settings.launchedByCoordinator);
36896
+ }
36897
+ // EVTTRACE correlation context for this session's completion lifecycle. taskId is
36898
+ // the primary grep anchor; instanceId is the session fallback.
36899
+ meshTraceCtx(event = "agent:generating_completed") {
36900
+ return {
36901
+ taskId: this.settings.meshActiveTaskId,
36902
+ sessionId: this.instanceId,
36903
+ nodeId: this.settings.meshNodeId,
36904
+ meshId: this.settings.meshNodeFor,
36905
+ event
36906
+ };
36907
+ }
36484
36908
  flushCompletedDebounceIfFinalized() {
36485
36909
  const pending = this.completedDebouncePending;
36486
36910
  if (!pending) {
@@ -36501,24 +36925,33 @@ var CliProviderInstance = class _CliProviderInstance {
36501
36925
  if (block2) {
36502
36926
  const blockReason = block2.reason;
36503
36927
  const waitedMs = Date.now() - pending.firstObservedAt;
36504
- LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36505
- if (block2.terminal && !block2.allowTimeout || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
36928
+ const isTranscriptEvidenceGate = block2.allowTimeout === true;
36929
+ LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} allowTimeout=${isTranscriptEvidenceGate} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36930
+ if (!isTranscriptEvidenceGate && (block2.terminal || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS)) {
36506
36931
  if (pending.loggedBlockReason !== blockReason) {
36507
36932
  LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
36933
+ if (this.isMeshWorkerSession()) {
36934
+ traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
36935
+ }
36508
36936
  pending.loggedBlockReason = blockReason;
36509
36937
  }
36510
36938
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
36511
36939
  return;
36512
36940
  }
36941
+ const emittedAfterFinalizationTimeout = waitedMs >= COMPLETED_FINALIZATION_MAX_WAIT_MS;
36513
36942
  const completionDiagnostic = this.buildCompletedFinalizationDiagnostic({
36514
36943
  blockReason,
36515
36944
  latestStatus,
36516
36945
  latestVisibleStatus,
36517
36946
  waitedMs,
36518
36947
  pending,
36519
- emittedAfterFinalizationTimeout: true
36948
+ emittedAfterFinalizationTimeout
36520
36949
  });
36521
- LOG.warn("CLI", `[${this.type}] emitting completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
36950
+ completionDiagnostic.decoupledImmediateEmit = isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout;
36951
+ LOG.warn("CLI", `[${this.type}] emitting completed event (${isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "CANON-C decoupled-immediate, transcript pending" : `after ${waitedMs}ms`}) without finalized assistant turn (${blockReason})`);
36952
+ if (this.isMeshWorkerSession()) {
36953
+ traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
36954
+ }
36522
36955
  this.pushEvent({
36523
36956
  event: "agent:generating_completed",
36524
36957
  chatTitle: pending.chatTitle,
@@ -36541,6 +36974,9 @@ var CliProviderInstance = class _CliProviderInstance {
36541
36974
  return;
36542
36975
  }
36543
36976
  LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
36977
+ if (this.isMeshWorkerSession()) {
36978
+ traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
36979
+ }
36544
36980
  this.pushEvent({
36545
36981
  event: "agent:generating_completed",
36546
36982
  chatTitle: pending.chatTitle,
@@ -36554,6 +36990,18 @@ var CliProviderInstance = class _CliProviderInstance {
36554
36990
  this.lastApprovalEventFingerprint = "";
36555
36991
  }
36556
36992
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
36993
+ if (adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove() && this.manualAttendance.isAttended(now)) {
36994
+ this.lastAutoApprovalSignature = "";
36995
+ this.pendingAutoApprovalSignature = "";
36996
+ this.pendingAutoApprovalSince = 0;
36997
+ this.autoApproveInactiveSince = 0;
36998
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36999
+ this.autoApproveSettleTimer = setTimeout(() => {
37000
+ this.autoApproveSettleTimer = null;
37001
+ this.recheckAutoApproveSettled();
37002
+ }, this.manualAttendance.remainingMs(now) + 20);
37003
+ return false;
37004
+ }
36557
37005
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
36558
37006
  if (!autoApproveActive) {
36559
37007
  this.lastAutoApprovalSignature = "";
@@ -36767,6 +37215,9 @@ var CliProviderInstance = class _CliProviderInstance {
36767
37215
  if (missingEvidence && !hasMeshContext) {
36768
37216
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, no mesh context (source=${shortEvidenceSource})`);
36769
37217
  } else {
37218
+ if (this.isMeshWorkerSession()) {
37219
+ traceMeshEventStage("fired", this.meshTraceCtx(), `short-generating idle (source=${shortEvidenceSource})`);
37220
+ }
36770
37221
  this.pushEvent({
36771
37222
  event: "agent:generating_completed",
36772
37223
  chatTitle,
@@ -36843,6 +37294,9 @@ var CliProviderInstance = class _CliProviderInstance {
36843
37294
  const monitorParsedStatus = parsedStatus;
36844
37295
  for (const me of monitorEvents) {
36845
37296
  if (me.type === "monitor:no_progress" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
37297
+ if (this.isMeshWorkerSession()) {
37298
+ traceMeshEventStage("fired", this.meshTraceCtx(), "no_progress_monitor_final_summary");
37299
+ }
36846
37300
  this.pushEvent({
36847
37301
  event: "agent:generating_completed",
36848
37302
  chatTitle,
@@ -37019,6 +37473,21 @@ ${effect.notification.body || ""}`.trim();
37019
37473
  }
37020
37474
  return false;
37021
37475
  }
37476
+ /** @see ProviderInstance.noteManualInteraction */
37477
+ noteManualInteraction(now = Date.now()) {
37478
+ this.manualAttendance.note(now);
37479
+ }
37480
+ /**
37481
+ * Whether auto-approve should be treated as active *right now* for display
37482
+ * and firing decisions: the configured intent AND the user is not currently
37483
+ * attending this session by hand. When a human is attending, auto-approve is
37484
+ * held so the modal stays visible and they can drive it via the controlbar.
37485
+ * Provider-agnostic — the attendance signal is the command set, never any
37486
+ * CLI-specific modal text.
37487
+ */
37488
+ autoApproveEffectivelyActive(status, now = Date.now()) {
37489
+ return status === "waiting_approval" && this.shouldAutoApprove() && !this.manualAttendance.isAttended(now);
37490
+ }
37022
37491
  recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
37023
37492
  this.appendRuntimeSystemMessage(
37024
37493
  formatAutoApprovalMessage(modalMessage, buttonLabel),
@@ -37964,7 +38433,7 @@ var AcpProviderInstance = class {
37964
38433
  input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
37965
38434
  });
37966
38435
  }
37967
- if (this.settings.autoApprove !== false) {
38436
+ if (this.settings.autoApprove !== false && !this.manualAttendance.isAttended()) {
37968
38437
  const toolTitle = tc.title || tc.toolCallId || "tool call";
37969
38438
  this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
37970
38439
  this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
@@ -38195,6 +38664,15 @@ var AcpProviderInstance = class {
38195
38664
  this.detectStatusTransition();
38196
38665
  }
38197
38666
  permissionResolvers = [];
38667
+ // Provider-common manual-attendance signal: while a human is actively driving
38668
+ // this session from the dashboard, auto-approve holds so they can decide on
38669
+ // the permission request themselves. Background workers are never attended →
38670
+ // delegated auto-approve is unaffected.
38671
+ manualAttendance = new ManualAttendanceTracker();
38672
+ /** @see ProviderInstance.noteManualInteraction */
38673
+ noteManualInteraction(now = Date.now()) {
38674
+ this.manualAttendance.note(now);
38675
+ }
38198
38676
  async resolvePermission(approved) {
38199
38677
  const resolver = this.permissionResolvers.shift();
38200
38678
  if (resolver) {
@@ -39352,6 +39830,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
39352
39830
  );
39353
39831
  continue;
39354
39832
  }
39833
+ const restoredSettings = { ...this.providerLoader.getSettings(normalizedType) };
39834
+ const coordinatorEntry = getCoordinatorForSession(record.runtimeId);
39835
+ if (coordinatorEntry?.meshId) {
39836
+ restoredSettings.meshCoordinatorFor = coordinatorEntry.meshId;
39837
+ }
39355
39838
  try {
39356
39839
  await this.registerCliInstance(
39357
39840
  record.runtimeId,
@@ -39360,7 +39843,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39360
39843
  record.workspace,
39361
39844
  record.cliArgs,
39362
39845
  resolvedProvider,
39363
- {},
39846
+ restoredSettings,
39364
39847
  true,
39365
39848
  {
39366
39849
  providerSessionId: sessionBinding.providerSessionId,
@@ -40677,7 +41160,7 @@ function parsePbFile(filePath, sessionId) {
40677
41160
  }
40678
41161
  if (buf.length === 0) return null;
40679
41162
  const strings = extractStringsFromBuffer(buf);
40680
- const meaningful = strings.filter((s) => s.length >= MIN_PRINTABLE_RUN && /\w/.test(s));
41163
+ const meaningful = strings.filter((s2) => s2.length >= MIN_PRINTABLE_RUN && /\w/.test(s2));
40681
41164
  if (meaningful.length === 0) return null;
40682
41165
  const content = meaningful.join("\n");
40683
41166
  const sourceMtimeMs = statMtimeMs3(filePath);
@@ -40885,10 +41368,10 @@ function readSession4(sessionPath) {
40885
41368
  };
40886
41369
  }
40887
41370
  function normalizeHermesRole(r) {
40888
- const s = String(r ?? "").toLowerCase();
40889
- if (s === "user" || s === "human") return "user";
40890
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
40891
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41371
+ const s2 = String(r ?? "").toLowerCase();
41372
+ if (s2 === "user" || s2 === "human") return "user";
41373
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41374
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
40892
41375
  return "system";
40893
41376
  }
40894
41377
 
@@ -41114,10 +41597,10 @@ function safeMtime(p) {
41114
41597
  }
41115
41598
  }
41116
41599
  function normalizeRole2(r) {
41117
- const s = String(r ?? "").toLowerCase();
41118
- if (s === "user" || s === "human") return "user";
41119
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
41120
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41600
+ const s2 = String(r ?? "").toLowerCase();
41601
+ if (s2 === "user" || s2 === "human") return "user";
41602
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41603
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
41121
41604
  return "system";
41122
41605
  }
41123
41606
 
@@ -41137,7 +41620,7 @@ function synthesizeControlsFromControlBar(specControls) {
41137
41620
  const actionType = ctl?.action?.type;
41138
41621
  if (!id || !actionType) return;
41139
41622
  const label = typeof ctl?.label === "string" && ctl.label.trim() ? ctl.label : id;
41140
- const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s) => typeof s === "string") : void 0;
41623
+ const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s2) => typeof s2 === "string") : void 0;
41141
41624
  if (actionType === "open_picker") {
41142
41625
  out.push({
41143
41626
  id,
@@ -48053,7 +48536,7 @@ var DaemonCommandRouter = class {
48053
48536
  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.";
48054
48537
  if (!firstFailedCmd) return base;
48055
48538
  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 : "";
48056
- const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s) => typeof s === "string" && s.length > 0).join("\n");
48539
+ const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
48057
48540
  const tail = rawOutput.length > 800 ? rawOutput.slice(-800) : rawOutput;
48058
48541
  return [
48059
48542
  base,
@@ -48804,7 +49287,7 @@ ${hintLines.join("\n")}` : "",
48804
49287
  convergence = "blocked_review";
48805
49288
  }
48806
49289
  const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
48807
- const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
49290
+ const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s2) => s2.status === "failed").map((s2) => s2.stage).filter(Boolean).pop() : void 0;
48808
49291
  results.push({
48809
49292
  nodeId: node.id,
48810
49293
  workspace: node.workspace,
@@ -49801,7 +50284,7 @@ ${hintLines.join("\n")}` : "",
49801
50284
  return {
49802
50285
  success: true,
49803
50286
  screenLineCount: lines.length,
49804
- sections: resolved.map((s) => ({ id: s.id, fromLine: s.fromLine, toLine: s.toLine, text: s.text }))
50287
+ sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
49805
50288
  };
49806
50289
  } catch (e) {
49807
50290
  return { success: false, error: `resolve failed: ${e.message}` };
@@ -50370,7 +50853,7 @@ ${hintLines.join("\n")}` : "",
50370
50853
  if (!meshId) return { success: false, error: "meshId required" };
50371
50854
  try {
50372
50855
  const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
50373
- const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
50856
+ const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
50374
50857
  const rawQueue = getQueue2(meshId, { status });
50375
50858
  const statusById = new Map(getQueue2(meshId).map((task) => [task.id, task.status]));
50376
50859
  const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById) } : task);
@@ -50651,7 +51134,7 @@ ${hintLines.join("\n")}` : "",
50651
51134
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50652
51135
  }
50653
51136
  const selfDaemonId = this.deps.statusInstanceId;
50654
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51137
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50655
51138
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50656
51139
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
50657
51140
  ...typeof args === "object" && args !== null ? args : {},
@@ -50685,7 +51168,7 @@ ${hintLines.join("\n")}` : "",
50685
51168
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50686
51169
  }
50687
51170
  const selfDaemonId = this.deps.statusInstanceId;
50688
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51171
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50689
51172
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50690
51173
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "get_mesh_node_logs", {
50691
51174
  ...typeof args === "object" && args !== null ? args : {},
@@ -50733,7 +51216,7 @@ ${hintLines.join("\n")}` : "",
50733
51216
  const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
50734
51217
  const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
50735
51218
  const selfDaemonId = this.deps.statusInstanceId;
50736
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
51219
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50737
51220
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50738
51221
  const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
50739
51222
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
@@ -50820,7 +51303,7 @@ ${hintLines.join("\n")}` : "",
50820
51303
  let worktreeCleanup;
50821
51304
  if (node?.isLocalWorktree) {
50822
51305
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
50823
- const isRemoteWorktree = nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
51306
+ const isRemoteWorktree = nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
50824
51307
  if (isRemoteWorktree) {
50825
51308
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "remove_mesh_node", {
50826
51309
  ...typeof args === "object" && args !== null ? args : {},
@@ -50902,7 +51385,7 @@ ${hintLines.join("\n")}` : "",
50902
51385
  const sourceNode = mesh.nodes?.find((n) => meshNodeIdMatches(n, sourceNodeId));
50903
51386
  if (!sourceNode) return { success: false, error: `Source node '${sourceNodeId}' not found in mesh` };
50904
51387
  const sourceDaemonId = typeof sourceNode.daemonId === "string" ? sourceNode.daemonId.trim() : void 0;
50905
- if (sourceDaemonId && sourceDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51388
+ if (sourceDaemonId && !daemonIdsEquivalent(sourceDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50906
51389
  const forwarded = await this.deps.dispatchMeshCommand(sourceDaemonId, "clone_mesh_node", {
50907
51390
  ...typeof args === "object" && args !== null ? args : {},
50908
51391
  _meshDirectDispatch: true
@@ -51144,7 +51627,7 @@ ${hintLines.join("\n")}` : "",
51144
51627
  if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
51145
51628
  if (!node.isLocalWorktree) return { success: false, error: "Node is not a local worktree node" };
51146
51629
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
51147
- if (nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51630
+ if (nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51148
51631
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "retry_mesh_node_bootstrap", {
51149
51632
  ...typeof args === "object" && args !== null ? args : {},
51150
51633
  _meshDirectDispatch: true
@@ -52354,16 +52837,16 @@ var DaemonStatusReporter = class {
52354
52837
  const now = this.lastStatusSentAt;
52355
52838
  const target = opts?.p2pOnly ? "P2P" : serverConnected ? "P2P+Server" : "P2P";
52356
52839
  const allStates = this.deps.instanceManager.collectAllStates();
52357
- const ideStates = allStates.filter((s) => s.category === "ide");
52358
- const cliStates = allStates.filter((s) => s.category === "cli");
52359
- const acpStates = allStates.filter((s) => s.category === "acp");
52360
- const ideSummary = ideStates.map((s) => {
52361
- const msgs = s.activeChat?.messages?.length || 0;
52362
- const exts = s.extensions.length;
52363
- return `${s.type}(${s.status},${msgs}msg,${exts}ext)`;
52840
+ const ideStates = allStates.filter((s2) => s2.category === "ide");
52841
+ const cliStates = allStates.filter((s2) => s2.category === "cli");
52842
+ const acpStates = allStates.filter((s2) => s2.category === "acp");
52843
+ const ideSummary = ideStates.map((s2) => {
52844
+ const msgs = s2.activeChat?.messages?.length || 0;
52845
+ const exts = s2.extensions.length;
52846
+ return `${s2.type}(${s2.status},${msgs}msg,${exts}ext)`;
52364
52847
  }).join(", ");
52365
- const cliSummary = cliStates.map((s) => `${s.type}(${s.status})`).join(", ");
52366
- const acpSummary = acpStates.map((s) => `${s.type}(${s.status})`).join(", ");
52848
+ const cliSummary = cliStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52849
+ const acpSummary = acpStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52367
52850
  const logLevel = opts?.p2pOnly ? "debug" : "info";
52368
52851
  const baseSummary = `IDE: ${ideStates.length} [${ideSummary}] CLI: ${cliStates.length} [${cliSummary}] ACP: ${acpStates.length} [${acpSummary}]`;
52369
52852
  const summaryChanged = baseSummary !== this.lastStatusSummary;
@@ -52474,10 +52957,10 @@ var DaemonStatusReporter = class {
52474
52957
  }
52475
52958
  return false;
52476
52959
  }
52477
- simpleHash(s) {
52960
+ simpleHash(s2) {
52478
52961
  let h = 2166136261;
52479
- for (let i = 0; i < s.length; i++) {
52480
- h ^= s.charCodeAt(i);
52962
+ for (let i = 0; i < s2.length; i++) {
52963
+ h ^= s2.charCodeAt(i);
52481
52964
  h = h * 16777619 >>> 0;
52482
52965
  }
52483
52966
  return h.toString(36);
@@ -53703,7 +54186,7 @@ var ProviderInstanceManager = class {
53703
54186
  * Per-category status collect
53704
54187
  */
53705
54188
  collectStatesByCategory(category) {
53706
- return this.collectAllStates().filter((s) => s.category === category);
54189
+ return this.collectAllStates().filter((s2) => s2.category === category);
53707
54190
  }
53708
54191
  // ─── Tick engine ─────────────────────────────────
53709
54192
  /**
@@ -55603,9 +56086,9 @@ function getCliProviderResolutionMeta(ctx, type, adapter) {
55603
56086
  function findCliTarget(ctx, type, instanceId) {
55604
56087
  if (!ctx.instanceManager) return null;
55605
56088
  const cliStates = ctx.instanceManager.collectAllStates().filter(isCliTargetState);
55606
- if (instanceId) return cliStates.find((s) => s.instanceId === instanceId) || null;
56089
+ if (instanceId) return cliStates.find((s2) => s2.instanceId === instanceId) || null;
55607
56090
  if (!type) return cliStates[cliStates.length - 1] || null;
55608
- const matches = cliStates.filter((s) => s.type === type);
56091
+ const matches = cliStates.filter((s2) => s2.type === type);
55609
56092
  return matches[matches.length - 1] || null;
55610
56093
  }
55611
56094
  function getCliTargetBundle(ctx, type, instanceId) {
@@ -55968,20 +56451,20 @@ async function handleCliStatus(ctx, _req, res) {
55968
56451
  return;
55969
56452
  }
55970
56453
  const allStates = ctx.instanceManager.collectAllStates();
55971
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
55972
- const result = cliStates.map((s) => ({
55973
- instanceId: s.instanceId,
55974
- type: s.type,
55975
- name: s.name,
55976
- category: s.category,
55977
- status: s.status,
55978
- mode: s.mode,
55979
- workspace: s.workspace,
55980
- messageCount: s.activeChat?.messages?.length || 0,
55981
- lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
55982
- activeModal: s.activeChat?.activeModal || null,
55983
- pendingEvents: s.pendingEvents || [],
55984
- settings: s.settings
56454
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56455
+ const result = cliStates.map((s2) => ({
56456
+ instanceId: s2.instanceId,
56457
+ type: s2.type,
56458
+ name: s2.name,
56459
+ category: s2.category,
56460
+ status: s2.status,
56461
+ mode: s2.mode,
56462
+ workspace: s2.workspace,
56463
+ messageCount: s2.activeChat?.messages?.length || 0,
56464
+ lastMessage: s2.activeChat?.messages?.slice(-1)[0] || null,
56465
+ activeModal: s2.activeChat?.activeModal || null,
56466
+ pendingEvents: s2.pendingEvents || [],
56467
+ settings: s2.settings
55985
56468
  }));
55986
56469
  ctx.json(res, 200, { instances: result, count: result.length });
55987
56470
  }
@@ -56070,9 +56553,9 @@ function handleCliSSE(ctx, cliSSEClients, _req, res) {
56070
56553
  }
56071
56554
  if (ctx.instanceManager) {
56072
56555
  const allStates = ctx.instanceManager.collectAllStates();
56073
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
56074
- for (const s of cliStates) {
56075
- ctx.sendCliSSE({ event: "snapshot", providerType: s.type, status: s.status, instanceId: s.instanceId });
56556
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56557
+ for (const s2 of cliStates) {
56558
+ ctx.sendCliSSE({ event: "snapshot", providerType: s2.type, status: s2.status, instanceId: s2.instanceId });
56076
56559
  }
56077
56560
  }
56078
56561
  _req.on("close", () => {
@@ -56088,7 +56571,7 @@ async function handleCliDebug(ctx, type, _req, res) {
56088
56571
  const target = findCliTarget(ctx, type);
56089
56572
  if (!target) {
56090
56573
  const allStates = ctx.instanceManager.collectAllStates();
56091
- ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type) });
56574
+ ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type) });
56092
56575
  return;
56093
56576
  }
56094
56577
  const instance = ctx.instanceManager.getInstance(target.instanceId);
@@ -56134,7 +56617,7 @@ async function handleCliTrace(ctx, type, req, res) {
56134
56617
  const allStates = ctx.instanceManager.collectAllStates();
56135
56618
  ctx.json(res, 404, {
56136
56619
  error: `No running instance for: ${type}`,
56137
- available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type)
56620
+ available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type)
56138
56621
  });
56139
56622
  return;
56140
56623
  }
@@ -56939,7 +57422,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56939
57422
  child.write("\x1B[12;1R");
56940
57423
  ctx.log("Terminal CPR request (\\x1b[6n) intercepted in PTY, responding with dummy coordinates [12;1R]");
56941
57424
  }
56942
- checkAutoApproval(data, (s) => child.write(s));
57425
+ checkAutoApproval(data, (s2) => child.write(s2));
56943
57426
  sendAutoImplSSE(ctx, { event: "output", data: { chunk: data, stream: "stdout" } });
56944
57427
  scheduleAutoStopForVerification();
56945
57428
  });
@@ -56952,7 +57435,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56952
57435
  stdout += chunk;
56953
57436
  clearAutoStopTimer();
56954
57437
  if (chunk.includes("\x1B[6n")) child.stdin?.write("\x1B[1;1R");
56955
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57438
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
56956
57439
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stdout" } });
56957
57440
  scheduleAutoStopForVerification();
56958
57441
  });
@@ -56960,7 +57443,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56960
57443
  const chunk = d.toString();
56961
57444
  stderr += chunk;
56962
57445
  clearAutoStopTimer();
56963
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57446
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
56964
57447
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stderr" } });
56965
57448
  scheduleAutoStopForVerification();
56966
57449
  });
@@ -57775,59 +58258,59 @@ var DevServer = class _DevServer {
57775
58258
  // ─── Route Table ─────────────────────────────────────
57776
58259
  routes = [
57777
58260
  // Static routes
57778
- { method: "GET", pattern: "/api/providers", handler: (q, s) => this.handleListProviders(q, s) },
57779
- { method: "GET", pattern: "/api/providers/source-config", handler: (q, s) => this.handleGetProviderSourceConfig(q, s) },
57780
- { method: "POST", pattern: "/api/providers/source-config", handler: (q, s) => this.handleSetProviderSourceConfig(q, s) },
57781
- { method: "GET", pattern: "/api/providers/versions", handler: (q, s) => this.handleDetectVersions(q, s) },
57782
- { method: "POST", pattern: "/api/providers/reload", handler: (q, s) => this.handleReload(q, s) },
57783
- { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s) => this.handleCdpEvaluate(q, s) },
57784
- { method: "POST", pattern: "/api/cdp/click", handler: (q, s) => this.handleCdpClick(q, s) },
57785
- { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s) => this.handleCdpDomQuery(q, s) },
57786
- { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s) => this.handleDomInspect(q, s) },
57787
- { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s) => this.handleDomChildren(q, s) },
57788
- { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s) => this.handleDomAnalyze(q, s) },
57789
- { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s) => this.handleFindByText(q, s) },
57790
- { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s) => this.handleFindCommon(q, s) },
57791
- { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s) => this.handleScreenshot(q, s) },
57792
- { method: "GET", pattern: "/api/cdp/targets", handler: (q, s) => this.handleCdpTargets(q, s) },
57793
- { method: "POST", pattern: "/api/scripts/run", handler: (q, s) => this.handleScriptsRun(q, s) },
57794
- { method: "GET", pattern: "/api/status", handler: (q, s) => this.handleStatus(q, s) },
57795
- { method: "POST", pattern: "/api/watch/start", handler: (q, s) => this.handleWatchStart(q, s) },
57796
- { method: "POST", pattern: "/api/watch/stop", handler: (q, s) => this.handleWatchStop(q, s) },
57797
- { method: "GET", pattern: "/api/watch/events", handler: (q, s) => this.handleSSE(q, s) },
57798
- { method: "POST", pattern: "/api/scaffold", handler: (q, s) => this.handleScaffold(q, s) },
58261
+ { method: "GET", pattern: "/api/providers", handler: (q, s2) => this.handleListProviders(q, s2) },
58262
+ { method: "GET", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleGetProviderSourceConfig(q, s2) },
58263
+ { method: "POST", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleSetProviderSourceConfig(q, s2) },
58264
+ { method: "GET", pattern: "/api/providers/versions", handler: (q, s2) => this.handleDetectVersions(q, s2) },
58265
+ { method: "POST", pattern: "/api/providers/reload", handler: (q, s2) => this.handleReload(q, s2) },
58266
+ { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s2) => this.handleCdpEvaluate(q, s2) },
58267
+ { method: "POST", pattern: "/api/cdp/click", handler: (q, s2) => this.handleCdpClick(q, s2) },
58268
+ { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s2) => this.handleCdpDomQuery(q, s2) },
58269
+ { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s2) => this.handleDomInspect(q, s2) },
58270
+ { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s2) => this.handleDomChildren(q, s2) },
58271
+ { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s2) => this.handleDomAnalyze(q, s2) },
58272
+ { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s2) => this.handleFindByText(q, s2) },
58273
+ { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s2) => this.handleFindCommon(q, s2) },
58274
+ { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s2) => this.handleScreenshot(q, s2) },
58275
+ { method: "GET", pattern: "/api/cdp/targets", handler: (q, s2) => this.handleCdpTargets(q, s2) },
58276
+ { method: "POST", pattern: "/api/scripts/run", handler: (q, s2) => this.handleScriptsRun(q, s2) },
58277
+ { method: "GET", pattern: "/api/status", handler: (q, s2) => this.handleStatus(q, s2) },
58278
+ { method: "POST", pattern: "/api/watch/start", handler: (q, s2) => this.handleWatchStart(q, s2) },
58279
+ { method: "POST", pattern: "/api/watch/stop", handler: (q, s2) => this.handleWatchStop(q, s2) },
58280
+ { method: "GET", pattern: "/api/watch/events", handler: (q, s2) => this.handleSSE(q, s2) },
58281
+ { method: "POST", pattern: "/api/scaffold", handler: (q, s2) => this.handleScaffold(q, s2) },
57799
58282
  // CLI Debug routes
57800
- { method: "GET", pattern: "/api/cli/status", handler: (q, s) => this.handleCliStatus(q, s) },
57801
- { method: "POST", pattern: "/api/cli/launch", handler: (q, s) => this.handleCliLaunch(q, s) },
57802
- { method: "POST", pattern: "/api/cli/send", handler: (q, s) => this.handleCliSend(q, s) },
57803
- { method: "POST", pattern: "/api/cli/exercise", handler: (q, s) => this.handleCliExercise(q, s) },
57804
- { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s) => this.handleCliFixtureCapture(q, s) },
57805
- { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s) => this.handleCliFixtureReplay(q, s) },
57806
- { method: "POST", pattern: "/api/cli/resolve", handler: (q, s) => this.handleCliResolve(q, s) },
57807
- { method: "POST", pattern: "/api/cli/raw", handler: (q, s) => this.handleCliRaw(q, s) },
57808
- { method: "POST", pattern: "/api/cli/stop", handler: (q, s) => this.handleCliStop(q, s) },
57809
- { method: "GET", pattern: "/api/cli/events", handler: (q, s) => this.handleCliSSE(q, s) },
57810
- { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s, p) => this.handleCliDebug(p[0], q, s) },
57811
- { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s, p) => this.handleCliTrace(p[0], q, s) },
57812
- { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s, p) => this.handleCliFixtureList(p[0], q, s) },
58283
+ { method: "GET", pattern: "/api/cli/status", handler: (q, s2) => this.handleCliStatus(q, s2) },
58284
+ { method: "POST", pattern: "/api/cli/launch", handler: (q, s2) => this.handleCliLaunch(q, s2) },
58285
+ { method: "POST", pattern: "/api/cli/send", handler: (q, s2) => this.handleCliSend(q, s2) },
58286
+ { method: "POST", pattern: "/api/cli/exercise", handler: (q, s2) => this.handleCliExercise(q, s2) },
58287
+ { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s2) => this.handleCliFixtureCapture(q, s2) },
58288
+ { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s2) => this.handleCliFixtureReplay(q, s2) },
58289
+ { method: "POST", pattern: "/api/cli/resolve", handler: (q, s2) => this.handleCliResolve(q, s2) },
58290
+ { method: "POST", pattern: "/api/cli/raw", handler: (q, s2) => this.handleCliRaw(q, s2) },
58291
+ { method: "POST", pattern: "/api/cli/stop", handler: (q, s2) => this.handleCliStop(q, s2) },
58292
+ { method: "GET", pattern: "/api/cli/events", handler: (q, s2) => this.handleCliSSE(q, s2) },
58293
+ { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s2, p) => this.handleCliDebug(p[0], q, s2) },
58294
+ { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s2, p) => this.handleCliTrace(p[0], q, s2) },
58295
+ { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s2, p) => this.handleCliFixtureList(p[0], q, s2) },
57813
58296
  // Dynamic routes (provider :type param)
57814
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s, p) => this.handleRunScript(p[0], q, s) },
57815
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s, p) => this.handleListFiles(p[0], q, s) },
57816
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleReadFile(p[0], q, s) },
57817
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleWriteFile(p[0], q, s) },
57818
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s, p) => this.handleSource(p[0], q, s) },
57819
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s, p) => this.handleSave(p[0], q, s) },
57820
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s, p) => this.handleTypeAndSend(p[0], q, s) },
57821
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s, p) => this.handleTypeAndSendAt(p[0], q, s) },
57822
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s, p) => this.handleProviderConfig(p[0], q, s) },
57823
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s, p) => this.handleDomContext(p[0], q, s) },
57824
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s, p) => this.handleAutoImplement(p[0], q, s) },
57825
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s, p) => this.handleAutoImplCancel(p[0], q, s) },
57826
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s, p) => this.handleAutoImplSSE(p[0], q, s) },
57827
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s, p) => this.handleSpawnTest(p[0], q, s) },
57828
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s, p) => this.handleValidate(p[0], q, s) },
57829
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s, p) => this.handleAcpChat(p[0], q, s) },
57830
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s, p) => this.handleScriptHints(p[0], q, s) }
58297
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s2, p) => this.handleRunScript(p[0], q, s2) },
58298
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s2, p) => this.handleListFiles(p[0], q, s2) },
58299
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleReadFile(p[0], q, s2) },
58300
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleWriteFile(p[0], q, s2) },
58301
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s2, p) => this.handleSource(p[0], q, s2) },
58302
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s2, p) => this.handleSave(p[0], q, s2) },
58303
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s2, p) => this.handleTypeAndSend(p[0], q, s2) },
58304
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s2, p) => this.handleTypeAndSendAt(p[0], q, s2) },
58305
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s2, p) => this.handleProviderConfig(p[0], q, s2) },
58306
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s2, p) => this.handleDomContext(p[0], q, s2) },
58307
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s2, p) => this.handleAutoImplement(p[0], q, s2) },
58308
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s2, p) => this.handleAutoImplCancel(p[0], q, s2) },
58309
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s2, p) => this.handleAutoImplSSE(p[0], q, s2) },
58310
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s2, p) => this.handleSpawnTest(p[0], q, s2) },
58311
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s2, p) => this.handleValidate(p[0], q, s2) },
58312
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s2, p) => this.handleAcpChat(p[0], q, s2) },
58313
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s2, p) => this.handleScriptHints(p[0], q, s2) }
57831
58314
  ];
57832
58315
  matchRoute(method, pathname) {
57833
58316
  for (const route of this.routes) {
@@ -58422,14 +58905,14 @@ var DevServer = class _DevServer {
58422
58905
  warnings.push(...validation.warnings);
58423
58906
  if (config.settings) {
58424
58907
  for (const [key, val] of Object.entries(config.settings)) {
58425
- const s = val;
58426
- if (!s.type) errors.push(`settings.${key}: missing type`);
58427
- else if (!["boolean", "number", "string", "select"].includes(s.type))
58428
- errors.push(`settings.${key}: invalid type '${s.type}'`);
58429
- if (s.default === void 0) warnings.push(`settings.${key}: no default value`);
58430
- if (s.type === "number" && s.min !== void 0 && s.max !== void 0 && s.min > s.max)
58431
- errors.push(`settings.${key}: min (${s.min}) > max (${s.max})`);
58432
- if (s.type === "select" && (!s.options || !Array.isArray(s.options) || s.options.length === 0))
58908
+ const s2 = val;
58909
+ if (!s2.type) errors.push(`settings.${key}: missing type`);
58910
+ else if (!["boolean", "number", "string", "select"].includes(s2.type))
58911
+ errors.push(`settings.${key}: invalid type '${s2.type}'`);
58912
+ if (s2.default === void 0) warnings.push(`settings.${key}: no default value`);
58913
+ if (s2.type === "number" && s2.min !== void 0 && s2.max !== void 0 && s2.min > s2.max)
58914
+ errors.push(`settings.${key}: min (${s2.min}) > max (${s2.max})`);
58915
+ if (s2.type === "select" && (!s2.options || !Array.isArray(s2.options) || s2.options.length === 0))
58433
58916
  errors.push(`settings.${key}: select type requires options[]`);
58434
58917
  }
58435
58918
  }