@adhdev/daemon-core 0.9.82-rc.6 → 0.9.82-rc.61

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.
Files changed (42) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +2 -0
  2. package/dist/commands/router.d.ts +24 -0
  3. package/dist/config/mesh-config.d.ts +66 -1
  4. package/dist/git/git-commands.d.ts +1 -0
  5. package/dist/git/git-status.d.ts +5 -0
  6. package/dist/git/git-types.d.ts +10 -0
  7. package/dist/index.d.ts +13 -6
  8. package/dist/index.js +3522 -593
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +3496 -587
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/mesh/mesh-active-work.d.ts +48 -0
  13. package/dist/mesh/mesh-events.d.ts +17 -5
  14. package/dist/mesh/mesh-fast-forward.d.ts +39 -0
  15. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  16. package/dist/mesh/mesh-ledger.d.ts +38 -1
  17. package/dist/mesh/mesh-work-queue.d.ts +23 -5
  18. package/dist/mesh/refine-config.d.ts +119 -0
  19. package/dist/providers/chat-message-normalization.d.ts +1 -0
  20. package/dist/providers/cli-provider-instance.d.ts +1 -0
  21. package/dist/repo-mesh-types.d.ts +160 -0
  22. package/package.json +1 -1
  23. package/src/boot/daemon-lifecycle.ts +4 -0
  24. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  25. package/src/commands/router.ts +2178 -419
  26. package/src/config/mesh-config.ts +244 -1
  27. package/src/git/git-commands.ts +3 -3
  28. package/src/git/git-status.ts +97 -6
  29. package/src/git/git-summary.ts +3 -0
  30. package/src/git/git-types.ts +11 -0
  31. package/src/index.ts +39 -5
  32. package/src/mesh/coordinator-prompt.ts +4 -2
  33. package/src/mesh/mesh-active-work.ts +205 -0
  34. package/src/mesh/mesh-events.ts +210 -38
  35. package/src/mesh/mesh-fast-forward.ts +430 -0
  36. package/src/mesh/mesh-host-ownership.ts +73 -0
  37. package/src/mesh/mesh-ledger.ts +137 -0
  38. package/src/mesh/mesh-work-queue.ts +202 -122
  39. package/src/mesh/refine-config.ts +306 -0
  40. package/src/providers/chat-message-normalization.ts +3 -1
  41. package/src/providers/cli-provider-instance.ts +66 -1
  42. package/src/repo-mesh-types.ts +174 -0
@@ -38,7 +38,18 @@ import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../l
38
38
  import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
39
39
  import { createHermesManualMeshCoordinatorSetup, resolveMeshCoordinatorSetup } from './mesh-coordinator.js';
40
40
  import { buildSessionEntries } from '../status/builders.js';
41
- import { handleMeshForwardEvent, drainPendingMeshCoordinatorEvents } from '../mesh/mesh-events.js';
41
+ import { handleMeshForwardEvent, drainPendingMeshCoordinatorEvents, queuePendingMeshCoordinatorEvent } from '../mesh/mesh-events.js';
42
+ import { buildMeshHostRequiredFailure, normalizeMeshDaemonRole, resolveMeshHostStatus } from '../mesh/mesh-host-ownership.js';
43
+ import { fastForwardMeshNode } from '../mesh/mesh-fast-forward.js';
44
+ import {
45
+ MESH_REFINE_CONFIG_LOCATIONS,
46
+ MESH_REFINE_CONFIG_SCHEMA,
47
+ loadMeshRefineConfig,
48
+ resolveMeshRefineValidationPlan,
49
+ suggestMeshRefineConfig,
50
+ validateMeshRefineConfig,
51
+ type MeshRefineValidationCommandPlan,
52
+ } from '../mesh/refine-config.js';
42
53
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
43
54
  import { getSessionCompletionMarker } from '../status/snapshot.js';
44
55
  import { execNpmCommandSync, resolveCurrentGlobalInstallSurface, spawnDetachedDaemonUpgradeHelper } from './upgrade-helper.js';
@@ -114,14 +125,93 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
114
125
  return undefined;
115
126
  }
116
127
 
117
- function readGitSubmodules(value: unknown): GitSubmoduleStatus[] | undefined {
128
+ function summarizeRepoMeshDebugGit(git: unknown): Record<string, unknown> | null {
129
+ const record = readObjectRecord(git);
130
+ if (!Object.keys(record).length) return null;
131
+ const submodules = Array.isArray(record.submodules)
132
+ ? record.submodules.map((entry: any) => ({
133
+ path: readStringValue(entry?.path) ?? null,
134
+ commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
135
+ dirty: readBooleanValue(entry?.dirty) ?? false,
136
+ outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false,
137
+ }))
138
+ : [];
139
+ return {
140
+ isGitRepo: readBooleanValue(record.isGitRepo),
141
+ workspace: readStringValue(record.workspace) ?? null,
142
+ repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
143
+ branch: readStringValue(record.branch) ?? null,
144
+ upstream: readStringValue(record.upstream) ?? null,
145
+ upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
146
+ headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
147
+ ahead: readNumberValue(record.ahead) ?? null,
148
+ behind: readNumberValue(record.behind) ?? null,
149
+ dirtyCounts: {
150
+ staged: readNumberValue(record.staged) ?? 0,
151
+ modified: readNumberValue(record.modified) ?? 0,
152
+ untracked: readNumberValue(record.untracked) ?? 0,
153
+ deleted: readNumberValue(record.deleted) ?? 0,
154
+ renamed: readNumberValue(record.renamed) ?? 0,
155
+ },
156
+ lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
157
+ submoduleCount: submodules.length,
158
+ submodules,
159
+ };
160
+ }
161
+
162
+ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
163
+ const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
164
+ return {
165
+ success: status?.success,
166
+ meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
167
+ refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
168
+ sourceOfTruth: status?.sourceOfTruth ?? null,
169
+ nodeCount: nodes.length,
170
+ nodes: nodes.map((node: any) => ({
171
+ nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
172
+ daemonId: readStringValue(node?.daemonId, node?.daemon_id) ?? null,
173
+ workspace: readStringValue(node?.workspace, node?.git?.workspace) ?? null,
174
+ health: readStringValue(node?.health) ?? null,
175
+ machineStatus: readStringValue(node?.machineStatus, node?.machine_status) ?? null,
176
+ connection: node?.connection && typeof node.connection === 'object' ? {
177
+ state: readStringValue(node.connection.state) ?? null,
178
+ transport: readStringValue(node.connection.transport) ?? null,
179
+ source: readStringValue(node.connection.source) ?? null,
180
+ reported: readBooleanValue(node.connection.reported) ?? null,
181
+ } : null,
182
+ gitProbePending: node?.gitProbePending === true,
183
+ launchReady: node?.launchReady === true,
184
+ git: summarizeRepoMeshDebugGit(node?.git),
185
+ })),
186
+ };
187
+ }
188
+
189
+ function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>): void {
190
+ try {
191
+ LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
192
+ } catch {
193
+ LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${event}`);
194
+ }
195
+ }
196
+
197
+ function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {
198
+ const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\/]+$/, '') : '';
199
+ const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : '';
200
+ if (!normalizedPath) return undefined;
201
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
202
+ if (!normalizedRoot) return undefined;
203
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, '')}`;
204
+ }
205
+
206
+ function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {
118
207
  if (!Array.isArray(value)) return undefined;
119
208
  const submodules = value
120
209
  .map(entry => {
121
210
  const submodule = readObjectRecord(entry);
122
211
  const path = readStringValue(submodule.path);
123
212
  const commit = readStringValue(submodule.commit);
124
- const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
213
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root)
214
+ ?? joinRepoPath(parentRepoRoot, path);
125
215
  if (!path || !commit || !repoPath) return null;
126
216
  return {
127
217
  path,
@@ -137,60 +227,11 @@ function readGitSubmodules(value: unknown): GitSubmoduleStatus[] | undefined {
137
227
  return submodules.length > 0 ? submodules : undefined;
138
228
  }
139
229
 
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
- : {};
230
+ function normalizeInlineMeshGitStatus(
231
+ status: Record<string, unknown>,
232
+ node: any,
233
+ options?: { lastCheckedAt?: number },
234
+ ): Record<string, unknown> | undefined {
194
235
  const isGitRepo = readBooleanValue(status.isGitRepo);
195
236
  if (!Object.keys(status).length || isGitRepo === undefined) return undefined;
196
237
  const conflictFiles = Array.isArray(status.conflictFiles)
@@ -198,10 +239,11 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
198
239
  : [];
199
240
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
200
241
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
201
- const submodules = readGitSubmodules(status.submodules);
242
+ const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || undefined;
243
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
202
244
  return {
203
245
  workspace: readStringValue(status.workspace, node?.workspace) || '',
204
- repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
246
+ repoRoot: repoRoot ?? null,
205
247
  isGitRepo,
206
248
  branch: readStringValue(status.branch) ?? null,
207
249
  headCommit: readStringValue(status.headCommit) ?? null,
@@ -217,31 +259,589 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
217
259
  hasConflicts,
218
260
  conflictFiles,
219
261
  stashCount: readNumberValue(status.stashCount) ?? 0,
220
- lastCheckedAt: Date.now(),
262
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
221
263
  ...(submodules ? { submodules } : {}),
222
264
  };
223
265
  }
224
266
 
225
- function applyCachedInlineMeshNodeStatus(status: Record<string, unknown>, node: any): boolean {
267
+ function scoreInlineMeshGitStatus(git: Record<string, unknown> | undefined): number {
268
+ if (!git) return Number.NEGATIVE_INFINITY;
269
+ let score = 0;
270
+ if (readBooleanValue(git.isGitRepo) === true) score += 50;
271
+ if (readBooleanValue(git.isGitRepo) === false) score -= 10;
272
+ if (readStringValue(git.branch)) score += 20;
273
+ if (readStringValue(git.headCommit)) score += 20;
274
+ if (readStringValue(git.upstream)) score += 10;
275
+ if (readStringValue(git.upstreamStatus)) score += 5;
276
+ if (readNumberValue(git.ahead) !== undefined) score += 2;
277
+ if (readNumberValue(git.behind) !== undefined) score += 2;
278
+ if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
279
+ if (readStringValue(git.error)) score -= 20;
280
+ return score;
281
+ }
282
+
283
+ function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
284
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
285
+ const gitResult = readObjectRecord(rawGit.result);
286
+ const directStatus = readObjectRecord(rawGit.status);
287
+ const nestedStatus = readObjectRecord(gitResult.status);
288
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
289
+ const probeGit = readObjectRecord(rawProbe.git);
290
+ const probeGitResult = readObjectRecord(probeGit.result);
291
+ const probeDirectStatus = readObjectRecord(probeGit.status);
292
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
293
+ const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
294
+ let best: { git: Record<string, unknown>; score: number } | null = null;
295
+ for (const status of candidates) {
296
+ const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
297
+ if (!normalized) continue;
298
+ const score = scoreInlineMeshGitStatus(normalized);
299
+ if (!best || score > best.score) best = { git: normalized, score };
300
+ }
301
+ return best?.git;
302
+ }
303
+
304
+ function shouldRefreshStalePendingAggregate(snapshot: any, options?: { requireDirectPeerTruth?: boolean }): boolean {
305
+ if (options?.requireDirectPeerTruth !== true || !Array.isArray(snapshot?.nodes)) return false;
306
+ return snapshot.nodes.some((node: any) => {
307
+ if (node?.gitProbePending !== true) return false;
308
+ const git = readObjectRecord(node?.git);
309
+ return !readBooleanValue(git.isGitRepo) && !readStringValue(git.branch, git.headCommit, git.upstream);
310
+ });
311
+ }
312
+
313
+ function buildLivePeerGitConnection(connection: Record<string, unknown>, timestamp = new Date().toISOString()): Record<string, unknown> {
314
+ const source = readStringValue(connection.source);
315
+ const transport = readStringValue(connection.transport);
316
+ return {
317
+ ...connection,
318
+ perspective: readStringValue(connection.perspective) ?? 'selected_coordinator',
319
+ source: source && source !== 'not_reported' ? source : 'mesh_peer_status',
320
+ state: 'connected',
321
+ transport: transport && transport !== 'unknown' ? transport : 'direct',
322
+ reported: true,
323
+ reason: 'Live peer git snapshot reported by the selected coordinator.',
324
+ lastStateChangeAt: readStringValue(connection.lastStateChangeAt) ?? timestamp,
325
+ };
326
+ }
327
+
328
+ function recordInlineMeshDirectGitTruth(
329
+ node: any,
330
+ git: Record<string, unknown>,
331
+ source: 'selected_coordinator_local_git' | 'selected_coordinator_mesh_p2p_git',
332
+ ): void {
333
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return;
334
+ const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
335
+ const updatedAt = new Date(checkedAt).toISOString();
336
+ const nextGit: Record<string, unknown> = {
337
+ ...git,
338
+ lastCheckedAt: checkedAt,
339
+ };
340
+ node.lastGit = {
341
+ source,
342
+ checkedAt,
343
+ status: nextGit,
344
+ };
345
+ node.last_git = node.lastGit;
346
+ node.machineStatus = 'online';
347
+ node.updatedAt = updatedAt;
348
+ node.lastSeenAt = updatedAt;
349
+ const repoRoot = readStringValue(nextGit.repoRoot);
350
+ if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
351
+ }
352
+
353
+ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
354
+ const liveGit = buildInlineMeshTransitGitStatus(node);
355
+ if (liveGit) return liveGit;
356
+
357
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
358
+ const cachedGit = readObjectRecord(cachedStatus.git);
359
+ if (!Object.keys(cachedGit).length) return undefined;
360
+ return normalizeInlineMeshGitStatus(cachedGit, node);
361
+ }
362
+
363
+ function shouldDiscardCachedInlineMeshStatus(node: any): boolean {
364
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
365
+ if (!Object.keys(cachedStatus).length) return false;
366
+ const cachedGit = readObjectRecord(cachedStatus.git);
367
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
368
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
369
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
370
+ const branch = readStringValue(cachedGit.branch);
371
+ const headCommit = readStringValue(cachedGit.headCommit);
372
+ return isGitRepo === false && !branch && !headCommit;
373
+ }
374
+
375
+ function stripInlineMeshTransientNodeState(node: any): any {
376
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return node;
377
+ const {
378
+ cachedStatus,
379
+ lastGit: _lastGit,
380
+ last_git: _lastGitLegacy,
381
+ lastProbe: _lastProbe,
382
+ last_probe: _lastProbeLegacy,
383
+ error: _error,
384
+ health: _health,
385
+ machineStatus: _machineStatus,
386
+ lastSeenAt: _lastSeenAt,
387
+ last_seen_at: _lastSeenAtLegacy,
388
+ updatedAt: _updatedAt,
389
+ updated_at: _updatedAtLegacy,
390
+ activeSession: _activeSession,
391
+ active_session: _activeSessionLegacy,
392
+ activeSessionId: _activeSessionId,
393
+ active_session_id: _activeSessionIdLegacy,
394
+ sessionId: _sessionId,
395
+ session_id: _sessionIdLegacy,
396
+ providerType: _providerType,
397
+ provider_type: _providerTypeLegacy,
398
+ ...rest
399
+ } = node as Record<string, unknown>;
400
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
401
+ return { ...rest, cachedStatus };
402
+ }
403
+ return rest;
404
+ }
405
+
406
+ function hasInlineMeshTransientNodeState(node: any): boolean {
407
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
408
+ return 'cachedStatus' in node
409
+ || 'lastGit' in node
410
+ || 'last_git' in node
411
+ || 'lastProbe' in node
412
+ || 'last_probe' in node
413
+ || 'error' in node
414
+ || 'health' in node
415
+ || 'machineStatus' in node
416
+ || 'lastSeenAt' in node
417
+ || 'last_seen_at' in node
418
+ || 'updatedAt' in node
419
+ || 'updated_at' in node
420
+ || 'activeSession' in node
421
+ || 'active_session' in node
422
+ || 'activeSessionId' in node
423
+ || 'active_session_id' in node
424
+ || 'sessionId' in node
425
+ || 'session_id' in node
426
+ || 'providerType' in node
427
+ || 'provider_type' in node;
428
+ }
429
+
430
+ function inlineMeshCarriesTransientNodeTruth(inlineMesh: any): boolean {
431
+ if (!inlineMesh || typeof inlineMesh !== 'object' || Array.isArray(inlineMesh)) return false;
432
+ if (!Array.isArray(inlineMesh.nodes) || inlineMesh.nodes.length === 0) return false;
433
+ return inlineMesh.nodes.some((node: any) => hasInlineMeshTransientNodeState(node));
434
+ }
435
+
436
+ function readInlineMeshNodeId(node: any): string {
437
+ return readStringValue(node?.id, node?.nodeId) || '';
438
+ }
439
+
440
+ function sanitizeInlineMesh(inlineMesh: any): any {
441
+ if (!inlineMesh || typeof inlineMesh !== 'object' || Array.isArray(inlineMesh)) return inlineMesh;
442
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
443
+ let changed = false;
444
+ const nodes = inlineMesh.nodes.map((node: any) => {
445
+ if (!hasInlineMeshTransientNodeState(node)) return node;
446
+ changed = true;
447
+ return stripInlineMeshTransientNodeState(node);
448
+ });
449
+ if (!changed) return inlineMesh;
450
+ return {
451
+ ...inlineMesh,
452
+ nodes,
453
+ };
454
+ }
455
+
456
+ function reconcileInlineMeshCache(cached: any, incoming: any): any {
457
+ if (!cached || typeof cached !== 'object' || Array.isArray(cached)) return incoming;
458
+ if (!incoming || typeof incoming !== 'object' || Array.isArray(incoming)) return cached;
459
+ const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
460
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
461
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
462
+
463
+ const cachedUpdatedAt = Date.parse(readStringValue(cached.updatedAt, cached.updated_at) || '');
464
+ const incomingUpdatedAt = Date.parse(readStringValue(incoming.updatedAt, incoming.updated_at) || '');
465
+ const preserveCachedMembership = Number.isFinite(cachedUpdatedAt)
466
+ && (!Number.isFinite(incomingUpdatedAt) || cachedUpdatedAt > incomingUpdatedAt);
467
+
468
+ const cachedById = new Map<string, any>();
469
+ for (const node of cachedNodes) {
470
+ const nodeId = readInlineMeshNodeId(node);
471
+ if (nodeId) cachedById.set(nodeId, node);
472
+ }
473
+
474
+ const nodes = incomingNodes.map((incomingNode: any) => {
475
+ const nodeId = readInlineMeshNodeId(incomingNode);
476
+ const cachedNode = nodeId ? cachedById.get(nodeId) : undefined;
477
+ if (!cachedNode && preserveCachedMembership) return null;
478
+ if (!cachedNode) return incomingNode;
479
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
480
+ return { ...cachedNode, ...incomingNode };
481
+ }
482
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
483
+ }).filter(Boolean);
484
+
485
+ return {
486
+ ...cached,
487
+ ...incoming,
488
+ nodes,
489
+ };
490
+ }
491
+
492
+ function hasGitWorktreeChanges(git: Record<string, unknown> | null | undefined): boolean {
493
+ if (!git) return false;
494
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
495
+ }
496
+
497
+ function getGitSubmoduleDriftState(git: Record<string, unknown> | null | undefined): { dirty: boolean; outOfSync: boolean } {
498
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
499
+ let dirty = false;
500
+ let outOfSync = false;
501
+ for (const entry of submodules) {
502
+ const submodule = readObjectRecord(entry);
503
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
504
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
505
+ }
506
+ return { dirty, outOfSync };
507
+ }
508
+
509
+ function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
510
+ if (!git || readBooleanValue(git.isGitRepo) === false) return 'degraded';
511
+ const branch = readStringValue(git.branch);
512
+ if (!branch) return 'degraded';
513
+ const submoduleDrift = getGitSubmoduleDriftState(git);
514
+ if (submoduleDrift.outOfSync) return 'degraded';
515
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return 'dirty';
516
+ return 'online';
517
+ }
518
+
519
+ function readCachedInlineMeshActiveSessions(node: any): string[] {
520
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
521
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
522
+ const fallbackSession = Object.keys(activeSession).length
523
+ ? activeSession
524
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
525
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
526
+ return sessionId ? [sessionId] : [];
527
+ }
528
+
529
+ function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>> {
226
530
  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);
