@adhdev/daemon-core 0.9.82-rc.3 → 0.9.82-rc.31

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.
@@ -26,6 +26,8 @@ import { getSavedProviderSessions } from '../config/saved-sessions.js';
26
26
  import { listProviderHistorySessions } from '../config/chat-history.js';
27
27
  import { detectIDEs } from '../detection/ide-detector.js';
28
28
  import { detectCLI } from '../detection/cli-detector.js';
29
+ import { getGitRepoStatus } from '../git/git-status.js';
30
+ import type { GitSubmoduleStatus } from '../git/git-types.js';
29
31
  import { SessionRegistry } from '../sessions/registry.js';
30
32
  import { LOG } from '../logging/logger.js';
31
33
  import { logCommand } from '../logging/command-log.js';
@@ -112,58 +114,44 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
112
114
  return undefined;
113
115
  }
114
116
 
115
- function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
116
- const cachedStatus = readObjectRecord(node?.cachedStatus);
117
- const cachedGit = readObjectRecord(cachedStatus.git);
118
- if (Object.keys(cachedGit).length) {
119
- const conflictFiles = Array.isArray(cachedGit.conflictFiles)
120
- ? cachedGit.conflictFiles.filter((value: unknown): value is string => typeof value === 'string')
121
- : [];
122
- const conflictCount = readNumberValue(cachedGit.conflicts) ?? conflictFiles.length;
123
- const hasConflicts = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount > 0;
124
- const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
125
- if (isGitRepo !== undefined) {
117
+ function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {
118
+ const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\/]+$/, '') : '';
119
+ const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : '';
120
+ if (!normalizedPath) return undefined;
121
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
122
+ if (!normalizedRoot) return undefined;
123
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, '')}`;
124
+ }
125
+
126
+ function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {
127
+ if (!Array.isArray(value)) return undefined;
128
+ const submodules = value
129
+ .map(entry => {
130
+ const submodule = readObjectRecord(entry);
131
+ const path = readStringValue(submodule.path);
132
+ const commit = readStringValue(submodule.commit);
133
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root)
134
+ ?? joinRepoPath(parentRepoRoot, path);
135
+ if (!path || !commit || !repoPath) return null;
126
136
  return {
127
- workspace: readStringValue(cachedGit.workspace, node?.workspace) || '',
128
- repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
129
- isGitRepo,
130
- branch: readStringValue(cachedGit.branch) ?? null,
131
- headCommit: readStringValue(cachedGit.headCommit) ?? null,
132
- headMessage: readStringValue(cachedGit.headMessage) ?? null,
133
- upstream: readStringValue(cachedGit.upstream) ?? null,
134
- ahead: readNumberValue(cachedGit.ahead) ?? 0,
135
- behind: readNumberValue(cachedGit.behind) ?? 0,
136
- staged: readNumberValue(cachedGit.staged) ?? 0,
137
- modified: readNumberValue(cachedGit.modified) ?? 0,
138
- untracked: readNumberValue(cachedGit.untracked) ?? 0,
139
- deleted: readNumberValue(cachedGit.deleted) ?? 0,
140
- renamed: readNumberValue(cachedGit.renamed) ?? 0,
141
- hasConflicts,
142
- conflictFiles,
143
- stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
144
- lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now(),
137
+ path,
138
+ commit,
139
+ repoPath,
140
+ dirty: readBooleanValue(submodule.dirty) ?? false,
141
+ outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
142
+ lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
143
+ ...(readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}),
145
144
  };
146
- }
147
- }
145
+ })
146
+ .filter((entry): entry is GitSubmoduleStatus => entry !== null);
147
+ return submodules.length > 0 ? submodules : undefined;
148
+ }
148
149
 
149
- const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
150
- const gitResult = readObjectRecord(rawGit.result);
151
- const directStatus = readObjectRecord(rawGit.status);
152
- const nestedStatus = readObjectRecord(gitResult.status);
153
- const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
154
- const probeGit = readObjectRecord(rawProbe.git);
155
- const probeGitResult = readObjectRecord(probeGit.result);
156
- const probeDirectStatus = readObjectRecord(probeGit.status);
157
- const probeNestedStatus = readObjectRecord(probeGitResult.status);
158
- const status = Object.keys(directStatus).length
159
- ? directStatus
160
- : Object.keys(nestedStatus).length
161
- ? nestedStatus
162
- : Object.keys(probeDirectStatus).length
163
- ? probeDirectStatus
164
- : Object.keys(probeNestedStatus).length
165
- ? probeNestedStatus
166
- : {};
150
+ function normalizeInlineMeshGitStatus(
151
+ status: Record<string, unknown>,
152
+ node: any,
153
+ options?: { lastCheckedAt?: number },
154
+ ): Record<string, unknown> | undefined {
167
155
  const isGitRepo = readBooleanValue(status.isGitRepo);
168
156
  if (!Object.keys(status).length || isGitRepo === undefined) return undefined;
169
157
  const conflictFiles = Array.isArray(status.conflictFiles)
@@ -171,9 +159,11 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
171
159
  : [];
172
160
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
173
161
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
162
+ const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || undefined;
163
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
174
164
  return {
175
165
  workspace: readStringValue(status.workspace, node?.workspace) || '',
176
- repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
166
+ repoRoot: repoRoot ?? null,
177
167
  isGitRepo,
178
168
  branch: readStringValue(status.branch) ?? null,
179
169
  headCommit: readStringValue(status.headCommit) ?? null,
@@ -189,30 +179,538 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
189
179
  hasConflicts,
190
180
  conflictFiles,
191
181
  stashCount: readNumberValue(status.stashCount) ?? 0,
192
- lastCheckedAt: Date.now(),
182
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
183
+ ...(submodules ? { submodules } : {}),
193
184
  };
194
185
  }
195
186
 
196
- function applyCachedInlineMeshNodeStatus(status: Record<string, unknown>, node: any): boolean {
187
+ function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
188
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
189
+ const gitResult = readObjectRecord(rawGit.result);
190
+ const directStatus = readObjectRecord(rawGit.status);
191
+ const nestedStatus = readObjectRecord(gitResult.status);
192
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
193
+ const probeGit = readObjectRecord(rawProbe.git);
194
+ const probeGitResult = readObjectRecord(probeGit.result);
195
+ const probeDirectStatus = readObjectRecord(probeGit.status);
196
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
197
+ const status = Object.keys(directStatus).length
198
+ ? directStatus
199
+ : Object.keys(nestedStatus).length
200
+ ? nestedStatus
201
+ : Object.keys(probeDirectStatus).length
202
+ ? probeDirectStatus
203
+ : Object.keys(probeNestedStatus).length
204
+ ? probeNestedStatus
205
+ : {};
206
+ return normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
207
+ }
208
+
209
+ function recordInlineMeshDirectGitTruth(
210
+ node: any,
211
+ git: Record<string, unknown>,
212
+ source: 'selected_coordinator_local_git' | 'selected_coordinator_mesh_p2p_git',
213
+ ): void {
214
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return;
215
+ const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
216
+ const updatedAt = new Date(checkedAt).toISOString();
217
+ const nextGit: Record<string, unknown> = {
218
+ ...git,
219
+ lastCheckedAt: checkedAt,
220
+ };
221
+ node.lastGit = {
222
+ source,
223
+ checkedAt,
224
+ status: nextGit,
225
+ };
226
+ node.last_git = node.lastGit;
227
+ node.machineStatus = 'online';
228
+ node.updatedAt = updatedAt;
229
+ node.lastSeenAt = updatedAt;
230
+ const repoRoot = readStringValue(nextGit.repoRoot);
231
+ if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
232
+ }
233
+
234
+ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
235
+ const liveGit = buildInlineMeshTransitGitStatus(node);
236
+ if (liveGit) return liveGit;
237
+
197
238
  const cachedStatus = readObjectRecord(node?.cachedStatus);
198
- const git = buildCachedInlineMeshGitStatus(node);
199
- const error = readStringValue(cachedStatus.error, node?.error);
200
- const health = readStringValue(cachedStatus.health, node?.health);
239
+ const cachedGit = readObjectRecord(cachedStatus.git);
240
+ if (!Object.keys(cachedGit).length) return undefined;
241
+ return normalizeInlineMeshGitStatus(cachedGit, node);
242
+ }
243
+
244
+ function shouldDiscardCachedInlineMeshStatus(node: any): boolean {
245
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
246
+ if (!Object.keys(cachedStatus).length) return false;
247
+ const cachedGit = readObjectRecord(cachedStatus.git);
248
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
249
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
250
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
251
+ const branch = readStringValue(cachedGit.branch);
252
+ const headCommit = readStringValue(cachedGit.headCommit);
253
+ return isGitRepo === false && !branch && !headCommit;
254
+ }
255
+
256
+ function stripInlineMeshTransientNodeState(node: any): any {
257
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return node;
258
+ const {
259
+ cachedStatus,
260
+ lastGit: _lastGit,
261
+ last_git: _lastGitLegacy,
262
+ lastProbe: _lastProbe,
263
+ last_probe: _lastProbeLegacy,
264
+ error: _error,
265
+ health: _health,
266
+ machineStatus: _machineStatus,
267
+ lastSeenAt: _lastSeenAt,
268
+ last_seen_at: _lastSeenAtLegacy,
269
+ updatedAt: _updatedAt,
270
+ updated_at: _updatedAtLegacy,
271
+ activeSession: _activeSession,
272
+ active_session: _activeSessionLegacy,
273
+ activeSessionId: _activeSessionId,
274
+ active_session_id: _activeSessionIdLegacy,
275
+ sessionId: _sessionId,
276
+ session_id: _sessionIdLegacy,
277
+ providerType: _providerType,
278
+ provider_type: _providerTypeLegacy,
279
+ ...rest
280
+ } = node as Record<string, unknown>;
281
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
282
+ return { ...rest, cachedStatus };
283
+ }
284
+ return rest;
285
+ }
286
+
287
+ function hasInlineMeshTransientNodeState(node: any): boolean {
288
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
289
+ return 'cachedStatus' in node
290
+ || 'lastGit' in node
291
+ || 'last_git' in node
292
+ || 'lastProbe' in node
293
+ || 'last_probe' in node
294
+ || 'error' in node
295
+ || 'health' in node
296
+ || 'machineStatus' in node
297
+ || 'lastSeenAt' in node
298
+ || 'last_seen_at' in node
299
+ || 'updatedAt' in node
300
+ || 'updated_at' in node
301
+ || 'activeSession' in node
302
+ || 'active_session' in node
303
+ || 'activeSessionId' in node
304
+ || 'active_session_id' in node
305
+ || 'sessionId' in node
306
+ || 'session_id' in node
307
+ || 'providerType' in node
308
+ || 'provider_type' in node;
309
+ }
310
+
311
+ function readInlineMeshNodeId(node: any): string {
312
+ return readStringValue(node?.id, node?.nodeId) || '';
313
+ }
314
+
315
+ function sanitizeInlineMesh(inlineMesh: any): any {
316
+ if (!inlineMesh || typeof inlineMesh !== 'object' || Array.isArray(inlineMesh)) return inlineMesh;
317
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
318
+ let changed = false;
319
+ const nodes = inlineMesh.nodes.map((node: any) => {
320
+ if (!hasInlineMeshTransientNodeState(node)) return node;
321
+ changed = true;
322
+ return stripInlineMeshTransientNodeState(node);
323
+ });
324
+ if (!changed) return inlineMesh;
325
+ return {
326
+ ...inlineMesh,
327
+ nodes,
328
+ };
329
+ }
330
+
331
+ function reconcileInlineMeshCache(cached: any, incoming: any): any {
332
+ if (!cached || typeof cached !== 'object' || Array.isArray(cached)) return incoming;
333
+ if (!incoming || typeof incoming !== 'object' || Array.isArray(incoming)) return cached;
334
+ const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
335
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
336
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
337
+
338
+ const incomingById = new Map<string, any>();
339
+ for (const node of incomingNodes) {
340
+ const nodeId = readInlineMeshNodeId(node);
341
+ if (nodeId) incomingById.set(nodeId, node);
342
+ }
343
+
344
+ const nodes = cachedNodes.map((cachedNode: any) => {
345
+ const nodeId = readInlineMeshNodeId(cachedNode);
346
+ const incomingNode = nodeId ? incomingById.get(nodeId) : undefined;
347
+ if (!incomingNode) return cachedNode;
348
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
349
+ return { ...cachedNode, ...incomingNode };
350
+ }
351
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
352
+ });
353
+
354
+ return {
355
+ ...cached,
356
+ ...incoming,
357
+ nodes,
358
+ };
359
+ }
360
+
361
+ function hasGitWorktreeChanges(git: Record<string, unknown> | null | undefined): boolean {
362
+ if (!git) return false;
363
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
364
+ }
365
+
366
+ function getGitSubmoduleDriftState(git: Record<string, unknown> | null | undefined): { dirty: boolean; outOfSync: boolean } {
367
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
368
+ let dirty = false;
369
+ let outOfSync = false;
370
+ for (const entry of submodules) {
371
+ const submodule = readObjectRecord(entry);
372
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
373
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
374
+ }
375
+ return { dirty, outOfSync };
376
+ }
377
+
378
+ function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
379
+ if (!git || readBooleanValue(git.isGitRepo) === false) return 'degraded';
380
+ const branch = readStringValue(git.branch);
381
+ if (!branch) return 'degraded';
382
+ const submoduleDrift = getGitSubmoduleDriftState(git);
383
+ if (submoduleDrift.outOfSync) return 'degraded';
384
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return 'dirty';
385
+ return 'online';
386
+ }
387
+
388
+ function readCachedInlineMeshActiveSessions(node: any): string[] {
389
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
390
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
391
+ const fallbackSession = Object.keys(activeSession).length
392
+ ? activeSession
393
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
394
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
395
+ return sessionId ? [sessionId] : [];
396
+ }
397
+
398
+ function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>> {
399
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
400
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
401
+ const fallbackSession = Object.keys(activeSession).length
402
+ ? activeSession
403
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
404
+ const sessionId = readStringValue(
405
+ fallbackSession.id,
406
+ fallbackSession.sessionId,
407
+ fallbackSession.session_id,
408
+ node?.activeSessionId,
409
+ node?.active_session_id,
410
+ node?.sessionId,
411
+ node?.session_id,
412
+ );
413
+ if (!sessionId) return [];
414
+ return [{
415
+ sessionId,
416
+ providerType: readStringValue(
417
+ fallbackSession.providerType,
418
+ fallbackSession.provider_type,
419
+ fallbackSession.cliType,
420
+ fallbackSession.cli_type,
421
+ fallbackSession.provider,
422
+ node?.providerType,
423
+ node?.provider_type,
424
+ ),
425
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
426
+ lifecycle: readStringValue(fallbackSession.lifecycle),
427
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
428
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
429
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
430
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
431
+ isCached: true,
432
+ }];
433
+ }
434
+
435
+ function readLiveMeshSessionState(record: any): string | undefined {
436
+ return readStringValue(
437
+ record?.meta?.sessionStatus,
438
+ record?.meta?.status,
439
+ record?.meta?.providerStatus,
440
+ record?.status,
441
+ record?.state,
442
+ record?.lifecycle,
443
+ );
444
+ }
445
+
446
+ function toIsoTimestamp(value: unknown): string | null {
447
+ if (typeof value === 'number' && Number.isFinite(value)) return new Date(value).toISOString();
448
+ const stringValue = readStringValue(value);
449
+ return stringValue || null;
450
+ }
451
+
452
+ function synthesizeMeshNodeFreshnessFromConnection(status: Record<string, unknown>): void {
453
+ const connection = readObjectRecord(status.connection);
454
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
455
+ const git = readObjectRecord(status.git);
456
+ const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
457
+ if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
458
+ if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
459
+ status.updatedAt = gitCheckedAt ?? connectionFreshAt;
460
+ }
461
+ }
462
+
463
+ function finalizeMeshNodeStatus(args: {
464
+ status: Record<string, unknown>;
465
+ node: any;
466
+ daemonId?: string;
467
+ isSelfNode: boolean;
468
+ }): void {
469
+ const { status, node, daemonId, isSelfNode } = args;
470
+ if (!readStringValue(status.machineStatus)) {
471
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
472
+ const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
473
+ if (machineStatus) status.machineStatus = machineStatus;
474
+ }
475
+ synthesizeMeshNodeFreshnessFromConnection(status);
476
+ const connectionState = readStringValue(readObjectRecord(status.connection).state);
477
+ status.launchReady = !!daemonId && (
478
+ readStringValue(status.machineStatus) === 'online'
479
+ || connectionState === 'connected'
480
+ || isSelfNode
481
+ );
482
+ }
483
+
484
+ async function probeRemoteMeshGitStatus(args: {
485
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
486
+ daemonId: string;
487
+ workspace: string;
488
+ timeoutMs: number;
489
+ }): Promise<Record<string, unknown> | null> {
490
+ if (!args.dispatchMeshCommand) return null;
491
+ const remoteResult = await Promise.race([
492
+ args.dispatchMeshCommand(args.daemonId, 'git_status', { workspace: args.workspace }),
493
+ new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), args.timeoutMs)),
494
+ ]) as any;
495
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
496
+ return remoteGit && typeof remoteGit === 'object' && typeof remoteGit.isGitRepo === 'boolean'
497
+ ? remoteGit as Record<string, unknown>
498
+ : null;
499
+ }
500
+
501
+ async function hydrateInlineMeshDirectTruth(args: {
502
+ mesh: any;
503
+ meshSource: 'inline_cache' | 'inline_bootstrap' | 'local_config';
504
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
505
+ statusInstanceId?: string;
506
+ localMachineId?: string;
507
+ }): Promise<{
508
+ directEvidenceCount: number;
509
+ localConfirmedCount: number;
510
+ peerAttemptedCount: number;
511
+ peerConfirmedCount: number;
512
+ unavailableNodeIds: string[];
513
+ }> {
514
+ const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
515
+ if (!nodes.length) {
516
+ return {
517
+ directEvidenceCount: 0,
518
+ localConfirmedCount: 0,
519
+ peerAttemptedCount: 0,
520
+ peerConfirmedCount: 0,
521
+ unavailableNodeIds: [],
522
+ };
523
+ }
524
+
525
+ const selectedCoordinatorNodeId = readStringValue(
526
+ args.mesh?.coordinator?.preferredNodeId,
527
+ nodes[0]?.id,
528
+ nodes[0]?.nodeId,
529
+ );
530
+
531
+ let localConfirmedCount = 0;
532
+ let peerAttemptedCount = 0;
533
+ let peerConfirmedCount = 0;
534
+ const unavailableNodeIds: string[] = [];
535
+
536
+ for (const [nodeIndex, node] of nodes.entries()) {
537
+ const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
538
+ const workspace = readStringValue(node?.workspace);
539
+ const daemonId = readStringValue(node?.daemonId);
540
+ const isSelfNode = Boolean(
541
+ nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
542
+ ) || Boolean(
543
+ daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId),
544
+ ) || Boolean(args.meshSource !== 'local_config' && nodeIndex === 0);
545
+
546
+ if (!workspace) {
547
+ if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
548
+ continue;
549
+ }
550
+
551
+ if (isSelfNode && fs.existsSync(workspace)) {
552
+ try {
553
+ const localGit = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
554
+ if (localGit?.isGitRepo) {
555
+ recordInlineMeshDirectGitTruth(node, localGit as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
556
+ localConfirmedCount += 1;
557
+ continue;
558
+ }
559
+ } catch {
560
+ // Fall through to remote classification.
561
+ }
562
+ }
563
+
564
+ if (!daemonId || !args.dispatchMeshCommand) {
565
+ if (!isSelfNode) unavailableNodeIds.push(nodeId);
566
+ continue;
567
+ }
568
+
569
+ peerAttemptedCount += 1;
570
+ try {
571
+ const remoteGit = await probeRemoteMeshGitStatus({
572
+ dispatchMeshCommand: args.dispatchMeshCommand,
573
+ daemonId,
574
+ workspace,
575
+ timeoutMs: 8_000,
576
+ });
577
+ if (remoteGit) {
578
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
579
+ peerConfirmedCount += 1;
580
+ continue;
581
+ }
582
+ } catch {
583
+ // Strict direct-only path: do not fall back to persisted cloud truth here.
584
+ }
585
+
586
+ unavailableNodeIds.push(nodeId);
587
+ }
588
+
589
+ return {
590
+ directEvidenceCount: localConfirmedCount + peerConfirmedCount,
591
+ localConfirmedCount,
592
+ peerAttemptedCount,
593
+ peerConfirmedCount,
594
+ unavailableNodeIds,
595
+ };
596
+ }
597
+
598
+ function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
599
+ return {
600
+ sessionId: readStringValue(record?.sessionId) || 'unknown',
601
+ providerType: readStringValue(record?.providerType),
602
+ state: readLiveMeshSessionState(record),
603
+ lifecycle: readStringValue(record?.lifecycle),
604
+ surfaceKind: getSessionHostSurfaceKind(record as any),
605
+ recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
606
+ workspace: readStringValue(record?.workspace) ?? null,
607
+ title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
608
+ lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
609
+ isCached: false,
610
+ };
611
+ }
612
+
613
+ function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string): boolean {
614
+ const recordNodeId = readStringValue(record?.meta?.meshNodeId);
615
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
616
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
617
+ return !recordMeshId || recordMeshId === meshId;
618
+ }
619
+
620
+ function liveSessionRecordMatchesMeshWorkspace(record: any, meshId: string, workspace: string): boolean {
621
+ const recordWorkspace = readStringValue(record?.workspace);
622
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
623
+
624
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
625
+ if (recordMeshId) return recordMeshId === meshId;
626
+
627
+ return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
628
+ }
629
+
630
+ function readLiveMeshNodeWorkspace(args: {
631
+ meshId: string;
632
+ nodeId: string;
633
+ liveSessionRecords: any[];
634
+ allowCoordinatorSession?: boolean;
635
+ }): string {
636
+ const directNodeWorkspace = args.liveSessionRecords.find((record) => (
637
+ liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)
638
+ && readStringValue(record?.workspace)
639
+ ));
640
+ if (directNodeWorkspace) {
641
+ return readStringValue(directNodeWorkspace.workspace) || '';
642
+ }
643
+
644
+ if (args.allowCoordinatorSession) {
645
+ const coordinatorWorkspace = args.liveSessionRecords.find((record) => (
646
+ readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId
647
+ && readStringValue(record?.workspace)
648
+ ));
649
+ if (coordinatorWorkspace) {
650
+ return readStringValue(coordinatorWorkspace.workspace) || '';
651
+ }
652
+ }
653
+
654
+ return '';
655
+ }
656
+
657
+ function collectLiveMeshSessionRecords(args: {
658
+ meshId: string;
659
+ node: any;
660
+ nodeId: string;
661
+ liveSessionRecords: any[];
662
+ allowCoordinatorSession?: boolean;
663
+ }): any[] {
664
+ const matches = args.liveSessionRecords.filter((record) => {
665
+ const nodeWorkspace = readStringValue(args.node?.workspace);
666
+ if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)) return true;
667
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
668
+ });
669
+
670
+ if (args.allowCoordinatorSession) {
671
+ for (const record of args.liveSessionRecords) {
672
+ if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
673
+ const sessionId = readStringValue(record?.sessionId);
674
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
675
+ matches.push(record);
676
+ }
677
+ }
678
+
679
+ return matches;
680
+ }
681
+
682
+ function applyCachedInlineMeshNodeStatus(
683
+ status: Record<string, unknown>,
684
+ node: any,
685
+ options?: { skipGit?: boolean; skipError?: boolean; skipHealth?: boolean },
686
+ ): boolean {
687
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
688
+ const liveGit = buildInlineMeshTransitGitStatus(node);
689
+ const git = options?.skipGit ? undefined : (liveGit ?? buildCachedInlineMeshGitStatus(node));
690
+ const error = options?.skipError ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.error, node?.error));
691
+ const health = options?.skipHealth ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.health, node?.health));
201
692
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
202
- if (!git && !error && !health) return false;
203
- if (!machineStatus && !git && !error) return false;
693
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
694
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
695
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
696
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
697
+ if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
204
698
  if (git) status.git = git;
205
699
  if (error) status.error = error;
700
+ if (machineStatus) status.machineStatus = machineStatus;
701
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
702
+ if (updatedAt) status.updatedAt = updatedAt;
703
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
704
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
206
705
  if (health) {
207
706
  status.health = health;
208
707
  return true;
209
708
  }
210
709
  if (git) {
211
- const dirty = Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
212
- status.health = git.isGitRepo === false ? 'degraded' : dirty ? 'dirty' : 'online';
710
+ status.health = deriveMeshNodeHealthFromGit(git);
213
711
  return true;
214
712
  }
215
- return false;
713
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
216
714
  }
217
715
 
218
716
  async function resolveProviderTypeFromPriority(args: {
@@ -632,6 +1130,10 @@ export interface CommandRouterDeps {
632
1130
  statusVersion?: string;
633
1131
  /** Session host control plane */
634
1132
  sessionHostControl?: SessionHostControlPlane | null;
1133
+ /** Selected-coordinator mesh peer telemetry surface for target daemons, when supported by the runtime. */
1134
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
1135
+ /** Dispatch a command to a remote mesh node via P2P/relay. Injected by cloud runtime; absent in standalone. */
1136
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
635
1137
  }
636
1138
 
637
1139
  export interface CommandRouterResult {
@@ -756,29 +1258,45 @@ export class DaemonCommandRouter {
756
1258
 
757
1259
  public getCachedInlineMesh(meshId: string, inlineMesh?: unknown): any | undefined {
758
1260
  if (inlineMesh && typeof inlineMesh === 'object') {
759
- this.inlineMeshCache.set(meshId, inlineMesh as any);
760
- return inlineMesh as any;
1261
+ return this.warmInlineMeshCache(meshId, inlineMesh);
761
1262
  }
762
1263
  return this.inlineMeshCache.get(meshId);
763
1264
  }
764
1265
 
1266
+ private warmInlineMeshCache(meshId: string, inlineMesh?: unknown): any | undefined {
1267
+ if (!inlineMesh || typeof inlineMesh !== 'object') return undefined;
1268
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh as any);
1269
+ const cached = this.inlineMeshCache.get(meshId);
1270
+ if (cached) {
1271
+ const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
1272
+ this.inlineMeshCache.set(meshId, merged);
1273
+ return merged;
1274
+ }
1275
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh as any);
1276
+ return sanitizedInlineMesh as any;
1277
+ }
1278
+
765
1279
  private async getMeshForCommand(
766
1280
  meshId: string,
767
1281
  inlineMesh?: unknown,
768
1282
  options?: { preferInline?: boolean },
769
- ): Promise<{ mesh: any; inline: boolean } | null> {
1283
+ ): Promise<{ mesh: any; inline: boolean; source: 'inline_cache' | 'inline_bootstrap' | 'local_config' } | null> {
770
1284
  const preferInline = options?.preferInline === true;
771
1285
  if (preferInline) {
772
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
773
- if (cached) return { mesh: cached, inline: true };
1286
+ const cached = this.getCachedInlineMesh(meshId);
1287
+ if (cached) return { mesh: cached, inline: true, source: 'inline_cache' };
1288
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
1289
+ if (warmedInline) return { mesh: warmedInline, inline: true, source: 'inline_bootstrap' };
774
1290
  }
775
1291
  try {
776
1292
  const { getMesh } = await import('../config/mesh-config.js');
777
1293
  const mesh = getMesh(meshId);
778
- if (mesh) return { mesh, inline: false };
1294
+ if (mesh) return { mesh, inline: false, source: 'local_config' };
779
1295
  } catch { /* fall through to inline cache */ }
780
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
781
- return cached ? { mesh: cached, inline: true } : null;
1296
+ const cached = this.getCachedInlineMesh(meshId);
1297
+ if (cached) return { mesh: cached, inline: true, source: 'inline_cache' };
1298
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
1299
+ return warmedInline ? { mesh: warmedInline, inline: true, source: 'inline_bootstrap' } : null;
782
1300
  }
783
1301
 
784
1302
  private updateInlineMeshNode(meshId: string, mesh: any, node: any): void {
@@ -1075,6 +1593,7 @@ export class DaemonCommandRouter {
1075
1593
  const deletedSessionIds: string[] = [];
1076
1594
  const skippedSessionIds: string[] = [];
1077
1595
  const skippedLiveSessionIds: string[] = [];
1596
+ const skippedCoordinatorSessionIds: string[] = [];
1078
1597
  const deleteUnsupportedSessionIds: string[] = [];
1079
1598
  const recordsRemainSessionIds: string[] = [];
1080
1599
  const errors: Array<{ sessionId: string; error: string }> = [];
@@ -1109,6 +1628,12 @@ export class DaemonCommandRouter {
1109
1628
  const completed = this.isCompletedHostedSession(record);
1110
1629
  const surfaceKind = getSessionHostSurfaceKind(record);
1111
1630
  const liveRuntime = surfaceKind === 'live_runtime';
1631
+ const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
1632
+ if (!hasExplicitSessionIds && coordinatorSession) {
1633
+ skippedSessionIds.push(sessionId);
1634
+ skippedCoordinatorSessionIds.push(sessionId);
1635
+ continue;
1636
+ }
1112
1637
  if (!hasExplicitSessionIds && liveRuntime) {
1113
1638
  skippedSessionIds.push(sessionId);
1114
1639
  skippedLiveSessionIds.push(sessionId);
@@ -1178,6 +1703,7 @@ export class DaemonCommandRouter {
1178
1703
  deletedSessionIds,
1179
1704
  skippedSessionIds,
1180
1705
  skippedLiveSessionIds,
1706
+ skippedCoordinatorSessionIds,
1181
1707
  ...(deleteUnsupported ? {
1182
1708
  deleteUnsupported: true,
1183
1709
  effectiveCleanup: args.mode === 'stop_and_delete'
@@ -1332,7 +1858,8 @@ export class DaemonCommandRouter {
1332
1858
  }
1333
1859
 
1334
1860
  case 'get_pending_mesh_events': {
1335
- const events = drainPendingMeshCoordinatorEvents();
1861
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
1862
+ const events = drainPendingMeshCoordinatorEvents(meshId || undefined);
1336
1863
  return { success: true, events };
1337
1864
  }
1338
1865
 
@@ -1937,15 +2464,44 @@ export class DaemonCommandRouter {
1937
2464
  case 'get_mesh': {
1938
2465
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
1939
2466
  if (!meshId) return { success: false, error: 'meshId required' };
1940
- try {
1941
- const { getMesh } = await import('../config/mesh-config.js');
1942
- const mesh = getMesh(meshId);
1943
- if (mesh) return { success: true, mesh };
1944
- } catch { /* fall through to inline cache */ }
1945
- // Fallback: check in-memory cache for cloud-originating meshes
1946
- const cached = this.inlineMeshCache.get(meshId);
1947
- if (cached) return { success: true, mesh: cached };
1948
- return { success: false, error: 'Mesh not found' };
2467
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2468
+ if (!meshRecord?.mesh) return { success: false, error: 'Mesh not found' };
2469
+
2470
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
2471
+ const directTruth = await hydrateInlineMeshDirectTruth({
2472
+ mesh: meshRecord.mesh,
2473
+ meshSource: meshRecord.source,
2474
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
2475
+ statusInstanceId: this.deps.statusInstanceId,
2476
+ localMachineId: loadConfig().machineId || '',
2477
+ });
2478
+ const directTruthSatisfied = meshRecord.source !== 'inline_bootstrap' || directTruth.directEvidenceCount > 0;
2479
+ const sourceOfTruth = {
2480
+ membership: meshRecord.source === 'inline_cache'
2481
+ ? 'coordinator_inline_mesh_cache'
2482
+ : meshRecord.source === 'local_config'
2483
+ ? 'local_mesh_config'
2484
+ : 'inline_bootstrap_snapshot',
2485
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
2486
+ directPeerTruth: {
2487
+ required: requireDirectPeerTruth,
2488
+ satisfied: directTruthSatisfied,
2489
+ directEvidenceCount: directTruth.directEvidenceCount,
2490
+ localConfirmedCount: directTruth.localConfirmedCount,
2491
+ peerAttemptedCount: directTruth.peerAttemptedCount,
2492
+ peerConfirmedCount: directTruth.peerConfirmedCount,
2493
+ unavailableNodeIds: directTruth.unavailableNodeIds,
2494
+ },
2495
+ };
2496
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
2497
+ return {
2498
+ success: false,
2499
+ code: 'mesh_direct_peer_truth_unavailable',
2500
+ error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.',
2501
+ sourceOfTruth,
2502
+ };
2503
+ }
2504
+ return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
1949
2505
  }
1950
2506
 
1951
2507
  case 'create_mesh': {
@@ -2531,7 +3087,16 @@ export class DaemonCommandRouter {
2531
3087
  cliType,
2532
3088
  };
2533
3089
  }
2534
- const workspace = typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '';
3090
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
3091
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
3092
+ : [];
3093
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
3094
+ const workspace = readLiveMeshNodeWorkspace({
3095
+ meshId,
3096
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ''),
3097
+ liveSessionRecords: liveMeshSessions,
3098
+ allowCoordinatorSession: true,
3099
+ }) || (typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '');
2535
3100
  if (!workspace) return { success: false, error: 'Coordinator node workspace required', meshId, cliType };
2536
3101
  if (!cliType) {
2537
3102
  const resolved = await resolveProviderTypeFromPriority({
@@ -2884,98 +3449,210 @@ export class DaemonCommandRouter {
2884
3449
  const { readLedgerEntries, getLedgerSummary } = await import('../mesh/mesh-ledger.js');
2885
3450
  const ledgerEntries = readLedgerEntries(meshId, { tail: 20 });
2886
3451
  const ledgerSummary = getLedgerSummary(meshId);
3452
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
3453
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
3454
+ : [];
3455
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
2887
3456
 
3457
+ const localMachineId = loadConfig().machineId || '';
3458
+ const selectedCoordinatorNodeId = readStringValue(
3459
+ mesh.coordinator?.preferredNodeId,
3460
+ (mesh.nodes?.[0] as any)?.id,
3461
+ (mesh.nodes?.[0] as any)?.nodeId,
3462
+ );
3463
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes)
3464
+ ? selectedCoordinatorNodeId
3465
+ : undefined;
3466
+ const refreshedAt = new Date().toISOString();
2888
3467
  const nodeStatuses = [];
2889
- for (const node of mesh.nodes || []) {
3468
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
3469
+ const nodeId = String(node.id || node.nodeId || '');
3470
+ const daemonId = readStringValue(node.daemonId);
3471
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
3472
+ const isSelfNode = Boolean(
3473
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
3474
+ ) || Boolean(
3475
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId),
3476
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
2890
3477
  const status: Record<string, unknown> = {
2891
- nodeId: node.id || node.nodeId,
3478
+ nodeId,
2892
3479
  machineLabel: node.machineLabel || node.id || node.nodeId,
2893
3480
  workspace: node.workspace,
2894
3481
  repoRoot: node.repoRoot,
2895
3482
  isLocalWorktree: node.isLocalWorktree,
2896
3483
  worktreeBranch: node.worktreeBranch,
2897
- daemonId: node.daemonId,
3484
+ daemonId,
2898
3485
  machineId: node.machineId,
3486
+ machineStatus: node.machineStatus,
2899
3487
  health: 'unknown',
2900
3488
  providers: node.providers || [],
3489
+ providerPriority,
2901
3490
  activeSessions: [],
3491
+ activeSessionDetails: [],
3492
+ launchReady: false,
2902
3493
  };
2903
- if (node.workspace && typeof node.workspace === 'string') {
2904
- if (!fs.existsSync(node.workspace as string) && applyCachedInlineMeshNodeStatus(status, node)) {
2905
- nodeStatuses.push(status);
2906
- continue;
3494
+ if (isSelfNode) {
3495
+ status.connection = {
3496
+ perspective: 'selected_coordinator',
3497
+ source: 'mesh_peer_status',
3498
+ state: 'self',
3499
+ transport: 'local',
3500
+ reported: true,
3501
+ reason: 'Selected coordinator daemon',
3502
+ lastStateChangeAt: refreshedAt,
3503
+ };
3504
+ } else if (daemonId) {
3505
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
3506
+ status.connection = connection ?? {
3507
+ perspective: 'selected_coordinator',
3508
+ source: 'not_reported',
3509
+ state: 'unknown',
3510
+ transport: 'unknown',
3511
+ reported: false,
3512
+ reason: 'No live mesh peer telemetry reported by the selected coordinator yet.',
3513
+ };
3514
+ } else {
3515
+ status.connection = {
3516
+ perspective: 'selected_coordinator',
3517
+ source: 'not_reported',
3518
+ state: 'unknown',
3519
+ transport: 'unknown',
3520
+ reported: false,
3521
+ reason: 'Node has no daemon id, so mesh transport cannot be reported from the selected coordinator.',
3522
+ };
3523
+ }
3524
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
3525
+ meshId,
3526
+ node,
3527
+ nodeId,
3528
+ liveSessionRecords: liveMeshSessions,
3529
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
3530
+ });
3531
+ const workspace = readLiveMeshNodeWorkspace({
3532
+ meshId,
3533
+ nodeId,
3534
+ liveSessionRecords: matchedLiveSessionRecords,
3535
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
3536
+ }) || (typeof node.workspace === 'string' ? node.workspace : '');
3537
+ status.workspace = workspace || node.workspace;
3538
+ if (matchedLiveSessionRecords.length > 0) {
3539
+ const sessionIds = matchedLiveSessionRecords
3540
+ .map((record: any) => typeof record?.sessionId === 'string' ? record.sessionId : '')
3541
+ .filter(Boolean);
3542
+ const providerTypes = matchedLiveSessionRecords
3543
+ .map((record: any) => readStringValue(record?.providerType))
3544
+ .filter(Boolean) as string[];
3545
+ status.activeSessions = sessionIds;
3546
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
3547
+ if (providerTypes.length > 0) {
3548
+ status.providers = Array.from(new Set([...(Array.isArray(status.providers) ? status.providers as string[] : []), ...providerTypes]));
2907
3549
  }
2908
- try {
2909
- const { execFile } = await import('node:child_process');
2910
- const { promisify } = await import('node:util');
2911
- const execFileAsync = promisify(execFile);
2912
-
2913
- const runGit = async (args: string[]): Promise<string> => {
2914
- const result = await execFileAsync('git', ['-C', node.workspace as string, ...args], {
2915
- encoding: 'utf8',
2916
- timeout: 10_000,
2917
- });
2918
- return result.stdout.trim();
2919
- };
2920
-
2921
- const branch = await runGit(['branch', '--show-current']).catch(() => '');
2922
- const porc = await runGit(['status', '--porcelain']).catch(() => '');
2923
- const headCommit = await runGit(['rev-parse', '--short', 'HEAD']).catch(() => null);
2924
- const headMessage = await runGit(['log', '-1', '--format=%s']).catch(() => null);
2925
- const upstream = await runGit(['rev-parse', '--abbrev-ref', '@{upstream}']).catch(() => null);
2926
- const aheadBehind = await runGit(['rev-list', '--left-right', '--count', '@{upstream}...HEAD']).catch(() => '');
2927
- const stashCount = await runGit(['stash', 'list']).catch(() => '');
2928
-
2929
- let ahead = 0, behind = 0;
2930
- if (aheadBehind) {
2931
- const parts = aheadBehind.split(/\s+/);
2932
- if (parts.length >= 2) {
2933
- behind = parseInt(parts[0], 10) || 0;
2934
- ahead = parseInt(parts[1], 10) || 0;
3550
+ }
3551
+ if (workspace) {
3552
+ if (!fs.existsSync(workspace)) {
3553
+ // Workspace not local — prefer direct live inline truth, then attempt a P2P git probe.
3554
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
3555
+ let remoteProbeApplied = false;
3556
+ if (inlineTransitGit) {
3557
+ status.git = inlineTransitGit;
3558
+ status.health = inlineTransitGit.isGitRepo
3559
+ ? deriveMeshNodeHealthFromGit(inlineTransitGit as unknown as Record<string, unknown>)
3560
+ : 'degraded';
3561
+ remoteProbeApplied = true;
3562
+ } else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand) {
3563
+ try {
3564
+ const remoteGit = await probeRemoteMeshGitStatus({
3565
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
3566
+ daemonId,
3567
+ workspace,
3568
+ timeoutMs: 8000,
3569
+ });
3570
+ if (remoteGit) {
3571
+ status.git = remoteGit;
3572
+ status.health = remoteGit.isGitRepo
3573
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
3574
+ : 'degraded';
3575
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
3576
+ remoteProbeApplied = true;
3577
+ }
3578
+ } catch {
3579
+ const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
3580
+ const refreshedConnectionState = readStringValue(refreshedConnection?.state);
3581
+ if (refreshedConnection && refreshedConnectionState === 'connected') {
3582
+ status.connection = refreshedConnection;
3583
+ try {
3584
+ const remoteGit = await probeRemoteMeshGitStatus({
3585
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
3586
+ daemonId,
3587
+ workspace,
3588
+ timeoutMs: 12000,
3589
+ });
3590
+ if (remoteGit) {
3591
+ status.git = remoteGit;
3592
+ status.health = remoteGit.isGitRepo
3593
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
3594
+ : 'degraded';
3595
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
3596
+ remoteProbeApplied = true;
3597
+ }
3598
+ } catch {
3599
+ // Probe timed out again or P2P unavailable — fall back to cached status
3600
+ }
3601
+ }
2935
3602
  }
2936
3603
  }
2937
-
2938
- const dirty = porc.length > 0;
2939
- const lines = porc ? porc.split('\n').filter(Boolean) : [];
2940
- let staged = 0, modified = 0, untracked = 0, deleted = 0, renamed = 0;
2941
- for (const line of lines) {
2942
- const xy = line.slice(0, 2);
2943
- if (xy[0] !== ' ' && xy[0] !== '?') staged++;
2944
- if (xy[1] === 'M') modified++;
2945
- if (xy[1] === 'D') deleted++;
2946
- if (xy[0] === 'R' || xy[1] === 'R') renamed++;
2947
- if (xy === '??') untracked++;
3604
+ if (!remoteProbeApplied) {
3605
+ const connectionState = readStringValue((status.connection as any)?.state);
3606
+ const pendingPeerGitProbe = !inlineTransitGit
3607
+ && !isSelfNode
3608
+ && !!daemonId
3609
+ && (
3610
+ readStringValue(status.machineStatus) === 'online'
3611
+ || readStringValue(status.health) === 'online'
3612
+ || connectionState === 'connecting'
3613
+ || connectionState === 'connected'
3614
+ || connectionState === 'unknown'
3615
+ );
3616
+ if (pendingPeerGitProbe) {
3617
+ status.gitProbePending = true;
3618
+ status.health = 'unknown';
3619
+ }
3620
+ if (applyCachedInlineMeshNodeStatus(
3621
+ status,
3622
+ node,
3623
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : undefined,
3624
+ )) {
3625
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
3626
+ nodeStatuses.push(status);
3627
+ continue;
3628
+ }
3629
+ if (meshRecord?.source === 'inline_cache' && !isSelfNode) {
3630
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
3631
+ nodeStatuses.push(status);
3632
+ continue;
3633
+ }
2948
3634
  }
2949
-
2950
- status.git = {
2951
- workspace: node.workspace,
2952
- repoRoot: node.workspace,
2953
- isGitRepo: true,
2954
- branch: branch || null,
2955
- headCommit,
2956
- headMessage,
2957
- upstream,
2958
- ahead,
2959
- behind,
2960
- staged,
2961
- modified,
2962
- untracked,
2963
- deleted,
2964
- renamed,
2965
- hasConflicts: false,
2966
- conflictFiles: [],
2967
- stashCount: stashCount ? stashCount.split('\n').filter(Boolean).length : 0,
2968
- lastCheckedAt: Date.now(),
2969
- };
2970
- status.health = branch ? (dirty ? 'dirty' : 'online') : 'degraded';
2971
- } catch {
2972
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
2973
- status.health = 'degraded';
3635
+ } else {
3636
+ try {
3637
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
3638
+ status.git = gitStatus;
3639
+ recordInlineMeshDirectGitTruth(node, gitStatus as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
3640
+ if (gitStatus.isGitRepo) {
3641
+ status.health = deriveMeshNodeHealthFromGit(gitStatus as unknown as Record<string, unknown>);
3642
+ } else {
3643
+ status.health = 'degraded';
3644
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
3645
+ }
3646
+ } catch {
3647
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
3648
+ status.health = 'degraded';
3649
+ }
2974
3650
  }
2975
3651
  }
2976
3652
  } else {
2977
3653
  applyCachedInlineMeshNodeStatus(status, node);
2978
3654
  }
3655
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
2979
3656
  nodeStatuses.push(status);
2980
3657
  }
2981
3658
 
@@ -2985,6 +3662,16 @@ export class DaemonCommandRouter {
2985
3662
  meshName: mesh.name,
2986
3663
  repoIdentity: mesh.repoIdentity,
2987
3664
  defaultBranch: mesh.defaultBranch,
3665
+ refreshedAt: new Date().toISOString(),
3666
+ sourceOfTruth: {
3667
+ membership: meshRecord?.source === 'inline_cache'
3668
+ ? 'coordinator_inline_mesh_cache'
3669
+ : meshRecord?.source === 'local_config'
3670
+ ? 'local_mesh_config'
3671
+ : 'inline_bootstrap_snapshot',
3672
+ coordinatorOwnsLiveTruth: meshRecord?.source !== 'inline_bootstrap',
3673
+ historicalEvidenceOnly: ['recoveryHints', 'ledger.summary', 'queue.summary'],
3674
+ },
2988
3675
  nodes: nodeStatuses,
2989
3676
  queue: { tasks: queue, summary: queueSummary },
2990
3677
  ledger: { entries: ledgerEntries, summary: ledgerSummary },