@adhdev/daemon-core 0.9.82-rc.5 → 0.9.82-rc.51

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.
@@ -39,6 +39,16 @@ import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../sessi
39
39
  import { createHermesManualMeshCoordinatorSetup, resolveMeshCoordinatorSetup } from './mesh-coordinator.js';
40
40
  import { buildSessionEntries } from '../status/builders.js';
41
41
  import { handleMeshForwardEvent, drainPendingMeshCoordinatorEvents } from '../mesh/mesh-events.js';
42
+ import { buildMeshHostRequiredFailure, normalizeMeshDaemonRole, resolveMeshHostStatus } from '../mesh/mesh-host-ownership.js';
43
+ import {
44
+ MESH_REFINE_CONFIG_LOCATIONS,
45
+ MESH_REFINE_CONFIG_SCHEMA,
46
+ loadMeshRefineConfig,
47
+ resolveMeshRefineValidationPlan,
48
+ suggestMeshRefineConfig,
49
+ validateMeshRefineConfig,
50
+ type MeshRefineValidationCommandPlan,
51
+ } from '../mesh/refine-config.js';
42
52
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
43
53
  import { getSessionCompletionMarker } from '../status/snapshot.js';
44
54
  import { execNpmCommandSync, resolveCurrentGlobalInstallSurface, spawnDetachedDaemonUpgradeHelper } from './upgrade-helper.js';
@@ -114,14 +124,93 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
114
124
  return undefined;
115
125
  }
116
126
 