531
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
532
+ const fallbackSession = Object.keys(activeSession).length
533
+ ? activeSession
534
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
535
+ const sessionId = readStringValue(
536
+ fallbackSession.id,
537
+ fallbackSession.sessionId,
538
+ fallbackSession.session_id,
539
+ node?.activeSessionId,
540
+ node?.active_session_id,
541
+ node?.sessionId,
542
+ node?.session_id,
543
+ );
544
+ if (!sessionId) return [];
545
+ return [{
546
+ sessionId,
547
+ providerType: readStringValue(
548
+ fallbackSession.providerType,
549
+ fallbackSession.provider_type,
550
+ fallbackSession.cliType,
551
+ fallbackSession.cli_type,
552
+ fallbackSession.provider,
553
+ node?.providerType,
554
+ node?.provider_type,
555
+ ),
556
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
557
+ lifecycle: readStringValue(fallbackSession.lifecycle),
558
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
559
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
560
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
561
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
562
+ isCached: true,
563
+ }];
564
+ }
565
+
566
+ function readLiveMeshSessionState(record: any): string | undefined {
567
+ return readStringValue(
568
+ record?.meta?.sessionStatus,
569
+ record?.meta?.status,
570
+ record?.meta?.providerStatus,
571
+ record?.status,
572
+ record?.state,
573
+ record?.lifecycle,
574
+ );
575
+ }
576
+
577
+ function toIsoTimestamp(value: unknown): string | null {
578
+ if (typeof value === 'number' && Number.isFinite(value)) return new Date(value).toISOString();
579
+ const stringValue = readStringValue(value);
580
+ return stringValue || null;
581
+ }
582
+
583
+ function synthesizeMeshNodeFreshnessFromConnection(status: Record<string, unknown>): void {
584
+ const connection = readObjectRecord(status.connection);
585
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
586
+ const git = readObjectRecord(status.git);
587
+ const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
588
+ if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
589
+ if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
590
+ status.updatedAt = gitCheckedAt ?? connectionFreshAt;
591
+ }
592
+ }
593
+
594
+ function finalizeMeshNodeStatus(args: {
595
+ status: Record<string, unknown>;
596
+ node: any;
597
+ daemonId?: string;
598
+ isSelfNode: boolean;
599
+ }): void {
600
+ const { status, node, daemonId, isSelfNode } = args;
601
+ if (!readStringValue(status.machineStatus)) {
602
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
603
+ const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
604
+ if (machineStatus) status.machineStatus = machineStatus;
605
+ }
606
+ synthesizeMeshNodeFreshnessFromConnection(status);
607
+ const connectionState = readStringValue(readObjectRecord(status.connection).state);
608
+ status.launchReady = !!daemonId && (
609
+ readStringValue(status.machineStatus) === 'online'
610
+ || connectionState === 'connected'
611
+ || isSelfNode
612
+ );
613
+ }
614
+
615
+ async function probeRemoteMeshGitStatus(args: {
616
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
617
+ daemonId: string;
618
+ workspace: string;
619
+ timeoutMs: number;
620
+ }): Promise<Record<string, unknown> | null> {
621
+ if (!args.dispatchMeshCommand) return null;
622
+ const remoteResult = await Promise.race([
623
+ args.dispatchMeshCommand(args.daemonId, 'git_status', { workspace: args.workspace }),
624
+ new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), args.timeoutMs)),
625
+ ]) as any;
626
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
627
+ return remoteGit && typeof remoteGit === 'object' && typeof remoteGit.isGitRepo === 'boolean'
628
+ ? remoteGit as Record<string, unknown>
629
+ : null;
630
+ }
631
+
632
+ async function hydrateInlineMeshDirectTruth(args: {
633
+ mesh: any;
634
+ meshSource: 'inline_cache' | 'inline_bootstrap' | 'local_config';
635
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
636
+ statusInstanceId?: string;
637
+ localMachineId?: string;
638
+ }): Promise<{
639
+ directEvidenceCount: number;
640
+ localConfirmedCount: number;
641
+ peerAttemptedCount: number;
642
+ peerConfirmedCount: number;
643
+ unavailableNodeIds: string[];
644
+ }> {
645
+ const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
646
+ if (!nodes.length) {
647
+ return {
648
+ directEvidenceCount: 0,
649
+ localConfirmedCount: 0,
650
+ peerAttemptedCount: 0,
651
+ peerConfirmedCount: 0,
652
+ unavailableNodeIds: [],
653
+ };
654
+ }
655
+
656
+ const selectedCoordinatorNodeId = readStringValue(
657
+ args.mesh?.coordinator?.preferredNodeId,
658
+ nodes[0]?.id,
659
+ nodes[0]?.nodeId,
660
+ );
661
+
662
+ let localConfirmedCount = 0;
663
+ let peerAttemptedCount = 0;
664
+ let peerConfirmedCount = 0;
665
+ const unavailableNodeIds: string[] = [];
666
+
667
+ for (const [nodeIndex, node] of nodes.entries()) {
668
+ const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
669
+ const workspace = readStringValue(node?.workspace);
670
+ const daemonId = readStringValue(node?.daemonId);
671
+ const isSelfNode = Boolean(
672
+ nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
673
+ ) || Boolean(
674
+ daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId),
675
+ ) || Boolean(args.meshSource !== 'local_config' && nodeIndex === 0);
676
+
677
+ if (!workspace) {
678
+ if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
679
+ continue;
680
+ }
681
+
682
+ if (fs.existsSync(workspace)) {
683
+ try {
684
+ const localGit = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
685
+ if (localGit?.isGitRepo) {
686
+ recordInlineMeshDirectGitTruth(node, localGit as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
687
+ localConfirmedCount += 1;
688
+ continue;
689
+ }
690
+ } catch {
691
+ // Fall through to remote classification.
692
+ }
693
+ }
694
+
695
+ if (!daemonId || !args.dispatchMeshCommand) {
696
+ if (!isSelfNode) unavailableNodeIds.push(nodeId);
697
+ continue;
698
+ }
699
+
700
+ peerAttemptedCount += 1;
701
+ try {
702
+ const remoteGit = await probeRemoteMeshGitStatus({
703
+ dispatchMeshCommand: args.dispatchMeshCommand,
704
+ daemonId,
705
+ workspace,
706
+ timeoutMs: 8_000,
707
+ });
708
+ if (remoteGit) {
709
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
710
+ peerConfirmedCount += 1;
711
+ continue;
712
+ }
713
+ } catch {
714
+ // Strict direct-only path: do not fall back to persisted cloud truth here.
715
+ }
716
+
717
+ unavailableNodeIds.push(nodeId);
718
+ }
719
+
720
+ return {
721
+ directEvidenceCount: localConfirmedCount + peerConfirmedCount,
722
+ localConfirmedCount,
723
+ peerAttemptedCount,
724
+ peerConfirmedCount,
725
+ unavailableNodeIds,
726
+ };
727
+ }
728
+
729
+ function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
730
+ return {
731
+ sessionId: readStringValue(record?.sessionId) || 'unknown',
732
+ providerType: readStringValue(record?.providerType),
733
+ state: readLiveMeshSessionState(record),
734
+ lifecycle: readStringValue(record?.lifecycle),
735
+ surfaceKind: getSessionHostSurfaceKind(record as any),
736
+ recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
737
+ workspace: readStringValue(record?.workspace) ?? null,
738
+ title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
739
+ lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
740
+ isCached: false,
741
+ };
742
+ }
743
+
744
+ function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string): boolean {
745
+ const recordNodeId = readStringValue(record?.meta?.meshNodeId);
746
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
747
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
748
+ return !recordMeshId || recordMeshId === meshId;
749
+ }
750
+
751
+ function liveSessionRecordMatchesMeshWorkspace(record: any, meshId: string, workspace: string): boolean {
752
+ const recordWorkspace = readStringValue(record?.workspace);
753
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
754
+
755
+ const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
756
+ if (recordMeshId) return recordMeshId === meshId;
757
+
758
+ return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
759
+ }
760
+
761
+ function readLiveMeshNodeWorkspace(args: {
762
+ meshId: string;
763
+ nodeId: string;
764
+ liveSessionRecords: any[];
765
+ allowCoordinatorSession?: boolean;
766
+ }): string {
767
+ const directNodeWorkspace = args.liveSessionRecords.find((record) => (
768
+ liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)
769
+ && readStringValue(record?.workspace)
770
+ ));
771
+ if (directNodeWorkspace) {
772
+ return readStringValue(directNodeWorkspace.workspace) || '';
773
+ }
774
+
775
+ if (args.allowCoordinatorSession) {
776
+ const coordinatorWorkspace = args.liveSessionRecords.find((record) => (
777
+ readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId
778
+ && readStringValue(record?.workspace)
779
+ ));
780
+ if (coordinatorWorkspace) {
781
+ return readStringValue(coordinatorWorkspace.workspace) || '';
782
+ }
783
+ }
784
+
785
+ return '';
786
+ }
787
+
788
+ function collectLiveMeshSessionRecords(args: {
789
+ meshId: string;
790
+ node: any;
791
+ nodeId: string;
792
+ liveSessionRecords: any[];
793
+ allowCoordinatorSession?: boolean;
794
+ }): any[] {
795
+ const matches = args.liveSessionRecords.filter((record) => {
796
+ const nodeWorkspace = readStringValue(args.node?.workspace);
797
+ if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)) return true;
798
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
799
+ });
800
+
801
+ if (args.allowCoordinatorSession) {
802
+ for (const record of args.liveSessionRecords) {
803
+ if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
804
+ const sessionId = readStringValue(record?.sessionId);
805
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
806
+ matches.push(record);
807
+ }
808
+ }
809
+
810
+ return matches;
811
+ }
812
+
813
+ function applyCachedInlineMeshNodeStatus(
814
+ status: Record<string, unknown>,
815
+ node: any,
816
+ options?: { skipGit?: boolean; skipError?: boolean; skipHealth?: boolean },
817
+ ): boolean {
818
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
819
+ const liveGit = buildInlineMeshTransitGitStatus(node);
820
+ const git = options?.skipGit ? undefined : (liveGit ?? buildCachedInlineMeshGitStatus(node));
821
+ const error = options?.skipError ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.error, node?.error));
822
+ const health = options?.skipHealth ? undefined : (liveGit ? undefined : readStringValue(cachedStatus.health, node?.health));
230
823
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
231
- if (!git && !error && !health) return false;
232
- if (!machineStatus && !git && !error) return false;
824
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
825
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
826
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
827
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
828
+ if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
233
829
  if (git) status.git = git;
