@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.mjs CHANGED
@@ -311,10 +311,10 @@ function readInjected(value) {
311
311
  }
312
312
  function getDaemonBuildInfo() {
313
313
  if (cached) return cached;
314
- const commit = readInjected(true ? "dad22ef2758130d10a3d55e3c419f0095d5e44b7" : void 0) ?? "unknown";
315
- const commitShort = readInjected(true ? "dad22ef2" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
- const version = readInjected(true ? "0.9.82-rc.351" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
- const builtAt = readInjected(true ? "2026-06-22T04:04:59.936Z" : void 0);
314
+ const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
315
+ const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
+ const version = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
+ const builtAt = readInjected(true ? "2026-06-22T07:17:05.715Z" : void 0);
318
318
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
319
319
  return cached;
320
320
  }
@@ -3193,7 +3193,7 @@ function installGlobalInterceptor() {
3193
3193
  function getLogPath() {
3194
3194
  return currentLogFile;
3195
3195
  }
3196
- var 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;
3196
+ var 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;
3197
3197
  var init_logger = __esm({
3198
3198
  "src/logging/logger.ts"() {
3199
3199
  "use strict";
@@ -3201,7 +3201,8 @@ var init_logger = __esm({
3201
3201
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
3202
3202
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
3203
3203
  currentLevel = "info";
3204
- 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");
3204
+ ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
3205
+ LOG_DIR = path9.join(ADHDEV_HOME, "logs");
3205
3206
  MAX_LOG_SIZE = 5 * 1024 * 1024;
3206
3207
  MAX_LOG_DAYS = 7;
3207
3208
  try {
@@ -4001,6 +4002,7 @@ __export(mesh_work_queue_exports, {
4001
4002
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
4002
4003
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
4003
4004
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
4005
+ reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
4004
4006
  recordDirectDispatchTask: () => recordDirectDispatchTask,
4005
4007
  recordMeshToolCall: () => recordMeshToolCall,
4006
4008
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
@@ -4437,6 +4439,52 @@ function requeueTask(meshId, taskId, opts) {
4437
4439
  return entry;
4438
4440
  });
4439
4441
  }
4442
+ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
4443
+ requireMeshHostQueueOwner(opts);
4444
+ return withQueueLock(meshId, () => {
4445
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
4446
+ if (!entry) return null;
4447
+ if (entry.status !== "assigned") return null;
4448
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4449
+ const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
4450
+ const reclaims = (entry.strandedReclaimCount || 0) + 1;
4451
+ const prevNode = entry.assignedNodeId;
4452
+ const prevSession = entry.assignedSessionId;
4453
+ delete entry.assignedNodeId;
4454
+ delete entry.assignedSessionId;
4455
+ delete entry.assignedProviderType;
4456
+ delete entry.dispatchTimestamp;
4457
+ entry.strandedReclaimCount = reclaims;
4458
+ entry.updatedAt = now;
4459
+ if (reclaims > MAX_STRANDED_RECLAIMS) {
4460
+ entry.status = "failed";
4461
+ entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
4462
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4463
+ propagateDependencyFailure(meshId, taskId);
4464
+ } else {
4465
+ entry.status = "pending";
4466
+ entry.requeuedAt = now;
4467
+ entry.requeueReason = reason;
4468
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4469
+ }
4470
+ try {
4471
+ appendLedgerEntry(meshId, {
4472
+ kind: "task_reclaimed",
4473
+ nodeId: prevNode,
4474
+ sessionId: prevSession,
4475
+ payload: {
4476
+ taskId,
4477
+ reason,
4478
+ ...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
4479
+ reclaimCount: reclaims,
4480
+ outcome: entry.status
4481
+ }
4482
+ });
4483
+ } catch {
4484
+ }
4485
+ return entry;
4486
+ });
4487
+ }
4440
4488
  function updateSessionTaskStatus(meshId, sessionId, status, opts) {
4441
4489
  return withQueueLock(meshId, () => {
4442
4490
  const store = MeshRuntimeStore.getInstance();
@@ -4550,7 +4598,7 @@ function recordMeshToolCall(opts) {
4550
4598
  return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
4551
4599
  }
4552
4600
  }
4553
- var 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;
4601
+ var 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;
4554
4602
  var init_mesh_work_queue = __esm({
4555
4603
  "src/mesh/mesh-work-queue.ts"() {
4556
4604
  "use strict";
@@ -4559,6 +4607,7 @@ var init_mesh_work_queue = __esm({
4559
4607
  init_mesh_runtime_store();
4560
4608
  init_mesh_config();
4561
4609
  init_logger();
4610
+ init_mesh_ledger();
4562
4611
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
4563
4612
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
4564
4613
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -4611,6 +4660,7 @@ var init_mesh_work_queue = __esm({
4611
4660
  ]);
4612
4661
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
4613
4662
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
4663
+ MAX_STRANDED_RECLAIMS = 3;
4614
4664
  }
4615
4665
  });
4616
4666
 
@@ -5496,6 +5546,22 @@ var init_mesh_runtime_store = __esm({
5496
5546
  updatedAt: r.updated_at
5497
5547
  }));
5498
5548
  }
5549
+ /**
5550
+ * Bug B watchdog support: true when at least one delivery record for the task has
5551
+ * reached a confirmed-handed-off status (delivered / acked / completed). The
5552
+ * assigned-stranded watchdog uses this to distinguish a dispatch that was never
5553
+ * confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
5554
+ * in-flight or completion-lost task, which is PHASE 4's responsibility, not this
5555
+ * watchdog's). Indexed by (mesh_id, task_id).
5556
+ */
5557
+ taskHasConfirmedDelivery(meshId, taskId) {
5558
+ const row = this.db.prepare(`
5559
+ SELECT 1 FROM mesh_session_delivery
5560
+ WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
5561
+ LIMIT 1
5562
+ `).get(meshId, taskId);
5563
+ return !!row;
5564
+ }
5499
5565
  expireStaleSessionDeliveries(meshId) {
5500
5566
  const now = (/* @__PURE__ */ new Date()).toISOString();
5501
5567
  this.db.prepare(`
@@ -7919,6 +7985,35 @@ function meshNodeIdMatches(node, candidateId) {
7919
7985
  if (!trimmed) return false;
7920
7986
  return normalizeMeshNodeId(node) === trimmed;
7921
7987
  }
7988
+ function machineCoreFromDaemonId(id) {
7989
+ const trimmed = readString5(id);
7990
+ if (!trimmed) return void 0;
7991
+ for (const prefix of DAEMON_ID_PREFIXES) {
7992
+ if (trimmed.startsWith(prefix)) {
7993
+ const core = trimmed.slice(prefix.length).trim();
7994
+ return core || void 0;
7995
+ }
7996
+ }
7997
+ return trimmed;
7998
+ }
7999
+ function expandDaemonIdForms(ids) {
8000
+ const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
8001
+ const out = [];
8002
+ const seen = /* @__PURE__ */ new Set();
8003
+ const add = (value) => {
8004
+ if (!value || seen.has(value)) return;
8005
+ seen.add(value);
8006
+ out.push(value);
8007
+ };
8008
+ for (const raw of list) add(readString5(raw));
8009
+ for (const raw of list) {
8010
+ const core = machineCoreFromDaemonId(readString5(raw));
8011
+ if (!core || !core.startsWith("mach_")) continue;
8012
+ add(core);
8013
+ for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
8014
+ }
8015
+ return out;
8016
+ }
7922
8017
  function summarizeGitShape(status) {
7923
8018
  const record = readRecord3(status);
7924
8019
  if (!Object.keys(record).length) return null;
@@ -7953,9 +8048,11 @@ function summarizeGitShape(status) {
7953
8048
  submodules
7954
8049
  };
7955
8050
  }
8051
+ var DAEMON_ID_PREFIXES;
7956
8052
  var init_dist = __esm({
7957
8053
  "../mesh-shared/dist/index.mjs"() {
7958
8054
  "use strict";
8055
+ DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
7959
8056
  }
7960
8057
  });
7961
8058
 
@@ -8615,17 +8712,7 @@ import { appendFileSync as appendFileSync2, existsSync as existsSync14, readFile
8615
8712
  import { join as join15 } from "path";
8616
8713
  import { randomUUID as randomUUID7 } from "crypto";
8617
8714
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
8618
- const raw = Array.isArray(coordinatorDaemonId) ? coordinatorDaemonId : coordinatorDaemonId != null ? [coordinatorDaemonId] : [];
8619
- const seen = /* @__PURE__ */ new Set();
8620
- const out = [];
8621
- for (const id of raw) {
8622
- if (typeof id !== "string") continue;
8623
- const trimmed = id.trim();
8624
- if (!trimmed || seen.has(trimmed)) continue;
8625
- seen.add(trimmed);
8626
- out.push(trimmed);
8627
- }
8628
- return out;
8715
+ return expandDaemonIdForms(coordinatorDaemonId);
8629
8716
  }
8630
8717
  function readRefineJobId2(event) {
8631
8718
  const metadata = readRecord4(event.metadataEvent) || event;
@@ -9012,6 +9099,7 @@ var init_mesh_events_pending = __esm({
9012
9099
  init_mesh_ledger();
9013
9100
  init_mesh_runtime_store();
9014
9101
  init_mesh_events_utils();
9102
+ init_dist();
9015
9103
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9016
9104
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9017
9105
  MAX_PENDING_EVENTS_KEEP = 50;
@@ -12466,12 +12554,9 @@ var init_snapshot = __esm({
12466
12554
  // src/mesh/mesh-events-coordinator.ts
12467
12555
  import { existsSync as existsSync17 } from "fs";
12468
12556
  function resolveCoordinatorDrainDaemonIds(components) {
12469
- const ids = /* @__PURE__ */ new Set();
12470
12557
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
12471
- if (statusInstanceId) ids.add(statusInstanceId);
12472
12558
  const machineId = readNonEmptyString2(loadConfig().machineId);
12473
- if (machineId) ids.add(machineId);
12474
- return [...ids];
12559
+ return expandDaemonIdForms([statusInstanceId, machineId]);
12475
12560
  }
12476
12561
  function getCachedMeshByWorkspace(workspace) {
12477
12562
  const now = Date.now();
@@ -12622,6 +12707,55 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
12622
12707
  return void 0;
12623
12708
  }
12624
12709
  }
12710
+ function deliverTaskToSession(dispatchThunk, ctx) {
12711
+ const delivery = createSessionDelivery({
12712
+ meshId: ctx.meshId,
12713
+ nodeId: ctx.nodeId,
12714
+ sessionId: ctx.sessionId,
12715
+ providerType: ctx.providerType,
12716
+ taskId: ctx.task.id,
12717
+ kind: "task",
12718
+ message: ctx.task.message,
12719
+ status: "delivering",
12720
+ ...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
12721
+ ...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
12722
+ });
12723
+ let dispatchPromise;
12724
+ try {
12725
+ dispatchPromise = Promise.resolve(dispatchThunk());
12726
+ } catch (e) {
12727
+ dispatchPromise = Promise.reject(e);
12728
+ }
12729
+ let timer;
12730
+ const guarded = Promise.race([
12731
+ dispatchPromise,
12732
+ new Promise((_, reject) => {
12733
+ timer = setTimeout(
12734
+ () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
12735
+ DISPATCH_CONFIRM_TIMEOUT_MS
12736
+ );
12737
+ if (typeof timer?.unref === "function") timer.unref();
12738
+ })
12739
+ ]);
12740
+ guarded.then(() => {
12741
+ if (timer) clearTimeout(timer);
12742
+ updateSessionDeliveryStatus(delivery.id, "delivered");
12743
+ }).catch((e) => {
12744
+ if (timer) clearTimeout(timer);
12745
+ LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
12746
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12747
+ updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
12748
+ try {
12749
+ appendLedgerEntry(ctx.meshId, {
12750
+ kind: "dispatch_failed",
12751
+ nodeId: ctx.nodeId,
12752
+ sessionId: ctx.sessionId,
12753
+ payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
12754
+ });
12755
+ } catch {
12756
+ }
12757
+ });
12758
+ }
12625
12759
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
12626
12760
  const mesh = getMeshWithCache(components, meshId);
12627
12761
  const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
@@ -12640,46 +12774,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12640
12774
  if (!isLocalNode) {
12641
12775
  const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
12642
12776
  const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
12643
- const delivery2 = createSessionDelivery({
12644
- meshId,
12645
- nodeId,
12646
- sessionId,
12647
- providerType,
12648
- taskId: task.id,
12649
- kind: "task",
12650
- message: task.message,
12651
- status: "delivering",
12652
- ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12653
- ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12654
- });
12655
- components.dispatchMeshCommand(node.daemonId, "agent_command", {
12656
- targetSessionId: sessionId,
12657
- cliType: providerType,
12658
- action: "send_chat",
12659
- message: task.message,
12660
- meshContext: {
12777
+ const dispatchMeshCommand = components.dispatchMeshCommand;
12778
+ const remoteDaemonId = node.daemonId;
12779
+ deliverTaskToSession(
12780
+ () => dispatchMeshCommand(remoteDaemonId, "agent_command", {
12781
+ targetSessionId: sessionId,
12782
+ cliType: providerType,
12783
+ action: "send_chat",
12784
+ message: task.message,
12785
+ meshContext: {
12786
+ meshId,
12787
+ nodeId,
12788
+ taskId: task.id,
12789
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12790
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12791
+ }
12792
+ }),
12793
+ {
12661
12794
  meshId,
12662
12795
  nodeId,
12663
- taskId: task.id,
12664
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12665
- ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12666
- }
12667
- }).then(() => {
12668
- updateSessionDeliveryStatus(delivery2.id, "delivered");
12669
- }).catch((e) => {
12670
- LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
12671
- updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
12672
- updateTaskStatus(meshId, task.id, "pending");
12673
- try {
12674
- appendLedgerEntry(meshId, {
12675
- kind: "dispatch_failed",
12676
- nodeId,
12677
- sessionId,
12678
- payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
12679
- });
12680
- } catch {
12796
+ sessionId,
12797
+ providerType,
12798
+ task,
12799
+ transport: "remote",
12800
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12801
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12681
12802
  }
12682
- });
12803
+ );
12683
12804
  return true;
12684
12805
  }
12685
12806
  }
@@ -12701,39 +12822,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12701
12822
  }
12702
12823
  } catch {
12703
12824
  }
12704
- const delivery = createSessionDelivery({
12705
- meshId,
12706
- nodeId,
12707
- sessionId,
12708
- providerType,
12709
- taskId: task.id,
12710
- kind: "task",
12711
- message: task.message,
12712
- status: "delivering",
12713
- ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12714
- ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12715
- });
12716
- components.cliManager.handleCliCommand("agent_command", {
12717
- targetSessionId: sessionId,
12718
- cliType: providerType,
12719
- action: "send_chat",
12720
- message: task.message
12721
- }).then(() => {
12722
- updateSessionDeliveryStatus(delivery.id, "delivered");
12723
- }).catch((e) => {
12724
- LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
12725
- updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12726
- updateTaskStatus(meshId, task.id, "pending");
12727
- try {
12728
- appendLedgerEntry(meshId, {
12729
- kind: "dispatch_failed",
12730
- nodeId,
12731
- sessionId,
12732
- payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
12733
- });
12734
- } catch {
12825
+ deliverTaskToSession(
12826
+ () => components.cliManager.handleCliCommand("agent_command", {
12827
+ targetSessionId: sessionId,
12828
+ cliType: providerType,
12829
+ action: "send_chat",
12830
+ message: task.message
12831
+ }),
12832
+ {
12833
+ meshId,
12834
+ nodeId,
12835
+ sessionId,
12836
+ providerType,
12837
+ task,
12838
+ transport: "local",
12839
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12840
+ ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12735
12841
  }
12736
- });
12842
+ );
12737
12843
  return true;
12738
12844
  }
12739
12845
  function sweepExpiredCooldowns() {
@@ -12998,7 +13104,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12998
13104
  }
12999
13105
  }
13000
13106
  const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
13001
- if (task.targetNodeId && readMeshNodeId(node) !== task.targetNodeId) return false;
13107
+ if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
13002
13108
  if (task.requiredTags?.length) {
13003
13109
  const priorities = normalizeProviderPriority(node?.policy);
13004
13110
  const providerCandidates = priorities.length ? priorities : [void 0];
@@ -13009,7 +13115,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
13009
13115
  return true;
13010
13116
  }) : [];
13011
13117
  if (!candidateNodes.length) {
13012
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
13118
+ const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
13119
+ markAutoLaunch(meshId, task.id, {
13120
+ status: "skipped",
13121
+ reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
13122
+ nodeId: task.targetNodeId
13123
+ });
13013
13124
  continue;
13014
13125
  }
13015
13126
  const strategy = resolveSchedulingStrategy(mesh);
@@ -13960,7 +14071,7 @@ function setupMeshEventForwarding(components) {
13960
14071
  });
13961
14072
  });
13962
14073
  }
13963
- var 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;
14074
+ var 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;
13964
14075
  var init_mesh_events_coordinator = __esm({
13965
14076
  "src/mesh/mesh-events-coordinator.ts"() {
13966
14077
  "use strict";
@@ -13988,6 +14099,7 @@ var init_mesh_events_coordinator = __esm({
13988
14099
  idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
13989
14100
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
13990
14101
  RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
14102
+ DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
13991
14103
  autoLaunchInProgress = /* @__PURE__ */ new Set();
13992
14104
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
13993
14105
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -14043,12 +14155,9 @@ function resolveReconcileIntervalMs() {
14043
14155
  return DEFAULT_RECONCILE_INTERVAL_MS;
14044
14156
  }
14045
14157
  function resolveCoordinatorDaemonIds(components) {
14046
- const ids = /* @__PURE__ */ new Set();
14047
14158
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
14048
- if (statusInstanceId) ids.add(statusInstanceId);
14049
14159
  const machineId = readNonEmptyString2(loadConfig().machineId);
14050
- if (machineId) ids.add(machineId);
14051
- return [...ids];
14160
+ return expandDaemonIdForms([statusInstanceId, machineId]);
14052
14161
  }
14053
14162
  function daemonHostsMesh(mesh, daemonIds) {
14054
14163
  const host = mesh.meshHost;
@@ -14132,6 +14241,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
14132
14241
  }
14133
14242
  }
14134
14243
  }
14244
+ function recoverStrandedAssignedDispatches(meshId, store) {
14245
+ const assigned = getQueue(meshId, { status: ["assigned"] });
14246
+ if (!assigned.length) return;
14247
+ const nowMs = Date.now();
14248
+ for (const row of assigned) {
14249
+ const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
14250
+ if (!Number.isFinite(dispatchedAtMs)) continue;
14251
+ if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
14252
+ if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
14253
+ const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
14254
+ reason: "assigned_stranded_dispatch_unconfirmed",
14255
+ ageMs: nowMs - dispatchedAtMs
14256
+ });
14257
+ if (reclaimed) {
14258
+ 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})`);
14259
+ }
14260
+ }
14261
+ }
14135
14262
  async function runMeshReconcileTick(components) {
14136
14263
  const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
14137
14264
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -14161,6 +14288,17 @@ async function runMeshReconcileTick(components) {
14161
14288
  }
14162
14289
  }
14163
14290
  }
14291
+ if (store) {
14292
+ for (const mesh of listMeshes()) {
14293
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14294
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
14295
+ try {
14296
+ recoverStrandedAssignedDispatches(mesh.id, store);
14297
+ } catch (e) {
14298
+ LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
14299
+ }
14300
+ }
14301
+ }
14164
14302
  for (const mesh of listMeshes()) {
14165
14303
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14166
14304
  if (!daemonHostsMesh(mesh, selfIds)) continue;
@@ -14548,7 +14686,7 @@ function setupMeshReconcileLoop(components) {
14548
14686
  }
14549
14687
  };
14550
14688
  }
14551
- var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
14689
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
14552
14690
  var init_mesh_reconcile_loop = __esm({
14553
14691
  "src/mesh/mesh-reconcile-loop.ts"() {
14554
14692
  "use strict";
@@ -14561,6 +14699,7 @@ var init_mesh_reconcile_loop = __esm({
14561
14699
  init_mesh_events_coordinator();
14562
14700
  init_mesh_unresolved_forward_outbox();
14563
14701
  init_mesh_events_utils();
14702
+ init_dist();
14564
14703
  init_mesh_work_queue();
14565
14704
  init_mesh_ledger();
14566
14705
  init_mesh_active_work();
@@ -14569,6 +14708,7 @@ var init_mesh_reconcile_loop = __esm({
14569
14708
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
14570
14709
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
14571
14710
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
14711
+ ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
14572
14712
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
14573
14713
  }
14574
14714
  });
@@ -14601,6 +14741,84 @@ var init_mesh_events = __esm({
14601
14741
  }
14602
14742
  });
14603
14743
 
14744
+ // src/providers/approval-utils.ts
14745
+ function normalizeApprovalLabel(value) {
14746
+ return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
14747
+ }
14748
+ function isNegativeApprovalLabel(value) {
14749
+ const label = normalizeApprovalLabel(value);
14750
+ return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
14751
+ }
14752
+ function hasNegativeApprovalOption(buttons) {
14753
+ return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
14754
+ }
14755
+ function getApprovalPositiveHints(provider) {
14756
+ const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
14757
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
14758
+ }
14759
+ function pickApprovalButton(buttons, provider) {
14760
+ const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
14761
+ if (labels.length === 0) {
14762
+ return { index: -1, label: "" };
14763
+ }
14764
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
14765
+ const hints = getApprovalPositiveHints(provider);
14766
+ for (const hint of hints) {
14767
+ const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
14768
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
14769
+ const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
14770
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
14771
+ const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
14772
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
14773
+ }
14774
+ return { index: -1, label: "" };
14775
+ }
14776
+ function pickAutoApprovalButton(buttons) {
14777
+ const labels = (buttons || []).map((button) => String(button || "").trim());
14778
+ const index = labels.findIndex(Boolean);
14779
+ return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
14780
+ }
14781
+ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
14782
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
14783
+ const cleanMessage = String(modalMessage || "").trim();
14784
+ if (cleanMessage) lines.push(cleanMessage);
14785
+ return lines.join("\n");
14786
+ }
14787
+ function looksLikeActiveApprovalPromptText(content) {
14788
+ const text = content.trim();
14789
+ if (!text || text.length > 2e3) return false;
14790
+ 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);
14791
+ const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
14792
+ if (hasApprovalQuestion && hasNumberedChoices) return true;
14793
+ const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
14794
+ const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
14795
+ const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
14796
+ if (hasDontAskAgain && hasNoOption) return true;
14797
+ if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
14798
+ return false;
14799
+ }
14800
+ var DEFAULT_APPROVAL_POSITIVE_HINTS;
14801
+ var init_approval_utils = __esm({
14802
+ "src/providers/approval-utils.ts"() {
14803
+ "use strict";
14804
+ DEFAULT_APPROVAL_POSITIVE_HINTS = [
14805
+ "yes",
14806
+ "allow once",
14807
+ "approve",
14808
+ "accept",
14809
+ "continue",
14810
+ "run",
14811
+ "proceed",
14812
+ "confirm",
14813
+ "save",
14814
+ "ok",
14815
+ "trust",
14816
+ "allow",
14817
+ "always allow"
14818
+ ];
14819
+ }
14820
+ });
14821
+
14604
14822
  // src/logging/debug-config.ts
14605
14823
  function normalizeCategories(categories) {
14606
14824
  if (!Array.isArray(categories)) return [];
@@ -15587,6 +15805,27 @@ function compileSettledPromptMatchers(spec) {
15587
15805
  });
15588
15806
  return { prompt, footers };
15589
15807
  }
15808
+ function extractButtonLabels(spec, text) {
15809
+ if (!text) return [];
15810
+ const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
15811
+ const buttonRe = compile2(spec.buttonPattern, flags);
15812
+ const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
15813
+ const out = [];
15814
+ for (const line of text.split("\n")) {
15815
+ buttonRe.lastIndex = 0;
15816
+ const m = buttonRe.exec(line);
15817
+ if (!m) continue;
15818
+ const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
15819
+ if (captured && captured.trim()) out.push(captured.trim());
15820
+ }
15821
+ return out;
15822
+ }
15823
+ function buttonBlockApprovalCue(spec, text) {
15824
+ const labels = extractButtonLabels(spec, text);
15825
+ if (labels.length < 2) return false;
15826
+ if (pickApprovalButton(labels).index < 0) return false;
15827
+ return hasNegativeApprovalOption(labels);
15828
+ }
15590
15829
  function modalMatches(spec, input) {
15591
15830
  const text = input.screenText ?? "";
15592
15831
  const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
@@ -15595,6 +15834,7 @@ function modalMatches(spec, input) {
15595
15834
  const re = compile2(variant.regex, variant.flags ?? "i");
15596
15835
  if (re.test(text)) return true;
15597
15836
  }
15837
+ if (buttonBlockApprovalCue(spec, text)) return true;
15598
15838
  return false;
15599
15839
  }
15600
15840
  function evaluateGroup(group, spec, input, compiled) {
@@ -15649,6 +15889,7 @@ var init_detect_status = __esm({
15649
15889
  "src/providers/sdk/v1/builders/cli/detect-status.ts"() {
15650
15890
  "use strict";
15651
15891
  init_visible_region();
15892
+ init_approval_utils();
15652
15893
  DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
15653
15894
  }
15654
15895
  });
@@ -25746,76 +25987,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
25746
25987
  return normalized;
25747
25988
  }
25748
25989
 
25749
- // src/providers/approval-utils.ts
25750
- var DEFAULT_APPROVAL_POSITIVE_HINTS = [
25751
- "yes",
25752
- "allow once",
25753
- "approve",
25754
- "accept",
25755
- "continue",
25756
- "run",
25757
- "proceed",
25758
- "confirm",
25759
- "save",
25760
- "ok",
25761
- "trust",
25762
- "allow",
25763
- "always allow"
25764
- ];
25765
- function normalizeApprovalLabel(value) {
25766
- return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
25767
- }
25768
- function isNegativeApprovalLabel(value) {
25769
- const label = normalizeApprovalLabel(value);
25770
- return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
25771
- }
25772
- function getApprovalPositiveHints(provider) {
25773
- const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
25774
- return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
25775
- }
25776
- function pickApprovalButton(buttons, provider) {
25777
- const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
25778
- if (labels.length === 0) {
25779
- return { index: -1, label: "" };
25780
- }
25781
- const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
25782
- const hints = getApprovalPositiveHints(provider);
25783
- for (const hint of hints) {
25784
- const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
25785
- if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
25786
- const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
25787
- if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
25788
- const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
25789
- if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
25790
- }
25791
- return { index: -1, label: "" };
25792
- }
25793
- function pickAutoApprovalButton(buttons) {
25794
- const labels = (buttons || []).map((button) => String(button || "").trim());
25795
- const index = labels.findIndex(Boolean);
25796
- return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
25797
- }
25798
- function formatAutoApprovalMessage(modalMessage, buttonLabel) {
25799
- const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
25800
- const cleanMessage = String(modalMessage || "").trim();
25801
- if (cleanMessage) lines.push(cleanMessage);
25802
- return lines.join("\n");
25803
- }
25804
- function looksLikeActiveApprovalPromptText(content) {
25805
- const text = content.trim();
25806
- if (!text || text.length > 2e3) return false;
25807
- 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);
25808
- const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
25809
- if (hasApprovalQuestion && hasNumberedChoices) return true;
25810
- const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
25811
- const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
25812
- const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
25813
- if (hasDontAskAgain && hasNoOption) return true;
25814
- if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
25815
- return false;
25816
- }
25817
-
25818
25990
  // src/providers/ide-provider-instance.ts
25991
+ init_approval_utils();
25819
25992
  init_provider_patch_state();
25820
25993
  init_chat_message_normalization();
25821
25994
  init_open_panel_support();
@@ -26899,6 +27072,7 @@ import * as fs7 from "fs";
26899
27072
  import * as os10 from "os";
26900
27073
  import * as path16 from "path";
26901
27074
  import { randomUUID as randomUUID11 } from "crypto";
27075
+ init_approval_utils();
26902
27076
  init_coordinator_registry();
26903
27077
  init_logger();
26904
27078
 
@@ -34887,6 +35061,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
34887
35061
  // src/providers/cli-provider-instance.ts
34888
35062
  init_logger();
34889
35063
  init_control_effects();
35064
+ init_approval_utils();
34890
35065
  init_provider_patch_state();
34891
35066
 
34892
35067
  // src/providers/provider-session-id.ts
@@ -35148,6 +35323,20 @@ var CliProviderInstance = class _CliProviderInstance {
35148
35323
  * keystroke until the modal *content* has settled.
35149
35324
  */
35150
35325
  static AUTO_APPROVE_SETTLE_MS = 600;
35326
+ /**
35327
+ * Busy-side hysteresis for the settle gate. A momentary `generating` flip
35328
+ * while the SAME approval modal's button block is still on screen (its
35329
+ * question line scrolled out of the captured frame, only the buttons + a
35330
+ * residual `esc to interrupt` spinner remain) briefly reports
35331
+ * status!=waiting_approval. Without hysteresis that flip wipes the settle
35332
+ * clock, and the modal→generating→modal flap restarts the 600ms window
35333
+ * every time so auto-approve never fires. We keep the in-progress settle
35334
+ * gate warm across an inactive blip up to this bound; only once the modal
35335
+ * has genuinely stayed gone this long (a real resolution → idle) is the
35336
+ * gate cleared. Bounded so a genuinely new, later approval still re-settles
35337
+ * from scratch rather than firing on a stale timestamp.
35338
+ */
35339
+ static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
35151
35340
  adapter;
35152
35341
  context = null;
35153
35342
  events = [];
@@ -35169,6 +35358,10 @@ var CliProviderInstance = class _CliProviderInstance {
35169
35358
  pendingAutoApprovalSignature = "";
35170
35359
  pendingAutoApprovalSince = 0;
35171
35360
  autoApproveSettleTimer = null;
35361
+ // Wall-clock when auto-approve first observed status!=waiting_approval while
35362
+ // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35363
+ // brief generating flip does not immediately wipe the settle clock.
35364
+ autoApproveInactiveSince = 0;
35172
35365
  controlValues = {};
35173
35366
  summaryMetadata = void 0;
35174
35367
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -35989,14 +36182,28 @@ var CliProviderInstance = class _CliProviderInstance {
35989
36182
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
35990
36183
  if (!autoApproveActive) {
35991
36184
  this.lastAutoApprovalSignature = "";
36185
+ if (this.pendingAutoApprovalSince) {
36186
+ if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
36187
+ const goneForMs = now - this.autoApproveInactiveSince;
36188
+ if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
36189
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36190
+ this.autoApproveSettleTimer = setTimeout(() => {
36191
+ this.autoApproveSettleTimer = null;
36192
+ this.recheckAutoApproveSettled();
36193
+ }, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
36194
+ return autoApproveActive;
36195
+ }
36196
+ }
35992
36197
  this.pendingAutoApprovalSignature = "";
35993
36198
  this.pendingAutoApprovalSince = 0;
36199
+ this.autoApproveInactiveSince = 0;
35994
36200
  if (this.autoApproveSettleTimer) {
35995
36201
  clearTimeout(this.autoApproveSettleTimer);
35996
36202
  this.autoApproveSettleTimer = null;
35997
36203
  }
35998
36204
  return autoApproveActive;
35999
36205
  }
36206
+ this.autoApproveInactiveSince = 0;
36000
36207
  const modal = adapterStatus.activeModal;
36001
36208
  const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
36002
36209
  if (!modal || buttons.length === 0) {
@@ -36006,18 +36213,18 @@ var CliProviderInstance = class _CliProviderInstance {
36006
36213
  if (buttonIndex < 0) {
36007
36214
  return autoApproveActive;
36008
36215
  }
36009
- const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36010
- const signature = [
36011
- approvalEntrySeq,
36216
+ const modalSignature = [
36012
36217
  typeof modal?.message === "string" ? modal.message.trim() : "",
36013
36218
  buttons.join("|"),
36014
36219
  buttonIndex
36015
36220
  ].join("::");
36016
- if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
36221
+ const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36222
+ const busySignature = `${approvalEntrySeq}::${modalSignature}`;
36223
+ if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
36017
36224
  return autoApproveActive;
36018
36225
  }
36019
- if (signature !== this.pendingAutoApprovalSignature) {
36020
- this.pendingAutoApprovalSignature = signature;
36226
+ if (modalSignature !== this.pendingAutoApprovalSignature) {
36227
+ this.pendingAutoApprovalSignature = modalSignature;
36021
36228
  this.pendingAutoApprovalSince = now;
36022
36229
  }
36023
36230
  const settledForMs = now - this.pendingAutoApprovalSince;
@@ -36034,9 +36241,10 @@ var CliProviderInstance = class _CliProviderInstance {
36034
36241
  this.autoApproveSettleTimer = null;
36035
36242
  }
36036
36243
  this.autoApproveBusy = true;
36037
- this.lastAutoApprovalSignature = signature;
36244
+ this.lastAutoApprovalSignature = busySignature;
36038
36245
  this.pendingAutoApprovalSignature = "";
36039
36246
  this.pendingAutoApprovalSince = 0;
36247
+ this.autoApproveInactiveSince = 0;
36040
36248
  if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
36041
36249
  this.autoApproveBusyTimer = setTimeout(() => {
36042
36250
  this.autoApproveBusy = false;
@@ -42928,7 +43136,8 @@ init_logger();
42928
43136
  import * as fs23 from "fs";
42929
43137
  import * as path35 from "path";
42930
43138
  import * as os26 from "os";
42931
- 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");
43139
+ 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");
43140
+ var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
42932
43141
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
42933
43142
  var MAX_DAYS = 7;
42934
43143
  try {
@@ -43783,13 +43992,14 @@ async function waitForPidExit(pid, timeoutMs) {
43783
43992
  }
43784
43993
  }
43785
43994
  }
43786
- function stopSessionHostProcesses(appName) {
43995
+ async function stopSessionHostProcesses(appName) {
43787
43996
  const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
43997
+ let killedPid = null;
43788
43998
  try {
43789
43999
  if (fs25.existsSync(pidFile)) {
43790
44000
  const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
43791
44001
  if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
43792
- killPid(pid);
44002
+ if (killPid(pid)) killedPid = pid;
43793
44003
  }
43794
44004
  }
43795
44005
  } catch {
@@ -43799,6 +44009,15 @@ function stopSessionHostProcesses(appName) {
43799
44009
  } catch {
43800
44010
  }
43801
44011
  }
44012
+ if (killedPid !== null) {
44013
+ await waitForPidExit(killedPid, 15e3);
44014
+ }
44015
+ }
44016
+ function isRetriableInstallLockError(error) {
44017
+ const code = error?.code;
44018
+ if (code === "EBUSY" || code === "EPERM") return true;
44019
+ const text = `${error?.message || ""} ${error?.stderr || ""}`;
44020
+ return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
43802
44021
  }
43803
44022
  function removeDaemonPidFile() {
43804
44023
  const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
@@ -43878,22 +44097,37 @@ async function runDaemonUpgradeHelper(payload) {
43878
44097
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
43879
44098
  await waitForPidExit(payload.parentPid, 15e3);
43880
44099
  }
43881
- stopSessionHostProcesses(sessionHostAppName);
44100
+ await stopSessionHostProcesses(sessionHostAppName);
43882
44101
  removeDaemonPidFile();
43883
44102
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
43884
44103
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
43885
44104
  appendUpgradeLog(`Installing ${spec}`);
43886
- const installOutput = execFileSync5(
43887
- installCommand.command,
43888
- installCommand.args,
43889
- {
43890
- encoding: "utf8",
43891
- stdio: "pipe",
43892
- maxBuffer: 20 * 1024 * 1024,
43893
- env: buildInstallEnvWithNodeOnPath(),
43894
- ...installCommand.execOptions
44105
+ const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
44106
+ let installOutput = "";
44107
+ for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
44108
+ try {
44109
+ installOutput = String(execFileSync5(
44110
+ installCommand.command,
44111
+ installCommand.args,
44112
+ {
44113
+ encoding: "utf8",
44114
+ stdio: "pipe",
44115
+ maxBuffer: 20 * 1024 * 1024,
44116
+ env: buildInstallEnvWithNodeOnPath(),
44117
+ ...installCommand.execOptions
44118
+ }
44119
+ ));
44120
+ break;
44121
+ } catch (error) {
44122
+ if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
44123
+ appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
44124
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
44125
+ await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
44126
+ continue;
44127
+ }
44128
+ throw error;
43895
44129
  }
43896
- );
44130
+ }
43897
44131
  if (installOutput.trim()) {
43898
44132
  appendUpgradeLog(installOutput.trim());
43899
44133
  }
@@ -52655,6 +52889,7 @@ var DaemonAgentStreamManager = class {
52655
52889
 
52656
52890
  // src/agent-stream/poller.ts
52657
52891
  init_logger();
52892
+ init_approval_utils();
52658
52893
  init_chat_message_normalization();
52659
52894
  var AgentStreamPoller = class {
52660
52895
  deps;