@adhdev/daemon-standalone 0.9.82-rc.351 → 0.9.82-rc.352

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30036,10 +30036,10 @@ var require_dist3 = __commonJS({
30036
30036
  }
30037
30037
  function getDaemonBuildInfo() {
30038
30038
  if (cached2) return cached2;
30039
- const commit = readInjected(true ? "dad22ef2758130d10a3d55e3c419f0095d5e44b7" : void 0) ?? "unknown";
30040
- const commitShort = readInjected(true ? "dad22ef2" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
- const version2 = readInjected(true ? "0.9.82-rc.351" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
- const builtAt = readInjected(true ? "2026-06-22T04:05:27.792Z" : void 0);
30039
+ const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
30040
+ const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
+ const version2 = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
+ const builtAt = readInjected(true ? "2026-06-22T07:17:32.309Z" : void 0);
30043
30043
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30044
30044
  return cached2;
30045
30045
  }
@@ -32936,6 +32936,7 @@ Follow these recovery rules:
32936
32936
  var LEVEL_NUM;
32937
32937
  var LEVEL_LABEL;
32938
32938
  var currentLevel;
32939
+ var ADHDEV_HOME;
32939
32940
  var LOG_DIR;
32940
32941
  var MAX_LOG_SIZE;
32941
32942
  var MAX_LOG_DAYS;
@@ -32961,7 +32962,8 @@ Follow these recovery rules:
32961
32962
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
32962
32963
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
32963
32964
  currentLevel = "info";
32964
- LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os32.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os32.homedir(), "Library", "Logs", "adhdev") : path9.join(os32.homedir(), ".local", "share", "adhdev", "logs");
32965
+ ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os32.homedir(), ".adhdev");
32966
+ LOG_DIR = path9.join(ADHDEV_HOME, "logs");
32965
32967
  MAX_LOG_SIZE = 5 * 1024 * 1024;
32966
32968
  MAX_LOG_DAYS = 7;
32967
32969
  try {
@@ -33774,6 +33776,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33774
33776
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
33775
33777
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
33776
33778
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
33779
+ reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
33777
33780
  recordDirectDispatchTask: () => recordDirectDispatchTask,
33778
33781
  recordMeshToolCall: () => recordMeshToolCall,
33779
33782
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
@@ -34209,6 +34212,52 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
34209
34212
  return entry;
34210
34213
  });
34211
34214
  }
34215
+ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
34216
+ requireMeshHostQueueOwner(opts);
34217
+ return withQueueLock(meshId, () => {
34218
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
34219
+ if (!entry) return null;
34220
+ if (entry.status !== "assigned") return null;
34221
+ const now = (/* @__PURE__ */ new Date()).toISOString();
34222
+ const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
34223
+ const reclaims = (entry.strandedReclaimCount || 0) + 1;
34224
+ const prevNode = entry.assignedNodeId;
34225
+ const prevSession = entry.assignedSessionId;
34226
+ delete entry.assignedNodeId;
34227
+ delete entry.assignedSessionId;
34228
+ delete entry.assignedProviderType;
34229
+ delete entry.dispatchTimestamp;
34230
+ entry.strandedReclaimCount = reclaims;
34231
+ entry.updatedAt = now;
34232
+ if (reclaims > MAX_STRANDED_RECLAIMS) {
34233
+ entry.status = "failed";
34234
+ entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
34235
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
34236
+ propagateDependencyFailure(meshId, taskId);
34237
+ } else {
34238
+ entry.status = "pending";
34239
+ entry.requeuedAt = now;
34240
+ entry.requeueReason = reason;
34241
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
34242
+ }
34243
+ try {
34244
+ appendLedgerEntry(meshId, {
34245
+ kind: "task_reclaimed",
34246
+ nodeId: prevNode,
34247
+ sessionId: prevSession,
34248
+ payload: {
34249
+ taskId,
34250
+ reason,
34251
+ ...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
34252
+ reclaimCount: reclaims,
34253
+ outcome: entry.status
34254
+ }
34255
+ });
34256
+ } catch {
34257
+ }
34258
+ return entry;
34259
+ });
34260
+ }
34212
34261
  function updateSessionTaskStatus(meshId, sessionId, status, opts) {
34213
34262
  return withQueueLock(meshId, () => {
34214
34263
  const store = MeshRuntimeStore.getInstance();
@@ -34332,6 +34381,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
34332
34381
  var GIT_MUTATION_SUBCOMMANDS;
34333
34382
  var GIT_STASH_READONLY_SUBCOMMANDS;
34334
34383
  var DEPENDENCY_FAILURE_TERMINALS;
34384
+ var MAX_STRANDED_RECLAIMS;
34335
34385
  var init_mesh_work_queue = __esm2({
34336
34386
  "src/mesh/mesh-work-queue.ts"() {
34337
34387
  "use strict";
@@ -34341,6 +34391,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
34341
34391
  init_mesh_runtime_store();
34342
34392
  init_mesh_config();
34343
34393
  init_logger();
34394
+ init_mesh_ledger();
34344
34395
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
34345
34396
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
34346
34397
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -34393,6 +34444,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
34393
34444
  ]);
34394
34445
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
34395
34446
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
34447
+ MAX_STRANDED_RECLAIMS = 3;
34396
34448
  }
34397
34449
  });
34398
34450
  function loadDatabaseCtor() {
@@ -35280,6 +35332,22 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
35280
35332
  updatedAt: r.updated_at
35281
35333
  }));
