@adhdev/daemon-core 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
@@ -316,10 +316,10 @@ function readInjected(value) {
316
316
  }
317
317
  function getDaemonBuildInfo() {
318
318
  if (cached) return cached;
319
- const commit = readInjected(true ? "dad22ef2758130d10a3d55e3c419f0095d5e44b7" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "dad22ef2" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.351" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-22T04:04:59.936Z" : void 0);
319
+ const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-22T07:17:05.715Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -3195,7 +3195,7 @@ function installGlobalInterceptor() {
3195
3195
  function getLogPath() {
3196
3196
  return currentLogFile;
3197
3197
  }
3198
- var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3198
+ var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3199
3199
  var init_logger = __esm({
3200
3200
  "src/logging/logger.ts"() {
3201
3201
  "use strict";
@@ -3206,7 +3206,8 @@ var init_logger = __esm({
3206
3206
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
3207
3207
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
3208
3208
  currentLevel = "info";
3209
- LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
3209
+ ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
3210
+ LOG_DIR = path9.join(ADHDEV_HOME, "logs");
3210
3211
  MAX_LOG_SIZE = 5 * 1024 * 1024;
3211
3212
  MAX_LOG_DAYS = 7;
3212
3213
  try {
@@ -4007,6 +4008,7 @@ __export(mesh_work_queue_exports, {
4007
4008
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
4008
4009
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
4009
4010
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
4011
+ reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
4010
4012
  recordDirectDispatchTask: () => recordDirectDispatchTask,
4011
4013
  recordMeshToolCall: () => recordMeshToolCall,
4012
4014
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
@@ -4442,6 +4444,52 @@ function requeueTask(meshId, taskId, opts) {
4442
4444
  return entry;
4443
4445
  });
4444
4446
  }
4447
+ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
4448
+ requireMeshHostQueueOwner(opts);
4449
+ return withQueueLock(meshId, () => {
4450
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
4451
+ if (!entry) return null;
4452
+ if (entry.status !== "assigned") return null;
4453
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4454
+ const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
4455
+ const reclaims = (entry.strandedReclaimCount || 0) + 1;
4456
+ const prevNode = entry.assignedNodeId;
4457
+ const prevSession = entry.assignedSessionId;
4458
+ delete entry.assignedNodeId;
4459
+ delete entry.assignedSessionId;
4460
+ delete entry.assignedProviderType;
4461
+ delete entry.dispatchTimestamp;
4462
+ entry.strandedReclaimCount = reclaims;
4463
+ entry.updatedAt = now;
4464
+ if (reclaims > MAX_STRANDED_RECLAIMS) {
4465
+ entry.status = "failed";
4466
+ entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
4467
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4468
+ propagateDependencyFailure(meshId, taskId);
4469
+ } else {
4470
+ entry.status = "pending";
4471
+ entry.requeuedAt = now;
4472
+ entry.requeueReason = reason;
4473
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4474
+ }
4475
+ try {
4476
+ appendLedgerEntry(meshId, {
4477
+ kind: "task_reclaimed",
4478
+ nodeId: prevNode,
4479
+ sessionId: prevSession,
4480
+ payload: {
4481
+ taskId,
4482
+ reason,
4483
+ ...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
4484
+ reclaimCount: reclaims,
4485
+ outcome: entry.status
4486
+ }
4487
+ });
4488
+ } catch {
4489
+ }
4490
+ return entry;
4491
+ });
4492
+ }
4445
4493
  function updateSessionTaskStatus(meshId, sessionId, status, opts) {
4446
4494
  return withQueueLock(meshId, () => {
4447
4495
  const store = MeshRuntimeStore.getInstance();
@@ -4555,7 +4603,7 @@ function recordMeshToolCall(opts) {
4555
4603
  return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
4556
4604
  }
4557
4605
  }
4558
- var import_crypto5, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS;
4606
+ var import_crypto5, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS;
4559
4607
  var init_mesh_work_queue = __esm({
4560
4608
  "src/mesh/mesh-work-queue.ts"() {
4561
4609
  "use strict";
@@ -4565,6 +4613,7 @@ var init_mesh_work_queue = __esm({
4565
4613
  init_mesh_runtime_store();
4566
4614
  init_mesh_config();
4567
4615
  init_logger();
4616
+ init_mesh_ledger();
4568
4617
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
4569
4618
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
4570
4619
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -4617,6 +4666,7 @@ var init_mesh_work_queue = __esm({
4617
4666
  ]);
4618
4667
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
4619
4668
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
4669
+ MAX_STRANDED_RECLAIMS = 3;
4620
4670
  }
4621
4671
  });
4622
4672
 
@@ -5502,6 +5552,22 @@ var init_mesh_runtime_store = __esm({
5502
5552
  updatedAt: r.updated_at
5503
5553
  }));
5504
5554
  }
5555
+ /**
5556
+ * Bug B watchdog support: true when at least one delivery record for the task has
5557
+ * reached a confirmed-handed-off status (delivered / acked / completed). The
5558
+ * assigned-stranded watchdog uses this to distinguish a dispatch that was never
5559
+ * confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
5560
+ * in-flight or completion-lost task, which is PHASE 4's responsibility, not this
5561
+ * watchdog's). Indexed by (mesh_id, task_id).
5562
+ */
5563
+ taskHasConfirmedDelivery(meshId, taskId) {
5564
+ const row = this.db.prepare(`
5565
+ SELECT 1 FROM mesh_session_delivery
5566
+ WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
5567
+ LIMIT 1
5568
+ `).get(meshId, taskId);
5569
+ return !!row;
5570
+ }
5505
5571
  expireStaleSessionDeliveries(meshId) {
5506
5572
  const now = (/* @__PURE__ */ new Date()).toISOString();
5507
5573
  this.db.prepare(`
@@ -7925,6 +7991,35 @@ function meshNodeIdMatches(node, candidateId) {
7925
7991
  if (!trimmed) return false;
7926
7992
  return normalizeMeshNodeId(node) === trimmed;
7927
7993
  }
7994
+ function machineCoreFromDaemonId(id) {
7995
+ const trimmed = readString5(id);
7996
+ if (!trimmed) return void 0;
7997
+ for (const prefix of DAEMON_ID_PREFIXES) {
7998
+ if (trimmed.startsWith(prefix)) {
7999
+ const core = trimmed.slice(prefix.length).trim();
8000
+ return core || void 0;
8001
+ }
8002
+ }
8003
+ return trimmed;
8004
+ }
8005
+ function expandDaemonIdForms(ids) {
8006
+ const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
8007
+ const out = [];
8008
+ const seen = /* @__PURE__ */ new Set();
8009
+ const add = (value) => {
8010
+ if (!value || seen.has(value)) return;
8011
+ seen.add(value);
8012
+ out.push(value);
8013
+ };
8014
+ for (const raw of list) add(readString5(raw));
8015
+ for (const raw of list) {
8016
+ const core = machineCoreFromDaemonId(readString5(raw));
8017
+ if (!core || !core.startsWith("mach_")) continue;
8018
+ add(core);
8019
+ for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
8020
+ }
8021
+ return out;
8022
+ }
7928
8023
  function summarizeGitShape(status) {
7929
8024
  const record = readRecord3(status);
7930
8025
  if (!Object.keys(record).length) return null;
@@ -7959,9 +8054,11 @@ function summarizeGitShape(status) {
7959
8054
  submodules
7960
8055
  };
7961
8056
  }
8057
+ var DAEMON_ID_PREFIXES;
7962
8058
  var init_dist = __esm({
7963
8059
  "../mesh-shared/dist/index.mjs"() {
7964
8060
  "use strict";
8061
+ DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
7965
8062
  }
7966
8063
  });
7967
8064
 
@@ -8618,17 +8715,7 @@ var init_mesh_events_utils = __esm({
8618
8715
 
8619
8716
  // src/mesh/mesh-events-pending.ts
8620
8717
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
8621
- const raw = Array.isArray(coordinatorDaemonId) ? coordinatorDaemonId : coordinatorDaemonId != null ? [coordinatorDaemonId] : [];
8622
- const seen = /* @__PURE__ */ new Set();
8623
- const out = [];
8624
- for (const id of raw) {
8625
- if (typeof id !== "string") continue;
8626
- const trimmed = id.trim();
8627
- if (!trimmed || seen.has(trimmed)) continue;
8628
- seen.add(trimmed);
8629
- out.push(trimmed);
8630
- }
8631
- return out;
8718
+ return expandDaemonIdForms(coordinatorDaemonId);
8632
8719
  }
8633
8720
  function readRefineJobId2(event) {
8634
8721
  const metadata = readRecord4(event.metadataEvent) || event;
@@ -9018,6 +9105,7 @@ var init_mesh_events_pending = __esm({
9018
9105
  init_mesh_ledger();
9019
9106
  init_mesh_runtime_store();
9020
9107
  init_mesh_events_utils();
9108
+ init_dist();
9021
9109
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9022
9110
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9023
9111
  MAX_PENDING_EVENTS_KEEP = 50;
@@ -12469,12 +12557,9 @@ var init_snapshot = __esm({
12469
12557
 
12470
12558
  // src/mesh/mesh-events-coordinator.ts
12471
12559
  function resolveCoordinatorDrainDaemonIds(components) {
12472
- const ids = /* @__PURE__ */ new Set();
12473
12560
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
12474
- if (statusInstanceId) ids.add(statusInstanceId);
12475
12561
  const machineId = readNonEmptyString2(loadConfig().machineId);
12476
- if (machineId) ids.add(machineId);
12477
- return [...ids];
12562
+ return expandDaemonIdForms([statusInstanceId, machineId]);
12478
12563
  }
12479
12564
  function getCachedMeshByWorkspace(workspace) {
12480
12565
  const now = Date.now();
@@ -12625,6 +12710,55 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
12625
12710
  return void 0;
12626
12711
  }
12627
12712
  }
12713
+ function deliverTaskToSession(dispatchThunk, ctx) {
12714
+ const delivery = createSessionDelivery({
12715
+ meshId: ctx.meshId,
12716
+ nodeId: ctx.nodeId,
12717
+ sessionId: ctx.sessionId,
12718
+ providerType: ctx.providerType,
12719
+ taskId: ctx.task.id,
12720
+ kind: "task",
12721
+ message: ctx.task.message,
12722
+ status: "delivering",
12723
+ ...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
12724
+ ...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
12725
+ });
12726
+ let dispatchPromise;
12727
+ try {
12728
+ dispatchPromise = Promise.resolve(dispatchThunk());
12729
+ } catch (e) {
12730
+ dispatchPromise = Promise.reject(e);
12731
+ }
12732
+ let timer;
12733
+ const guarded = Promise.race([
12734
+ dispatchPromise,
12735
+ new Promise((_, reject) => {
12736
+ timer = setTimeout(
12737
+ () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
12738
+ DISPATCH_CONFIRM_TIMEOUT_MS
12739
+ );
12740
+ if (typeof timer?.unref === "function") timer.unref();
12741
+ })
12742
+ ]);
12743
+ guarded.then(() => {
12744
+ if (timer) clearTimeout(timer);
12745
+ updateSessionDeliveryStatus(delivery.id, "delivered");
12746
+ }).catch((e) => {
12747
+ if (timer) clearTimeout(timer);
12748
+ LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
12749
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12750
+ updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
12751
+ try {
12752
+ appendLedgerEntry(ctx.meshId, {
12753
+ kind: "dispatch_failed",
12754
+ nodeId: ctx.nodeId,
12755
+ sessionId: ctx.sessionId,
12756
+ payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
12757
+ });
12758
+ } catch {
12759
+ }
12760
+ });
12761
+ }
12628
12762
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
12629
12763
  const mesh = getMeshWithCache(components, meshId);
12630
12764
  const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
@@ -12643,46 +12777,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12643
12777
  if (!isLocalNode) {
12644
12778
  const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
12645
12779
  const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
12646
- const delivery2 = createSessionDelivery({
12647
- meshId,
12648
- nodeId,
12649
- sessionId,
12650
- providerType,
12651
- taskId: task.id,
12652
- kind: "task",
12653
- message: task.message,
12654
- status: "delivering",
12655
- ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12656
- ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12657
- });
12658
- components.dispatchMeshCommand(node.daemonId, "agent_command", {
12659
- targetSessionId: sessionId,
12660
- cliType: providerType,
12661
- action: "send_chat",
12662
- message: task.message,
12663
- meshContext: {
12780
+ const dispatchMeshCommand = components.dispatchMeshCommand;
12781
+ const remoteDaemonId = node.daemonId;
12782
+ deliverTaskToSession(
12783
+ () => dispatchMeshCommand(remoteDaemonId, "agent_command", {
12784
+ targetSessionId: sessionId,
12785
+ cliType: providerType,
12786
+ action: "send_chat",
12787
+ message: task.message,
12788
+ meshContext: {
12789
+ meshId,
12790
+ nodeId,
12791
+ taskId: task.id,
12792
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12793
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12794
+ }
12795
+ }),
12796
+ {
12664
12797
  meshId,
12665
12798
  nodeId,
12666
- taskId: task.id,
12667
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12668
- ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12669
- }
12670
- }).then(() => {
12671
- updateSessionDeliveryStatus(delivery2.id, "delivered");
12672
- }).catch((e) => {
12673
- LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
12674
- updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
12675
- updateTaskStatus(meshId, task.id, "pending");
12676
- try {
12677
- appendLedgerEntry(meshId, {
12678
- kind: "dispatch_failed",
12679
- nodeId,
12680
- sessionId,
12681
- payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
12682
- });
12683
- } catch {
12799
+ sessionId,
12800
+ providerType,
12801
+ task,
12802
+ transport: "remote",
12803
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12804
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12684
12805
  }
12685
- });
12806
+ );
12686
12807
  return true;
12687
12808
  }
12688
12809
  }
@@ -12704,39 +12825,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12704
12825
  }
12705
12826
  } catch {
12706
12827
  }
12707
- const delivery = createSessionDelivery({
12708
- meshId,
12709
- nodeId,
12710
- sessionId,
12711
- providerType,
12712
- taskId: task.id,
12713
- kind: "task",
12714
- message: task.message,
12715
- status: "delivering",
12716
- ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12717
- ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12718
- });
12719
- components.cliManager.handleCliCommand("agent_command", {
12720
- targetSessionId: sessionId,
12721
- cliType: providerType,
12722
- action: "send_chat",
12723
- message: task.message
12724
- }).then(() => {
12725
- updateSessionDeliveryStatus(delivery.id, "delivered");
12726
- }).catch((e) => {
12727
- LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
12728
- updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12729
- updateTaskStatus(meshId, task.id, "pending");
12730
- try {
12731
- appendLedgerEntry(meshId, {
12732
- kind: "dispatch_failed",
12733
- nodeId,
12734
- sessionId,
12735
- payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
12736
- });
12737
- } catch {
12828
+ deliverTaskToSession(
12829
+ () => components.cliManager.handleCliCommand("agent_command", {
12830
+ targetSessionId: sessionId,
12831
+ cliType: providerType,
12832
+ action: "send_chat",
12833
+ message: task.message
12834
+ }),
12835
+ {
12836
+ meshId,
12837
+ nodeId,
12838
+ sessionId,
12839
+ providerType,
12840
+ task,
12841
+ transport: "local",
12842
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12843
+ ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12738
12844
  }
12739
- });
12845
+ );
12740
12846
  return true;
12741
12847
  }
12742
12848
  function sweepExpiredCooldowns() {
@@ -13001,7 +13107,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
13001
13107
  }
13002
13108
  }
13003
13109
  const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
13004
- if (task.targetNodeId && readMeshNodeId(node) !== task.targetNodeId) return false;
13110
+ if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
13005
13111
  if (task.requiredTags?.length) {
13006
13112
  const priorities = normalizeProviderPriority(node?.policy);
13007
13113
  const providerCandidates = priorities.length ? priorities : [void 0];
@@ -13012,7 +13118,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
13012
13118
  return true;
13013
13119
  }) : [];
13014
13120
  if (!candidateNodes.length) {
13015
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
13121
+ const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
13122
+ markAutoLaunch(meshId, task.id, {
13123
+ status: "skipped",
13124
+ reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
13125
+ nodeId: task.targetNodeId
13126
+ });
13016
13127
  continue;
13017
13128
  }
13018
13129
  const strategy = resolveSchedulingStrategy(mesh);
@@ -13963,7 +14074,7 @@ function setupMeshEventForwarding(components) {
13963
14074
  });
13964
14075
  });
13965
14076
  }
13966
- var import_fs13, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
14077
+ var import_fs13, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
13967
14078
  var init_mesh_events_coordinator = __esm({
13968
14079
  "src/mesh/mesh-events-coordinator.ts"() {
13969
14080
  "use strict";
@@ -13992,6 +14103,7 @@ var init_mesh_events_coordinator = __esm({
13992
14103
  idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
13993
14104
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
13994
14105
  RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
14106
+ DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
13995
14107
  autoLaunchInProgress = /* @__PURE__ */ new Set();
13996
14108
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
13997
14109
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -14047,12 +14159,9 @@ function resolveReconcileIntervalMs() {
14047
14159
  return DEFAULT_RECONCILE_INTERVAL_MS;
14048
14160
  }
14049
14161
  function resolveCoordinatorDaemonIds(components) {
14050
- const ids = /* @__PURE__ */ new Set();
14051
14162
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
14052
- if (statusInstanceId) ids.add(statusInstanceId);
14053
14163
  const machineId = readNonEmptyString2(loadConfig().machineId);
14054
- if (machineId) ids.add(machineId);
14055
- return [...ids];
14164
+ return expandDaemonIdForms([statusInstanceId, machineId]);
14056
14165
  }
14057
14166
  function daemonHostsMesh(mesh, daemonIds) {
14058
14167
  const host = mesh.meshHost;
@@ -14136,6 +14245,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
14136
14245
  }
14137
14246
  }
14138
14247
  }
14248
+ function recoverStrandedAssignedDispatches(meshId, store) {
14249
+ const assigned = getQueue(meshId, { status: ["assigned"] });
14250
+ if (!assigned.length) return;
14251
+ const nowMs = Date.now();
14252
+ for (const row of assigned) {
14253
+ const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
14254
+ if (!Number.isFinite(dispatchedAtMs)) continue;
14255
+ if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
14256
+ if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
14257
+ const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
14258
+ reason: "assigned_stranded_dispatch_unconfirmed",
14259
+ ageMs: nowMs - dispatchedAtMs
14260
+ });
14261
+ if (reclaimed) {
14262
+ 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})`);
14263
+ }
14264
+ }
14265
+ }
14139
14266
  async function runMeshReconcileTick(components) {
14140
14267
  const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
14141
14268
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -14165,6 +14292,17 @@ async function runMeshReconcileTick(components) {
14165
14292
  }
14166
14293
  }
14167
14294
  }
14295
+ if (store) {
14296
+ for (const mesh of listMeshes()) {
14297
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14298
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
14299
+ try {
14300
+ recoverStrandedAssignedDispatches(mesh.id, store);
14301
+ } catch (e) {
14302
+ LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
14303
+ }
14304
+ }
14305
+ }
14168
14306
  for (const mesh of listMeshes()) {
14169
14307
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14170
14308
  if (!daemonHostsMesh(mesh, selfIds)) continue;
@@ -14552,7 +14690,7 @@ function setupMeshReconcileLoop(components) {
14552
14690
  }
14553
14691
  };
14554
14692
  }
14555
- var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
14693
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
14556
14694
  var init_mesh_reconcile_loop = __esm({
14557
14695
  "src/mesh/mesh-reconcile-loop.ts"() {
14558
14696
  "use strict";
@@ -14565,6 +14703,7 @@ var init_mesh_reconcile_loop = __esm({
14565
14703
  init_mesh_events_coordinator();
14566
14704
  init_mesh_unresolved_forward_outbox();
14567
14705
  init_mesh_events_utils();
14706
+ init_dist();
14568
14707
  init_mesh_work_queue();
14569
14708
  init_mesh_ledger();
14570
14709
  init_mesh_active_work();
@@ -14573,6 +14712,7 @@ var init_mesh_reconcile_loop = __esm({
14573
14712
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
14574
14713
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
14575
14714
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
14715
+ ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
14576
14716
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
14577
14717
  }
14578
14718
  });
@@ -14605,6 +14745,84 @@ var init_mesh_events = __esm({
14605
14745
  }
14606
14746
  });
14607
14747
 
14748
+ // src/providers/approval-utils.ts
14749
+ function normalizeApprovalLabel(value) {
14750
+ return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
14751
+ }
14752
+ function isNegativeApprovalLabel(value) {
14753
+ const label = normalizeApprovalLabel(value);
14754
+ return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
14755
+ }
14756
+ function hasNegativeApprovalOption(buttons) {
14757
+ return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
14758
+ }
14759
+ function getApprovalPositiveHints(provider) {
14760
+ const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
14761
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
14762
+ }
14763
+ function pickApprovalButton(buttons, provider) {
14764
+ const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
14765
+ if (labels.length === 0) {
14766
+ return { index: -1, label: "" };
14767
+ }
14768
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
14769
+ const hints = getApprovalPositiveHints(provider);
14770
+ for (const hint of hints) {
14771
+ const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
14772
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
14773
+ const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
14774
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
14775
+ const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
14776
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
14777
+ }
14778
+ return { index: -1, label: "" };
14779
+ }
14780
+ function pickAutoApprovalButton(buttons) {
14781
+ const labels = (buttons || []).map((button) => String(button || "").trim());
14782
+ const index = labels.findIndex(Boolean);
14783
+ return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
14784
+ }
14785
+ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
14786
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
14787
+ const cleanMessage = String(modalMessage || "").trim();
14788
+ if (cleanMessage) lines.push(cleanMessage);
14789
+ return lines.join("\n");
14790
+ }
14791
+ function looksLikeActiveApprovalPromptText(content) {
14792
+ const text = content.trim();
14793
+ if (!text || text.length > 2e3) return false;
14794
+ 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);
14795
+ const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
14796
+ if (hasApprovalQuestion && hasNumberedChoices) return true;
14797
+ const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
14798
+ const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
14799
+ const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
14800
+ if (hasDontAskAgain && hasNoOption) return true;
14801
+ if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
14802
+ return false;
14803
+ }
14804
+ var DEFAULT_APPROVAL_POSITIVE_HINTS;
14805
+ var init_approval_utils = __esm({
14806
+ "src/providers/approval-utils.ts"() {
14807
+ "use strict";
14808
+ DEFAULT_APPROVAL_POSITIVE_HINTS = [
14809
+ "yes",
14810
+ "allow once",
14811
+ "approve",
14812
+ "accept",
14813
+ "continue",
14814
+ "run",
14815
+ "proceed",
14816
+ "confirm",
14817
+ "save",
14818
+ "ok",
14819
+ "trust",
14820
+ "allow",
14821
+ "always allow"
14822
+ ];
14823
+ }
14824
+ });
14825
+
14608
14826
  // src/logging/debug-config.ts
14609
14827
  function normalizeCategories(categories) {
14610
14828
  if (!Array.isArray(categories)) return [];
@@ -15591,6 +15809,27 @@ function compileSettledPromptMatchers(spec) {
15591
15809
  });
15592
15810
  return { prompt, footers };
15593
15811
  }
15812
+ function extractButtonLabels(spec, text) {
15813
+ if (!text) return [];
15814
+ const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
15815
+ const buttonRe = compile2(spec.buttonPattern, flags);
15816
+ const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
15817
+ const out = [];
15818
+ for (const line of text.split("\n")) {
15819
+ buttonRe.lastIndex = 0;
15820
+ const m = buttonRe.exec(line);
15821
+ if (!m) continue;
15822
+ const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
15823
+ if (captured && captured.trim()) out.push(captured.trim());
15824
+ }
15825
+ return out;
15826
+ }
15827
+ function buttonBlockApprovalCue(spec, text) {
15828
+ const labels = extractButtonLabels(spec, text);
15829
+ if (labels.length < 2) return false;
15830
+ if (pickApprovalButton(labels).index < 0) return false;
15831
+ return hasNegativeApprovalOption(labels);
15832
+ }
15594
15833
  function modalMatches(spec, input) {
15595
15834
  const text = input.screenText ?? "";
15596
15835
  const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
@@ -15599,6 +15838,7 @@ function modalMatches(spec, input) {
15599
15838
  const re = compile2(variant.regex, variant.flags ?? "i");
15600
15839
  if (re.test(text)) return true;
15601
15840
  }
15841
+ if (buttonBlockApprovalCue(spec, text)) return true;
15602
15842
  return false;
15603
15843
  }
15604
15844
  function evaluateGroup(group, spec, input, compiled) {
@@ -15653,6 +15893,7 @@ var init_detect_status = __esm({
15653
15893
  "src/providers/sdk/v1/builders/cli/detect-status.ts"() {
15654
15894
  "use strict";
15655
15895
  init_visible_region();
15896
+ init_approval_utils();
15656
15897
  DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
15657
15898
  }
15658
15899
  });
@@ -26111,76 +26352,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
26111
26352
  return normalized;
26112
26353
  }
26113
26354
 
26114
- // src/providers/approval-utils.ts
26115
- var DEFAULT_APPROVAL_POSITIVE_HINTS = [
26116
- "yes",
26117
- "allow once",
26118
- "approve",
26119
- "accept",
26120
- "continue",
26121
- "run",
26122
- "proceed",
26123
- "confirm",
26124
- "save",
26125
- "ok",
26126
- "trust",
26127
- "allow",
26128
- "always allow"
26129
- ];
26130
- function normalizeApprovalLabel(value) {
26131
- return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
26132
- }
26133
- function isNegativeApprovalLabel(value) {
26134
- const label = normalizeApprovalLabel(value);
26135
- return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
26136
- }
26137
- function getApprovalPositiveHints(provider) {
26138
- const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
26139
- return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
26140
- }
26141
- function pickApprovalButton(buttons, provider) {
26142
- const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
26143
- if (labels.length === 0) {
26144
- return { index: -1, label: "" };
26145
- }
26146
- const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
26147
- const hints = getApprovalPositiveHints(provider);
26148
- for (const hint of hints) {
26149
- const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
26150
- if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
26151
- const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
26152
- if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
26153
- const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
26154
- if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
26155
- }
26156
- return { index: -1, label: "" };
26157
- }
26158
- function pickAutoApprovalButton(buttons) {
26159
- const labels = (buttons || []).map((button) => String(button || "").trim());
26160
- const index = labels.findIndex(Boolean);
26161
- return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
26162
- }
26163
- function formatAutoApprovalMessage(modalMessage, buttonLabel) {
26164
- const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
26165
- const cleanMessage = String(modalMessage || "").trim();
26166
- if (cleanMessage) lines.push(cleanMessage);
26167
- return lines.join("\n");
26168
- }
26169
- function looksLikeActiveApprovalPromptText(content) {
26170
- const text = content.trim();
26171
- if (!text || text.length > 2e3) return false;
26172
- 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);
26173
- const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
26174
- if (hasApprovalQuestion && hasNumberedChoices) return true;
26175
- const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
26176
- const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
26177
- const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
26178
- if (hasDontAskAgain && hasNoOption) return true;
26179
- if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
26180
- return false;
26181
- }
26182
-
26183
26355
  // src/providers/ide-provider-instance.ts
26356
+ init_approval_utils();
26184
26357
  init_provider_patch_state();
26185
26358
  init_chat_message_normalization();
26186
26359
  init_open_panel_support();
@@ -27264,6 +27437,7 @@ var path16 = __toESM(require("path"));
27264
27437
  var import_node_crypto3 = require("crypto");
27265
27438
  init_contracts();
27266
27439
  init_provider_input_support();
27440
+ init_approval_utils();
27267
27441
  init_coordinator_registry();
27268
27442
  init_logger();
27269
27443
 
@@ -35252,6 +35426,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
35252
35426
  // src/providers/cli-provider-instance.ts
35253
35427
  init_logger();
35254
35428
  init_control_effects();
35429
+ init_approval_utils();
35255
35430
  init_provider_patch_state();
35256
35431
 
35257
35432
  // src/providers/provider-session-id.ts
@@ -35513,6 +35688,20 @@ var CliProviderInstance = class _CliProviderInstance {
35513
35688
  * keystroke until the modal *content* has settled.
35514
35689
  */
35515
35690
  static AUTO_APPROVE_SETTLE_MS = 600;
35691
+ /**
35692
+ * Busy-side hysteresis for the settle gate. A momentary `generating` flip
35693
+ * while the SAME approval modal's button block is still on screen (its
35694
+ * question line scrolled out of the captured frame, only the buttons + a
35695
+ * residual `esc to interrupt` spinner remain) briefly reports
35696
+ * status!=waiting_approval. Without hysteresis that flip wipes the settle
35697
+ * clock, and the modal→generating→modal flap restarts the 600ms window
35698
+ * every time so auto-approve never fires. We keep the in-progress settle
35699
+ * gate warm across an inactive blip up to this bound; only once the modal
35700
+ * has genuinely stayed gone this long (a real resolution → idle) is the
35701
+ * gate cleared. Bounded so a genuinely new, later approval still re-settles
35702
+ * from scratch rather than firing on a stale timestamp.
35703
+ */
35704
+ static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
35516
35705
  adapter;
35517
35706
  context = null;
35518
35707
  events = [];
@@ -35534,6 +35723,10 @@ var CliProviderInstance = class _CliProviderInstance {
35534
35723
  pendingAutoApprovalSignature = "";
35535
35724
  pendingAutoApprovalSince = 0;
35536
35725
  autoApproveSettleTimer = null;
35726
+ // Wall-clock when auto-approve first observed status!=waiting_approval while
35727
+ // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35728
+ // brief generating flip does not immediately wipe the settle clock.
35729
+ autoApproveInactiveSince = 0;
35537
35730
  controlValues = {};
35538
35731
  summaryMetadata = void 0;
35539
35732
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -36354,14 +36547,28 @@ var CliProviderInstance = class _CliProviderInstance {
36354
36547
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
36355
36548
  if (!autoApproveActive) {
36356
36549
  this.lastAutoApprovalSignature = "";
36550
+ if (this.pendingAutoApprovalSince) {
36551
+ if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
36552
+ const goneForMs = now - this.autoApproveInactiveSince;
36553
+ if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
36554
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36555
+ this.autoApproveSettleTimer = setTimeout(() => {
36556
+ this.autoApproveSettleTimer = null;
36557
+ this.recheckAutoApproveSettled();
36558
+ }, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
36559
+ return autoApproveActive;
36560
+ }
36561
+ }
36357
36562
  this.pendingAutoApprovalSignature = "";
36358
36563
  this.pendingAutoApprovalSince = 0;
36564
+ this.autoApproveInactiveSince = 0;
36359
36565
  if (this.autoApproveSettleTimer) {
36360
36566
  clearTimeout(this.autoApproveSettleTimer);
36361
36567
  this.autoApproveSettleTimer = null;
36362
36568
  }
36363
36569
  return autoApproveActive;
36364
36570
  }
36571
+ this.autoApproveInactiveSince = 0;
36365
36572
  const modal = adapterStatus.activeModal;
36366
36573
  const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
36367
36574
  if (!modal || buttons.length === 0) {
@@ -36371,18 +36578,18 @@ var CliProviderInstance = class _CliProviderInstance {
36371
36578
  if (buttonIndex < 0) {
36372
36579
  return autoApproveActive;
36373
36580
  }
36374
- const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36375
- const signature = [
36376
- approvalEntrySeq,
36581
+ const modalSignature = [
36377
36582
  typeof modal?.message === "string" ? modal.message.trim() : "",
36378
36583
  buttons.join("|"),
36379
36584
  buttonIndex
36380
36585
  ].join("::");
36381
- if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
36586
+ const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36587
+ const busySignature = `${approvalEntrySeq}::${modalSignature}`;
36588
+ if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
36382
36589
  return autoApproveActive;
36383
36590
  }
36384
- if (signature !== this.pendingAutoApprovalSignature) {
36385
- this.pendingAutoApprovalSignature = signature;
36591
+ if (modalSignature !== this.pendingAutoApprovalSignature) {
36592
+ this.pendingAutoApprovalSignature = modalSignature;
36386
36593
  this.pendingAutoApprovalSince = now;
36387
36594
  }
36388
36595
  const settledForMs = now - this.pendingAutoApprovalSince;
@@ -36399,9 +36606,10 @@ var CliProviderInstance = class _CliProviderInstance {
36399
36606
  this.autoApproveSettleTimer = null;
36400
36607
  }
36401
36608
  this.autoApproveBusy = true;
36402
- this.lastAutoApprovalSignature = signature;
36609
+ this.lastAutoApprovalSignature = busySignature;
36403
36610
  this.pendingAutoApprovalSignature = "";
36404
36611
  this.pendingAutoApprovalSince = 0;
36612
+ this.autoApproveInactiveSince = 0;
36405
36613
  if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
36406
36614
  this.autoApproveBusyTimer = setTimeout(() => {
36407
36615
  this.autoApproveBusy = false;
@@ -43288,7 +43496,8 @@ init_logger();
43288
43496
  var fs23 = __toESM(require("fs"));
43289
43497
  var path35 = __toESM(require("path"));
43290
43498
  var os26 = __toESM(require("os"));
43291
- 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");
43499
+ 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");
43500
+ var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
43292
43501
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
43293
43502
  var MAX_DAYS = 7;
43294
43503
  try {
@@ -44143,13 +44352,14 @@ async function waitForPidExit(pid, timeoutMs) {
44143
44352
  }
44144
44353
  }
44145
44354
  }
44146
- function stopSessionHostProcesses(appName) {
44355
+ async function stopSessionHostProcesses(appName) {
44147
44356
  const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
44357
+ let killedPid = null;
44148
44358
  try {
44149
44359
  if (fs25.existsSync(pidFile)) {
44150
44360
  const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
44151
44361
  if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
44152
- killPid(pid);
44362
+ if (killPid(pid)) killedPid = pid;
44153
44363
  }
44154
44364
  }
44155
44365
  } catch {
@@ -44159,6 +44369,15 @@ function stopSessionHostProcesses(appName) {
44159
44369
  } catch {
44160
44370
  }
44161
44371
  }
44372
+ if (killedPid !== null) {
44373
+ await waitForPidExit(killedPid, 15e3);
44374
+ }
44375
+ }
44376
+ function isRetriableInstallLockError(error) {
44377
+ const code = error?.code;
44378
+ if (code === "EBUSY" || code === "EPERM") return true;
44379
+ const text = `${error?.message || ""} ${error?.stderr || ""}`;
44380
+ return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
44162
44381
  }
44163
44382
  function removeDaemonPidFile() {
44164
44383
  const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
@@ -44238,22 +44457,37 @@ async function runDaemonUpgradeHelper(payload) {
44238
44457
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
44239
44458
  await waitForPidExit(payload.parentPid, 15e3);
44240
44459
  }
44241
- stopSessionHostProcesses(sessionHostAppName);
44460
+ await stopSessionHostProcesses(sessionHostAppName);
44242
44461
  removeDaemonPidFile();
44243
44462
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
44244
44463
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
44245
44464
  appendUpgradeLog(`Installing ${spec}`);
44246
- const installOutput = (0, import_child_process8.execFileSync)(
44247
- installCommand.command,
44248
- installCommand.args,
44249
- {
44250
- encoding: "utf8",
44251
- stdio: "pipe",
44252
- maxBuffer: 20 * 1024 * 1024,
44253
- env: buildInstallEnvWithNodeOnPath(),
44254
- ...installCommand.execOptions
44465
+ const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
44466
+ let installOutput = "";
44467
+ for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
44468
+ try {
44469
+ installOutput = String((0, import_child_process8.execFileSync)(
44470
+ installCommand.command,
44471
+ installCommand.args,
44472
+ {
44473
+ encoding: "utf8",
44474
+ stdio: "pipe",
44475
+ maxBuffer: 20 * 1024 * 1024,
44476
+ env: buildInstallEnvWithNodeOnPath(),
44477
+ ...installCommand.execOptions
44478
+ }
44479
+ ));
44480
+ break;
44481
+ } catch (error) {
44482
+ if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
44483
+ appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
44484
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
44485
+ await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
44486
+ continue;
44487
+ }
44488
+ throw error;
44255
44489
  }
44256
- );
44490
+ }
44257
44491
  if (installOutput.trim()) {
44258
44492
  appendUpgradeLog(installOutput.trim());
44259
44493
  }
@@ -53015,6 +53249,7 @@ var DaemonAgentStreamManager = class {
53015
53249
 
53016
53250
  // src/agent-stream/poller.ts
53017
53251
  init_logger();
53252
+ init_approval_utils();
53018
53253
  init_chat_message_normalization();
53019
53254
  var AgentStreamPoller = class {
53020
53255
  deps;