234
830
  if (error) status.error = error;
831
+ if (machineStatus) status.machineStatus = machineStatus;
832
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
833
+ if (updatedAt) status.updatedAt = updatedAt;
834
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
835
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
235
836
  if (health) {
236
837
  status.health = health;
237
838
  return true;
238
839
  }
239
840
  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';
841
+ status.health = deriveMeshNodeHealthFromGit(git);
242
842
  return true;
243
843
  }
244
- return false;
844
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
245
845
  }
246
846
 
247
847
  async function resolveProviderTypeFromPriority(args: {
@@ -276,13 +876,7 @@ async function resolveProviderTypeFromPriority(args: {
276
876
  }
277
877
  type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
278
878
  type MeshRefineValidationStatus = 'passed' | 'failed' | 'skipped';
279
- type MeshRefineValidationCommand = {
280
- command: string;
281
- args: string[];
282
- displayCommand: string;
283
- category: string;
284
- source: string;
285
- };
879
+ type MeshRefineValidationCommand = MeshRefineValidationCommandPlan;
286
880
 
287
881
  type MeshRefineValidationSummary = {
288
882
  status: MeshRefineValidationStatus;
@@ -292,13 +886,65 @@ type MeshRefineValidationSummary = {
292
886
  skippedReason?: string;
293
887
  timeoutMs: number;
294
888
  outputLimitBytes: number;
889
+ configSource?: string;
890
+ configSourceType?: string;
891
+ suggestions?: unknown[];
892
+ suggestedConfig?: unknown;
893
+ };
894
+
895
+ type MeshRefineStageStatus = 'passed' | 'failed' | 'skipped';
896
+
897
+ type MeshRefinePatchEquivalenceSummary = {
898
+ status: MeshRefineStageStatus;
899
+ equivalent: boolean;
900
+ baseHead: string;
901
+ branchHead: string;
902
+ mergeBase?: string;
903
+ mergedTree?: string;
904
+ expectedPatchId?: string;
905
+ actualPatchId?: string;
906
+ durationMs: number;
907
+ error?: string;
908
+ stdout?: string;
909
+ stderr?: string;
910
+ };
911
+
912
+ type MeshRefineAsyncJobStatus = 'accepted' | 'completed' | 'failed';
913
+
914
+ type MeshRefineJobHandle = {
915
+ success: true;
916
+ async: true;
917
+ status: MeshRefineAsyncJobStatus;
918
+ jobId: string;
919
+ interactionId: string;
920
+ meshId: string;
921
+ nodeId: string;
922
+ targetNodeId: string;
923
+ targetDaemonId?: string;
924
+ workspace?: string;
925
+ startedAt: string;
926
+ completedAt?: string;
927
+ duplicate?: boolean;
928
+ retryOfJobId?: string;
929
+ eventDelivery: {
930
+ pendingEvents: true;
931
+ ledger: true;
932
+ };
933
+ evidence: {
934
+ pendingEventsCommand: 'get_pending_mesh_events';
935
+ ledgerCommand: 'get_mesh_ledger_slice';
936
+ taskHistoryKind: 'task_dispatched' | 'task_completed' | 'task_failed';
937
+ };
295
938
  };
296
939
 
940
+ type MeshRefineTerminalJob = MeshRefineJobHandle & { result?: Record<string, unknown> };
941
+
297
942
  const REFINE_VALIDATION_CATEGORIES = ['typecheck', 'test', 'lint', 'build'] as const;
298
943
  const REFINE_VALIDATION_TIMEOUT_MS = 120_000;
299
944
  const REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
300
945
  const REFINE_VALIDATION_SUMMARY_CHARS = 2_000;
301
946
  const REFINE_VALIDATION_MAX_COMMANDS = 4;
947
+ const REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
302
948
 
303
949
  function truncateValidationOutput(value: unknown): string {
304
950
  const text = typeof value === 'string' ? value : value == null ? '' : String(value);
@@ -306,171 +952,114 @@ function truncateValidationOutput(value: unknown): string {
306
952
  return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}\n[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
307
953
  }
308
954
 
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);
955
+ function recordMeshRefineStage(
956
+ stages: Array<Record<string, unknown>>,
957
+ stage: string,
958
+ status: MeshRefineStageStatus,
959
+ startedAt: number,
960
+ details?: Record<string, unknown>,
961
+ ): void {
962
+ stages.push({
963
+ stage,
964
+ status,
965
+ durationMs: Date.now() - startedAt,
966
+ ...(details || {}),
407
967
  });
408
968
  }
409
969
 
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);
970
+ async function computeGitPatchId(cwd: string, fromRef: string, toRef: string): Promise<string> {
971
+ const { execFileSync } = await import('node:child_process');
972
+ const diff = execFileSync('git', ['diff', '--patch', '--full-index', fromRef, toRef], {
973
+ cwd,
974
+ encoding: 'utf8',
975
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
976
+ });
977
+ if (!diff.trim()) return '';
978
+ const patchId = execFileSync('git', ['patch-id', '--stable'], {
979
+ cwd,
980
+ input: diff,
981
+ encoding: 'utf8',
982
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
983
+ }).trim();
984
+ return patchId.split(/\s+/)[0] || '';
426
985
  }
427
986
 
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;
987
+ async function runMeshRefinePatchEquivalenceGate(
988
+ repoRoot: string,
989
+ baseHead: string,
990
+ branchHead: string,
991
+ ): Promise<MeshRefinePatchEquivalenceSummary> {
992
+ const startedAt = Date.now();
993
+ try {
994
+ const { execFileSync } = await import('node:child_process');
995
+ const git = (args: string[]) => execFileSync('git', args, {
996
+ cwd: repoRoot,
997
+ encoding: 'utf8',
998
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
999
+ });
1000
+ const mergeBase = git(['merge-base', baseHead, branchHead]).trim();
1001
+ const mergeTreeStdout = git(['merge-tree', '--write-tree', baseHead, branchHead]);
1002
+ const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || '';
1003
+ if (!mergeBase || !mergedTree) {
1004
+ return {
1005
+ status: 'failed',
1006
+ equivalent: false,
1007
+ baseHead,
1008
+ branchHead,
1009
+ mergeBase: mergeBase || undefined,
1010
+ mergedTree: mergedTree || undefined,
1011
+ durationMs: Date.now() - startedAt,
1012
+ error: 'patch equivalence preflight could not resolve merge-base or synthetic merge tree',
1013
+ stdout: truncateValidationOutput(mergeTreeStdout),
1014
+ };
461
1015
  }
1016
+ const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
1017
+ const actualPatchId = await computeGitPatchId(repoRoot, baseHead, mergedTree);
1018
+ const equivalent = expectedPatchId === actualPatchId;
1019
+ return {
1020
+ status: equivalent ? 'passed' : 'failed',
1021
+ equivalent,
1022
+ baseHead,
1023
+ branchHead,
1024
+ mergeBase,
1025
+ mergedTree,
1026
+ expectedPatchId,
1027
+ actualPatchId,
1028
+ durationMs: Date.now() - startedAt,
1029
+ };
1030
+ } catch (e: any) {
1031
+ return {
1032
+ status: 'failed',
1033
+ equivalent: false,
1034
+ baseHead,
1035
+ branchHead,
1036
+ durationMs: Date.now() - startedAt,
1037
+ error: e?.message || String(e),
1038
+ stdout: truncateValidationOutput(e?.stdout),
1039
+ stderr: truncateValidationOutput(e?.stderr),
1040
+ };
462
1041
  }
1042
+ }
463
1043
 
1044
+ function buildMeshRefineValidationPlan(mesh: any, workspace: string): Record<string, unknown> {
1045
+ const plan = resolveMeshRefineValidationPlan(mesh, workspace);
464
1046
  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',
1047
+ source: plan.source,
1048
+ sourceType: plan.sourceType,
1049
+ commands: plan.commands.map(command => ({
1050
+ displayCommand: command.displayCommand,
1051
+ category: command.category,
1052
+ source: command.source,
1053
+ cwd: command.cwd,
1054
+ timeoutMs: command.timeoutMs,
1055
+ })),
1056
+ unavailableReason: plan.unavailableReason,
1057
+ rejectedCommands: plan.rejectedCommands,
1058
+ suggestions: plan.suggestions,
1059
+ suggestedConfig: plan.suggestedConfig,
1060
+ note: plan.sourceType === 'unavailable'
1061
+ ? 'No validation command will be executed until a repo mesh/refine config is provided. Heuristics are suggestions only.'
1062
+ : 'Validation commands are resolved from repo mesh/refine config; heuristics are suggestions only.',
474
1063
  };
475
1064
  }
476
1065
 
@@ -478,7 +1067,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
478
1067
  const { execFile } = await import('node:child_process');
479
1068
  const { promisify } = await import('node:util');
480
1069
  const execFileAsync = promisify(execFile);
481
- const selection = selectMeshRefineValidationCommands(mesh, workspace);
1070
+ const selection = resolveMeshRefineValidationPlan(mesh, workspace);
482
1071
  const summary: MeshRefineValidationSummary = {
483
1072
  status: 'skipped',
484
1073
  required: true,
@@ -487,22 +1076,28 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
487
1076
  skippedReason: undefined,
488
1077
  timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
489
1078
  outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
1079
+ configSource: selection.source,
1080
+ configSourceType: selection.sourceType,
1081
+ suggestions: selection.suggestions,
1082
+ suggestedConfig: selection.suggestedConfig,
490
1083
  };
491
1084
 
492
1085
  if (!selection.commands.length) {
493
- summary.skippedReason = 'validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available';
1086
+ summary.skippedReason = selection.unavailableReason || 'validation_unavailable: repo mesh/refine config did not provide executable validation.commands';
494
1087
  return summary;
495
1088
  }
496
1089
 
497
1090
  for (const candidate of selection.commands) {
498
1091
  const startedAt = Date.now();
1092
+ const cwd = candidate.cwd ? pathResolve(workspace, candidate.cwd) : workspace;
1093
+ const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
499
1094
  try {
500
1095
  const result = await execFileAsync(candidate.command, candidate.args, {
501
- cwd: workspace,
1096
+ cwd,
502
1097
  encoding: 'utf8',
503
- timeout: REFINE_VALIDATION_TIMEOUT_MS,
1098
+ timeout,
504
1099
  maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
505
- env: { ...process.env, CI: process.env.CI || '1' },
1100
+ env: { ...process.env, CI: process.env.CI || '1', ...(candidate.env || {}) },
506
1101
  });
507
1102
  summary.commandsRun.push({
508
1103
  command: candidate.command,
@@ -510,6 +1105,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
510
1105
  displayCommand: candidate.displayCommand,
511
1106
  category: candidate.category,
512
1107
  source: candidate.source,
1108
+ cwd,
513
1109
  passed: true,
514
1110
  exitCode: 0,
515
1111
  durationMs: Date.now() - startedAt,
@@ -523,6 +1119,7 @@ async function runMeshRefineValidationGate(mesh: any, workspace: string): Promis
523
1119
  displayCommand: candidate.displayCommand,
524
1120
  category: candidate.category,
525
1121
  source: candidate.source,
1122
+ cwd,
526
1123
  passed: false,
527
1124
  exitCode: typeof error?.code === 'number' ? error.code : null,
528
1125
  signal: typeof error?.signal === 'string' ? error.signal : null,
@@ -661,6 +1258,10 @@ export interface CommandRouterDeps {
661
1258
  statusVersion?: string;
662
1259
  /** Session host control plane */
663
1260
  sessionHostControl?: SessionHostControlPlane | null;
1261
+ /** Selected-coordinator mesh peer telemetry surface for target daemons, when supported by the runtime. */
1262
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
1263
+ /** Dispatch a command to a remote mesh node via P2P/relay. Injected by cloud runtime; absent in standalone. */
1264
+ dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
664
1265
  }
665
1266
 
666
1267
  export interface CommandRouterResult {
@@ -772,42 +1373,259 @@ function summarizeSessionHostPruneResult(result: unknown): Record<string, unknow
772
1373
  };
773
1374
  }
774
1375
 
1376
+ function normalizeStandaloneHostCommandUrl(hostAddress: string): string {
1377
+ const raw = hostAddress.trim();
1378
+ if (!raw) throw new Error('hostAddress required');
1379
+ const url = new URL(raw.replace(/^ws:/, 'http:').replace(/^wss:/, 'https:'));
1380
+ url.pathname = '/api/v1/command';
1381
+ url.search = '';
1382
+ url.hash = '';
1383
+ return url.toString();
1384
+ }
1385
+
1386
+ function buildMemberJoinNode(mesh: any, args: any, fallbackDaemonId?: string): Record<string, unknown> | null {
1387
+ const requestedNodeId = typeof args?.memberNodeId === 'string' ? args.memberNodeId.trim() : '';
1388
+ const explicit = args?.memberNode && typeof args.memberNode === 'object' && !Array.isArray(args.memberNode)
1389
+ ? args.memberNode as Record<string, any>
1390
+ : null;
1391
+ const configured = Array.isArray(mesh?.nodes)
1392
+ ? (requestedNodeId
1393
+ ? mesh.nodes.find((node: any) => node?.id === requestedNodeId || node?.nodeId === requestedNodeId)
1394
+ : mesh.nodes[0])
1395
+ : null;
1396
+ const source = explicit || configured;
1397
+ const workspace = typeof source?.workspace === 'string' && source.workspace.trim()
1398
+ ? source.workspace.trim()
1399
+ : typeof args?.workspace === 'string' && args.workspace.trim()
1400
+ ? args.workspace.trim()
1401
+ : process.cwd();
1402
+ if (!workspace) return null;
1403
+ const nodeId = typeof source?.id === 'string' && source.id.trim()
1404
+ ? source.id.trim()
1405
+ : typeof source?.nodeId === 'string' && source.nodeId.trim()
1406
+ ? source.nodeId.trim()
1407
+ : undefined;
1408
+ return {
1409
+ ...(nodeId ? { id: nodeId } : {}),
1410
+ workspace,
1411
+ ...(typeof source?.repoRoot === 'string' && source.repoRoot.trim() ? { repoRoot: source.repoRoot.trim() } : {}),
1412
+ ...(typeof source?.daemonId === 'string' && source.daemonId.trim() ? { daemonId: source.daemonId.trim() } : fallbackDaemonId ? { daemonId: fallbackDaemonId } : {}),
1413
+ ...(typeof source?.machineId === 'string' && source.machineId.trim() ? { machineId: source.machineId.trim() } : {}),
1414
+ userOverrides: source?.userOverrides && typeof source.userOverrides === 'object' && !Array.isArray(source.userOverrides) ? source.userOverrides : {},
1415
+ policy: source?.policy && typeof source.policy === 'object' && !Array.isArray(source.policy) ? source.policy : {},
1416
+ role: 'member',
1417
+ };
1418
+ }
1419
+
775
1420
  export class DaemonCommandRouter {
776
1421
  private deps: CommandRouterDeps;
777
1422
  /** In-memory cache for cloud-originating meshes passed via inlineMesh.
778
1423
  * Allows the MCP server to query mesh data via get_mesh even when
779
1424
  * the mesh doesn't exist in the local meshes.json file. */
780
1425
  private inlineMeshCache = new Map<string, any>();
1426
+ /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
1427
+ private aggregateMeshStatusCache = new Map<string, { builtAt: number; snapshot: any }>();
1428
+ /** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests. */
1429
+ private runningRefineJobs = new Map<string, MeshRefineJobHandle>();
1430
+ /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
1431
+ private terminalRefineJobs = new Map<string, MeshRefineTerminalJob>();
781
1432
 
782
1433
  constructor(deps: CommandRouterDeps) {
783
1434
  this.deps = deps;
784
1435
  }
785
1436
 
1437
+ private cloneJsonValue<T>(value: T): T {
1438
+ if (typeof structuredClone === 'function') return structuredClone(value);
1439
+ return JSON.parse(JSON.stringify(value)) as T;
1440
+ }
1441
+
1442
+ private hydrateCachedAggregateMeshStatusFromInline(snapshot: any, mesh: any, options?: { requireDirectPeerTruth?: boolean }): any {
1443
+ if (!mesh || typeof mesh !== 'object' || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
1444
+ const inlineNodesById = new Map<string, any>();
1445
+ for (const node of mesh.nodes) {
1446
+ const nodeId = readInlineMeshNodeId(node);
1447
+ if (nodeId) inlineNodesById.set(nodeId, node);
1448
+ }
1449
+ if (!inlineNodesById.size) return snapshot;
1450
+
1451
+ let changed = false;
1452
+ const unavailableNodeIds = new Set<string>();
1453
+ const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
1454
+ const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
1455
+ for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
1456
+ const nodeId = readStringValue(entry);
1457
+ if (nodeId) unavailableNodeIds.add(nodeId);
1458
+ }
1459
+
1460
+ const nodes = snapshot.nodes.map((statusNode: any) => {
1461
+ const nodeId = readStringValue(statusNode?.nodeId, statusNode?.id);
1462
+ const inlineNode = nodeId ? inlineNodesById.get(nodeId) : undefined;
1463
+ if (!inlineNode) return statusNode;
1464
+ const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
1465
+ if (!liveGit) return statusNode;
1466
+ const nextStatus = { ...statusNode };
1467
+ nextStatus.git = liveGit;
1468
+ nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
1469
+ nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
1470
+ const connection = readObjectRecord(nextStatus.connection);
1471
+ const connectionState = readStringValue(connection.state);
1472
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
1473
+ if (!connectionReported || connectionState === 'unknown') {
1474
+ nextStatus.connection = buildLivePeerGitConnection(connection);
1475
+ }
1476
+ delete nextStatus.gitProbePending;
1477
+ const error = readStringValue(nextStatus.error);
1478
+ if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
1479
+ if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = 'online';
1480
+ if (nodeId) unavailableNodeIds.delete(nodeId);
1481
+ changed = true;
1482
+ return nextStatus;
1483
+ });
1484
+
1485
+ if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0)) return snapshot;
1486
+ const nextSourceOfTruth = {
1487
+ ...sourceOfTruth,
1488
+ ...(Object.keys(directPeerTruth).length ? {
1489
+ directPeerTruth: {
1490
+ ...directPeerTruth,
1491
+ satisfied: options?.requireDirectPeerTruth === true ? unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
1492
+ unavailableNodeIds: [...unavailableNodeIds],
1493
+ },
1494
+ ...(options?.requireDirectPeerTruth === true ? {
1495
+ coordinatorOwnsLiveTruth: unavailableNodeIds.size === 0,
1496
+ currentStatus: unavailableNodeIds.size === 0 ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
1497
+ } : {}),
1498
+ } : {}),
1499
+ };
1500
+ return {
1501
+ ...snapshot,
1502
+ ...(options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 ? {
1503
+ success: false,
1504
+ code: 'mesh_direct_peer_truth_unavailable',
1505
+ error: 'Selected coordinator could not confirm direct mesh truth for every remote node yet.',
1506
+ } : {}),
1507
+ sourceOfTruth: nextSourceOfTruth,
1508
+ nodes,
1509
+ };
1510
+ }
1511
+
1512
+ private getCachedAggregateMeshStatus(meshId: string, mesh?: any, options?: { requireDirectPeerTruth?: boolean }): any | null {
1513
+ const cached = this.aggregateMeshStatusCache.get(meshId);
1514
+ if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
1515
+ let snapshot = this.cloneJsonValue(cached.snapshot);
1516
+ snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
1517
+ if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
1518
+ const ageMs = Math.max(0, Date.now() - cached.builtAt);
1519
+ const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === 'object'
1520
+ ? snapshot.sourceOfTruth
1521
+ : {};
1522
+ snapshot.sourceOfTruth = {
1523
+ ...sourceOfTruth,
1524
+ aggregateSnapshot: {
1525
+ ...(sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === 'object'
1526
+ ? sourceOfTruth.aggregateSnapshot
1527
+ : {}),
1528
+ owner: 'coordinator_daemon_memory',
1529
+ cached: true,
1530
+ source: 'memory',
1531
+ refreshReason: 'memory_cache_hit',
1532
+ ageMs,
1533
+ cachedAt: new Date(cached.builtAt).toISOString(),
1534
+ returnedAt: new Date().toISOString(),
1535
+ },
1536
+ };
1537
+ return snapshot;
1538
+ }
1539
+
1540
+ private rememberAggregateMeshStatus(meshId: string, snapshot: any, refreshReason: string): any {
1541
+ if (!snapshot || typeof snapshot !== 'object' || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
1542
+ const builtAt = Date.now();
1543
+ const next = this.cloneJsonValue(snapshot);
1544
+ const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === 'object'
1545
+ ? next.sourceOfTruth
1546
+ : {};
1547
+ next.sourceOfTruth = {
1548
+ ...sourceOfTruth,
1549
+ aggregateSnapshot: {
1550
+ owner: 'coordinator_daemon_memory',
1551
+ cached: false,
1552
+ source: 'live_refresh',
1553
+ refreshReason,
1554
+ ageMs: 0,
1555
+ cachedAt: new Date(builtAt).toISOString(),
1556
+ returnedAt: new Date(builtAt).toISOString(),
1557
+ },
1558
+ };
1559
+ this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next) });
1560
+ return next;
1561
+ }
1562
+
786
1563
  public getCachedInlineMesh(meshId: string, inlineMesh?: unknown): any | undefined {
787
1564
  if (inlineMesh && typeof inlineMesh === 'object') {
788
- this.inlineMeshCache.set(meshId, inlineMesh as any);
789
- return inlineMesh as any;
1565
+ return this.warmInlineMeshCache(meshId, inlineMesh);
790
1566
  }
791
1567
  return this.inlineMeshCache.get(meshId);
792
1568
  }
793
1569
 
1570
+ private warmInlineMeshCache(meshId: string, inlineMesh?: unknown): any | undefined {
1571
+ if (!inlineMesh || typeof inlineMesh !== 'object') return undefined;
1572
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh as any);
1573
+ const cached = this.inlineMeshCache.get(meshId);
1574
+ if (cached) {
1575
+ const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
1576
+ this.inlineMeshCache.set(meshId, merged);
1577
+ return merged;
1578
+ }
1579
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh as any);
1580
+ return sanitizedInlineMesh as any;
1581
+ }
1582
+
794
1583
  private async getMeshForCommand(
795
1584
  meshId: string,
796
1585
  inlineMesh?: unknown,
797
1586
  options?: { preferInline?: boolean },
798
- ): Promise<{ mesh: any; inline: boolean } | null> {
1587
+ ): Promise<{ mesh: any; inline: boolean; source: 'inline_cache' | 'inline_bootstrap' | 'local_config' } | null> {
799
1588
  const preferInline = options?.preferInline === true;
800
1589
  if (preferInline) {
801
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
802
- if (cached) return { mesh: cached, inline: true };
1590
+ const cached = this.getCachedInlineMesh(meshId);
1591
+ if (cached) {
1592
+ if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
1593
+ const merged = reconcileInlineMeshCache(cached, inlineMesh as any);
1594
+ this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
1595
+ return { mesh: merged, inline: true, source: 'inline_cache' };
1596
+ }
1597
+ return { mesh: cached, inline: true, source: 'inline_cache' };
1598
+ }
1599
+ if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
1600
+ this.warmInlineMeshCache(meshId, inlineMesh);
1601
+ return { mesh: inlineMesh, inline: true, source: 'inline_bootstrap' };
1602
+ }
803
1603
  }