117
- function readGitSubmodules(value: unknown): GitSubmoduleStatus[] | undefined {
127
+ function summarizeRepoMeshDebugGit(git: unknown): Record<string, unknown> | null {
128
+ const record = readObjectRecord(git);
129
+ if (!Object.keys(record).length) return null;
130
+ const submodules = Array.isArray(record.submodules)
131
+ ? record.submodules.map((entry: any) => ({
132
+ path: readStringValue(entry?.path) ?? null,
133
+ commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
134
+ dirty: readBooleanValue(entry?.dirty) ?? false,
135
+ outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false,
136
+ }))
137
+ : [];
138
+ return {
139
+ isGitRepo: readBooleanValue(record.isGitRepo),
140
+ workspace: readStringValue(record.workspace) ?? null,
141
+ repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
142
+ branch: readStringValue(record.branch) ?? null,
143
+ upstream: readStringValue(record.upstream) ?? null,
144
+ upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
145
+ headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
146
+ ahead: readNumberValue(record.ahead) ?? null,
147
+ behind: readNumberValue(record.behind) ?? null,
148
+ dirtyCounts: {
149
+ staged: readNumberValue(record.staged) ?? 0,
150
+ modified: readNumberValue(record.modified) ?? 0,
151
+ untracked: readNumberValue(record.untracked) ?? 0,
152
+ deleted: readNumberValue(record.deleted) ?? 0,
153
+ renamed: readNumberValue(record.renamed) ?? 0,
154
+ },
155
+ lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
156
+ submoduleCount: submodules.length,
157
+ submodules,
158
+ };
159
+ }
160
+
161
+ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
162
+ const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
163
+ return {
164
+ success: status?.success,
165
+ meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
166
+ refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
167
+ sourceOfTruth: status?.sourceOfTruth ?? null,
168
+ nodeCount: nodes.length,
169
+ nodes: nodes.map((node: any) => ({
170
+ nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
171
+ daemonId: readStringValue(node?.daemonId, node?.daemon_id) ?? null,
172
+ workspace: readStringValue(node?.workspace, node?.git?.workspace) ?? null,
173
+ health: readStringValue(node?.health) ?? null,
174
+ machineStatus: readStringValue(node?.machineStatus, node?.machine_status) ?? null,
175
+ connection: node?.connection && typeof node.connection === 'object' ? {
176
+ state: readStringValue(node.connection.state) ?? null,
177
+ transport: readStringValue(node.connection.transport) ?? null,
178
+ source: readStringValue(node.connection.source) ?? null,
179
+ reported: readBooleanValue(node.connection.reported) ?? null,
180
+ } : null,
181
+ gitProbePending: node?.gitProbePending === true,
182
+ launchReady: node?.launchReady === true,
183
+ git: summarizeRepoMeshDebugGit(node?.git),
184
+ })),
185
+ };
186
+ }
187
+
188
+ function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>): void {
189
+ try {
190
+ LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
191
+ } catch {
192
+ LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${event}`);
193
+ }
194
+ }
195
+
196
+ function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {
197
+ const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\/]+$/, '') : '';
198
+ const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : '';
199
+ if (!normalizedPath) return undefined;
200
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
201
+ if (!normalizedRoot) return undefined;
202
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, '')}`;
203
+ }
204
+
205
+ function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {
118
206
  if (!Array.isArray(value)) return undefined;
119
207
  const submodules = value
120
208
  .map(entry => {
121
209
  const submodule = readObjectRecord(entry);
122
210
  const path = readStringValue(submodule.path);
123
211
  const commit = readStringValue(submodule.commit);
124
- const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
212
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root)
213
+ ?? joinRepoPath(parentRepoRoot, path);
125
214
  if (!path || !commit || !repoPath) return null;
126
215
  return {
127
216
  path,
@@ -137,60 +226,11 @@ function readGitSubmodules(value: unknown): GitSubmoduleStatus[] | undefined {
137
226
  return submodules.length > 0 ? submodules : undefined;
138
227
  }
139
228
 
140
- function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
141
- const cachedStatus = readObjectRecord(node?.cachedStatus);
142
- const cachedGit = readObjectRecord(cachedStatus.git);
143
- if (Object.keys(cachedGit).length) {
144
- const conflictFiles = Array.isArray(cachedGit.conflictFiles)
145
- ? cachedGit.conflictFiles.filter((value: unknown): value is string => typeof value === 'string')
146
- : [];
147
- const conflictCount = readNumberValue(cachedGit.conflicts) ?? conflictFiles.length;
148
- const hasConflicts = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount > 0;
149
- const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
150
- if (isGitRepo !== undefined) {
151
- const submodules = readGitSubmodules(cachedGit.submodules);
152
- return {
153
- workspace: readStringValue(cachedGit.workspace, node?.workspace) || '',
154
- repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
155
- isGitRepo,
156
- branch: readStringValue(cachedGit.branch) ?? null,
157
- headCommit: readStringValue(cachedGit.headCommit) ?? null,
158
- headMessage: readStringValue(cachedGit.headMessage) ?? null,
159
- upstream: readStringValue(cachedGit.upstream) ?? null,
160
- ahead: readNumberValue(cachedGit.ahead) ?? 0,
161
- behind: readNumberValue(cachedGit.behind) ?? 0,
162
- staged: readNumberValue(cachedGit.staged) ?? 0,
163
- modified: readNumberValue(cachedGit.modified) ?? 0,
164
- untracked: readNumberValue(cachedGit.untracked) ?? 0,
165
- deleted: readNumberValue(cachedGit.deleted) ?? 0,
166
- renamed: readNumberValue(cachedGit.renamed) ?? 0,
167
- hasConflicts,
168
- conflictFiles,
169
- stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
170
- lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now(),
171
- ...(submodules ? { submodules } : {}),
172
- };
173
- }
174
- }
175
-
176
- const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
177
- const gitResult = readObjectRecord(rawGit.result);
178
- const directStatus = readObjectRecord(rawGit.status);
179
- const nestedStatus = readObjectRecord(gitResult.status);
180
- const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
181
- const probeGit = readObjectRecord(rawProbe.git);
182
- const probeGitResult = readObjectRecord(probeGit.result);
183
- const probeDirectStatus = readObjectRecord(probeGit.status);
184
- const probeNestedStatus = readObjectRecord(probeGitResult.status);
185
- const status = Object.keys(directStatus).length
186
- ? directStatus
187
- : Object.keys(nestedStatus).length
188
- ? nestedStatus
189
- : Object.keys(probeDirectStatus).length
190
- ? probeDirectStatus
191
- : Object.keys(probeNestedStatus).length
192
- ? probeNestedStatus
193
- : {};
229
+ function normalizeInlineMeshGitStatus(
230
+ status: Record<string, unknown>,
231
+ node: any,
232
+ options?: { lastCheckedAt?: number },
233
+ ): Record<string, unknown> | undefined {
194
234
  const isGitRepo = readBooleanValue(status.isGitRepo);
195
235
  if (!Object.keys(status).length || isGitRepo === undefined) return undefined;
196
236
  const conflictFiles = Array.isArray(status.conflictFiles)
@@ -198,10 +238,11 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
198
238
  : [];
199
239
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
200
240
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
201
- const submodules = readGitSubmodules(status.submodules);
241
+ const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || undefined;
242
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
202
243
  return {
203
244
  workspace: readStringValue(status.workspace, node?.workspace) || '',
204
- repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
245
+ repoRoot: repoRoot ?? null,
205
246
  isGitRepo,
206
247
  branch: readStringValue(status.branch) ?? null,
207
248
  headCommit: readStringValue(status.headCommit) ?? null,
@@ -217,31 +258,583 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
217
258
  hasConflicts,
218
259
  conflictFiles,
219
260
  stashCount: readNumberValue(status.stashCount) ?? 0,
220
- lastCheckedAt: Date.now(),
261
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
221
262
  ...(submodules ? { submodules } : {}),
222
263
  };
223
264
  }
224
265
 
225
- function applyCachedInlineMeshNodeStatus(status: Record<string, unknown>, node: any): boolean {
266
+ function scoreInlineMeshGitStatus(git: Record<string, unknown> | undefined): number {
267
+ if (!git) return Number.NEGATIVE_INFINITY;
268
+ let score = 0;
269
+ if (readBooleanValue(git.isGitRepo) === true) score += 50;
270
+ if (readBooleanValue(git.isGitRepo) === false) score -= 10;
271
+ if (readStringValue(git.branch)) score += 20;
272
+ if (readStringValue(git.headCommit)) score += 20;
273
+ if (readStringValue(git.upstream)) score += 10;
274
+ if (readStringValue(git.upstreamStatus)) score += 5;
275
+ if (readNumberValue(git.ahead) !== undefined) score += 2;
276
+ if (readNumberValue(git.behind) !== undefined) score += 2;
277
+ if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
278
+ if (readStringValue(git.error)) score -= 20;
279
+ return score;
280
+ }
281
+
282
+ function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
283
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
284
+ const gitResult = readObjectRecord(rawGit.result);
285
+ const directStatus = readObjectRecord(rawGit.status);
286
+ const nestedStatus = readObjectRecord(gitResult.status);
287
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
288
+ const probeGit = readObjectRecord(rawProbe.git);
289
+ const probeGitResult = readObjectRecord(probeGit.result);
290
+ const probeDirectStatus = readObjectRecord(probeGit.status);
291
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
292
+ const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
293
+ let best: { git: Record<string, unknown>; score: number } | null = null;
294
+ for (const status of candidates) {
295
+ const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
296
+ if (!normalized) continue;
297
+ const score = scoreInlineMeshGitStatus(normalized);
298
+ if (!best || score > best.score) best = { git: normalized, score };
299
+ }
300
+ return best?.git;
301
+ }
302
+
303
+ function shouldRefreshStalePendingAggregate(snapshot: any, options?: { requireDirectPeerTruth?: boolean }): boolean {
304
+ if (options?.requireDirectPeerTruth !== true || !Array.isArray(snapshot?.nodes)) return false;
305
+ return snapshot.nodes.some((node: any) => {
306
+ if (node?.gitProbePending !== true) return false;
307
+ const git = readObjectRecord(node?.git);
308
+ return !readBooleanValue(git.isGitRepo) && !readStringValue(git.branch, git.headCommit, git.upstream);
309
+ });
310
+ }
311
+
312
+ function buildLivePeerGitConnection(connection: Record<string, unknown>, timestamp = new Date().toISOString()): Record<string, unknown> {
313
+ const source = readStringValue(connection.source);
314
+ const transport = readStringValue(connection.transport);
315
+ return {
316
+ ...connection,
317
+ perspective: readStringValue(connection.perspective) ?? 'selected_coordinator',
318
+ source: source && source !== 'not_reported' ? source : 'mesh_peer_status',
319
+ state: 'connected',
320
+ transport: transport && transport !== 'unknown' ? transport : 'direct',
321
+ reported: true,
322
+ reason: 'Live peer git snapshot reported by the selected coordinator.',
323
+ lastStateChangeAt: readStringValue(connection.lastStateChangeAt) ?? timestamp,
324
+ };
325
+ }
326
+
327
+ function recordInlineMeshDirectGitTruth(
328
+ node: any,
329
+ git: Record<string, unknown>,
330
+ source: 'selected_coordinator_local_git' | 'selected_coordinator_mesh_p2p_git',
331
+ ): void {
332
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return;
333
+ const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
334
+ const updatedAt = new Date(checkedAt).toISOString();
335
+ const nextGit: Record<string, unknown> = {
336
+ ...git,
337
+ lastCheckedAt: checkedAt,
338
+ };
339
+ node.lastGit = {
340
+ source,
341
+ checkedAt,
342
+ status: nextGit,
343
+ };
344
+ node.last_git = node.lastGit;
345
+ node.machineStatus = 'online';
346
+ node.updatedAt = updatedAt;
347
+ node.lastSeenAt = updatedAt;
348
+ const repoRoot = readStringValue(nextGit.repoRoot);
349
+ if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
350
+ }
351
+
352
+ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
353
+ const liveGit = buildInlineMeshTransitGitStatus(node);
354
+ if (liveGit) return liveGit;
355
+
356
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
357
+ const cachedGit = readObjectRecord(cachedStatus.git);
358
+ if (!Object.keys(cachedGit).length) return undefined;
359
+ return normalizeInlineMeshGitStatus(cachedGit, node);
360
+ }
361
+
362
+ function shouldDiscardCachedInlineMeshStatus(node: any): boolean {
363
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
364
+ if (!Object.keys(cachedStatus).length) return false;
365
+ const cachedGit = readObjectRecord(cachedStatus.git);
366
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
367
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
368
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
369
+ const branch = readStringValue(cachedGit.branch);
370
+ const headCommit = readStringValue(cachedGit.headCommit);
371
+ return isGitRepo === false && !branch && !headCommit;
372
+ }
373
+
374
+ function stripInlineMeshTransientNodeState(node: any): any {
375
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return node;
376
+ const {
377
+ cachedStatus,
378
+ lastGit: _lastGit,
379
+ last_git: _lastGitLegacy,
380
+ lastProbe: _lastProbe,
381
+ last_probe: _lastProbeLegacy,
382
+ error: _error,
383
+ health: _health,
384
+ machineStatus: _machineStatus,
385
+ lastSeenAt: _lastSeenAt,
386
+ last_seen_at: _lastSeenAtLegacy,
387
+ updatedAt: _updatedAt,
388
+ updated_at: _updatedAtLegacy,
389
+ activeSession: _activeSession,
390
+ active_session: _activeSessionLegacy,
391
+ activeSessionId: _activeSessionId,
392
+ active_session_id: _activeSessionIdLegacy,
393
+ sessionId: _sessionId,
394
+ session_id: _sessionIdLegacy,
395
+ providerType: _providerType,
396
+ provider_type: _providerTypeLegacy,
397
+ ...rest
398
+ } = node as Record<string, unknown>;
399
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
400
+ return { ...rest, cachedStatus };
401
+ }
402
+ return rest;
403
+ }
404
+
405
+ function hasInlineMeshTransientNodeState(node: any): boolean {
406
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
407
+ return 'cachedStatus' in node
408
+ || 'lastGit' in node
409
+ || 'last_git' in node
410
+ || 'lastProbe' in node
411
+ || 'last_probe' in node
412
+ || 'error' in node
413
+ || 'health' in node
414
+ || 'machineStatus' in node
415
+ || 'lastSeenAt' in node
416
+ || 'last_seen_at' in node
417
+ || 'updatedAt' in node
418
+ || 'updated_at' in node
419
+ || 'activeSession' in node
420
+ || 'active_session' in node
421
+ || 'activeSessionId' in node
422
+ || 'active_session_id' in node
423
+ || 'sessionId' in node
424
+ || 'session_id' in node
425
+ || 'providerType' in node
426
+ || 'provider_type' in node;
427
+ }
428
+
429
+ function inlineMeshCarriesTransientNodeTruth(inlineMesh: any): boolean {
430
+ if (!inlineMesh || typeof inlineMesh !== 'object' || Array.isArray(inlineMesh)) return false;
431
+ if (!Array.isArray(inlineMesh.nodes) || inlineMesh.nodes.length === 0) return false;
432
+ return inlineMesh.nodes.some((node: any) => hasInlineMeshTransientNodeState(node));
433
+ }
434
+
435
+ function readInlineMeshNodeId(node: any): string {
436
+ return readStringValue(node?.id, node?.nodeId) || '';
437
+ }
438
+
439
+ function sanitizeInlineMesh(inlineMesh: any): any {
440
+ if (!inlineMesh || typeof inlineMesh !== 'object' || Array.isArray(inlineMesh)) return inlineMesh;
441
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
442
+ let changed = false;
443
+ const nodes = inlineMesh.nodes.map((node: any) => {
444
+ if (!hasInlineMeshTransientNodeState(node)) return node;
445
+ changed = true;
446
+ return stripInlineMeshTransientNodeState(node);
447
+ });
448
+ if (!changed) return inlineMesh;
449
+ return {
450
+ ...inlineMesh,
451
+ nodes,
452
+ };
453
+ }
454
+
455
+ function reconcileInlineMeshCache(cached: any, incoming: any): any {
456
+ if (!cached || typeof cached !== 'object' || Array.isArray(cached)) return incoming;
457
+ if (!incoming || typeof incoming !== 'object' || Array.isArray(incoming)) return cached;
458
+ const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
459
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
460
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
461
+
462
+ const incomingById = new Map<string, any>();
463
+ for (const node of incomingNodes) {
464
+ const nodeId = readInlineMeshNodeId(node);
465
+ if (nodeId) incomingById.set(nodeId, node);
466
+ }
467
+
468
+ const nodes = cachedNodes.map((cachedNode: any) => {
469
+ const nodeId = readInlineMeshNodeId(cachedNode);
470
+ const incomingNode = nodeId ? incomingById.get(nodeId) : undefined;
471
+ if (!incomingNode) return cachedNode;
472
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
473
+ return { ...cachedNode, ...incomingNode };
474
+ }
475
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
476
+ });
477
+
478
+ return {
479
+ ...cached,
480
+ ...incoming,
481
+ nodes,
482
+ };
483
+ }
484
+
485
+ function hasGitWorktreeChanges(git: Record<string, unknown> | null | undefined): boolean {
486
+ if (!git) return false;
487
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
488
+ }
489
+
490
+ function getGitSubmoduleDriftState(git: Record<string, unknown> | null | undefined): { dirty: boolean; outOfSync: boolean } {
491
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
492
+ let dirty = false;
493
+ let outOfSync = false;
494
+ for (const entry of submodules) {
495
+ const submodule = readObjectRecord(entry);
496
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
497
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
498
+ }
499
+ return { dirty, outOfSync };
500
+ }
501
+
502
+ function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
503
+ if (!git || readBooleanValue(git.isGitRepo) === false) return 'degraded';
504
+ const branch = readStringValue(git.branch);
505
+ if (!branch) return 'degraded';
506
+ const submoduleDrift = getGitSubmoduleDriftState(git);
507
+ if (submoduleDrift.outOfSync) return 'degraded';
508
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return 'dirty';
509
+ return 'online';
510
+ }
511
+
512
+ function readCachedInlineMeshActiveSessions(node: any): string[] {
513
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
514
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
515
+ const fallbackSession = Object.keys(activeSession).length
516
+ ? activeSession
517
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
518
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
519
+ return sessionId ? [sessionId] : [];
520
+ }
521
+
522
+ function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>> {
523
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
524
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
525
+ const fallbackSession = Object.keys(activeSession).length
526
+ ? activeSession
527
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
528
+ const sessionId = readStringValue(
529
+ fallbackSession.id,
530
+ fallbackSession.sessionId,
531
+ fallbackSession.session_id,
532
+ node?.activeSessionId,
533
+ node?.active_session_id,
534
+ node?.sessionId,
535
+ node?.session_id,
536
+ );
537
+ if (!sessionId) return [];
538
+ return [{
539
+ sessionId,
540
+ providerType: readStringValue(
541
+ fallbackSession.providerType,
542
+ fallbackSession.provider_type,
543
+ fallbackSession.cliType,
544
+ fallbackSession.cli_type,
545
+ fallbackSession.provider,
546
+ node?.providerType,
547
+ node?.provider_type,
548
+ ),
549
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
550
+ lifecycle: readStringValue(fallbackSession.lifecycle),
551
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
552
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
553
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
554
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
555
+ isCached: true,
556
+ }];
557
+ }
558
+
559
+ function readLiveMeshSessionState(record: any): string | undefined {
560
+ return readStringValue(
561
+ record?.meta?.sessionStatus,
562
+ record?.meta?.status,
563
+ record?.meta?.providerStatus,
564
+ record?.status,
565
+ record?.state,
566
+ record?.lifecycle,
567
+ );
568
+ }
569
+
570
+ function toIsoTimestamp(value: unknown): string | null {
571
+ if (typeof value === 'number' && Number.isFinite(value)) return new Date(value).toISOString();
572
+ const stringValue = readStringValue(value);
573
+ return stringValue || null;
574
+ }
575
+
576
+ function synthesizeMeshNodeFreshnessFromConnection(status: Record<string, unknown>): void {
577
+ const connection = readObjectRecord(status.connection);
578
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
579
+ const git = readObjectRecord(status.git);
580
+ const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
581
+ if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
582
+ if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
583
+ status.updatedAt = gitCheckedAt ?? connectionFreshAt;
584
+ }
585
+ }
586
+
587
+ function finalizeMeshNodeStatus(args: {
588
+ status: Record<string, unknown>;
589
+ node: any;
590
+ daemonId?: string;
591
+ isSelfNode: boolean;
592
+ }): void {
593
+ const { status, node, daemonId, isSelfNode } = args;
594
+ if (!readStringValue(status.machineStatus)) {
595
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
596
+ const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
597
+ if (machineStatus) status.machineStatus = machineStatus;
598
+ }
599
+ synthesizeMeshNodeFreshnessFromConnection(status);
600
+ const connectionState = readStringValue(readObjectRecord(status.connection).state);
601
+ status.launchReady = !!daemonId && (
602
+ readStringValue(status.machineStatus) === 'online'
603
+ || connectionState === 'connected'
604
+ || isSelfNode
605
+ );
606
+ }
607
+
608
+ async function probeRemoteMeshGitStatus(args: {
609
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
610
+ daemonId: string;
611
+ workspace: string;
612
+ timeoutMs: number;
613
+ }): Promise<Record<string, unknown> | null> {
614
+ if (!args.dispatchMeshCommand) return null;
615
+ const remoteResult = await Promise.race([
616
+ args.dispatchMeshCommand(args.daemonId, 'git_status', { workspace: args.workspace }),
617
+ new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), args.timeoutMs)),
618
+ ]) as any;
619
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
620
+ return remoteGit && typeof remoteGit === 'object' && typeof remoteGit.isGitRepo === 'boolean'
621
+ ? remoteGit as Record<string, unknown>
622
+ : null;
623
+ }
624
+
625
+ async function hydrateInlineMeshDirectTruth(args: {
626
+ mesh: any;
627
+ meshSource: 'inline_cache' | 'inline_bootstrap' | 'local_config';
628
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
629
+ statusInstanceId?: string;
630
+ localMachineId?: string;
631
+ }): Promise<{
632
+ directEvidenceCount: number;
633
+ localConfirmedCount: number;
634
+ peerAttemptedCount: number;
635
+ peerConfirmedCount: number;
636
+ unavailableNodeIds: string[];
637
+ }> {
638
+ const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
639
+ if (!nodes.length) {
640
+ return {
641
+ directEvidenceCount: 0,
642
+ localConfirmedCount: 0,
643
+ peerAttemptedCount: 0,
644
+ peerConfirmedCount: 0,
645
+ unavailableNodeIds: [],
646
+ };
647
+ }
648
+
649
+ const selectedCoordinatorNodeId = readStringValue(
650
+ args.mesh?.coordinator?.preferredNodeId,
651
+ nodes[0]?.id,
652
+ nodes[0]?.nodeId,
653
+ );
654
+
655
+ let localConfirmedCount = 0;
656
+ let peerAttemptedCount = 0;
657
+ let peerConfirmedCount = 0;
658
+ const unavailableNodeIds: string[] = [];
659
+
660
+ for (const [nodeIndex, node] of nodes.entries()) {
661
+ const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
662
+ const workspace = readStringValue(node?.workspace);
663
+ const daemonId = readStringValue(node?.daemonId);
664
+ const isSelfNode = Boolean(
665
+ nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
666
+ ) || Boolean(
667
+ daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId),
668
+ ) || Boolean(args.meshSource !== 'local_config' && nodeIndex === 0);
669
+
670
+ if (!workspace) {
671
+ if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
672
+ continue;
673
+ }
674
+
675
+ if (isSelfNode && fs.existsSync(workspace)) {
676
+ try {
677
+ const localGit = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
678
+ if (localGit?.isGitRepo) {
679
+ recordInlineMeshDirectGitTruth(node, localGit as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
680
+ localConfirmedCount += 1;
681
+ continue;
682
+ }
683
+ } catch {
684
+ // Fall through to remote classification.
685
+ }
686
+ }
687
+
688
+ if (!daemonId || !args.dispatchMeshCommand) {
689
+ if (!isSelfNode) unavailableNodeIds.push(nodeId);
690
+ continue;
691
+ }
692
+
693
+ peerAttemptedCount += 1;
694
+ try {
695
+ const remoteGit = await probeRemoteMeshGitStatus({
696
+ dispatchMeshCommand: args.dispatchMeshCommand,
697
+ daemonId,
698
+ workspace,
699
+ timeoutMs: 8_000,
700
+ });
701
+ if (remoteGit) {
702
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
703
+ peerConfirmedCount += 1;
704
+ continue;
705
+ }
706
+ } catch {
707
+ // Strict direct-only path: do not fall back to persisted cloud truth here.
708
+ }
709
+
710
+ unavailableNodeIds.push(nodeId);
711
+ }
712
+
713
+ return {
714
+ directEvidenceCount: localConfirmedCount + peerConfirmedCount,
715
+ localConfirmedCount,
716
+ peerAttemptedCount,
717
+ peerConfirmedCount,
718
+ unavailableNodeIds,
719
+ };
720
+ }
721
+
722
+ function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
723
+ return {
724
+ sessionId: readStringValue(record?.sessionId) || 'unknown',
725
+ providerType: readStringValue(record?.providerType),
726
+ state: readLiveMeshSessionState(record),
727
+ lifecycle: readStringValue(record?.lifecycle),
728
+ surfaceKind: getSessionHostSurfaceKind(record as any),
729
+ recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
730
+ workspace: readStringValue(record?.workspace) ?? null,
731
+ title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
732
+ lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
733
+ isCached: false,
734
+ };
735
+ }
736
+
737
+ function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string): boolean {
738
+ const recordNodeId = readStringValue(record?.meta?.meshNodeId);
739
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
740
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
741
+ return !recordMeshId || recordMeshId === meshId;
742
+ }
743
+
744
+ function liveSessionRecordMatchesMeshWorkspace(record: any, meshId: string, workspace: string): boolean {
745
+ const recordWorkspace = readStringValue(record?.workspace);
746
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
747
+
748
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
749
+ if (recordMeshId) return recordMeshId === meshId;
750
+
751
+ return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
752
+ }
753
+
754
+ function readLiveMeshNodeWorkspace(args: {
755
+ meshId: string;
756
+ nodeId: string;
757
+ liveSessionRecords: any[];
758
+ allowCoordinatorSession?: boolean;
759
+ }): string {
760
+ const directNodeWorkspace = args.liveSessionRecords.find((record) => (
761
+ liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)
762
+ && readStringValue(record?.workspace)
763
+ ));
764
+ if (directNodeWorkspace) {
765
+ return readStringValue(directNodeWorkspace.workspace) || '';
766
+ }
767
+
768
+ if (args.allowCoordinatorSession) {
769
+ const coordinatorWorkspace = args.liveSessionRecords.find((record) => (
770
+ readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId
771
+ && readStringValue(record?.workspace)
772
+ ));
773
+ if (coordinatorWorkspace) {
774
+ return readStringValue(coordinatorWorkspace.workspace) || '';
775
+ }
776
+ }
777
+
778
+ return '';
779
+ }
780
+
781
+ function collectLiveMeshSessionRecords(args: {
782
+ meshId: string;
783
+ node: any;
784
+ nodeId: string;
785
+ liveSessionRecords: any[];
786
+ allowCoordinatorSession?: boolean;
787
+ }): any[] {
788
+ const matches = args.liveSessionRecords.filter((record) => {
789
+ const nodeWorkspace = readStringValue(args.node?.workspace);
790
+ if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)) return true;
791
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
792
+ });
793
+
794
+ if (args.allowCoordinatorSession) {
795
+ for (const record of args.liveSessionRecords) {
796
+ if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
797
+ const sessionId = readStringValue(record?.sessionId);
798
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
799
+ matches.push(record);
800
+ }
801
+ }
802
+
803
+ return matches;
804
+ }
805
+
806
+ function applyCachedInlineMeshNodeStatus(
807
+ status: Record<string, unknown>,
808
+ node: any,
809
+ options?: { skipGit?: boolean; skipError?: boolean; skipHealth?: boolean },
810
+ ): boolean {
226
811
  const cachedStatus = readObjectRecord(node?.cachedStatus);
227
- const git = buildCachedInlineMeshGitStatus(node);
228
- const error = readStringValue(cachedStatus.error, node?.error);
229
- const health = readStringValue(cachedStatus.health, node?.health);
812
+ const liveGit = buildInlineMeshTransitGitStatus(node);
813
+ const git = options?.skipGit ? undefined : (liveGit ?? buildCachedInlineMeshGitStatus(node));
814
+ const error = options?.skipError ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.error, node?.error));
815
+ const health = options?.skipHealth ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.health, node?.health));
230
816
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
231
- if (!git && !error && !health) return false;
232
- if (!machineStatus && !git && !error) return false;
817
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
818
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
819
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
820
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
821
+ if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
233
822
  if (git) status.git = git;