35282
35334
  }
35335
+ /**
35336
+ * Bug B watchdog support: true when at least one delivery record for the task has
35337
+ * reached a confirmed-handed-off status (delivered / acked / completed). The
35338
+ * assigned-stranded watchdog uses this to distinguish a dispatch that was never
35339
+ * confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
35340
+ * in-flight or completion-lost task, which is PHASE 4's responsibility, not this
35341
+ * watchdog's). Indexed by (mesh_id, task_id).
35342
+ */
35343
+ taskHasConfirmedDelivery(meshId, taskId) {
35344
+ const row = this.db.prepare(`
35345
+ SELECT 1 FROM mesh_session_delivery
35346
+ WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
35347
+ LIMIT 1
35348
+ `).get(meshId, taskId);
35349
+ return !!row;
35350
+ }
35283
35351
  expireStaleSessionDeliveries(meshId) {
35284
35352
  const now = (/* @__PURE__ */ new Date()).toISOString();
35285
35353
  this.db.prepare(`
@@ -37704,6 +37772,35 @@ ${rendered}`, "utf-8");
37704
37772
  if (!trimmed) return false;
37705
37773
  return normalizeMeshNodeId(node) === trimmed;
37706
37774
  }
37775
+ function machineCoreFromDaemonId(id) {
37776
+ const trimmed = readString5(id);
37777
+ if (!trimmed) return void 0;
37778
+ for (const prefix of DAEMON_ID_PREFIXES) {
37779
+ if (trimmed.startsWith(prefix)) {
37780
+ const core = trimmed.slice(prefix.length).trim();
37781
+ return core || void 0;
37782
+ }
37783
+ }
37784
+ return trimmed;
37785
+ }
37786
+ function expandDaemonIdForms(ids) {
37787
+ const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
37788
+ const out = [];
37789
+ const seen = /* @__PURE__ */ new Set();
37790
+ const add = (value) => {
37791
+ if (!value || seen.has(value)) return;
37792
+ seen.add(value);
37793
+ out.push(value);
37794
+ };
37795
+ for (const raw of list) add(readString5(raw));
37796
+ for (const raw of list) {
37797
+ const core = machineCoreFromDaemonId(readString5(raw));
37798
+ if (!core || !core.startsWith("mach_")) continue;
37799
+ add(core);
37800
+ for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
37801
+ }
37802
+ return out;
37803
+ }
37707
37804
  function summarizeGitShape(status) {
37708
37805
  const record2 = readRecord3(status);
37709
37806
  if (!Object.keys(record2).length) return null;
@@ -37738,9 +37835,11 @@ ${rendered}`, "utf-8");
37738
37835
  submodules
37739
37836
  };
37740
37837
  }
37838
+ var DAEMON_ID_PREFIXES;
37741
37839
  var init_dist = __esm2({
37742
37840
  "../mesh-shared/dist/index.mjs"() {
37743
37841
  "use strict";
37842
+ DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
37744
37843
  }
37745
37844
  });
37746
37845
  function readString6(value) {
@@ -38395,17 +38494,7 @@ Next step: ${nextStep}`;
38395
38494
  }
38396
38495
  });
38397
38496
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
38398
- const raw = Array.isArray(coordinatorDaemonId) ? coordinatorDaemonId : coordinatorDaemonId != null ? [coordinatorDaemonId] : [];
38399
- const seen = /* @__PURE__ */ new Set();
38400
- const out = [];
38401
- for (const id of raw) {
38402
- if (typeof id !== "string") continue;
38403
- const trimmed = id.trim();
38404
- if (!trimmed || seen.has(trimmed)) continue;
38405
- seen.add(trimmed);
38406
- out.push(trimmed);
38407
- }
38408
- return out;
38497
+ return expandDaemonIdForms(coordinatorDaemonId);
38409
38498
  }
38410
38499
  function readRefineJobId2(event) {
38411
38500
  const metadata = readRecord4(event.metadataEvent) || event;
@@ -38800,6 +38889,7 @@ Next step: ${nextStep}`;
38800
38889
  init_mesh_ledger();
38801
38890
  init_mesh_runtime_store();
38802
38891
  init_mesh_events_utils();
38892
+ init_dist();
38803
38893
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
38804
38894
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
38805
38895
  MAX_PENDING_EVENTS_KEEP = 50;
@@ -42257,12 +42347,9 @@ ${cleanBody}`;
42257
42347
  }
42258
42348
  });
