@adhdev/daemon-core 0.9.82-rc.451 → 0.9.82-rc.453

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.451",
3
+ "version": "0.9.82-rc.453",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,8 +46,8 @@
46
46
  "author": "vilmire",
47
47
  "license": "AGPL-3.0-or-later",
48
48
  "dependencies": {
49
- "@adhdev/mesh-shared": "0.9.82-rc.451",
50
- "@adhdev/session-host-core": "*",
49
+ "@adhdev/mesh-shared": "0.9.82-rc.453",
50
+ "@adhdev/session-host-core": "0.9.82-rc.453",
51
51
  "@agentclientprotocol/sdk": "^0.16.1",
52
52
  "ajv": "^8.20.0",
53
53
  "ajv-formats": "^3.0.1",
@@ -2,7 +2,7 @@ import type { MeshLedgerEntry } from './mesh-ledger.js';
2
2
  import { appendLedgerEntry } from './mesh-ledger.js';
3
3
  import type { MeshWorkQueueEntry, DirectDispatchRecord } from './mesh-work-queue.js';
4
4
  import { deleteDirectDispatchesByTaskId } from './mesh-work-queue.js';
5
- import { meshNodeIdMatches } from '@adhdev/mesh-shared';
5
+ import { meshNodeIdMatches, daemonIdsEquivalent, sessionIdsEquivalent } from '@adhdev/mesh-shared';
6
6
 
7
7
  export type MeshActiveWorkSource = 'queue' | 'direct';
8
8
  export type MeshActiveWorkStatus = 'pending' | 'assigned' | 'generating' | 'idle' | 'failed' | 'awaiting_approval';
@@ -128,9 +128,9 @@ function sessionStatusFromNodes(nodes: any[] | undefined, nodeId?: string, sessi
128
128
  if (value && typeof value === 'object') candidates.push(value);
129
129
  }
130
130
  const session = candidates.find(item => {
131
- if (typeof item === 'string') return item === sessionId;
131
+ if (typeof item === 'string') return sessionIdsEquivalent(item, sessionId);
132
132
  const id = readString(item?.id) || readString(item?.sessionId) || readString(item?.session_id) || readString(item?.runtimeSessionId) || readString(item?.instanceId);
133
- return id === sessionId;
133
+ return sessionIdsEquivalent(id, sessionId);
134
134
  });
135
135
  if (!session) return { staleReason: 'direct task session is not present in live session records' };
136
136
  if (typeof session === 'string') return {};
@@ -158,8 +158,10 @@ function terminalMatchesDispatch(terminal: MeshLedgerEntry, dispatch: MeshLedger
158
158
  const terminalTaskId = readString(terminal.payload?.taskId);
159
159
  if (terminalTaskId && terminalTaskId === taskId) return true;
160
160
  if (terminalTaskId && terminalTaskId !== taskId) return false;
161
- if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
162
- return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
161
+ if (dispatch.sessionId && sessionIdsEquivalent(terminal.sessionId, dispatch.sessionId)) return true;
162
+ // Node ids can carry interchangeable daemon-id forms (bare `mach_X` vs
163
+ // `daemon_mach_X`); compare under the canonical machine core, not raw `===`.
164
+ return Boolean(dispatch.nodeId && daemonIdsEquivalent(terminal.nodeId, dispatch.nodeId) && !dispatch.sessionId);
163
165
  }
164
166
 
165
167
  function statusFromTerminal(entry: MeshLedgerEntry): MeshActiveWorkStatus {
@@ -15,7 +15,7 @@ import { enqueueUnresolvedDelegateForward, peekUnresolvedDelegateForwards, ackUn
15
15
  import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
16
16
  import { getLastDisplayMessage } from '../status/snapshot.js';
17
17
  import { resolveDelegatedWorkerAutoApprove } from '../repo-mesh-types.js';
18
- import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, type MeshNodeIdentified } from '@adhdev/mesh-shared';
18
+ import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent, type MeshNodeIdentified } from '@adhdev/mesh-shared';
19
19
  import {
20
20
  findRecentTerminalLedgerEvidence,
21
21
  hasDispatchAfterTerminal,
@@ -204,10 +204,9 @@ function hasRecentIntentionalCleanupStop(meshId: string, sessionId?: string, nod
204
204
  const timestamp = new Date(entry.timestamp).getTime();
205
205
  if (!Number.isNaN(timestamp) && timestamp < cutoff) break;
206
206
  if (!isIntentionalCleanupStopEntry(entry)) continue;
207
- // SESSION-ID IS SINGLE-FORM: a session id is one canonical UUID (crypto.randomUUID
208
- // in the provider instance), carried verbatim across daemons no serialization
209
- // variants like node/daemon ids. Exact `===` is correct; no equivalence helper.
210
- if (sessionId && entry.sessionId === sessionId) return true;
207
+ // Session ids are single-form; sessionIdsEquivalent is the one canonical
208
+ // exact-match predicate (see its doc for why no form expansion is needed).
209
+ if (sessionId && sessionIdsEquivalent(entry.sessionId, sessionId)) return true;
211
210
  // Normalized node-id match (P4): the cleanup-stop entry's node id may be stored as
212
211
  // `nodeId` or `node_id` and the `nodeId` arg can be in either form — a raw `===`
213
212
  // would miss a genuine intentional-cleanup entry and fail to suppress the stop event.
@@ -439,9 +438,9 @@ function supersedesTruncatedTerminalSummary(args: {
439
438
  // task surfaces as status='unknown' / terminalKind=null in computeMeshTaskStats.
440
439
  function resolveActiveDirectDispatchTaskId(meshId: string, sessionId: string): string | undefined {
441
440
  try {
442
- // SESSION-ID IS SINGLE-FORM (canonical crypto.randomUUID, carried verbatim across
443
- // daemons) — exact `===` filter is correct; no node-id-style form normalization.
444
- const matches = getActiveDirectDispatches(meshId).filter(d => d.sessionId === sessionId);
441
+ // Session ids are single-form; sessionIdsEquivalent is the one canonical
442
+ // exact-match predicate no node-id-style form normalization needed.
443
+ const matches = getActiveDirectDispatches(meshId).filter(d => sessionIdsEquivalent(d.sessionId, sessionId));
445
444
  if (!matches.length) return undefined;
446
445
  // getActiveDirectDispatches returns rows ordered by dispatched_at ASC; the last is
447
446
  // the most recent dispatch (the re-dispatch / nudge whose completion this is).
@@ -1747,7 +1746,7 @@ export function flushPendingForMeshIdleCoordinators(components: DaemonComponents
1747
1746
  for (const pending of pendingEvents) {
1748
1747
  const wantSession = readNonEmptyString(pending.targetCoordinatorSessionId);
1749
1748
  const targets = wantSession
1750
- ? idleCoordinators.filter(c => c.sessionId === wantSession)
1749
+ ? idleCoordinators.filter(c => sessionIdsEquivalent(c.sessionId, wantSession))
1751
1750
  : idleCoordinators;
1752
1751
  // Not deliverable into an idle target here (wrong/absent session), or a message-less
1753
1752
  // lifecycle event (agent:ready / generating_started carry no coordinatorMessage and
@@ -1826,7 +1825,7 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
1826
1825
  let hasDirectDispatch = false;
1827
1826
  try {
1828
1827
  hasDirectDispatch =
1829
- getActiveDirectDispatches(coordinatorMeshId).some(d => d.sessionId === flushInstanceId)
1828
+ getActiveDirectDispatches(coordinatorMeshId).some(d => sessionIdsEquivalent(d.sessionId, flushInstanceId))
1830
1829
  || hasUnterminalDirectDispatchLedgerEntry(coordinatorMeshId, flushInstanceId);
1831
1830
  } catch { /* best-effort */ }
1832
1831
  if (!hasDirectDispatch) return;
@@ -4,7 +4,7 @@ import { updateDirectDispatchStatus, cleanupTerminalDirectDispatches } from './m
4
4
  import { markSessionDeliveriesTerminal } from './mesh-delivery-policy.js';
5
5
  import { queuePendingMeshCoordinatorEvent } from './mesh-events-pending.js';
6
6
  import { readNonEmptyString, readRecord, resolveEventSessionId, readWorkerResultMetadata, isWeakCompletionEvidence, buildMeshSystemMessage } from './mesh-events-utils.js';
7
- import { meshNodeIdMatches, type MeshNodeIdentified } from '@adhdev/mesh-shared';
7
+ import { meshNodeIdMatches, sessionIdsEquivalent, type MeshNodeIdentified } from '@adhdev/mesh-shared';
8
8
 
9
9
  // ---------------------------------------------------------------------------
10
10
  // Stale direct-dispatch detection & transcript reconciliation
@@ -42,7 +42,7 @@ export function findRecentTerminalLedgerEvidence(args: {
42
42
  for (let i = entries.length - 1; i >= 0; i--) {
43
43
  const entry = entries[i];
44
44
  if (entry.kind !== 'task_completed' && entry.kind !== 'task_failed' && entry.kind !== 'task_stalled') continue;
45
- if (args.sessionId && entry.sessionId === args.sessionId) {
45
+ if (args.sessionId && sessionIdsEquivalent(entry.sessionId, args.sessionId)) {
46
46
  return { id: entry.id, kind: entry.kind, payload: entry.payload || {}, timestamp: entry.timestamp };
47
47
  }
48
48
  // Normalized node-id match (P4): a ledger entry may store its node id as `nodeId`
@@ -70,7 +70,7 @@ export function hasDispatchAfterTerminal(meshId: string, sessionId: string, term
70
70
  if (entry.id === terminalId) pastTerminal = true;
71
71
  continue;
72
72
  }
73
- if (entry.kind === 'task_dispatched' && entry.sessionId === sessionId) return true;
73
+ if (entry.kind === 'task_dispatched' && sessionIdsEquivalent(entry.sessionId, sessionId)) return true;
74
74
  }
75
75
  return false;
76
76
  }
@@ -82,7 +82,7 @@ export function hasUnterminalDirectDispatchLedgerEntry(meshId: string, sessionId
82
82
  const entries = readLedgerEntries(meshId, { tail: 200 });
83
83
  for (let i = entries.length - 1; i >= 0; i--) {
84
84
  const entry = entries[i];
85
- if (entry.sessionId !== sessionId) continue;
85
+ if (!sessionIdsEquivalent(entry.sessionId, sessionId)) continue;
86
86
  if (entry.kind === 'task_completed' || entry.kind === 'task_failed' || entry.kind === 'task_stalled') {
87
87
  return false;
88
88
  }
@@ -109,7 +109,7 @@ export function findTerminalLedgerEvidenceForTask(args: {
109
109
  const terminalTaskId = readNonEmptyString(entry.payload?.taskId);
110
110
  if (terminalTaskId !== taskId) continue;
111
111
  if (entry.kind === 'task_completed' && isWeakCompletionEvidence(entry.payload)) continue;
112
- if (args.sessionId && entry.sessionId && entry.sessionId !== args.sessionId) continue;
112
+ if (args.sessionId && entry.sessionId && !sessionIdsEquivalent(entry.sessionId, args.sessionId)) continue;
113
113
  if (!args.sessionId && args.nodeId && entry.nodeId && !meshNodeIdMatches(entry as unknown as MeshNodeIdentified, args.nodeId)) continue;
114
114
  return { id: entry.id, kind: entry.kind, payload: entry.payload || {}, timestamp: entry.timestamp };
115
115
  }
@@ -127,7 +127,7 @@ function findDirectDispatchLedgerEntry(args: {
127
127
  if (entry.kind !== 'task_dispatched') continue;
128
128
  const payloadTaskId = readNonEmptyString(entry.payload?.taskId);
129
129
  if (payloadTaskId !== args.taskId) continue;
130
- if (args.sessionId && entry.sessionId && entry.sessionId !== args.sessionId) continue;
130
+ if (args.sessionId && entry.sessionId && !sessionIdsEquivalent(entry.sessionId, args.sessionId)) continue;
131
131
  return {
132
132
  id: entry.id,
133
133
  timestamp: entry.timestamp,
@@ -172,7 +172,7 @@ function hasTerminalLedgerAfterDispatch(args: {
172
172
  const terminalTaskId = readNonEmptyString(entry.payload?.taskId);
173
173
  if (terminalTaskId && terminalTaskId === args.taskId) return true;
174
174
  if (terminalTaskId && terminalTaskId !== args.taskId) continue;
175
- if (args.sessionId && entry.sessionId === args.sessionId) return true;
175
+ if (args.sessionId && sessionIdsEquivalent(entry.sessionId, args.sessionId)) return true;
176
176
  }
177
177
  return false;
178
178
  }
@@ -17,6 +17,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, renameSy
17
17
  import { join } from 'path';
18
18
  import { randomUUID } from 'crypto';
19
19
  import { getConfigDir } from '../config/config.js';
20
+ import { daemonIdsEquivalent, sessionIdsEquivalent } from '@adhdev/mesh-shared';
20
21
  import { EventEmitter } from 'events';
21
22
  import { MeshRuntimeStore } from './mesh-runtime-store.js';
22
23
  // ─── Types ──────────────────────────────────────
@@ -1224,7 +1225,7 @@ export function getSessionRecoveryContext(
1224
1225
  if (!failureCountDone) {
1225
1226
  if (ts < recentWindow) {
1226
1227
  failureCountDone = true;
1227
- } else if (opts.nodeId && e.nodeId !== opts.nodeId) {
1228
+ } else if (opts.nodeId && !daemonIdsEquivalent(e.nodeId, opts.nodeId)) {
1228
1229
  // Entry for a different node — skip for failure counting but continue scanning for dispatch
1229
1230
  } else if (e.kind === 'task_failed') {
1230
1231
  if (!isIntentionalCleanupStopEntry(e)) consecutiveNodeFailures++;
@@ -1236,8 +1237,8 @@ export function getSessionRecoveryContext(
1236
1237
 
1237
1238
  // Dispatch search: find the last dispatch matching this session or node
1238
1239
  if (lastDispatch === null && e.kind === 'task_dispatched') {
1239
- if (opts.sessionId && e.sessionId === opts.sessionId) { lastDispatch = e; }
1240
- else if (!opts.sessionId && opts.nodeId && e.nodeId === opts.nodeId) { lastDispatch = e; }
1240
+ if (opts.sessionId && sessionIdsEquivalent(e.sessionId, opts.sessionId)) { lastDispatch = e; }
1241
+ else if (!opts.sessionId && opts.nodeId && daemonIdsEquivalent(e.nodeId, opts.nodeId)) { lastDispatch = e; }
1241
1242
  }
1242
1243
 
1243
1244
  // Stop once both tasks are done
@@ -14,7 +14,7 @@
14
14
  import type { ProviderLoader } from '../providers/provider-loader.js';
15
15
  import { detectCLI } from '../detection/cli-detector.js';
16
16
  import { getGitRepoStatus } from '../git/git-status.js';
17
- import { normalizeGitStatus as sharedNormalizeGitStatus, pickBestTransitGitStatus as sharedPickBestTransitGitStatus, summarizeGitShape as sharedSummarizeGitShape, normalizeMeshNodeId, daemonIdsEquivalent, meshWorkspacesEquivalent } from '@adhdev/mesh-shared';
17
+ import { normalizeGitStatus as sharedNormalizeGitStatus, pickBestTransitGitStatus as sharedPickBestTransitGitStatus, summarizeGitShape as sharedSummarizeGitShape, normalizeMeshNodeId, daemonIdsEquivalent, meshWorkspacesEquivalent, sessionIdsEquivalent } from '@adhdev/mesh-shared';
18
18
  import { LOG } from '../logging/logger.js';
19
19
  import { getSessionHostSurfaceKind } from '../session-host/runtime-surface.js';
20
20
  import { awaitWithWarmupDeadline, resolveWarmupDeadlineOpts } from '../mesh/mesh-warmup-deadline.js';
@@ -1606,7 +1606,7 @@ export async function hydrateInlineMeshDirectTruth(args: {
1606
1606
  const workspace = readStringValue(node?.workspace);
1607
1607
  const daemonId = readStringValue(node?.daemonId);
1608
1608
  const isSelfNode = Boolean(
1609
- nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
1609
+ nodeId && selectedCoordinatorNodeId && daemonIdsEquivalent(nodeId, selectedCoordinatorNodeId),
1610
1610
  ) || Boolean(
1611
1611
  daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId)),
1612
1612
  ) || Boolean(args.meshSource !== 'local_config' && nodeIndex === 0);
@@ -1720,7 +1720,7 @@ export async function hydrateInlineMeshDirectTruth(args: {
1720
1720
  const nodeId = normalizeMeshNodeId(node) || `node_${nodeIndex}`;
1721
1721
  const daemonId = readStringValue(node?.daemonId);
1722
1722
  const isSelfNode = Boolean(
1723
- nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
1723
+ nodeId && selectedCoordinatorNodeId && daemonIdsEquivalent(nodeId, selectedCoordinatorNodeId),
1724
1724
  ) || Boolean(
1725
1725
  daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId)),
1726
1726
  );
@@ -1794,7 +1794,10 @@ export function summarizeMeshSessionRecord(record: any): Record<string, unknown>
1794
1794
 
1795
1795
  function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string, nodeWorkspace = '', nodeIsMissingLocalWorktree = false): boolean {
1796
1796
  const recordNodeId = readStringValue(record?.meta?.meshNodeId);
1797
- if (!recordNodeId || recordNodeId !== nodeId) return false;
1797
+ // A session's stamped meshNodeId and the node's id can carry interchangeable
1798
+ // daemon-id forms (bare `mach_X` vs `daemon_mach_X`) — compare under the
1799
+ // canonical machine core, not raw `!==` (CANON-IDENTITY class).
1800
+ if (!recordNodeId || !daemonIdsEquivalent(recordNodeId, nodeId)) return false;
1798
1801
  if (nodeIsMissingLocalWorktree) return false;
1799
1802
  const recordWorkspace = readStringValue(record?.workspace);
1800
1803
  // Normalized compare (shared WTCLAIM rule): a base node and a co-located worktree
@@ -1855,7 +1858,7 @@ export function collectLiveMeshSessionRecords(args: {
1855
1858
  && !fs.existsSync(nodeWorkspace);
1856
1859
  const matches = args.liveSessionRecords.filter((record) => {
1857
1860
  const recordNodeId = readStringValue(record?.meta?.meshNodeId);
1858
- if (recordNodeId && recordNodeId !== args.nodeId) return false;
1861
+ if (recordNodeId && !daemonIdsEquivalent(recordNodeId, args.nodeId)) return false;
1859
1862
  if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId, nodeWorkspace || '', nodeIsMissingLocalWorktree)) return true;
1860
1863
  if (nodeIsMissingLocalWorktree) return false;
1861
1864
  return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
@@ -1865,7 +1868,7 @@ export function collectLiveMeshSessionRecords(args: {
1865
1868
  for (const record of args.liveSessionRecords) {
1866
1869
  if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
1867
1870
  const sessionId = readStringValue(record?.sessionId);
1868
- if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
1871
+ if (sessionId && matches.some((entry) => sessionIdsEquivalent(readStringValue(entry?.sessionId), sessionId))) continue;
1869
1872
  matches.push(record);
1870
1873
  }
1871
1874
  }
@@ -15,7 +15,7 @@ import { traceMeshEventDrop } from './mesh-event-trace.js';
15
15
  import { awaitWithWarmupDeadline, resolveWarmupDeadlineOpts } from './mesh-warmup-deadline.js';
16
16
  import { resolveDelegatedWorkerAutoApprove, resolveProviderMaxParallel, resolveNodeSchedulingPriority, normalizeMeshSchedulingStrategy, resolveMaxParallelTasks, resolveMaxReadonlyParallelTasks } from '../repo-mesh-types.js';
17
17
  import type { RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
18
- import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, type MeshNodeIdentified } from '@adhdev/mesh-shared';
18
+ import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, sessionIdsEquivalent, type MeshNodeIdentified } from '@adhdev/mesh-shared';
19
19
  import { findTerminalLedgerEvidenceForTask, hasUnterminalDirectDispatchLedgerEntry } from './mesh-events-stale.js';
20
20
  import { readNonEmptyString } from './mesh-events-utils.js';
21
21
  import { queuePendingMeshCoordinatorEvent, retractPendingDispatchBlockedEvent } from './mesh-events-pending.js';
@@ -834,7 +834,7 @@ function nodeHasActiveMeshWork(components: DaemonComponents, meshId: string, nod
834
834
  // task already running here — the CANON-IDENTITY duplicate dispatch.
835
835
  if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
836
836
  const sessionId = readNonEmptyString(state.instanceId);
837
- if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
837
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState(state)) return false;
838
838
  return sessionStateLooksActive(state);
839
839
  });
840
840
  }
@@ -1042,7 +1042,7 @@ function activeProviderAssignedCount(meshId: string, nodeId: string, providerTyp
1042
1042
  }
1043
1043
 
1044
1044
  export function sessionHasActiveAssignment(meshId: string, sessionId: string): boolean {
1045
- if (getQueue(meshId, { status: ['assigned'] as any }).some(task => task.assignedSessionId === sessionId)) {
1045
+ if (getQueue(meshId, { status: ['assigned'] as any }).some(task => sessionIdsEquivalent(task.assignedSessionId, sessionId))) {
1046
1046
  return true;
1047
1047
  }
1048
1048
  // Direct dispatches (mesh_send_task) are tracked in mesh_direct_dispatches, not the
@@ -1053,7 +1053,7 @@ export function sessionHasActiveAssignment(meshId: string, sessionId: string): b
1053
1053
  // session goes silently idle. This check runs before markSessionTerminal marks the
1054
1054
  // dispatch terminal, so the in-flight dispatch is still observable here.
1055
1055
  try {
1056
- if (getActiveDirectDispatches(meshId).some(d => d.sessionId === sessionId)) return true;
1056
+ if (getActiveDirectDispatches(meshId).some(d => sessionIdsEquivalent(d.sessionId, sessionId))) return true;
1057
1057
  if (hasUnterminalDirectDispatchLedgerEntry(meshId, sessionId)) return true;
1058
1058
  } catch { /* best-effort — fall through to false */ }
1059
1059
  return false;
@@ -57,7 +57,7 @@ import {
57
57
  } from './mesh-unresolved-forward-outbox.js';
58
58
  import { readNonEmptyString, readMeshCompletionSummary, buildMeshSystemMessage } from './mesh-events-utils.js';
59
59
  import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
60
- import { expandDaemonIdForms, daemonIdsEquivalent } from '@adhdev/mesh-shared';
60
+ import { expandDaemonIdForms, daemonIdsEquivalent, sessionIdsEquivalent } from '@adhdev/mesh-shared';
61
61
  import { getActiveDirectDispatches, getQueue, reclaimStrandedAssignedTask, updateTaskStatus } from './mesh-work-queue.js';
62
62
  import { readLedgerEntries } from './mesh-ledger.js';
63
63
  import { pruneStaleDirectDispatches } from './mesh-active-work.js';
@@ -753,11 +753,10 @@ function drainAndInjectIntoTargets(
753
753
  // (unchanged behaviour — regression-0 for the common case).
754
754
  const wantSession = readNonEmptyString(pending.targetCoordinatorSessionId);
755
755
  if (wantSession) {
756
- // SESSION-ID IS SINGLE-FORM: a coordinator session id is one canonical
757
- // UUID (crypto.randomUUID), carried verbatim end-to-end no node/daemon-id
758
- // style serialization variants. Exact `===` is the correct match; unlike
759
- // the daemon-level set below it needs no equivalence helper.
760
- const matched = targetCoordinators.filter(c => c.sessionId === wantSession);
756
+ // Session ids are single-form; sessionIdsEquivalent is the one canonical
757
+ // exact-match predicate unlike the daemon-level set below it needs no
758
+ // form expansion.
759
+ const matched = targetCoordinators.filter(c => sessionIdsEquivalent(c.sessionId, wantSession));
761
760
  if (matched.length === 0) {
762
761
  // The originating coordinator session is not deliverable on this daemon
763
762
  // right now (gone, or modal-parked and excluded from targets). Strict mode
@@ -12,6 +12,7 @@
12
12
 
13
13
  import type { MeshLedgerEntry } from './mesh-ledger.js';
14
14
  import { buildMeshAsyncRefineJobs } from './mesh-refine-status.js';
15
+ import { daemonIdsEquivalent } from '@adhdev/mesh-shared';
15
16
 
16
17
  export type MeshReviewInboxReason = 'merge_candidate' | 'refine_blocked_review';
17
18
 
@@ -171,7 +172,7 @@ function resolveNodeEvidence(nodeId: string, ledgerEntries: MeshLedgerEntry[]):
171
172
 
172
173
  for (let i = ledgerEntries.length - 1; i >= 0; i--) {
173
174
  const entry = ledgerEntries[i];
174
- if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
175
+ if (!daemonIdsEquivalent(entry.nodeId, nodeId) || !isTerminalLedgerKind(entry.kind)) continue;
175
176
  const payload = readRecord(entry.payload) ?? {};
176
177
 
177
178
  if (!evidence.available) {
@@ -227,7 +228,7 @@ function resolveNodeEvidence(nodeId: string, ledgerEntries: MeshLedgerEntry[]):
227
228
  function hasBlockedReviewRefineResult(nodeId: string, ledgerEntries: MeshLedgerEntry[]): boolean {
228
229
  for (let i = ledgerEntries.length - 1; i >= 0; i--) {
229
230
  const entry = ledgerEntries[i];
230
- if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
231
+ if (!daemonIdsEquivalent(entry.nodeId, nodeId) || !isTerminalLedgerKind(entry.kind)) continue;
231
232
  const payload = readRecord(entry.payload) ?? {};
232
233
  if (payload.source !== 'refine_mesh_node_async_job') continue;
233
234
  const result = readRecord(payload.result);
@@ -275,7 +276,7 @@ export function deriveMeshReviewInboxItems(args: {
275
276
 
276
277
  const { evidence, transcriptHandle } = resolveNodeEvidence(nodeId, args.ledgerEntries);
277
278
  const activeRefineJob = [...refineJobs].reverse().find(job =>
278
- (job.nodeId === nodeId || job.targetNodeId === nodeId)
279
+ (daemonIdsEquivalent(job.nodeId, nodeId) || daemonIdsEquivalent(job.targetNodeId, nodeId))
279
280
  && (job.status === 'accepted' || job.status === 'running'),
280
281
  ) ?? null;
281
282
 
@@ -4,7 +4,7 @@ import { hasUnterminalDirectDispatchLedgerEntry } from './mesh-events-stale.js';
4
4
  import { appendLedgerEntry, readLedgerEntries } from './mesh-ledger.js';
5
5
  import { LOG } from '../logging/logger.js';
6
6
  import { readNonEmptyString } from './mesh-events-utils.js';
7
- import { meshNodeIdMatches } from '@adhdev/mesh-shared';
7
+ import { meshNodeIdMatches, sessionIdsEquivalent } from '@adhdev/mesh-shared';
8
8
 
9
9
  // ---------------------------------------------------------------------------
10
10
  // R1: single-source coordinator routing resolution
@@ -117,7 +117,7 @@ export function resolveWorkerDelegateRouting(
117
117
  let hasActiveDispatch = false;
118
118
  try {
119
119
  hasActiveDispatch =
120
- getActiveDirectDispatches(coordinatorMeshId).some(d => d.sessionId === instanceId)
120
+ getActiveDirectDispatches(coordinatorMeshId).some(d => sessionIdsEquivalent(d.sessionId, instanceId))
121
121
  || hasUnterminalDirectDispatchLedgerEntry(coordinatorMeshId, instanceId);
122
122
  } catch { /* best-effort */ }
123
123
  if (!hasActiveDispatch) return reject('coordinator_not_dispatch_target');
@@ -4,7 +4,7 @@ import { LOG } from '../logging/logger.js';
4
4
  import { loadBetterSqlite3 } from '../system/load-better-sqlite3.js';
5
5
  import { getLedgerDir } from './mesh-ledger.js';
6
6
  import { nodeSatisfiesRequiredTags, isTaskReadonly } from './mesh-work-queue.js';
7
- import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms } from '@adhdev/mesh-shared';
7
+ import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent } from '@adhdev/mesh-shared';
8
8
  import type { MeshTaskStatus, MeshWorkQueueEntry } from './mesh-work-queue.js';
9
9
  import type BetterSqlite3 from 'better-sqlite3';
10
10
  import type { Database as DatabaseHandle } from 'better-sqlite3';
@@ -795,13 +795,11 @@ export class MeshRuntimeStore {
795
795
  // an empty session. Accept the candidate when the target resolves to the
796
796
  // same node under ANY equivalent form; keep targetSessionId an exact match.
797
797
  const targetMatches = (candidate: MeshWorkQueueEntry): boolean => {
798
- // SESSION-ID IS SINGLE-FORM: unlike node/daemon ids (3 serialization
799
- // forms requiring expandDaemonIdForms), a session id is a single
800
- // canonical UUID minted once via crypto.randomUUID() in the provider
801
- // instance (cli/acp/extension/ide) and carried verbatim across daemons
802
- // (resolveEventSessionId applies no transformation). So an exact `!==`
803
- // is correct here and needs no normalization helper.
804
- if (candidate.targetSessionId && candidate.targetSessionId !== sessionId) return false;
798
+ // Session ids are single-form (unlike node/daemon ids with their 3
799
+ // serialization forms requiring expandDaemonIdForms) see the
800
+ // sessionIdsEquivalent doc; it is the one canonical exact-match
801
+ // predicate for them.
802
+ if (candidate.targetSessionId && !sessionIdsEquivalent(candidate.targetSessionId, sessionId)) return false;
805
803
  if (
806
804
  candidate.targetNodeId
807
805
  && !daemonIdsEquivalent(candidate.targetNodeId, nodeId)
@@ -9,6 +9,7 @@ import { appendLedgerEntry } from './mesh-ledger.js';
9
9
  import type { MeshLedgerKind } from './mesh-ledger.js';
10
10
  import { createSessionDelivery } from './mesh-delivery-policy.js';
11
11
  import { isTaskDispatchInFlight, endTaskDispatchInFlight } from './mesh-task-inflight.js';
12
+ import { sessionIdsEquivalent } from '@adhdev/mesh-shared';
12
13
 
13
14
  export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
14
15
  export type MeshActiveTaskStatus = Extract<MeshTaskStatus, 'pending' | 'assigned'>;
@@ -1248,7 +1249,7 @@ export function updateSessionTaskStatus(
1248
1249
  // `assigned` for 19 minutes. If the session still has an assigned row we
1249
1250
  // failed to resolve, surface it loudly instead of dropping the completion.
1250
1251
  const assignedRows = store.getActiveAssignmentDetails(meshId)
1251
- .filter(r => r.sessionId === sessionId);
1252
+ .filter(r => sessionIdsEquivalent(r.sessionId, sessionId));
1252
1253
  if (assignedRows.length > 0) {
1253
1254
  LOG.warn('MeshQueue', `No assigned queue row matched completion for mesh ${meshId} session ${sessionId} `
1254
1255
  + `(taskId=${opts?.taskId ?? 'none'}, occurredAt=${occurredAtIso ?? 'none'}); `