234
823
  if (error) status.error = error;
824
+ if (machineStatus) status.machineStatus = machineStatus;
825
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
826
+ if (updatedAt) status.updatedAt = updatedAt;
827
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
828
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
235
829
  if (health) {
236
830
  status.health = health;
237
831
  return true;
238
832
  }
239
833
  if (git) {
240
- const dirty = Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
241
- status.health = git.isGitRepo === false ? 'degraded' : dirty ? 'dirty' : 'online';
834
+ status.health = deriveMeshNodeHealthFromGit(git);
242
835
  return true;
243
836
  }
244
- return false;
837
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
245
838
  }
246
839
 
247
840
  async function resolveProviderTypeFromPriority(args: {
@@ -276,13 +869,7 @@ async function resolveProviderTypeFromPriority(args: {
276
869
  }
277
870
  type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
278
871
  type MeshRefineValidationStatus = 'passed' | 'failed' | 'skipped';
279
- type MeshRefineValidationCommand = {
280
- command: string;
281
- args: string[];
282
- displayCommand: string;
283
- category: string;
284
- source: string;
285
- };
872
+ type MeshRefineValidationCommand = MeshRefineValidationCommandPlan;
286
873
 
287
874
  type MeshRefineValidationSummary = {
288
875
  status: MeshRefineValidationStatus;
@@ -292,6 +879,27 @@ type MeshRefineValidationSummary = {
292
879
  skippedReason?: string;
293
880
  timeoutMs: number;
294
881
  outputLimitBytes: number;
882
+ configSource?: string;
883
+ configSourceType?: string;
884
+ suggestions?: unknown[];
885
+ suggestedConfig?: unknown;
886
+ };
887
+
888
+ type MeshRefineStageStatus = 'passed' | 'failed' | 'skipped';
889
+
890
+ type MeshRefinePatchEquivalenceSummary = {
891
+ status: MeshRefineStageStatus;
892
+ equivalent: boolean;
893
+ baseHead: string;
894
+ branchHead: string;
895
+ mergeBase?: string;
896
+ mergedTree?: string;
897
+ expectedPatchId?: string;
898
+ actualPatchId?: string;
899
+ durationMs: number;
900
+ error?: string;
901
+ stdout?: string;
902
+ stderr?: string;
295
903
  };
296
904
 
297
905
  const REFINE_VALIDATION_CATEGORIES = ['typecheck', 'test', 'lint', 'build'] as const;
@@ -299,6 +907,7 @@ const REFINE_VALIDATION_TIMEOUT_MS = 120_000;
299
907
  const REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
300
908
  const REFINE_VALIDATION_SUMMARY_CHARS = 2_000;
301
909
  const REFINE_VALIDATION_MAX_COMMANDS = 4;
910
+ const REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
302
911
 
303
912
  function truncateValidationOutput(value: unknown): string {
304
913
  const text = typeof value === 'string' ? value : value == null ? '' : String(value);
@@ -306,171 +915,114 @@ function truncateValidationOutput(value: unknown): string {
306
915
  return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}\n[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
307
916
  }
308
917
 
309
- function readPackageScripts(workspace: string): Record<string, string> {
310
- try {
311
- const packageJsonPath = pathJoin(workspace, 'package.json');
312
- const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
313
- return parsed?.scripts && typeof parsed.scripts === 'object' && !Array.isArray(parsed.scripts)
314
- ? parsed.scripts as Record<string, string>
315
- : {};
316
- } catch {
317
- return {};
318
- }
319
- }
320
-
321
- function tokenizeValidationCommand(command: string): string[] | null {
322
- const trimmed = command.trim();
323
- if (!trimmed) return null;
324
- // Fail closed: the gate never hands shell syntax to a shell. Package-manager
325
- // scripts are invoked via execFile(binary, args), and metacharacters/quotes are
326
- // rejected before tokenization so `npm run test && rm -rf` cannot be smuggled in.
327
- if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
328
- const tokens = trimmed.split(/\s+/).filter(Boolean);
329
- if (!tokens.length) return null;
330
- if (tokens.some(token => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
331
- return tokens;
332
- }
333
-
334
- function scriptMatchesValidationCategory(scriptName: string, category: string): boolean {
335
- return scriptName === category || scriptName.startsWith(`${category}:`);
336
- }
337
-
338
- function parsePackageManagerValidationCommand(
339
- rawCommand: string,
340
- category: string,
341
- scripts: Record<string, string>,
342
- source: string,
343
- ): { command?: MeshRefineValidationCommand; rejected?: Record<string, unknown> } {
344
- const tokens = tokenizeValidationCommand(rawCommand);
345
- if (!tokens) {
346
- return { rejected: { command: rawCommand, category, source, reason: 'unsafe command string is not allowlisted' } };
347
- }
348
-
349
- const [binary, second, third, ...rest] = tokens;
350
- let scriptName = '';
351
- let command = binary;
352
- let args: string[] = [];
353
-
354
- if ((binary === 'npm' || binary === 'pnpm' || binary === 'bun') && second === 'run' && third) {
355
- scriptName = third;
356
- args = ['run', scriptName, ...rest];
357
- } else if (binary === 'npm' && second === 'test' && !third) {
358
- scriptName = 'test';
359
- args = ['test'];
360
- } else if (binary === 'yarn' && second === 'run' && third) {
361
- scriptName = third;
362
- args = ['run', scriptName, ...rest];
363
- } else if (binary === 'yarn' && second && !third) {
364
- scriptName = second;
365
- args = [scriptName];
366
- } else {
367
- return { rejected: { command: rawCommand, category, source, reason: 'command is not a supported package-manager script invocation' } };
368
- }
369
-
370
- if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
371
- return { rejected: { command: rawCommand, category, source, script: scriptName, reason: 'script is not declared in package.json' } };
372
- }
373
- if (!scriptMatchesValidationCategory(scriptName, category)) {
374
- return { rejected: { command: rawCommand, category, source, script: scriptName, reason: 'script name is outside the validation category allowlist' } };
375
- }
376
-
377
- return {
378
- command: {
379
- command,
380
- args,
381
- displayCommand: [command, ...args].join(' '),
382
- category,
383
- source,
384
- },
385
- };
386
- }
387
-
388
- function collectProjectContextValidationCandidates(mesh: any): Array<{ command: string; category: string; source: string; confidence?: string }> {
389
- const commands = mesh?.projectContext?.commands;
390
- if (!commands || typeof commands !== 'object' || Array.isArray(commands)) return [];
391
- const candidates: Array<{ command: string; category: string; source: string; confidence?: string }> = [];
392
- for (const category of REFINE_VALIDATION_CATEGORIES) {
393
- const entries = Array.isArray(commands[category]) ? commands[category] : [];
394
- for (const entry of entries) {
395
- if (typeof entry?.command !== 'string') continue;
396
- candidates.push({
397
- command: entry.command,
398
- category,
399
- source: typeof entry.sourcePath === 'string' ? entry.sourcePath : 'projectContext.commands',
400
- confidence: typeof entry.confidence === 'string' ? entry.confidence : undefined,
401
- });
402
- }
403
- }
404
- return candidates.sort((a, b) => {
405
- const rank = (value?: string) => value === 'high' ? 0 : value === 'medium' ? 1 : 2;
406
- return rank(a.confidence) - rank(b.confidence);
918
+ function recordMeshRefineStage(
919
+ stages: Array<Record<string, unknown>>,
920
+ stage: string,
921
+ status: MeshRefineStageStatus,
922
+ startedAt: number,
923
+ details?: Record<string, unknown>,
924
+ ): void {
925
+ stages.push({
926
+ stage,
927
+ status,
928
+ durationMs: Date.now() - startedAt,
929
+ ...(details || {}),
407
930
  });
408
931
  }
409
932
 
410
- function collectPolicyValidationCandidates(mesh: any): Array<{ command: string; category: string; source: string }> {
411
- const policy = mesh?.policy && typeof mesh.policy === 'object' && !Array.isArray(mesh.policy) ? mesh.policy : {};
412
- const configured = Array.isArray(policy.validationCommands)
413
- ? policy.validationCommands
414
- : Array.isArray(policy.validationGate?.commands)
415
- ? policy.validationGate.commands
416
- : [];
417
- return configured
418
- .map((entry: any) => typeof entry === 'string' ? { command: entry, category: '', source: 'mesh.policy.validationCommands' } : entry)
419
- .filter((entry: any) => entry && typeof entry.command === 'string')
420
- .map((entry: any) => {
421
- const commandText = entry.command.trim();
422
- const category = REFINE_VALIDATION_CATEGORIES.find(cat => commandText.includes(` ${cat}`)) ?? '';
423
- return { command: commandText, category, source: 'mesh.policy.validationCommands' };
424
- })
425
- .filter((entry: any) => !!entry.category);
933
+ async function computeGitPatchId(cwd: string, fromRef: string, toRef: string): Promise<string> {
934
+ const { execFileSync } = await import('node:child_process');
935
+ const diff = execFileSync('git', ['diff', '--patch', '--full-index', fromRef, toRef], {
936
+ cwd,
937
+ encoding: 'utf8',
938
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
939
+ });
940
+ if (!diff.trim()) return '';
941
+ const patchId = execFileSync('git', ['patch-id', '--stable'], {
942
+ cwd,
943
+ input: diff,
944
+ encoding: 'utf8',
945
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
946
+ }).trim();
947
+ return patchId.split(/\s+/)[0] || '';
426
948
  }
427
949
 
428
- function selectMeshRefineValidationCommands(mesh: any, workspace: string): { commands: MeshRefineValidationCommand[]; rejectedCommands: Array<Record<string, unknown>>; source: string } {
429
- const scripts = readPackageScripts(workspace);
430
- const rejectedCommands: Array<Record<string, unknown>> = [];
431
- const selected: MeshRefineValidationCommand[] = [];
432
- const seen = new Set<string>();
433
- const candidates = [
434
- ...collectPolicyValidationCandidates(mesh),
435
- ...collectProjectContextValidationCandidates(mesh),
436
- ];
437
-
438
- for (const candidate of candidates) {
439
- const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
440
- if (parsed.rejected) {
441
- rejectedCommands.push(parsed.rejected);
442
- continue;
443
- }
444
- if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
445
- selected.push(parsed.command);
446
- seen.add(parsed.command.displayCommand);
447
- if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
448
- }
449
-
450
- if (!selected.length && candidates.length === 0) {
451
- for (const category of REFINE_VALIDATION_CATEGORIES) {
452
- if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
453
- const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, 'package.json:scripts');
454
- if (fallback.command && !seen.has(fallback.command.displayCommand)) {
455
- selected.push(fallback.command);
456
- seen.add(fallback.command.displayCommand);
457
- } else if (fallback.rejected) {
458
- rejectedCommands.push(fallback.rejected);
459
- }
460
- if (selected.length >= 2) break;
950
+ async function runMeshRefinePatchEquivalenceGate(
951
+ repoRoot: string,
952
+ baseHead: string,
953
+ branchHead: string,
954
+ ): Promise<MeshRefinePatchEquivalenceSummary> {
955
+ const startedAt = Date.now();
956
+ try {
957
+ const { execFileSync } = await import('node:child_process');
958
+ const git = (args: string[]) => execFileSync('git', args, {
959
+ cwd: repoRoot,
960
+ encoding: 'utf8',
961
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
962
+ });
963
+ const mergeBase = git(['merge-base', baseHead, branchHead]).trim();
964
+ const mergeTreeStdout = git(['merge-tree', '--write-tree', baseHead, branchHead]);
965
+ const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || '';
966
+ if (!mergeBase || !mergedTree) {
967
+ return {
968
+ status: 'failed',
969
+ equivalent: false,
970
+ baseHead,
971
+ branchHead,
972
+ mergeBase: mergeBase || undefined,
973
+ mergedTree: mergedTree || undefined,
974
+ durationMs: Date.now() - startedAt,
975
+ error: 'patch equivalence preflight could not resolve merge-base or synthetic merge tree',
976
+ stdout: truncateValidationOutput(mergeTreeStdout),
977
+ };
461
978
  }
979
+ const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
980
+ const actualPatchId = await computeGitPatchId(repoRoot, baseHead, mergedTree);
981
+ const equivalent = expectedPatchId === actualPatchId;
982
+ return {
983
+ status: equivalent ? 'passed' : 'failed',
984
+ equivalent,
985
+ baseHead,
986
+ branchHead,
987
+ mergeBase,
988
+ mergedTree,
989
+ expectedPatchId,
990
+ actualPatchId,
991
+ durationMs: Date.now() - startedAt,
992
+ };
993
+ } catch (e: any) {
994
+ return {
995
+ status: 'failed',
996
+ equivalent: false,
997
+ baseHead,
998
+ branchHead,
999
+ durationMs: Date.now() - startedAt,
1000
+ error: e?.message || String(e),
1001
+ stdout: truncateValidationOutput(e?.stdout),
1002
+ stderr: truncateValidationOutput(e?.stderr),
1003
+ };
462
1004
  }
1005
+ }
463
1006
 
1007
+ function buildMeshRefineValidationPlan(mesh: any, workspace: string): Record<string, unknown> {
1008
+ const plan = resolveMeshRefineValidationPlan(mesh, workspace);
464
1009
  return {
465
- commands: selected,
466
- rejectedCommands,
467
- source: selected.some(command => command.source === 'mesh.policy.validationCommands')
468
- ? 'mesh_policy'
469
- : selected.some(command => command.source !== 'package.json:scripts')
470
- ? 'project_context'
471
- : selected.length
472
- ? 'package_json_scripts'
473
- : 'unavailable',
1010
+ source: plan.source,
1011
+ sourceType: plan.sourceType,
1012
+ commands: plan.commands.map(command => ({
1013
+ displayCommand: command.displayCommand,
1014
+ category: command.category,
1015
+ source: command.source,
1016
+ cwd: command.cwd,
1017
+ timeoutMs: command.timeoutMs,
1018
+ })),
1019
+ unavailableReason: plan.unavailableReason,
1020
+ rejectedCommands: plan.rejectedCommands,
1021
+ suggestions: plan.suggestions,
1022
+ suggestedConfig: plan.suggestedConfig,
1023
+ note: plan.sourceType === 'unavailable'
1024
+ ? 'No validation command will be executed until a repo mesh/refine config is provided. Heuristics are suggestions only.'
1025
+ : 'Validation commands are resolved from repo mesh/refine config; heuristics are suggestions only.',
474
1026
  };
475
1027
  }
476
1028
 
@@ -478,7 +1030,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
478
1030
  const { execFile } = await import('node:child_process');
479
1031
  const { promisify } = await import('node:util');
480
1032
  const execFileAsync = promisify(execFile);
481
- const selection = selectMeshRefineValidationCommands(mesh, workspace);
1033
+ const selection = resolveMeshRefineValidationPlan(mesh, workspace);
482
1034
  const summary: MeshRefineValidationSummary = {
483
1035
  status: 'skipped',
484
1036
  required: true,
@@ -487,22 +1039,28 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
487
1039
  skippedReason: undefined,
488
1040
  timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
489
1041
  outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
1042
+ configSource: selection.source,
1043
+ configSourceType: selection.sourceType,
1044
+ suggestions: selection.suggestions,
1045
+ suggestedConfig: selection.suggestedConfig,
490
1046
  };
491
1047
 
492
1048
  if (!selection.commands.length) {
493
- summary.skippedReason = 'validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available';
1049
+ summary.skippedReason = selection.unavailableReason || 'validation_unavailable: repo mesh/refine config did not provide executable validation.commands';
494
1050
  return summary;
495
1051
  }
496
1052
 
497
1053
  for (const candidate of selection.commands) {
498
1054
  const startedAt = Date.now();
1055
+ const cwd = candidate.cwd ? pathResolve(workspace, candidate.cwd) : workspace;
1056
+ const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
499
1057
  try {
500
1058
  const result = await execFileAsync(candidate.command, candidate.args, {
501
- cwd: workspace,
1059
+ cwd,
502
1060
  encoding: 'utf8',
503
- timeout: REFINE_VALIDATION_TIMEOUT_MS,
1061
+ timeout,
504
1062
  maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
505
- env: { ...process.env, CI: process.env.CI || '1' },
1063
+ env: { ...process.env, CI: process.env.CI || '1', ...(candidate.env || {}) },
506
1064
  });
507
1065
  summary.commandsRun.push({
508
1066
  command: candidate.command,
@@ -510,6 +1068,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
510
1068
  displayCommand: candidate.displayCommand,
511
1069
  category: candidate.category,
512
1070
  source: candidate.source,
1071
+ cwd,
513
1072
  passed: true,
514
1073
  exitCode: 0,
515
1074
  durationMs: Date.now() - startedAt,
@@ -523,6 +1082,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
523
1082
  displayCommand: candidate.displayCommand,
524
1083
  category: candidate.category,
525
1084
  source: candidate.source,
1085
+ cwd,
526
1086
  passed: false,
527
1087
  exitCode: typeof error?.code === 'number' ? error.code : null,
528
1088
  signal: typeof error?.signal === 'string' ? error.signal : null,
@@ -661,6 +1221,10 @@ export interface CommandRouterDeps {
661
1221
  statusVersion?: string;
662
1222
  /** Session host control plane */
663
1223
  sessionHostControl?: SessionHostControlPlane | null;
1224
+ /** Selected-coordinator mesh peer telemetry surface for target daemons, when supported by the runtime. */
1225
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
1226
+ /** Dispatch a command to a remote mesh node via P2P/relay. Injected by cloud runtime; absent in standalone. */
1227
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
664
1228
  }
665
1229
 
666
1230
  export interface CommandRouterResult {
@@ -772,42 +1336,255 @@ function summarizeSessionHostPruneResult(result: unknown): Record<string, unknow
772
1336
  };
773
1337
  }
774
1338
 
1339
+ function normalizeStandaloneHostCommandUrl(hostAddress: string): string {
1340
+ const raw = hostAddress.trim();
1341
+ if (!raw) throw new Error('hostAddress required');
1342
+ const url = new URL(raw.replace(/^ws:/, 'http:').replace(/^wss:/, 'https:'));
1343
+ url.pathname = '/api/v1/command';
1344
+ url.search = '';
1345
+ url.hash = '';
1346
+ return url.toString();
1347
+ }
1348
+
1349
+ function buildMemberJoinNode(mesh: any, args: any, fallbackDaemonId?: string): Record<string, unknown> | null {
1350
+ const requestedNodeId = typeof args?.memberNodeId === 'string' ? args.memberNodeId.trim() : '';
1351
+ const explicit = args?.memberNode && typeof args.memberNode === 'object' && !Array.isArray(args.memberNode)
1352
+ ? args.memberNode as Record<string, any>
1353
+ : null;
1354
+ const configured = Array.isArray(mesh?.nodes)
1355
+ ? (requestedNodeId
1356
+ ? mesh.nodes.find((node: any) => node?.id === requestedNodeId || node?.nodeId === requestedNodeId)
1357
+ : mesh.nodes[0])
1358
+ : null;
1359
+ const source = explicit || configured;
1360
+ const workspace = typeof source?.workspace === 'string' && source.workspace.trim()
1361
+ ? source.workspace.trim()
1362
+ : typeof args?.workspace === 'string' && args.workspace.trim()
1363
+ ? args.workspace.trim()
1364
+ : process.cwd();
1365
+ if (!workspace) return null;
1366
+ const nodeId = typeof source?.id === 'string' && source.id.trim()
1367
+ ? source.id.trim()
1368
+ : typeof source?.nodeId === 'string' && source.nodeId.trim()
1369
+ ? source.nodeId.trim()
1370
+ : undefined;
1371
+ return {
1372
+ ...(nodeId ? { id: nodeId } : {}),
1373
+ workspace,
1374
+ ...(typeof source?.repoRoot === 'string' && source.repoRoot.trim() ? { repoRoot: source.repoRoot.trim() } : {}),
1375
+ ...(typeof source?.daemonId === 'string' && source.daemonId.trim() ? { daemonId: source.daemonId.trim() } : fallbackDaemonId ? { daemonId: fallbackDaemonId } : {}),
1376
+ ...(typeof source?.machineId === 'string' && source.machineId.trim() ? { machineId: source.machineId.trim() } : {}),
1377
+ userOverrides: source?.userOverrides && typeof source.userOverrides === 'object' && !Array.isArray(source.userOverrides) ? source.userOverrides : {},
1378
+ policy: source?.policy && typeof source.policy === 'object' && !Array.isArray(source.policy) ? source.policy : {},
1379
+ role: 'member',
1380
+ };
1381
+ }
1382
+
775
1383
  export class DaemonCommandRouter {
776
1384
  private deps: CommandRouterDeps;
777
1385
  /** In-memory cache for cloud-originating meshes passed via inlineMesh.
778
1386
  * Allows the MCP server to query mesh data via get_mesh even when
779
1387
  * the mesh doesn't exist in the local meshes.json file. */
780
1388
  private inlineMeshCache = new Map<string, any>();
1389
+ /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
1390
+ private aggregateMeshStatusCache = new Map<string, { builtAt: number; snapshot: any }>();
781
1391
 
782
1392
  constructor(deps: CommandRouterDeps) {
783
1393
  this.deps = deps;
784
1394
  }
785
1395
 
1396
+ private cloneJsonValue<T>(value: T): T {
1397
+ if (typeof structuredClone === 'function') return structuredClone(value);
1398
+ return JSON.parse(JSON.stringify(value)) as T;
1399
+ }
1400
+
1401
+ private hydrateCachedAggregateMeshStatusFromInline(snapshot: any, mesh: any, options?: { requireDirectPeerTruth?: boolean }): any {
1402
+ if (!mesh || typeof mesh !== 'object' || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
1403
+ const inlineNodesById = new Map<string, any>();
1404
+ for (const node of mesh.nodes) {
1405
+ const nodeId = readInlineMeshNodeId(node);
1406
+ if (nodeId) inlineNodesById.set(nodeId, node);
1407
+ }
1408
+ if (!inlineNodesById.size) return snapshot;
1409
+
1410
+ let changed = false;
1411
+ const unavailableNodeIds = new Set<string>();
1412
+ const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
1413
+ const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
1414
+ for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
1415
+ const nodeId = readStringValue(entry);
1416
+ if (nodeId) unavailableNodeIds.add(nodeId);
1417
+ }
1418
+
1419
+ const nodes = snapshot.nodes.map((statusNode: any) => {
1420
+ const nodeId = readStringValue(statusNode?.nodeId, statusNode?.id);
1421
+ const inlineNode = nodeId ? inlineNodesById.get(nodeId) : undefined;
1422
+ if (!inlineNode) return statusNode;
1423
+ const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
1424
+ if (!liveGit) return statusNode;
1425
+ const nextStatus = { ...statusNode };
1426
+ nextStatus.git = liveGit;
1427
+ nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
1428
+ nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
1429
+ const connection = readObjectRecord(nextStatus.connection);
1430
+ const connectionState = readStringValue(connection.state);
1431
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
1432
+ if (!connectionReported || connectionState === 'unknown') {
1433
+ nextStatus.connection = buildLivePeerGitConnection(connection);
1434
+ }
1435
+ delete nextStatus.gitProbePending;
1436
+ const error = readStringValue(nextStatus.error);
1437
+ if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
1438
+ if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = 'online';
1439
+ if (nodeId) unavailableNodeIds.delete(nodeId);
1440
+ changed = true;
1441
+ return nextStatus;
1442
+ });
1443
+
1444
+ if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0)) return snapshot;
1445
+ const nextSourceOfTruth = {
1446
+ ...sourceOfTruth,
1447
+ ...(Object.keys(directPeerTruth).length ? {
1448
+ directPeerTruth: {
1449
+ ...directPeerTruth,
1450
+ satisfied: options?.requireDirectPeerTruth === true ? unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
1451
+ unavailableNodeIds: [...unavailableNodeIds],
1452
+ },
1453
+ ...(options?.requireDirectPeerTruth === true ? {
1454
+ coordinatorOwnsLiveTruth: unavailableNodeIds.size === 0,
1455
+ currentStatus: unavailableNodeIds.size === 0 ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
1456
+ } : {}),
1457
+ } : {}),
1458
+ };
1459
+ return {
1460
+ ...snapshot,
1461
+ ...(options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 ? {
1462
+ success: false,
1463
+ code: 'mesh_direct_peer_truth_unavailable',
1464
+ error: 'Selected coordinator could not confirm direct mesh truth for every remote node yet.',
1465
+ } : {}),
1466
+ sourceOfTruth: nextSourceOfTruth,
1467
+ nodes,
1468
+ };
1469
+ }
1470
+
1471
+ private getCachedAggregateMeshStatus(meshId: string, mesh?: any, options?: { requireDirectPeerTruth?: boolean }): any | null {
1472
+ const cached = this.aggregateMeshStatusCache.get(meshId);
1473
+ if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
1474
+ let snapshot = this.cloneJsonValue(cached.snapshot);
1475
+ snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
1476
+ if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
1477
+ const ageMs = Math.max(0, Date.now() - cached.builtAt);
1478
+ const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === 'object'
1479
+ ? snapshot.sourceOfTruth
1480
+ : {};
1481
+ snapshot.sourceOfTruth = {
1482
+ ...sourceOfTruth,
1483
+ aggregateSnapshot: {
1484
+ ...(sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === 'object'
1485
+ ? sourceOfTruth.aggregateSnapshot
1486
+ : {}),
1487
+ owner: 'coordinator_daemon_memory',
1488
+ cached: true,
1489
+ source: 'memory',
1490
+ refreshReason: 'memory_cache_hit',
1491
+ ageMs,
1492
+ cachedAt: new Date(cached.builtAt).toISOString(),
1493
+ returnedAt: new Date().toISOString(),
1494
+ },
1495
+ };
1496
+ return snapshot;
1497
+ }
1498
+
1499
+ private rememberAggregateMeshStatus(meshId: string, snapshot: any, refreshReason: string): any {
1500
+ if (!snapshot || typeof snapshot !== 'object' || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
1501
+ const builtAt = Date.now();
1502
+ const next = this.cloneJsonValue(snapshot);
1503
+ const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === 'object'
1504
+ ? next.sourceOfTruth
1505
+ : {};
1506
+ next.sourceOfTruth = {
1507
+ ...sourceOfTruth,
1508
+ aggregateSnapshot: {
1509
+ owner: 'coordinator_daemon_memory',
1510
+ cached: false,
1511
+ source: 'live_refresh',
1512
+ refreshReason,
1513
+ ageMs: 0,
1514
+ cachedAt: new Date(builtAt).toISOString(),
1515
+ returnedAt: new Date(builtAt).toISOString(),
1516
+ },
1517
+ };
1518
+ this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next) });
1519
+ return next;
1520
+ }
1521
+
786
1522
  public getCachedInlineMesh(meshId: string, inlineMesh?: unknown): any | undefined {
787
1523
  if (inlineMesh && typeof inlineMesh === 'object') {
788
- this.inlineMeshCache.set(meshId, inlineMesh as any);
789
- return inlineMesh as any;
1524
+ return this.warmInlineMeshCache(meshId, inlineMesh);
790
1525
  }
791
1526
  return this.inlineMeshCache.get(meshId);
792
1527
  }
793
1528
 
1529
+ private warmInlineMeshCache(meshId: string, inlineMesh?: unknown): any | undefined {
1530
+ if (!inlineMesh || typeof inlineMesh !== 'object') return undefined;
1531
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh as any);
1532
+ const cached = this.inlineMeshCache.get(meshId);
1533
+ if (cached) {
1534
+ const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
1535
+ this.inlineMeshCache.set(meshId, merged);
1536
+ return merged;
1537
+ }
1538
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh as any);
1539
+ return sanitizedInlineMesh as any;
1540
+ }
1541
+
794
1542
  private async getMeshForCommand(
795
1543
  meshId: string,
796
1544
  inlineMesh?: unknown,
797
1545
  options?: { preferInline?: boolean },
798
- ): Promise<{ mesh: any; inline: boolean } | null> {
1546
+ ): Promise<{ mesh: any; inline: boolean; source: 'inline_cache' | 'inline_bootstrap' | 'local_config' } | null> {
799
1547
  const preferInline = options?.preferInline === true;
800
1548
  if (preferInline) {
801
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
802
- if (cached) return { mesh: cached, inline: true };
1549
+ const cached = this.getCachedInlineMesh(meshId);
1550
+ if (cached) {
1551
+ if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
1552
+ const merged = reconcileInlineMeshCache(cached, inlineMesh as any);
1553
+ this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
1554
+ return { mesh: merged, inline: true, source: 'inline_cache' };
1555
+ }
1556
+ return { mesh: cached, inline: true, source: 'inline_cache' };
1557
+ }
1558
+ if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
1559
+ this.warmInlineMeshCache(meshId, inlineMesh);
1560
+ return { mesh: inlineMesh, inline: true, source: 'inline_bootstrap' };
1561
+ }
803
1562
  }
804
1563
  try {
805
1564
  const { getMesh } = await import('../config/mesh-config.js');
806
1565
  const mesh = getMesh(meshId);
807
- if (mesh) return { mesh, inline: false };
1566
+ if (mesh) return { mesh, inline: false, source: 'local_config' };
808
1567
  } catch { /* fall through to inline cache */ }
809
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
810
- return cached ? { mesh: cached, inline: true } : null;
1568
+ const cached = this.getCachedInlineMesh(meshId);
1569
+ if (cached) return { mesh: cached, inline: true, source: 'inline_cache' };
1570
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
1571
+ return warmedInline ? { mesh: warmedInline, inline: true, source: 'inline_bootstrap' } : null;
1572
+ }
1573
+
1574
+ private invalidateAggregateMeshStatus(meshId: string): void {
1575
+ this.aggregateMeshStatusCache.delete(meshId);
1576
+ }
1577
+
1578
+
1579
+ private async requireMeshHostMutationOwner(meshId: string, inlineMesh: unknown, operation: string): Promise<CommandRouterResult | null> {
1580
+ const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
1581
+ const mesh = meshRecord?.mesh;
1582
+ if (!mesh) return { success: false, error: 'Mesh not found' };
1583
+ const meshHost = resolveMeshHostStatus(mesh);
1584
+ if (!meshHost.canOwnCoordinator || !meshHost.canOwnQueue) {
1585
+ return { ...buildMeshHostRequiredFailure(mesh, operation), success: false, meshId };
1586
+ }
1587
+ return null;
811
1588
  }
812
1589
 
813
1590
  private updateInlineMeshNode(meshId: string, mesh: any, node: any): void {
@@ -817,6 +1594,7 @@ export class DaemonCommandRouter {
817
1594
  else mesh.nodes.push(node);
818
1595
  mesh.updatedAt = new Date().toISOString();
819
1596
  this.inlineMeshCache.set(meshId, mesh);
1597
+ this.invalidateAggregateMeshStatus(meshId);
820
1598
  }
821
1599
 
822
1600
  private removeInlineMeshNode(meshId: string, mesh: any, nodeId: string): boolean {
@@ -826,6 +1604,7 @@ export class DaemonCommandRouter {
826
1604
  mesh.nodes.splice(idx, 1);
827
1605
  mesh.updatedAt = new Date().toISOString();
828
1606
  this.inlineMeshCache.set(meshId, mesh);
1607
+ this.invalidateAggregateMeshStatus(meshId);
829
1608
  return true;
830
1609
  }
831
1610
 
@@ -1104,6 +1883,7 @@ export class DaemonCommandRouter {
1104
1883
  const deletedSessionIds: string[] = [];
1105
1884
  const skippedSessionIds: string[] = [];
1106
1885
  const skippedLiveSessionIds: string[] = [];
1886
+ const skippedCoordinatorSessionIds: string[] = [];
1107
1887
  const deleteUnsupportedSessionIds: string[] = [];
1108
1888
  const recordsRemainSessionIds: string[] = [];
1109
1889
  const errors: Array<{ sessionId: string; error: string }> = [];
@@ -1138,6 +1918,12 @@ export class DaemonCommandRouter {
1138
1918
  const completed = this.isCompletedHostedSession(record);
1139
1919
  const surfaceKind = getSessionHostSurfaceKind(record);
1140
1920
  const liveRuntime = surfaceKind === 'live_runtime';
1921
+ const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
1922
+ if (!hasExplicitSessionIds && coordinatorSession) {
1923
+ skippedSessionIds.push(sessionId);
1924
+ skippedCoordinatorSessionIds.push(sessionId);
1925
+ continue;
1926
+ }
1141
1927
  if (!hasExplicitSessionIds && liveRuntime) {
1142
1928
  skippedSessionIds.push(sessionId);
1143
1929
  skippedLiveSessionIds.push(sessionId);
@@ -1207,6 +1993,7 @@ export class DaemonCommandRouter {
1207
1993
  deletedSessionIds,
1208
1994
  skippedSessionIds,
1209
1995
  skippedLiveSessionIds,
1996
+ skippedCoordinatorSessionIds,
1210
1997
  ...(deleteUnsupported ? {
1211
1998
  deleteUnsupported: true,
1212
1999
  effectiveCleanup: args.mode === 'stop_and_delete'
@@ -1361,7 +2148,8 @@ export class DaemonCommandRouter {
1361
2148
  }
1362
2149
 
1363
2150
  case 'get_pending_mesh_events': {
1364
- const events = drainPendingMeshCoordinatorEvents();
2151
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2152
+ const events = drainPendingMeshCoordinatorEvents(meshId || undefined);
1365
2153
  return { success: true, events };
1366
2154
  }
1367
2155
 
@@ -1966,15 +2754,44 @@ export class DaemonCommandRouter {
1966
2754
  case 'get_mesh': {
1967
2755
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
1968
2756
  if (!meshId) return { success: false, error: 'meshId required' };
1969
- try {
1970
- const { getMesh } = await import('../config/mesh-config.js');
1971
- const mesh = getMesh(meshId);
1972
- if (mesh) return { success: true, mesh };
1973
- } catch { /* fall through to inline cache */ }
1974
- // Fallback: check in-memory cache for cloud-originating meshes
1975
- const cached = this.inlineMeshCache.get(meshId);
1976
- if (cached) return { success: true, mesh: cached };
1977
- return { success: false, error: 'Mesh not found' };
2757
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2758
+ if (!meshRecord?.mesh) return { success: false, error: 'Mesh not found' };
2759
+
2760
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
2761
+ const directTruth = await hydrateInlineMeshDirectTruth({
2762
+ mesh: meshRecord.mesh,
2763
+ meshSource: meshRecord.source,
2764
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
2765
+ statusInstanceId: this.deps.statusInstanceId,
2766
+ localMachineId: loadConfig().machineId || '',
2767
+ });
2768
+ const directTruthSatisfied = meshRecord.source !== 'inline_bootstrap' || directTruth.directEvidenceCount > 0;
2769
+ const sourceOfTruth = {
2770
+ membership: meshRecord.source === 'inline_cache'
2771
+ ? 'coordinator_inline_mesh_cache'
2772
+ : meshRecord.source === 'local_config'
2773
+ ? 'local_mesh_config'
2774
+ : 'inline_bootstrap_snapshot',
2775
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
2776
+ directPeerTruth: {
2777
+ required: requireDirectPeerTruth,
2778
+ satisfied: directTruthSatisfied,
2779
+ directEvidenceCount: directTruth.directEvidenceCount,
2780
+ localConfirmedCount: directTruth.localConfirmedCount,
2781
+ peerAttemptedCount: directTruth.peerAttemptedCount,
2782
+ peerConfirmedCount: directTruth.peerConfirmedCount,
2783
+ unavailableNodeIds: directTruth.unavailableNodeIds,
2784
+ },
2785
+ };
2786
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
2787
+ return {
2788
+ success: false,
2789
+ code: 'mesh_direct_peer_truth_unavailable',
2790
+ error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.',
2791
+ sourceOfTruth,
2792
+ };
2793
+ }
2794
+ return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
1978
2795
  }
1979
2796
 
1980
2797
  case 'create_mesh': {
@@ -1985,7 +2802,10 @@ export class DaemonCommandRouter {
1985
2802
  if (!name) return { success: false, error: 'name required' };
1986
2803
  try {
1987
2804
  const { createMesh } = await import('../config/mesh-config.js');
1988
- const mesh = createMesh({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy });
2805
+ const meshHost = args?.meshHost && typeof args.meshHost === 'object' && !Array.isArray(args.meshHost)
2806
+ ? args.meshHost
2807
+ : undefined;
2808
+ const mesh = createMesh({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
1989
2809
  return { success: true, mesh };
1990
2810
  } catch (e: any) {
1991
2811
  return { success: false, error: e.message };
@@ -2002,16 +2822,237 @@ export class DaemonCommandRouter {
2002
2822
  if (typeof args?.defaultBranch === 'string') patch.defaultBranch = args.defaultBranch;
2003
2823
  if (args?.policy && typeof args.policy === 'object' && !Array.isArray(args.policy)) patch.policy = args.policy;
2004
2824
  if (args?.coordinator && typeof args.coordinator === 'object' && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
2825
+ if (args?.meshHost && typeof args.meshHost === 'object' && !Array.isArray(args.meshHost)) patch.meshHost = args.meshHost;
2005
2826
  if (!Object.keys(patch).length) return { success: false, error: 'No updates provided' };
2006
2827
  const mesh = updateMesh(meshId, patch as any);
2007
2828
  if (!mesh) return { success: false, error: 'Mesh not found' };
2008
2829
  this.inlineMeshCache.set(meshId, mesh);
2830
+ this.invalidateAggregateMeshStatus(meshId);
2009
2831
  return { success: true, mesh };
2010
2832
  } catch (e: any) {
2011
2833
  return { success: false, error: e.message };
2012
2834
  }
2013
2835
  }
2014
2836
 
2837
+ case 'get_mesh_host_pairing': {
2838
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2839
+ if (!meshId) return { success: false, error: 'meshId required' };
2840
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2841
+ const mesh = meshRecord?.mesh;
2842
+ if (!mesh) return { success: false, error: 'Mesh not found' };
2843
+ const meshHost = resolveMeshHostStatus(mesh);
2844
+ const pairingStatus = meshHost.pairing?.status || 'not_configured';
2845
+ return {
2846
+ success: true,
2847
+ code: pairingStatus === 'not_configured' ? 'mesh_host_pairing_not_configured' : 'mesh_host_pairing_pending',
2848
+ meshId,
2849
+ hostAddress: meshHost.hostAddress,
2850
+ meshHost,
2851
+ manualPairing: {
2852
+ status: pairingStatus,
2853
+ joinImplemented: true,
2854
+ protocol: 'standalone_command_direct_v1',
2855
+ description: 'Standalone manual pairing can save address/token metadata, apply a host join over direct standalone command HTTP or injected mesh command dispatch, and check persisted status. P2P signaling remains outside this slice.',
2856
+ },
2857
+ };
2858
+ }
2859
+
2860
+ case 'configure_mesh_host_pairing': {
2861
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2862
+ const hostAddress = typeof args?.hostAddress === 'string' ? args.hostAddress.trim() : '';
2863
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
2864
+ if (!meshId) return { success: false, error: 'meshId required' };
2865
+ if (!hostAddress || !token) return { success: false, error: 'hostAddress and token required' };
2866
+ try {
2867
+ const { configureMeshHostPairing } = await import('../config/mesh-config.js');
2868
+ const configured = configureMeshHostPairing(meshId, { hostAddress, token });
2869
+ if (!configured) return { success: false, error: 'Mesh not found' };
2870
+ this.inlineMeshCache.set(meshId, configured.mesh);
2871
+ const meshHost = resolveMeshHostStatus(configured.mesh);
2872
+ return {
2873
+ success: true,
2874
+ code: 'mesh_host_pairing_pending',
2875
+ meshId,
2876
+ hostAddress: configured.hostAddress,
2877
+ meshHost,
2878
+ manualPairing: {
2879
+ status: meshHost.pairing?.status || 'pairing',
2880
+ joinImplemented: true,
2881
+ protocol: 'standalone_command_direct_v1',
2882
+ description: 'Manual Mesh Host pairing config was saved locally. Use join_mesh_host_pairing to apply it to the host. Raw token was not persisted.',
2883
+ },
2884
+ };
2885
+ } catch (e: any) {
2886
+ return { success: false, code: 'mesh_host_pairing_invalid', meshId, hostAddress, error: e.message };
2887
+ }
2888
+ }
2889
+
2890
+ case 'create_mesh_host_pairing_token': {
2891
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2892
+ if (!meshId) return { success: false, error: 'meshId required' };
2893
+ try {
2894
+ const { createMeshHostPairingToken } = await import('../config/mesh-config.js');
2895
+ const created = createMeshHostPairingToken(meshId, {
2896
+ token: typeof args?.token === 'string' ? args.token : undefined,
2897
+ expiresAt: typeof args?.expiresAt === 'string' ? args.expiresAt : undefined,
2898
+ });
2899
+ if (!created) return { success: false, error: 'Mesh not found' };
2900
+ this.inlineMeshCache.set(meshId, created.mesh);
2901
+ this.invalidateAggregateMeshStatus(meshId);
2902
+ return {
2903
+ success: true,
2904
+ code: 'mesh_host_pairing_token_created',
2905
+ meshId,
2906
+ token: created.token,
2907
+ tokenId: created.tokenId,
2908
+ expiresAt: created.expiresAt,
2909
+ meshHost: resolveMeshHostStatus(created.mesh),
2910
+ warning: 'Raw token is returned once and is not persisted; share it with member daemons over a trusted channel.',
2911
+ };
2912
+ } catch (e: any) {
2913
+ return { success: false, code: 'mesh_host_pairing_token_invalid', meshId, error: e.message };
2914
+ }
2915
+ }
2916
+
2917
+ case 'apply_mesh_host_join': {
2918
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2919
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
2920
+ const memberNode = args?.memberNode && typeof args.memberNode === 'object' && !Array.isArray(args.memberNode)
2921
+ ? args.memberNode
2922
+ : null;
2923
+ if (!meshId) return { success: false, error: 'meshId required' };
2924
+ if (!token || !memberNode) return { success: false, error: 'token and memberNode required' };
2925
+ try {
2926
+ const { applyMeshHostJoinRequest } = await import('../config/mesh-config.js');
2927
+ const applied = applyMeshHostJoinRequest(meshId, {
2928
+ token,
2929
+ memberNode: memberNode as any,
2930
+ memberMeshId: typeof args?.memberMeshId === 'string' ? args.memberMeshId : undefined,
2931
+ });
2932
+ if (!applied) return { success: false, error: 'Mesh not found' };
2933
+ if (!applied.accepted) {
2934
+ return {
2935
+ success: false,
2936
+ code: 'mesh_host_join_rejected',
2937
+ meshId,
2938
+ tokenId: applied.tokenId,
2939
+ meshHost: applied.meshHost ? resolveMeshHostStatus({ meshHost: applied.meshHost }) : undefined,
2940
+ error: applied.reason,
2941
+ };
2942
+ }
2943
+ this.inlineMeshCache.set(meshId, applied.mesh);
2944
+ this.invalidateAggregateMeshStatus(meshId);
2945
+ try {
2946
+ const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
2947
+ appendLedgerEntry(meshId, {
2948
+ kind: 'node_joined',
2949
+ nodeId: applied.node.id,
2950
+ payload: { role: 'member', tokenId: applied.tokenId, workspace: applied.node.workspace },
2951
+ });
2952
+ } catch { /* ledger append is best-effort */ }
2953
+ return {
2954
+ success: true,
2955
+ code: 'mesh_host_join_accepted',
2956
+ meshId,
2957
+ node: applied.node,
2958
+ tokenId: applied.tokenId,
2959
+ meshHost: resolveMeshHostStatus(applied.mesh),
2960
+ };
2961
+ } catch (e: any) {
2962
+ return { success: false, code: 'mesh_host_join_failed', meshId, error: e.message };
2963
+ }
2964
+ }
2965
+
2966
+ case 'join_mesh_host_pairing': {
2967
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2968
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
2969
+ if (!meshId) return { success: false, error: 'meshId required' };
2970
+ if (!token) return { success: false, error: 'token required because raw pairing tokens are not persisted' };
2971
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2972
+ const mesh = meshRecord?.mesh;
2973
+ if (!mesh) return { success: false, error: 'Mesh not found' };
2974
+ const meshHost = resolveMeshHostStatus(mesh);
2975
+ if (meshHost.role !== 'member') {
2976
+ return { success: false, code: 'mesh_host_join_not_member', meshId, meshHost, error: 'join_mesh_host_pairing must run from a member daemon configured with a Mesh Host address/token.' };
2977
+ }
2978
+ try {
2979
+ const { tokenIdForManualPairing, markMeshHostPairingJoined } = await import('../config/mesh-config.js');
2980
+ const tokenId = tokenIdForManualPairing(token);
2981
+ if (meshHost.pairing?.tokenId && meshHost.pairing.tokenId !== tokenId) {
2982
+ return { success: false, code: 'mesh_host_join_rejected', meshId, tokenId, meshHost, error: 'invalid pairing token' };
2983
+ }
2984
+ const memberNode = buildMemberJoinNode(mesh, args, this.deps.statusInstanceId);
2985
+ if (!memberNode) return { success: false, error: 'member node metadata unavailable' };
2986
+ const hostMeshId = typeof args?.hostMeshId === 'string' && args.hostMeshId.trim() ? args.hostMeshId.trim() : meshId;
2987
+ const hostDaemonId = typeof args?.hostDaemonId === 'string' && args.hostDaemonId.trim()
2988
+ ? args.hostDaemonId.trim()
2989
+ : meshHost.hostDaemonId;
2990
+ let hostResult: any;
2991
+ let transport: string;
2992
+ if (hostDaemonId && this.deps.dispatchMeshCommand) {
2993
+ transport = 'mesh_command_dispatch';
2994
+ hostResult = await this.deps.dispatchMeshCommand(hostDaemonId, 'apply_mesh_host_join', {
2995
+ meshId: hostMeshId,
2996
+ token,
2997
+ memberMeshId: meshId,
2998
+ memberNode,
2999
+ });
3000
+ } else if (meshHost.hostAddress) {
3001
+ transport = 'standalone_http_command';
3002
+ const commandUrl = normalizeStandaloneHostCommandUrl(meshHost.hostAddress);
3003
+ const response = await fetch(commandUrl, {
3004
+ method: 'POST',
3005
+ headers: { 'Content-Type': 'application/json' },
3006
+ body: JSON.stringify({ type: 'apply_mesh_host_join', payload: { meshId: hostMeshId, token, memberMeshId: meshId, memberNode } }),
3007
+ });
3008
+ hostResult = await response.json().catch(() => ({ success: false, error: `Host returned HTTP ${response.status}` }));
3009
+ if (!response.ok && hostResult?.success !== false) hostResult = { success: false, error: `Host returned HTTP ${response.status}` };
3010
+ } else {
3011
+ return {
3012
+ success: false,
3013
+ code: 'mesh_host_join_transport_unavailable',
3014
+ meshId,
3015
+ meshHost,
3016
+ error: 'No hostDaemonId dispatch path or hostAddress HTTP command path is available. P2P signaling join is not implemented in this slice.',
3017
+ };
3018
+ }
3019
+ if (!hostResult?.success) {
3020
+ return { success: false, code: hostResult?.code || 'mesh_host_join_rejected', meshId, meshHost, transport, error: hostResult?.error || 'Mesh Host rejected join request', hostResult };
3021
+ }
3022
+ const joined = meshRecord.inline
3023
+ ? null
3024
+ : markMeshHostPairingJoined(meshId, {
3025
+ tokenId: hostResult.tokenId || tokenId,
3026
+ hostDaemonId: hostResult.meshHost?.hostDaemonId || hostDaemonId,
3027
+ hostNodeId: hostResult.meshHost?.hostNodeId,
3028
+ joinedAt: hostResult.meshHost?.pairing?.joinedAt,
3029
+ });
3030
+ if (joined) {
3031
+ this.inlineMeshCache.set(meshId, joined.mesh);
3032
+ this.invalidateAggregateMeshStatus(meshId);
3033
+ }
3034
+ return {
3035
+ success: true,
3036
+ code: 'mesh_host_join_applied',
3037
+ meshId,
3038
+ hostMeshId,
3039
+ transport,
3040
+ node: hostResult.node,
3041
+ tokenId: hostResult.tokenId || tokenId,
3042
+ meshHost: joined ? resolveMeshHostStatus(joined.mesh) : { ...meshHost, pairing: { ...(meshHost.pairing || {}), status: 'paired', tokenId: hostResult.tokenId || tokenId } },
3043
+ hostResult,
3044
+ manualPairing: {
3045
+ status: 'paired',
3046
+ joinImplemented: true,
3047
+ protocol: 'standalone_command_direct_v1',
3048
+ description: 'Mesh Host accepted the join and local member pairing status was marked paired. P2P runtime signaling remains outside this slice.',
3049
+ },
3050
+ };
3051
+ } catch (e: any) {
3052
+ return { success: false, code: 'mesh_host_join_failed', meshId, meshHost, error: e.message };
3053
+ }
3054
+ }
3055
+
2015
3056
  case 'delete_mesh': {
2016
3057
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2017
3058
  if (!meshId) return { success: false, error: 'meshId required' };
@@ -2105,6 +3146,8 @@ export class DaemonCommandRouter {
2105
3146
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2106
3147
  const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
2107
3148
  if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
3149
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue cancellation');
3150
+ if (ownerFailure) return ownerFailure;
2108
3151
  try {
2109
3152
  const { cancelTask } = await import('../mesh/mesh-work-queue.js');
2110
3153
  const reason = typeof args?.reason === 'string' ? args.reason : undefined;
@@ -2120,6 +3163,8 @@ export class DaemonCommandRouter {
2120
3163
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2121
3164
  const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
2122
3165
  if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
3166
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue requeue');
3167
+ if (ownerFailure) return ownerFailure;
2123
3168
  try {
2124
3169
  const { requeueTask } = await import('../mesh/mesh-work-queue.js');
2125
3170
  const task = requeueTask(meshId, taskId, {
@@ -2141,6 +3186,8 @@ export class DaemonCommandRouter {
2141
3186
  const workspace = typeof args?.workspace === 'string' ? args.workspace.trim() : '';
2142
3187
  if (!meshId) return { success: false, error: 'meshId required' };
2143
3188
  if (!workspace) return { success: false, error: 'workspace required' };
3189
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node addition');
3190
+ if (ownerFailure) return ownerFailure;
2144
3191
  try {
2145
3192
  const { addNode } = await import('../config/mesh-config.js');
2146
3193
  const providerPriority = Array.isArray(args?.providerPriority)
@@ -2151,7 +3198,8 @@ export class DaemonCommandRouter {
2151
3198
  ...(readOnly ? { readOnly: true } : {}),
2152
3199
  ...(providerPriority.length ? { providerPriority } : {}),
2153
3200
  };
2154
- const node = addNode(meshId, { workspace, ...(policy ? { policy } : {}) });
3201
+ const role = normalizeMeshDaemonRole(args?.role);
3202
+ const node = addNode(meshId, { workspace, ...(policy ? { policy } : {}), ...(role ? { role } : {}) });
2155
3203
  if (!node) return { success: false, error: 'Mesh not found' };
2156
3204
  return { success: true, node };
2157
3205
  } catch (e: any) {
@@ -2163,6 +3211,8 @@ export class DaemonCommandRouter {
2163
3211
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2164
3212
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2165
3213
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3214
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node update');
3215
+ if (ownerFailure) return ownerFailure;
2166
3216
  try {
2167
3217
  const { updateNode } = await import('../config/mesh-config.js');
2168
3218
  const policy = args?.policy && typeof args.policy === 'object' && !Array.isArray(args.policy)
@@ -2191,6 +3241,8 @@ export class DaemonCommandRouter {
2191
3241
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2192
3242
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2193
3243
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3244
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node removal');
3245
+ if (ownerFailure) return ownerFailure;
2194
3246
  try {
2195
3247
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2196
3248
  const mesh = meshRecord?.mesh;
@@ -2216,38 +3268,104 @@ export class DaemonCommandRouter {
2216
3268
  }
2217
3269
  }
2218
3270
 
3271
+ case 'get_mesh_refine_config_schema': {
3272
+ return {
3273
+ success: true,
3274
+ schema: MESH_REFINE_CONFIG_SCHEMA,
3275
+ locations: MESH_REFINE_CONFIG_LOCATIONS,
3276
+ sourceOfTruth: 'repo mesh/refine config',
3277
+ heuristicRole: 'suggestions_only_not_execution_path',
3278
+ };
3279
+ }
3280
+
3281
+ case 'validate_mesh_refine_config': {
3282
+ const workspace = typeof args?.workspace === 'string' ? args.workspace : process.cwd();
3283
+ const mesh = args?.inlineMesh || {};
3284
+ const loaded = args?.config !== undefined
3285
+ ? { config: args.config, source: 'inline', sourceType: 'mesh_policy' as const }
3286
+ : loadMeshRefineConfig(mesh, workspace);
3287
+ const validation = loaded.config
3288
+ ? validateMeshRefineConfig(loaded.config, loaded.source)
3289
+ : { valid: false, errors: [((loaded as { error?: string }).error) || 'repo mesh/refine config unavailable'], commands: [], rejectedCommands: [] };
3290
+ return { success: validation.valid, ...loaded, ...validation };
3291
+ }
3292
+
3293
+ case 'suggest_mesh_refine_config': {
3294
+ const workspace = typeof args?.workspace === 'string' ? args.workspace : process.cwd();
3295
+ const mesh = args?.inlineMesh || {};
3296
+ return {
3297
+ success: true,
3298
+ ...suggestMeshRefineConfig(mesh, workspace),
3299
+ note: 'Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config.',
3300
+ };
3301
+ }
3302
+
3303
+ case 'plan_mesh_refine_node': {
3304
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3305
+ const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
3306
+ if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3307
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
3308
+ const mesh = meshRecord?.mesh;
3309
+ const node = mesh?.nodes?.find((n: any) => n.id === nodeId || n.nodeId === nodeId);
3310
+ if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
3311
+ return {
3312
+ success: true,
3313
+ dryRun: true,
3314
+ nodeId,
3315
+ workspace: node.workspace,
3316
+ validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
3317
+ mergeWillRun: false,
3318
+ cleanupWillRun: false,
3319
+ };
3320
+ }
3321
+
2219
3322
  case 'refine_mesh_node': {
2220
3323
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2221
3324
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2222
3325
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3326
+ const refineStages: Array<Record<string, unknown>> = [];
2223
3327
  try {
2224
3328
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2225
3329
  const mesh = meshRecord?.mesh;
2226
3330
  const node = mesh?.nodes?.find((n: any) => n.id === nodeId || n.nodeId === nodeId);
2227
- if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
3331
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
2228
3332
 
2229
3333
  if (!node.isLocalWorktree || !node.workspace) {
2230
- return { success: false, error: `Refinery requires a local worktree node` };
3334
+ return { success: false, error: `Refinery requires a local worktree node`, refineStages };
2231
3335
  }
2232
3336
 
2233
3337
  const sourceNode = node.clonedFromNodeId
2234
3338
  ? mesh?.nodes.find((n: any) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId)
2235
3339
  : mesh?.nodes.find((n: any) => !n.isLocalWorktree);
2236
3340
  const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
2237
- if (!repoRoot) return { success: false, error: 'Source node repoRoot not found' };
3341
+ if (!repoRoot) return { success: false, error: 'Source node repoRoot not found', refineStages };
2238
3342
 
2239
3343
  const { execFile } = await import('node:child_process');
2240
3344
  const { promisify } = await import('node:util');
2241
3345
  const execFileAsync = promisify(execFile);
2242
3346
 
3347
+ const resolveStarted = Date.now();
2243
3348
  const { stdout: branchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: node.workspace, encoding: 'utf8' });
2244
3349
  const branch = branchStdout.trim();
2245
- if (!branch) return { success: false, error: 'Could not determine branch of the worktree node' };
3350
+ if (!branch) return { success: false, error: 'Could not determine branch of the worktree node', refineStages };
2246
3351
 
2247
3352
  const { stdout: baseBranchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: repoRoot, encoding: 'utf8' });
2248
3353
  const baseBranch = baseBranchStdout.trim();
3354
+ const { stdout: baseHeadStdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot, encoding: 'utf8' });
3355
+ const { stdout: branchHeadStdout } = await execFileAsync('git', ['rev-parse', branch], { cwd: node.workspace, encoding: 'utf8' });
3356
+ const baseHead = baseHeadStdout.trim();
3357
+ const branchHead = branchHeadStdout.trim();
3358
+ recordMeshRefineStage(refineStages, 'resolve_refs', 'passed', resolveStarted, { branch, baseBranch, baseHead, branchHead });
2249
3359
 
3360
+ const validationStarted = Date.now();
2250
3361
  const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
3362
+ recordMeshRefineStage(
3363
+ refineStages,
3364
+ 'validation',
3365
+ validationSummary.status === 'passed' ? 'passed' : validationSummary.status === 'failed' ? 'failed' : 'skipped',
3366
+ validationStarted,
3367
+ { validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length },
3368
+ );
2251
3369
  if (validationSummary.status === 'failed') {
2252
3370
  return {
2253
3371
  success: false,
@@ -2257,6 +3375,7 @@ export class DaemonCommandRouter {
2257
3375
  branch,
2258
3376
  into: baseBranch,
2259
3377
  validationSummary,
3378
+ refineStages,
2260
3379
  finalBranchConvergenceState: {
2261
3380
  branch,
2262
3381
  baseBranch,
@@ -2276,6 +3395,7 @@ export class DaemonCommandRouter {
2276
3395
  branch,
2277
3396
  into: baseBranch,
2278
3397
  validationSummary,
3398
+ refineStages,
2279
3399
  finalBranchConvergenceState: {
2280
3400
  branch,
2281
3401
  baseBranch,
@@ -2287,39 +3407,127 @@ export class DaemonCommandRouter {
2287
3407
  };
2288
3408
  }
2289
3409
 
3410
+ const patchEquivalenceStarted = Date.now();
3411
+ const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
3412
+ recordMeshRefineStage(refineStages, 'patch_equivalence', patchEquivalence.status, patchEquivalenceStarted, {
3413
+ equivalent: patchEquivalence.equivalent,
3414
+ expectedPatchId: patchEquivalence.expectedPatchId,
3415
+ actualPatchId: patchEquivalence.actualPatchId,
3416
+ error: patchEquivalence.error,
3417
+ });
3418
+ if (!patchEquivalence.equivalent) {
3419
+ return {
3420
+ success: false,
3421
+ code: 'patch_equivalence_failed',
3422
+ convergenceStatus: 'blocked_review',
3423
+ error: 'Refinery patch-equivalence preflight failed; merge/refine was not attempted.',
3424
+ branch,
3425
+ into: baseBranch,
3426
+ validationSummary,
3427
+ patchEquivalence,
3428
+ refineStages,
3429
+ finalBranchConvergenceState: {
3430
+ branch,
3431
+ baseBranch,
3432
+ merged: false,
3433
+ removed: false,
3434
+ validation: 'passed',
3435
+ patchEquivalence: 'failed',
3436
+ status: 'blocked_review',
3437
+ },
3438
+ };
3439
+ }
3440
+
3441
+ let mergeResult: Record<string, unknown> | undefined;
3442
+ const mergeStarted = Date.now();
2290
3443
  try {
2291
- await execFileAsync('git', ['merge', '--no-ff', branch, '-m', `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: 'utf8' });
3444
+ const result = await execFileAsync('git', ['merge', '--no-ff', branch, '-m', `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: 'utf8' });
3445
+ mergeResult = {
3446
+ stdout: truncateValidationOutput(result.stdout),
3447
+ stderr: truncateValidationOutput(result.stderr),
3448
+ durationMs: Date.now() - mergeStarted,
3449
+ };
3450
+ recordMeshRefineStage(refineStages, 'merge', 'passed', mergeStarted, mergeResult);
2292
3451
  } catch (e: any) {
3452
+ recordMeshRefineStage(refineStages, 'merge', 'failed', mergeStarted, {
3453
+ error: e?.message || String(e),
3454
+ stdout: truncateValidationOutput(e?.stdout),
3455
+ stderr: truncateValidationOutput(e?.stderr),
3456
+ });
2293
3457
  return {
2294
3458
  success: false,
2295
3459
  error: `Merge failed (conflicts?): ${e.message}`,
2296
3460
  validationSummary,
3461
+ patchEquivalence,
3462
+ refineStages,
2297
3463
  finalBranchConvergenceState: {
2298
3464
  branch,
2299
3465
  baseBranch,
2300
3466
  merged: false,
2301
3467
  removed: false,
2302
3468
  validation: 'passed',
3469
+ patchEquivalence: 'passed',
2303
3470
  status: 'not_mergeable',
2304
3471
  },
2305
3472
  };
2306
3473
  }
2307
3474
 
3475
+ const cleanupStarted = Date.now();
2308
3476
  const removeResult = await this.execute('remove_mesh_node', {
2309
3477
  meshId,
2310
3478
  nodeId,
2311
- sessionCleanupMode: 'kill',
3479
+ sessionCleanupMode: 'preserve',
2312
3480
  inlineMesh: args?.inlineMesh,
2313
3481
  });
3482
+ recordMeshRefineStage(refineStages, 'cleanup', removeResult?.success === false ? 'failed' : 'passed', cleanupStarted, {
3483
+ removed: removeResult?.removed,
3484
+ code: removeResult?.code,
3485
+ error: removeResult?.error,
3486
+ });
2314
3487
 
3488
+ let ledgerError: string | undefined;
3489
+ const ledgerStarted = Date.now();
2315
3490
  try {
2316
3491
  const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
2317
3492
  appendLedgerEntry(meshId, {
2318
3493
  kind: 'node_removed',
2319
3494
  nodeId,
2320
- payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary },
3495
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence },
2321
3496
  });
2322
- } catch {}
3497
+ recordMeshRefineStage(refineStages, 'ledger', 'passed', ledgerStarted);
3498
+ } catch (e: any) {
3499
+ ledgerError = e?.message || String(e);
3500
+ recordMeshRefineStage(refineStages, 'ledger', 'failed', ledgerStarted, { error: ledgerError });
3501
+ }
3502
+
3503
+ const finalBranchConvergenceState = {
3504
+ branch: baseBranch,
3505
+ mergedBranch: branch,
3506
+ baseBranch,
3507
+ merged: true,
3508
+ removed: removeResult?.success !== false,
3509
+ validation: 'passed',
3510
+ patchEquivalence: 'passed',
3511
+ status: removeResult?.success === false ? 'merged_cleanup_failed' : 'merged',
3512
+ };
3513
+
3514
+ if (removeResult?.success === false) {
3515
+ return {
3516
+ success: false,
3517
+ code: 'cleanup_failed',
3518
+ error: 'Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.',
3519
+ merged: true,
3520
+ branch,
3521
+ into: baseBranch,
3522
+ removeResult,
3523
+ validationSummary,
3524
+ patchEquivalence,
3525
+ mergeResult,
3526
+ refineStages,
3527
+ ...(ledgerError ? { ledgerError } : {}),
3528
+ finalBranchConvergenceState,
3529
+ };
3530
+ }
2323
3531
 
2324
3532
  return {
2325
3533
  success: true,
@@ -2328,18 +3536,14 @@ export class DaemonCommandRouter {
2328
3536
  into: baseBranch,
2329
3537
  removeResult,
2330
3538
  validationSummary,
2331
- finalBranchConvergenceState: {
2332
- branch: baseBranch,
2333
- mergedBranch: branch,
2334
- baseBranch,
2335
- merged: true,
2336
- removed: removeResult?.success !== false,
2337
- validation: 'passed',
2338
- status: removeResult?.success === false ? 'merged_cleanup_failed' : 'merged',
2339
- },
3539
+ patchEquivalence,
3540
+ mergeResult,
3541
+ refineStages,
3542
+ ...(ledgerError ? { ledgerError } : {}),
3543
+ finalBranchConvergenceState,
2340
3544
  };
2341
3545
  } catch (e: any) {
2342
- return { success: false, error: e.message };
3546
+ return { success: false, error: e.message, refineStages };
2343
3547
  }
2344
3548
  }
2345
3549
 
@@ -2384,6 +3588,7 @@ export class DaemonCommandRouter {
2384
3588
  } else {
2385
3589
  const { removeNode } = await import('../config/mesh-config.js');
2386
3590
  removed = removeNode(meshId, nodeId);
3591
+ if (removed) this.invalidateAggregateMeshStatus(meshId);
2387
3592
  }
2388
3593
 
2389
3594
  // Record in task ledger
@@ -2421,6 +3626,8 @@ export class DaemonCommandRouter {
2421
3626
  if (!meshId) return { success: false, error: 'meshId required' };
2422
3627
  if (!sourceNodeId) return { success: false, error: 'sourceNodeId required' };
2423
3628
  if (!branch) return { success: false, error: 'branch required' };
3629
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'worktree clone');
3630
+ if (ownerFailure) return ownerFailure;
2424
3631
 
2425
3632
  try {
2426
3633
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
@@ -2469,6 +3676,7 @@ export class DaemonCommandRouter {
2469
3676
  policy: { ...(sourceNode.policy || {}) },
2470
3677
  });
2471
3678
  if (!node) return { success: false, error: 'Failed to register worktree node' };
3679
+ this.invalidateAggregateMeshStatus(meshId);
2472
3680
  }
2473
3681
 
2474
3682
  // Initialize submodules if policy allows (default: true)
@@ -2510,6 +3718,8 @@ export class DaemonCommandRouter {
2510
3718
  case 'trigger_mesh_queue': {
2511
3719
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2512
3720
  if (!meshId) return { success: false, error: 'meshId required' };
3721
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue trigger');
3722
+ if (ownerFailure) return ownerFailure;
2513
3723
  try {
2514
3724
  const { triggerMeshQueue } = await import('../mesh/mesh-events.js');
2515
3725
  if (meshId) {
@@ -2541,6 +3751,15 @@ export class DaemonCommandRouter {
2541
3751
  mesh = getMesh(meshId);
2542
3752
  }
2543
3753
  if (!mesh) return { success: false, error: 'Mesh not found' };
3754
+ const meshHost = resolveMeshHostStatus(mesh);
3755
+ if (!meshHost.canOwnCoordinator) {
3756
+ return {
3757
+ success: false,
3758
+ ...buildMeshHostRequiredFailure(mesh, 'coordinator launch'),
3759
+ meshId,
3760
+ cliType,
3761
+ };
3762
+ }
2544
3763
  if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: 'No nodes in mesh' };
2545
3764
 
2546
3765
  const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === 'string'
@@ -2560,7 +3779,16 @@ export class DaemonCommandRouter {
2560
3779
  cliType,
2561
3780
  };
2562
3781
  }
2563
- const workspace = typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '';
3782
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
3783
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
3784
+ : [];
3785
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
3786
+ const workspace = readLiveMeshNodeWorkspace({
3787
+ meshId,
3788
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ''),
3789
+ liveSessionRecords: liveMeshSessions,
3790
+ allowCoordinatorSession: true,
3791
+ }) || (typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '');
2564
3792
  if (!workspace) return { success: false, error: 'Coordinator node workspace required', meshId, cliType };
2565
3793
  if (!cliType) {
2566
3794
  const resolved = await resolveProviderTypeFromPriority({
@@ -2905,6 +4133,27 @@ export class DaemonCommandRouter {
2905
4133
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2906
4134
  const mesh = meshRecord?.mesh;
2907
4135
  if (!mesh) return { success: false, error: 'Mesh not found' };
4136
+ const meshHost = resolveMeshHostStatus(mesh);
4137
+
4138
+ const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
4139
+ const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
4140
+ if (!refreshRequested) {
4141
+ const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
4142
+ if (cachedStatus) {
4143
+ logRepoMeshStatusDebug('return_cached', {
4144
+ meshId,
4145
+ command: 'mesh_status',
4146
+ refreshRequested,
4147
+ summary: summarizeRepoMeshStatusDebug(cachedStatus),
4148
+ });
4149
+ return cachedStatus;
4150
+ }
4151
+ }
4152
+ const refreshReason = refreshRequested
4153
+ ? 'explicit_refresh'
4154
+ : hadAggregateCache
4155
+ ? 'stale_pending_cache_refresh'
4156
+ : 'cold_cache_miss';
2908
4157
 
2909
4158
  const { getMeshQueueStats, getQueue } = await import('../mesh/mesh-work-queue.js');
2910
4159
  const queue = getQueue(meshId);
@@ -2913,58 +4162,344 @@ export class DaemonCommandRouter {
2913
4162
  const { readLedgerEntries, getLedgerSummary } = await import('../mesh/mesh-ledger.js');
2914
4163
  const ledgerEntries = readLedgerEntries(meshId, { tail: 20 });
2915
4164
  const ledgerSummary = getLedgerSummary(meshId);
2916
-
4165
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
4166
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
4167
+ : [];
4168
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
4169
+
4170
+ const localMachineId = loadConfig().machineId || '';
4171
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
4172
+ const directTruth = requireDirectPeerTruth
4173
+ ? await hydrateInlineMeshDirectTruth({
4174
+ mesh,
4175
+ meshSource: meshRecord.source,
4176
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4177
+ statusInstanceId: this.deps.statusInstanceId,
4178
+ localMachineId,
4179
+ })
4180
+ : {
4181
+ directEvidenceCount: 0,
4182
+ localConfirmedCount: 0,
4183
+ peerAttemptedCount: 0,
4184
+ peerConfirmedCount: 0,
4185
+ unavailableNodeIds: [] as string[],
4186
+ };
4187
+ // Default/cached loads may not attempt a remote peer probe yet; do not surface that as
4188
+ // a direct mesh truth failure until an explicit probe attempt actually fails.
4189
+ const passivePeerTruthNotAttempted = requireDirectPeerTruth
4190
+ && !refreshRequested
4191
+ && directTruth.directEvidenceCount > 0
4192
+ && directTruth.peerAttemptedCount === 0;
4193
+ const effectiveDirectTruth = passivePeerTruthNotAttempted
4194
+ ? { ...directTruth, unavailableNodeIds: [] as string[] }
4195
+ : directTruth;
4196
+ const directTruthSatisfied = !requireDirectPeerTruth
4197
+ || (effectiveDirectTruth.directEvidenceCount > 0 && effectiveDirectTruth.unavailableNodeIds.length === 0);
4198
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
4199
+ const failureResult = {
4200
+ success: false,
4201
+ code: 'mesh_direct_peer_truth_unavailable',
4202
+ error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.',
4203
+ sourceOfTruth: {
4204
+ membership: meshRecord.source === 'inline_cache'
4205
+ ? 'coordinator_inline_mesh_cache'
4206
+ : meshRecord.source === 'local_config'
4207
+ ? 'local_mesh_config'
4208
+ : 'inline_bootstrap_snapshot',
4209
+ coordinatorOwnsLiveTruth: false,
4210
+ currentStatus: 'direct_peer_truth_unavailable',
4211
+ directPeerTruth: {
4212
+ required: true,
4213
+ satisfied: false,
4214
+ directEvidenceCount: directTruth.directEvidenceCount,
4215
+ localConfirmedCount: directTruth.localConfirmedCount,
4216
+ peerAttemptedCount: directTruth.peerAttemptedCount,
4217
+ peerConfirmedCount: directTruth.peerConfirmedCount,
4218
+ unavailableNodeIds: directTruth.unavailableNodeIds,
4219
+ },
4220
+ },
4221
+ };
4222
+ logRepoMeshStatusDebug('direct_truth_unavailable', {
4223
+ meshId,
4224
+ command: 'mesh_status',
4225
+ refreshRequested,
4226
+ meshSource: meshRecord.source,
4227
+ directTruth,
4228
+ });
4229
+ return failureResult;
4230
+ }
4231
+ const directTruthUnavailableNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
4232
+ const selectedCoordinatorNodeId = readStringValue(
4233
+ mesh.coordinator?.preferredNodeId,
4234
+ (mesh.nodes?.[0] as any)?.id,
4235
+ (mesh.nodes?.[0] as any)?.nodeId,
4236
+ );
4237
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes)
4238
+ ? selectedCoordinatorNodeId
4239
+ : undefined;
4240
+ const refreshedAt = new Date().toISOString();
2917
4241
  const nodeStatuses = [];
2918
- for (const node of mesh.nodes || []) {
4242
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
4243
+ const nodeId = String(node.id || node.nodeId || '');
4244
+ const daemonId = readStringValue(node.daemonId);
4245
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
4246
+ const isSelfNode = Boolean(
4247
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
4248
+ ) || Boolean(
4249
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId),
4250
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
2919
4251
  const status: Record<string, unknown> = {
2920
- nodeId: node.id || node.nodeId,
4252
+ nodeId,
2921
4253
  machineLabel: node.machineLabel || node.id || node.nodeId,
2922
4254
  workspace: node.workspace,
2923
4255
  repoRoot: node.repoRoot,
2924
4256
  isLocalWorktree: node.isLocalWorktree,
2925
4257
  worktreeBranch: node.worktreeBranch,
2926
- daemonId: node.daemonId,
4258
+ role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? 'host' : undefined),
4259
+ daemonId,
2927
4260
  machineId: node.machineId,
4261
+ machineStatus: node.machineStatus,
2928
4262
  health: 'unknown',
2929
4263
  providers: node.providers || [],
4264
+ providerPriority,
2930
4265
  activeSessions: [],
4266
+ activeSessionDetails: [],
4267
+ launchReady: false,
2931
4268
  };
2932
- if (node.workspace && typeof node.workspace === 'string') {
2933
- if (!fs.existsSync(node.workspace as string) && applyCachedInlineMeshNodeStatus(status, node)) {
2934
- nodeStatuses.push(status);
2935
- continue;
4269
+ if (isSelfNode) {
4270
+ status.connection = {
4271
+ perspective: 'selected_coordinator',
4272
+ source: 'mesh_peer_status',
4273
+ state: 'self',
4274
+ transport: 'local',
4275
+ reported: true,
4276
+ reason: 'Selected coordinator daemon',
4277
+ lastStateChangeAt: refreshedAt,
4278
+ };
4279
+ } else if (daemonId) {
4280
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
4281
+ status.connection = connection ?? {
4282
+ perspective: 'selected_coordinator',
4283
+ source: 'not_reported',
4284
+ state: 'unknown',
4285
+ transport: 'unknown',
4286
+ reported: false,
4287
+ reason: 'No live mesh peer telemetry reported by the selected coordinator yet.',
4288
+ };
4289
+ } else {
4290
+ status.connection = {
4291
+ perspective: 'selected_coordinator',
4292
+ source: 'not_reported',
4293
+ state: 'unknown',
4294
+ transport: 'unknown',
4295
+ reported: false,
4296
+ reason: 'Node has no daemon id, so mesh transport cannot be reported from the selected coordinator.',
4297
+ };
4298
+ }
4299
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
4300
+ meshId,
4301
+ node,
4302
+ nodeId,
4303
+ liveSessionRecords: liveMeshSessions,
4304
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
4305
+ });
4306
+ const workspace = readLiveMeshNodeWorkspace({
4307
+ meshId,
4308
+ nodeId,
4309
+ liveSessionRecords: matchedLiveSessionRecords,
4310
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
4311
+ }) || (typeof node.workspace === 'string' ? node.workspace : '');
4312
+ status.workspace = workspace || node.workspace;
4313
+ if (matchedLiveSessionRecords.length > 0) {
4314
+ const sessionIds = matchedLiveSessionRecords
4315
+ .map((record: any) => typeof record?.sessionId === 'string' ? record.sessionId : '')
4316
+ .filter(Boolean);
4317
+ const providerTypes = matchedLiveSessionRecords
4318
+ .map((record: any) => readStringValue(record?.providerType))
4319
+ .filter(Boolean) as string[];
4320
+ status.activeSessions = sessionIds;
4321
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
4322
+ if (providerTypes.length > 0) {
4323
+ status.providers = Array.from(new Set([...(Array.isArray(status.providers) ? status.providers as string[] : []), ...providerTypes]));
2936
4324
  }
2937
- try {
2938
- const gitStatus = await getGitRepoStatus(node.workspace as string, { timeoutMs: 10_000 });
2939
- status.git = gitStatus;
2940
- if (gitStatus.isGitRepo) {
2941
- const dirty = (gitStatus.staged + gitStatus.modified + gitStatus.untracked + gitStatus.deleted + gitStatus.renamed) > 0;
2942
- status.health = gitStatus.branch ? (dirty ? 'dirty' : 'online') : 'degraded';
2943
- } else {
2944
- status.health = 'degraded';
2945
- if (gitStatus.error && !status.error) status.error = gitStatus.error;
4325
+ }
4326
+ if (workspace) {
4327
+ if (!fs.existsSync(workspace)) {
4328
+ // Workspace not local — prefer direct live inline truth, then attempt a P2P git probe.
4329
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
4330
+ let remoteProbeApplied = false;
4331
+ if (inlineTransitGit) {
4332
+ status.git = inlineTransitGit;
4333
+ status.health = inlineTransitGit.isGitRepo
4334
+ ? deriveMeshNodeHealthFromGit(inlineTransitGit as unknown as Record<string, unknown>)
4335
+ : 'degraded';
4336
+ const connection = readObjectRecord(status.connection);
4337
+ const connectionState = readStringValue(connection.state);
4338
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4339
+ if (!connectionReported || connectionState === 'unknown') {
4340
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4341
+ }
4342
+ remoteProbeApplied = true;
4343
+ } else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
4344
+ try {
4345
+ const remoteGit = await probeRemoteMeshGitStatus({
4346
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4347
+ daemonId,
4348
+ workspace,
4349
+ timeoutMs: 8000,
4350
+ });
4351
+ if (remoteGit) {
4352
+ status.git = remoteGit;
4353
+ status.health = remoteGit.isGitRepo
4354
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
4355
+ : 'degraded';
4356
+ const connection = readObjectRecord(status.connection);
4357
+ const connectionState = readStringValue(connection.state);
4358
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4359
+ if (!connectionReported || connectionState === 'unknown') {
4360
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4361
+ }
4362
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
4363
+ remoteProbeApplied = true;
4364
+ }
4365
+ } catch {
4366
+ const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
4367
+ const refreshedConnectionState = readStringValue(refreshedConnection?.state);
4368
+ if (refreshedConnection && refreshedConnectionState === 'connected') {
4369
+ status.connection = refreshedConnection;
4370
+ try {
4371
+ const remoteGit = await probeRemoteMeshGitStatus({
4372
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4373
+ daemonId,
4374
+ workspace,
4375
+ timeoutMs: 12000,
4376
+ });
4377
+ if (remoteGit) {
4378
+ status.git = remoteGit;
4379
+ status.health = remoteGit.isGitRepo
4380
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
4381
+ : 'degraded';
4382
+ const connection = readObjectRecord(status.connection);
4383
+ const connectionState = readStringValue(connection.state);
4384
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4385
+ if (!connectionReported || connectionState === 'unknown') {
4386
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4387
+ }
4388
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
4389
+ remoteProbeApplied = true;
4390
+ }
4391
+ } catch {
4392
+ // Probe timed out again or P2P unavailable — fall back to cached status
4393
+ }
4394
+ }
4395
+ }
2946
4396
  }
2947
- } catch {
2948
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
2949
- status.health = 'degraded';
4397
+ if (!remoteProbeApplied) {
4398
+ const connectionState = readStringValue((status.connection as any)?.state);
4399
+ const pendingPeerGitProbe = !inlineTransitGit
4400
+ && !isSelfNode
4401
+ && !!daemonId
4402
+ && (
4403
+ readStringValue(status.machineStatus) === 'online'
4404
+ || readStringValue(status.health) === 'online'
4405
+ || connectionState === 'connecting'
4406
+ || connectionState === 'connected'
4407
+ || connectionState === 'unknown'
4408
+ );
4409
+ if (pendingPeerGitProbe) {
4410
+ status.gitProbePending = true;
4411
+ status.health = 'unknown';
4412
+ }
4413
+ if (applyCachedInlineMeshNodeStatus(
4414
+ status,
4415
+ node,
4416
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : undefined,
4417
+ )) {
4418
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4419
+ nodeStatuses.push(status);
4420
+ continue;
4421
+ }
4422
+ if (meshRecord?.source === 'inline_cache' && !isSelfNode) {
4423
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4424
+ nodeStatuses.push(status);
4425
+ continue;
4426
+ }
4427
+ }
4428
+ } else {
4429
+ try {
4430
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
4431
+ status.git = gitStatus;
4432
+ recordInlineMeshDirectGitTruth(node, gitStatus as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
4433
+ if (gitStatus.isGitRepo) {
4434
+ status.health = deriveMeshNodeHealthFromGit(gitStatus as unknown as Record<string, unknown>);
4435
+ } else {
4436
+ status.health = 'degraded';
4437
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
4438
+ }
4439
+ } catch {
4440
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
4441
+ status.health = 'degraded';
4442
+ }
2950
4443
  }
2951
4444
  }
2952
4445
  } else {
2953
4446
  applyCachedInlineMeshNodeStatus(status, node);
2954
4447
  }
4448
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
2955
4449
  nodeStatuses.push(status);
2956
4450
  }
2957
4451
 
2958
- return {
4452
+ const statusResult = {
2959
4453
  success: true,
2960
4454
  meshId: mesh.id,
2961
4455
  meshName: mesh.name,
2962
4456
  repoIdentity: mesh.repoIdentity,
2963
4457
  defaultBranch: mesh.defaultBranch,
4458
+ refreshedAt,
4459
+ meshHost,
4460
+ sourceOfTruth: {
4461
+ membership: meshRecord?.source === 'inline_cache'
4462
+ ? 'coordinator_inline_mesh_cache'
4463
+ : meshRecord?.source === 'local_config'
4464
+ ? 'local_mesh_config'
4465
+ : 'inline_bootstrap_snapshot',
4466
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
4467
+ meshHost: {
4468
+ owner: 'mesh_host_daemon',
4469
+ localRole: meshHost.role,
4470
+ hostDaemonId: meshHost.hostDaemonId,
4471
+ hostNodeId: meshHost.hostNodeId,
4472
+ hostAddress: meshHost.hostAddress,
4473
+ },
4474
+ ...(requireDirectPeerTruth ? {
4475
+ currentStatus: directTruthSatisfied ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
4476
+ directPeerTruth: {
4477
+ required: true,
4478
+ satisfied: directTruthSatisfied,
4479
+ directEvidenceCount: effectiveDirectTruth.directEvidenceCount,
4480
+ localConfirmedCount: effectiveDirectTruth.localConfirmedCount,
4481
+ peerAttemptedCount: effectiveDirectTruth.peerAttemptedCount,
4482
+ peerConfirmedCount: effectiveDirectTruth.peerConfirmedCount,
4483
+ unavailableNodeIds: effectiveDirectTruth.unavailableNodeIds,
4484
+ },
4485
+ } : {}),
4486
+ historicalEvidenceOnly: ['recoveryHints', 'ledger.summary', 'queue.summary'],
4487
+ },
2964
4488
  nodes: nodeStatuses,
2965
4489
  queue: { tasks: queue, summary: queueSummary },
2966
4490
  ledger: { entries: ledgerEntries, summary: ledgerSummary },
2967
4491
  };
4492
+ const rememberedStatus = this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
4493
+ logRepoMeshStatusDebug('return_live', {
4494
+ meshId,
4495
+ command: 'mesh_status',
4496
+ refreshRequested,
4497
+ refreshReason,
4498
+ meshSource: meshRecord.source,
4499
+ directTruth,
4500
+ summary: summarizeRepoMeshStatusDebug(rememberedStatus),
4501
+ });
4502
+ return rememberedStatus;
2968
4503
  } catch (e: any) {
2969
4504
  return { success: false, error: e.message };
2970
4505
  }