42259
42349
  function resolveCoordinatorDrainDaemonIds(components) {
42260
- const ids = /* @__PURE__ */ new Set();
42261
42350
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
42262
- if (statusInstanceId) ids.add(statusInstanceId);
42263
42351
  const machineId = readNonEmptyString2(loadConfig2().machineId);
42264
- if (machineId) ids.add(machineId);
42265
- return [...ids];
42352
+ return expandDaemonIdForms([statusInstanceId, machineId]);
42266
42353
  }
42267
42354
  function getCachedMeshByWorkspace(workspace) {
42268
42355
  const now = Date.now();
@@ -42413,6 +42500,55 @@ ${cleanBody}`;
42413
42500
  return void 0;
42414
42501
  }
42415
42502
  }
42503
+ function deliverTaskToSession(dispatchThunk, ctx) {
42504
+ const delivery = createSessionDelivery({
42505
+ meshId: ctx.meshId,
42506
+ nodeId: ctx.nodeId,
42507
+ sessionId: ctx.sessionId,
42508
+ providerType: ctx.providerType,
42509
+ taskId: ctx.task.id,
42510
+ kind: "task",
42511
+ message: ctx.task.message,
42512
+ status: "delivering",
42513
+ ...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
42514
+ ...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
42515
+ });
42516
+ let dispatchPromise;
42517
+ try {
42518
+ dispatchPromise = Promise.resolve(dispatchThunk());
42519
+ } catch (e) {
42520
+ dispatchPromise = Promise.reject(e);
42521
+ }
42522
+ let timer;
42523
+ const guarded = Promise.race([
42524
+ dispatchPromise,
42525
+ new Promise((_, reject) => {
42526
+ timer = setTimeout(
42527
+ () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
42528
+ DISPATCH_CONFIRM_TIMEOUT_MS
42529
+ );
42530
+ if (typeof timer?.unref === "function") timer.unref();
42531
+ })
42532
+ ]);
42533
+ guarded.then(() => {
42534
+ if (timer) clearTimeout(timer);
42535
+ updateSessionDeliveryStatus(delivery.id, "delivered");
42536
+ }).catch((e) => {
42537
+ if (timer) clearTimeout(timer);
42538
+ LOG2.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
42539
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
42540
+ updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
42541
+ try {
42542
+ appendLedgerEntry(ctx.meshId, {
42543
+ kind: "dispatch_failed",
42544
+ nodeId: ctx.nodeId,
42545
+ sessionId: ctx.sessionId,
42546
+ payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
42547
+ });
42548
+ } catch {
42549
+ }
42550
+ });
42551
+ }
42416
42552
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
42417
42553
  const mesh = getMeshWithCache(components, meshId);
42418
42554
  const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
@@ -42431,46 +42567,33 @@ ${cleanBody}`;
42431
42567
  if (!isLocalNode) {
42432
42568
  const localDaemonIdForDispatch = readNonEmptyString2(loadConfig2().machineId) || void 0;
42433
42569
  const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
42434
- const delivery2 = createSessionDelivery({
42435
- meshId,
42436
- nodeId,
42437
- sessionId,
42438
- providerType,
42439
- taskId: task.id,
42440
- kind: "task",
42441
- message: task.message,
42442
- status: "delivering",
42443
- ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
42444
- ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
42445
- });
42446
- components.dispatchMeshCommand(node.daemonId, "agent_command", {
42447
- targetSessionId: sessionId,
42448
- cliType: providerType,
42449
- action: "send_chat",
42450
- message: task.message,
42451
- meshContext: {
42570
+ const dispatchMeshCommand = components.dispatchMeshCommand;
42571
+ const remoteDaemonId = node.daemonId;
42572
+ deliverTaskToSession(
42573
+ () => dispatchMeshCommand(remoteDaemonId, "agent_command", {
42574
+ targetSessionId: sessionId,
42575
+ cliType: providerType,
42576
+ action: "send_chat",
42577
+ message: task.message,
42578
+ meshContext: {
42579
+ meshId,
42580
+ nodeId,
42581
+ taskId: task.id,
42582
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
42583
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
42584
+ }
42585
+ }),
42586
+ {
42452
42587
  meshId,
42453
42588
  nodeId,
42454
- taskId: task.id,
42455
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
42456
- ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
42457
- }
42458
- }).then(() => {
42459
- updateSessionDeliveryStatus(delivery2.id, "delivered");
42460
- }).catch((e) => {
42461
- LOG2.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
42462
- updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
42463
- updateTaskStatus(meshId, task.id, "pending");
42464
- try {
42465
- appendLedgerEntry(meshId, {
42466
- kind: "dispatch_failed",
42467
- nodeId,
42468
- sessionId,
42469
- payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
42470
- });
42471
- } catch {
42589
+ sessionId,
42590
+ providerType,
42591
+ task,
42592
+ transport: "remote",
42593
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
42594
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
42472
42595
  }
42473
- });
42596
+ );
42474
42597
  return true;
42475
42598
  }
42476
42599
  }
@@ -42492,39 +42615,24 @@ ${cleanBody}`;
42492
42615
  }
42493
42616
  } catch {
42494
42617
  }
42495
- const delivery = createSessionDelivery({
42496
- meshId,
42497
- nodeId,
42498
- sessionId,
42499
- providerType,
42500
- taskId: task.id,
42501
- kind: "task",
42502
- message: task.message,
42503
- status: "delivering",
42504
- ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
42505
- ...readNonEmptyString2(loadConfig2().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig2().machineId) } : {}
42506
- });
42507
- components.cliManager.handleCliCommand("agent_command", {
42508
- targetSessionId: sessionId,
42509
- cliType: providerType,
42510
- action: "send_chat",
42511
- message: task.message
42512
- }).then(() => {
42513
- updateSessionDeliveryStatus(delivery.id, "delivered");
42514
- }).catch((e) => {
42515
- LOG2.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
42516
- updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
42517
- updateTaskStatus(meshId, task.id, "pending");
42518
- try {
42519
- appendLedgerEntry(meshId, {
42520
- kind: "dispatch_failed",
42521
- nodeId,
42522
- sessionId,
42523
- payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
42524
- });
42525
- } catch {
42618
+ deliverTaskToSession(
42619
+ () => components.cliManager.handleCliCommand("agent_command", {
42620
+ targetSessionId: sessionId,
42621
+ cliType: providerType,
42622
+ action: "send_chat",
42623
+ message: task.message
42624
+ }),
42625
+ {
42626
+ meshId,
42627
+ nodeId,
42628
+ sessionId,
42629
+ providerType,
42630
+ task,
42631
+ transport: "local",
42632
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
42633
+ ...readNonEmptyString2(loadConfig2().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig2().machineId) } : {}
42526
42634
  }
42527
- });
42635
+ );
42528
42636
  return true;
42529
42637
  }
42530
42638
  function sweepExpiredCooldowns() {
@@ -42789,7 +42897,7 @@ ${cleanBody}`;
42789
42897
  }
