@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.js CHANGED
@@ -3424,6 +3424,49 @@ var init_mesh_runtime_store = __esm({
3424
3424
  metadata TEXT,
3425
3425
  PRIMARY KEY (node_id, session_id)
3426
3426
  );
3427
+
3428
+ CREATE TABLE IF NOT EXISTS mesh_session_delivery (
3429
+ id TEXT PRIMARY KEY,
3430
+ mesh_id TEXT NOT NULL,
3431
+ node_id TEXT,
3432
+ session_id TEXT,
3433
+ provider_type TEXT,
3434
+ task_id TEXT,
3435
+ kind TEXT NOT NULL,
3436
+ priority INTEGER NOT NULL DEFAULT 0,
3437
+ message TEXT NOT NULL,
3438
+ status TEXT NOT NULL DEFAULT 'queued',
3439
+ deliver_after TEXT,
3440
+ expires_at TEXT,
3441
+ attempt_count INTEGER NOT NULL DEFAULT 0,
3442
+ source_coordinator_session_id TEXT,
3443
+ source_coordinator_daemon_id TEXT,
3444
+ last_error TEXT,
3445
+ created_at TEXT NOT NULL,
3446
+ updated_at TEXT NOT NULL
3447
+ );
3448
+
3449
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_mesh_status
3450
+ ON mesh_session_delivery(mesh_id, status, created_at);
3451
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_session
3452
+ ON mesh_session_delivery(mesh_id, session_id, status);
3453
+ CREATE INDEX IF NOT EXISTS idx_mesh_session_delivery_task
3454
+ ON mesh_session_delivery(mesh_id, task_id);
3455
+
3456
+ CREATE TABLE IF NOT EXISTS mesh_completion_conflicts (
3457
+ id TEXT PRIMARY KEY,
3458
+ mesh_id TEXT NOT NULL,
3459
+ fingerprint TEXT NOT NULL,
3460
+ conflicting_task_id TEXT,
3461
+ conflicting_session_id TEXT,
3462
+ original_task_id TEXT,
3463
+ original_session_id TEXT,
3464
+ event TEXT NOT NULL,
3465
+ created_at TEXT NOT NULL
3466
+ );
3467
+
3468
+ CREATE INDEX IF NOT EXISTS idx_mesh_completion_conflicts_mesh
3469
+ ON mesh_completion_conflicts(mesh_id, created_at);
3427
3470
  `);
3428
3471
  }
3429
3472
  hasCompletionFingerprint(fingerprint) {
@@ -3748,6 +3791,131 @@ var init_mesh_runtime_store = __esm({
3748
3791
  pruneExpiredRemoteIdleSessions() {
3749
3792
  this.db.prepare("DELETE FROM remote_idle_sessions WHERE expires_at <= ?").run(Date.now());
3750
3793
  }
3794
+ // ── Session Delivery Queue ───────────────────────────────────────────────
3795
+ insertSessionDelivery(entry) {
3796
+ this.db.prepare(`
3797
+ INSERT OR REPLACE INTO mesh_session_delivery (
3798
+ id, mesh_id, node_id, session_id, provider_type, task_id, kind, priority,
3799
+ message, status, deliver_after, expires_at, attempt_count,
3800
+ source_coordinator_session_id, source_coordinator_daemon_id,
3801
+ last_error, created_at, updated_at
3802
+ ) VALUES (
3803
+ @id, @meshId, @nodeId, @sessionId, @providerType, @taskId, @kind, @priority,
3804
+ @message, @status, @deliverAfter, @expiresAt, 0,
3805
+ @sourceCoordinatorSessionId, @sourceCoordinatorDaemonId,
3806
+ NULL, @createdAt, @updatedAt
3807
+ )
3808
+ `).run({
3809
+ id: entry.id,
3810
+ meshId: entry.meshId,
3811
+ nodeId: entry.nodeId ?? null,
3812
+ sessionId: entry.sessionId ?? null,
3813
+ providerType: entry.providerType ?? null,
3814
+ taskId: entry.taskId ?? null,
3815
+ kind: entry.kind,
3816
+ priority: entry.priority ?? 0,
3817
+ message: entry.message,
3818
+ status: entry.status,
3819
+ deliverAfter: entry.deliverAfter ?? null,
3820
+ expiresAt: entry.expiresAt ?? null,
3821
+ sourceCoordinatorSessionId: entry.sourceCoordinatorSessionId ?? null,
3822
+ sourceCoordinatorDaemonId: entry.sourceCoordinatorDaemonId ?? null,
3823
+ createdAt: entry.createdAt,
3824
+ updatedAt: entry.updatedAt
3825
+ });
3826
+ this.maybeCheckpointWal();
3827
+ }
3828
+ updateSessionDeliveryStatus(id, status, opts) {
3829
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3830
+ if (opts?.incrementAttempt) {
3831
+ this.db.prepare(`
3832
+ UPDATE mesh_session_delivery
3833
+ SET status = @status, last_error = @lastError, attempt_count = attempt_count + 1, updated_at = @updatedAt
3834
+ WHERE id = @id
3835
+ `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
3836
+ } else {
3837
+ this.db.prepare(`
3838
+ UPDATE mesh_session_delivery
3839
+ SET status = @status, last_error = @lastError, updated_at = @updatedAt
3840
+ WHERE id = @id
3841
+ `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
3842
+ }
3843
+ }
3844
+ getActiveSessionDeliveries(meshId, sessionId) {
3845
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3846
+ 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`;
3847
+ const rows = sessionId ? this.db.prepare(sql).all(meshId, sessionId, now) : this.db.prepare(sql).all(meshId, now);
3848
+ return rows.map((r) => ({
3849
+ id: r.id,
3850
+ meshId: r.mesh_id,
3851
+ nodeId: r.node_id,
3852
+ sessionId: r.session_id,
3853
+ providerType: r.provider_type,
3854
+ taskId: r.task_id,
3855
+ kind: r.kind,
3856
+ priority: r.priority,
3857
+ message: r.message,
3858
+ status: r.status,
3859
+ deliverAfter: r.deliver_after,
3860
+ expiresAt: r.expires_at,
3861
+ attemptCount: r.attempt_count,
3862
+ sourceCoordinatorSessionId: r.source_coordinator_session_id,
3863
+ sourceCoordinatorDaemonId: r.source_coordinator_daemon_id,
3864
+ lastError: r.last_error,
3865
+ createdAt: r.created_at,
3866
+ updatedAt: r.updated_at
3867
+ }));
3868
+ }
3869
+ expireStaleSessionDeliveries(meshId) {
3870
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3871
+ this.db.prepare(`
3872
+ UPDATE mesh_session_delivery
3873
+ SET status = 'expired', updated_at = ?
3874
+ WHERE mesh_id = ? AND expires_at IS NOT NULL AND expires_at <= ?
3875
+ AND status NOT IN ('delivered','completed','failed','expired','cancelled')
3876
+ `).run(now, meshId, now);
3877
+ }
3878
+ deleteSessionDeliveries(meshId) {
3879
+ this.db.prepare("DELETE FROM mesh_session_delivery WHERE mesh_id = ?").run(meshId);
3880
+ }
3881
+ // ── Completion Conflict Diagnostics ──────────────────────────────────────
3882
+ recordCompletionConflict(entry) {
3883
+ this.db.prepare(`
3884
+ INSERT OR IGNORE INTO mesh_completion_conflicts
3885
+ (id, mesh_id, fingerprint, conflicting_task_id, conflicting_session_id,
3886
+ original_task_id, original_session_id, event, created_at)
3887
+ VALUES (@id, @meshId, @fingerprint, @conflictingTaskId, @conflictingSessionId,
3888
+ @originalTaskId, @originalSessionId, @event, @createdAt)
3889
+ `).run({
3890
+ id: entry.id,
3891
+ meshId: entry.meshId,
3892
+ fingerprint: entry.fingerprint,
3893
+ conflictingTaskId: entry.conflictingTaskId ?? null,
3894
+ conflictingSessionId: entry.conflictingSessionId ?? null,
3895
+ originalTaskId: entry.originalTaskId ?? null,
3896
+ originalSessionId: entry.originalSessionId ?? null,
3897
+ event: entry.event,
3898
+ createdAt: entry.createdAt
3899
+ });
3900
+ this.maybeCheckpointWal();
3901
+ }
3902
+ getRecentCompletionConflicts(meshId, limitMs = 60 * 60 * 1e3) {
3903
+ const cutoff = new Date(Date.now() - limitMs).toISOString();
3904
+ const rows = this.db.prepare(
3905
+ "SELECT * FROM mesh_completion_conflicts WHERE mesh_id = ? AND created_at >= ? ORDER BY created_at DESC LIMIT 50"
3906
+ ).all(meshId, cutoff);
3907
+ return rows.map((r) => ({
3908
+ id: r.id,
3909
+ meshId: r.mesh_id,
3910
+ fingerprint: r.fingerprint,
3911
+ conflictingTaskId: r.conflicting_task_id,
3912
+ conflictingSessionId: r.conflicting_session_id,
3913
+ originalTaskId: r.original_task_id,
3914
+ originalSessionId: r.original_session_id,
3915
+ event: r.event,
3916
+ createdAt: r.created_at
3917
+ }));
3918
+ }
3751
3919
  };
