@adhdev/daemon-core 0.9.82-rc.196 → 0.9.82-rc.198

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
@@ -3418,6 +3418,49 @@ var init_mesh_runtime_store = __esm({
3418
3418
  metadata TEXT,
3419
3419
  PRIMARY KEY (node_id, session_id)
3420
3420
  );
3421
+
3422
+ CREATE TABLE IF NOT EXISTS mesh_session_delivery (
3423
+ id TEXT PRIMARY KEY,
3424
+ mesh_id TEXT NOT NULL,
3425
+ node_id TEXT,
3426
+ session_id TEXT,
3427
+ provider_type TEXT,
3428
+ task_id TEXT,
3429
+ kind TEXT NOT NULL,
3430
+ priority INTEGER NOT NULL DEFAULT 0,
3431
+ message TEXT NOT NULL,
3432
+ status TEXT NOT NULL DEFAULT 'queued',
3433
+ deliver_after TEXT,
3434
+ expires_at TEXT,
3435
+ attempt_count INTEGER NOT NULL DEFAULT 0,
3436
+ source_coordinator_session_id TEXT,
3437
+ source_coordinator_daemon_id TEXT,
3438
+ last_error TEXT,
3439
+ created_at TEXT NOT NULL,
3440
+ updated_at TEXT NOT NULL
3441
+ );
3442
+
3443
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_mesh_status
3444
+ ON mesh_session_delivery(mesh_id, status, created_at);
3445
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_session
3446
+ ON mesh_session_delivery(mesh_id, session_id, status);
3447
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_task
3448
+ ON mesh_session_delivery(mesh_id, task_id);
3449
+
3450
+ CREATE TABLE IF NOT EXISTS mesh_completion_conflicts (
3451
+ id TEXT PRIMARY KEY,
3452
+ mesh_id TEXT NOT NULL,
3453
+ fingerprint TEXT NOT NULL,
3454
+ conflicting_task_id TEXT,
3455
+ conflicting_session_id TEXT,
3456
+ original_task_id TEXT,
3457
+ original_session_id TEXT,
3458
+ event TEXT NOT NULL,
3459
+ created_at TEXT NOT NULL
3460
+ );
3461
+
3462
+ CREATE INDEX IF NOT EXISTS idx_mesh_completion_conflicts_mesh
3463
+ ON mesh_completion_conflicts(mesh_id, created_at);
3421
3464
  `);
3422
3465
  }
3423
3466
  hasCompletionFingerprint(fingerprint) {
@@ -3742,6 +3785,131 @@ var init_mesh_runtime_store = __esm({
3742
3785
  pruneExpiredRemoteIdleSessions() {
3743
3786
  this.db.prepare("DELETE FROM remote_idle_sessions WHERE expires_at <= ?").run(Date.now());
3744
3787
  }
3788
+ // ── Session Delivery Queue ───────────────────────────────────────────────
3789
+ insertSessionDelivery(entry) {
3790
+ this.db.prepare(`
3791
+ INSERT OR REPLACE INTO mesh_session_delivery (
3792
+ id, mesh_id, node_id, session_id, provider_type, task_id, kind, priority,
3793
+ message, status, deliver_after, expires_at, attempt_count,
3794
+ source_coordinator_session_id, source_coordinator_daemon_id,
3795
+ last_error, created_at, updated_at
3796
+ ) VALUES (
3797
+ @id, @meshId, @nodeId, @sessionId, @providerType, @taskId, @kind, @priority,
3798
+ @message, @status, @deliverAfter, @expiresAt, 0,
3799
+ @sourceCoordinatorSessionId, @sourceCoordinatorDaemonId,
3800
+ NULL, @createdAt, @updatedAt
3801
+ )
3802
+ `).run({
3803
+ id: entry.id,
3804
+ meshId: entry.meshId,
3805
+ nodeId: entry.nodeId ?? null,
3806
+ sessionId: entry.sessionId ?? null,
3807
+ providerType: entry.providerType ?? null,
3808
+ taskId: entry.taskId ?? null,
3809
+ kind: entry.kind,
3810
+ priority: entry.priority ?? 0,
3811
+ message: entry.message,
3812
+ status: entry.status,
3813
+ deliverAfter: entry.deliverAfter ?? null,
3814
+ expiresAt: entry.expiresAt ?? null,
3815
+ sourceCoordinatorSessionId: entry.sourceCoordinatorSessionId ?? null,
3816
+ sourceCoordinatorDaemonId: entry.sourceCoordinatorDaemonId ?? null,
3817
+ createdAt: entry.createdAt,
3818
+ updatedAt: entry.updatedAt
3819
+ });
3820
+ this.maybeCheckpointWal();
3821
+ }
3822
+ updateSessionDeliveryStatus(id, status, opts) {
3823
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3824
+ if (opts?.incrementAttempt) {
3825
+ this.db.prepare(`
3826
+ UPDATE mesh_session_delivery
3827
+ SET status = @status, last_error = @lastError, attempt_count = attempt_count + 1, updated_at = @updatedAt
3828
+ WHERE id = @id
3829
+ `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
3830
+ } else {
3831
+ this.db.prepare(`
3832
+ UPDATE mesh_session_delivery
3833
+ SET status = @status, last_error = @lastError, updated_at = @updatedAt
3834
+ WHERE id = @id
3835
+ `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
3836
+ }
3837
+ }
3838
+ getActiveSessionDeliveries(meshId, sessionId) {
3839
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3840
+ const sql = sessionId ? `SELECT * FROM mesh_session_delivery WHERE mesh_id = ? AND session_id = ? AND status NOT IN ('delivered','completed','failed','expired','cancelled') AND (expires_at IS NULL OR expires_at > ?) ORDER BY priority DESC, created_at ASC` : `SELECT * FROM mesh_session_delivery WHERE mesh_id = ? AND status NOT IN ('delivered','completed','failed','expired','cancelled') AND (expires_at IS NULL OR expires_at > ?) ORDER BY priority DESC, created_at ASC`;
3841
+ const rows = sessionId ? this.db.prepare(sql).all(meshId, sessionId, now) : this.db.prepare(sql).all(meshId, now);
3842
+ return rows.map((r) => ({
3843
+ id: r.id,
3844
+ meshId: r.mesh_id,
3845
+ nodeId: r.node_id,
3846
+ sessionId: r.session_id,
3847
+ providerType: r.provider_type,
3848
+ taskId: r.task_id,
3849
+ kind: r.kind,
3850
+ priority: r.priority,
3851
+ message: r.message,
3852
+ status: r.status,
3853
+ deliverAfter: r.deliver_after,
3854
+ expiresAt: r.expires_at,
3855
+ attemptCount: r.attempt_count,
3856
+ sourceCoordinatorSessionId: r.source_coordinator_session_id,
3857
+ sourceCoordinatorDaemonId: r.source_coordinator_daemon_id,
3858
+ lastError: r.last_error,
3859
+ createdAt: r.created_at,
3860
+ updatedAt: r.updated_at
3861
+ }));
3862
+ }
3863
+ expireStaleSessionDeliveries(meshId) {
3864
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3865
+ this.db.prepare(`
3866
+ UPDATE mesh_session_delivery
3867
+ SET status = 'expired', updated_at = ?
3868
+ WHERE mesh_id = ? AND expires_at IS NOT NULL AND expires_at <= ?
3869
+ AND status NOT IN ('delivered','completed','failed','expired','cancelled')
3870
+ `).run(now, meshId, now);
3871
+ }
3872
+ deleteSessionDeliveries(meshId) {
3873
+ this.db.prepare("DELETE FROM mesh_session_delivery WHERE mesh_id = ?").run(meshId);
3874
+ }
3875
+ // ── Completion Conflict Diagnostics ──────────────────────────────────────
3876
+ recordCompletionConflict(entry) {
3877
+ this.db.prepare(`
3878
+ INSERT OR IGNORE INTO mesh_completion_conflicts
3879
+ (id, mesh_id, fingerprint, conflicting_task_id, conflicting_session_id,
3880
+ original_task_id, original_session_id, event, created_at)
3881
+ VALUES (@id, @meshId, @fingerprint, @conflictingTaskId, @conflictingSessionId,
3882
+ @originalTaskId, @originalSessionId, @event, @createdAt)
3883
+ `).run({
3884
+ id: entry.id,
3885
+ meshId: entry.meshId,
3886
+ fingerprint: entry.fingerprint,
3887
+ conflictingTaskId: entry.conflictingTaskId ?? null,
3888
+ conflictingSessionId: entry.conflictingSessionId ?? null,
3889
+ originalTaskId: entry.originalTaskId ?? null,
3890
+ originalSessionId: entry.originalSessionId ?? null,
3891
+ event: entry.event,
3892
+ createdAt: entry.createdAt
3893
+ });
3894
+ this.maybeCheckpointWal();
3895
+ }
3896
+ getRecentCompletionConflicts(meshId, limitMs = 60 * 60 * 1e3) {
3897
+ const cutoff = new Date(Date.now() - limitMs).toISOString();
3898
+ const rows = this.db.prepare(
3899
+ "SELECT * FROM mesh_completion_conflicts WHERE mesh_id = ? AND created_at >= ? ORDER BY created_at DESC LIMIT 50"
3900
+ ).all(meshId, cutoff);
3901
+ return rows.map((r) => ({
3902
+ id: r.id,
3903
+ meshId: r.mesh_id,
3904
+ fingerprint: r.fingerprint,
3905
+ conflictingTaskId: r.conflicting_task_id,
3906
+ conflictingSessionId: r.conflicting_session_id,
3907
+ originalTaskId: r.original_task_id,
3908
+ originalSessionId: r.original_session_id,
3909
+ event: r.event,
3910
+ createdAt: r.created_at
3911
+ }));
3912
+ }
3745
3913
  };
3746
3914
  }
3747
3915
  });
@@ -4155,6 +4323,167 @@ var init_cli_detector = __esm({
4155
4323
  }
4156
4324
  });
4157
4325
 
4326
+ // src/mesh/mesh-delivery-policy.ts
4327
+ import { randomUUID as randomUUID6 } from "crypto";
4328
+ function resolveDeliveryDecision(sessionStatus, opts) {
4329
+ const status = (sessionStatus || "").trim().toLowerCase();
4330
+ if (!status) {
4331
+ return {
4332
+ decision: "rejected",
4333
+ reason: "unknown_session_status",
4334
+ message: "Session status is unknown. Delivery rejected (fail-closed). Use mesh_launch_session to start a fresh session."
4335
+ };
4336
+ }
4337
+ if (IMMEDIATE_DELIVERY_STATUSES.has(status)) {
4338
+ return {
4339
+ decision: "immediate",
4340
+ reason: `session_${status}`,
4341
+ message: `Session is ${status} \u2014 delivery allowed immediately.`
4342
+ };
4343
+ }
4344
+ if (BUSY_DELIVERY_STATUSES.has(status)) {
4345
+ if (opts?.allowBusyInjection) {
4346
+ return {
4347
+ decision: "immediate",
4348
+ reason: `session_${status}_busy_injection_allowed`,
4349
+ message: `Session is ${status} but provider supports busy injection. Delivered immediately.`
4350
+ };
4351
+ }
4352
+ if (status === "waiting_approval" && opts?.kind === "approval") {
4353
+ return {
4354
+ decision: "immediate",
4355
+ reason: "session_waiting_approval_approval_message",
4356
+ message: "Session is waiting for approval \u2014 approval message delivered immediately."
4357
+ };
4358
+ }
4359
+ return {
4360
+ decision: "queued",
4361
+ reason: `session_${status}_busy`,
4362
+ message: `Session is ${status}. Task queued for delivery when session becomes idle. Do not inject directly into a busy session.`
4363
+ };
4364
+ }
4365
+ if (TERMINAL_DELIVERY_STATUSES.has(status)) {
4366
+ return {
4367
+ decision: "rejected",
4368
+ reason: `session_${status}_terminal`,
4369
+ message: `Session is ${status} (terminal). Delivery rejected. Launch a new session before dispatching tasks.`
4370
+ };
4371
+ }
4372
+ return {
4373
+ decision: "rejected",
4374
+ reason: "unrecognized_session_status",
4375
+ message: `Session status '${sessionStatus}' is not recognized. Delivery rejected (fail-closed). Inspect session state before retrying.`
4376
+ };
4377
+ }
4378
+ function createSessionDelivery(opts) {
4379
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4380
+ const id = randomUUID6();
4381
+ const record = {
4382
+ id,
4383
+ meshId: opts.meshId,
4384
+ nodeId: opts.nodeId,
4385
+ sessionId: opts.sessionId,
4386
+ providerType: opts.providerType,
4387
+ taskId: opts.taskId,
4388
+ kind: opts.kind,
4389
+ priority: opts.priority ?? 0,
4390
+ message: opts.message,
4391
+ status: opts.status,
4392
+ deliverAfter: opts.deliverAfter,
4393
+ expiresAt: opts.expiresAt,
4394
+ attemptCount: 0,
4395
+ sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId,
4396
+ sourceCoordinatorDaemonId: opts.sourceCoordinatorDaemonId,
4397
+ createdAt: now,
4398
+ updatedAt: now
4399
+ };
4400
+ MeshRuntimeStore.getInstance().insertSessionDelivery({
4401
+ id,
4402
+ meshId: opts.meshId,
4403
+ nodeId: opts.nodeId,
4404
+ sessionId: opts.sessionId,
4405
+ providerType: opts.providerType,
4406
+ taskId: opts.taskId,
4407
+ kind: opts.kind,
4408
+ priority: opts.priority ?? 0,
4409
+ message: opts.message,
4410
+ status: opts.status,
4411
+ deliverAfter: opts.deliverAfter,
4412
+ expiresAt: opts.expiresAt,
4413
+ sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId,
4414
+ sourceCoordinatorDaemonId: opts.sourceCoordinatorDaemonId,
4415
+ createdAt: now,
4416
+ updatedAt: now
4417
+ });
4418
+ return record;
4419
+ }
4420
+ function updateSessionDeliveryStatus(id, status, opts) {
4421
+ try {
4422
+ MeshRuntimeStore.getInstance().updateSessionDeliveryStatus(id, status, opts);
4423
+ } catch {
4424
+ }
4425
+ }
4426
+ function getActiveSessionDeliveries(meshId, sessionId) {
4427
+ try {
4428
+ return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(meshId, sessionId);
4429
+ } catch {
4430
+ return [];
4431
+ }
4432
+ }
4433
+ function recordCompletionConflict(opts) {
4434
+ try {
4435
+ MeshRuntimeStore.getInstance().recordCompletionConflict({
4436
+ id: randomUUID6(),
4437
+ meshId: opts.meshId,
4438
+ fingerprint: opts.fingerprint,
4439
+ conflictingTaskId: opts.conflictingTaskId,
4440
+ conflictingSessionId: opts.conflictingSessionId,
4441
+ originalTaskId: opts.originalTaskId,
4442
+ originalSessionId: opts.originalSessionId,
4443
+ event: opts.event,
4444
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
4445
+ });
4446
+ } catch {
4447
+ }
4448
+ }
4449
+ function getRecentCompletionConflicts(meshId, limitMs) {
4450
+ try {
4451
+ return MeshRuntimeStore.getInstance().getRecentCompletionConflicts(meshId, limitMs);
4452
+ } catch {
4453
+ return [];
4454
+ }
4455
+ }
4456
+ var IMMEDIATE_DELIVERY_STATUSES, BUSY_DELIVERY_STATUSES, TERMINAL_DELIVERY_STATUSES;
4457
+ var init_mesh_delivery_policy = __esm({
4458
+ "src/mesh/mesh-delivery-policy.ts"() {
4459
+ "use strict";
4460
+ init_mesh_runtime_store();
4461
+ IMMEDIATE_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4462
+ "idle",
4463
+ "waiting_input",
4464
+ "ready"
4465
+ ]);
4466
+ BUSY_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4467
+ "generating",
4468
+ "running",
4469
+ "streaming",
4470
+ "busy",
4471
+ "starting",
4472
+ "initializing",
4473
+ "waiting_approval"
4474
+ ]);
4475
+ TERMINAL_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4476
+ "stopped",
4477
+ "failed",
4478
+ "terminated",
4479
+ "exited",
4480
+ "closed",
4481
+ "deleted",
4482
+ "error"
4483
+ ]);
4484
+ }
4485
+ });
4486
+
4158
4487
  // src/mesh/mesh-events.ts
4159
4488
  var mesh_events_exports = {};
4160
4489
  __export(mesh_events_exports, {
@@ -4501,7 +4830,18 @@ function buildMeshCompletionFingerprint(args) {
4501
4830
  function isDuplicateMeshCompletionEvent(args) {
4502
4831
  const fingerprint = buildMeshCompletionFingerprint(args);
4503
4832
  if (!fingerprint) return false;
4504
- if (hasFingerprintSeen(fingerprint)) return true;
4833
+ if (hasFingerprintSeen(fingerprint)) {
4834
+ if (args.taskId) {
4835
+ recordCompletionConflict({
4836
+ meshId: args.meshId,
4837
+ fingerprint,
4838
+ conflictingTaskId: args.taskId,
4839
+ conflictingSessionId: args.sessionId,
4840
+ event: args.event
4841
+ });
4842
+ }
4843
+ return true;
4844
+ }
4505
4845
  recordFingerprintSeen(fingerprint);
4506
4846
  return false;
4507
4847
  }
@@ -4752,20 +5092,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4752
5092
  if (node?.daemonId && components.dispatchMeshCommand) {
4753
5093
  const isLocalNode = components.cliManager.adapters.has(sessionId);
4754
5094
  if (!isLocalNode) {
5095
+ const delivery2 = createSessionDelivery({
5096
+ meshId,
5097
+ nodeId,
5098
+ sessionId,
5099
+ providerType,
5100
+ taskId: task.id,
5101
+ kind: "task",
5102
+ message: task.message,
5103
+ status: "delivering"
5104
+ });
4755
5105
  components.dispatchMeshCommand(node.daemonId, "agent_command", {
4756
5106
  targetSessionId: sessionId,
4757
5107
  cliType: providerType,
4758
5108
  action: "send_chat",
4759
5109
  message: task.message
5110
+ }).then(() => {
5111
+ updateSessionDeliveryStatus(delivery2.id, "delivered");
4760
5112
  }).catch((e) => {
4761
5113
  LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
5114
+ updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
4762
5115
  updateTaskStatus(meshId, task.id, "pending");
4763
5116
  try {
4764
5117
  appendLedgerEntry(meshId, {
4765
5118
  kind: "dispatch_failed",
4766
5119
  nodeId,
4767
5120
  sessionId,
4768
- payload: { taskId: task.id, error: e?.message, retryable: true }
5121
+ payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
4769
5122
  });
4770
5123
  } catch {
4771
5124
  }
@@ -4773,13 +5126,26 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4773
5126
  return true;
4774
5127
  }
4775
5128
  }
5129
+ const delivery = createSessionDelivery({
5130
+ meshId,
5131
+ nodeId,
5132
+ sessionId,
5133
+ providerType,
5134
+ taskId: task.id,
5135
+ kind: "task",
5136
+ message: task.message,
5137
+ status: "delivering"
5138
+ });
4776
5139
  components.cliManager.handleCliCommand("agent_command", {
4777
5140
  targetSessionId: sessionId,
4778
5141
  cliType: providerType,
4779
5142
  action: "send_chat",
4780
5143
  message: task.message
5144
+ }).then(() => {
5145
+ updateSessionDeliveryStatus(delivery.id, "delivered");
4781
5146
  }).catch((e) => {
4782
5147
  LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
5148
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
4783
5149
  updateTaskStatus(meshId, task.id, "failed");
4784
5150
  });
4785
5151
  return true;
@@ -5384,7 +5750,9 @@ function injectMeshSystemMessage(components, args) {
5384
5750
  finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0,
5385
5751
  // Scope dedup to the coordinator daemon so two coordinators for the same mesh
5386
5752
  // don't suppress each other's completion events via shared fingerprint table.
5387
- coordinatorDaemonId: workerCoordinatorDaemonId || void 0
5753
+ coordinatorDaemonId: workerCoordinatorDaemonId || void 0,
5754
+ taskId: readNonEmptyString2(args.metadataEvent.taskId) || void 0,
5755
+ nodeId: eventNodeId || void 0
5388
5756
  });
5389
5757
  if (duplicateCompletion) {
5390
5758
  LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
@@ -5400,7 +5768,9 @@ function injectMeshSystemMessage(components, args) {
5400
5768
  providerSessionId: readNonEmptyString2(args.metadataEvent.providerSessionId) || void 0,
5401
5769
  timestamp: eventTimestamp,
5402
5770
  finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0,
5403
- coordinatorDaemonId: workerCoordinatorDaemonId || void 0
5771
+ coordinatorDaemonId: workerCoordinatorDaemonId || void 0,
5772
+ taskId: readNonEmptyString2(args.metadataEvent.taskId) || void 0,
5773
+ nodeId: eventNodeId || void 0
5404
5774
  });
5405
5775
  if (duplicateStopped) {
5406
5776
  LOG.info("MeshEvents", `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
@@ -5775,6 +6145,7 @@ var init_mesh_events = __esm({
5775
6145
  init_mesh_work_queue();
5776
6146
  init_mesh_runtime_store();
5777
6147
  init_mesh_fast_forward();
6148
+ init_mesh_delivery_policy();
5778
6149
  REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
5779
6150
  meshByWorkspaceCache = /* @__PURE__ */ new Map();
5780
6151
  MESH_WORKSPACE_CACHE_TTL_MS = 5e3;
@@ -14048,6 +14419,7 @@ function buildMeshAsyncRefineJobs(args) {
14048
14419
  // src/index.ts
14049
14420
  init_mesh_host_ownership();
14050
14421
  init_mesh_events();
14422
+ init_mesh_delivery_policy();
14051
14423
 
14052
14424
  // src/mesh/p2p-relay-failure.ts
14053
14425
  var NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
@@ -14317,8 +14689,8 @@ async function detectIDEs(providerLoader) {
14317
14689
  if (existsSync15(bundledCli)) resolvedCli = bundledCli;
14318
14690
  }
14319
14691
  if (!resolvedCli && appPath && os29 === "win32") {
14320
- const { dirname: dirname11 } = await import("path");
14321
- const appDir = dirname11(appPath);
14692
+ const { dirname: dirname12 } = await import("path");
14693
+ const appDir = dirname12(appPath);
14322
14694
  const candidates = [
14323
14695
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
14324
14696
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -20379,7 +20751,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
20379
20751
  import * as fs6 from "fs";
20380
20752
  import * as os8 from "os";
20381
20753
  import * as path13 from "path";
20382
- import { randomUUID as randomUUID7 } from "crypto";
20754
+ import { randomUUID as randomUUID8 } from "crypto";
20383
20755
  init_logger();
20384
20756
 
20385
20757
  // src/logging/debug-trace.ts
@@ -21919,7 +22291,7 @@ function safeBundleIdSegment(value, fallback) {
21919
22291
  function createChatDebugBundleId(targetSessionId) {
21920
22292
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.]/g, "").replace("T", "T").replace("Z", "Z");
21921
22293
  const sessionSegment = safeBundleIdSegment(targetSessionId, "unknown-session");
21922
- return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID7().slice(0, 8)}`;
22294
+ return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID8().slice(0, 8)}`;
21923
22295
  }
21924
22296
  function buildChatDebugBundleSummary(bundle) {
21925
22297
  const target = bundle.target && typeof bundle.target === "object" ? bundle.target : {};
@@ -26801,7 +27173,15 @@ var SpecDriver = class {
26801
27173
  * explicit wake-up there's nothing to trigger the busy → idle
26802
27174
  * downshift. */
26803
27175
  busyExpiryTimer = null;
27176
+ /** Pending idle-commit timer. Armed when the evaluator first returns idle;
27177
+ * fires after idle_hold_ms if no non-idle reading has cancelled it. */
27178
+ idleHoldTimer = null;
27179
+ /** State snapshot captured when the idle hold was armed — emitted on commit. */
27180
+ pendingIdleState = null;
26804
27181
  specWatcher = null;
27182
+ /** Ring buffer of committed state transitions (max 50). */
27183
+ stateHistory = [];
27184
+ prevStateAt = 0;
26805
27185
  /** Subscribe to outbound events. Returns an unsubscribe fn. */
26806
27186
  subscribe(listener) {
26807
27187
  this.listeners.add(listener);
@@ -26852,9 +27232,40 @@ var SpecDriver = class {
26852
27232
  shutdown() {
26853
27233
  for (const t of this.delegateTimers.values()) clearTimeout(t);
26854
27234
  this.delegateTimers.clear();
27235
+ this.cancelIdleHold();
27236
+ if (this.busyExpiryTimer) {
27237
+ clearTimeout(this.busyExpiryTimer);
27238
+ this.busyExpiryTimer = null;
27239
+ }
26855
27240
  this.specWatcher?.close();
26856
27241
  this.adapter.kill();
26857
27242
  }
27243
+ cancelIdleHold() {
27244
+ if (this.idleHoldTimer) {
27245
+ clearTimeout(this.idleHoldTimer);
27246
+ this.idleHoldTimer = null;
27247
+ }
27248
+ this.pendingIdleState = null;
27249
+ }
27250
+ pushHistory(stateId, label) {
27251
+ const now = Date.now();
27252
+ const durationMs = this.prevStateAt > 0 ? now - this.prevStateAt : 0;
27253
+ this.prevStateAt = now;
27254
+ this.stateHistory.push({ stateId, label, at: now, durationMs });
27255
+ if (this.stateHistory.length > 50) this.stateHistory.shift();
27256
+ }
27257
+ getStateHistory() {
27258
+ return this.stateHistory;
27259
+ }
27260
+ getLastBusyAt() {
27261
+ return this.lastBusyAt;
27262
+ }
27263
+ hasIdleHoldPending() {
27264
+ return this.idleHoldTimer !== null;
27265
+ }
27266
+ getSpecPath() {
27267
+ return this.opts.specPath;
27268
+ }
26858
27269
  // ────────────────────────────────────────────────────────────────────
26859
27270
  // Loading & adapter wiring
26860
27271
  // ────────────────────────────────────────────────────────────────────
@@ -26878,7 +27289,10 @@ var SpecDriver = class {
26878
27289
  }
26879
27290
  armSpecWatcher() {
26880
27291
  try {
26881
- this.specWatcher = fs10.watch(this.opts.specPath, { persistent: false }, () => {
27292
+ const dir = path19.dirname(this.opts.specPath);
27293
+ const base = path19.basename(this.opts.specPath);
27294
+ this.specWatcher = fs10.watch(dir, { persistent: false }, (_event, filename) => {
27295
+ if (filename && filename !== base) return;
26882
27296
  const res = loadSpec(this.opts.specPath);
26883
27297
  if (!res.ok) {
26884
27298
  this.emit({ kind: "spec_error", errors: res.errors });
@@ -26962,7 +27376,46 @@ var SpecDriver = class {
26962
27376
  if (evState.id === "busy") {
26963
27377
  this.lastBusyAt = Date.now();
26964
27378
  this.lastBusyState = evState;
27379
+ this.cancelIdleHold();
26965
27380
  this.scheduleBusyExpiry(busyWakeMs);
27381
+ } else if (evState.id !== this.currentStateId && evState.id !== "busy") {
27382
+ if (evState.id !== (this.spec.default_state ?? "idle")) {
27383
+ this.cancelIdleHold();
27384
+ }
27385
+ }
27386
+ const idleHoldMs = this.spec.debounce?.idle_hold_ms ?? 0;
27387
+ const isIdleState = evState.id === (this.spec.default_state ?? "idle");
27388
+ if (isIdleState && idleHoldMs > 0 && this.currentStateId !== evState.id) {
27389
+ if (!this.idleHoldTimer) {
27390
+ this.pendingIdleState = evState;
27391
+ this.idleHoldTimer = setTimeout(() => {
27392
+ this.idleHoldTimer = null;
27393
+ const committed = this.pendingIdleState;
27394
+ this.pendingIdleState = null;
27395
+ if (!committed) return;
27396
+ LOG.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] idleHold committed after ${idleHoldMs}ms`);
27397
+ this.currentStateId = committed.id;
27398
+ this.currentEval = ev;
27399
+ this.pushHistory(committed.id, committed.label);
27400
+ this.emit({
27401
+ kind: "state_changed",
27402
+ state: committed,
27403
+ modal: null,
27404
+ controls: ev.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
27405
+ });
27406
+ this.armOrCancelDelegateTimers(committed.id);
27407
+ if (this.opts.emitTrace) this.emit({ kind: "spec_trace", entries: ev.trace });
27408
+ }, idleHoldMs);
27409
+ }
27410
+ this.currentEval = ev;
27411
+ const graceMs2 = this.spec.debounce?.startup_grace_ms ?? STARTUP_GRACE_MS;
27412
+ if (!this.idleSeenOnce && Date.now() - this.startedAtMs >= graceMs2) {
27413
+ this.idleSeenOnce = true;
27414
+ const queued = this.pendingSends.splice(0);
27415
+ for (const text of queued) setTimeout(() => this.actuallySendMessage(text), 50);
27416
+ }
27417
+ if (this.pickerInProgress) this.tryAdvancePicker(screen);
27418
+ return;
26966
27419
  }
26967
27420
  const changed = forceEmit || evState.id !== this.currentStateId || !shallowSameModal(ev, this.currentEval) || !shallowSameControls(ev, this.currentEval);
26968
27421
  if (this.pickerInProgress) this.tryAdvancePicker(screen);
@@ -26978,6 +27431,7 @@ var SpecDriver = class {
26978
27431
  }
26979
27432
  if (changed) {
26980
27433
  this.currentStateId = evState.id;
27434
+ this.pushHistory(evState.id, evState.label);
26981
27435
  this.emit({
26982
27436
  kind: "state_changed",
26983
27437
  state: evState,
@@ -27411,7 +27865,11 @@ var SpecCliAdapter = class {
27411
27865
  activeInteractivePrompt: this.activeInteractivePrompt,
27412
27866
  exited: this.exited,
27413
27867
  screen,
27414
- sections
27868
+ sections,
27869
+ stateHistory: this.driver.getStateHistory(),
27870
+ idleHoldPending: this.driver.hasIdleHoldPending(),
27871
+ lastBusyAt: this.driver.getLastBusyAt(),
27872
+ specPath: this.driver.getSpecPath()
27415
27873
  };
27416
27874
  }
27417
27875
  getRuntimeMetadata() {
@@ -32955,17 +33413,17 @@ function parseTranscriptFile(filePath, sessionId, workspaceFallback) {
32955
33413
  }
32956
33414
  function readSession(sessionPath) {
32957
33415
  if (!sessionPath || !path25.isAbsolute(sessionPath)) return null;
32958
- const basename13 = path25.basename(sessionPath, ".jsonl");
32959
- if (!isSafeSessionId(basename13)) return null;
33416
+ const basename14 = path25.basename(sessionPath, ".jsonl");
33417
+ if (!isSafeSessionId(basename14)) return null;
32960
33418
  if (!fs14.existsSync(sessionPath)) return null;
32961
33419
  const sourceMtimeMs = statMtimeMs(sessionPath);
32962
- const messages = parseTranscriptFile(sessionPath, basename13);
33420
+ const messages = parseTranscriptFile(sessionPath, basename14);
32963
33421
  if (messages.length === 0) return null;
32964
33422
  const firstSystem = messages.find((m) => m.kind === "session_start");
32965
33423
  const workspace = firstSystem?.workspace || firstSystem?.content || void 0;
32966
33424
  return {
32967
33425
  messages,
32968
- providerSessionId: basename13,
33426
+ providerSessionId: basename14,
32969
33427
  source: "provider-native",
32970
33428
  sourcePath: sessionPath,
32971
33429
  sourceMtimeMs,
@@ -33167,8 +33625,8 @@ function readSession2(sessionPath) {
33167
33625
  if (!fs15.existsSync(sessionPath)) return null;
33168
33626
  const meta = readSessionMeta(sessionPath);
33169
33627
  const metaId = String(meta?.id ?? "").trim();
33170
- const basename13 = path26.basename(sessionPath, ".jsonl");
33171
- const uuidMatch = basename13.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
33628
+ const basename14 = path26.basename(sessionPath, ".jsonl");
33629
+ const uuidMatch = basename14.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
33172
33630
  const filenameUuid2 = uuidMatch ? uuidMatch[1] : "";
33173
33631
  if (metaId && filenameUuid2 && metaId !== filenameUuid2) return null;
33174
33632
  const sessionId = metaId || filenameUuid2;
@@ -40380,6 +40838,21 @@ var DaemonCommandRouter = class {
40380
40838
  } : null
40381
40839
  };
40382
40840
  }
40841
+ case "get_spec_debug": {
40842
+ const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
40843
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
40844
+ const target = this.deps.sessionRegistry.get(sessionId);
40845
+ if (!target) return { success: false, error: "Session not found", sessionId };
40846
+ const adapter = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
40847
+ const snapshot = adapter && typeof adapter.getDebugSnapshot === "function" ? adapter.getDebugSnapshot() : null;
40848
+ return {
40849
+ success: true,
40850
+ sessionId,
40851
+ providerType: target.providerType,
40852
+ isSpecProvider: snapshot !== null,
40853
+ snapshot
40854
+ };
40855
+ }
40383
40856
  // ── User-level coordinator-prompt files (~/.adhdev/coordinator-prompts/).
40384
40857
  // These live on this daemon's filesystem and never sync to the
40385
40858
  // cloud / other daemons — they're per-machine config. The
@@ -41262,9 +41735,9 @@ var DaemonCommandRouter = class {
41262
41735
  });
41263
41736
  let node;
41264
41737
  if (meshRecord.inline) {
41265
- const { randomUUID: randomUUID11 } = await import("crypto");
41738
+ const { randomUUID: randomUUID12 } = await import("crypto");
41266
41739
  node = {
41267
- id: `node_${randomUUID11().replace(/-/g, "")}`,
41740
+ id: `node_${randomUUID12().replace(/-/g, "")}`,
41268
41741
  workspace: result.worktreePath,
41269
41742
  repoRoot: result.worktreePath,
41270
41743
  daemonId: sourceNode.daemonId,
@@ -41707,7 +42180,7 @@ ${ptyResult.output.slice(-2e3)}`);
41707
42180
  };
41708
42181
  }
41709
42182
  const { existsSync: existsSync39, readFileSync: readFileSync33, writeFileSync: writeFileSync21, copyFileSync: copyFileSync4, mkdirSync: mkdirSync19 } = await import("fs");
41710
- const { dirname: dirname11 } = await import("path");
42183
+ const { dirname: dirname12 } = await import("path");
41711
42184
  const mcpConfigPath = coordinatorSetup.configPath;
41712
42185
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
41713
42186
  let hermesBaseConfig = null;
@@ -41742,7 +42215,7 @@ ${ptyResult.output.slice(-2e3)}`);
41742
42215
  };
41743
42216
  }
41744
42217
  try {
41745
- mkdirSync19(dirname11(mcpConfigPath), { recursive: true });
42218
+ mkdirSync19(dirname12(mcpConfigPath), { recursive: true });
41746
42219
  } catch (error) {
41747
42220
  const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
41748
42221
  LOG.error("MeshCoordinator", message);
@@ -41752,7 +42225,7 @@ ${ptyResult.output.slice(-2e3)}`);
41752
42225
  const hadExistingMcpConfig = existsSync39(mcpConfigPath);
41753
42226
  let existingMcpConfig = hermesBaseConfig?.config || {};
41754
42227
  if (hermesBaseConfig) {
41755
- copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname11(mcpConfigPath));
42228
+ copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname12(mcpConfigPath));
41756
42229
  }
41757
42230
  if (hadExistingMcpConfig) {
41758
42231
  try {
@@ -41790,7 +42263,7 @@ ${ptyResult.output.slice(-2e3)}`);
41790
42263
  const cliArgs = [];
41791
42264
  const launchEnv = {};
41792
42265
  if (configFormat === "hermes_config_yaml") {
41793
- launchEnv.HERMES_HOME = dirname11(mcpConfigPath);
42266
+ launchEnv.HERMES_HOME = dirname12(mcpConfigPath);
41794
42267
  launchEnv.HERMES_IGNORE_USER_CONFIG = "";
41795
42268
  }
41796
42269
  let autoImportContextFilePath;
@@ -49714,7 +50187,7 @@ var SessionHostPtyTransportFactory = class {
49714
50187
  };
49715
50188
 
49716
50189
  // src/cli-adapters/raw-terminal-io.ts
49717
- import { randomUUID as randomUUID10 } from "crypto";
50190
+ import { randomUUID as randomUUID11 } from "crypto";
49718
50191
  import {
49719
50192
  SessionHostClient as SessionHostClient2
49720
50193
  } from "@adhdev/session-host-core";
@@ -49814,7 +50287,7 @@ var RawTerminalAttachment = class _RawTerminalAttachment {
49814
50287
  const sessionId = String(options.sessionId || "").trim();
49815
50288
  if (!sessionId) throw new Error("sessionId is required");
49816
50289
  const mode = options.mode || "read";
49817
- const clientId = options.clientId || `raw-terminal-${process.pid}-${randomUUID10().slice(0, 8)}`;
50290
+ const clientId = options.clientId || `raw-terminal-${process.pid}-${randomUUID11().slice(0, 8)}`;
49818
50291
  const client = options.client || new SessionHostClient2({ endpoint: options.endpoint });
49819
50292
  await client.connect();
49820
50293
  const attachResponse = await client.request({
@@ -50732,11 +51205,11 @@ init_parse_session();
50732
51205
  // src/providers/sdk/v1/fixture-tooling/replay.ts
50733
51206
  init_provider_cli_shared();
50734
51207
  import { readFileSync as readFileSync31 } from "fs";
50735
- import { dirname as dirname9, resolve as resolve21 } from "path";
51208
+ import { dirname as dirname10, resolve as resolve21 } from "path";
50736
51209
 
50737
51210
  // src/providers/sdk/v1/validators/taint.ts
50738
51211
  import { readFileSync as readFileSync32, existsSync as existsSync38 } from "fs";
50739
- import { resolve as resolve22, dirname as dirname10, join as join43 } from "path";
51212
+ import { resolve as resolve22, dirname as dirname11, join as join43 } from "path";
50740
51213
 
50741
51214
  // src/providers/sdk/v1/validators/index.ts
50742
51215
  init_manifest();
@@ -50930,6 +51403,7 @@ export {
50930
51403
  createInteractionId,
50931
51404
  createMesh,
50932
51405
  createNativeHistoryDispatcher,
51406
+ createSessionDelivery,
50933
51407
  createWorktree,
50934
51408
  deleteMesh,
50935
51409
  detectAllVersions,
@@ -50953,6 +51427,7 @@ export {
50953
51427
  forwardAgentStreamsToIdeInstance,
50954
51428
  getAIExtensions,
50955
51429
  getActiveDirectDispatches,
51430
+ getActiveSessionDeliveries,
50956
51431
  getAvailableIdeIds,
50957
51432
  getCoordinatorForSession,
50958
51433
  getCurrentDaemonLogPath,
@@ -50975,6 +51450,7 @@ export {
50975
51450
  getQueue,
50976
51451
  getRecentActivity,
50977
51452
  getRecentCommands,
51453
+ getRecentCompletionConflicts,
50978
51454
  getRecentDebugTrace,
50979
51455
  getRecentLogs,
50980
51456
  getSavedProviderSessions,
@@ -51062,6 +51538,7 @@ export {
51062
51538
  readLedgerEntries,
51063
51539
  readLedgerSlice,
51064
51540
  reconcileDirectDispatchCompletionFromTranscript,
51541
+ recordCompletionConflict,
51065
51542
  recordDebugTrace,
51066
51543
  registerExtensionProviders,
51067
51544
  registerMeshCoordinator,
@@ -51075,6 +51552,7 @@ export {
51075
51552
  resolveChatMessageKind,
51076
51553
  resolveCurrentGlobalInstallSurface,
51077
51554
  resolveDebugRuntimeConfig,
51555
+ resolveDeliveryDecision,
51078
51556
  resolveGitRepository,
51079
51557
  resolveMeshHostStatus,
51080
51558
  resolveMeshRefineValidationPlan,
@@ -51104,6 +51582,7 @@ export {
51104
51582
  updateDirectDispatchStatus,
51105
51583
  updateMesh,
51106
51584
  updateNode,
51585
+ updateSessionDeliveryStatus,
51107
51586
  updateSessionTaskStatus,
51108
51587
  updateTaskStatus,
51109
51588
  upsertSavedProviderSession,