42790
42898
  }
42791
42899
  const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
42792
- if (task.targetNodeId && readMeshNodeId(node) !== task.targetNodeId) return false;
42900
+ if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
42793
42901
  if (task.requiredTags?.length) {
42794
42902
  const priorities = normalizeProviderPriority(node?.policy);
42795
42903
  const providerCandidates = priorities.length ? priorities : [void 0];
@@ -42800,7 +42908,12 @@ ${cleanBody}`;
42800
42908
  return true;
42801
42909
  }) : [];
42802
42910
  if (!candidateNodes.length) {
42803
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
42911
+ const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
42912
+ markAutoLaunch(meshId, task.id, {
42913
+ status: "skipped",
42914
+ reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
42915
+ nodeId: task.targetNodeId
42916
+ });
42804
42917
  continue;
42805
42918
  }
42806
42919
  const strategy = resolveSchedulingStrategy(mesh);
@@ -43759,6 +43872,7 @@ ${cleanBody}`;
43759
43872
  var idleAutoFastForwardLastAttempt;
43760
43873
  var INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS;
43761
43874
  var RECENT_COMPLETION_FINGERPRINT_TTL_MS;
43875
+ var DISPATCH_CONFIRM_TIMEOUT_MS;
43762
43876
  var autoLaunchInProgress;
43763
43877
  var autoLaunchCooldownUntil;
43764
43878
  var AUTO_LAUNCH_COOLDOWN_MS;
@@ -43796,6 +43910,7 @@ ${cleanBody}`;
43796
43910
  idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
43797
43911
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
43798
43912
  RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
43913
+ DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
43799
43914
  autoLaunchInProgress = /* @__PURE__ */ new Set();
43800
43915
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
43801
43916
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -43849,12 +43964,9 @@ ${cleanBody}`;
43849
43964
  return DEFAULT_RECONCILE_INTERVAL_MS;
43850
43965
  }
43851
43966
  function resolveCoordinatorDaemonIds(components) {
43852
- const ids = /* @__PURE__ */ new Set();
43853
43967
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
43854
- if (statusInstanceId) ids.add(statusInstanceId);
43855
43968
  const machineId = readNonEmptyString2(loadConfig2().machineId);
43856
- if (machineId) ids.add(machineId);
43857
- return [...ids];
43969
+ return expandDaemonIdForms([statusInstanceId, machineId]);
43858
43970
  }
