@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.mjs CHANGED
@@ -311,10 +311,10 @@ function readInjected(value) {
311
311
  }
312
312
  function getDaemonBuildInfo() {
313
313
  if (cached) return cached;
314
- const commit = readInjected(true ? "a680b74d9b940b82f691ebb85a725a1a72445bfc" : void 0) ?? "unknown";
315
- const commitShort = readInjected(true ? "a680b74d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
- const version = readInjected(true ? "0.9.82-rc.353" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
- const builtAt = readInjected(true ? "2026-06-22T09:11:13.499Z" : void 0);
314
+ const commit = readInjected(true ? "a45106605e2ae1c10c0bc6cbe48c2cac4e862ded" : void 0) ?? "unknown";
315
+ const commitShort = readInjected(true ? "a4510660" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
+ const version = readInjected(true ? "0.9.82-rc.355" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
+ const builtAt = readInjected(true ? "2026-06-22T15:21:19.981Z" : void 0);
318
318
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
319
319
  return cached;
320
320
  }
@@ -1067,6 +1067,9 @@ __export(git_diff_exports, {
1067
1067
  });
1068
1068
  import { readFile, realpath as realpath2 } from "fs/promises";
1069
1069
  import * as path2 from "path";
1070
+ function withCollectionTimeout(options) {
1071
+ return options.timeoutMs === void 0 ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
1072
+ }
1070
1073
  function validateBaseRef(ref) {
1071
1074
  const trimmed = ref.trim();
1072
1075
  if (!trimmed || trimmed.startsWith("-") || trimmed.includes("..") || !/^[A-Za-z0-9][A-Za-z0-9._/@-]*$/.test(trimmed)) {
@@ -1076,14 +1079,15 @@ function validateBaseRef(ref) {
1076
1079
  }
1077
1080
  async function getGitDiffSummary(workspace, options = {}) {
1078
1081
  const lastCheckedAt = Date.now();
1082
+ const effectiveOptions = withCollectionTimeout(options);
1079
1083
  try {
1080
- const repo = await resolveGitRepository(workspace, options);
1084
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1081
1085
  const repoRoot = repo.repoRoot;
1082
1086
  if (options.baseRef) {
1083
1087
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1084
1088
  const [nameStatus, numstat] = await Promise.all([
1085
- runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...options, cwd: repoRoot }),
1086
- runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...options, cwd: repoRoot })
1089
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...effectiveOptions, cwd: repoRoot }),
1090
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...effectiveOptions, cwd: repoRoot })
1087
1091
  ]);
1088
1092
  const outputBytes2 = byteLength(nameStatus.stdout + numstat.stdout);
1089
1093
  const changes2 = combineDiffEntries(nameStatus.stdout, numstat.stdout, false);
@@ -1102,11 +1106,11 @@ async function getGitDiffSummary(workspace, options = {}) {
1102
1106
  };
1103
1107
  }
1104
1108
  const [unstagedNameStatus, unstagedNumstat, stagedNameStatus, stagedNumstat, untracked] = await Promise.all([
1105
- runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1106
- runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1107
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1108
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1109
- runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...options, cwd: repoRoot })
1109
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1110
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1111
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1112
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1113
+ runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...effectiveOptions, cwd: repoRoot })
1110
1114
  ]);
1111
1115
  const outputBytes = byteLength(
1112
1116
  unstagedNameStatus.stdout + unstagedNumstat.stdout + stagedNameStatus.stdout + stagedNumstat.stdout + untracked.stdout
@@ -1148,13 +1152,14 @@ async function getGitDiffSummary(workspace, options = {}) {
1148
1152
  }
1149
1153
  async function getGitFileDiff(workspace, filePath, options = {}) {
1150
1154
  const lastCheckedAt = Date.now();
1151
- const repo = await resolveGitRepository(workspace, options);
1155
+ const effectiveOptions = withCollectionTimeout(options);
1156
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1152
1157
  const repoRoot = repo.repoRoot;
1153
1158
  const selected = await resolveRepoFilePath(repoRoot, filePath);
1154
1159
  const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
1155
1160
  if (options.baseRef) {
1156
1161
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1157
- const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...options, cwd: repoRoot });
1162
+ const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot });
1158
1163
  const bounded2 = truncateText(result.stdout, maxBytes);
1159
1164
  return {
1160
1165
  workspace: repo.workspace,
@@ -1167,13 +1172,13 @@ async function getGitFileDiff(workspace, filePath, options = {}) {
1167
1172
  };
1168
1173
  }
1169
1174
  const [unstaged, staged] = await Promise.all([
1170
- runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot }),
1171
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot })
1175
+ runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot }),
1176
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot })
1172
1177
  ]);
1173
1178
  let diff = [unstaged.stdout, staged.stdout].filter((part) => part.length > 0).join("\n");
1174
1179
  if (!diff) {
1175
1180
  const untracked = await runGit(repo, ["ls-files", "--others", "--exclude-standard", "--", selected.relativePath], {
1176
- ...options,
1181
+ ...effectiveOptions,
1177
1182
  cwd: repoRoot
1178
1183
  });
1179
1184
  const untrackedFiles = untracked.stdout.split("\n").filter(Boolean);
@@ -4817,9 +4822,12 @@ function getActiveDirectDispatches(meshId) {
4817
4822
  return [];
4818
4823
  }
4819
4824
  }
4820
- function updateDirectDispatchStatus(meshId, sessionId, status) {
4825
+ function updateDirectDispatchStatus(meshId, sessionId, status, taskId) {
4821
4826
  try {
4822
- MeshRuntimeStore.getInstance().updateDirectDispatchStatus(meshId, sessionId, status);
4827
+ if (!taskId) {
4828
+ 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)`);
4829
+ }
4830
+ MeshRuntimeStore.getInstance().updateDirectDispatchStatus(meshId, sessionId, status, taskId);
4823
4831
  } catch {
4824
4832
  }
4825
4833
  }
@@ -5651,9 +5659,25 @@ var init_mesh_runtime_store = __esm({
5651
5659
  updatedAt: r.updated_at
5652
5660
  }));
5653
5661
  }
5654
- updateDirectDispatchStatus(meshId, sessionId, status) {
5655
- if (!sessionId) return;
5662
+ // CANON-B (dispatch identity): mesh_direct_dispatches is keyed by task_id (PK), but a
5663
+ // single session can host several sequential direct dispatches (re-dispatch / nudge), so
5664
+ // matching a status flip by session_id alone hits EVERY non-terminal row for that session
5665
+ // — flipping a sibling task's row and stranding the one whose event actually fired (the
5666
+ // assigned-stranded watchdog then requeues a task that is really still generating). When
5667
+ // the firing event carries a taskId, target the single PK row; the session_id match is the
5668
+ // legacy fallback only for events that arrive without a taskId.
5669
+ updateDirectDispatchStatus(meshId, sessionId, status, taskId) {
5656
5670
  const now = (/* @__PURE__ */ new Date()).toISOString();
5671
+ if (taskId) {
5672
+ this.db.prepare(`
5673
+ UPDATE mesh_direct_dispatches
5674
+ SET status = @status, updated_at = @updatedAt
5675
+ WHERE mesh_id = @meshId AND task_id = @taskId
5676
+ AND status NOT IN ('completed', 'failed')
5677
+ `).run({ status, meshId, taskId, updatedAt: now });
5678
+ return;
5679
+ }
5680
+ if (!sessionId) return;
5657
5681
  this.db.prepare(`
5658
5682
  UPDATE mesh_direct_dispatches
5659
5683
  SET status = @status, updated_at = @updatedAt
@@ -7382,7 +7406,7 @@ function resolveWin32Executable(command) {
7382
7406
  windowsHide: true
7383
7407
  }).trim();
7384
7408
  if (out) {
7385
- const matches = out.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
7409
+ const matches = out.split(/\r?\n/).map((s2) => s2.trim()).filter(Boolean);
7386
7410
  const direct = matches.find((m) => DIRECT_EXEC_EXT.has(path10.extname(m).toLowerCase()));
7387
7411
  return direct || matches[0] || command;
7388
7412
  }
@@ -8734,11 +8758,29 @@ function hasPendingRefineTerminalEventDuplicate(event) {
8734
8758
  (pending) => pending.event === event.event && readRefineJobId2(pending) === jobId
8735
8759
  );
8736
8760
  }
8761
+ function isWeakCompletionMetadata(metadata) {
8762
+ const evidenceLevel = readNonEmptyString2(metadata.evidenceLevel);
8763
+ if (evidenceLevel === "insufficient" || evidenceLevel === "weak") return true;
8764
+ if (metadata.reviewRecommended === true) return true;
8765
+ const diag = readRecord4(metadata.completionDiagnostic);
8766
+ return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
8767
+ }
8737
8768
  function buildPendingEventFingerprint(event) {
8738
8769
  const metadata = readRecord4(event.metadataEvent) || {};
8739
8770
  if (event.event === "worktree_bootstrap_complete" || event.event === "worktree_bootstrap_failed") {
8740
8771
  return [event.meshId, event.event, event.nodeId || ""].join("::");
8741
8772
  }
8773
+ if (TERMINAL_COMPLETION_EVENTS.has(event.event)) {
8774
+ const terminalTaskId = readNonEmptyString2(metadata.taskId) || readNonEmptyString2(readRecord4(metadata.payload)?.taskId);
8775
+ if (terminalTaskId) {
8776
+ return [
8777
+ event.meshId,
8778
+ event.event,
8779
+ terminalTaskId,
8780
+ isWeakCompletionMetadata(metadata) ? "weak" : "genuine"
8781
+ ].join("::");
8782
+ }
8783
+ }
8742
8784
  const sessionId = resolveEventSessionId(metadata);
8743
8785
  const providerSessionId = readNonEmptyString2(metadata.providerSessionId);
8744
8786
  const taskId = readNonEmptyString2(metadata.taskId) || readNonEmptyString2(readRecord4(metadata.payload)?.taskId);
@@ -9097,7 +9139,7 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
9097
9139
  }
9098
9140
  }
9099
9141
  }