3752
3920
  }
3753
3921
  });
@@ -4162,6 +4330,167 @@ var init_cli_detector = __esm({
4162
4330
  }
4163
4331
  });
4164
4332
 
4333
+ // src/mesh/mesh-delivery-policy.ts
4334
+ function resolveDeliveryDecision(sessionStatus, opts) {
4335
+ const status = (sessionStatus || "").trim().toLowerCase();
4336
+ if (!status) {
4337
+ return {
4338
+ decision: "rejected",
4339
+ reason: "unknown_session_status",
4340
+ message: "Session status is unknown. Delivery rejected (fail-closed). Use mesh_launch_session to start a fresh session."
4341
+ };
4342
+ }
4343
+ if (IMMEDIATE_DELIVERY_STATUSES.has(status)) {
4344
+ return {
4345
+ decision: "immediate",
4346
+ reason: `session_${status}`,
4347
+ message: `Session is ${status} \u2014 delivery allowed immediately.`
4348
+ };
4349
+ }
4350
+ if (BUSY_DELIVERY_STATUSES.has(status)) {
4351
+ if (opts?.allowBusyInjection) {
4352
+ return {
4353
+ decision: "immediate",
4354
+ reason: `session_${status}_busy_injection_allowed`,
4355
+ message: `Session is ${status} but provider supports busy injection. Delivered immediately.`
4356
+ };
4357
+ }
4358
+ if (status === "waiting_approval" && opts?.kind === "approval") {
4359
+ return {
4360
+ decision: "immediate",
4361
+ reason: "session_waiting_approval_approval_message",
4362
+ message: "Session is waiting for approval \u2014 approval message delivered immediately."
4363
+ };
4364
+ }
4365
+ return {
4366
+ decision: "queued",
4367
+ reason: `session_${status}_busy`,
4368
+ message: `Session is ${status}. Task queued for delivery when session becomes idle. Do not inject directly into a busy session.`
4369
+ };
4370
+ }
4371
+ if (TERMINAL_DELIVERY_STATUSES.has(status)) {
4372
+ return {
4373
+ decision: "rejected",
4374
+ reason: `session_${status}_terminal`,
4375
+ message: `Session is ${status} (terminal). Delivery rejected. Launch a new session before dispatching tasks.`
4376
+ };
4377
+ }
4378
+ return {
4379
+ decision: "rejected",
4380
+ reason: "unrecognized_session_status",
4381
+ message: `Session status '${sessionStatus}' is not recognized. Delivery rejected (fail-closed). Inspect session state before retrying.`
4382
+ };
4383
+ }
4384
+ function createSessionDelivery(opts) {
4385
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4386
+ const id = (0, import_crypto6.randomUUID)();
4387
+ const record = {
4388
+ id,
4389
+ meshId: opts.meshId,
4390
+ nodeId: opts.nodeId,
4391
+ sessionId: opts.sessionId,
4392
+ providerType: opts.providerType,
4393
+ taskId: opts.taskId,
4394
+ kind: opts.kind,
4395
+ priority: opts.priority ?? 0,
4396
+ message: opts.message,
4397
+ status: opts.status,
4398
+ deliverAfter: opts.deliverAfter,
4399
+ expiresAt: opts.expiresAt,
4400
+ attemptCount: 0,
4401
+ sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId,
4402
+ sourceCoordinatorDaemonId: opts.sourceCoordinatorDaemonId,
4403
+ createdAt: now,
4404
+ updatedAt: now
4405
+ };
4406
+ MeshRuntimeStore.getInstance().insertSessionDelivery({
4407
+ id,
4408
+ meshId: opts.meshId,
4409
+ nodeId: opts.nodeId,
4410
+ sessionId: opts.sessionId,
4411
+ providerType: opts.providerType,
4412
+ taskId: opts.taskId,
4413
+ kind: opts.kind,
4414
+ priority: opts.priority ?? 0,
4415
+ message: opts.message,
4416
+ status: opts.status,
4417
+ deliverAfter: opts.deliverAfter,
4418
+ expiresAt: opts.expiresAt,
4419
+ sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId,
4420
+ sourceCoordinatorDaemonId: opts.sourceCoordinatorDaemonId,
4421
+ createdAt: now,
4422
+ updatedAt: now
4423
+ });
4424
+ return record;
4425
+ }
4426
+ function updateSessionDeliveryStatus(id, status, opts) {
4427
+ try {
4428
+ MeshRuntimeStore.getInstance().updateSessionDeliveryStatus(id, status, opts);
4429
+ } catch {
4430
+ }
4431
+ }
4432
+ function getActiveSessionDeliveries(meshId, sessionId) {
4433
+ try {
4434
+ return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(meshId, sessionId);
4435
+ } catch {
4436
+ return [];
4437
+ }
4438
+ }
4439
+ function recordCompletionConflict(opts) {
4440
+ try {
4441
+ MeshRuntimeStore.getInstance().recordCompletionConflict({
4442
+ id: (0, import_crypto6.randomUUID)(),
4443
+ meshId: opts.meshId,
4444
+ fingerprint: opts.fingerprint,
4445
+ conflictingTaskId: opts.conflictingTaskId,
4446
+ conflictingSessionId: opts.conflictingSessionId,
4447
+ originalTaskId: opts.originalTaskId,
4448
+ originalSessionId: opts.originalSessionId,
4449
+ event: opts.event,
4450
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
4451
+ });
4452
+ } catch {
4453
+ }
4454
+ }
4455
+ function getRecentCompletionConflicts(meshId, limitMs) {
4456
+ try {
4457
+ return MeshRuntimeStore.getInstance().getRecentCompletionConflicts(meshId, limitMs);
4458
+ } catch {
4459
+ return [];
4460
+ }
4461
+ }
4462
+ var import_crypto6, IMMEDIATE_DELIVERY_STATUSES, BUSY_DELIVERY_STATUSES, TERMINAL_DELIVERY_STATUSES;
4463
+ var init_mesh_delivery_policy = __esm({
4464
+ "src/mesh/mesh-delivery-policy.ts"() {
4465
+ "use strict";
4466
+ import_crypto6 = require("crypto");
4467
+ init_mesh_runtime_store();
4468
+ IMMEDIATE_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4469
+ "idle",
4470
+ "waiting_input",
4471
+ "ready"
4472
+ ]);
4473
+ BUSY_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4474
+ "generating",
4475
+ "running",
4476
+ "streaming",
4477
+ "busy",
4478
+ "starting",
4479
+ "initializing",
4480
+ "waiting_approval"
4481
+ ]);
4482
+ TERMINAL_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
4483
+ "stopped",
4484
+ "failed",
4485
+ "terminated",
4486
+ "exited",
4487
+ "closed",
4488
+ "deleted",
4489
+ "error"
4490
+ ]);
4491
+ }
4492
+ });
4493
+
4165
4494
  // src/mesh/mesh-events.ts