804
1604
  try {
805
1605
  const { getMesh } = await import('../config/mesh-config.js');
806
1606
  const mesh = getMesh(meshId);
807
- if (mesh) return { mesh, inline: false };
1607
+ if (mesh) return { mesh, inline: false, source: 'local_config' };
808
1608
  } catch { /* fall through to inline cache */ }
809
- const cached = this.getCachedInlineMesh(meshId, inlineMesh);
810
- return cached ? { mesh: cached, inline: true } : null;
1609
+ const cached = this.getCachedInlineMesh(meshId);
1610
+ if (cached) return { mesh: cached, inline: true, source: 'inline_cache' };
1611
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
1612
+ return warmedInline ? { mesh: warmedInline, inline: true, source: 'inline_bootstrap' } : null;
1613
+ }
1614
+
1615
+ private invalidateAggregateMeshStatus(meshId: string): void {
1616
+ this.aggregateMeshStatusCache.delete(meshId);
1617
+ }
1618
+
1619
+
1620
+ private async requireMeshHostMutationOwner(meshId: string, inlineMesh: unknown, operation: string): Promise<CommandRouterResult | null> {
1621
+ const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
1622
+ const mesh = meshRecord?.mesh;
1623
+ if (!mesh) return { success: false, error: 'Mesh not found' };
1624
+ const meshHost = resolveMeshHostStatus(mesh);
1625
+ if (!meshHost.canOwnCoordinator || !meshHost.canOwnQueue) {
1626
+ return { ...buildMeshHostRequiredFailure(mesh, operation), success: false, meshId };
1627
+ }
1628
+ return null;
811
1629
  }
812
1630
 