43859
43971
  function daemonHostsMesh(mesh, daemonIds) {
43860
43972
  const host = mesh.meshHost;
@@ -43938,6 +44050,24 @@ ${cleanBody}`;
43938
44050
  }
43939
44051
  }
43940
44052
  }
44053
+ function recoverStrandedAssignedDispatches(meshId, store) {
44054
+ const assigned = getQueue(meshId, { status: ["assigned"] });
44055
+ if (!assigned.length) return;
44056
+ const nowMs = Date.now();
44057
+ for (const row of assigned) {
44058
+ const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
44059
+ if (!Number.isFinite(dispatchedAtMs)) continue;
44060
+ if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
44061
+ if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
44062
+ const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
44063
+ reason: "assigned_stranded_dispatch_unconfirmed",
44064
+ ageMs: nowMs - dispatchedAtMs
44065
+ });
44066
+ if (reclaimed) {
44067
+ LOG2.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})`);
44068
+ }
44069
+ }
44070
+ }
43941
44071
  async function runMeshReconcileTick(components) {
43942
44072
  const localDaemonId = readNonEmptyString2(loadConfig2().machineId) || void 0;
43943
44073
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -43967,6 +44097,17 @@ ${cleanBody}`;
43967
44097
  }
43968
44098
  }
43969
44099
  }
44100
+ if (store) {
44101
+ for (const mesh of listMeshes()) {
44102
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
44103
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
44104
+ try {
44105
+ recoverStrandedAssignedDispatches(mesh.id, store);
44106
+ } catch (e) {
44107
+ LOG2.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
44108
+ }
44109
+ }
44110
+ }
43970
44111
  for (const mesh of listMeshes()) {
43971
44112
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
43972
44113
  if (!daemonHostsMesh(mesh, selfIds)) continue;
@@ -44357,6 +44498,7 @@ ${cleanBody}`;
44357
44498
  var DEFAULT_RECONCILE_INTERVAL_MS;
44358
44499
  var DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
44359
44500
  var heldEventLedgerRecorded;
44501
+ var ASSIGNED_STRANDED_DEADLINE_MS;
44360
44502
  var STRICT_SESSION_MATCH_TTL_MS;
44361
44503
  var init_mesh_reconcile_loop = __esm2({
44362
44504
  "src/mesh/mesh-reconcile-loop.ts"() {
@@ -44370,6 +44512,7 @@ ${cleanBody}`;
44370
44512
  init_mesh_events_coordinator();
44371
44513
  init_mesh_unresolved_forward_outbox();
44372
44514
  init_mesh_events_utils();
44515
+ init_dist();
44373
44516
  init_mesh_work_queue();
44374
44517
  init_mesh_ledger();
44375
44518
  init_mesh_active_work();
@@ -44378,6 +44521,7 @@ ${cleanBody}`;
44378
44521
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
44379
44522
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
44380
44523
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
44524
+ ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
44381
44525
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
44382
44526
  }
44383
44527
  });
@@ -44407,6 +44551,82 @@ ${cleanBody}`;
44407
44551
  init_mesh_events_coordinator();
44408
44552
  }
44409
44553
  });
44554
+ function normalizeApprovalLabel(value) {
44555
+ return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
44556
+ }
44557
+ function isNegativeApprovalLabel(value) {
44558
+ const label = normalizeApprovalLabel(value);
44559
+ return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
44560
+ }
44561
+ function hasNegativeApprovalOption(buttons) {
44562
+ return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
44563
+ }
44564
+ function getApprovalPositiveHints(provider) {
44565
+ const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
44566
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
44567
+ }
44568
+ function pickApprovalButton(buttons, provider) {
44569
+ const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
44570
+ if (labels.length === 0) {
44571
+ return { index: -1, label: "" };
44572
+ }
44573
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
44574
+ const hints = getApprovalPositiveHints(provider);
44575
+ for (const hint of hints) {
44576
+ const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
44577
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
44578
+ const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
44579
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
44580
+ const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
44581
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
44582
+ }
44583
+ return { index: -1, label: "" };
44584
+ }
44585
+ function pickAutoApprovalButton(buttons) {
44586
+ const labels = (buttons || []).map((button) => String(button || "").trim());
44587
+ const index = labels.findIndex(Boolean);
44588
+ return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
44589
+ }
44590
+ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
44591
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
44592
+ const cleanMessage = String(modalMessage || "").trim();
44593
+ if (cleanMessage) lines.push(cleanMessage);
44594
+ return lines.join("\n");
44595
+ }
44596
+ function looksLikeActiveApprovalPromptText(content) {
44597
+ const text = content.trim();
44598
+ if (!text || text.length > 2e3) return false;
44599
+ const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
44600
+ const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
44601
+ if (hasApprovalQuestion && hasNumberedChoices) return true;
44602
+ const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
44603
+ const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
44604
+ const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
44605
+ if (hasDontAskAgain && hasNoOption) return true;
44606
+ if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
44607
+ return false;
44608
+ }
44609
+ var DEFAULT_APPROVAL_POSITIVE_HINTS;
44610
+ var init_approval_utils = __esm2({
44611
+ "src/providers/approval-utils.ts"() {
44612
+ "use strict";
44613
+ DEFAULT_APPROVAL_POSITIVE_HINTS = [
44614
+ "yes",
44615
+ "allow once",
44616
+ "approve",
44617
+ "accept",
44618
+ "continue",
44619
+ "run",
44620
+ "proceed",
44621
+ "confirm",
44622
+ "save",
44623
+ "ok",
44624
+ "trust",
44625
+ "allow",
44626
+ "always allow"
44627
+ ];
44628
+ }
44629
+ });
44410
44630
  function normalizeCategories(categories) {
44411
44631
  if (!Array.isArray(categories)) return [];
44412
44632
  return categories.map((category) => String(category || "").trim()).filter(Boolean);
@@ -45393,6 +45613,27 @@ ${cleanBody}`;
45393
45613
  });