4166
4495
  var mesh_events_exports = {};
4167
4496
  __export(mesh_events_exports, {
@@ -4506,7 +4835,18 @@ function buildMeshCompletionFingerprint(args) {
4506
4835
  function isDuplicateMeshCompletionEvent(args) {
4507
4836
  const fingerprint = buildMeshCompletionFingerprint(args);
4508
4837
  if (!fingerprint) return false;
4509
- if (hasFingerprintSeen(fingerprint)) return true;
4838
+ if (hasFingerprintSeen(fingerprint)) {
4839
+ if (args.taskId) {
4840
+ recordCompletionConflict({
4841
+ meshId: args.meshId,
4842
+ fingerprint,
4843
+ conflictingTaskId: args.taskId,
4844
+ conflictingSessionId: args.sessionId,
4845
+ event: args.event
4846
+ });
4847
+ }
4848
+ return true;
4849
+ }
4510
4850
  recordFingerprintSeen(fingerprint);
4511
4851
  return false;
4512
4852
  }
@@ -4757,20 +5097,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4757
5097
  if (node?.daemonId && components.dispatchMeshCommand) {
4758
5098
  const isLocalNode = components.cliManager.adapters.has(sessionId);
4759
5099
  if (!isLocalNode) {
5100
+ const delivery2 = createSessionDelivery({
5101
+ meshId,
5102
+ nodeId,
5103
+ sessionId,
5104
+ providerType,
5105
+ taskId: task.id,
5106
+ kind: "task",
5107
+ message: task.message,
5108
+ status: "delivering"
5109
+ });
4760
5110
  components.dispatchMeshCommand(node.daemonId, "agent_command", {
4761
5111
  targetSessionId: sessionId,
4762
5112
  cliType: providerType,
4763
5113
  action: "send_chat",
4764
5114
  message: task.message
5115
+ }).then(() => {
5116
+ updateSessionDeliveryStatus(delivery2.id, "delivered");
4765
5117
  }).catch((e) => {
4766
5118
  LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
5119
+ updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
4767
5120
  updateTaskStatus(meshId, task.id, "pending");
4768
5121
  try {
4769
5122
  appendLedgerEntry(meshId, {
4770
5123
  kind: "dispatch_failed",
4771
5124
  nodeId,
4772
5125
  sessionId,
4773
- payload: { taskId: task.id, error: e?.message, retryable: true }
5126
+ payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
4774
5127
  });
4775
5128
  } catch {
4776
5129
  }
@@ -4778,13 +5131,26 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4778
5131
  return true;
4779
5132
  }
4780
5133
  }
5134
+ const delivery = createSessionDelivery({
5135
+ meshId,
5136
+ nodeId,
5137
+ sessionId,
5138
+ providerType,
5139
+ taskId: task.id,
5140
+ kind: "task",
5141
+ message: task.message,
5142
+ status: "delivering"
5143
+ });
4781
5144
  components.cliManager.handleCliCommand("agent_command", {
4782
5145
  targetSessionId: sessionId,
4783
5146
  cliType: providerType,
4784
5147
  action: "send_chat",
4785
5148
  message: task.message
5149
+ }).then(() => {
5150
+ updateSessionDeliveryStatus(delivery.id, "delivered");
4786
5151
  }).catch((e) => {
4787
5152
  LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
5153
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
4788
5154
  updateTaskStatus(meshId, task.id, "failed");
4789
5155
  });
4790
5156
  return true;
@@ -5389,7 +5755,9 @@ function injectMeshSystemMessage(components, args) {
5389
5755
  finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0,
5390
5756
  // Scope dedup to the coordinator daemon so two coordinators for the same mesh
5391
5757
  // don't suppress each other's completion events via shared fingerprint table.
5392
- coordinatorDaemonId: workerCoordinatorDaemonId || void 0
5758
+ coordinatorDaemonId: workerCoordinatorDaemonId || void 0,
5759
+ taskId: readNonEmptyString2(args.metadataEvent.taskId) || void 0,
5760
+ nodeId: eventNodeId || void 0
5393
5761
  });
5394
5762
  if (duplicateCompletion) {
5395
5763
  LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
@@ -5405,7 +5773,9 @@ function injectMeshSystemMessage(components, args) {
5405
5773
  providerSessionId: readNonEmptyString2(args.metadataEvent.providerSessionId) || void 0,
5406
5774
  timestamp: eventTimestamp,
5407
5775
  finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0,
5408
- coordinatorDaemonId: workerCoordinatorDaemonId || void 0
5776
+ coordinatorDaemonId: workerCoordinatorDaemonId || void 0,
5777
+ taskId: readNonEmptyString2(args.metadataEvent.taskId) || void 0,
5778
+ nodeId: eventNodeId || void 0
5409
5779
  });
5410
5780
  if (duplicateStopped) {
5411
5781
  LOG.info("MeshEvents", `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
@@ -5782,6 +6152,7 @@ var init_mesh_events = __esm({
5782
6152
  init_mesh_work_queue();
5783
6153
  init_mesh_runtime_store();
5784
6154
  init_mesh_fast_forward();
6155
+ init_mesh_delivery_policy();
5785
6156
  REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
5786
6157
  meshByWorkspaceCache = /* @__PURE__ */ new Map();
5787
6158
  MESH_WORKSPACE_CACHE_TTL_MS = 5e3;
@@ -11333,6 +11704,7 @@ __export(index_exports, {
11333
11704
  createInteractionId: () => createInteractionId,
11334
11705
  createMesh: () => createMesh,
11335
11706
  createNativeHistoryDispatcher: () => createNativeHistoryDispatcher,
11707
+ createSessionDelivery: () => createSessionDelivery,
11336
11708
  createWorktree: () => createWorktree,
11337
11709
  deleteMesh: () => deleteMesh,
11338
11710
  detectAllVersions: () => detectAllVersions,
@@ -11356,6 +11728,7 @@ __export(index_exports, {
11356
11728
  forwardAgentStreamsToIdeInstance: () => forwardAgentStreamsToIdeInstance,
11357
11729
  getAIExtensions: () => getAIExtensions,
11358
11730
  getActiveDirectDispatches: () => getActiveDirectDispatches,
11731
+ getActiveSessionDeliveries: () => getActiveSessionDeliveries,
11359
11732
  getAvailableIdeIds: () => getAvailableIdeIds,
11360
11733
  getCoordinatorForSession: () => getCoordinatorForSession,
11361
11734
  getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
@@ -11378,6 +11751,7 @@ __export(index_exports, {
11378
11751
  getQueue: () => getQueue,
11379
11752
  getRecentActivity: () => getRecentActivity,
11380
11753
  getRecentCommands: () => getRecentCommands,
11754
+ getRecentCompletionConflicts: () => getRecentCompletionConflicts,
11381
11755
  getRecentDebugTrace: () => getRecentDebugTrace,
11382
11756
  getRecentLogs: () => getRecentLogs,
11383
11757
  getSavedProviderSessions: () => getSavedProviderSessions,
@@ -11465,6 +11839,7 @@ __export(index_exports, {
11465
11839
  readLedgerEntries: () => readLedgerEntries,
11466
11840
  readLedgerSlice: () => readLedgerSlice,
11467
11841
  reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
11842
+ recordCompletionConflict: () => recordCompletionConflict,
11468
11843
  recordDebugTrace: () => recordDebugTrace,
11469
11844
  registerExtensionProviders: () => registerExtensionProviders,
11470
11845
  registerMeshCoordinator: () => registerMeshCoordinator,
@@ -11478,6 +11853,7 @@ __export(index_exports, {
11478
11853
  resolveChatMessageKind: () => resolveChatMessageKind,
11479
11854
  resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
11480
11855
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
11856
+ resolveDeliveryDecision: () => resolveDeliveryDecision,
11481
11857
  resolveGitRepository: () => resolveGitRepository,
11482
11858
  resolveMeshHostStatus: () => resolveMeshHostStatus,
11483
11859
  resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
@@ -11507,6 +11883,7 @@ __export(index_exports, {
11507
11883
  updateDirectDispatchStatus: () => updateDirectDispatchStatus,
11508
11884
  updateMesh: () => updateMesh,
11509
11885
  updateNode: () => updateNode,
11886
+ updateSessionDeliveryStatus: () => updateSessionDeliveryStatus,
11510
11887
  updateSessionTaskStatus: () => updateSessionTaskStatus,
11511
11888
  updateTaskStatus: () => updateTaskStatus,
11512
11889
  upsertSavedProviderSession: () => upsertSavedProviderSession,
@@ -14358,6 +14735,7 @@ function buildMeshAsyncRefineJobs(args) {
14358
14735
  // src/index.ts
14359
14736
  init_mesh_host_ownership();
14360
14737
  init_mesh_events();
14738
+ init_mesh_delivery_policy();
14361
14739
 
14362
14740
  // src/mesh/p2p-relay-failure.ts
14363
14741
  var NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
@@ -14627,8 +15005,8 @@ async function detectIDEs(providerLoader) {
14627
15005
  if ((0, import_fs11.existsSync)(bundledCli)) resolvedCli = bundledCli;
14628
15006
  }
14629
15007
  if (!resolvedCli && appPath && os29 === "win32") {
14630
- const { dirname: dirname11 } = await import("path");
14631
- const appDir = dirname11(appPath);
15008
+ const { dirname: dirname12 } = await import("path");
15009
+ const appDir = dirname12(appPath);
14632
15010
  const candidates = [
14633
15011
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
14634
15012
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -27111,7 +27489,15 @@ var SpecDriver = class {
27111
27489
  * explicit wake-up there's nothing to trigger the busy → idle
27112
27490
  * downshift. */
27113
27491
  busyExpiryTimer = null;
27492
+ /** Pending idle-commit timer. Armed when the evaluator first returns idle;
27493
+ * fires after idle_hold_ms if no non-idle reading has cancelled it. */
27494
+ idleHoldTimer = null;
27495
+ /** State snapshot captured when the idle hold was armed — emitted on commit. */
27496
+ pendingIdleState = null;
27114
27497
  specWatcher = null;
27498
+ /** Ring buffer of committed state transitions (max 50). */
27499
+ stateHistory = [];
27500
+ prevStateAt = 0;
27115
27501
  /** Subscribe to outbound events. Returns an unsubscribe fn. */
27116
27502
  subscribe(listener) {
27117
27503
  this.listeners.add(listener);
@@ -27162,9 +27548,40 @@ var SpecDriver = class {
27162
27548
  shutdown() {
27163
27549
  for (const t of this.delegateTimers.values()) clearTimeout(t);
27164
27550
  this.delegateTimers.clear();
27551
+ this.cancelIdleHold();
27552
+ if (this.busyExpiryTimer) {
27553
+ clearTimeout(this.busyExpiryTimer);
27554
+ this.busyExpiryTimer = null;
27555
+ }
27165
27556
  this.specWatcher?.close();
27166
27557
  this.adapter.kill();
27167
27558
  }
27559
+ cancelIdleHold() {
27560
+ if (this.idleHoldTimer) {
27561
+ clearTimeout(this.idleHoldTimer);
27562
+ this.idleHoldTimer = null;
27563
+ }
27564
+ this.pendingIdleState = null;
27565
+ }
27566
+ pushHistory(stateId, label) {
27567
+ const now = Date.now();
27568
+ const durationMs = this.prevStateAt > 0 ? now - this.prevStateAt : 0;
27569
+ this.prevStateAt = now;
27570
+ this.stateHistory.push({ stateId, label, at: now, durationMs });
27571
+ if (this.stateHistory.length > 50) this.stateHistory.shift();
27572
+ }
27573
+ getStateHistory() {
27574
+ return this.stateHistory;
27575
+ }
27576
+ getLastBusyAt() {
27577
+ return this.lastBusyAt;
27578
+ }
27579
+ hasIdleHoldPending() {
27580
+ return this.idleHoldTimer !== null;
27581
+ }
27582
+ getSpecPath() {
27583
+ return this.opts.specPath;
27584
+ }
27168
27585
  // ────────────────────────────────────────────────────────────────────
27169
27586
  // Loading & adapter wiring
27170
27587
  // ────────────────────────────────────────────────────────────────────
@@ -27188,7 +27605,10 @@ var SpecDriver = class {
27188
27605
  }
27189
27606
  armSpecWatcher() {
27190
27607
  try {
27191
- this.specWatcher = fs10.watch(this.opts.specPath, { persistent: false }, () => {
27608
+ const dir = path19.dirname(this.opts.specPath);
27609
+ const base = path19.basename(this.opts.specPath);
27610
+ this.specWatcher = fs10.watch(dir, { persistent: false }, (_event, filename) => {
27611
+ if (filename && filename !== base) return;
27192
27612
  const res = loadSpec(this.opts.specPath);
27193
27613
  if (!res.ok) {
27194
27614
  this.emit({ kind: "spec_error", errors: res.errors });
@@ -27272,7 +27692,46 @@ var SpecDriver = class {
27272
27692
  if (evState.id === "busy") {
27273
27693
  this.lastBusyAt = Date.now();
27274
27694
  this.lastBusyState = evState;
27695
+ this.cancelIdleHold();
27275
27696
  this.scheduleBusyExpiry(busyWakeMs);
27697
+ } else if (evState.id !== this.currentStateId && evState.id !== "busy") {
27698
+ if (evState.id !== (this.spec.default_state ?? "idle")) {
27699
+ this.cancelIdleHold();
27700
+ }
27701
+ }
27702
+ const idleHoldMs = this.spec.debounce?.idle_hold_ms ?? 0;
27703
+ const isIdleState = evState.id === (this.spec.default_state ?? "idle");
27704
+ if (isIdleState && idleHoldMs > 0 && this.currentStateId !== evState.id) {
27705
+ if (!this.idleHoldTimer) {
27706
+ this.pendingIdleState = evState;
27707
+ this.idleHoldTimer = setTimeout(() => {
27708
+ this.idleHoldTimer = null;
27709
+ const committed = this.pendingIdleState;
27710
+ this.pendingIdleState = null;
27711
+ if (!committed) return;
27712
+ LOG.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] idleHold committed after ${idleHoldMs}ms`);
27713
+ this.currentStateId = committed.id;
27714
+ this.currentEval = ev;
27715
+ this.pushHistory(committed.id, committed.label);
27716
+ this.emit({
27717
+ kind: "state_changed",
27718
+ state: committed,
27719
+ modal: null,
27720
+ controls: ev.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
27721
+ });
27722
+ this.armOrCancelDelegateTimers(committed.id);
27723
+ if (this.opts.emitTrace) this.emit({ kind: "spec_trace", entries: ev.trace });
27724
+ }, idleHoldMs);
27725
+ }
27726
+ this.currentEval = ev;
27727
+ const graceMs2 = this.spec.debounce?.startup_grace_ms ?? STARTUP_GRACE_MS;
27728
+ if (!this.idleSeenOnce && Date.now() - this.startedAtMs >= graceMs2) {
27729
+ this.idleSeenOnce = true;
27730
+ const queued = this.pendingSends.splice(0);
27731
+ for (const text of queued) setTimeout(() => this.actuallySendMessage(text), 50);
27732
+ }
27733
+ if (this.pickerInProgress) this.tryAdvancePicker(screen);
27734
+ return;
27276
27735
  }
27277
27736
  const changed = forceEmit || evState.id !== this.currentStateId || !shallowSameModal(ev, this.currentEval) || !shallowSameControls(ev, this.currentEval);
27278
27737
  if (this.pickerInProgress) this.tryAdvancePicker(screen);
@@ -27288,6 +27747,7 @@ var SpecDriver = class {
27288
27747
  }
27289
27748
  if (changed) {
27290
27749
  this.currentStateId = evState.id;
27750
+ this.pushHistory(evState.id, evState.label);
27291
27751
  this.emit({
27292
27752
  kind: "state_changed",
27293
27753
  state: evState,
@@ -27721,7 +28181,11 @@ var SpecCliAdapter = class {
27721
28181
  activeInteractivePrompt: this.activeInteractivePrompt,
27722
28182
  exited: this.exited,
27723
28183
  screen,
27724
- sections
28184
+ sections,
28185
+ stateHistory: this.driver.getStateHistory(),
28186
+ idleHoldPending: this.driver.hasIdleHoldPending(),
28187
+ lastBusyAt: this.driver.getLastBusyAt(),
28188
+ specPath: this.driver.getSpecPath()
27725
28189
  };
27726
28190
  }
27727
28191
  getRuntimeMetadata() {
@@ -33260,17 +33724,17 @@ function parseTranscriptFile(filePath, sessionId, workspaceFallback) {
33260
33724
  }
33261
33725
  function readSession(sessionPath) {
33262
33726
  if (!sessionPath || !path25.isAbsolute(sessionPath)) return null;
33263
- const basename13 = path25.basename(sessionPath, ".jsonl");
33264
- if (!isSafeSessionId(basename13)) return null;
33727
+ const basename14 = path25.basename(sessionPath, ".jsonl");
33728
+ if (!isSafeSessionId(basename14)) return null;
33265
33729
  if (!fs14.existsSync(sessionPath)) return null;
33266
33730
  const sourceMtimeMs = statMtimeMs(sessionPath);
33267
- const messages = parseTranscriptFile(sessionPath, basename13);
33731
+ const messages = parseTranscriptFile(sessionPath, basename14);
33268
33732
  if (messages.length === 0) return null;
33269
33733
  const firstSystem = messages.find((m) => m.kind === "session_start");
33270
33734
  const workspace = firstSystem?.workspace || firstSystem?.content || void 0;
33271
33735
  return {
33272
33736
  messages,
33273
- providerSessionId: basename13,
33737
+ providerSessionId: basename14,
33274
33738
  source: "provider-native",
33275
33739
  sourcePath: sessionPath,
33276
33740
  sourceMtimeMs,
@@ -33472,8 +33936,8 @@ function readSession2(sessionPath) {
33472
33936
  if (!fs15.existsSync(sessionPath)) return null;
33473
33937
  const meta = readSessionMeta(sessionPath);
33474
33938
  const metaId = String(meta?.id ?? "").trim();
33475
- const basename13 = path26.basename(sessionPath, ".jsonl");
33476
- 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);
33939
+ const basename14 = path26.basename(sessionPath, ".jsonl");
33940
+ 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);
33477
33941
  const filenameUuid2 = uuidMatch ? uuidMatch[1] : "";
33478
33942
  if (metaId && filenameUuid2 && metaId !== filenameUuid2) return null;
33479
33943
  const sessionId = metaId || filenameUuid2;
@@ -40685,6 +41149,21 @@ var DaemonCommandRouter = class {
40685
41149
  } : null
40686
41150
  };
40687
41151
  }
41152
+ case "get_spec_debug": {
41153
+ const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
41154
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
41155
+ const target = this.deps.sessionRegistry.get(sessionId);
41156
+ if (!target) return { success: false, error: "Session not found", sessionId };
41157
+ const adapter = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
41158
+ const snapshot = adapter && typeof adapter.getDebugSnapshot === "function" ? adapter.getDebugSnapshot() : null;
41159
+ return {
41160
+ success: true,
41161
+ sessionId,
41162
+ providerType: target.providerType,
41163
+ isSpecProvider: snapshot !== null,
41164
+ snapshot
41165
+ };
41166
+ }
40688
41167
  // ── User-level coordinator-prompt files (~/.adhdev/coordinator-prompts/).
40689
41168
  // These live on this daemon's filesystem and never sync to the
40690
41169
  // cloud / other daemons — they're per-machine config. The
@@ -41567,9 +42046,9 @@ var DaemonCommandRouter = class {
41567
42046
  });
41568
42047
  let node;
41569
42048
  if (meshRecord.inline) {
41570
- const { randomUUID: randomUUID11 } = await import("crypto");
42049
+ const { randomUUID: randomUUID12 } = await import("crypto");
41571
42050
  node = {
41572
- id: `node_${randomUUID11().replace(/-/g, "")}`,
42051
+ id: `node_${randomUUID12().replace(/-/g, "")}`,
41573
42052
  workspace: result.worktreePath,
41574
42053
  repoRoot: result.worktreePath,
41575
42054
  daemonId: sourceNode.daemonId,
@@ -42012,7 +42491,7 @@ ${ptyResult.output.slice(-2e3)}`);
42012
42491
  };
42013
42492
  }
42014
42493
  const { existsSync: existsSync39, readFileSync: readFileSync33, writeFileSync: writeFileSync21, copyFileSync: copyFileSync4, mkdirSync: mkdirSync19 } = await import("fs");
42015
- const { dirname: dirname11 } = await import("path");
42494
+ const { dirname: dirname12 } = await import("path");
42016
42495
  const mcpConfigPath = coordinatorSetup.configPath;
42017
42496
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
42018
42497
  let hermesBaseConfig = null;
@@ -42047,7 +42526,7 @@ ${ptyResult.output.slice(-2e3)}`);
42047
42526
  };
42048
42527
  }
42049
42528
  try {
42050
- mkdirSync19(dirname11(mcpConfigPath), { recursive: true });
42529
+ mkdirSync19(dirname12(mcpConfigPath), { recursive: true });
42051
42530
  } catch (error) {
42052
42531
  const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
42053
42532
  LOG.error("MeshCoordinator", message);
@@ -42057,7 +42536,7 @@ ${ptyResult.output.slice(-2e3)}`);
42057
42536
  const hadExistingMcpConfig = existsSync39(mcpConfigPath);
42058
42537
  let existingMcpConfig = hermesBaseConfig?.config || {};
42059
42538
  if (hermesBaseConfig) {
42060
- copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname11(mcpConfigPath));
42539
+ copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname12(mcpConfigPath));
42061
42540
  }
42062
42541
  if (hadExistingMcpConfig) {
42063
42542
  try {
@@ -42095,7 +42574,7 @@ ${ptyResult.output.slice(-2e3)}`);
42095
42574
  const cliArgs = [];
42096
42575
  const launchEnv = {};
42097
42576
  if (configFormat === "hermes_config_yaml") {
42098
- launchEnv.HERMES_HOME = dirname11(mcpConfigPath);
42577
+ launchEnv.HERMES_HOME = dirname12(mcpConfigPath);
42099
42578
  launchEnv.HERMES_IGNORE_USER_CONFIG = "";
42100
42579
  }
42101
42580
  let autoImportContextFilePath;
@@ -50017,7 +50496,7 @@ var SessionHostPtyTransportFactory = class {
50017
50496
  };
50018
50497
 
50019
50498
  // src/cli-adapters/raw-terminal-io.ts
50020
- var import_crypto6 = require("crypto");
50499
+ var import_crypto7 = require("crypto");
50021
50500
  var import_session_host_core4 = require("@adhdev/session-host-core");
50022
50501
  var BASE_KEY_SEQUENCES = {
50023
50502
  enter: "\r",
@@ -50115,7 +50594,7 @@ var RawTerminalAttachment = class _RawTerminalAttachment {
50115
50594
  const sessionId = String(options.sessionId || "").trim();
50116
50595
  if (!sessionId) throw new Error("sessionId is required");
50117
50596
  const mode = options.mode || "read";
50118
- const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto6.randomUUID)().slice(0, 8)}`;
50597
+ const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto7.randomUUID)().slice(0, 8)}`;
50119
50598
  const client = options.client || new import_session_host_core4.SessionHostClient({ endpoint: options.endpoint });
50120
50599
  await client.connect();
50121
50600
  const attachResponse = await client.request({
@@ -51229,6 +51708,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51229
51708
  createInteractionId,
51230
51709
  createMesh,
51231
51710
  createNativeHistoryDispatcher,
51711
+ createSessionDelivery,
51232
51712
  createWorktree,
51233
51713
  deleteMesh,
51234
51714
  detectAllVersions,
@@ -51252,6 +51732,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51252
51732
  forwardAgentStreamsToIdeInstance,
51253
51733
  getAIExtensions,
51254
51734
  getActiveDirectDispatches,
51735
+ getActiveSessionDeliveries,
51255
51736
  getAvailableIdeIds,
51256
51737
  getCoordinatorForSession,
51257
51738
  getCurrentDaemonLogPath,
@@ -51274,6 +51755,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51274
51755
  getQueue,
51275
51756
  getRecentActivity,
51276
51757
  getRecentCommands,
51758
+ getRecentCompletionConflicts,
51277
51759
  getRecentDebugTrace,
51278
51760
  getRecentLogs,
51279
51761
  getSavedProviderSessions,
@@ -51361,6 +51843,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51361
51843
  readLedgerEntries,
51362
51844
  readLedgerSlice,
51363
51845
  reconcileDirectDispatchCompletionFromTranscript,
51846
+ recordCompletionConflict,
51364
51847
  recordDebugTrace,
51365
51848
  registerExtensionProviders,
51366
51849
  registerMeshCoordinator,
@@ -51374,6 +51857,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51374
51857
  resolveChatMessageKind,
51375
51858
  resolveCurrentGlobalInstallSurface,
51376
51859
  resolveDebugRuntimeConfig,
51860
+ resolveDeliveryDecision,
51377
51861
  resolveGitRepository,
51378
51862
  resolveMeshHostStatus,
51379
51863
  resolveMeshRefineValidationPlan,
@@ -51403,6 +51887,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
51403
51887
  updateDirectDispatchStatus,
51404
51888
  updateMesh,
51405
51889
  updateNode,
51890
+ updateSessionDeliveryStatus,
51406
51891
  updateSessionTaskStatus,
51407
51892
  updateTaskStatus,
51408
51893
  upsertSavedProviderSession,