813
1631
  private updateInlineMeshNode(meshId: string, mesh: any, node: any): void {
@@ -817,6 +1635,7 @@ export class DaemonCommandRouter {
817
1635
  else mesh.nodes.push(node);
818
1636
  mesh.updatedAt = new Date().toISOString();
819
1637
  this.inlineMeshCache.set(meshId, mesh);
1638
+ this.invalidateAggregateMeshStatus(meshId);
820
1639
  }
821
1640
 
822
1641
  private removeInlineMeshNode(meshId: string, mesh: any, nodeId: string): boolean {
@@ -826,6 +1645,7 @@ export class DaemonCommandRouter {
826
1645
  mesh.nodes.splice(idx, 1);
827
1646
  mesh.updatedAt = new Date().toISOString();
828
1647
  this.inlineMeshCache.set(meshId, mesh);
1648
+ this.invalidateAggregateMeshStatus(meshId);
829
1649
  return true;
830
1650
  }
831
1651
 
@@ -1104,6 +1924,7 @@ export class DaemonCommandRouter {
1104
1924
  const deletedSessionIds: string[] = [];
1105
1925
  const skippedSessionIds: string[] = [];
1106
1926
  const skippedLiveSessionIds: string[] = [];
1927
+ const skippedCoordinatorSessionIds: string[] = [];
1107
1928
  const deleteUnsupportedSessionIds: string[] = [];
1108
1929
  const recordsRemainSessionIds: string[] = [];
1109
1930
  const errors: Array<{ sessionId: string; error: string }> = [];
@@ -1138,6 +1959,12 @@ export class DaemonCommandRouter {
1138
1959
  const completed = this.isCompletedHostedSession(record);
1139
1960
  const surfaceKind = getSessionHostSurfaceKind(record);
1140
1961
  const liveRuntime = surfaceKind === 'live_runtime';
1962
+ const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
1963
+ if (!hasExplicitSessionIds && coordinatorSession) {
1964
+ skippedSessionIds.push(sessionId);
1965
+ skippedCoordinatorSessionIds.push(sessionId);
1966
+ continue;
1967
+ }
1141
1968
  if (!hasExplicitSessionIds && liveRuntime) {
1142
1969
  skippedSessionIds.push(sessionId);
1143
1970
  skippedLiveSessionIds.push(sessionId);
@@ -1207,6 +2034,7 @@ export class DaemonCommandRouter {
1207
2034
  deletedSessionIds,
1208
2035
  skippedSessionIds,
1209
2036
  skippedLiveSessionIds,
2037
+ skippedCoordinatorSessionIds,
1210
2038
  ...(deleteUnsupported ? {
1211
2039
  deleteUnsupported: true,
1212
2040
  effectiveCleanup: args.mode === 'stop_and_delete'
@@ -1317,34 +2145,409 @@ export class DaemonCommandRouter {
1317
2145
  return daemonResult;
1318
2146
  }
1319
2147
 
1320
- // 2. Delegate to DaemonCommandHandler
1321
- const handlerResult = await this.deps.commandHandler.handle(cmd, normalizedArgs);
1322
- logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: handlerResult.success, durationMs: Date.now() - cmdStart });
1323
- recordDebugTrace({
1324
- interactionId,
1325
- category: 'command',
1326
- stage: 'completed',
1327
- level: handlerResult.success ? 'info' : 'warn',
1328
- payload: { cmd, source: logSource, success: handlerResult.success, durationMs: Date.now() - cmdStart },
1329
- });
2148
+ // 2. Delegate to DaemonCommandHandler
2149
+ const handlerResult = await this.deps.commandHandler.handle(cmd, normalizedArgs);
2150
+ logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: handlerResult.success, durationMs: Date.now() - cmdStart });
2151
+ recordDebugTrace({
2152
+ interactionId,
2153
+ category: 'command',
2154
+ stage: 'completed',
2155
+ level: handlerResult.success ? 'info' : 'warn',
2156
+ payload: { cmd, source: logSource, success: handlerResult.success, durationMs: Date.now() - cmdStart },
2157
+ });
2158
+
2159
+ // 3. Post-chat command callback
2160
+ if (CHAT_COMMANDS.includes(cmd) && this.deps.onPostChatCommand) {
2161
+ this.deps.onPostChatCommand();
2162
+ }
2163
+
2164
+ return handlerResult;
2165
+ } catch (e: any) {
2166
+ logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: false, error: e.message, durationMs: Date.now() - cmdStart });
2167
+ recordDebugTrace({
2168
+ interactionId,
2169
+ category: 'command',
2170
+ stage: 'failed',
2171
+ level: 'error',
2172
+ payload: { cmd, source: logSource, error: e?.message || String(e), durationMs: Date.now() - cmdStart },
2173
+ });
2174
+ throw e;
2175
+ }
2176
+ }
2177
+
2178
+
2179
+ private buildRefineJobKey(meshId: string, nodeId: string): string {
2180
+ return `${meshId}:${nodeId}`;
2181
+ }
2182
+
2183
+ private buildRefineJobHandle(args: {
2184
+ meshId: string;
2185
+ nodeId: string;
2186
+ node?: any;
2187
+ status?: MeshRefineAsyncJobStatus;
2188
+ startedAt?: string;
2189
+ completedAt?: string;
2190
+ jobId?: string;
2191
+ interactionId?: string;
2192
+ retryOfJobId?: string;
2193
+ }): MeshRefineJobHandle {
2194
+ return {
2195
+ success: true,
2196
+ async: true,
2197
+ status: args.status || 'accepted',
2198
+ jobId: args.jobId || `refine_${createInteractionId()}`,
2199
+ interactionId: args.interactionId || createInteractionId(),
2200
+ meshId: args.meshId,
2201
+ nodeId: args.nodeId,
2202
+ targetNodeId: args.nodeId,
2203
+ targetDaemonId: readStringValue(args.node?.daemonId),
2204
+ workspace: readStringValue(args.node?.workspace),
2205
+ startedAt: args.startedAt || new Date().toISOString(),
2206
+ ...(args.completedAt ? { completedAt: args.completedAt } : {}),
2207
+ ...(args.retryOfJobId ? { retryOfJobId: args.retryOfJobId } : {}),
2208
+ eventDelivery: { pendingEvents: true, ledger: true },
2209
+ evidence: {
2210
+ pendingEventsCommand: 'get_pending_mesh_events',
2211
+ ledgerCommand: 'get_mesh_ledger_slice',
2212
+ taskHistoryKind: args.status === 'completed' ? 'task_completed' : args.status === 'failed' ? 'task_failed' : 'task_dispatched',
2213
+ },
2214
+ };
2215
+ }
2216
+
2217
+ private queueRefineJobEvent(event: 'refine:accepted' | 'refine:completed' | 'refine:failed', handle: MeshRefineJobHandle, result?: Record<string, unknown>): void {
2218
+ queuePendingMeshCoordinatorEvent({
2219
+ event,
2220
+ meshId: handle.meshId,
2221
+ nodeLabel: handle.targetNodeId,
2222
+ nodeId: handle.targetNodeId,
2223
+ workspace: handle.workspace,
2224
+ metadataEvent: {
2225
+ source: 'refine_mesh_node_async_job',
2226
+ jobId: handle.jobId,
2227
+ interactionId: handle.interactionId,
2228
+ meshId: handle.meshId,
2229
+ nodeId: handle.targetNodeId,
2230
+ targetDaemonId: handle.targetDaemonId,
2231
+ workspace: handle.workspace,
2232
+ status: handle.status,
2233
+ startedAt: handle.startedAt,
2234
+ completedAt: handle.completedAt,
2235
+ retryOfJobId: handle.retryOfJobId,
2236
+ ...(result ? { result } : {}),
2237
+ },
2238
+ queuedAt: Date.now(),
2239
+ });
2240
+ }
2241
+
2242
+ private async appendRefineJobLedger(kind: 'task_dispatched' | 'task_completed' | 'task_failed', handle: MeshRefineJobHandle, result?: Record<string, unknown>): Promise<void> {
2243
+ try {
2244
+ const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
2245
+ appendLedgerEntry(handle.meshId, {
2246
+ kind,
2247
+ nodeId: handle.targetNodeId,
2248
+ payload: {
2249
+ source: 'refine_mesh_node_async_job',
2250
+ refineJob: {
2251
+ jobId: handle.jobId,
2252
+ interactionId: handle.interactionId,
2253
+ status: handle.status,
2254
+ meshId: handle.meshId,
2255
+ nodeId: handle.targetNodeId,
2256
+ targetDaemonId: handle.targetDaemonId,
2257
+ workspace: handle.workspace,
2258
+ startedAt: handle.startedAt,
2259
+ completedAt: handle.completedAt,
2260
+ retryOfJobId: handle.retryOfJobId,
2261
+ },
2262
+ async: true,
2263
+ retryOfJobId: handle.retryOfJobId,
2264
+ ...(result ? {
2265
+ success: result.success === true,
2266
+ result,
2267
+ finalBranchConvergenceState: result.finalBranchConvergenceState,
2268
+ } : {}),
2269
+ },
2270
+ });
2271
+ } catch (e: any) {
2272
+ LOG.warn('Mesh', `[Refinery] Failed to append async refine ledger entry: ${e?.message || e}`);
2273
+ }
2274
+ }
2275
+
2276
+ private async executeMeshRefineNodeSynchronously(meshId: string, nodeId: string, args: any): Promise<CommandRouterResult> {
2277
+ const refineStages: Array<Record<string, unknown>> = [];
2278
+ try {
2279
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2280
+ const mesh = meshRecord?.mesh;
2281
+ const node = mesh?.nodes?.find((n: any) => n.id === nodeId || n.nodeId === nodeId);
2282
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
2283
+
2284
+ if (!node.isLocalWorktree || !node.workspace) {
2285
+ return { success: false, error: `Refinery requires a local worktree node`, refineStages };
2286
+ }
2287
+
2288
+ const sourceNode = node.clonedFromNodeId
2289
+ ? mesh?.nodes.find((n: any) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId)
2290
+ : mesh?.nodes.find((n: any) => !n.isLocalWorktree);
2291
+ const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
2292
+ if (!repoRoot) return { success: false, error: 'Source node repoRoot not found', refineStages };
2293
+
2294
+ const { execFile } = await import('node:child_process');
2295
+ const { promisify } = await import('node:util');
2296
+ const execFileAsync = promisify(execFile);
2297
+
2298
+ const resolveStarted = Date.now();
2299
+ const { stdout: branchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: node.workspace, encoding: 'utf8' });
2300
+ const branch = branchStdout.trim();
2301
+ if (!branch) return { success: false, error: 'Could not determine branch of the worktree node', refineStages };
2302
+
2303
+ const { stdout: baseBranchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: repoRoot, encoding: 'utf8' });
2304
+ const baseBranch = baseBranchStdout.trim();
2305
+ const { stdout: baseHeadStdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot, encoding: 'utf8' });
2306
+ const { stdout: branchHeadStdout } = await execFileAsync('git', ['rev-parse', branch], { cwd: node.workspace, encoding: 'utf8' });
2307
+ const baseHead = baseHeadStdout.trim();
2308
+ const branchHead = branchHeadStdout.trim();
2309
+ recordMeshRefineStage(refineStages, 'resolve_refs', 'passed', resolveStarted, { branch, baseBranch, baseHead, branchHead });
2310
+
2311
+ const validationStarted = Date.now();
2312
+ const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
2313
+ recordMeshRefineStage(
2314
+ refineStages,
2315
+ 'validation',
2316
+ validationSummary.status === 'passed' ? 'passed' : validationSummary.status === 'failed' ? 'failed' : 'skipped',
2317
+ validationStarted,
2318
+ { validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length },
2319
+ );
2320
+ if (validationSummary.status === 'failed') {
2321
+ return {
2322
+ success: false,
2323
+ code: 'validation_failed',
2324
+ convergenceStatus: 'blocked_review',
2325
+ error: 'Refinery validation gate failed; merge/refine was not attempted.',
2326
+ branch,
2327
+ into: baseBranch,
2328
+ validationSummary,
2329
+ refineStages,
2330
+ finalBranchConvergenceState: {
2331
+ branch,
2332
+ baseBranch,
2333
+ merged: false,
2334
+ removed: false,
2335
+ validation: 'failed',
2336
+ status: 'blocked_review',
2337
+ },
2338
+ };
2339
+ }
2340
+ if (validationSummary.status === 'skipped') {
2341
+ return {
2342
+ success: false,
2343
+ code: 'validation_unavailable',
2344
+ convergenceStatus: 'blocked_review',
2345
+ error: 'Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.',
2346
+ branch,
2347
+ into: baseBranch,
2348
+ validationSummary,
2349
+ refineStages,
2350
+ finalBranchConvergenceState: {
2351
+ branch,
2352
+ baseBranch,
2353
+ merged: false,
2354
+ removed: false,
2355
+ validation: 'unavailable',
2356
+ status: 'blocked_review',
2357
+ },
2358
+ };
2359
+ }
2360
+
2361
+ const patchEquivalenceStarted = Date.now();
2362
+ const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
2363
+ recordMeshRefineStage(refineStages, 'patch_equivalence', patchEquivalence.status, patchEquivalenceStarted, {
2364
+ equivalent: patchEquivalence.equivalent,
2365
+ expectedPatchId: patchEquivalence.expectedPatchId,
2366
+ actualPatchId: patchEquivalence.actualPatchId,
2367
+ error: patchEquivalence.error,
2368
+ });
2369
+ if (!patchEquivalence.equivalent) {
2370
+ return {
2371
+ success: false,
2372
+ code: 'patch_equivalence_failed',
2373
+ convergenceStatus: 'blocked_review',
2374
+ error: 'Refinery patch-equivalence preflight failed; merge/refine was not attempted.',
2375
+ branch,
2376
+ into: baseBranch,
2377
+ validationSummary,
2378
+ patchEquivalence,
2379
+ refineStages,
2380
+ finalBranchConvergenceState: {
2381
+ branch,
2382
+ baseBranch,
2383
+ merged: false,
2384
+ removed: false,
2385
+ validation: 'passed',
2386
+ patchEquivalence: 'failed',
2387
+ status: 'blocked_review',
2388
+ },
2389
+ };
2390
+ }
2391
+
2392
+ let mergeResult: Record<string, unknown> | undefined;
2393
+ const mergeStarted = Date.now();
2394
+ try {
2395
+ const result = await execFileAsync('git', ['merge', '--no-ff', branch, '-m', `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: 'utf8' });
2396
+ mergeResult = {
2397
+ stdout: truncateValidationOutput(result.stdout),
2398
+ stderr: truncateValidationOutput(result.stderr),
2399
+ durationMs: Date.now() - mergeStarted,
2400
+ };
2401
+ recordMeshRefineStage(refineStages, 'merge', 'passed', mergeStarted, mergeResult);
2402
+ } catch (e: any) {
2403
+ recordMeshRefineStage(refineStages, 'merge', 'failed', mergeStarted, {
2404
+ error: e?.message || String(e),
2405
+ stdout: truncateValidationOutput(e?.stdout),
2406
+ stderr: truncateValidationOutput(e?.stderr),
2407
+ });
2408
+ return {
2409
+ success: false,
2410
+ error: `Merge failed (conflicts?): ${e.message}`,
2411
+ validationSummary,
2412
+ patchEquivalence,
2413
+ refineStages,
2414
+ finalBranchConvergenceState: {
2415
+ branch,
2416
+ baseBranch,
2417
+ merged: false,
2418
+ removed: false,
2419
+ validation: 'passed',
2420
+ patchEquivalence: 'passed',
2421
+ status: 'not_mergeable',
2422
+ },
2423
+ };
2424
+ }
2425
+
2426
+ const cleanupStarted = Date.now();
2427
+ const removeResult = await this.execute('remove_mesh_node', {
2428
+ meshId,
2429
+ nodeId,
2430
+ sessionCleanupMode: 'preserve',
2431
+ inlineMesh: args?.inlineMesh,
2432
+ });
2433
+ recordMeshRefineStage(refineStages, 'cleanup', removeResult?.success === false ? 'failed' : 'passed', cleanupStarted, {
2434
+ removed: removeResult?.removed,
2435
+ code: removeResult?.code,
2436
+ error: removeResult?.error,
2437
+ });
2438
+
2439
+ let ledgerError: string | undefined;
2440
+ const ledgerStarted = Date.now();
2441
+ try {
2442
+ const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
2443
+ appendLedgerEntry(meshId, {
2444
+ kind: 'node_removed',
2445
+ nodeId,
2446
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence },
2447
+ });
2448
+ recordMeshRefineStage(refineStages, 'ledger', 'passed', ledgerStarted);
2449
+ } catch (e: any) {
2450
+ ledgerError = e?.message || String(e);
2451
+ recordMeshRefineStage(refineStages, 'ledger', 'failed', ledgerStarted, { error: ledgerError });
2452
+ }
2453
+
2454
+ const finalBranchConvergenceState = {
2455
+ branch: baseBranch,
2456
+ mergedBranch: branch,
2457
+ baseBranch,
2458
+ merged: true,
2459
+ removed: removeResult?.success !== false,
2460
+ validation: 'passed',
2461
+ patchEquivalence: 'passed',
2462
+ status: removeResult?.success === false ? 'merged_cleanup_failed' : 'merged',
2463
+ };
1330
2464
 
1331
- // 3. Post-chat command callback
1332
- if (CHAT_COMMANDS.includes(cmd) && this.deps.onPostChatCommand) {
1333
- this.deps.onPostChatCommand();
2465
+ if (removeResult?.success === false) {
2466
+ return {
2467
+ success: false,
2468
+ code: 'cleanup_failed',
2469
+ error: 'Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.',
2470
+ merged: true,
2471
+ branch,
2472
+ into: baseBranch,
2473
+ removeResult,
2474
+ validationSummary,
2475
+ patchEquivalence,
2476
+ mergeResult,
2477
+ refineStages,
2478
+ ...(ledgerError ? { ledgerError } : {}),
2479
+ finalBranchConvergenceState,
2480
+ };
1334
2481
  }
1335
2482
 
1336
- return handlerResult;
2483
+ return {
2484
+ success: true,
2485
+ merged: true,
2486
+ branch,
2487
+ into: baseBranch,
2488
+ removeResult,
2489
+ validationSummary,
2490
+ patchEquivalence,
2491
+ mergeResult,
2492
+ refineStages,
2493
+ ...(ledgerError ? { ledgerError } : {}),
2494
+ finalBranchConvergenceState,
2495
+ };
1337
2496
  } catch (e: any) {
1338
- logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: false, error: e.message, durationMs: Date.now() - cmdStart });
1339
- recordDebugTrace({
1340
- interactionId,
1341
- category: 'command',
1342
- stage: 'failed',
1343
- level: 'error',
1344
- payload: { cmd, source: logSource, error: e?.message || String(e), durationMs: Date.now() - cmdStart },
1345
- });
1346
- throw e;
2497
+ return { success: false, error: e.message, refineStages };
2498
+ }
2499
+ }
2500
+
2501
+ private async finishMeshRefineJob(handle: MeshRefineJobHandle, args: any): Promise<void> {
2502
+ const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
2503
+ let result: Record<string, unknown>;
2504
+ try {
2505
+ result = await this.executeMeshRefineNodeSynchronously(handle.meshId, handle.targetNodeId, args) as Record<string, unknown>;
2506
+ } catch (e: any) {
2507
+ result = { success: false, error: e?.message || String(e) };
1347
2508
  }
2509
+ const completedAt = new Date().toISOString();
2510
+ const terminalHandle = this.buildRefineJobHandle({
2511
+ meshId: handle.meshId,
2512
+ nodeId: handle.targetNodeId,
2513
+ status: result.success === true ? 'completed' : 'failed',
2514
+ startedAt: handle.startedAt,
2515
+ completedAt,
2516
+ jobId: handle.jobId,
2517
+ interactionId: handle.interactionId,
2518
+ retryOfJobId: handle.retryOfJobId,
2519
+ node: { daemonId: handle.targetDaemonId, workspace: handle.workspace },
2520
+ });
2521
+ const terminal: MeshRefineTerminalJob = { ...terminalHandle, result };
2522
+ this.terminalRefineJobs.set(key, terminal);
2523
+ this.runningRefineJobs.delete(key);
2524
+ this.invalidateAggregateMeshStatus(handle.meshId);
2525
+ await this.appendRefineJobLedger(result.success === true ? 'task_completed' : 'task_failed', terminalHandle, result);
2526
+ this.queueRefineJobEvent(result.success === true ? 'refine:completed' : 'refine:failed', terminalHandle, result);
2527
+ }
2528
+
2529
+ private async startMeshRefineJob(meshId: string, nodeId: string, args: any): Promise<CommandRouterResult> {
2530
+ const key = this.buildRefineJobKey(meshId, nodeId);
2531
+ const running = this.runningRefineJobs.get(key);
2532
+ if (running) return { ...running, duplicate: true };
2533
+ const terminal = this.terminalRefineJobs.get(key);
2534
+
2535
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2536
+ const mesh = meshRecord?.mesh;
2537
+ const node = mesh?.nodes?.find((n: any) => n.id === nodeId || n.nodeId === nodeId);
2538
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
2539
+ if (!node.isLocalWorktree || !node.workspace) return { success: false, error: `Refinery requires a local worktree node` };
2540
+
2541
+ const handle = this.buildRefineJobHandle({ meshId, nodeId, node, retryOfJobId: terminal?.jobId });
2542
+ this.runningRefineJobs.set(key, handle);
2543
+ await this.appendRefineJobLedger('task_dispatched', handle);
2544
+ this.queueRefineJobEvent('refine:accepted', handle);
2545
+
2546
+ setImmediate(() => {
2547
+ void this.finishMeshRefineJob(handle, args);
2548
+ });
2549
+
2550
+ return handle;
1348
2551
  }
1349
2552
 
1350
2553
  // ─── Daemon-level command core ───────────────────
@@ -1361,7 +2564,8 @@ export class DaemonCommandRouter {
1361
2564
  }
1362
2565
 
1363
2566
  case 'get_pending_mesh_events': {
1364
- const events = drainPendingMeshCoordinatorEvents();
2567
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2568
+ const events = drainPendingMeshCoordinatorEvents(meshId || undefined);
1365
2569
  return { success: true, events };
1366
2570
  }
1367
2571
 
@@ -1966,15 +3170,44 @@ export class DaemonCommandRouter {
1966
3170
  case 'get_mesh': {
1967
3171
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
1968
3172
  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' };
3173
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
3174
+ if (!meshRecord?.mesh) return { success: false, error: 'Mesh not found' };
3175
+
3176
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
3177
+ const directTruth = await hydrateInlineMeshDirectTruth({
3178
+ mesh: meshRecord.mesh,
3179
+ meshSource: meshRecord.source,
3180
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
3181
+ statusInstanceId: this.deps.statusInstanceId,
3182
+ localMachineId: loadConfig().machineId || '',
3183
+ });
3184
+ const directTruthSatisfied = meshRecord.source !== 'inline_bootstrap' || directTruth.directEvidenceCount > 0;
3185
+ const sourceOfTruth = {
3186
+ membership: meshRecord.source === 'inline_cache'
3187
+ ? 'coordinator_inline_mesh_cache'
3188
+ : meshRecord.source === 'local_config'
3189
+ ? 'local_mesh_config'
3190
+ : 'inline_bootstrap_snapshot',
3191
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
3192
+ directPeerTruth: {
3193
+ required: requireDirectPeerTruth,
3194
+ satisfied: directTruthSatisfied,
3195
+ directEvidenceCount: directTruth.directEvidenceCount,
3196
+ localConfirmedCount: directTruth.localConfirmedCount,
3197
+ peerAttemptedCount: directTruth.peerAttemptedCount,
3198
+ peerConfirmedCount: directTruth.peerConfirmedCount,
3199
+ unavailableNodeIds: directTruth.unavailableNodeIds,
3200
+ },
3201
+ };
3202
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
3203
+ return {
3204
+ success: false,
3205
+ code: 'mesh_direct_peer_truth_unavailable',
3206
+ error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.',
3207
+ sourceOfTruth,
3208
+ };
3209
+ }
3210
+ return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
1978
3211
  }
1979
3212
 
1980
3213
  case 'create_mesh': {
@@ -1985,7 +3218,10 @@ export class DaemonCommandRouter {
1985
3218
  if (!name) return { success: false, error: 'name required' };
1986
3219
  try {
1987
3220
  const { createMesh } = await import('../config/mesh-config.js');
1988
- const mesh = createMesh({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy });
3221
+ const meshHost = args?.meshHost && typeof args.meshHost === 'object' && !Array.isArray(args.meshHost)
3222
+ ? args.meshHost
3223
+ : undefined;
3224
+ const mesh = createMesh({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
1989
3225
  return { success: true, mesh };
1990
3226
  } catch (e: any) {
1991
3227
  return { success: false, error: e.message };
@@ -2002,16 +3238,237 @@ export class DaemonCommandRouter {
2002
3238
  if (typeof args?.defaultBranch === 'string') patch.defaultBranch = args.defaultBranch;
2003
3239
  if (args?.policy && typeof args.policy === 'object' && !Array.isArray(args.policy)) patch.policy = args.policy;
2004
3240
  if (args?.coordinator && typeof args.coordinator === 'object' && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
3241
+ if (args?.meshHost && typeof args.meshHost === 'object' && !Array.isArray(args.meshHost)) patch.meshHost = args.meshHost;
2005
3242
  if (!Object.keys(patch).length) return { success: false, error: 'No updates provided' };
2006
3243
  const mesh = updateMesh(meshId, patch as any);
2007
3244
  if (!mesh) return { success: false, error: 'Mesh not found' };
2008
3245
  this.inlineMeshCache.set(meshId, mesh);
3246
+ this.invalidateAggregateMeshStatus(meshId);
2009
3247
  return { success: true, mesh };
2010
3248
  } catch (e: any) {
2011
3249
  return { success: false, error: e.message };
2012
3250
  }
2013
3251
  }
2014
3252
 
3253
+ case 'get_mesh_host_pairing': {
3254
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3255
+ if (!meshId) return { success: false, error: 'meshId required' };
3256
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
3257
+ const mesh = meshRecord?.mesh;
3258
+ if (!mesh) return { success: false, error: 'Mesh not found' };
3259
+ const meshHost = resolveMeshHostStatus(mesh);
3260
+ const pairingStatus = meshHost.pairing?.status || 'not_configured';
3261
+ return {
3262
+ success: true,
3263
+ code: pairingStatus === 'not_configured' ? 'mesh_host_pairing_not_configured' : 'mesh_host_pairing_pending',
3264
+ meshId,
3265
+ hostAddress: meshHost.hostAddress,
3266
+ meshHost,
3267
+ manualPairing: {
3268
+ status: pairingStatus,
3269
+ joinImplemented: true,
3270
+ protocol: 'standalone_command_direct_v1',
3271
+ 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.',
3272
+ },
3273
+ };
3274
+ }
3275
+
3276
+ case 'configure_mesh_host_pairing': {
3277
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3278
+ const hostAddress = typeof args?.hostAddress === 'string' ? args.hostAddress.trim() : '';
3279
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
3280
+ if (!meshId) return { success: false, error: 'meshId required' };
3281
+ if (!hostAddress || !token) return { success: false, error: 'hostAddress and token required' };
3282
+ try {
3283
+ const { configureMeshHostPairing } = await import('../config/mesh-config.js');
3284
+ const configured = configureMeshHostPairing(meshId, { hostAddress, token });
3285
+ if (!configured) return { success: false, error: 'Mesh not found' };
3286
+ this.inlineMeshCache.set(meshId, configured.mesh);
3287
+ const meshHost = resolveMeshHostStatus(configured.mesh);
3288
+ return {
3289
+ success: true,
3290
+ code: 'mesh_host_pairing_pending',
3291
+ meshId,
3292
+ hostAddress: configured.hostAddress,
3293
+ meshHost,
3294
+ manualPairing: {
3295
+ status: meshHost.pairing?.status || 'pairing',
3296
+ joinImplemented: true,
3297
+ protocol: 'standalone_command_direct_v1',
3298
+ 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.',
3299
+ },
3300
+ };
3301
+ } catch (e: any) {
3302
+ return { success: false, code: 'mesh_host_pairing_invalid', meshId, hostAddress, error: e.message };
3303
+ }
3304
+ }
3305
+
3306
+ case 'create_mesh_host_pairing_token': {
3307
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3308
+ if (!meshId) return { success: false, error: 'meshId required' };
3309
+ try {
3310
+ const { createMeshHostPairingToken } = await import('../config/mesh-config.js');
3311
+ const created = createMeshHostPairingToken(meshId, {
3312
+ token: typeof args?.token === 'string' ? args.token : undefined,
3313
+ expiresAt: typeof args?.expiresAt === 'string' ? args.expiresAt : undefined,
3314
+ });
3315
+ if (!created) return { success: false, error: 'Mesh not found' };
3316
+ this.inlineMeshCache.set(meshId, created.mesh);
3317
+ this.invalidateAggregateMeshStatus(meshId);
3318
+ return {
3319
+ success: true,
3320
+ code: 'mesh_host_pairing_token_created',
3321
+ meshId,
3322
+ token: created.token,
3323
+ tokenId: created.tokenId,
3324
+ expiresAt: created.expiresAt,
3325
+ meshHost: resolveMeshHostStatus(created.mesh),
3326
+ warning: 'Raw token is returned once and is not persisted; share it with member daemons over a trusted channel.',
3327
+ };
3328
+ } catch (e: any) {
3329
+ return { success: false, code: 'mesh_host_pairing_token_invalid', meshId, error: e.message };
3330
+ }
3331
+ }
3332
+
3333
+ case 'apply_mesh_host_join': {
3334
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3335
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
3336
+ const memberNode = args?.memberNode && typeof args.memberNode === 'object' && !Array.isArray(args.memberNode)
3337
+ ? args.memberNode
3338
+ : null;
3339
+ if (!meshId) return { success: false, error: 'meshId required' };
3340
+ if (!token || !memberNode) return { success: false, error: 'token and memberNode required' };
3341
+ try {
3342
+ const { applyMeshHostJoinRequest } = await import('../config/mesh-config.js');
3343
+ const applied = applyMeshHostJoinRequest(meshId, {
3344
+ token,
3345
+ memberNode: memberNode as any,
3346
+ memberMeshId: typeof args?.memberMeshId === 'string' ? args.memberMeshId : undefined,
3347
+ });
3348
+ if (!applied) return { success: false, error: 'Mesh not found' };
3349
+ if (!applied.accepted) {
3350
+ return {
3351
+ success: false,
3352
+ code: 'mesh_host_join_rejected',
3353
+ meshId,
3354
+ tokenId: applied.tokenId,
3355
+ meshHost: applied.meshHost ? resolveMeshHostStatus({ meshHost: applied.meshHost }) : undefined,
3356
+ error: applied.reason,
3357
+ };
3358
+ }
3359
+ this.inlineMeshCache.set(meshId, applied.mesh);
3360
+ this.invalidateAggregateMeshStatus(meshId);
3361
+ try {
3362
+ const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
3363
+ appendLedgerEntry(meshId, {
3364
+ kind: 'node_joined',
3365
+ nodeId: applied.node.id,
3366
+ payload: { role: 'member', tokenId: applied.tokenId, workspace: applied.node.workspace },
3367
+ });
3368
+ } catch { /* ledger append is best-effort */ }
3369
+ return {
3370
+ success: true,
3371
+ code: 'mesh_host_join_accepted',
3372
+ meshId,
3373
+ node: applied.node,
3374
+ tokenId: applied.tokenId,
3375
+ meshHost: resolveMeshHostStatus(applied.mesh),
3376
+ };
3377
+ } catch (e: any) {
3378
+ return { success: false, code: 'mesh_host_join_failed', meshId, error: e.message };
3379
+ }
3380
+ }
3381
+
3382
+ case 'join_mesh_host_pairing': {
3383
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3384
+ const token = typeof args?.token === 'string' ? args.token.trim() : '';
3385
+ if (!meshId) return { success: false, error: 'meshId required' };
3386
+ if (!token) return { success: false, error: 'token required because raw pairing tokens are not persisted' };
3387
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
3388
+ const mesh = meshRecord?.mesh;
3389
+ if (!mesh) return { success: false, error: 'Mesh not found' };
3390
+ const meshHost = resolveMeshHostStatus(mesh);
3391
+ if (meshHost.role !== 'member') {
3392
+ 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.' };
3393
+ }
3394
+ try {
3395
+ const { tokenIdForManualPairing, markMeshHostPairingJoined } = await import('../config/mesh-config.js');
3396
+ const tokenId = tokenIdForManualPairing(token);
3397
+ if (meshHost.pairing?.tokenId && meshHost.pairing.tokenId !== tokenId) {
3398
+ return { success: false, code: 'mesh_host_join_rejected', meshId, tokenId, meshHost, error: 'invalid pairing token' };
3399
+ }
3400
+ const memberNode = buildMemberJoinNode(mesh, args, this.deps.statusInstanceId);
3401
+ if (!memberNode) return { success: false, error: 'member node metadata unavailable' };
3402
+ const hostMeshId = typeof args?.hostMeshId === 'string' && args.hostMeshId.trim() ? args.hostMeshId.trim() : meshId;
3403
+ const hostDaemonId = typeof args?.hostDaemonId === 'string' && args.hostDaemonId.trim()
3404
+ ? args.hostDaemonId.trim()
3405
+ : meshHost.hostDaemonId;
3406
+ let hostResult: any;
3407
+ let transport: string;
3408
+ if (hostDaemonId && this.deps.dispatchMeshCommand) {
3409
+ transport = 'mesh_command_dispatch';
3410
+ hostResult = await this.deps.dispatchMeshCommand(hostDaemonId, 'apply_mesh_host_join', {
3411
+ meshId: hostMeshId,
3412
+ token,
3413
+ memberMeshId: meshId,
3414
+ memberNode,
3415
+ });
3416
+ } else if (meshHost.hostAddress) {
3417
+ transport = 'standalone_http_command';
3418
+ const commandUrl = normalizeStandaloneHostCommandUrl(meshHost.hostAddress);
3419
+ const response = await fetch(commandUrl, {
3420
+ method: 'POST',
3421
+ headers: { 'Content-Type': 'application/json' },
3422
+ body: JSON.stringify({ type: 'apply_mesh_host_join', payload: { meshId: hostMeshId, token, memberMeshId: meshId, memberNode } }),
3423
+ });
3424
+ hostResult = await response.json().catch(() => ({ success: false, error: `Host returned HTTP ${response.status}` }));
3425
+ if (!response.ok && hostResult?.success !== false) hostResult = { success: false, error: `Host returned HTTP ${response.status}` };
3426
+ } else {
3427
+ return {
3428
+ success: false,
3429
+ code: 'mesh_host_join_transport_unavailable',
3430
+ meshId,
3431
+ meshHost,
3432
+ error: 'No hostDaemonId dispatch path or hostAddress HTTP command path is available. P2P signaling join is not implemented in this slice.',
3433
+ };
3434
+ }
3435
+ if (!hostResult?.success) {
3436
+ return { success: false, code: hostResult?.code || 'mesh_host_join_rejected', meshId, meshHost, transport, error: hostResult?.error || 'Mesh Host rejected join request', hostResult };
3437
+ }
3438
+ const joined = meshRecord.inline
3439
+ ? null
3440
+ : markMeshHostPairingJoined(meshId, {
3441
+ tokenId: hostResult.tokenId || tokenId,
3442
+ hostDaemonId: hostResult.meshHost?.hostDaemonId || hostDaemonId,
3443
+ hostNodeId: hostResult.meshHost?.hostNodeId,
3444
+ joinedAt: hostResult.meshHost?.pairing?.joinedAt,
3445
+ });
3446
+ if (joined) {
3447
+ this.inlineMeshCache.set(meshId, joined.mesh);
3448
+ this.invalidateAggregateMeshStatus(meshId);
3449
+ }
3450
+ return {
3451
+ success: true,
3452
+ code: 'mesh_host_join_applied',
3453
+ meshId,
3454
+ hostMeshId,
3455
+ transport,
3456
+ node: hostResult.node,
3457
+ tokenId: hostResult.tokenId || tokenId,
3458
+ meshHost: joined ? resolveMeshHostStatus(joined.mesh) : { ...meshHost, pairing: { ...(meshHost.pairing || {}), status: 'paired', tokenId: hostResult.tokenId || tokenId } },
3459
+ hostResult,
3460
+ manualPairing: {
3461
+ status: 'paired',
3462
+ joinImplemented: true,
3463
+ protocol: 'standalone_command_direct_v1',
3464
+ description: 'Mesh Host accepted the join and local member pairing status was marked paired. P2P runtime signaling remains outside this slice.',
3465
+ },
3466
+ };
3467
+ } catch (e: any) {
3468
+ return { success: false, code: 'mesh_host_join_failed', meshId, meshHost, error: e.message };
3469
+ }
3470
+ }
3471
+
2015
3472
  case 'delete_mesh': {
2016
3473
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2017
3474
  if (!meshId) return { success: false, error: 'meshId required' };
@@ -2105,6 +3562,8 @@ export class DaemonCommandRouter {
2105
3562
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2106
3563
  const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
2107
3564
  if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
3565
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue cancellation');
3566
+ if (ownerFailure) return ownerFailure;
2108
3567
  try {
2109
3568
  const { cancelTask } = await import('../mesh/mesh-work-queue.js');
2110
3569
  const reason = typeof args?.reason === 'string' ? args.reason : undefined;
@@ -2120,6 +3579,8 @@ export class DaemonCommandRouter {
2120
3579
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2121
3580
  const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
2122
3581
  if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
3582
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue requeue');
3583
+ if (ownerFailure) return ownerFailure;
2123
3584
  try {
2124
3585
  const { requeueTask } = await import('../mesh/mesh-work-queue.js');
2125
3586
  const task = requeueTask(meshId, taskId, {
@@ -2141,6 +3602,8 @@ export class DaemonCommandRouter {
2141
3602
  const workspace = typeof args?.workspace === 'string' ? args.workspace.trim() : '';
2142
3603
  if (!meshId) return { success: false, error: 'meshId required' };
2143
3604
  if (!workspace) return { success: false, error: 'workspace required' };
3605
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node addition');
3606
+ if (ownerFailure) return ownerFailure;
2144
3607
  try {
2145
3608
  const { addNode } = await import('../config/mesh-config.js');
2146
3609
  const providerPriority = Array.isArray(args?.providerPriority)
@@ -2151,7 +3614,8 @@ export class DaemonCommandRouter {
2151
3614
  ...(readOnly ? { readOnly: true } : {}),
2152
3615
  ...(providerPriority.length ? { providerPriority } : {}),
2153
3616
  };
2154
- const node = addNode(meshId, { workspace, ...(policy ? { policy } : {}) });
3617
+ const role = normalizeMeshDaemonRole(args?.role);
3618
+ const node = addNode(meshId, { workspace, ...(policy ? { policy } : {}), ...(role ? { role } : {}) });
2155
3619
  if (!node) return { success: false, error: 'Mesh not found' };
2156
3620
  return { success: true, node };
2157
3621
  } catch (e: any) {
@@ -2163,6 +3627,8 @@ export class DaemonCommandRouter {
2163
3627
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2164
3628
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2165
3629
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3630
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node update');
3631
+ if (ownerFailure) return ownerFailure;
2166
3632
  try {
2167
3633
  const { updateNode } = await import('../config/mesh-config.js');
2168
3634
  const policy = args?.policy && typeof args.policy === 'object' && !Array.isArray(args.policy)
@@ -2191,6 +3657,8 @@ export class DaemonCommandRouter {
2191
3657
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2192
3658
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2193
3659
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3660
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'node removal');
3661
+ if (ownerFailure) return ownerFailure;
2194
3662
  try {
2195
3663
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2196
3664
  const mesh = meshRecord?.mesh;
@@ -2216,131 +3684,91 @@ export class DaemonCommandRouter {
2216
3684
  }
2217
3685
  }
2218
3686
 
2219
- case 'refine_mesh_node': {
3687
+ case 'get_mesh_refine_config_schema': {
3688
+ return {
3689
+ success: true,
3690
+ schema: MESH_REFINE_CONFIG_SCHEMA,
3691
+ locations: MESH_REFINE_CONFIG_LOCATIONS,
3692
+ sourceOfTruth: 'repo mesh/refine config',
3693
+ heuristicRole: 'suggestions_only_not_execution_path',
3694
+ };
3695
+ }
3696
+
3697
+ case 'validate_mesh_refine_config': {
3698
+ const workspace = typeof args?.workspace === 'string' ? args.workspace : process.cwd();
3699
+ const mesh = args?.inlineMesh || {};
3700
+ const loaded = args?.config !== undefined
3701
+ ? { config: args.config, source: 'inline', sourceType: 'mesh_policy' as const }
3702
+ : loadMeshRefineConfig(mesh, workspace);
3703
+ const validation = loaded.config
3704
+ ? validateMeshRefineConfig(loaded.config, loaded.source)
3705
+ : { valid: false, errors: [((loaded as { error?: string }).error) || 'repo mesh/refine config unavailable'], commands: [], rejectedCommands: [] };
3706
+ return { success: validation.valid, ...loaded, ...validation };
3707
+ }
3708
+
3709
+ case 'suggest_mesh_refine_config': {
3710
+ const workspace = typeof args?.workspace === 'string' ? args.workspace : process.cwd();
3711
+ const mesh = args?.inlineMesh || {};
3712
+ return {
3713
+ success: true,
3714
+ ...suggestMeshRefineConfig(mesh, workspace),
3715
+ note: 'Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config.',
3716
+ };
3717
+ }
3718
+
3719
+ case 'plan_mesh_refine_node': {
2220
3720
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2221
3721
  const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
2222
3722
  if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
2223
- try {
3723
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
3724
+ const mesh = meshRecord?.mesh;
3725
+ const node = mesh?.nodes?.find((n: any) => n.id === nodeId || n.nodeId === nodeId);
3726
+ if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
3727
+ return {
3728
+ success: true,
3729
+ dryRun: true,
3730
+ nodeId,
3731
+ workspace: node.workspace,
3732
+ validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
3733
+ mergeWillRun: false,
3734
+ cleanupWillRun: false,
3735
+ };
3736
+ }
3737
+
3738
+ case 'fast_forward_mesh_node': {
3739
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3740
+ const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
3741
+ let workspace = typeof args?.workspace === 'string' ? args.workspace.trim() : '';
3742
+ let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths)
3743
+ ? args.submoduleIgnorePaths.filter((value: unknown): value is string => typeof value === 'string')
3744
+ : undefined;
3745
+ if (!workspace && meshId && nodeId) {
2224
3746
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
2225
3747
  const mesh = meshRecord?.mesh;
2226
3748
  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` };
2228
-
2229
- if (!node.isLocalWorktree || !node.workspace) {
2230
- return { success: false, error: `Refinery requires a local worktree node` };
2231
- }
2232
-
2233
- const sourceNode = node.clonedFromNodeId
2234
- ? mesh?.nodes.find((n: any) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId)
2235
- : mesh?.nodes.find((n: any) => !n.isLocalWorktree);
2236
- const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
2237
- if (!repoRoot) return { success: false, error: 'Source node repoRoot not found' };
2238
-
2239
- const { execFile } = await import('node:child_process');
2240
- const { promisify } = await import('node:util');
2241
- const execFileAsync = promisify(execFile);
2242
-
2243
- const { stdout: branchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: node.workspace, encoding: 'utf8' });
2244
- const branch = branchStdout.trim();
2245
- if (!branch) return { success: false, error: 'Could not determine branch of the worktree node' };
2246
-
2247
- const { stdout: baseBranchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: repoRoot, encoding: 'utf8' });
2248
- const baseBranch = baseBranchStdout.trim();
2249
-
2250
- const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
2251
- if (validationSummary.status === 'failed') {
2252
- return {
2253
- success: false,
2254
- code: 'validation_failed',
2255
- convergenceStatus: 'blocked_review',
2256
- error: 'Refinery validation gate failed; merge/refine was not attempted.',
2257
- branch,
2258
- into: baseBranch,
2259
- validationSummary,
2260
- finalBranchConvergenceState: {
2261
- branch,
2262
- baseBranch,
2263
- merged: false,
2264
- removed: false,
2265
- validation: 'failed',
2266
- status: 'blocked_review',
2267
- },
2268
- };
2269
- }
2270
- if (validationSummary.status === 'skipped') {
2271
- return {
2272
- success: false,
2273
- code: 'validation_unavailable',
2274
- convergenceStatus: 'blocked_review',
2275
- error: 'Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.',
2276
- branch,
2277
- into: baseBranch,
2278
- validationSummary,
2279
- finalBranchConvergenceState: {
2280
- branch,
2281
- baseBranch,
2282
- merged: false,
2283
- removed: false,
2284
- validation: 'unavailable',
2285
- status: 'blocked_review',
2286
- },
2287
- };
2288
- }
2289
-
2290
- try {
2291
- await execFileAsync('git', ['merge', '--no-ff', branch, '-m', `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: 'utf8' });
2292
- } catch (e: any) {
2293
- return {
2294
- success: false,
2295
- error: `Merge failed (conflicts?): ${e.message}`,
2296
- validationSummary,
2297
- finalBranchConvergenceState: {
2298
- branch,
2299
- baseBranch,
2300
- merged: false,
2301
- removed: false,
2302
- validation: 'passed',
2303
- status: 'not_mergeable',
2304
- },
2305
- };
3749
+ workspace = typeof node?.workspace === 'string' ? node.workspace.trim() : '';
3750
+ if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
3751
+ submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value: unknown): value is string => typeof value === 'string');
2306
3752
  }
2307
-
2308
- const removeResult = await this.execute('remove_mesh_node', {
2309
- meshId,
2310
- nodeId,
2311
- sessionCleanupMode: 'kill',
2312
- inlineMesh: args?.inlineMesh,
2313
- });
2314
-
2315
- try {
2316
- const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
2317
- appendLedgerEntry(meshId, {
2318
- kind: 'node_removed',
2319
- nodeId,
2320
- payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary },
2321
- });
2322
- } catch {}
2323
-
2324
- return {
2325
- success: true,
2326
- merged: true,
2327
- branch,
2328
- into: baseBranch,
2329
- removeResult,
2330
- 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
- },
2340
- };
2341
- } catch (e: any) {
2342
- return { success: false, error: e.message };
2343
3753
  }
3754
+ const result = await (fastForwardMeshNode({
3755
+ meshId: meshId || undefined,
3756
+ nodeId: nodeId || undefined,
3757
+ workspace,
3758
+ branch: typeof args?.branch === 'string' ? args.branch : undefined,
3759
+ execute: args?.execute === true,
3760
+ dryRun: args?.dryRun === true,
3761
+ updateSubmodules: args?.updateSubmodules === true,
3762
+ submoduleIgnorePaths,
3763
+ }) as Promise<unknown>);
3764
+ return result as CommandRouterResult;
3765
+ }
3766
+
3767
+ case 'refine_mesh_node': {
3768
+ const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
3769
+ const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
3770
+ if (!meshId || !nodeId) return { success: false, error: 'meshId and nodeId required' };
3771
+ return this.startMeshRefineJob(meshId, nodeId, args);
2344
3772
  }
2345
3773
 
2346
3774
  case 'remove_mesh_node': {
@@ -2384,6 +3812,7 @@ export class DaemonCommandRouter {
2384
3812
  } else {
2385
3813
  const { removeNode } = await import('../config/mesh-config.js');
2386
3814
  removed = removeNode(meshId, nodeId);
3815
+ if (removed) this.invalidateAggregateMeshStatus(meshId);
2387
3816
  }
2388
3817
 
2389
3818
  // Record in task ledger
@@ -2421,6 +3850,8 @@ export class DaemonCommandRouter {
2421
3850
  if (!meshId) return { success: false, error: 'meshId required' };
2422
3851
  if (!sourceNodeId) return { success: false, error: 'sourceNodeId required' };
2423
3852
  if (!branch) return { success: false, error: 'branch required' };
3853
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'worktree clone');
3854
+ if (ownerFailure) return ownerFailure;
2424
3855
 
2425
3856
  try {
2426
3857
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
@@ -2469,6 +3900,7 @@ export class DaemonCommandRouter {
2469
3900
  policy: { ...(sourceNode.policy || {}) },
2470
3901
  });
2471
3902
  if (!node) return { success: false, error: 'Failed to register worktree node' };
3903
+ this.invalidateAggregateMeshStatus(meshId);
2472
3904
  }
2473
3905
 
2474
3906
  // Initialize submodules if policy allows (default: true)
@@ -2510,6 +3942,8 @@ export class DaemonCommandRouter {
2510
3942
  case 'trigger_mesh_queue': {
2511
3943
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
2512
3944
  if (!meshId) return { success: false, error: 'meshId required' };
3945
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue trigger');
3946
+ if (ownerFailure) return ownerFailure;
2513
3947
  try {
2514
3948
  const { triggerMeshQueue } = await import('../mesh/mesh-events.js');
2515
3949
  if (meshId) {
@@ -2541,6 +3975,15 @@ export class DaemonCommandRouter {
2541
3975
  mesh = getMesh(meshId);
2542
3976
  }
2543
3977
  if (!mesh) return { success: false, error: 'Mesh not found' };
3978
+ const meshHost = resolveMeshHostStatus(mesh);
3979
+ if (!meshHost.canOwnCoordinator) {
3980
+ return {
3981
+ success: false,
3982
+ ...buildMeshHostRequiredFailure(mesh, 'coordinator launch'),
3983
+ meshId,
3984
+ cliType,
3985
+ };
3986
+ }
2544
3987
  if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: 'No nodes in mesh' };
2545
3988
 
2546
3989
  const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === 'string'
@@ -2560,7 +4003,16 @@ export class DaemonCommandRouter {
2560
4003
  cliType,
2561
4004
  };
2562
4005
  }
2563
- const workspace = typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '';
4006
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
4007
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
4008
+ : [];
4009
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
4010
+ const workspace = readLiveMeshNodeWorkspace({
4011
+ meshId,
4012
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ''),
4013
+ liveSessionRecords: liveMeshSessions,
4014
+ allowCoordinatorSession: true,
4015
+ }) || (typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '');
2564
4016
  if (!workspace) return { success: false, error: 'Coordinator node workspace required', meshId, cliType };
2565
4017
  if (!cliType) {
2566
4018
  const resolved = await resolveProviderTypeFromPriority({
@@ -2905,6 +4357,27 @@ export class DaemonCommandRouter {
2905
4357
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
2906
4358
  const mesh = meshRecord?.mesh;
2907
4359
  if (!mesh) return { success: false, error: 'Mesh not found' };
4360
+ const meshHost = resolveMeshHostStatus(mesh);
4361
+
4362
+ const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
4363
+ const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
4364
+ if (!refreshRequested) {
4365
+ const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
4366
+ if (cachedStatus) {
4367
+ logRepoMeshStatusDebug('return_cached', {
4368
+ meshId,
4369
+ command: 'mesh_status',
4370
+ refreshRequested,
4371
+ summary: summarizeRepoMeshStatusDebug(cachedStatus),
4372
+ });
4373
+ return cachedStatus;
4374
+ }
4375
+ }
4376
+ const refreshReason = refreshRequested
4377
+ ? 'explicit_refresh'
4378
+ : hadAggregateCache
4379
+ ? 'stale_pending_cache_refresh'
4380
+ : 'cold_cache_miss';
2908
4381
 
2909
4382
  const { getMeshQueueStats, getQueue } = await import('../mesh/mesh-work-queue.js');
2910
4383
  const queue = getQueue(meshId);
@@ -2913,58 +4386,344 @@ export class DaemonCommandRouter {
2913
4386
  const { readLedgerEntries, getLedgerSummary } = await import('../mesh/mesh-ledger.js');
2914
4387
  const ledgerEntries = readLedgerEntries(meshId, { tail: 20 });
2915
4388
  const ledgerSummary = getLedgerSummary(meshId);
2916
-
4389
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
4390
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
4391
+ : [];
4392
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
4393
+
4394
+ const localMachineId = loadConfig().machineId || '';
4395
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
4396
+ const directTruth = requireDirectPeerTruth
4397
+ ? await hydrateInlineMeshDirectTruth({
4398
+ mesh,
4399
+ meshSource: meshRecord.source,
4400
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4401
+ statusInstanceId: this.deps.statusInstanceId,
4402
+ localMachineId,
4403
+ })
4404
+ : {
4405
+ directEvidenceCount: 0,
4406
+ localConfirmedCount: 0,
4407
+ peerAttemptedCount: 0,
4408
+ peerConfirmedCount: 0,
4409
+ unavailableNodeIds: [] as string[],
4410
+ };
4411
+ // Default/cached loads may not attempt a remote peer probe yet; do not surface that as
4412
+ // a direct mesh truth failure until an explicit probe attempt actually fails.
4413
+ const passivePeerTruthNotAttempted = requireDirectPeerTruth
4414
+ && !refreshRequested
4415
+ && directTruth.directEvidenceCount > 0
4416
+ && directTruth.peerAttemptedCount === 0;
4417
+ const effectiveDirectTruth = passivePeerTruthNotAttempted
4418
+ ? { ...directTruth, unavailableNodeIds: [] as string[] }
4419
+ : directTruth;
4420
+ const directTruthSatisfied = !requireDirectPeerTruth
4421
+ || (effectiveDirectTruth.directEvidenceCount > 0 && effectiveDirectTruth.unavailableNodeIds.length === 0);
4422
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
4423
+ const failureResult = {
4424
+ success: false,
4425
+ code: 'mesh_direct_peer_truth_unavailable',
4426
+ error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.',
4427
+ sourceOfTruth: {
4428
+ membership: meshRecord.source === 'inline_cache'
4429
+ ? 'coordinator_inline_mesh_cache'
4430
+ : meshRecord.source === 'local_config'
4431
+ ? 'local_mesh_config'
4432
+ : 'inline_bootstrap_snapshot',
4433
+ coordinatorOwnsLiveTruth: false,
4434
+ currentStatus: 'direct_peer_truth_unavailable',
4435
+ directPeerTruth: {
4436
+ required: true,
4437
+ satisfied: false,
4438
+ directEvidenceCount: directTruth.directEvidenceCount,
4439
+ localConfirmedCount: directTruth.localConfirmedCount,
4440
+ peerAttemptedCount: directTruth.peerAttemptedCount,
4441
+ peerConfirmedCount: directTruth.peerConfirmedCount,
4442
+ unavailableNodeIds: directTruth.unavailableNodeIds,
4443
+ },
4444
+ },
4445
+ };
4446
+ logRepoMeshStatusDebug('direct_truth_unavailable', {
4447
+ meshId,
4448
+ command: 'mesh_status',
4449
+ refreshRequested,
4450
+ meshSource: meshRecord.source,
4451
+ directTruth,
4452
+ });
4453
+ return failureResult;
4454
+ }
4455
+ const directTruthUnavailableNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
4456
+ const selectedCoordinatorNodeId = readStringValue(
4457
+ mesh.coordinator?.preferredNodeId,
4458
+ (mesh.nodes?.[0] as any)?.id,
4459
+ (mesh.nodes?.[0] as any)?.nodeId,
4460
+ );
4461
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes)
4462
+ ? selectedCoordinatorNodeId
4463
+ : undefined;
4464
+ const refreshedAt = new Date().toISOString();
2917
4465
  const nodeStatuses = [];
2918
- for (const node of mesh.nodes || []) {
4466
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
4467
+ const nodeId = String(node.id || node.nodeId || '');
4468
+ const daemonId = readStringValue(node.daemonId);
4469
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
4470
+ const isSelfNode = Boolean(
4471
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
4472
+ ) || Boolean(
4473
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId),
4474
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
2919
4475
  const status: Record<string, unknown> = {
2920
- nodeId: node.id || node.nodeId,
4476
+ nodeId,
2921
4477
  machineLabel: node.machineLabel || node.id || node.nodeId,
2922
4478
  workspace: node.workspace,
2923
4479
  repoRoot: node.repoRoot,
2924
4480
  isLocalWorktree: node.isLocalWorktree,
2925
4481
  worktreeBranch: node.worktreeBranch,
2926
- daemonId: node.daemonId,
4482
+ role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? 'host' : undefined),
4483
+ daemonId,
2927
4484
  machineId: node.machineId,
4485
+ machineStatus: node.machineStatus,
2928
4486
  health: 'unknown',
2929
4487
  providers: node.providers || [],
4488
+ providerPriority,
2930
4489
  activeSessions: [],
4490
+ activeSessionDetails: [],
4491
+ launchReady: false,
2931
4492
  };
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;
4493
+ if (isSelfNode) {
4494
+ status.connection = {
4495
+ perspective: 'selected_coordinator',
4496
+ source: 'mesh_peer_status',
4497
+ state: 'self',
4498
+ transport: 'local',
4499
+ reported: true,
4500
+ reason: 'Selected coordinator daemon',
4501
+ lastStateChangeAt: refreshedAt,
4502
+ };
4503
+ } else if (daemonId) {
4504
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
4505
+ status.connection = connection ?? {
4506
+ perspective: 'selected_coordinator',
4507
+ source: 'not_reported',
4508
+ state: 'unknown',
4509
+ transport: 'unknown',
4510
+ reported: false,
4511
+ reason: 'No live mesh peer telemetry reported by the selected coordinator yet.',
4512
+ };
4513
+ } else {
4514
+ status.connection = {
4515
+ perspective: 'selected_coordinator',
4516
+ source: 'not_reported',
4517
+ state: 'unknown',
4518
+ transport: 'unknown',
4519
+ reported: false,
4520
+ reason: 'Node has no daemon id, so mesh transport cannot be reported from the selected coordinator.',
4521
+ };
4522
+ }
4523
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
4524
+ meshId,
4525
+ node,
4526
+ nodeId,
4527
+ liveSessionRecords: liveMeshSessions,
4528
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
4529
+ });
4530
+ const workspace = readLiveMeshNodeWorkspace({
4531
+ meshId,
4532
+ nodeId,
4533
+ liveSessionRecords: matchedLiveSessionRecords,
4534
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
4535
+ }) || (typeof node.workspace === 'string' ? node.workspace : '');
4536
+ status.workspace = workspace || node.workspace;
4537
+ if (matchedLiveSessionRecords.length > 0) {
4538
+ const sessionIds = matchedLiveSessionRecords
4539
+ .map((record: any) => typeof record?.sessionId === 'string' ? record.sessionId : '')
4540
+ .filter(Boolean);
4541
+ const providerTypes = matchedLiveSessionRecords
4542
+ .map((record: any) => readStringValue(record?.providerType))
4543
+ .filter(Boolean) as string[];
4544
+ status.activeSessions = sessionIds;
4545
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
4546
+ if (providerTypes.length > 0) {
4547
+ status.providers = Array.from(new Set([...(Array.isArray(status.providers) ? status.providers as string[] : []), ...providerTypes]));
2936
4548
  }
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;
4549
+ }
4550
+ if (workspace) {
4551
+ if (!fs.existsSync(workspace)) {
4552
+ // Workspace not local — prefer direct live inline truth, then attempt a P2P git probe.
4553
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
4554
+ let remoteProbeApplied = false;
4555
+ if (inlineTransitGit) {
4556
+ status.git = inlineTransitGit;
4557
+ status.health = inlineTransitGit.isGitRepo
4558
+ ? deriveMeshNodeHealthFromGit(inlineTransitGit as unknown as Record<string, unknown>)
4559
+ : 'degraded';
4560
+ const connection = readObjectRecord(status.connection);
4561
+ const connectionState = readStringValue(connection.state);
4562
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4563
+ if (!connectionReported || connectionState === 'unknown') {
4564
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4565
+ }
4566
+ remoteProbeApplied = true;
4567
+ } else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
4568
+ try {
4569
+ const remoteGit = await probeRemoteMeshGitStatus({
4570
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4571
+ daemonId,
4572
+ workspace,
4573
+ timeoutMs: 8000,
4574
+ });
4575
+ if (remoteGit) {
4576
+ status.git = remoteGit;
4577
+ status.health = remoteGit.isGitRepo
4578
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
4579
+ : 'degraded';
4580
+ const connection = readObjectRecord(status.connection);
4581
+ const connectionState = readStringValue(connection.state);
4582
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4583
+ if (!connectionReported || connectionState === 'unknown') {
4584
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4585
+ }
4586
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
4587
+ remoteProbeApplied = true;
4588
+ }
4589
+ } catch {
4590
+ const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
4591
+ const refreshedConnectionState = readStringValue(refreshedConnection?.state);
4592
+ if (refreshedConnection && refreshedConnectionState === 'connected') {
4593
+ status.connection = refreshedConnection;
4594
+ try {
4595
+ const remoteGit = await probeRemoteMeshGitStatus({
4596
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
4597
+ daemonId,
4598
+ workspace,
4599
+ timeoutMs: 12000,
4600
+ });
4601
+ if (remoteGit) {
4602
+ status.git = remoteGit;
4603
+ status.health = remoteGit.isGitRepo
4604
+ ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
4605
+ : 'degraded';
4606
+ const connection = readObjectRecord(status.connection);
4607
+ const connectionState = readStringValue(connection.state);
4608
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
4609
+ if (!connectionReported || connectionState === 'unknown') {
4610
+ status.connection = buildLivePeerGitConnection(connection, refreshedAt);
4611
+ }
4612
+ recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
4613
+ remoteProbeApplied = true;
4614
+ }
4615
+ } catch {
4616
+ // Probe timed out again or P2P unavailable — fall back to cached status
4617
+ }
4618
+ }
4619
+ }
2946
4620
  }
2947
- } catch {
2948
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
2949
- status.health = 'degraded';
4621
+ if (!remoteProbeApplied) {
4622
+ const connectionState = readStringValue((status.connection as any)?.state);
4623
+ const pendingPeerGitProbe = !inlineTransitGit
4624
+ && !isSelfNode
4625
+ && !!daemonId
4626
+ && (
4627
+ readStringValue(status.machineStatus) === 'online'
4628
+ || readStringValue(status.health) === 'online'
4629
+ || connectionState === 'connecting'
4630
+ || connectionState === 'connected'
4631
+ || connectionState === 'unknown'
4632
+ );
4633
+ if (pendingPeerGitProbe) {
4634
+ status.gitProbePending = true;
4635
+ status.health = 'unknown';
4636
+ }
4637
+ if (applyCachedInlineMeshNodeStatus(
4638
+ status,
4639
+ node,
4640
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : undefined,
4641
+ )) {
4642
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4643
+ nodeStatuses.push(status);
4644
+ continue;
4645
+ }
4646
+ if (meshRecord?.source === 'inline_cache' && !isSelfNode) {
4647
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4648
+ nodeStatuses.push(status);
4649
+ continue;
4650
+ }
4651
+ }
4652
+ } else {
4653
+ try {
4654
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
4655
+ status.git = gitStatus;
4656
+ recordInlineMeshDirectGitTruth(node, gitStatus as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
4657
+ if (gitStatus.isGitRepo) {
4658
+ status.health = deriveMeshNodeHealthFromGit(gitStatus as unknown as Record<string, unknown>);
4659
+ } else {
4660
+ status.health = 'degraded';
4661
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
4662
+ }
4663
+ } catch {
4664
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
4665
+ status.health = 'degraded';
4666
+ }
2950
4667
  }
2951
4668
  }
2952
4669
  } else {
2953
4670
  applyCachedInlineMeshNodeStatus(status, node);
2954
4671
  }
4672
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
2955
4673
  nodeStatuses.push(status);
2956
4674
  }
2957
4675
 
2958
- return {
4676
+ const statusResult = {
2959
4677
  success: true,
2960
4678
  meshId: mesh.id,
2961
4679
  meshName: mesh.name,
2962
4680
  repoIdentity: mesh.repoIdentity,
2963
4681
  defaultBranch: mesh.defaultBranch,
4682
+ refreshedAt,
4683
+ meshHost,
4684
+ sourceOfTruth: {
4685
+ membership: meshRecord?.source === 'inline_cache'
4686
+ ? 'coordinator_inline_mesh_cache'
4687
+ : meshRecord?.source === 'local_config'
4688
+ ? 'local_mesh_config'
4689
+ : 'inline_bootstrap_snapshot',
4690
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
4691
+ meshHost: {
4692
+ owner: 'mesh_host_daemon',
4693
+ localRole: meshHost.role,
4694
+ hostDaemonId: meshHost.hostDaemonId,
4695
+ hostNodeId: meshHost.hostNodeId,
4696
+ hostAddress: meshHost.hostAddress,
4697
+ },
4698
+ ...(requireDirectPeerTruth ? {
4699
+ currentStatus: directTruthSatisfied ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
4700
+ directPeerTruth: {
4701
+ required: true,
4702
+ satisfied: directTruthSatisfied,
4703
+ directEvidenceCount: effectiveDirectTruth.directEvidenceCount,
4704
+ localConfirmedCount: effectiveDirectTruth.localConfirmedCount,
4705
+ peerAttemptedCount: effectiveDirectTruth.peerAttemptedCount,
4706
+ peerConfirmedCount: effectiveDirectTruth.peerConfirmedCount,
4707
+ unavailableNodeIds: effectiveDirectTruth.unavailableNodeIds,
4708
+ },
4709
+ } : {}),
4710
+ historicalEvidenceOnly: ['recoveryHints', 'ledger.summary', 'queue.summary'],
4711
+ },
2964
4712
  nodes: nodeStatuses,
2965
4713
  queue: { tasks: queue, summary: queueSummary },
2966
4714
  ledger: { entries: ledgerEntries, summary: ledgerSummary },
2967
4715
  };
4716
+ const rememberedStatus = this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
4717
+ logRepoMeshStatusDebug('return_live', {
4718
+ meshId,
4719
+ command: 'mesh_status',
4720
+ refreshRequested,
4721
+ refreshReason,
4722
+ meshSource: meshRecord.source,
4723
+ directTruth,
4724
+ summary: summarizeRepoMeshStatusDebug(rememberedStatus),
4725
+ });
4726
+ return rememberedStatus;
2968
4727
  } catch (e: any) {
2969
4728
  return { success: false, error: e.message };
2970
4729
  }