45394
45614
  return { prompt, footers };
45395
45615
  }
45616
+ function extractButtonLabels(spec, text) {
45617
+ if (!text) return [];
45618
+ const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
45619
+ const buttonRe = compile2(spec.buttonPattern, flags);
45620
+ const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
45621
+ const out = [];
45622
+ for (const line of text.split("\n")) {
45623
+ buttonRe.lastIndex = 0;
45624
+ const m = buttonRe.exec(line);
45625
+ if (!m) continue;
45626
+ const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
45627
+ if (captured && captured.trim()) out.push(captured.trim());
45628
+ }
45629
+ return out;
45630
+ }
45631
+ function buttonBlockApprovalCue(spec, text) {
45632
+ const labels = extractButtonLabels(spec, text);
45633
+ if (labels.length < 2) return false;
45634
+ if (pickApprovalButton(labels).index < 0) return false;
45635
+ return hasNegativeApprovalOption(labels);
45636
+ }
45396
45637
  function modalMatches(spec, input) {
45397
45638
  const text = input.screenText ?? "";
45398
45639
  const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
@@ -45401,6 +45642,7 @@ ${cleanBody}`;
45401
45642
  const re = compile2(variant.regex, variant.flags ?? "i");
45402
45643
  if (re.test(text)) return true;
45403
45644
  }
45645
+ if (buttonBlockApprovalCue(spec, text)) return true;
45404
45646
  return false;
45405
45647
  }
45406
45648
  function evaluateGroup(group, spec, input, compiled) {
@@ -45455,6 +45697,7 @@ ${cleanBody}`;
45455
45697
  "src/providers/sdk/v1/builders/cli/detect-status.ts"() {
45456
45698
  "use strict";
45457
45699
  init_visible_region();
45700
+ init_approval_utils();
45458
45701
  DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
45459
45702
  }
45460
45703
  });
@@ -55843,73 +56086,7 @@ ${effect.notification.body || ""}`.trim();
55843
56086
  if (raw.coverage === "full" || raw.coverage === "tail" || raw.coverage === "current-turn") normalized.coverage = raw.coverage;
55844
56087
  return normalized;
55845
56088
  }
55846
- var DEFAULT_APPROVAL_POSITIVE_HINTS = [
55847
- "yes",
55848
- "allow once",
55849
- "approve",
55850
- "accept",
55851
- "continue",
55852
- "run",
55853
- "proceed",
55854
- "confirm",
55855
- "save",
55856
- "ok",
55857
- "trust",
55858
- "allow",
55859
- "always allow"
55860
- ];
55861
- function normalizeApprovalLabel(value) {
55862
- return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
55863
- }
55864
- function isNegativeApprovalLabel(value) {
55865
- const label = normalizeApprovalLabel(value);
55866
- return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
55867
- }
55868
- function getApprovalPositiveHints(provider) {
55869
- const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
55870
- return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
55871
- }
55872
- function pickApprovalButton(buttons, provider) {
55873
- const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
55874
- if (labels.length === 0) {
55875
- return { index: -1, label: "" };
55876
- }
55877
- const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
55878
- const hints = getApprovalPositiveHints(provider);
55879
- for (const hint of hints) {
55880
- const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
55881
- if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
55882
- const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
55883
- if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
55884
- const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
55885
- if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
55886
- }
55887
- return { index: -1, label: "" };
55888
- }
55889
- function pickAutoApprovalButton(buttons) {
55890
- const labels = (buttons || []).map((button) => String(button || "").trim());
55891
- const index = labels.findIndex(Boolean);
55892
- return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
55893
- }
55894
- function formatAutoApprovalMessage(modalMessage, buttonLabel) {
55895
- const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
55896
- const cleanMessage = String(modalMessage || "").trim();
55897
- if (cleanMessage) lines.push(cleanMessage);
55898
- return lines.join("\n");
55899
- }
55900
- function looksLikeActiveApprovalPromptText(content) {
55901
- const text = content.trim();
55902
- if (!text || text.length > 2e3) return false;
55903
- const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
55904
- const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
55905
- if (hasApprovalQuestion && hasNumberedChoices) return true;
55906
- const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
55907
- const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
55908
- const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
55909
- if (hasDontAskAgain && hasNoOption) return true;
55910
- if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
55911
- return false;
55912
- }
56089
+ init_approval_utils();
55913
56090
  init_provider_patch_state();
55914
56091
  init_chat_message_normalization();
55915
56092
  init_open_panel_support();
@@ -56973,6 +57150,7 @@ ${effect.notification.body || ""}`.trim();
56973
57150
  var import_node_crypto3 = require("crypto");