9100
- var REFINE_TERMINAL_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
9142
+ var REFINE_TERMINAL_EVENTS, TERMINAL_COMPLETION_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
9101
9143
  var init_mesh_events_pending = __esm({
9102
9144
  "src/mesh/mesh-events-pending.ts"() {
9103
9145
  "use strict";
@@ -9107,6 +9149,7 @@ var init_mesh_events_pending = __esm({
9107
9149
  init_mesh_events_utils();
9108
9150
  init_dist();
9109
9151
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9152
+ TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
9110
9153
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9111
9154
  MAX_PENDING_EVENTS_KEEP = 50;
9112
9155
  }
@@ -9436,7 +9479,7 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
9436
9479
  evidence
9437
9480
  }
9438
9481
  });
9439
- updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9482
+ updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed", args.taskId);
9440
9483
  markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
9441
9484
  setImmediate(() => cleanupTerminalDirectDispatches());
9442
9485
  queuePendingMeshCoordinatorEvent({
@@ -9731,8 +9774,8 @@ function parsePatternEntry(x) {
9731
9774
  if (x instanceof RegExp) return x;
9732
9775
  if (x && typeof x === "object" && typeof x.source === "string") {
9733
9776
  try {
9734
- const s = x;
9735
- return new RegExp(s.source, s.flags || "");
9777
+ const s2 = x;
9778
+ return new RegExp(s2.source, s2.flags || "");
9736
9779
  } catch {
9737
9780
  return null;
9738
9781
  }
@@ -10300,6 +10343,38 @@ var init_mesh_unresolved_forward_outbox = __esm({
10300
10343
  }
10301
10344
  });
10302
10345
 
10346
+ // src/mesh/mesh-event-trace.ts
10347
+ function s(v) {
10348
+ return typeof v === "string" && v.trim() ? v.trim() : "";
10349
+ }
10350
+ function meshEventTraceKey(ctx) {
10351
+ const segs = [`task=${s(ctx.taskId) || "-"}`];
10352
+ const eventId = s(ctx.eventId);
10353
+ if (eventId) segs.push(`evt=${eventId}`);
10354
+ segs.push(`sess=${s(ctx.sessionId) || "-"}`);
10355
+ const nodeId = s(ctx.nodeId);
10356
+ if (nodeId) segs.push(`node=${nodeId}`);
10357
+ const meshId = s(ctx.meshId);
10358
+ if (meshId) segs.push(`mesh=${meshId}`);
10359
+ const event = s(ctx.event);
10360
+ if (event) segs.push(`event=${event}`);
10361
+ return segs.join(" ");
10362
+ }
10363
+ function traceMeshEventStage(stage, ctx, detail) {
10364
+ LOG.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
10365
+ }
10366
+ function traceMeshEventDrop(reason, ctx, detail) {
10367
+ LOG.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
10368
+ }
10369
+ var CAT;
10370
+ var init_mesh_event_trace = __esm({
10371
+ "src/mesh/mesh-event-trace.ts"() {
10372
+ "use strict";
10373
+ init_logger();
10374
+ CAT = "EvtTrace";
10375
+ }
10376
+ });
10377
+
10303
10378
  // src/config/state-store.ts
10304
10379
  import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
10305
10380
  import { join as join18 } from "path";
@@ -12085,9 +12160,9 @@ function buildAcpSession(state, options) {
12085
12160
  }
12086
12161
  function buildSessionEntries(allStates, cdpManagers, options = {}) {
12087
12162
  const sessions = [];
12088
- const ideStates = allStates.filter((s) => s.category === "ide");
12089
- const cliStates = allStates.filter((s) => s.category === "cli");
12090
- const acpStates = allStates.filter((s) => s.category === "acp");
12163
+ const ideStates = allStates.filter((s2) => s2.category === "ide");
12164
+ const cliStates = allStates.filter((s2) => s2.category === "cli");
12165
+ const acpStates = allStates.filter((s2) => s2.category === "acp");
12091
12166
  for (const state of ideStates) {
12092
12167
  sessions.push(buildWorkspaceSession(state, cdpManagers, options));
12093
12168
  for (const ext of state.extensions) {
@@ -12572,6 +12647,15 @@ function getCachedMeshByWorkspace(workspace) {
12572
12647
  meshByWorkspaceCache.set(workspace, { mesh, cachedAt: now });
12573
12648
  return mesh;
12574
12649
  }
12650
+ function recoverMeshIdByNodeId(nodeId) {
12651
+ if (!nodeId) return "";
12652
+ for (const mesh of listMeshes()) {
12653
+ if (Array.isArray(mesh.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, nodeId))) {
12654
+ return readNonEmptyString2(mesh.id);
12655
+ }
12656
+ }
12657
+ return "";
12658
+ }
12575
12659
  function __resetIdleAutoFastForwardForTests() {
12576
12660
  idleAutoFastForwardLastAttempt.clear();
12577
12661
  }
@@ -13474,6 +13558,13 @@ function shouldForceInjectMeshEvent(eventName) {
13474
13558
  function injectMeshSystemMessage(components, args) {
13475
13559
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
13476
13560
  const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
13561
+ const traceCtx = {
13562
+ taskId: args.metadataEvent.taskId,
13563
+ sessionId: eventSessionId,
13564
+ nodeId: eventNodeId,
13565
+ meshId: args.meshId,
13566
+ event: args.event
13567
+ };
13477
13568
  const sourceSession = args.sourceInstanceId ? components.instanceManager.getInstance(args.sourceInstanceId) : void 0;
13478
13569
  const workerCoordinatorDaemonId = readNonEmptyString2(
13479
13570
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
@@ -13527,6 +13618,7 @@ function injectMeshSystemMessage(components, args) {
13527
13618
  }
13528
13619
  }
13529
13620
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
13621
+ traceMeshEventDrop("intentional_cleanup_stop", traceCtx);
13530
13622
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
13531
13623
  }
13532
13624
  if (args.event === "monitor:no_progress") {
@@ -13547,6 +13639,7 @@ function injectMeshSystemMessage(components, args) {
13547
13639
  }
13548
13640
  if (reconciledCompletion?.source === "no_progress_terminal_ledger_suppression") {
13549
13641
  LOG.info("MeshEvents", `Suppressed no-progress monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
13642
+ traceMeshEventDrop("no_progress_terminal_ledger_suppression", traceCtx, `terminalKind=${reconciledCompletion.terminalLedgerKind}`);
13550
13643
  return {
13551
13644
  success: true,
13552
13645
  forwarded: 0,
@@ -13558,6 +13651,7 @@ function injectMeshSystemMessage(components, args) {
13558
13651
  }
13559
13652
  if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
13560
13653
  LOG.info("MeshEvents", `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
13654
+ traceMeshEventDrop("duplicate_refine_terminal", traceCtx);
13561
13655
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
13562
13656
  }
13563
13657
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
@@ -13572,6 +13666,7 @@ function injectMeshSystemMessage(components, args) {
13572
13666
  });
13573
13667
  if (duplicateApproval) {
13574
13668
  LOG.info("MeshEvents", `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
13669
+ traceMeshEventDrop("duplicate_approval", traceCtx);
13575
13670
  return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
13576
13671
  }
13577
13672
  }
@@ -13591,6 +13686,7 @@ function injectMeshSystemMessage(components, args) {
13591
13686
  const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
13592
13687
  if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "no_progress_reconciliation") {
13593
13688
  LOG.info("MeshEvents", `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
13689
+ traceMeshEventDrop("duplicate_completion_terminal_ledger", traceCtx);
13594
13690
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
13595
13691
  }
13596
13692
  }
@@ -13609,6 +13705,7 @@ function injectMeshSystemMessage(components, args) {
13609
13705
  });
13610
13706
  if (duplicateCompletion) {
13611
13707
  LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
13708
+ traceMeshEventDrop("duplicate_completion", traceCtx);
13612
13709
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
13613
13710
  }
13614
13711
  }
@@ -13627,6 +13724,7 @@ function injectMeshSystemMessage(components, args) {
13627
13724
  });
13628
13725
  if (duplicateStopped) {
13629
13726
  LOG.info("MeshEvents", `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
13727
+ traceMeshEventDrop("duplicate_stopped", traceCtx);
13630
13728
  return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
13631
13729
  }
13632
13730
  }
@@ -13638,7 +13736,7 @@ function injectMeshSystemMessage(components, args) {
13638
13736
  });
13639
13737
  const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
13640
13738
  if (!leaveDirectDispatchActive) {
13641
- updateDirectDispatchStatus(args.meshId, sessionId, outcome);
13739
+ updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
13642
13740
  }
13643
13741
  markSessionDeliveriesTerminal(args.meshId, sessionId, outcome);
13644
13742
  setImmediate(() => cleanupTerminalDirectDispatches());
@@ -13651,7 +13749,7 @@ function injectMeshSystemMessage(components, args) {
13651
13749
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
13652
13750
  const providerType = readNonEmptyString2(args.metadataEvent.providerType);
13653
13751
  if (sessionId) {
13654
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13752
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13655
13753
  const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
13656
13754
  completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
13657
13755
  if (nodeId && providerType) {
@@ -13734,7 +13832,8 @@ function injectMeshSystemMessage(components, args) {
13734
13832
  }
13735
13833
  }
13736
13834
  if (sessionId) {
13737
- updateDirectDispatchStatus(args.meshId, sessionId, "acked");
13835
+ const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
13836
+ updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
13738
13837
  const activeDeliveries = (() => {
13739
13838
  try {
13740
13839
  return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(args.meshId, sessionId);
@@ -13742,7 +13841,8 @@ function injectMeshSystemMessage(components, args) {
13742
13841
  return [];
13743
13842
  }
13744
13843
  })();
13745
- for (const d of activeDeliveries) {
13844
+ const deliveriesToAck = startedTaskId ? activeDeliveries.filter((d) => d.taskId === startedTaskId) : activeDeliveries;
13845
+ for (const d of deliveriesToAck) {
13746
13846
  updateSessionDeliveryStatus(d.id, "acked");
13747
13847
  }
13748
13848
  }
@@ -13756,7 +13856,7 @@ function injectMeshSystemMessage(components, args) {
13756
13856
  }
13757
13857
  }
13758
13858
  if (sessionId) {
13759
- directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13859
+ directDispatchTaskIdForLedger = readNonEmptyString2(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
13760
13860
  completedTaskForLedger = markSessionTerminal(sessionId, "failed");
13761
13861
  }
13762
13862
  }
@@ -13902,6 +14002,9 @@ function injectMeshSystemMessage(components, args) {
13902
14002
  };
13903
14003
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
13904
14004
  LOG.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""}${workerCoordinatorSessionId ? `, coordinator session ${workerCoordinatorSessionId}` : ""})`);
14005
+ traceMeshEventStage("queued", traceCtx, workerCoordinatorDaemonId ? `coordinatorDaemon=${workerCoordinatorDaemonId}` : "broadcast");
14006
+ } else {
14007
+ traceMeshEventDrop("queue_dedup", traceCtx);
13905
14008
  }
13906
14009
  return { success: true, forwarded: 0 };
13907
14010
  }
@@ -13912,8 +14015,23 @@ function handleMeshForwardEvent(components, payload) {
13912
14015
  }
13913
14016
  const nodeId = readNonEmptyString2(payload.nodeId);
13914
14017
  const workspace = readNonEmptyString2(payload.workspace);
13915
- const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "");
13916
- if (!meshId) return { success: false, error: "meshId required" };
14018
+ const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "") || recoverMeshIdByNodeId(nodeId);
14019
+ if (!meshId) {
14020
+ traceMeshEventDrop("meshId_required", {
14021
+ taskId: payload.taskId,
14022
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14023
+ nodeId,
14024
+ event: eventName
14025
+ }, workspace ? `workspace=${workspace} unresolved` : "no workspace/nodeId");
14026
+ return { success: false, error: "meshId required" };
14027
+ }
14028
+ traceMeshEventStage("received", {
14029
+ taskId: payload.taskId,
14030
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14031
+ nodeId,
14032
+ meshId,
14033
+ event: eventName
14034
+ });
13917
14035
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
13918
14036
  const relayModalMessage = readNonEmptyString2(payload.modalMessage);
13919
14037
  const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
@@ -13994,9 +14112,18 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
13994
14112
  workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
13995
14113
  };
13996
14114
  const persisted = enqueueUnresolvedDelegateForward(coordinatorDaemonId, eventName, payload);
14115
+ const fwdTraceCtx = {
14116
+ taskId: payload.taskId,
14117
+ sessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId),
14118
+ nodeId: readNonEmptyString2(routing.nodeId) || readNonEmptyString2(event.meshNodeId),
14119
+ event: eventName
14120
+ };
14121
+ traceMeshEventStage("outbox_enqueue", fwdTraceCtx, `coordinatorDaemon=${coordinatorDaemonId} meshId=absent`);
14122
+ traceMeshEventStage("forward_send", fwdTraceCtx, "immediate push");
13997
14123
  Promise.resolve(components.dispatchMeshCommand(coordinatorDaemonId, "mesh_forward_event", payload)).then((result) => {
13998
14124
  if (result && result.success === false) {
13999
14125
  LOG.warn("MeshEvents", `Immediate forward of ${eventName} to coordinator ${coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued for retry`);
14126
+ traceMeshEventDrop("immediate_forward_rejected", fwdTraceCtx, readNonEmptyString2(result.error) || "no reason");
14000
14127
  return;
14001
14128
  }
14002
14129
  if (persisted) ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload);
@@ -14064,6 +14191,14 @@ function setupMeshEventForwarding(components) {
14064
14191
  if (isUnroutableDelegateRejection(routing) && forwardUnresolvedDelegateEvent(components, routing, event)) {
14065
14192
  return;
14066
14193
  }
14194
+ if (isUnroutableDelegateRejection(routing)) {
14195
+ traceMeshEventDrop("unroutable", {
14196
+ taskId: event.meshActiveTaskId ?? event.taskId,
14197
+ sessionId: routing.sessionId,
14198
+ nodeId: routing.nodeId,
14199
+ event: event.event
14200
+ }, "no coordinator anchor / mesh_unresolved");
14201
+ }
14067
14202
  recordUnroutableDelegateEvent(routing, event.event);
14068
14203
  return;
14069
14204
  }
@@ -14093,6 +14228,7 @@ var init_mesh_events_coordinator = __esm({
14093
14228
  init_mesh_events_pending();
14094
14229
  init_mesh_routing();
14095
14230
  init_mesh_unresolved_forward_outbox();
14231
+ init_mesh_event_trace();
14096
14232
  init_snapshot();
14097
14233
  init_repo_mesh_types();
14098
14234
  init_dist();
@@ -14204,6 +14340,13 @@ function findLiveCoordinators(components) {
14204
14340
  function injectPendingIntoCoordinator(coordinator, pending) {
14205
14341
  if (!coordinator || !pending.coordinatorMessage) return;
14206
14342
  const force = shouldForceInjectMeshEvent(pending.event);
14343
+ traceMeshEventStage("surfaced", {
14344
+ taskId: pending.metadataEvent?.taskId,
14345
+ sessionId: pending.metadataEvent?.targetSessionId ?? pending.targetCoordinatorSessionId,
14346
+ nodeId: pending.nodeId,
14347
+ meshId: pending.meshId,
14348
+ event: pending.event
14349
+ }, force ? "force-inject" : "inject");
14207
14350
  coordinator.onEvent("send_message", {
14208
14351
  input: { text: pending.coordinatorMessage, textFallback: pending.coordinatorMessage },
14209
14352
  ...force ? { force: true } : {}
@@ -14262,6 +14405,13 @@ function recoverStrandedAssignedDispatches(meshId, store) {
14262
14405
  });
14263
14406
  if (reclaimed) {
14264
14407
  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})`);
14408
+ traceMeshEventDrop("assigned_stranded_reclaim", {
14409
+ taskId: row.id,
14410
+ sessionId: row.assignedSessionId,
14411
+ nodeId: row.assignedNodeId,
14412
+ meshId,
14413
+ event: "agent:generating_completed"
14414
+ }, `unconfirmed ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s \u2192 ${reclaimed.status}`);
14265
14415
  }
14266
14416
  }
14267
14417
  }
@@ -14421,6 +14571,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14421
14571
  try {
14422
14572
  queuePendingMeshCoordinatorEvent(pending);
14423
14573
  LOG.info("MeshReconcile", `Strict route hold: coordinator session ${wantSession} not live on mesh ${meshId} \u2014 re-queued (${pending.event})`);
14574
+ traceMeshEventDrop("strict_route_hold", {
14575
+ taskId: pending.metadataEvent?.taskId,
14576
+ sessionId: pending.metadataEvent?.targetSessionId ?? wantSession,
14577
+ nodeId: pending.nodeId,
14578
+ meshId,
14579
+ event: pending.event
14580
+ }, `coordinatorSession=${wantSession} not live`);
14424
14581
  } catch (e) {
14425
14582
  LOG.warn("MeshReconcile", `Strict route re-queue failed for ${pending.event} on mesh ${meshId}: ${e?.message || e}`);
14426
14583
  }
@@ -14444,6 +14601,13 @@ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
14444
14601
  }
14445
14602
  });
14446
14603
  LOG.warn("MeshReconcile", `Strict route expire: coordinator session ${wantSession} never returned for mesh ${meshId} \u2014 recorded to ledger (recoverable), dropped (${pending.event})`);
14604
+ traceMeshEventDrop("strict_route_expired", {
14605
+ taskId: pending.metadataEvent?.taskId,
14606
+ sessionId: pending.metadataEvent?.targetSessionId ?? wantSession,
14607
+ nodeId: pending.nodeId,
14608
+ meshId,
14609
+ event: pending.event
14610
+ }, `coordinatorSession=${wantSession} never returned`);
14447
14611
  } catch (e) {
14448
14612
  LOG.warn("MeshReconcile", `Failed to ledger-expire strict-unmatched ${pending.event} for mesh ${meshId}: ${e?.message || e}`);
14449
14613
  }
@@ -14455,15 +14619,24 @@ async function retryUnresolvedDelegateForwards(components) {
14455
14619
  const entries = peekUnresolvedDelegateForwards();
14456
14620
  if (entries.length === 0) return;
14457
14621
  for (const entry of entries) {
14622
+ const entryTraceCtx = {
14623
+ taskId: entry.payload.taskId,
14624
+ sessionId: readNonEmptyString2(entry.payload.targetSessionId) || readNonEmptyString2(entry.payload.sessionId),
14625
+ nodeId: readNonEmptyString2(entry.payload.nodeId),
14626
+ event: readNonEmptyString2(entry.payload.event)
14627
+ };
14458
14628
  let result;
14459
14629
  try {
14630
+ traceMeshEventStage("forward_send", entryTraceCtx, `retry \u2192 ${entry.coordinatorDaemonId}`);
14460
14631
  result = await dispatchMeshCommand(entry.coordinatorDaemonId, "mesh_forward_event", entry.payload);
14461
14632
  } catch (e) {
14462
14633
  LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} failed: ${e?.message || e} \u2014 left queued`);
14634
+ traceMeshEventDrop("retry_forward_failed", entryTraceCtx, e?.message || String(e));
14463
14635
  continue;
14464
14636
  }
14465
14637
  if (result && result.success === false) {
14466
14638
  LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued`);
14639
+ traceMeshEventDrop("retry_forward_rejected", entryTraceCtx, readNonEmptyString2(result.error) || "no reason");
14467
14640
  continue;
14468
14641
  }
14469
14642
  ackUnresolvedDelegateForward(entry.id);
@@ -14705,6 +14878,7 @@ var init_mesh_reconcile_loop = __esm({
14705
14878
  init_mesh_events_coordinator();
14706
14879
  init_mesh_unresolved_forward_outbox();
14707
14880
  init_mesh_events_utils();
14881
+ init_mesh_event_trace();
14708
14882
  init_dist();
14709
14883
  init_mesh_work_queue();
14710
14884
  init_mesh_ledger();
@@ -15543,8 +15717,8 @@ function saveProvidersActive(file) {
15543
15717
  }
15544
15718
  function isValidSource(x) {
15545
15719
  if (!x || typeof x !== "object") return false;
15546
- const s = x;
15547
- 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";
15720
+ const s2 = x;
15721
+ 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";
15548
15722
  }
15549
15723
  function deriveSourceName(url) {
15550
15724
  const m = url.match(/[/:]([^/:]+)\/([^/]+?)(?:\.git)?$/);
@@ -15600,7 +15774,7 @@ function inventoryExternalSources() {
15600
15774
  }
15601
15775
  function sourcesProviding(category, type) {
15602
15776
  const inventory = inventoryExternalSources();
15603
- return inventory.filter((s) => (s.providers[category] || []).includes(type)).map((s) => s.sourceName);
15777
+ return inventory.filter((s2) => (s2.providers[category] || []).includes(type)).map((s2) => s2.sourceName);
15604
15778
  }
15605
15779
  function resolveActiveSource(category, type, activeFile) {
15606
15780
  const candidates = sourcesProviding(category, type);
@@ -15804,10 +15978,10 @@ function compileSettledPromptMatchers(spec) {
15804
15978
  const footers = (spec.withFooter ?? []).map((f) => {
15805
15979
  if (f.kind === "regex") {
15806
15980
  const re = compile2(f.pattern, f.flags ?? "i");
15807
- return { test: (s) => re.test(s) };
15981
+ return { test: (s2) => re.test(s2) };
15808
15982
  }
15809
15983
  const needle = f.pattern.toLowerCase();
15810
- return { test: (s) => s.toLowerCase().includes(needle) };
15984
+ return { test: (s2) => s2.toLowerCase().includes(needle) };
15811
15985
  });
15812
15986
  return { prompt, footers };
15813
15987
  }
@@ -16681,7 +16855,7 @@ var init_cli_state_engine = __esm({
16681
16855
  }
16682
16856
  resolveModal(buttonIndex) {
16683
16857
  const snap = this.transport.getSnapshot();
16684
- const parseApproval = typeof this.transport.runParseApproval === "function" ? (s) => this.transport.runParseApproval(s.recentOutputBuffer.slice(-500)) : (s) => this.runParseApproval(s);
16858
+ const parseApproval = typeof this.transport.runParseApproval === "function" ? (s2) => this.transport.runParseApproval(s2.recentOutputBuffer.slice(-500)) : (s2) => this.runParseApproval(s2);
16685
16859
  let modal = this.activeModal ?? parseApproval(snap);
16686
16860
  if (!modal && this.runner.hasParseSession()) {
16687
16861
  try {
@@ -19369,22 +19543,23 @@ function resolveSections(sectionsObj, lines) {
19369
19543
  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]));
19370
19544
  let idx = -1;
19371
19545
  for (const c of candidates) {
19546
+ let candIdx = -1;
19372
19547
  if (sec.anchor_last) {
19373
19548
  for (let i = total - 1; i >= 0; i--) {
19374
19549
  if (matchesCandidate(c, i)) {
19375
- idx = i;
19550
+ candIdx = i;
19376
19551
  break;
19377
19552
  }
19378
19553
  }
19379
19554
  } else {
19380
19555
  for (let i = 0; i < total; i++) {
19381
19556
  if (matchesCandidate(c, i)) {
19382
- idx = i;
19557
+ candIdx = i;
19383
19558
  break;
19384
19559
  }
19385
19560
  }
19386
19561
  }
19387
- if (idx !== -1) break;
19562
+ if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
19388
19563
  }
19389
19564
  if (idx !== -1) {
19390
19565
  from = idx;
@@ -19436,7 +19611,7 @@ function resolveSections(sectionsObj, lines) {
19436
19611
  }
19437
19612
  function sectionText(sections, sectionId, fullScreen) {
19438
19613
  if (!sectionId) return fullScreen;
19439
- const found = sections.find((s) => s.id === sectionId);
19614
+ const found = sections.find((s2) => s2.id === sectionId);
19440
19615
  return found ? found.text : "";
19441
19616
  }
19442
19617
  function isRegexCondition(c) {
@@ -19604,10 +19779,10 @@ function isV4Spec(raw) {
19604
19779
  return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
19605
19780
  }
19606
19781
  function initialState(spec) {
19607
- return spec.states.find((s) => s.initial) ?? spec.states[0];
19782
+ return spec.states.find((s2) => s2.initial) ?? spec.states[0];
19608
19783
  }
19609
19784
  function stateById(spec, id) {
19610
- return spec.states.find((s) => s.id === id);
19785
+ return spec.states.find((s2) => s2.id === id);
19611
19786
  }
19612
19787
  function outgoingTransitions(spec, stateId) {
19613
19788
  const matches = spec.transitions.filter((t) => {
@@ -19696,7 +19871,17 @@ function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTr
19696
19871
  const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
19697
19872
  const kind = isRegex(cond) ? "regex" : "changed";
19698
19873
  const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
19699
- return { kind, result, detail };
19874
+ let matchedText;
19875
+ if (result && isRegex(cond)) {
19876
+ try {
19877
+ const hay = sectionText(sections, cond.section, fullScreen);
19878
+ const re = new RegExp(cond.matches, cond.flags ?? "i");
19879
+ const m = re.exec(hay);
19880
+ if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
19881
+ } catch {
19882
+ }
19883
+ }
19884
+ return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
19700
19885
  }
19701
19886
  return { kind: "all", result: false, detail: "unknown condition" };
19702
19887
  }
@@ -19812,17 +19997,17 @@ function validateFsmSpec(raw) {
19812
19997
  }
19813
19998
  const ids = /* @__PURE__ */ new Set();
19814
19999
  let initialCount = 0;
19815
- for (const [i, s] of spec.states.entries()) {
19816
- if (!s.id) {
20000
+ for (const [i, s2] of spec.states.entries()) {
20001
+ if (!s2.id) {
19817
20002
  errs.push(`states[${i}].id is required`);
19818
20003
  continue;
19819
20004
  }
19820
- if (ids.has(s.id)) errs.push(`states[${i}].id "${s.id}" is duplicated`);
19821
- ids.add(s.id);
19822
- if (!s.label) errs.push(`states[${i}].label is required`);
19823
- if (s.initial) initialCount += 1;
19824
- if (s.status && !["idle", "generating", "approval"].includes(s.status)) {
19825
- errs.push(`states[${i}].status "${s.status}" must be idle|generating|approval`);
20005
+ if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
20006
+ ids.add(s2.id);
20007
+ if (!s2.label) errs.push(`states[${i}].label is required`);
20008
+ if (s2.initial) initialCount += 1;
20009
+ if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
20010
+ errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
19826
20011
  }
19827
20012
  }
19828
20013
  if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
@@ -19838,10 +20023,10 @@ function validateFsmSpec(raw) {
19838
20023
  else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
19839
20024
  if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
19840
20025
  }
19841
- for (const [i, s] of spec.states.entries()) {
19842
- const sec = s.extract?.title?.section;
20026
+ for (const [i, s2] of spec.states.entries()) {
20027
+ const sec = s2.extract?.title?.section;
19843
20028
  if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
19844
- const bsec = s.extract?.buttons?.section;
20029
+ const bsec = s2.extract?.buttons?.section;
19845
20030
  if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
19846
20031
  }
19847
20032
  return errs;
@@ -27072,6 +27257,42 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
27072
27257
  return fn() || null;
27073
27258
  }
27074
27259
 
27260
+ // src/providers/manual-attendance.ts
27261
+ var AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS = 6e4;
27262
+ var ManualAttendanceTracker = class {
27263
+ constructor(suppressMs = AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS) {
27264
+ this.suppressMs = suppressMs;
27265
+ }
27266
+ lastInteractionAt = 0;
27267
+ /** Record that a human just drove this session by hand. */
27268
+ note(now = Date.now()) {
27269
+ this.lastInteractionAt = now;
27270
+ }
27271
+ /** True while a manual interaction is recent enough to suppress auto-approve. */
27272
+ isAttended(now = Date.now()) {
27273
+ return this.lastInteractionAt > 0 && now - this.lastInteractionAt < this.suppressMs;
27274
+ }
27275
+ /**
27276
+ * Milliseconds remaining in the current suppression window, or 0 when not
27277
+ * attended. Used to re-arm a re-check timer so auto-approve fires the moment
27278
+ * the window lapses even if the PTY/agent has since gone silent.
27279
+ */
27280
+ remainingMs(now = Date.now()) {
27281
+ if (this.lastInteractionAt <= 0) return 0;
27282
+ return Math.max(0, this.suppressMs - (now - this.lastInteractionAt));
27283
+ }
27284
+ };
27285
+ var MANUAL_ATTENDANCE_COMMANDS = /* @__PURE__ */ new Set([
27286
+ "select_session",
27287
+ "open_panel",
27288
+ "invoke_provider_script",
27289
+ "set_mode",
27290
+ "change_model",
27291
+ "set_thought_level",
27292
+ "resolve_action",
27293
+ "pty_input"
27294
+ ]);
27295
+
27075
27296
  // src/commands/chat-commands.ts
27076
27297
  init_contracts();
27077
27298
  init_provider_input_support();
@@ -31411,11 +31632,38 @@ var DaemonCommandHandler = class {
31411
31632
  setAgentStreamManager(manager) {
31412
31633
  this._agentStream = manager;
31413
31634
  }
31635
+ /**
31636
+ * When a command in the manual-attendance set arrives for a session this
31637
+ * daemon hosts, stamp the live instance so auto-approve holds while the user
31638
+ * drives the session by hand. Provider-common: the signal is the command
31639
+ * (foreground select_session / open_panel, controlbar invoke_provider_script
31640
+ * / set_mode / change_model / set_thought_level, manual resolve_action,
31641
+ * pty_input), never any CLI-specific modal text — so it works identically for
31642
+ * every CLI/ACP provider. send_chat is deliberately excluded because a
31643
+ * coordinator delegating a task to a worker also uses send_chat; counting it
31644
+ * would wrongly suppress the worker's delegated auto-approve. For a remote
31645
+ * mesh worker session the controlbar commands are forwarded to the owning
31646
+ * worker daemon, which runs this same hook there, so attendance is recorded
31647
+ * on the daemon that actually hosts the instance.
31648
+ */
31649
+ noteManualAttendanceIfApplicable(cmd, args) {
31650
+ if (!MANUAL_ATTENDANCE_COMMANDS.has(cmd)) return;
31651
+ const sessionId = this._currentRoute.session?.sessionId || (typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "");
31652
+ if (!sessionId) return;
31653
+ const session = this._ctx.sessionRegistry?.get(sessionId);
31654
+ const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
31655
+ const instance = this._ctx.instanceManager?.getInstance(instanceKey);
31656
+ try {
31657
+ instance?.noteManualInteraction?.();
31658
+ } catch {
31659
+ }
31660
+ }
31414
31661
  // ─── Command Dispatcher ──────────────────────────
31415
31662
  async handle(cmd, args) {
31416
31663
  this._currentRoute = this.resolveRoute(args);
31417
31664
  const startedAt = Date.now();
31418
31665
  this.logCommandStart(cmd, args);
31666
+ this.noteManualAttendanceIfApplicable(cmd, args);
31419
31667
  let result;
31420
31668
  if (isGitCommandName(cmd)) {
31421
31669
  result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
@@ -32150,10 +32398,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32150
32398
  const path42 = __require("path");
32151
32399
  const { spawnSync: spawnSync2 } = __require("child_process");
32152
32400
  const file = ext.loadExternalSources();
32153
- if (file.sources.some((s) => s.name === requestedName)) {
32401
+ if (file.sources.some((s2) => s2.name === requestedName)) {
32154
32402
  return { success: false, error: `source name "${requestedName}" is already registered` };
32155
32403
  }
32156
- if (file.sources.some((s) => s.url === url && s.ref === ref)) {
32404
+ if (file.sources.some((s2) => s2.url === url && s2.ref === ref)) {
32157
32405
  return { success: false, error: `source url+ref already registered (use a different name to track another ref)` };
32158
32406
  }
32159
32407
  const sourceDir = path42.join(ext.externalRoot(), requestedName);
@@ -32215,7 +32463,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32215
32463
  const fs32 = __require("fs");
32216
32464
  const path42 = __require("path");
32217
32465
  const file = ext.loadExternalSources();
32218
- const match = file.sources.find((s) => s.name === name);
32466
+ const match = file.sources.find((s2) => s2.name === name);
32219
32467
  if (!match) return { success: false, error: `source "${name}" not registered` };
32220
32468
  const sourceDir = path42.join(ext.externalRoot(), name);
32221
32469
  if (fs32.existsSync(sourceDir)) {
@@ -32227,7 +32475,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32227
32475
  }
32228
32476
  ext.saveExternalSources({
32229
32477
  schema: 1,
32230
- sources: file.sources.filter((s) => s.name !== name)
32478
+ sources: file.sources.filter((s2) => s2.name !== name)
32231
32479
  });
32232
32480
  const active = ext.loadProvidersActive();
32233
32481
  const filteredActive = {};
@@ -32251,10 +32499,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
32251
32499
  const file = ext.loadExternalSources();
32252
32500
  const inventory = ext.inventoryExternalSources();
32253
32501
  const active = ext.loadProvidersActive();
32254
- const sources = file.sources.map((s) => {
32255
- const inv = inventory.find((e) => e.sourceName === s.name);
32502
+ const sources = file.sources.map((s2) => {
32503
+ const inv = inventory.find((e) => e.sourceName === s2.name);
32256
32504
  return {
32257
- ...s,
32505
+ ...s2,
32258
32506
  providers: inv?.providers ?? {}
32259
32507
  };
32260
32508
  });
@@ -32431,6 +32679,21 @@ import * as path21 from "path";
32431
32679
  // src/providers/spec/adapter.ts
32432
32680
  init_terminal_screen();
32433
32681
  import { DEFAULT_SESSION_HOST_COLS as DEFAULT_SESSION_HOST_COLS5, DEFAULT_SESSION_HOST_ROWS as DEFAULT_SESSION_HOST_ROWS5 } from "@adhdev/session-host-core";
32682
+ var MAX_PTY_EVENTS = 300;
32683
+ var EVENT_CONTENT_CAP = 240;
32684
+ function escapeControl(text) {
32685
+ return String(text).replace(/[\x00-\x1f\x7f]/g, (ch) => {
32686
+ const code = ch.charCodeAt(0);
32687
+ if (ch === "\r") return "\\r";
32688
+ if (ch === "\n") return "\\n";
32689
+ if (ch === " ") return "\\t";
32690
+ if (code === 27) return "\\x1b";
32691
+ return "\\x" + code.toString(16).padStart(2, "0");
32692
+ });
32693
+ }
32694
+ function capPreview(text) {
32695
+ return text.length > EVENT_CONTENT_CAP ? text.slice(0, EVENT_CONTENT_CAP) + `\u2026(+${text.length - EVENT_CONTENT_CAP})` : text;
32696
+ }
32434
32697
  var TerminalAdapter = class {
32435
32698
  constructor(opts, handlers) {
32436
32699
  this.opts = opts;
@@ -32457,6 +32720,9 @@ var TerminalAdapter = class {
32457
32720
  screenTimer = null;
32458
32721
  tickTimer = null;
32459
32722
  lastScreen = "";
32723
+ /** Debug-only ring buffer of PTY input/output/resize/cursor events. */
32724
+ events = [];
32725
+ lastCursorKey = "";
32460
32726
  start() {
32461
32727
  const env = this.opts.envIsComplete ? this.opts.env ?? {} : { ...process.env, ...this.opts.env ?? {} };
32462
32728
  this.pty = this.factory.spawn(this.opts.binary, this.opts.args ?? [], {
@@ -32465,10 +32731,12 @@ var TerminalAdapter = class {
32465
32731
  cols: this.cols,
32466
32732
  rows: this.rows
32467
32733
  });
32734
+ this.recordEvent("spawn", `${this.opts.binary} (${this.cols}x${this.rows})`);
32468
32735
  this.handlers.init?.({ pid: this.pty.pid });
32469
32736
  this.pty.onData((chunk) => this.onChunk(chunk));
32470
32737
  this.pty.onExit((info) => {
32471
32738
  this.stopTimers();
32739
+ this.recordEvent("exit", `exitCode=${typeof info.exitCode === "number" ? info.exitCode : 0}`);
32472
32740
  this.handlers.on_exit?.({ exitCode: typeof info.exitCode === "number" ? info.exitCode : 0 });
32473
32741
  this.pty = null;
32474
32742
  });
@@ -32479,6 +32747,7 @@ var TerminalAdapter = class {
32479
32747
  resize(cols, rows) {
32480
32748
  this.cols = cols;
32481
32749
  this.rows = rows;
32750
+ this.recordEvent("resize", `${cols}x${rows}`);
32482
32751
  this.pty?.resize(cols, rows);
32483
32752
  this.screen.resize(rows, cols);
32484
32753
  }
@@ -32499,8 +32768,21 @@ var TerminalAdapter = class {
32499
32768
  return { row: pos.row, col: pos.col };
32500
32769
  }
32501
32770
  send_keys(text) {
32771
+ this.recordEvent("input", capPreview(escapeControl(text)), text.length);
32502
32772
  this.pty?.write(text);
32503
32773
  }
32774
+ /** Debug-only: most-recent PTY input/output/resize/cursor events, oldest
32775
+ * first. Pure observation — never consulted by the FSM. */
32776
+ getEventTimeline(limit = MAX_PTY_EVENTS) {
32777
+ const n = Math.max(0, Math.min(limit, this.events.length));
32778
+ return this.events.slice(this.events.length - n);
32779
+ }
32780
+ recordEvent(kind, content, bytes) {
32781
+ const ev = { ts: Date.now(), kind, content };
32782
+ if (typeof bytes === "number") ev.bytes = bytes;
32783
+ this.events.push(ev);
32784
+ if (this.events.length > MAX_PTY_EVENTS) this.events.splice(0, this.events.length - MAX_PTY_EVENTS);
32785
+ }
32504
32786
  kill() {
32505
32787
  this.stopTimers();
32506
32788
  try {
@@ -32511,6 +32793,7 @@ var TerminalAdapter = class {
32511
32793
  this.screen.dispose();
32512
32794
  }
32513
32795
  onChunk(chunk) {
32796
+ this.recordEvent("output", capPreview(escapeControl(chunk)), chunk.length);
32514
32797
  try {
32515
32798
  this.handlers.on_pty_data?.(chunk);
32516
32799
  } catch {
@@ -32520,6 +32803,12 @@ var TerminalAdapter = class {
32520
32803
  this.screenTimer = setTimeout(() => {
32521
32804
  this.screenTimer = null;
32522
32805
  const snap = this.computeScreen();
32806
+ const cur = this.screen.getCursorPosition();
32807
+ const curKey = `${cur.row},${cur.col}`;
32808
+ if (curKey !== this.lastCursorKey) {
32809
+ this.lastCursorKey = curKey;
32810
+ this.recordEvent("cursor", `(${cur.row},${cur.col})`);
32811
+ }
32523
32812
  if (snap === this.lastScreen) return;
32524
32813
  this.lastScreen = snap;
32525
32814
  try {
@@ -32604,20 +32893,40 @@ function applyPreLaunchTrust(trust, workingDir) {
32604
32893
 
32605
32894
  // src/providers/spec/fsm-driver.ts
32606
32895
  init_logger();
32607
- function countNewlines(s) {
32896
+ function countNewlines(s2) {
32608
32897
  let n = 0;
32609
- for (let i = 0; i < s.length; i += 1) if (s.charCodeAt(i) === 10) n += 1;
32898
+ for (let i = 0; i < s2.length; i += 1) if (s2.charCodeAt(i) === 10) n += 1;
32610
32899
  return n;
32611
32900
  }
32612
32901
  var SUBMIT_DELAY_FLOOR_MS = 200;
32613
32902
  var WIN32_SUBMIT_RESEND_GAP_MS = 350;
32614
32903
  var WIN32_SUBMIT_MAX_RESENDS = 14;
32904
+ var WIN32_SUBMIT_SETTLE_MS = 500;
32905
+ var WIN32_SUBMIT_MAX_SETTLE_WAIT_MS = 1e4;
32906
+ var WIN32_SUBMIT_SETTLE_POLL_MS = 120;
32907
+ var WIN32_PTY_WRITE_CHUNK_CHARS = 1024;
32908
+ var WIN32_PTY_WRITE_CHUNK_GAP_MS = 8;
32615
32909
  function resolveSubmitDelayMs(specBeforeSubmit, text) {
32616
32910
  const lines = countNewlines(text);
32617
32911
  const linesBonus = Math.min(800, lines * 80);
32618
32912
  const spec = typeof specBeforeSubmit === "number" && specBeforeSubmit > 0 ? specBeforeSubmit : 0;
32619
32913
  return Math.max(spec, SUBMIT_DELAY_FLOOR_MS + linesBonus);
32620
32914
  }
32915
+ function chunkPreservingSurrogates(text, size) {
32916
+ const chunks = [];
32917
+ let offset = 0;
32918
+ while (offset < text.length) {
32919
+ let end = Math.min(text.length, offset + size);
32920
+ if (end < text.length) {
32921
+ const code = text.charCodeAt(end - 1);
32922
+ if (code >= 55296 && code <= 56319) end -= 1;
32923
+ }
32924
+ if (end <= offset) end = Math.min(text.length, offset + size);
32925
+ chunks.push(text.slice(offset, end));
32926
+ offset = end;
32927
+ }
32928
+ return chunks;
32929
+ }
32621
32930
  function guessExt(mime) {
32622
32931
  if (/png/i.test(mime)) return ".png";
32623
32932
  if (/jpe?g/i.test(mime)) return ".jpg";
@@ -32633,7 +32942,10 @@ var FsmDriver = class {
32633
32942
  this.buildAdapterOpts(),
32634
32943
  {
32635
32944
  init: () => this.emitInitialState(),
32636
- on_pty_data: (chunk) => this.emit({ kind: "pty_data", chunk }),
32945
+ on_pty_data: (chunk) => {
32946
+ this.lastPtyDataAt = Date.now();
32947
+ this.emit({ kind: "pty_data", chunk });
32948
+ },
32637
32949
  on_screen_changed: () => this.reevaluate(),
32638
32950
  on_exit: ({ exitCode }) => this.handleExit(exitCode)
32639
32951
  }
@@ -32667,6 +32979,16 @@ var FsmDriver = class {
32667
32979
  * WIN32_SUBMIT_* and scheduleWin32Submit). Re-arms itself until the FSM
32668
32980
  * leaves idle (submitted) or the resend budget is spent. */
32669
32981
  win32SubmitTimer = null;
32982
+ /** Wall-clock (ms) of the most recent raw PTY output chunk. Advances on every
32983
+ * on_pty_data — including the echo of text written into the composer — so the
32984
+ * win32 submit settle-gate can tell when input has finished landing. */
32985
+ lastPtyDataAt = 0;
32986
+ /** Wall-clock (ms) of the most recent win32 message-body input write. Bridges
32987
+ * the gap between writing a chunk and its echo so the settle-gate does not
32988
+ * declare "quiet" mid-write. */
32989
+ lastWin32WriteAt = 0;
32990
+ /** Pending paced chunk-write timer for a large win32 body (see writeWin32Body). */
32991
+ win32WriteTimer = null;
32670
32992
  currentEval = null;
32671
32993
  stateHistory = [];
32672
32994
  prevStateAt = 0;
@@ -32787,6 +33109,10 @@ var FsmDriver = class {
32787
33109
  clearTimeout(this.win32SubmitTimer);
32788
33110
  this.win32SubmitTimer = null;
32789
33111
  }
33112
+ if (this.win32WriteTimer) {
33113
+ clearTimeout(this.win32WriteTimer);
33114
+ this.win32WriteTimer = null;
33115
+ }
32790
33116
  this.specWatcher?.close();
32791
33117
  this.adapter.kill();
32792
33118
  }
@@ -32817,11 +33143,15 @@ var FsmDriver = class {
32817
33143
  getFsmSnapshotHistory() {
32818
33144
  return this.fsmSnapshotHistory;
32819
33145
  }
33146
+ /** Debug-only PTY input/output/resize/cursor timeline from the adapter. */
33147
+ getEventTimeline(limit) {
33148
+ return this.adapter.getEventTimeline(limit);
33149
+ }
32820
33150
  getSections() {
32821
33151
  try {
32822
33152
  const screen = this.adapter.snapshot();
32823
33153
  const lines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
32824
- return resolveSections(this.spec.sections ?? {}, lines).map((s) => ({ id: s.id, text: s.text }));
33154
+ return resolveSections(this.spec.sections ?? {}, lines).map((s2) => ({ id: s2.id, text: s2.text }));
32825
33155
  } catch {
32826
33156
  return null;
32827
33157
  }
@@ -33199,7 +33529,7 @@ var FsmDriver = class {
33199
33529
  const perChar = sm.delay_ms_per_char ?? 0;
33200
33530
  const beforeSubmit = resolveSubmitDelayMs(sm.delay_ms_before_submit, text);
33201
33531
  if (process.platform === "win32") {
33202
- this.adapter.send_keys(text);
33532
+ this.writeWin32Body(text);
33203
33533
  this.scheduleWin32Submit(sm.submit_key, beforeSubmit);
33204
33534
  return;
33205
33535
  }
@@ -33225,20 +33555,72 @@ var FsmDriver = class {
33225
33555
  const st = stateById(this.spec, this.currentStateId);
33226
33556
  return st ? statusForState(st) : "idle";
33227
33557
  }
33558
+ /** Record a win32 body write so the settle-gate counts it as input activity
33559
+ * even before the echo arrives. */
33560
+ markWin32Write() {
33561
+ this.lastWin32WriteAt = Date.now();
33562
+ }
33563
+ /** Most recent win32 input activity — a write we issued OR a PTY output chunk
33564
+ * (echo). The submit settle-gate waits for this to go quiet. */
33565
+ lastWin32InputActivityAt() {
33566
+ return Math.max(this.lastPtyDataAt, this.lastWin32WriteAt);
33567
+ }
33228
33568
  /**
33229
- * win32 verification-based submit. Sends the submit key, waits a gap, and if
33230
- * the FSM is still 'idle' (the prompt did not submit — the CR was absorbed as
33231
- * a multiline-paste newline) resends, up to WIN32_SUBMIT_MAX_RESENDS. The
33232
- * first CR always fires (so a stale/edge status never suppresses the submit);
33233
- * subsequent resends are gated on still being idle, and stop the instant the
33234
- * agent leaves idle (submitted generating / approval). This converges the
33235
- * nondeterministic multiline window without spamming Enter into the next turn.
33569
+ * Write the message body to the PTY for win32, paced into bounded chunks. A
33570
+ * single unbounded ConPTY write can overflow the input pipe and drop leading
33571
+ * bytes; splitting it with a short inter-chunk gap keeps the console input
33572
+ * buffer from overflowing. Small bodies still go out in a single write. Each
33573
+ * chunk advances lastWin32WriteAt so the submit settle-gate keeps waiting until
33574
+ * the final chunk is out and echoed.
33575
+ */
33576
+ writeWin32Body(text) {
33577
+ if (this.win32WriteTimer) {
33578
+ clearTimeout(this.win32WriteTimer);
33579
+ this.win32WriteTimer = null;
33580
+ }
33581
+ if (text.length <= WIN32_PTY_WRITE_CHUNK_CHARS) {
33582
+ this.markWin32Write();
33583
+ this.adapter.send_keys(text);
33584
+ return;
33585
+ }
33586
+ const chunks = chunkPreservingSurrogates(text, WIN32_PTY_WRITE_CHUNK_CHARS);
33587
+ let idx = 0;
33588
+ const writeNext = () => {
33589
+ this.win32WriteTimer = null;
33590
+ if (idx >= chunks.length) return;
33591
+ this.markWin32Write();
33592
+ this.adapter.send_keys(chunks[idx]);
33593
+ idx += 1;
33594
+ if (idx < chunks.length) {
33595
+ this.win32WriteTimer = setTimeout(writeNext, WIN32_PTY_WRITE_CHUNK_GAP_MS);
33596
+ }
33597
+ };
33598
+ writeNext();
33599
+ }
33600
+ /**
33601
+ * win32 submit. Two phases:
33602
+ *
33603
+ * Phase 1 (settle-gate): hold the first CR until the PTY output has been quiet
33604
+ * for WIN32_SUBMIT_SETTLE_MS after the last input write — i.e. the full
33605
+ * (possibly multi-KB / multiline) body has finished arriving in the composer
33606
+ * and echoing. Honors an initial minimum delay and is bounded by
33607
+ * WIN32_SUBMIT_MAX_SETTLE_WAIT_MS so a noisy screen can never hang the submit.
33608
+ * This is what stops a long message from being submitted half-arrived (its
33609
+ * leading lines lost). A short message settles almost immediately.
33610
+ *
33611
+ * Phase 2 (verified resend — unchanged): send the submit key, wait a gap, and
33612
+ * if the FSM is still 'idle' (the CR was absorbed as a multiline-paste
33613
+ * newline) resend, up to WIN32_SUBMIT_MAX_RESENDS. The first CR always fires
33614
+ * (a stale/edge status never suppresses it); resends are gated on still being
33615
+ * idle and stop the instant the agent leaves idle (submitted → generating /
33616
+ * approval). This preserves the win32 lone-CR-swallow handling.
33236
33617
  */
33237
33618
  scheduleWin32Submit(submitKey, initialDelayMs) {
33238
33619
  if (this.win32SubmitTimer) {
33239
33620
  clearTimeout(this.win32SubmitTimer);
33240
33621
  this.win32SubmitTimer = null;
33241
33622
  }
33623
+ const startedAt = Date.now();
33242
33624
  const fire = (attempt) => {
33243
33625
  this.win32SubmitTimer = null;
33244
33626
  this.adapter.send_keys(submitKey);
@@ -33251,8 +33633,20 @@ var FsmDriver = class {
33251
33633
  fire(attempt + 1);
33252
33634
  }, WIN32_SUBMIT_RESEND_GAP_MS);
33253
33635
  };
33254
- if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(() => fire(0), initialDelayMs);
33255
- else fire(0);
33636
+ const waitForSettle = () => {
33637
+ this.win32SubmitTimer = null;
33638
+ const now = Date.now();
33639
+ const quietFor = now - this.lastWin32InputActivityAt();
33640
+ const waited = now - startedAt;
33641
+ if (quietFor >= WIN32_SUBMIT_SETTLE_MS || waited >= WIN32_SUBMIT_MAX_SETTLE_WAIT_MS) {
33642
+ fire(0);
33643
+ return;
33644
+ }
33645
+ const recheckIn = Math.min(WIN32_SUBMIT_SETTLE_MS - quietFor, WIN32_SUBMIT_SETTLE_POLL_MS);
33646
+ this.win32SubmitTimer = setTimeout(waitForSettle, Math.max(recheckIn, 30));
33647
+ };
33648
+ if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(waitForSettle, initialDelayMs);
33649
+ else waitForSettle();
33256
33650
  }
33257
33651
  handleClickControl(controlId, payload) {
33258
33652
  const ctl = (this.spec.control_bar ?? []).find((c) => c.id === controlId);
@@ -33365,7 +33759,8 @@ function summarizeTransition(t) {
33365
33759
  return out;
33366
33760
  }
33367
33761
  function flattenCond(c, out, depth) {
33368
- out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}`);
33762
+ const matched = c.matchedText ? ` matched=${JSON.stringify(c.matchedText)}` : "";
33763
+ out.push(`${" ".repeat(depth)}${c.kind} ${c.detail} = ${c.result}${c.remainingMs ? ` (${c.remainingMs}ms left)` : ""}${matched}`);
33369
33764
  for (const child of c.children ?? []) flattenCond(child, out, depth + 1);
33370
33765
  }
33371
33766
  function findStable(c) {
@@ -34083,8 +34478,8 @@ function projectToolBlock(block2, role, tmap) {
34083
34478
  }
34084
34479
  return null;
34085
34480
  }
34086
- function oneLine(s, max) {
34087
- const flat = s.replace(/\s+/g, " ").trim();
34481
+ function oneLine(s2, max) {
34482
+ const flat = s2.replace(/\s+/g, " ").trim();
34088
34483
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
34089
34484
  }
34090
34485
  function parseTimestamp(v) {
@@ -34104,10 +34499,10 @@ function parseTimestamp(v) {
34104
34499
  return null;
34105
34500
  }
34106
34501
  function normalizeRole(r) {
34107
- const s = String(r ?? "").toLowerCase();
34108
- if (s === "user" || s === "human" || s === "user_explicit") return "user";
34109
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
34110
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
34502
+ const s2 = String(r ?? "").toLowerCase();
34503
+ if (s2 === "user" || s2 === "human" || s2 === "user_explicit") return "user";
34504
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
34505
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
34111
34506
  return "system";
34112
34507
  }
34113
34508
  function stringifyContent(v) {
@@ -34155,18 +34550,18 @@ function compileWhere(src) {
34155
34550
  return (record) => ors.some((ands) => ands.every((t) => evalTerm(t, record)));
34156
34551
  }
34157
34552
  function parseTerm(src) {
34158
- let s = src.trim();
34553
+ let s2 = src.trim();
34159
34554
  let negate = false;
34160
- if (s.startsWith("!")) {
34555
+ if (s2.startsWith("!")) {
34161
34556
  negate = true;
34162
- s = s.slice(1).trim();
34557
+ s2 = s2.slice(1).trim();
34163
34558
  }
34164
- const fnMatch = s.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34559
+ const fnMatch = s2.match(/^(startsWith|endsWith|contains)\s*\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);
34165
34560
  if (fnMatch) {
34166
34561
  const [, op2, pathExpr, litExpr] = fnMatch;
34167
34562
  return { path: pathExpr, op: op2, lit: parseLiteral(litExpr), negate };
34168
34563
  }
34169
- const opMatch = s.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34564
+ const opMatch = s2.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
34170
34565
  if (!opMatch) return null;
34171
34566
  const [, lhs, op, rhsRaw] = opMatch;
34172
34567
  return { path: lhs.trim(), op, lit: parseLiteral(rhsRaw.trim()), negate };
@@ -34598,7 +34993,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
34598
34993
  try {
34599
34994
  const sections = this.driver.getSections();
34600
34995
  if (sectionId && sections) {
34601
- const hit = sections.find((s) => s.id === sectionId);
34996
+ const hit = sections.find((s2) => s2.id === sectionId);
34602
34997
  if (hit) return hit.text;
34603
34998
  }
34604
34999
  return this.driver.getScreen();
@@ -34613,7 +35008,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
34613
35008
  screen = this.driver.snapshot();
34614
35009
  const driverSections = this.driver.getSections?.();
34615
35010
  if (driverSections) {
34616
- sections = Object.fromEntries(driverSections.map((s) => [s.id, s.text]));
35011
+ sections = Object.fromEntries(driverSections.map((s2) => [s2.id, s2.text]));
34617
35012
  } else {
34618
35013
  sections = this.readCurrentScreenSections(screen);
34619
35014
  }
@@ -34659,6 +35054,10 @@ var SpecCliAdapter = class _SpecCliAdapter {
34659
35054
  // answers "why did this rule fire" after the fact, unlike the live
34660
35055
  // `fsm` field which only reflects the current instant.
34661
35056
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35057
+ // PTY input/output/resize/cursor event timeline (debug-only) so the
35058
+ // snapshot shows what we typed / what the PTY printed around each
35059
+ // status transition. Null for drivers without the timeline.
35060
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
34662
35061
  // Extended fields
34663
35062
  name: this.cliName,
34664
35063
  status: this.getStatus().status,
@@ -35023,6 +35422,8 @@ var SpecCliAdapter = class _SpecCliAdapter {
35023
35422
  // v4 FSM transition snapshot history — the captured pre-transition
35024
35423
  // evaluation table at each transition (null for v3 specs).
35025
35424
  fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
35425
+ // PTY input/output/resize/cursor event timeline (debug-only).
35426
+ eventTimeline: this.driver.getEventTimeline?.() ?? null,
35026
35427
  messages,
35027
35428
  committedMessages: messages
35028
35429
  };
@@ -35067,6 +35468,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
35067
35468
 
35068
35469
  // src/providers/cli-provider-instance.ts
35069
35470
  init_logger();
35471
+ init_mesh_event_trace();
35070
35472
  init_control_effects();
35071
35473
  init_approval_utils();
35072
35474
  init_provider_patch_state();
@@ -35369,6 +35771,11 @@ var CliProviderInstance = class _CliProviderInstance {
35369
35771
  // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35370
35772
  // brief generating flip does not immediately wipe the settle clock.
35371
35773
  autoApproveInactiveSince = 0;
35774
+ // Provider-common manual-attendance signal: while a human is actively driving
35775
+ // this session from the dashboard, auto-approve holds so they can take manual
35776
+ // control. Background mesh workers are never attended → delegated auto-approve
35777
+ // is unaffected.
35778
+ manualAttendance = new ManualAttendanceTracker();
35372
35779
  controlValues = {};
35373
35780
  summaryMetadata = void 0;
35374
35781
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -35644,7 +36051,7 @@ var CliProviderInstance = class _CliProviderInstance {
35644
36051
  }
35645
36052
  getHotChatSessionState() {
35646
36053
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
35647
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36054
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
35648
36055
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
35649
36056
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
35650
36057
  const runtime = this.adapter.getRuntimeMetadata();
@@ -35659,7 +36066,7 @@ var CliProviderInstance = class _CliProviderInstance {
35659
36066
  }
35660
36067
  getSessionModalState(sessionId) {
35661
36068
  const adapterStatus = this.adapter.getStatus({ allowParse: true });
35662
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36069
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
35663
36070
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
35664
36071
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
35665
36072
  const dirName = workingDirBasename(this.workingDir);
@@ -35740,7 +36147,7 @@ var CliProviderInstance = class _CliProviderInstance {
35740
36147
  } catch {
35741
36148
  return null;
35742
36149
  }
35743
- if (adapterStatus.status === "waiting_approval" && !this.shouldAutoApprove()) {
36150
+ if (adapterStatus.status === "waiting_approval" && !this.autoApproveEffectivelyActive(adapterStatus.status)) {
35744
36151
  return "waiting_approval";
35745
36152
  }
35746
36153
  return null;
@@ -36113,6 +36520,23 @@ var CliProviderInstance = class _CliProviderInstance {
36113
36520
  if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
36114
36521
  this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
36115
36522
  }
36523
+ // EVTTRACE (observation-only): is this a mesh worker session whose completion
36524
+ // events must route to a coordinator? Used purely to gate trace logging so a
36525
+ // non-mesh CLI session's completions don't add EvtTrace noise. No decision logic.
36526
+ isMeshWorkerSession() {
36527
+ return !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.meshNodeId || this.settings.launchedByCoordinator);
36528
+ }
36529
+ // EVTTRACE correlation context for this session's completion lifecycle. taskId is
36530
+ // the primary grep anchor; instanceId is the session fallback.
36531
+ meshTraceCtx(event = "agent:generating_completed") {
36532
+ return {
36533
+ taskId: this.settings.meshActiveTaskId,
36534
+ sessionId: this.instanceId,
36535
+ nodeId: this.settings.meshNodeId,
36536
+ meshId: this.settings.meshNodeFor,
36537
+ event
36538
+ };
36539
+ }
36116
36540
  flushCompletedDebounceIfFinalized() {
36117
36541
  const pending = this.completedDebouncePending;
36118
36542
  if (!pending) {
@@ -36133,24 +36557,33 @@ var CliProviderInstance = class _CliProviderInstance {
36133
36557
  if (block2) {
36134
36558
  const blockReason = block2.reason;
36135
36559
  const waitedMs = Date.now() - pending.firstObservedAt;
36136
- LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36137
- if (block2.terminal && !block2.allowTimeout || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
36560
+ const isTranscriptEvidenceGate = block2.allowTimeout === true;
36561
+ LOG.debug("CLI", `[${this.type}] finalization block: reason=${blockReason} terminal=${block2.terminal} allowTimeout=${isTranscriptEvidenceGate} waitedMs=${waitedMs} maxWait=${COMPLETED_FINALIZATION_MAX_WAIT_MS}`);
36562
+ if (!isTranscriptEvidenceGate && (block2.terminal || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS)) {
36138
36563
  if (pending.loggedBlockReason !== blockReason) {
36139
36564
  LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
36565
+ if (this.isMeshWorkerSession()) {
36566
+ traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
36567
+ }
36140
36568
  pending.loggedBlockReason = blockReason;
36141
36569
  }
36142
36570
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
36143
36571
  return;
36144
36572
  }
36573
+ const emittedAfterFinalizationTimeout = waitedMs >= COMPLETED_FINALIZATION_MAX_WAIT_MS;
36145
36574
  const completionDiagnostic = this.buildCompletedFinalizationDiagnostic({
36146
36575
  blockReason,
36147
36576
  latestStatus,
36148
36577
  latestVisibleStatus,
36149
36578
  waitedMs,
36150
36579
  pending,
36151
- emittedAfterFinalizationTimeout: true
36580
+ emittedAfterFinalizationTimeout
36152
36581
  });
36153
- LOG.warn("CLI", `[${this.type}] emitting completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
36582
+ completionDiagnostic.decoupledImmediateEmit = isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout;
36583
+ LOG.warn("CLI", `[${this.type}] emitting completed event (${isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "CANON-C decoupled-immediate, transcript pending" : `after ${waitedMs}ms`}) without finalized assistant turn (${blockReason})`);
36584
+ if (this.isMeshWorkerSession()) {
36585
+ traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
36586
+ }
36154
36587
  this.pushEvent({
36155
36588
  event: "agent:generating_completed",
36156
36589
  chatTitle: pending.chatTitle,
@@ -36173,6 +36606,9 @@ var CliProviderInstance = class _CliProviderInstance {
36173
36606
  return;
36174
36607
  }
36175
36608
  LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
36609
+ if (this.isMeshWorkerSession()) {
36610
+ traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
36611
+ }
36176
36612
  this.pushEvent({
36177
36613
  event: "agent:generating_completed",
36178
36614
  chatTitle: pending.chatTitle,
@@ -36186,6 +36622,18 @@ var CliProviderInstance = class _CliProviderInstance {
36186
36622
  this.lastApprovalEventFingerprint = "";
36187
36623
  }
36188
36624
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
36625
+ if (adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove() && this.manualAttendance.isAttended(now)) {
36626
+ this.lastAutoApprovalSignature = "";
36627
+ this.pendingAutoApprovalSignature = "";
36628
+ this.pendingAutoApprovalSince = 0;
36629
+ this.autoApproveInactiveSince = 0;
36630
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36631
+ this.autoApproveSettleTimer = setTimeout(() => {
36632
+ this.autoApproveSettleTimer = null;
36633
+ this.recheckAutoApproveSettled();
36634
+ }, this.manualAttendance.remainingMs(now) + 20);
36635
+ return false;
36636
+ }
36189
36637
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
36190
36638
  if (!autoApproveActive) {
36191
36639
  this.lastAutoApprovalSignature = "";
@@ -36399,6 +36847,9 @@ var CliProviderInstance = class _CliProviderInstance {
36399
36847
  if (missingEvidence && !hasMeshContext) {
36400
36848
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, no mesh context (source=${shortEvidenceSource})`);
36401
36849
  } else {
36850
+ if (this.isMeshWorkerSession()) {
36851
+ traceMeshEventStage("fired", this.meshTraceCtx(), `short-generating idle (source=${shortEvidenceSource})`);
36852
+ }
36402
36853
  this.pushEvent({
36403
36854
  event: "agent:generating_completed",
36404
36855
  chatTitle,
@@ -36475,6 +36926,9 @@ var CliProviderInstance = class _CliProviderInstance {
36475
36926
  const monitorParsedStatus = parsedStatus;
36476
36927
  for (const me of monitorEvents) {
36477
36928
  if (me.type === "monitor:no_progress" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
36929
+ if (this.isMeshWorkerSession()) {
36930
+ traceMeshEventStage("fired", this.meshTraceCtx(), "no_progress_monitor_final_summary");
36931
+ }
36478
36932
  this.pushEvent({
36479
36933
  event: "agent:generating_completed",
36480
36934
  chatTitle,
@@ -36651,6 +37105,21 @@ ${effect.notification.body || ""}`.trim();
36651
37105
  }
36652
37106
  return false;
36653
37107
  }
37108
+ /** @see ProviderInstance.noteManualInteraction */
37109
+ noteManualInteraction(now = Date.now()) {
37110
+ this.manualAttendance.note(now);
37111
+ }
37112
+ /**
37113
+ * Whether auto-approve should be treated as active *right now* for display
37114
+ * and firing decisions: the configured intent AND the user is not currently
37115
+ * attending this session by hand. When a human is attending, auto-approve is
37116
+ * held so the modal stays visible and they can drive it via the controlbar.
37117
+ * Provider-agnostic — the attendance signal is the command set, never any
37118
+ * CLI-specific modal text.
37119
+ */
37120
+ autoApproveEffectivelyActive(status, now = Date.now()) {
37121
+ return status === "waiting_approval" && this.shouldAutoApprove() && !this.manualAttendance.isAttended(now);
37122
+ }
36654
37123
  recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
36655
37124
  this.appendRuntimeSystemMessage(
36656
37125
  formatAutoApprovalMessage(modalMessage, buttonLabel),
@@ -37601,7 +38070,7 @@ var AcpProviderInstance = class {
37601
38070
  input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
37602
38071
  });
37603
38072
  }
37604
- if (this.settings.autoApprove !== false) {
38073
+ if (this.settings.autoApprove !== false && !this.manualAttendance.isAttended()) {
37605
38074
  const toolTitle = tc.title || tc.toolCallId || "tool call";
37606
38075
  this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
37607
38076
  this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
@@ -37832,6 +38301,15 @@ var AcpProviderInstance = class {
37832
38301
  this.detectStatusTransition();
37833
38302
  }
37834
38303
  permissionResolvers = [];
38304
+ // Provider-common manual-attendance signal: while a human is actively driving
38305
+ // this session from the dashboard, auto-approve holds so they can decide on
38306
+ // the permission request themselves. Background workers are never attended →
38307
+ // delegated auto-approve is unaffected.
38308
+ manualAttendance = new ManualAttendanceTracker();
38309
+ /** @see ProviderInstance.noteManualInteraction */
38310
+ noteManualInteraction(now = Date.now()) {
38311
+ this.manualAttendance.note(now);
38312
+ }
37835
38313
  async resolvePermission(approved) {
37836
38314
  const resolver = this.permissionResolvers.shift();
37837
38315
  if (resolver) {
@@ -38989,6 +39467,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
38989
39467
  );
38990
39468
  continue;
38991
39469
  }
39470
+ const restoredSettings = { ...this.providerLoader.getSettings(normalizedType) };
39471
+ const coordinatorEntry = getCoordinatorForSession(record.runtimeId);
39472
+ if (coordinatorEntry?.meshId) {
39473
+ restoredSettings.meshCoordinatorFor = coordinatorEntry.meshId;
39474
+ }
38992
39475
  try {
38993
39476
  await this.registerCliInstance(
38994
39477
  record.runtimeId,
@@ -38997,7 +39480,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38997
39480
  record.workspace,
38998
39481
  record.cliArgs,
38999
39482
  resolvedProvider,
39000
- {},
39483
+ restoredSettings,
39001
39484
  true,
39002
39485
  {
39003
39486
  providerSessionId: sessionBinding.providerSessionId,
@@ -40314,7 +40797,7 @@ function parsePbFile(filePath, sessionId) {
40314
40797
  }
40315
40798
  if (buf.length === 0) return null;
40316
40799
  const strings = extractStringsFromBuffer(buf);
40317
- const meaningful = strings.filter((s) => s.length >= MIN_PRINTABLE_RUN && /\w/.test(s));
40800
+ const meaningful = strings.filter((s2) => s2.length >= MIN_PRINTABLE_RUN && /\w/.test(s2));
40318
40801
  if (meaningful.length === 0) return null;
40319
40802
  const content = meaningful.join("\n");
40320
40803
  const sourceMtimeMs = statMtimeMs3(filePath);
@@ -40522,10 +41005,10 @@ function readSession4(sessionPath) {
40522
41005
  };
40523
41006
  }
40524
41007
  function normalizeHermesRole(r) {
40525
- const s = String(r ?? "").toLowerCase();
40526
- if (s === "user" || s === "human") return "user";
40527
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
40528
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41008
+ const s2 = String(r ?? "").toLowerCase();
41009
+ if (s2 === "user" || s2 === "human") return "user";
41010
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41011
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
40529
41012
  return "system";
40530
41013
  }
40531
41014
 
@@ -40751,10 +41234,10 @@ function safeMtime(p) {
40751
41234
  }
40752
41235
  }
40753
41236
  function normalizeRole2(r) {
40754
- const s = String(r ?? "").toLowerCase();
40755
- if (s === "user" || s === "human") return "user";
40756
- if (s === "assistant" || s === "ai" || s === "model") return "assistant";
40757
- if (s === "tool" || s === "tool_result" || s === "function") return "assistant";
41237
+ const s2 = String(r ?? "").toLowerCase();
41238
+ if (s2 === "user" || s2 === "human") return "user";
41239
+ if (s2 === "assistant" || s2 === "ai" || s2 === "model") return "assistant";
41240
+ if (s2 === "tool" || s2 === "tool_result" || s2 === "function") return "assistant";
40758
41241
  return "system";
40759
41242
  }
40760
41243
 
@@ -40774,7 +41257,7 @@ function synthesizeControlsFromControlBar(specControls) {
40774
41257
  const actionType = ctl?.action?.type;
40775
41258
  if (!id || !actionType) return;
40776
41259
  const label = typeof ctl?.label === "string" && ctl.label.trim() ? ctl.label : id;
40777
- const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s) => typeof s === "string") : void 0;
41260
+ const visibleWhenState = Array.isArray(ctl?.visible_when_state) ? ctl.visible_when_state.filter((s2) => typeof s2 === "string") : void 0;
40778
41261
  if (actionType === "open_picker") {
40779
41262
  out.push({
40780
41263
  id,
@@ -47690,7 +48173,7 @@ var DaemonCommandRouter = class {
47690
48173
  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.";
47691
48174
  if (!firstFailedCmd) return base;
47692
48175
  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 : "";
47693
- const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s) => typeof s === "string" && s.length > 0).join("\n");
48176
+ const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
47694
48177
  const tail = rawOutput.length > 800 ? rawOutput.slice(-800) : rawOutput;
47695
48178
  return [
47696
48179
  base,
@@ -48441,7 +48924,7 @@ ${hintLines.join("\n")}` : "",
48441
48924
  convergence = "blocked_review";
48442
48925
  }
48443
48926
  const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
48444
- const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
48927
+ const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s2) => s2.status === "failed").map((s2) => s2.stage).filter(Boolean).pop() : void 0;
48445
48928
  results.push({
48446
48929
  nodeId: node.id,
48447
48930
  workspace: node.workspace,
@@ -49438,7 +49921,7 @@ ${hintLines.join("\n")}` : "",
49438
49921
  return {
49439
49922
  success: true,
49440
49923
  screenLineCount: lines.length,
49441
- sections: resolved.map((s) => ({ id: s.id, fromLine: s.fromLine, toLine: s.toLine, text: s.text }))
49924
+ sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
49442
49925
  };
49443
49926
  } catch (e) {
49444
49927
  return { success: false, error: `resolve failed: ${e.message}` };
@@ -50007,7 +50490,7 @@ ${hintLines.join("\n")}` : "",
50007
50490
  if (!meshId) return { success: false, error: "meshId required" };
50008
50491
  try {
50009
50492
  const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
50010
- const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
50493
+ const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
50011
50494
  const rawQueue = getQueue2(meshId, { status });
50012
50495
  const statusById = new Map(getQueue2(meshId).map((task) => [task.id, task.status]));
50013
50496
  const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById) } : task);
@@ -50288,7 +50771,7 @@ ${hintLines.join("\n")}` : "",
50288
50771
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50289
50772
  }
50290
50773
  const selfDaemonId = this.deps.statusInstanceId;
50291
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
50774
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50292
50775
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50293
50776
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
50294
50777
  ...typeof args === "object" && args !== null ? args : {},
@@ -50322,7 +50805,7 @@ ${hintLines.join("\n")}` : "",
50322
50805
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
50323
50806
  }
50324
50807
  const selfDaemonId = this.deps.statusInstanceId;
50325
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
50808
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50326
50809
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50327
50810
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "get_mesh_node_logs", {
50328
50811
  ...typeof args === "object" && args !== null ? args : {},
@@ -50370,7 +50853,7 @@ ${hintLines.join("\n")}` : "",
50370
50853
  const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
50371
50854
  const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
50372
50855
  const selfDaemonId = this.deps.statusInstanceId;
50373
- const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
50856
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
50374
50857
  if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50375
50858
  const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
50376
50859
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
@@ -50457,7 +50940,7 @@ ${hintLines.join("\n")}` : "",
50457
50940
  let worktreeCleanup;
50458
50941
  if (node?.isLocalWorktree) {
50459
50942
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
50460
- const isRemoteWorktree = nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
50943
+ const isRemoteWorktree = nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch;
50461
50944
  if (isRemoteWorktree) {
50462
50945
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "remove_mesh_node", {
50463
50946
  ...typeof args === "object" && args !== null ? args : {},
@@ -50539,7 +51022,7 @@ ${hintLines.join("\n")}` : "",
50539
51022
  const sourceNode = mesh.nodes?.find((n) => meshNodeIdMatches(n, sourceNodeId));
50540
51023
  if (!sourceNode) return { success: false, error: `Source node '${sourceNodeId}' not found in mesh` };
50541
51024
  const sourceDaemonId = typeof sourceNode.daemonId === "string" ? sourceNode.daemonId.trim() : void 0;
50542
- if (sourceDaemonId && sourceDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51025
+ if (sourceDaemonId && !daemonIdsEquivalent(sourceDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50543
51026
  const forwarded = await this.deps.dispatchMeshCommand(sourceDaemonId, "clone_mesh_node", {
50544
51027
  ...typeof args === "object" && args !== null ? args : {},
50545
51028
  _meshDirectDispatch: true
@@ -50781,7 +51264,7 @@ ${hintLines.join("\n")}` : "",
50781
51264
  if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
50782
51265
  if (!node.isLocalWorktree) return { success: false, error: "Node is not a local worktree node" };
50783
51266
  const nodeDaemonId = typeof node.daemonId === "string" ? node.daemonId.trim() : void 0;
50784
- if (nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
51267
+ if (nodeDaemonId && !daemonIdsEquivalent(nodeDaemonId, this.deps.statusInstanceId) && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
50785
51268
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "retry_mesh_node_bootstrap", {
50786
51269
  ...typeof args === "object" && args !== null ? args : {},
50787
51270
  _meshDirectDispatch: true
@@ -51991,16 +52474,16 @@ var DaemonStatusReporter = class {
51991
52474
  const now = this.lastStatusSentAt;
51992
52475
  const target = opts?.p2pOnly ? "P2P" : serverConnected ? "P2P+Server" : "P2P";
51993
52476
  const allStates = this.deps.instanceManager.collectAllStates();
51994
- const ideStates = allStates.filter((s) => s.category === "ide");
51995
- const cliStates = allStates.filter((s) => s.category === "cli");
51996
- const acpStates = allStates.filter((s) => s.category === "acp");
51997
- const ideSummary = ideStates.map((s) => {
51998
- const msgs = s.activeChat?.messages?.length || 0;
51999
- const exts = s.extensions.length;
52000
- return `${s.type}(${s.status},${msgs}msg,${exts}ext)`;
52477
+ const ideStates = allStates.filter((s2) => s2.category === "ide");
52478
+ const cliStates = allStates.filter((s2) => s2.category === "cli");
52479
+ const acpStates = allStates.filter((s2) => s2.category === "acp");
52480
+ const ideSummary = ideStates.map((s2) => {
52481
+ const msgs = s2.activeChat?.messages?.length || 0;
52482
+ const exts = s2.extensions.length;
52483
+ return `${s2.type}(${s2.status},${msgs}msg,${exts}ext)`;
52001
52484
  }).join(", ");
52002
- const cliSummary = cliStates.map((s) => `${s.type}(${s.status})`).join(", ");
52003
- const acpSummary = acpStates.map((s) => `${s.type}(${s.status})`).join(", ");
52485
+ const cliSummary = cliStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52486
+ const acpSummary = acpStates.map((s2) => `${s2.type}(${s2.status})`).join(", ");
52004
52487
  const logLevel = opts?.p2pOnly ? "debug" : "info";
52005
52488
  const baseSummary = `IDE: ${ideStates.length} [${ideSummary}] CLI: ${cliStates.length} [${cliSummary}] ACP: ${acpStates.length} [${acpSummary}]`;
52006
52489
  const summaryChanged = baseSummary !== this.lastStatusSummary;
@@ -52111,10 +52594,10 @@ var DaemonStatusReporter = class {
52111
52594
  }
52112
52595
  return false;
52113
52596
  }
52114
- simpleHash(s) {
52597
+ simpleHash(s2) {
52115
52598
  let h = 2166136261;
52116
- for (let i = 0; i < s.length; i++) {
52117
- h ^= s.charCodeAt(i);
52599
+ for (let i = 0; i < s2.length; i++) {
52600
+ h ^= s2.charCodeAt(i);
52118
52601
  h = h * 16777619 >>> 0;
52119
52602
  }
52120
52603
  return h.toString(36);
@@ -53340,7 +53823,7 @@ var ProviderInstanceManager = class {
53340
53823
  * Per-category status collect
53341
53824
  */
53342
53825
  collectStatesByCategory(category) {
53343
- return this.collectAllStates().filter((s) => s.category === category);
53826
+ return this.collectAllStates().filter((s2) => s2.category === category);
53344
53827
  }
53345
53828
  // ─── Tick engine ─────────────────────────────────
53346
53829
  /**
@@ -55240,9 +55723,9 @@ function getCliProviderResolutionMeta(ctx, type, adapter) {
55240
55723
  function findCliTarget(ctx, type, instanceId) {
55241
55724
  if (!ctx.instanceManager) return null;
55242
55725
  const cliStates = ctx.instanceManager.collectAllStates().filter(isCliTargetState);
55243
- if (instanceId) return cliStates.find((s) => s.instanceId === instanceId) || null;
55726
+ if (instanceId) return cliStates.find((s2) => s2.instanceId === instanceId) || null;
55244
55727
  if (!type) return cliStates[cliStates.length - 1] || null;
55245
- const matches = cliStates.filter((s) => s.type === type);
55728
+ const matches = cliStates.filter((s2) => s2.type === type);
55246
55729
  return matches[matches.length - 1] || null;
55247
55730
  }
55248
55731
  function getCliTargetBundle(ctx, type, instanceId) {
@@ -55605,20 +56088,20 @@ async function handleCliStatus(ctx, _req, res) {
55605
56088
  return;
55606
56089
  }
55607
56090
  const allStates = ctx.instanceManager.collectAllStates();
55608
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
55609
- const result = cliStates.map((s) => ({
55610
- instanceId: s.instanceId,
55611
- type: s.type,
55612
- name: s.name,
55613
- category: s.category,
55614
- status: s.status,
55615
- mode: s.mode,
55616
- workspace: s.workspace,
55617
- messageCount: s.activeChat?.messages?.length || 0,
55618
- lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
55619
- activeModal: s.activeChat?.activeModal || null,
55620
- pendingEvents: s.pendingEvents || [],
55621
- settings: s.settings
56091
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56092
+ const result = cliStates.map((s2) => ({
56093
+ instanceId: s2.instanceId,
56094
+ type: s2.type,
56095
+ name: s2.name,
56096
+ category: s2.category,
56097
+ status: s2.status,
56098
+ mode: s2.mode,
56099
+ workspace: s2.workspace,
56100
+ messageCount: s2.activeChat?.messages?.length || 0,
56101
+ lastMessage: s2.activeChat?.messages?.slice(-1)[0] || null,
56102
+ activeModal: s2.activeChat?.activeModal || null,
56103
+ pendingEvents: s2.pendingEvents || [],
56104
+ settings: s2.settings
55622
56105
  }));
55623
56106
  ctx.json(res, 200, { instances: result, count: result.length });
55624
56107
  }
@@ -55707,9 +56190,9 @@ function handleCliSSE(ctx, cliSSEClients, _req, res) {
55707
56190
  }
55708
56191
  if (ctx.instanceManager) {
55709
56192
  const allStates = ctx.instanceManager.collectAllStates();
55710
- const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
55711
- for (const s of cliStates) {
55712
- ctx.sendCliSSE({ event: "snapshot", providerType: s.type, status: s.status, instanceId: s.instanceId });
56193
+ const cliStates = allStates.filter((s2) => s2.category === "cli" || s2.category === "acp");
56194
+ for (const s2 of cliStates) {
56195
+ ctx.sendCliSSE({ event: "snapshot", providerType: s2.type, status: s2.status, instanceId: s2.instanceId });
55713
56196
  }
55714
56197
  }
55715
56198
  _req.on("close", () => {
@@ -55725,7 +56208,7 @@ async function handleCliDebug(ctx, type, _req, res) {
55725
56208
  const target = findCliTarget(ctx, type);
55726
56209
  if (!target) {
55727
56210
  const allStates = ctx.instanceManager.collectAllStates();
55728
- ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type) });
56211
+ ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type) });
55729
56212
  return;
55730
56213
  }
55731
56214
  const instance = ctx.instanceManager.getInstance(target.instanceId);
@@ -55771,7 +56254,7 @@ async function handleCliTrace(ctx, type, req, res) {
55771
56254
  const allStates = ctx.instanceManager.collectAllStates();
55772
56255
  ctx.json(res, 404, {
55773
56256
  error: `No running instance for: ${type}`,
55774
- available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type)
56257
+ available: allStates.filter((s2) => s2.category === "cli" || s2.category === "acp").map((s2) => s2.type)
55775
56258
  });
55776
56259
  return;
55777
56260
  }
@@ -56576,7 +57059,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56576
57059
  child.write("\x1B[12;1R");
56577
57060
  ctx.log("Terminal CPR request (\\x1b[6n) intercepted in PTY, responding with dummy coordinates [12;1R]");
56578
57061
  }
56579
- checkAutoApproval(data, (s) => child.write(s));
57062
+ checkAutoApproval(data, (s2) => child.write(s2));
56580
57063
  sendAutoImplSSE(ctx, { event: "output", data: { chunk: data, stream: "stdout" } });
56581
57064
  scheduleAutoStopForVerification();
56582
57065
  });
@@ -56589,7 +57072,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56589
57072
  stdout += chunk;
56590
57073
  clearAutoStopTimer();
56591
57074
  if (chunk.includes("\x1B[6n")) child.stdin?.write("\x1B[1;1R");
56592
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57075
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
56593
57076
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stdout" } });
56594
57077
  scheduleAutoStopForVerification();
56595
57078
  });
@@ -56597,7 +57080,7 @@ async function handleAutoImplement(ctx, type, req, res) {
56597
57080
  const chunk = d.toString();
56598
57081
  stderr += chunk;
56599
57082
  clearAutoStopTimer();
56600
- checkAutoApproval(chunk, (s) => child.stdin?.write(s));
57083
+ checkAutoApproval(chunk, (s2) => child.stdin?.write(s2));
56601
57084
  sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stderr" } });
56602
57085
  scheduleAutoStopForVerification();
56603
57086
  });
@@ -57412,59 +57895,59 @@ var DevServer = class _DevServer {
57412
57895
  // ─── Route Table ─────────────────────────────────────
57413
57896
  routes = [
57414
57897
  // Static routes
57415
- { method: "GET", pattern: "/api/providers", handler: (q, s) => this.handleListProviders(q, s) },
57416
- { method: "GET", pattern: "/api/providers/source-config", handler: (q, s) => this.handleGetProviderSourceConfig(q, s) },
57417
- { method: "POST", pattern: "/api/providers/source-config", handler: (q, s) => this.handleSetProviderSourceConfig(q, s) },
57418
- { method: "GET", pattern: "/api/providers/versions", handler: (q, s) => this.handleDetectVersions(q, s) },
57419
- { method: "POST", pattern: "/api/providers/reload", handler: (q, s) => this.handleReload(q, s) },
57420
- { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s) => this.handleCdpEvaluate(q, s) },
57421
- { method: "POST", pattern: "/api/cdp/click", handler: (q, s) => this.handleCdpClick(q, s) },
57422
- { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s) => this.handleCdpDomQuery(q, s) },
57423
- { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s) => this.handleDomInspect(q, s) },
57424
- { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s) => this.handleDomChildren(q, s) },
57425
- { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s) => this.handleDomAnalyze(q, s) },
57426
- { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s) => this.handleFindByText(q, s) },
57427
- { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s) => this.handleFindCommon(q, s) },
57428
- { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s) => this.handleScreenshot(q, s) },
57429
- { method: "GET", pattern: "/api/cdp/targets", handler: (q, s) => this.handleCdpTargets(q, s) },
57430
- { method: "POST", pattern: "/api/scripts/run", handler: (q, s) => this.handleScriptsRun(q, s) },
57431
- { method: "GET", pattern: "/api/status", handler: (q, s) => this.handleStatus(q, s) },
57432
- { method: "POST", pattern: "/api/watch/start", handler: (q, s) => this.handleWatchStart(q, s) },
57433
- { method: "POST", pattern: "/api/watch/stop", handler: (q, s) => this.handleWatchStop(q, s) },
57434
- { method: "GET", pattern: "/api/watch/events", handler: (q, s) => this.handleSSE(q, s) },
57435
- { method: "POST", pattern: "/api/scaffold", handler: (q, s) => this.handleScaffold(q, s) },
57898
+ { method: "GET", pattern: "/api/providers", handler: (q, s2) => this.handleListProviders(q, s2) },
57899
+ { method: "GET", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleGetProviderSourceConfig(q, s2) },
57900
+ { method: "POST", pattern: "/api/providers/source-config", handler: (q, s2) => this.handleSetProviderSourceConfig(q, s2) },
57901
+ { method: "GET", pattern: "/api/providers/versions", handler: (q, s2) => this.handleDetectVersions(q, s2) },
57902
+ { method: "POST", pattern: "/api/providers/reload", handler: (q, s2) => this.handleReload(q, s2) },
57903
+ { method: "POST", pattern: "/api/cdp/evaluate", handler: (q, s2) => this.handleCdpEvaluate(q, s2) },
57904
+ { method: "POST", pattern: "/api/cdp/click", handler: (q, s2) => this.handleCdpClick(q, s2) },
57905
+ { method: "POST", pattern: "/api/cdp/dom/query", handler: (q, s2) => this.handleCdpDomQuery(q, s2) },
57906
+ { method: "POST", pattern: "/api/cdp/dom/inspect", handler: (q, s2) => this.handleDomInspect(q, s2) },
57907
+ { method: "POST", pattern: "/api/cdp/dom/children", handler: (q, s2) => this.handleDomChildren(q, s2) },
57908
+ { method: "POST", pattern: "/api/cdp/dom/analyze", handler: (q, s2) => this.handleDomAnalyze(q, s2) },
57909
+ { method: "POST", pattern: "/api/cdp/dom/find-text", handler: (q, s2) => this.handleFindByText(q, s2) },
57910
+ { method: "POST", pattern: "/api/cdp/dom/find-common", handler: (q, s2) => this.handleFindCommon(q, s2) },
57911
+ { method: "GET", pattern: "/api/cdp/screenshot", handler: (q, s2) => this.handleScreenshot(q, s2) },
57912
+ { method: "GET", pattern: "/api/cdp/targets", handler: (q, s2) => this.handleCdpTargets(q, s2) },
57913
+ { method: "POST", pattern: "/api/scripts/run", handler: (q, s2) => this.handleScriptsRun(q, s2) },
57914
+ { method: "GET", pattern: "/api/status", handler: (q, s2) => this.handleStatus(q, s2) },
57915
+ { method: "POST", pattern: "/api/watch/start", handler: (q, s2) => this.handleWatchStart(q, s2) },
57916
+ { method: "POST", pattern: "/api/watch/stop", handler: (q, s2) => this.handleWatchStop(q, s2) },
57917
+ { method: "GET", pattern: "/api/watch/events", handler: (q, s2) => this.handleSSE(q, s2) },
57918
+ { method: "POST", pattern: "/api/scaffold", handler: (q, s2) => this.handleScaffold(q, s2) },
57436
57919
  // CLI Debug routes
57437
- { method: "GET", pattern: "/api/cli/status", handler: (q, s) => this.handleCliStatus(q, s) },
57438
- { method: "POST", pattern: "/api/cli/launch", handler: (q, s) => this.handleCliLaunch(q, s) },
57439
- { method: "POST", pattern: "/api/cli/send", handler: (q, s) => this.handleCliSend(q, s) },
57440
- { method: "POST", pattern: "/api/cli/exercise", handler: (q, s) => this.handleCliExercise(q, s) },
57441
- { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s) => this.handleCliFixtureCapture(q, s) },
57442
- { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s) => this.handleCliFixtureReplay(q, s) },
57443
- { method: "POST", pattern: "/api/cli/resolve", handler: (q, s) => this.handleCliResolve(q, s) },
57444
- { method: "POST", pattern: "/api/cli/raw", handler: (q, s) => this.handleCliRaw(q, s) },
57445
- { method: "POST", pattern: "/api/cli/stop", handler: (q, s) => this.handleCliStop(q, s) },
57446
- { method: "GET", pattern: "/api/cli/events", handler: (q, s) => this.handleCliSSE(q, s) },
57447
- { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s, p) => this.handleCliDebug(p[0], q, s) },
57448
- { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s, p) => this.handleCliTrace(p[0], q, s) },
57449
- { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s, p) => this.handleCliFixtureList(p[0], q, s) },
57920
+ { method: "GET", pattern: "/api/cli/status", handler: (q, s2) => this.handleCliStatus(q, s2) },
57921
+ { method: "POST", pattern: "/api/cli/launch", handler: (q, s2) => this.handleCliLaunch(q, s2) },
57922
+ { method: "POST", pattern: "/api/cli/send", handler: (q, s2) => this.handleCliSend(q, s2) },
57923
+ { method: "POST", pattern: "/api/cli/exercise", handler: (q, s2) => this.handleCliExercise(q, s2) },
57924
+ { method: "POST", pattern: "/api/cli/fixture/capture", handler: (q, s2) => this.handleCliFixtureCapture(q, s2) },
57925
+ { method: "POST", pattern: "/api/cli/fixture/replay", handler: (q, s2) => this.handleCliFixtureReplay(q, s2) },
57926
+ { method: "POST", pattern: "/api/cli/resolve", handler: (q, s2) => this.handleCliResolve(q, s2) },
57927
+ { method: "POST", pattern: "/api/cli/raw", handler: (q, s2) => this.handleCliRaw(q, s2) },
57928
+ { method: "POST", pattern: "/api/cli/stop", handler: (q, s2) => this.handleCliStop(q, s2) },
57929
+ { method: "GET", pattern: "/api/cli/events", handler: (q, s2) => this.handleCliSSE(q, s2) },
57930
+ { method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s2, p) => this.handleCliDebug(p[0], q, s2) },
57931
+ { method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q, s2, p) => this.handleCliTrace(p[0], q, s2) },
57932
+ { method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q, s2, p) => this.handleCliFixtureList(p[0], q, s2) },
57450
57933
  // Dynamic routes (provider :type param)
57451
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s, p) => this.handleRunScript(p[0], q, s) },
57452
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s, p) => this.handleListFiles(p[0], q, s) },
57453
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleReadFile(p[0], q, s) },
57454
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleWriteFile(p[0], q, s) },
57455
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s, p) => this.handleSource(p[0], q, s) },
57456
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s, p) => this.handleSave(p[0], q, s) },
57457
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s, p) => this.handleTypeAndSend(p[0], q, s) },
57458
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s, p) => this.handleTypeAndSendAt(p[0], q, s) },
57459
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s, p) => this.handleProviderConfig(p[0], q, s) },
57460
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s, p) => this.handleDomContext(p[0], q, s) },
57461
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s, p) => this.handleAutoImplement(p[0], q, s) },
57462
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s, p) => this.handleAutoImplCancel(p[0], q, s) },
57463
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s, p) => this.handleAutoImplSSE(p[0], q, s) },
57464
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s, p) => this.handleSpawnTest(p[0], q, s) },
57465
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s, p) => this.handleValidate(p[0], q, s) },
57466
- { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s, p) => this.handleAcpChat(p[0], q, s) },
57467
- { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s, p) => this.handleScriptHints(p[0], q, s) }
57934
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s2, p) => this.handleRunScript(p[0], q, s2) },
57935
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s2, p) => this.handleListFiles(p[0], q, s2) },
57936
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleReadFile(p[0], q, s2) },
57937
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s2, p) => this.handleWriteFile(p[0], q, s2) },
57938
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s2, p) => this.handleSource(p[0], q, s2) },
57939
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s2, p) => this.handleSave(p[0], q, s2) },
57940
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s2, p) => this.handleTypeAndSend(p[0], q, s2) },
57941
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/typeAndSendAt$/, handler: (q, s2, p) => this.handleTypeAndSendAt(p[0], q, s2) },
57942
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s2, p) => this.handleProviderConfig(p[0], q, s2) },
57943
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s2, p) => this.handleDomContext(p[0], q, s2) },
57944
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s2, p) => this.handleAutoImplement(p[0], q, s2) },
57945
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/cancel$/, handler: (q, s2, p) => this.handleAutoImplCancel(p[0], q, s2) },
57946
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/auto-implement\/status$/, handler: (q, s2, p) => this.handleAutoImplSSE(p[0], q, s2) },
57947
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/spawn-test$/, handler: (q, s2, p) => this.handleSpawnTest(p[0], q, s2) },
57948
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/validate$/, handler: (q, s2, p) => this.handleValidate(p[0], q, s2) },
57949
+ { method: "POST", pattern: /^\/api\/providers\/([^/]+)\/acp-chat$/, handler: (q, s2, p) => this.handleAcpChat(p[0], q, s2) },
57950
+ { method: "GET", pattern: /^\/api\/providers\/([^/]+)\/script-hints$/, handler: (q, s2, p) => this.handleScriptHints(p[0], q, s2) }
57468
57951
  ];
57469
57952
  matchRoute(method, pathname) {
57470
57953
  for (const route of this.routes) {
@@ -58059,14 +58542,14 @@ var DevServer = class _DevServer {
58059
58542
  warnings.push(...validation.warnings);
58060
58543
  if (config.settings) {
58061
58544
  for (const [key, val] of Object.entries(config.settings)) {
58062
- const s = val;
58063
- if (!s.type) errors.push(`settings.${key}: missing type`);
58064
- else if (!["boolean", "number", "string", "select"].includes(s.type))
58065
- errors.push(`settings.${key}: invalid type '${s.type}'`);
58066
- if (s.default === void 0) warnings.push(`settings.${key}: no default value`);
58067
- if (s.type === "number" && s.min !== void 0 && s.max !== void 0 && s.min > s.max)
58068
- errors.push(`settings.${key}: min (${s.min}) > max (${s.max})`);
58069
- if (s.type === "select" && (!s.options || !Array.isArray(s.options) || s.options.length === 0))
58545
+ const s2 = val;
58546
+ if (!s2.type) errors.push(`settings.${key}: missing type`);
58547
+ else if (!["boolean", "number", "string", "select"].includes(s2.type))
58548
+ errors.push(`settings.${key}: invalid type '${s2.type}'`);
58549
+ if (s2.default === void 0) warnings.push(`settings.${key}: no default value`);
58550
+ if (s2.type === "number" && s2.min !== void 0 && s2.max !== void 0 && s2.min > s2.max)
58551
+ errors.push(`settings.${key}: min (${s2.min}) > max (${s2.max})`);
58552
+ if (s2.type === "select" && (!s2.options || !Array.isArray(s2.options) || s2.options.length === 0))
58070
58553
  errors.push(`settings.${key}: select type requires options[]`);
58071
58554
  }
58072
58555
  }