56974
57151
  init_contracts();
56975
57152
  init_provider_input_support();
57153
+ init_approval_utils();
56976
57154
  init_coordinator_registry();
56977
57155
  init_logger();
56978
57156
  init_debug_config();
@@ -64913,6 +65091,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
64913
65091
  }
64914
65092
  init_logger();
64915
65093
  init_control_effects();
65094
+ init_approval_utils();
64916
65095
  init_provider_patch_state();
64917
65096
  function normalizeProviderSessionId(provider, providerSessionId) {
64918
65097
  const normalizedId = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
@@ -65166,6 +65345,20 @@ ${formatManifestValidationIssues2(validation.issues)}`,
65166
65345
  * keystroke until the modal *content* has settled.
65167
65346
  */
65168
65347
  static AUTO_APPROVE_SETTLE_MS = 600;
65348
+ /**
65349
+ * Busy-side hysteresis for the settle gate. A momentary `generating` flip
65350
+ * while the SAME approval modal's button block is still on screen (its
65351
+ * question line scrolled out of the captured frame, only the buttons + a
65352
+ * residual `esc to interrupt` spinner remain) briefly reports
65353
+ * status!=waiting_approval. Without hysteresis that flip wipes the settle
65354
+ * clock, and the modal→generating→modal flap restarts the 600ms window
65355
+ * every time so auto-approve never fires. We keep the in-progress settle
65356
+ * gate warm across an inactive blip up to this bound; only once the modal
65357
+ * has genuinely stayed gone this long (a real resolution → idle) is the
65358
+ * gate cleared. Bounded so a genuinely new, later approval still re-settles
65359
+ * from scratch rather than firing on a stale timestamp.
65360
+ */
65361
+ static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
65169
65362
  adapter;
65170
65363
  context = null;
65171
65364
  events = [];
@@ -65187,6 +65380,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
65187
65380
  pendingAutoApprovalSignature = "";
65188
65381
  pendingAutoApprovalSince = 0;
65189
65382
  autoApproveSettleTimer = null;
65383
+ // Wall-clock when auto-approve first observed status!=waiting_approval while
65384
+ // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
65385
+ // brief generating flip does not immediately wipe the settle clock.
65386
+ autoApproveInactiveSince = 0;
65190
65387
  controlValues = {};
65191
65388
  summaryMetadata = void 0;
65192
65389
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -66007,14 +66204,28 @@ ${formatManifestValidationIssues2(validation.issues)}`,
66007
66204
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
66008
66205
  if (!autoApproveActive) {
66009
66206
  this.lastAutoApprovalSignature = "";
66207
+ if (this.pendingAutoApprovalSince) {
66208
+ if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
66209
+ const goneForMs = now - this.autoApproveInactiveSince;
66210
+ if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
66211
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
66212
+ this.autoApproveSettleTimer = setTimeout(() => {
66213
+ this.autoApproveSettleTimer = null;
66214
+ this.recheckAutoApproveSettled();
66215
+ }, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
66216
+ return autoApproveActive;
66217
+ }
66218
+ }
66010
66219
  this.pendingAutoApprovalSignature = "";
66011
66220
  this.pendingAutoApprovalSince = 0;
66221
+ this.autoApproveInactiveSince = 0;
66012
66222
  if (this.autoApproveSettleTimer) {
66013
66223
  clearTimeout(this.autoApproveSettleTimer);
66014
66224
  this.autoApproveSettleTimer = null;
66015
66225
  }
66016
66226
  return autoApproveActive;
66017
66227
  }
66228
+ this.autoApproveInactiveSince = 0;
66018
66229
  const modal = adapterStatus.activeModal;
66019
66230
  const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
66020
66231
  if (!modal || buttons.length === 0) {
@@ -66024,18 +66235,18 @@ ${formatManifestValidationIssues2(validation.issues)}`,
66024
66235
  if (buttonIndex < 0) {
66025
66236
  return autoApproveActive;
66026
66237
  }
66027
- const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
66028
- const signature = [
66029
- approvalEntrySeq,
66238
+ const modalSignature = [
66030
66239
  typeof modal?.message === "string" ? modal.message.trim() : "",
66031
66240
  buttons.join("|"),
66032
66241
  buttonIndex
66033
66242
  ].join("::");
66034
- if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
66243
+ const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
66244
+ const busySignature = `${approvalEntrySeq}::${modalSignature}`;
66245
+ if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
66035
66246
  return autoApproveActive;
66036
66247
  }
66037
- if (signature !== this.pendingAutoApprovalSignature) {
66038
- this.pendingAutoApprovalSignature = signature;
66248
+ if (modalSignature !== this.pendingAutoApprovalSignature) {
66249
+ this.pendingAutoApprovalSignature = modalSignature;
66039
66250
  this.pendingAutoApprovalSince = now;
66040
66251
  }
66041
66252
  const settledForMs = now - this.pendingAutoApprovalSince;
@@ -66052,9 +66263,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
66052
66263
  this.autoApproveSettleTimer = null;
66053
66264
  }
66054
66265
  this.autoApproveBusy = true;
66055
- this.lastAutoApprovalSignature = signature;
66266
+ this.lastAutoApprovalSignature = busySignature;
66056
66267
  this.pendingAutoApprovalSignature = "";
66057
66268
  this.pendingAutoApprovalSince = 0;
66269
+ this.autoApproveInactiveSince = 0;
66058
66270
  if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
66059
66271
  this.autoApproveBusyTimer = setTimeout(() => {
66060
66272
  this.autoApproveBusy = false;
@@ -72903,7 +73115,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
72903
73115
  var fs23 = __toESM2(require("fs"));
72904
73116
  var path35 = __toESM2(require("path"));
72905
73117
  var os26 = __toESM2(require("os"));
72906
- var LOG_DIR2 = process.platform === "win32" ? path35.join(process.env.LOCALAPPDATA || process.env.APPDATA || path35.join(os26.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path35.join(os26.homedir(), "Library", "Logs", "adhdev") : path35.join(os26.homedir(), ".local", "share", "adhdev", "logs");
73118
+ var ADHDEV_HOME2 = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path35.join(os26.homedir(), ".adhdev");
73119
+ var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
72907
73120
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
72908
73121
  var MAX_DAYS = 7;
72909
73122
  try {
@@ -73738,13 +73951,14 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
73738
73951
  }
73739
73952
  }
73740
73953
  }
73741
- function stopSessionHostProcesses(appName) {
73954
+ async function stopSessionHostProcesses(appName) {
73742
73955
  const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
73956
+ let killedPid = null;
73743
73957
  try {
73744
73958
  if (fs25.existsSync(pidFile)) {
73745
73959
  const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
73746
73960
  if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
73747
- killPid2(pid);
73961
+ if (killPid2(pid)) killedPid = pid;
73748
73962
  }
73749
73963
  }
73750
73964
  } catch {
@@ -73754,6 +73968,15 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
73754
73968
  } catch {
73755
73969
  }
73756
73970
  }
73971
+ if (killedPid !== null) {
73972
+ await waitForPidExit(killedPid, 15e3);
73973
+ }
73974
+ }
73975
+ function isRetriableInstallLockError(error48) {
73976
+ const code = error48?.code;
73977
+ if (code === "EBUSY" || code === "EPERM") return true;
73978
+ const text = `${error48?.message || ""} ${error48?.stderr || ""}`;
73979
+ return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
73757
73980
  }
73758
73981
  function removeDaemonPidFile() {
73759
73982
  const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
@@ -73833,22 +74056,37 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
73833
74056
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
73834
74057
  await waitForPidExit(payload.parentPid, 15e3);
73835
74058
  }
73836
- stopSessionHostProcesses(sessionHostAppName);
74059
+ await stopSessionHostProcesses(sessionHostAppName);
73837
74060
  removeDaemonPidFile();
73838
74061
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
73839
74062
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
73840
74063
  appendUpgradeLog(`Installing ${spec}`);
73841
- const installOutput = (0, import_child_process8.execFileSync)(
73842
- installCommand.command,
73843
- installCommand.args,
73844
- {
73845
- encoding: "utf8",
73846
- stdio: "pipe",
73847
- maxBuffer: 20 * 1024 * 1024,
73848
- env: buildInstallEnvWithNodeOnPath(),
73849
- ...installCommand.execOptions
74064
+ const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
74065
+ let installOutput = "";
74066
+ for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
74067
+ try {
74068
+ installOutput = String((0, import_child_process8.execFileSync)(
74069
+ installCommand.command,
74070
+ installCommand.args,
74071
+ {
74072
+ encoding: "utf8",
74073
+ stdio: "pipe",
74074
+ maxBuffer: 20 * 1024 * 1024,
74075
+ env: buildInstallEnvWithNodeOnPath(),
74076
+ ...installCommand.execOptions
74077
+ }
74078
+ ));
74079
+ break;
74080
+ } catch (error48) {
74081
+ if (attempt < maxInstallAttempts && isRetriableInstallLockError(error48)) {
74082
+ appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error48?.code || "lock"}); cleaning staging and retrying after backoff`);
74083
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
74084
+ await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
74085
+ continue;
74086
+ }
74087
+ throw error48;
73850
74088
  }
73851
- );
74089
+ }
73852
74090
  if (installOutput.trim()) {
73853
74091
  appendUpgradeLog(installOutput.trim());
73854
74092
  }
@@ -82592,6 +82830,7 @@ ${ptyResult.output.slice(-2e3)}`);
82592
82830
  }
82593
82831
  };
82594
82832
  init_logger();
82833
+ init_approval_utils();
82595
82834
  init_chat_message_normalization();
82596
82835
  var AgentStreamPoller = class {
82597
82836
  deps;