@adhdev/daemon-core 0.9.82-rc.365 → 0.9.82-rc.366

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.
@@ -16,6 +16,8 @@ import { lowFamilyRegistry } from './low-family/index.js';
16
16
  import { medFamilyRegistry } from './med-family/index.js';
17
17
  import { launchIde } from './med-family/ide.js';
18
18
  import type { MedFamilyContext } from './med-family/index.js';
19
+ import { highFamilyRegistry } from './high-family/index.js';
20
+ import type { HighFamilyContext } from './high-family/index.js';
19
21
  import { DaemonCliManager } from './cli-manager.js';
20
22
  import { supportsExplicitSessionResume } from './cli-manager.js';
21
23
  import type { HostedCliRuntimeDescriptor } from './cli-manager.js';
@@ -52,17 +54,12 @@ import { logCommand } from '../logging/command-log.js';
52
54
  import type { CommandLogEntry } from '../logging/command-log.js';
53
55
  import * as yaml from 'js-yaml';
54
56
  import { createInteractionId, recordDebugTrace } from '../logging/debug-trace.js';
55
- import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
56
- import { createHermesManualMeshCoordinatorSetup, resolveMeshCoordinatorSetup } from './mesh-coordinator.js';
57
- import { registerMeshCoordinator } from '../mesh/coordinator-registry.js';
58
- import { handleMeshForwardEvent, drainPendingMeshCoordinatorEvents, getPendingMeshCoordinatorEvents, queuePendingMeshCoordinatorEvent, type PendingMeshCoordinatorEvent } from '../mesh/mesh-events.js';
59
- import { getRecentUnroutableDeliveries } from '../mesh/mesh-routing.js';
57
+ import { getSessionHostSurfaceKind } from '../session-host/runtime-surface.js';
58
+ import { handleMeshForwardEvent, queuePendingMeshCoordinatorEvent } from '../mesh/mesh-events.js';
60
59
  import { buildMeshWorkerRelayStamp } from '../mesh/mesh-events-utils.js';
61
- import { buildMeshHostRequiredFailure, normalizeMeshDaemonRole, resolveMeshHostStatus } from '../mesh/mesh-host-ownership.js';
60
+ import { buildMeshHostRequiredFailure, resolveMeshHostStatus } from '../mesh/mesh-host-ownership.js';
62
61
  import { fastForwardMeshNode } from '../mesh/mesh-fast-forward.js';
63
62
  import { analyzeMeshRefineNodeChangeArea, orderMeshRefineBatchNodes } from '../mesh/mesh-refine-batch.js';
64
- import { buildPreviewFreshness } from '../mesh/preview-freshness.js';
65
- import { buildMeshAsyncRefineJobs } from '../mesh/mesh-refine-status.js';
66
63
  import {
67
64
  MESH_REFINE_CONFIG_LOCATIONS,
68
65
  MESH_REFINE_CONFIG_SCHEMA,
@@ -88,11 +85,10 @@ import { homedir, hostname as osHostname } from 'os';
88
85
  import { basename as pathBasename, join as pathJoin, resolve as pathResolve } from 'path';
89
86
  import * as fs from 'fs';
90
87
  import { execFileSync } from 'node:child_process';
91
- import { normalizeInteractivePromptResponse } from '../providers/types/interactive-prompt.js';
92
88
  import { workingDirBasename } from '../providers/working-dir.js';
93
89
  import { resolveWin32Executable } from '../cli-adapters/resolve-executable.js';
94
90
 
95
- function readProviderPriorityFromPolicy(policy: unknown): string[] {
91
+ export function readProviderPriorityFromPolicy(policy: unknown): string[] {
96
92
  const record = policy && typeof policy === 'object' && !Array.isArray(policy)
97
93
  ? policy as Record<string, unknown>
98
94
  : {};
@@ -153,7 +149,7 @@ function readNumberValue(...values: unknown[]): number | undefined {
153
149
  return undefined;
154
150
  }
155
151
 
156
- function readBooleanValue(...values: unknown[]): boolean | undefined {
152
+ export function readBooleanValue(...values: unknown[]): boolean | undefined {
157
153
  for (const value of values) {
158
154
  if (typeof value === 'boolean') return value;
159
155
  }
@@ -163,7 +159,7 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
163
159
  // summarizeRepoMeshDebugGit was a hand-synced copy of the cloud git-shape
164
160
  // summarizer; both now call shared summarizeGitShape (@adhdev/mesh-shared).
165
161
 
166
- function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
162
+ export function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
167
163
  const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
168
164
  return {
169
165
  success: status?.success,
@@ -195,7 +191,7 @@ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
195
191
  };
196
192
  }
197
193
 
198
- function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>): void {
194
+ export function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>): void {
199
195
  try {
200
196
  LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
201
197
  } catch {
@@ -257,7 +253,7 @@ function readMeshNodeDaemonId(node: Record<string, unknown>): string | undefined
257
253
  );
258
254
  }
259
255
 
260
- function readMeshNodeHostname(node: Record<string, unknown>): string | undefined {
256
+ export function readMeshNodeHostname(node: Record<string, unknown>): string | undefined {
261
257
  return readStringValue(
262
258
  node.hostname,
263
259
  node.host,
@@ -297,7 +293,7 @@ function compactMeshIdentityEvidence(value: string | undefined): string | undefi
297
293
  return value.length > 24 ? `${value.slice(0, 12)}…${value.slice(-8)}` : value;
298
294
  }
299
295
 
300
- function buildMeshNodeMachineIdentity(node: Record<string, unknown>, opts: {
296
+ export function buildMeshNodeMachineIdentity(node: Record<string, unknown>, opts: {
301
297
  localMachineId?: string;
302
298
  localDaemonId?: string;
303
299
  coordinatorHostname?: string;
@@ -357,7 +353,7 @@ function normalizeInlineMeshGitStatus(
357
353
  return sharedNormalizeGitStatus(status, readObjectRecord(node), options) as Record<string, unknown> | undefined;
358
354
  }
359
355
 
360
- function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
356
+ export function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
361
357
  return sharedPickBestTransitGitStatus(readObjectRecord(node), { lastCheckedAt: Date.now() }) as Record<string, unknown> | undefined;
362
358
  }
363
359
 
@@ -370,7 +366,7 @@ function shouldRefreshStalePendingAggregate(snapshot: any, options?: { requireDi
370
366
  });
371
367
  }
372
368
 
373
- function buildLivePeerGitConnection(connection: Record<string, unknown>, timestamp = new Date().toISOString()): Record<string, unknown> {
369
+ export function buildLivePeerGitConnection(connection: Record<string, unknown>, timestamp = new Date().toISOString()): Record<string, unknown> {
374
370
  const source = readStringValue(connection.source);
375
371
  const transport = readStringValue(connection.transport);
376
372
  return {
@@ -385,7 +381,7 @@ function buildLivePeerGitConnection(connection: Record<string, unknown>, timesta
385
381
  };
386
382
  }
387
383
 
388
- function recordInlineMeshDirectGitTruth(
384
+ export function recordInlineMeshDirectGitTruth(
389
385
  node: any,
390
386
  git: Record<string, unknown>,
391
387
  source: 'selected_coordinator_local_git' | 'selected_coordinator_mesh_p2p_git',
@@ -462,7 +458,7 @@ function stampNodeReporterPlatform(node: any, platform: string | null, arch: str
462
458
  * Fire-and-forget (same pattern as the worktreeBootstrap writer) — a persistence
463
459
  * failure must never block the status response.
464
460
  */
465
- function persistNodeReporterPlatform(
461
+ export function persistNodeReporterPlatform(
466
462
  meshSource: 'inline_cache' | 'inline_bootstrap' | 'local_config',
467
463
  mesh: any,
468
464
  nodeId: string | undefined,
@@ -736,7 +732,7 @@ function isInlineMeshAutoFastForwardEligible(git: Record<string, unknown> | null
736
732
  return dirty !== true && countGitWorktreeChanges(git) === 0;
737
733
  }
738
734
 
739
- function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
735
+ export function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
740
736
  if (!git || readBooleanValue(git.isGitRepo) === false) return 'degraded';
741
737
  const branch = readStringValue(git.branch);
742
738
  if (!branch) return 'degraded';
@@ -875,7 +871,7 @@ function buildInlineMeshBranchConvergence(args: {
875
871
  };
876
872
  }
877
873
 
878
- function applyInlineMeshBranchConvergence(mesh: any, node: any, status: Record<string, unknown>): void {
874
+ export function applyInlineMeshBranchConvergence(mesh: any, node: any, status: Record<string, unknown>): void {
879
875
  const git = readObjectRecord(status.git);
880
876
  if (Object.keys(git).length === 0 && !status.gitProbePending) return;
881
877
  const uncommittedChanges = countGitWorktreeChanges(git);
@@ -890,7 +886,7 @@ function applyInlineMeshBranchConvergence(mesh: any, node: any, status: Record<s
890
886
  }
891
887
  }
892
888
 
893
- function summarizeInlineMeshBranchConvergence(nodes: Array<Record<string, unknown>>): Record<string, unknown> {
889
+ export function summarizeInlineMeshBranchConvergence(nodes: Array<Record<string, unknown>>): Record<string, unknown> {
894
890
  const followUps = nodes
895
891
  .filter(node => {
896
892
  if (readObjectRecord(node.branchConvergence).needsConvergence !== true) return false;
@@ -1076,7 +1072,7 @@ function synthesizeMeshNodeFreshnessFromConnection(status: Record<string, unknow
1076
1072
  }
1077
1073
  }
1078
1074
 
1079
- function finalizeMeshNodeStatus(args: {
1075
+ export function finalizeMeshNodeStatus(args: {
1080
1076
  status: Record<string, unknown>;
1081
1077
  node: any;
1082
1078
  daemonId?: string;
@@ -1132,8 +1128,8 @@ function readMeshTimeoutEnvMs(name: string, defaultMs: number): number {
1132
1128
  // round-trip to slow (often TURN-relayed) peers, so such a node was permanently
1133
1129
  // marked unavailable and blocked the whole mesh graph. Default raised to 25s
1134
1130
  // (still under the P2P REQUEST_TIMEOUT of 30s) and made env-overridable.
1135
- const MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs('MESH_DIRECT_PROBE_TIMEOUT_MS', 25_000);
1136
- const MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs('MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS', 25_000);
1131
+ export const MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs('MESH_DIRECT_PROBE_TIMEOUT_MS', 25_000);
1132
+ export const MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs('MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS', 25_000);
1137
1133
  // How long a successful per-peer git_status probe stays fresh enough to be
1138
1134
  // reused instead of issuing another blocking `refreshUpstream:true` fan-out.
1139
1135
  // A slow (TURN-relayed) peer's probe can take 9-23s, and the dashboard's
@@ -1266,7 +1262,7 @@ function isMeshConnectionDefinitivelyDown(
1266
1262
  * attempt; a non-`connected` state short-circuits the retry loop (the very first
1267
1263
  * attempt always runs so a missing connection getter still gets one try).
1268
1264
  */
1269
- async function probeRemoteMeshGitStatusWithRetry(args: {
1265
+ export async function probeRemoteMeshGitStatusWithRetry(args: {
1270
1266
  dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
1271
1267
  daemonId: string;
1272
1268
  workspace: string;
@@ -1483,7 +1479,7 @@ export async function hydrateInlineMeshDirectTruth(args: {
1483
1479
  };
1484
1480
  }
1485
1481
 
1486
- function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
1482
+ export function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
1487
1483
  const meta = readObjectRecord(record?.meta);
1488
1484
  const isSelfCoordinator = Boolean(readStringValue(meta.meshCoordinatorFor));
1489
1485
  const chatStatus = readStringValue(record?.chatStatus, record?.activeChat?.status, meta.chatStatus, meta.sessionStatus);
@@ -1531,7 +1527,7 @@ function liveSessionRecordMatchesMeshWorkspace(record: any, meshId: string, work
1531
1527
  return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
1532
1528
  }
1533
1529
 
1534
- function readLiveMeshNodeWorkspace(args: {
1530
+ export function readLiveMeshNodeWorkspace(args: {
1535
1531
  meshId: string;
1536
1532
  nodeId: string;
1537
1533
  liveSessionRecords: any[];
@@ -1558,7 +1554,7 @@ function readLiveMeshNodeWorkspace(args: {
1558
1554
  return '';
1559
1555
  }
1560
1556
 
1561
- function collectLiveMeshSessionRecords(args: {
1557
+ export function collectLiveMeshSessionRecords(args: {
1562
1558
  meshId: string;
1563
1559
  node: any;
1564
1560
  nodeId: string;
@@ -1589,7 +1585,7 @@ function collectLiveMeshSessionRecords(args: {
1589
1585
  return matches;
1590
1586
  }
1591
1587
 
1592
- function buildHistoricalMeshSessions(args: {
1588
+ export function buildHistoricalMeshSessions(args: {
1593
1589
  meshId: string;
1594
1590
  nodes: any[];
1595
1591
  liveSessionRecords: any[];
@@ -1635,7 +1631,7 @@ function buildHistoricalMeshSessions(args: {
1635
1631
  };
1636
1632
  }
1637
1633
 
1638
- function applyCachedInlineMeshNodeStatus(
1634
+ export function applyCachedInlineMeshNodeStatus(
1639
1635
  status: Record<string, unknown>,
1640
1636
  node: any,
1641
1637
  options?: { skipGit?: boolean; skipError?: boolean; skipHealth?: boolean },
@@ -1669,7 +1665,7 @@ function applyCachedInlineMeshNodeStatus(
1669
1665
  return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
1670
1666
  }
1671
1667
 
1672
- async function resolveProviderTypeFromPriority(args: {
1668
+ export async function resolveProviderTypeFromPriority(args: {
1673
1669
  nodeId: string;
1674
1670
  providerPriority: string[];
1675
1671
  providerLoader: ProviderLoader;
@@ -1699,7 +1695,7 @@ async function resolveProviderTypeFromPriority(args: {
1699
1695
 
1700
1696
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join('; ')}` };
1701
1697
  }
1702
- type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
1698
+ export type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
1703
1699
  type MeshRefineValidationStatus = 'passed' | 'failed' | 'skipped';
1704
1700
  type MeshRefineValidationCommand = MeshRefineValidationCommandPlan;
1705
1701
 
@@ -1849,7 +1845,7 @@ type MeshRefineSubmoduleReachabilitySummary = {
1849
1845
 
1850
1846
  type MeshRefineAsyncJobStatus = 'accepted' | 'completed' | 'failed';
1851
1847
 
1852
- type MeshRefineJobHandle = {
1848
+ export type MeshRefineJobHandle = {
1853
1849
  success: true;
1854
1850
  async: true;
1855
1851
  status: MeshRefineAsyncJobStatus;
@@ -1982,6 +1978,51 @@ function buildSubmodulePublishRequiredNextStep(entries: MeshRefineSubmoduleReach
1982
1978
  return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to the configured submodule remote main branch, then rerun mesh_refine_node. Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main.`;
1983
1979
  }
1984
1980
 
1981
+ /**
1982
+ * Async git exec helper used across the synchronous-refine stage pipeline. Bound
1983
+ * once in the orchestrator and threaded through RefineContext so every stage runs
1984
+ * git the same way (execFile + promisify, utf8). Returns the child's stdout/stderr.
1985
+ */
1986
+ type RefineExecFileAsync = (file: string, args: string[], options: { cwd: string; encoding: 'utf8' }) => Promise<{ stdout: string; stderr: string }>;
1987
+
1988
+ /**
1989
+ * Accumulated state shared by the synchronous-refine stages. The orchestrator
1990
+ * (executeMeshRefineNodeSynchronously) seeds this in the resolve_refs stage and
1991
+ * each later stage reads / extends it. `branchHead` and `patchEquivalence` are the
1992
+ * only fields a stage mutates after creation (auto-rebase updates both), so they
1993
+ * are carried on the mutable context rather than re-threaded through return types.
1994
+ */
1995
+ interface RefineContext {
1996
+ meshId: string;
1997
+ nodeId: string;
1998
+ args: any;
1999
+ refineStages: Array<Record<string, unknown>>;
2000
+ execFileAsync: RefineExecFileAsync;
2001
+ mesh: any;
2002
+ node: any;
2003
+ sourceNode: any;
2004
+ repoRoot: string;
2005
+ branch: string;
2006
+ baseBranch: string;
2007
+ baseHead: string;
2008
+ branchHead: string;
2009
+ validationSummary: Awaited<ReturnType<typeof runMeshRefineValidationGate>>;
2010
+ patchEquivalence: Awaited<ReturnType<typeof runMeshRefinePatchEquivalenceGate>>;
2011
+ submoduleReachability: Awaited<ReturnType<typeof runMeshRefineSubmoduleReachabilityGate>>;
2012
+ }
2013
+
2014
+ /**
2015
+ * Stage outcome for the synchronous-refine pipeline. A stage either produces a
2016
+ * terminal CommandRouterResult (an early-exit gate failure, or a successful
2017
+ * already-merged short-circuit), in which case the orchestrator returns it
2018
+ * immediately, or it returns `continue` with the (possibly extended) context for
2019
+ * the next stage. This makes the orchestrator a flat sequence of stage calls
2020
+ * while preserving the original body's exact early-return control flow.
2021
+ */
2022
+ type RefineStageOutcome =
2023
+ | { kind: 'terminal'; result: CommandRouterResult }
2024
+ | { kind: 'continue'; ctx: RefineContext };
2025
+
1985
2026
  function resolveRefineryAutoPublishSubmoduleMainCommits(mesh: any, workspace: string): { enabled: boolean; source?: string } {
1986
2027
  if (mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true) {
1987
2028
  process.stderr.write(
@@ -3285,18 +3326,18 @@ function loadYamlModule(): { load: (input: string) => any; dump: (input: any, op
3285
3326
  return yaml as { load: (input: string) => any; dump: (input: any, options?: Record<string, any>) => string };
3286
3327
  }
3287
3328
 
3288
- function getMcpServersKey(format: MeshCoordinatorConfigFormat): 'mcpServers' | 'mcp_servers' {
3329
+ export function getMcpServersKey(format: MeshCoordinatorConfigFormat): 'mcpServers' | 'mcp_servers' {
3289
3330
  return format === 'hermes_config_yaml' ? 'mcp_servers' : 'mcpServers';
3290
3331
  }
3291
3332
 
3292
- function parseMeshCoordinatorMcpConfig(text: string, format: MeshCoordinatorConfigFormat): Record<string, any> {
3333
+ export function parseMeshCoordinatorMcpConfig(text: string, format: MeshCoordinatorConfigFormat): Record<string, any> {
3293
3334
  if (!text.trim()) return {};
3294
3335
  if (format === 'claude_mcp_json') return JSON.parse(text);
3295
3336
  const parsed = loadYamlModule().load(text);
3296
3337
  return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
3297
3338
  }
3298
3339
 
3299
- function serializeMeshCoordinatorMcpConfig(config: Record<string, any>, format: MeshCoordinatorConfigFormat): string {
3340
+ export function serializeMeshCoordinatorMcpConfig(config: Record<string, any>, format: MeshCoordinatorConfigFormat): string {
3300
3341
  if (format === 'claude_mcp_json') return JSON.stringify(config, null, 2);
3301
3342
  return loadYamlModule().dump(config, { noRefs: true, lineWidth: 120 });
3302
3343
  }
@@ -3306,7 +3347,7 @@ function resolveHermesUserHome(): string {
3306
3347
  return explicitHome || pathJoin(homedir(), '.hermes');
3307
3348
  }
3308
3349
 
3309
- function loadHermesCoordinatorBaseConfig(targetConfigPath: string): { config: Record<string, any>; sourceHome: string; sourceConfigPath: string } {
3350
+ export function loadHermesCoordinatorBaseConfig(targetConfigPath: string): { config: Record<string, any>; sourceHome: string; sourceConfigPath: string } {
3310
3351
  const sourceHome = resolveHermesUserHome();
3311
3352
  const sourceConfigPath = pathJoin(sourceHome, 'config.yaml');
3312
3353
  if (!fs.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
@@ -3317,7 +3358,7 @@ function loadHermesCoordinatorBaseConfig(targetConfigPath: string): { config: Re
3317
3358
  return { config: baseConfig, sourceHome, sourceConfigPath };
3318
3359
  }
3319
3360
 
3320
- function stripHermesCoordinatorTempModelProviderOverrides(config: Record<string, any>): Record<string, any> {
3361
+ export function stripHermesCoordinatorTempModelProviderOverrides(config: Record<string, any>): Record<string, any> {
3321
3362
  const {
3322
3363
  model: _model,
3323
3364
  provider: _provider,
@@ -3346,7 +3387,7 @@ function stripHermesCoordinatorTempModelProviderOverrides(config: Record<string,
3346
3387
  return sanitized;
3347
3388
  }
3348
3389
 
3349
- function copyHermesCoordinatorCredentialFiles(sourceHome: string, targetHome: string) {
3390
+ export function copyHermesCoordinatorCredentialFiles(sourceHome: string, targetHome: string) {
3350
3391
  if (pathResolve(sourceHome) === pathResolve(targetHome)) return;
3351
3392
  for (const fileName of ['.env', 'auth.json']) {
3352
3393
  const sourcePath = pathJoin(sourceHome, fileName);
@@ -3897,6 +3938,30 @@ export class DaemonCommandRouter {
3897
3938
  return ctx;
3898
3939
  }
3899
3940
 
3941
+ /**
3942
+ * Build the HighFamilyContext handed to RF-ROUTER HIGH family handlers. Binds
3943
+ * the router-private collaborators those handlers need (mesh resolution, the
3944
+ * aggregate-status memory cache + its bound read/write helpers, the
3945
+ * running-refine-job table, inline-mesh + git-probe caches, and the router's
3946
+ * own `execute` for the get_mesh_review_inbox mesh_status re-entry). HIGH
3947
+ * handlers reach more router-owned state than MED, but the binding shape is
3948
+ * the same: bound methods + direct field references, none reachable from
3949
+ * `deps`.
3950
+ */
3951
+ private buildHighFamilyContext(): HighFamilyContext {
3952
+ return {
3953
+ deps: this.deps,
3954
+ getMeshForCommand: this.getMeshForCommand.bind(this),
3955
+ getCachedAggregateMeshStatus: this.getCachedAggregateMeshStatus.bind(this),
3956
+ rememberAggregateMeshStatus: this.rememberAggregateMeshStatus.bind(this),
3957
+ execute: this.execute.bind(this),
3958
+ aggregateMeshStatusCache: this.aggregateMeshStatusCache,
3959
+ runningRefineJobs: this.runningRefineJobs,
3960
+ inlineMeshCache: this.inlineMeshCache,
3961
+ meshGitProbeCache: this.meshGitProbeCache,
3962
+ };
3963
+ }
3964
+
3900
3965
 
3901
3966
  private async requireMeshHostMutationOwner(meshId: string, inlineMesh: unknown, operation: string): Promise<CommandRouterResult | null> {
3902
3967
  const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
@@ -4801,33 +4866,77 @@ export class DaemonCommandRouter {
4801
4866
  }
4802
4867
  }
4803
4868
 
4869
+ /**
4870
+ * Synchronous refinery for a single worktree node — the gate pipeline that
4871
+ * validates, preflights (patch-equivalence / submodule-reachability /
4872
+ * no-op), merges, aligns submodules, cleans up the worktree node and
4873
+ * (optionally) pushes. The body is a flat sequence of stage methods; each
4874
+ * stage either returns a terminal CommandRouterResult (gate failure or a
4875
+ * successful already-merged short-circuit) or `continue` with the extended
4876
+ * context. Behavior — stage order, every early-exit, and every result shape —
4877
+ * is identical to the previous single inlined body.
4878
+ */
4804
4879
  private async executeMeshRefineNodeSynchronously(meshId: string, nodeId: string, args: any): Promise<CommandRouterResult> {
4805
4880
  const refineStages: Array<Record<string, unknown>> = [];
4806
4881
  try {
4882
+ const resolved = await this.refineResolveRefsStage(meshId, nodeId, args, refineStages);
4883
+ if (resolved.kind === 'terminal') return resolved.result;
4884
+ const ctx = resolved.ctx;
4885
+
4886
+ const validation = await this.refineValidationStage(ctx);
4887
+ if (validation.kind === 'terminal') return validation.result;
4888
+
4889
+ const patchEquivalence = await this.refinePatchEquivalenceStage(ctx);
4890
+ if (patchEquivalence.kind === 'terminal') return patchEquivalence.result;
4891
+
4892
+ const submoduleReachability = await this.refineSubmoduleReachabilityStage(ctx);
4893
+ if (submoduleReachability.kind === 'terminal') return submoduleReachability.result;
4894
+
4895
+ const effectiveDiff = await this.refineEffectiveDiffStage(ctx);
4896
+ if (effectiveDiff.kind === 'terminal') return effectiveDiff.result;
4897
+
4898
+ const merge = await this.refineMergeAndFinalizeStage(ctx);
4899
+ return (merge as { kind: 'terminal'; result: CommandRouterResult }).result;
4900
+ } catch (e: any) {
4901
+ return { success: false, error: e.message, refineStages };
4902
+ }
4903
+ }
4904
+
4905
+ /**
4906
+ * resolve_refs stage: resolve the mesh / worktree node / source node /
4907
+ * repoRoot, then the worktree branch, base branch, fetched base head and
4908
+ * branch head. Seeds the RefineContext consumed by every later stage.
4909
+ */
4910
+ private async refineResolveRefsStage(
4911
+ meshId: string,
4912
+ nodeId: string,
4913
+ args: any,
4914
+ refineStages: Array<Record<string, unknown>>,
4915
+ ): Promise<RefineStageOutcome> {
4807
4916
  // preferInline: same as startMeshRefineJob — inline-cache-only clone nodes must resolve.
4808
4917
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
4809
4918
  const mesh = meshRecord?.mesh;
4810
4919
  const node = mesh?.nodes?.find((n: any) => meshNodeIdMatches(n, nodeId));
4811
- if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
4920
+ if (!node) return { kind: 'terminal', result: { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages } };
4812
4921
 
4813
4922
  if (!node.isLocalWorktree || !node.workspace) {
4814
- return { success: false, error: `Refinery requires a local worktree node`, refineStages };
4923
+ return { kind: 'terminal', result: { success: false, error: `Refinery requires a local worktree node`, refineStages } };
4815
4924
  }
4816
4925
 
4817
4926
  const sourceNode = node.clonedFromNodeId
4818
4927
  ? mesh?.nodes.find((n: any) => meshNodeIdMatches(n, node.clonedFromNodeId))
4819
4928
  : mesh?.nodes.find((n: any) => !n.isLocalWorktree);
4820
4929
  const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
4821
- if (!repoRoot) return { success: false, error: 'Source node repoRoot not found', refineStages };
4930
+ if (!repoRoot) return { kind: 'terminal', result: { success: false, error: 'Source node repoRoot not found', refineStages } };
4822
4931
 
4823
4932
  const { execFile } = await import('node:child_process');
4824
4933
  const { promisify } = await import('node:util');
4825
- const execFileAsync = promisify(execFile);
4934
+ const execFileAsync = promisify(execFile) as unknown as RefineExecFileAsync;
4826
4935
 
4827
4936
  const resolveStarted = Date.now();
4828
4937
  const { stdout: branchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: node.workspace, encoding: 'utf8' });
4829
4938
  const branch = branchStdout.trim();
4830
- if (!branch) return { success: false, error: 'Could not determine branch of the worktree node', refineStages };
4939
+ if (!branch) return { kind: 'terminal', result: { success: false, error: 'Could not determine branch of the worktree node', refineStages } };
4831
4940
 
4832
4941
  const { stdout: baseBranchStdout } = await execFileAsync('git', ['branch', '--show-current'], { cwd: repoRoot, encoding: 'utf8' });
4833
4942
  const baseBranch = baseBranchStdout.trim();
@@ -4854,9 +4963,39 @@ export class DaemonCommandRouter {
4854
4963
 
4855
4964
  const { stdout: branchHeadStdout } = await execFileAsync('git', ['rev-parse', branch], { cwd: node.workspace, encoding: 'utf8' });
4856
4965
  const baseHead = baseHeadRaw;
4857
- let branchHead = branchHeadStdout.trim();
4966
+ const branchHead = branchHeadStdout.trim();
4858
4967
  recordMeshRefineStage(refineStages, 'resolve_refs', 'passed', resolveStarted, { branch, baseBranch, baseHead, branchHead, ...(fetchWarning ? { fetchWarning } : {}) });
4859
4968
 
4969
+ return {
4970
+ kind: 'continue',
4971
+ ctx: {
4972
+ meshId,
4973
+ nodeId,
4974
+ args,
4975
+ refineStages,
4976
+ execFileAsync,
4977
+ mesh,
4978
+ node,
4979
+ sourceNode,
4980
+ repoRoot,
4981
+ branch,
4982
+ baseBranch,
4983
+ baseHead,
4984
+ branchHead,
4985
+ validationSummary: undefined as any,
4986
+ patchEquivalence: undefined as any,
4987
+ submoduleReachability: undefined as any,
4988
+ },
4989
+ };
4990
+ }
4991
+
4992
+ /**
4993
+ * validation stage: run the refinery validation gate (typecheck / test /
4994
+ * lint / build per node config) and block on failure or when no allowlisted
4995
+ * command was available. On pass, stores the summary on the context.
4996
+ */
4997
+ private async refineValidationStage(ctx: RefineContext): Promise<RefineStageOutcome> {
4998
+ const { mesh, node, branch, baseBranch, refineStages } = ctx;
4860
4999
  const validationStarted = Date.now();
4861
5000
  const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace, {
4862
5001
  // M2-2: consume the node's persisted bootstrap state; persist re-runs.
@@ -4868,6 +5007,7 @@ export class DaemonCommandRouter {
4868
5007
  .catch(() => { /* persistence is best-effort */ });
4869
5008
  },
4870
5009
  });
5010
+ ctx.validationSummary = validationSummary;
4871
5011
  recordMeshRefineStage(
4872
5012
  refineStages,
4873
5013
  'validation',
@@ -4903,7 +5043,7 @@ export class DaemonCommandRouter {
4903
5043
  tail ? `Output (tail):\n${tail}` : '',
4904
5044
  ].filter(Boolean).join('\n');
4905
5045
  };
4906
- return {
5046
+ return { kind: 'terminal', result: {
4907
5047
  success: false,
4908
5048
  code: validationSummary.failureCode || 'validation_failed',
4909
5049
  convergenceStatus: 'blocked_review',
@@ -4920,10 +5060,10 @@ export class DaemonCommandRouter {
4920
5060
  validation: 'failed',
4921
5061
  status: 'blocked_review',
4922
5062
  },
4923
- };
5063
+ } };
4924
5064
  }
4925
5065
  if (validationSummary.status === 'skipped') {
4926
- return {
5066
+ return { kind: 'terminal', result: {
4927
5067
  success: false,
4928
5068
  code: 'validation_unavailable',
4929
5069
  convergenceStatus: 'blocked_review',
@@ -4940,9 +5080,22 @@ export class DaemonCommandRouter {
4940
5080
  validation: 'unavailable',
4941
5081
  status: 'blocked_review',
4942
5082
  },
4943
- };
5083
+ } };
4944
5084
  }
4945
5085
 
5086
+ return { kind: 'continue', ctx };
5087
+ }
5088
+
5089
+ /**
5090
+ * patch_equivalence stage: preflight that the worktree branch's cumulative
5091
+ * patch is equivalent to base+branch. On a "behind base" branch, auto-rebase
5092
+ * once and re-check; on an empty merge-tree with real branch changes, treat as
5093
+ * already-merged-via-another-path and short-circuit to cleanup. Mutates the
5094
+ * context's branchHead (after rebase) and patchEquivalence (rebased gate).
5095
+ */
5096
+ private async refinePatchEquivalenceStage(ctx: RefineContext): Promise<RefineStageOutcome> {
5097
+ const { meshId, nodeId, args, repoRoot, baseHead, node, branch, baseBranch, validationSummary, refineStages, execFileAsync } = ctx;
5098
+ let branchHead = ctx.branchHead;
4946
5099
  const patchEquivalenceStarted = Date.now();
4947
5100
  let patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
4948
5101
  recordMeshRefineStage(refineStages, 'patch_equivalence', patchEquivalence.status, patchEquivalenceStarted, {
@@ -4985,7 +5138,7 @@ export class DaemonCommandRouter {
4985
5138
  patchEquivalence = rebasedPatchEquivalence;
4986
5139
  didAutoRebase = true;
4987
5140
  } else {
4988
- return {
5141
+ return { kind: 'terminal', result: {
4989
5142
  success: false,
4990
5143
  code: 'needs_rebase',
4991
5144
  convergenceStatus: 'blocked_review',
@@ -5004,14 +5157,14 @@ export class DaemonCommandRouter {
5004
5157
  patchEquivalence: 'failed',
5005
5158
  status: 'blocked_review',
5006
5159
  },
5007
- };
5160
+ } };
5008
5161
  }
5009
5162
  } catch (rebaseErr: any) {
5010
5163
  try { execFileSync('git', ['rebase', '--abort'], { cwd: node.workspace, stdio: 'ignore' }); } catch { /* ignore */ }
5011
5164
  recordMeshRefineStage(refineStages, 'patch_equivalence_after_auto_rebase', 'failed', autoRebaseStarted, {
5012
5165
  error: rebaseErr?.message || String(rebaseErr),
5013
5166
  });
5014
- return {
5167
+ return { kind: 'terminal', result: {
5015
5168
  success: false,
5016
5169
  code: 'needs_rebase_with_conflicts',
5017
5170
  convergenceStatus: 'blocked_review',
@@ -5030,7 +5183,7 @@ export class DaemonCommandRouter {
5030
5183
  patchEquivalence: 'failed',
5031
5184
  status: 'blocked_review',
5032
5185
  },
5033
- };
5186
+ } };
5034
5187
  }
5035
5188
  }
5036
5189
 
@@ -5046,7 +5199,7 @@ export class DaemonCommandRouter {
5046
5199
  // is a degenerate worktree case, not an "already merged" scenario.
5047
5200
  const alreadyMergedViaOtherPath = !patchEquivalence.actualPatchId && !!patchEquivalence.expectedPatchId;
5048
5201
  if (!didAutoRebase && !alreadyMergedViaOtherPath) {
5049
- return {
5202
+ return { kind: 'terminal', result: {
5050
5203
  success: false,
5051
5204
  code: 'patch_equivalence_failed',
5052
5205
  convergenceStatus: 'blocked_review',
@@ -5065,7 +5218,7 @@ export class DaemonCommandRouter {
5065
5218
  patchEquivalence: 'failed',
5066
5219
  status: 'blocked_review',
5067
5220
  },
5068
- };
5221
+ } };
5069
5222
  }
5070
5223
 
5071
5224
  if (!didAutoRebase && alreadyMergedViaOtherPath) {
@@ -5094,7 +5247,7 @@ export class DaemonCommandRouter {
5094
5247
  payload: { alreadyMergedViaOtherPath: true, branch, into: baseBranch, validationSummary, patchEquivalence },
5095
5248
  });
5096
5249
  } catch { /* ledger append is best-effort */ }
5097
- return {
5250
+ return { kind: 'terminal', result: {
5098
5251
  success: removeResult?.success !== false,
5099
5252
  code: 'already_merged',
5100
5253
  merged: false,
@@ -5116,10 +5269,23 @@ export class DaemonCommandRouter {
5116
5269
  patchEquivalence: 'already_merged',
5117
5270
  status: removeResult?.success === false ? 'merged_cleanup_failed' : 'merged_to_main',
5118
5271
  },
5119
- };
5272
+ } };
5120
5273
  }
5121
5274
  }
5122
5275
 
5276
+ ctx.branchHead = branchHead;
5277
+ ctx.patchEquivalence = patchEquivalence;
5278
+ return { kind: 'continue', ctx };
5279
+ }
5280
+
5281
+ /**
5282
+ * submodule_reachability stage: verify every submodule gitlink commit that
5283
+ * would land via the merge is reachable from its configured remote main
5284
+ * branch (optionally auto-publishing when policy allows). Blocks the merge
5285
+ * when any commit is unreachable. Stores the result on the context.
5286
+ */
5287
+ private async refineSubmoduleReachabilityStage(ctx: RefineContext): Promise<RefineStageOutcome> {
5288
+ const { mesh, node, repoRoot, branch, baseBranch, branchHead, validationSummary, patchEquivalence, refineStages } = ctx;
5123
5289
  const submoduleReachabilityStarted = Date.now();
5124
5290
  const autoPublishSubmoduleMainCommits = resolveRefineryAutoPublishSubmoduleMainCommits(mesh, node.workspace);
5125
5291
  const submoduleReachability = await runMeshRefineSubmoduleReachabilityGate(repoRoot, patchEquivalence.mergedTree || branchHead, {
@@ -5176,7 +5342,7 @@ export class DaemonCommandRouter {
5176
5342
  });
5177
5343
  if (submoduleReachability.status === 'failed') {
5178
5344
  const nextStep = buildSubmodulePublishRequiredNextStep(submoduleReachability.unreachable);
5179
- return {
5345
+ return { kind: 'terminal', result: {
5180
5346
  success: false,
5181
5347
  code: 'submodule_reachability_failed',
5182
5348
  convergenceStatus: 'blocked_review',
@@ -5224,9 +5390,21 @@ export class DaemonCommandRouter {
5224
5390
  reason: 'submodule_publish_required',
5225
5391
  nextStep,
5226
5392
  },
5227
- };
5393
+ } };
5228
5394
  }
5229
5395
 
5396
+ ctx.submoduleReachability = submoduleReachability;
5397
+ return { kind: 'continue', ctx };
5398
+ }
5399
+
5400
+ /**
5401
+ * effective_diff stage (no-op guard): block a silent no-op merge where the
5402
+ * branch produces no effective root-tree diff against base — typically a
5403
+ * submodule that has commits but whose root-level gitlink (pointer) bump was
5404
+ * never committed, so the merge would land nothing real on main.
5405
+ */
5406
+ private async refineEffectiveDiffStage(ctx: RefineContext): Promise<RefineStageOutcome> {
5407
+ const { repoRoot, baseHead, branchHead, branch, baseBranch, validationSummary, patchEquivalence, refineStages } = ctx;
5230
5408
  // No-op guard: block a silent no-op merge where the root tree is identical to base.
5231
5409
  // This catches the trap where a submodule has commits but the root branch never
5232
5410
  // committed the gitlink (oss-pointer) bump — merging would report success while the
@@ -5248,7 +5426,7 @@ export class DaemonCommandRouter {
5248
5426
  hintLines.length ? `Submodules with uncommitted pointer bumps:\n${hintLines.join('\n')}` : '',
5249
5427
  `Fix: commit the submodule pointer bump on '${branch}' (git add <submodule-path> && git commit), then re-run refine.`,
5250
5428
  ].filter(Boolean).join('\n');
5251
- return {
5429
+ return { kind: 'terminal', result: {
5252
5430
  success: false,
5253
5431
  code: 'no_effective_diff',
5254
5432
  convergenceStatus: 'blocked_review',
@@ -5271,9 +5449,20 @@ export class DaemonCommandRouter {
5271
5449
  reason: 'no_effective_diff',
5272
5450
  ...(effectiveDiff.submoduleHints?.length ? { submoduleHints: effectiveDiff.submoduleHints } : {}),
5273
5451
  },
5274
- };
5452
+ } };
5275
5453
  }
5276
5454
 
5455
+ return { kind: 'continue', ctx };
5456
+ }
5457
+
5458
+ /**
5459
+ * merge + finalize stage: perform the --no-ff merge, align submodule
5460
+ * checkouts after merge, clean up (remove) the worktree node per policy,
5461
+ * append the refinery ledger entry, and (unless approval is required) push the
5462
+ * base branch. Always terminal — produces the final CommandRouterResult.
5463
+ */
5464
+ private async refineMergeAndFinalizeStage(ctx: RefineContext): Promise<RefineStageOutcome> {
5465
+ const { meshId, nodeId, args, repoRoot, baseHead, node, branch, baseBranch, sourceNode, validationSummary, patchEquivalence, submoduleReachability, mesh, refineStages, execFileAsync } = ctx;
5277
5466
  let mergeResult: Record<string, unknown> | undefined;
5278
5467
  const mergeStarted = Date.now();
5279
5468
  try {
@@ -5290,7 +5479,7 @@ export class DaemonCommandRouter {
5290
5479
  stdout: truncateValidationOutput(e?.stdout),
5291
5480
  stderr: truncateValidationOutput(e?.stderr),
5292
5481
  });
5293
- return {
5482
+ return { kind: 'terminal', result: {
5294
5483
  success: false,
5295
5484
  error: `Merge failed (conflicts?): ${e.message}`,
5296
5485
  validationSummary,
@@ -5305,7 +5494,7 @@ export class DaemonCommandRouter {
5305
5494
  patchEquivalence: 'passed',
5306
5495
  status: 'not_mergeable',
5307
5496
  },
5308
- };
5497
+ } };
5309
5498
  }
5310
5499
 
5311
5500
  const submoduleAlignmentStarted = Date.now();
@@ -5325,7 +5514,7 @@ export class DaemonCommandRouter {
5325
5514
  });
5326
5515
  }
5327
5516
  if (submoduleAlignment.status === 'failed') {
5328
- return {
5517
+ return { kind: 'terminal', result: {
5329
5518
  success: false,
5330
5519
  code: 'post_merge_submodule_alignment_failed',
5331
5520
  error: 'Refinery merge completed but post-merge submodule checkout alignment failed; run the reported git submodule update command and re-check base workspace status.',
@@ -5351,7 +5540,7 @@ export class DaemonCommandRouter {
5351
5540
  status: 'post_merge_alignment_failed',
5352
5541
  nextStep: submoduleAlignment.command || 'Run git submodule update --init --recursive for the reported path(s), then re-check base workspace status.',
5353
5542
  },
5354
- };
5543
+ } };
5355
5544
  }
5356
5545
 
5357
5546
  const cleanupStarted = Date.now();
@@ -5431,7 +5620,7 @@ export class DaemonCommandRouter {
5431
5620
  };
5432
5621
 
5433
5622
  if (removeResult?.success === false) {
5434
- return {
5623
+ return { kind: 'terminal', result: {
5435
5624
  success: false,
5436
5625
  code: 'cleanup_failed',
5437
5626
  error: 'Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.',
@@ -5447,7 +5636,7 @@ export class DaemonCommandRouter {
5447
5636
  refineStages,
5448
5637
  ...(ledgerError ? { ledgerError } : {}),
5449
5638
  finalBranchConvergenceState,
5450
- };
5639
+ } };
5451
5640
  }
5452
5641
 
5453
5642
  // Push logic: after a successful merge, either auto-push or surface push info
@@ -5474,7 +5663,7 @@ export class DaemonCommandRouter {
5474
5663
  }
5475
5664
  }
5476
5665
 
5477
- return {
5666
+ return { kind: 'terminal', result: {
5478
5667
  success: true,
5479
5668
  merged: true,
5480
5669
  branch,
@@ -5496,10 +5685,7 @@ export class DaemonCommandRouter {
5496
5685
  pushCommand: `git push origin ${baseBranch}`,
5497
5686
  pushNote: 'requireApprovalForPush is enabled — run the push command or obtain user approval before pushing.',
5498
5687
  }),
5499
- };
5500
- } catch (e: any) {
5501
- return { success: false, error: e.message, refineStages };
5502
- }
5688
+ } };
5503
5689
  }
5504
5690
 
5505
5691
  /**
@@ -6231,1179 +6417,19 @@ export class DaemonCommandRouter {
6231
6417
  return await medFamilyHandler(this.buildMedFamilyContext(), args);
6232
6418
  }
6233
6419
 
6234
- switch (cmd) {
6235
- // ─── CLI / ACP commands ───
6236
- case 'mesh_forward_event': {
6237
- return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager } as any, args as Record<string, unknown>);
6238
- }
6239
-
6240
- case 'get_pending_mesh_events': {
6241
- const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
6242
- // (B3) Respect coordinatorDaemonId when the caller declares it
6243
- // so unicast events route to the right coordinator instead of
6244
- // being silently consumed by the first drainer.
6245
- const coordinatorDaemonId = typeof args?.coordinatorDaemonId === 'string' && args.coordinatorDaemonId.trim()
6246
- ? args.coordinatorDaemonId.trim()
6247
- : undefined;
6248
- const events = drainPendingMeshCoordinatorEvents(meshId || undefined, coordinatorDaemonId);
6249
- return { success: true, events };
6250
- }
6251
-
6252
- case 'interactive_prompt_response': {
6253
- const sessionId = typeof args?.targetSessionId === 'string' && args.targetSessionId.trim()
6254
- ? args.targetSessionId.trim()
6255
- : typeof args?.sessionId === 'string' && args.sessionId.trim()
6256
- ? args.sessionId.trim()
6257
- : '';
6258
- if (!sessionId) return { success: false, error: 'targetSessionId required' };
6259
- const response = normalizeInteractivePromptResponse(args?.response ?? args);
6260
- const instance = this.deps.instanceManager.getInstance(sessionId);
6261
- if (!instance) return { success: false, error: `No running instance for session ${sessionId}` };
6262
- this.deps.instanceManager.sendEvent(sessionId, 'interactive_prompt_response', response);
6263
- return { success: true };
6264
- }
6265
-
6266
- // ─── Mesh Coordinator Launch ───
6267
- case 'launch_mesh_coordinator': {
6268
- const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
6269
- let cliType = typeof args?.cliType === 'string' ? args.cliType.trim() : '';
6270
- // Optional per-launch system-prompt addition. Dashboard or API
6271
- // callers (e.g. when spawning a mesh-node-specific coordinator)
6272
- // can pass extra context that gets appended to the rendered
6273
- // default prompt under the "## Additional Context" section.
6274
- // Going through buildCoordinatorSystemPrompt's userInstruction
6275
- // means user-level override files (~/.adhdev/coordinator-prompts)
6276
- // and this per-launch addition compose cleanly: an override
6277
- // wins outright, but if there's no override, the default
6278
- // prompt + the optional append.md file + this extra context
6279
- // all stack in declared order.
6280
- const extraSystemPrompt = typeof args?.extraSystemPrompt === 'string'
6281
- ? args.extraSystemPrompt.trim()
6282
- : '';
6283
- if (!meshId) return { success: false, error: 'meshId required' };
6284
-
6285
- try {
6286
- const { buildCoordinatorSystemPrompt } = await import('../mesh/coordinator-prompt.js');
6287
- const { buildMissionPromptSection } = await import('../mesh/mesh-missions.js');
6288
- // M3-3: inject the active mission summary into the coordinator prompt.
6289
- // Best-effort — a store failure must not block coordinator launch.
6290
- const buildMissionSectionBestEffort = (id: string): string => {
6291
- try { return buildMissionPromptSection(id); } catch { return ''; }
6292
- };
6293
-
6294
- // Support inline mesh data from cloud (bypasses local meshes.json lookup)
6295
- let mesh: any;
6296
- if (args?.inlineMesh && typeof args.inlineMesh === 'object') {
6297
- mesh = args.inlineMesh;
6298
- // Cache cloud mesh so the MCP server can retrieve it via get_mesh
6299
- this.inlineMeshCache.set(meshId, mesh);
6300
- } else {
6301
- const { getMesh } = await import('../config/mesh-config.js');
6302
- mesh = getMesh(meshId);
6303
- }
6304
- if (!mesh) return { success: false, error: 'Mesh not found' };
6305
- const meshHost = resolveMeshHostStatus(mesh);
6306
- if (!meshHost.canOwnCoordinator) {
6307
- return {
6308
- success: false,
6309
- ...buildMeshHostRequiredFailure(mesh, 'coordinator launch'),
6310
- meshId,
6311
- cliType,
6312
- };
6313
- }
6314
- if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: 'No nodes in mesh' };
6315
-
6316
- const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === 'string'
6317
- ? args.coordinatorNodeId.trim()
6318
- : '';
6319
- const preferredCoordinatorNodeId = requestedCoordinatorNodeId
6320
- || (typeof mesh.coordinator?.preferredNodeId === 'string' ? mesh.coordinator.preferredNodeId.trim() : '');
6321
- const coordinatorNode = preferredCoordinatorNodeId
6322
- ? mesh.nodes.find((node: any) => node?.id === preferredCoordinatorNodeId || node?.nodeId === preferredCoordinatorNodeId)
6323
- : mesh.nodes[0];
6324
- if (!coordinatorNode) {
6325
- return {
6326
- success: false,
6327
- code: 'mesh_coordinator_node_not_found',
6328
- error: `Coordinator node ${preferredCoordinatorNodeId} was not found in mesh`,
6329
- meshId,
6330
- cliType,
6331
- };
6332
- }
6333
- const sessionHostRecords = this.deps.sessionHostControl?.listSessions
6334
- ? await this.deps.sessionHostControl.listSessions().catch(() => [])
6335
- : [];
6336
- const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
6337
- const workspace = readLiveMeshNodeWorkspace({
6338
- meshId,
6339
- nodeId: String(normalizeMeshNodeId(coordinatorNode) || preferredCoordinatorNodeId || ''),
6340
- liveSessionRecords: liveMeshSessions,
6341
- allowCoordinatorSession: true,
6342
- }) || (typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '');
6343
- if (!workspace) return { success: false, error: 'Coordinator node workspace required', meshId, cliType };
6344
- if (!cliType) {
6345
- const resolved = await resolveProviderTypeFromPriority({
6346
- nodeId: String(normalizeMeshNodeId(coordinatorNode) || preferredCoordinatorNodeId || 'coordinator'),
6347
- providerPriority: readProviderPriorityFromPolicy(coordinatorNode.policy),
6348
- providerLoader: this.deps.providerLoader,
6349
- onStatusChange: this.deps.onStatusChange,
6350
- });
6351
- if (!resolved.providerType) {
6352
- return {
6353
- success: false,
6354
- code: 'mesh_coordinator_provider_priority_unusable',
6355
- error: resolved.error || 'No usable provider found from node providerPriority',
6356
- meshId,
6357
- cliType,
6358
- workspace,
6359
- };
6360
- }
6361
- cliType = resolved.providerType;
6362
- }
6363
- const providerMeta = this.deps.providerLoader.resolve?.(cliType) || this.deps.providerLoader.getMeta(cliType);
6364
- const coordinatorSetup = resolveMeshCoordinatorSetup({
6365
- provider: providerMeta,
6366
- cliType,
6367
- meshId,
6368
- workspace,
6369
- });
6370
-
6371
- if (coordinatorSetup.kind === 'unsupported') {
6372
- return {
6373
- success: false,
6374
- code: 'mesh_coordinator_unsupported',
6375
- error: coordinatorSetup.reason,
6376
- meshId,
6377
- cliType,
6378
- workspace,
6379
- };
6380
- }
6381
-
6382
- if (coordinatorSetup.kind === 'manual') {
6383
- return {
6384
- success: false,
6385
- code: 'mesh_coordinator_manual_mcp_setup_required',
6386
- error: coordinatorSetup.instructions,
6387
- meshId,
6388
- cliType,
6389
- workspace,
6390
- meshCoordinatorSetup: coordinatorSetup,
6391
- };
6392
- }
6393
-
6394
- // ─── CLI-command MCP registration (Codex, Gemini CLI) ───────────
6395
- if (coordinatorSetup.kind === 'cli_command') {
6396
- // Build coordinator prompt first — fail closed on errors.
6397
- let cliCmdSystemPrompt = '';
6398
- try {
6399
- cliCmdSystemPrompt = buildCoordinatorSystemPrompt({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id) });
6400
- } catch (error: any) {
6401
- const message = error?.message || String(error);
6402
- LOG.error('MeshCoordinator', `Failed to build coordinator prompt: ${message}`);
6403
- return {
6404
- success: false,
6405
- code: 'mesh_coordinator_prompt_failed',
6406
- error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
6407
- meshId, cliType, workspace,
6408
- };
6409
- }
6410
-
6411
- // Run the provider's MCP registration command under a
6412
- // PTY. Some providers (agy, future bubbletea CLIs)
6413
- // refuse to run without /dev/tty, so pipe-only
6414
- // execFileSync silently no-ops the registration and
6415
- // the coordinator ends up without any mcp tools. With
6416
- // a real PTY the registration goes through and the
6417
- // exit code tells us whether it actually persisted.
6418
- let mcpRegistrationOk = false;
6419
- let mcpRegistrationFailure: {
6420
- command: string;
6421
- output: string;
6422
- exitCode: number | null;
6423
- signal: number | null;
6424
- timedOut: boolean;
6425
- } | null = null;
6426
- try {
6427
- const { buildMeshCoordinatorRegistrationPlan, execUnderPty } = await import('./mesh-coordinator.js');
6428
- const registrationPlan = buildMeshCoordinatorRegistrationPlan(
6429
- cliType,
6430
- coordinatorSetup.serverName,
6431
- coordinatorSetup.command,
6432
- );
6433
- for (const step of registrationPlan) {
6434
- const renderedCommand = [step.command, ...step.args].join(' ');
6435
- LOG.info('MeshCoordinator', `Running MCP ${step.label} (pty): ${renderedCommand}`);
6436
- const ptyResult = await execUnderPty(step.command, step.args, { cwd: workspace, timeoutMs: 20_000 });
6437
- if (ptyResult.exitCode === 0 && !ptyResult.timedOut) {
6438
- if (step.required) mcpRegistrationOk = true;
6439
- continue;
6440
- }
6441
- LOG.warn('MeshCoordinator', `MCP ${step.label} failed exit=${ptyResult.exitCode} signal=${ptyResult.signal} timedOut=${ptyResult.timedOut} — output:\n${ptyResult.output.slice(-2000)}`);
6442
- if (step.required) {
6443
- mcpRegistrationFailure = {
6444
- command: renderedCommand,
6445
- output: ptyResult.output.slice(-2000),
6446
- exitCode: ptyResult.exitCode,
6447
- signal: ptyResult.signal,
6448
- timedOut: ptyResult.timedOut,
6449
- };
6450
- break;
6451
- }
6452
- }
6453
- } catch (error: any) {
6454
- LOG.warn('MeshCoordinator', `MCP registration command failed: ${error?.message || error}`);
6455
- mcpRegistrationFailure = {
6456
- command: coordinatorSetup.command,
6457
- output: error?.message || String(error),
6458
- exitCode: null,
6459
- signal: null,
6460
- timedOut: false,
6461
- };
6462
- }
6463
-
6464
- if (!mcpRegistrationOk) {
6465
- return {
6466
- success: false,
6467
- code: 'mesh_coordinator_mcp_registration_failed',
6468
- error: `Could not register ${coordinatorSetup.serverName}; coordinator session was not launched`,
6469
- meshId,
6470
- cliType,
6471
- workspace,
6472
- registration: mcpRegistrationFailure,
6473
- };
6474
- }
6475
-
6476
- // Codex gives repo-local .mcp.json precedence over its
6477
- // global `codex mcp add` registration. Refresh an
6478
- // existing ADHDev entry so a stale workspace command
6479
- // cannot shadow the registration we just verified.
6480
- if (cliType === 'codex-cli') {
6481
- const repoMcpConfigPath = pathJoin(workspace, '.mcp.json');
6482
- if (fs.existsSync(repoMcpConfigPath)) {
6483
- try {
6484
- const repoMcpConfig = parseMeshCoordinatorMcpConfig(
6485
- fs.readFileSync(repoMcpConfigPath, 'utf-8'),
6486
- 'claude_mcp_json',
6487
- );
6488
- const existingServers = repoMcpConfig.mcpServers;
6489
- if (
6490
- existingServers
6491
- && typeof existingServers === 'object'
6492
- && !Array.isArray(existingServers)
6493
- && existingServers[coordinatorSetup.serverName]
6494
- ) {
6495
- fs.writeFileSync(repoMcpConfigPath, serializeMeshCoordinatorMcpConfig({
6496
- ...repoMcpConfig,
6497
- mcpServers: {
6498
- ...existingServers,
6499
- [coordinatorSetup.serverName]: coordinatorSetup.mcpServer,
6500
- },
6501
- }, 'claude_mcp_json'), 'utf-8');
6502
- LOG.info('MeshCoordinator', `Refreshed repo-local ${repoMcpConfigPath} entry for ${coordinatorSetup.serverName}`);
6503
- }
6504
- } catch (error: any) {
6505
- return {
6506
- success: false,
6507
- code: 'mesh_coordinator_config_write_failed',
6508
- error: `Could not refresh repo-local MCP config: ${error?.message || error}`,
6509
- meshId,
6510
- cliType,
6511
- workspace,
6512
- };
6513
- }
6514
- }
6515
- }
6516
-
6517
- // Inject system prompt declaratively from provider.v1.json.
6518
- const cliCmdArgs: string[] = [];
6519
- const cliCmdEnv: Record<string, string> = {};
6520
- let cliCmdContextFilePath: string | undefined;
6521
- if (cliCmdSystemPrompt) {
6522
- const { applyMeshCoordinatorSystemPromptInjection } = await import('./mesh-coordinator.js');
6523
- const effect = applyMeshCoordinatorSystemPromptInjection(
6524
- cliCmdSystemPrompt,
6525
- providerMeta?.meshCoordinator?.systemPromptInjection,
6526
- { cliArgs: cliCmdArgs, launchEnv: cliCmdEnv, workspace, cliType },
6527
- );
6528
- cliCmdContextFilePath = effect.contextFilePath;
6529
- }
6530
-
6531
- const cliCmdLaunch: any = await this.deps.cliManager.handleCliCommand('launch_cli', {
6532
- cliType,
6533
- dir: workspace,
6534
- cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : undefined,
6535
- env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : undefined,
6536
- settings: { meshCoordinatorFor: meshId },
6537
- });
6538
-
6539
- // R48 inject-then-remove. Spawn was just kicked off above; agy and
6540
- // gemini-cli read AGENTS.md / GEMINI.md exactly once at startup and
6541
- // cache it for the rest of the session, so we can safely strip
6542
- // the wrapper from disk shortly after launch. That keeps any
6543
- // worker session launched into the same workspace later from
6544
- // picking up our wrapper block.
6545
- if (cliCmdLaunch?.success && cliCmdContextFilePath) {
6546
- const stripPath = cliCmdContextFilePath;
6547
- setTimeout(() => {
6548
- void import('./mesh-coordinator.js').then(({ stripCoordinatorWrapperFile }) => {
6549
- stripCoordinatorWrapperFile(stripPath);
6550
- LOG.info('MeshCoordinator', `Stripped wrapper from ${stripPath} after launch settle (cli_command)`);
6551
- }).catch(() => { /* best-effort */ });
6552
- }, 5000);
6553
- }
6554
-
6555
- if (!cliCmdLaunch?.success) {
6556
- return { success: false, error: cliCmdLaunch?.error || 'Failed to launch CLI session' };
6557
- }
6558
-
6559
- LOG.info('MeshCoordinator', `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
6560
- const cliCmdSessionId = cliCmdLaunch.sessionId || cliCmdLaunch.id;
6561
- if (cliCmdSessionId) {
6562
- const cliCmdInjectionDecl = providerMeta?.meshCoordinator?.systemPromptInjection;
6563
- registerMeshCoordinator({
6564
- meshId,
6565
- sessionId: cliCmdSessionId,
6566
- workspace,
6567
- startedAt: Date.now(),
6568
- cliType,
6569
- systemPrompt: cliCmdSystemPrompt || undefined,
6570
- extraSystemPrompt: extraSystemPrompt || undefined,
6571
- injection: cliCmdInjectionDecl ? {
6572
- mode: cliCmdInjectionDecl.mode,
6573
- target: 'flag' in cliCmdInjectionDecl ? cliCmdInjectionDecl.flag
6574
- : 'name' in cliCmdInjectionDecl ? cliCmdInjectionDecl.name
6575
- : 'path' in cliCmdInjectionDecl ? cliCmdInjectionDecl.path
6576
- : undefined,
6577
- } : undefined,
6578
- });
6579
- }
6580
- try {
6581
- const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
6582
- appendLedgerEntry(meshId, {
6583
- kind: 'coordinator_started',
6584
- sessionId: cliCmdSessionId,
6585
- providerType: cliType,
6586
- payload: { workspace },
6587
- });
6588
- } catch { /* best-effort */ }
6589
-
6590
- return {
6591
- success: true,
6592
- meshId,
6593
- cliType,
6594
- workspace,
6595
- sessionId: cliCmdSessionId,
6596
- mcpRegistered: mcpRegistrationOk,
6597
- };
6598
- }
6599
-
6600
- const configFormat = coordinatorSetup.configFormat as MeshCoordinatorConfigFormat;
6601
- if (configFormat !== 'claude_mcp_json' && configFormat !== 'hermes_config_yaml') {
6602
- return {
6603
- success: false,
6604
- code: 'mesh_coordinator_unsupported',
6605
- error: `Unsupported auto-import MCP config format: ${String(coordinatorSetup.configFormat)}`,
6606
- meshId,
6607
- cliType,
6608
- workspace,
6609
- };
6610
- }
6611
-
6612
- // Build the coordinator prompt before mutating workspace config or launching.
6613
- // Prompt generation failures are configuration/data-shape errors; fail closed so
6614
- // broken mesh state is visible instead of silently launching with weaker rules.
6615
- let systemPrompt = '';
6616
- try {
6617
- systemPrompt = buildCoordinatorSystemPrompt({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id) });
6618
- } catch (error: any) {
6619
- const message = error?.message || String(error);
6620
- LOG.error('MeshCoordinator', `Failed to build coordinator prompt: ${message}`);
6621
- return {
6622
- success: false,
6623
- code: 'mesh_coordinator_prompt_failed',
6624
- error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
6625
- meshId,
6626
- cliType,
6627
- workspace,
6628
- };
6629
- }
6630
-
6631
- // 1. Write provider-declared MCP config for CLIs that auto-import it.
6632
- const { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } = await import('fs');
6633
- const { dirname } = await import('path');
6634
- const mcpConfigPath = coordinatorSetup.configPath;
6635
- const hermesManualFallback = cliType === 'hermes-cli' && configFormat === 'hermes_config_yaml'
6636
- ? createHermesManualMeshCoordinatorSetup(meshId, workspace)
6637
- : null;
6638
- let hermesBaseConfig: { config: Record<string, any>; sourceHome: string; sourceConfigPath: string } | null = null;
6639
- if (hermesManualFallback) {
6640
- try {
6641
- hermesBaseConfig = loadHermesCoordinatorBaseConfig(mcpConfigPath);
6642
- } catch (error: any) {
6643
- const message = `Failed to parse Hermes base config for automatic coordinator setup: ${error?.message || error}`;
6644
- LOG.error('MeshCoordinator', message);
6645
- return { success: false, code: 'mesh_coordinator_config_parse_failed', error: message, meshId, cliType, workspace };
6646
- }
6647
- }
6648
- const returnManualFallback = (message: string) => ({
6649
- success: false,
6650
- code: 'mesh_coordinator_manual_mcp_setup_required',
6651
- error: message,
6652
- meshId,
6653
- cliType,
6654
- workspace,
6655
- meshCoordinatorSetup: hermesManualFallback,
6656
- });
6657
-
6658
- // Merge ADHDev mesh server into existing config.
6659
- // Pass full mesh data as env var so the MCP server can bootstrap
6660
- // without depending on meshes.json or a running daemon.
6661
- const mcpServerEntry: Record<string, any> = {
6662
- command: coordinatorSetup.mcpServer.command,
6663
- args: coordinatorSetup.mcpServer.args,
6664
- };
6665
- if (args?.inlineMesh) {
6666
- const modeArgIndex = coordinatorSetup.mcpServer.args.findIndex((value: string) => value === '--mode');
6667
- const mcpTransport = modeArgIndex >= 0 ? coordinatorSetup.mcpServer.args[modeArgIndex + 1] : 'ipc';
6668
- mcpServerEntry.env = {
6669
- ADHDEV_INLINE_MESH: JSON.stringify(mesh),
6670
- ADHDEV_MCP_TRANSPORT: mcpTransport === 'local' ? 'local' : 'ipc',
6671
- };
6672
- }
6673
-
6674
- try {
6675
- mkdirSync(dirname(mcpConfigPath), { recursive: true });
6676
- } catch (error: any) {
6677
- const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
6678
- LOG.error('MeshCoordinator', message);
6679
- if (hermesManualFallback) return returnManualFallback(message);
6680
- return { success: false, code: 'mesh_coordinator_config_write_failed', error: message, meshId, cliType, workspace };
6681
- }
6682
-
6683
- // Backup existing MCP config if present.
6684
- const hadExistingMcpConfig = existsSync(mcpConfigPath);
6685
- let existingMcpConfig: Record<string, any> = hermesBaseConfig?.config || {};
6686
- if (hermesBaseConfig) {
6687
- copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname(mcpConfigPath));
6688
- }
6689
- if (hadExistingMcpConfig) {
6690
- try {
6691
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync(mcpConfigPath, 'utf-8'), configFormat);
6692
- const existingCoordinatorConfig = hermesManualFallback
6693
- ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig)
6694
- : parsedExistingMcpConfig;
6695
- existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
6696
- copyFileSync(mcpConfigPath, mcpConfigPath + '.backup');
6697
- } catch (error: any) {
6698
- LOG.error('MeshCoordinator', `Failed to parse existing MCP config ${mcpConfigPath}: ${error?.message || error}`);
6699
- return {
6700
- success: false,
6701
- code: 'mesh_coordinator_config_parse_failed',
6702
- error: `Failed to parse existing MCP config at ${mcpConfigPath}`,
6703
- };
6704
- }
6705
- }
6706
-
6707
- const mcpServersKey = getMcpServersKey(configFormat);
6708
- const existingServers = existingMcpConfig[mcpServersKey];
6709
- const mcpConfig = {
6710
- ...existingMcpConfig,
6711
- [mcpServersKey]: {
6712
- ...(existingServers && typeof existingServers === 'object' && !Array.isArray(existingServers) ? existingServers : {}),
6713
- [coordinatorSetup.serverName]: mcpServerEntry,
6714
- },
6715
- };
6716
- try {
6717
- writeFileSync(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), 'utf-8');
6718
- } catch (error: any) {
6719
- const message = `Could not write MCP config for automatic setup: ${error?.message || error}`;
6720
- LOG.error('MeshCoordinator', message);
6721
- if (hermesManualFallback) return returnManualFallback(message);
6722
- return { success: false, code: 'mesh_coordinator_config_write_failed', error: message, meshId, cliType, workspace };
6723
- }
6724
- LOG.info('MeshCoordinator', `Wrote ${mcpConfigPath} with ${coordinatorSetup.serverName} server`);
6725
-
6726
- const cliArgs: string[] = [];
6727
- const launchEnv: Record<string, string> = {};
6728
- if (configFormat === 'hermes_config_yaml') {
6729
- launchEnv.HERMES_HOME = dirname(mcpConfigPath);
6730
- launchEnv.HERMES_IGNORE_USER_CONFIG = '';
6731
- }
6732
- let autoImportContextFilePath: string | undefined;
6733
- if (systemPrompt) {
6734
- const { applyMeshCoordinatorSystemPromptInjection } = await import('./mesh-coordinator.js');
6735
- const effect = applyMeshCoordinatorSystemPromptInjection(
6736
- systemPrompt,
6737
- providerMeta?.meshCoordinator?.systemPromptInjection,
6738
- { cliArgs, launchEnv, workspace, cliType },
6739
- );
6740
- autoImportContextFilePath = effect.contextFilePath;
6741
- }
6742
- if (cliType === 'claude-cli') {
6743
- cliArgs.push('--mcp-config', coordinatorSetup.configPath);
6744
- }
6745
-
6746
- // 3. Launch CLI session via existing cliManager.
6747
- // Provider-specific prompt injection remains fail-closed: Claude gets
6748
- // explicit CLI args, while Hermes reads HERMES_EPHEMERAL_SYSTEM_PROMPT.
6749
- const launchResult: any = await this.deps.cliManager.handleCliCommand('launch_cli', {
6750
- cliType,
6751
- dir: workspace,
6752
- cliArgs: cliArgs.length > 0 ? cliArgs : undefined,
6753
- env: Object.keys(launchEnv).length > 0 ? launchEnv : undefined,
6754
- settings: {
6755
- meshCoordinatorFor: meshId
6756
- }
6757
- });
6758
-
6759
- // R48 inject-then-remove. See the cli_command branch for context;
6760
- // same idea: strip the wrapper from disk ~5s after launch so the
6761
- // user's AGENTS.md / GEMINI.md is untouched the moment any
6762
- // worker session opens up in the same workspace.
6763
- if (launchResult?.success && autoImportContextFilePath) {
6764
- const stripPath = autoImportContextFilePath;
6765
- setTimeout(() => {
6766
- void import('./mesh-coordinator.js').then(({ stripCoordinatorWrapperFile }) => {
6767
- stripCoordinatorWrapperFile(stripPath);
6768
- LOG.info('MeshCoordinator', `Stripped wrapper from ${stripPath} after launch settle (auto_import)`);
6769
- }).catch(() => { /* best-effort */ });
6770
- }, 5000);
6771
- }
6772
-
6773
- if (!launchResult?.success) {
6774
- return { success: false, error: launchResult?.error || 'Failed to launch CLI session' };
6775
- }
6776
-
6777
- LOG.info('MeshCoordinator', `Launched ${cliType} coordinator for mesh ${meshId} in ${workspace}`);
6778
- const launchSessionId = launchResult.sessionId || launchResult.id;
6779
- if (launchSessionId) {
6780
- const autoImportInjectionDecl = providerMeta?.meshCoordinator?.systemPromptInjection;
6781
- registerMeshCoordinator({
6782
- meshId,
6783
- sessionId: launchSessionId,
6784
- workspace,
6785
- startedAt: Date.now(),
6786
- cliType,
6787
- systemPrompt: systemPrompt || undefined,
6788
- extraSystemPrompt: extraSystemPrompt || undefined,
6789
- mcpConfigPath,
6790
- injection: autoImportInjectionDecl ? {
6791
- mode: autoImportInjectionDecl.mode,
6792
- target: 'flag' in autoImportInjectionDecl ? autoImportInjectionDecl.flag
6793
- : 'name' in autoImportInjectionDecl ? autoImportInjectionDecl.name
6794
- : 'path' in autoImportInjectionDecl ? autoImportInjectionDecl.path
6795
- : undefined,
6796
- } : undefined,
6797
- });
6798
- }
6799
-
6800
- // Record coordinator launch in task ledger
6801
- try {
6802
- const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
6803
- appendLedgerEntry(meshId, {
6804
- kind: 'coordinator_started',
6805
- sessionId: launchSessionId,
6806
- providerType: cliType,
6807
- payload: { workspace },
6808
- });
6809
- } catch { /* ledger append is best-effort */ }
6810
-
6811
- return {
6812
- success: true,
6813
- meshId,
6814
- cliType,
6815
- workspace,
6816
- sessionId: launchSessionId,
6817
- mcpConfigWritten: true,
6818
- };
6819
- } catch (e: any) {
6820
- LOG.error('MeshCoordinator', `Failed: ${e.message}`);
6821
- return { success: false, error: e.message };
6822
- }
6823
- }
6824
-
6825
- case 'mesh_status': {
6826
- const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
6827
- if (!meshId) return { success: false, error: 'meshId required' };
6828
- try {
6829
- const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
6830
- const mesh = meshRecord?.mesh;
6831
- if (!mesh) return { success: false, error: 'Mesh not found' };
6832
- const meshHost = resolveMeshHostStatus(mesh);
6833
-
6834
- const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
6835
- // Compact (default) elides each mission's full goal text from the
6836
- // payload — coordinators polling node health don't need every
6837
- // mission's multi-hundred-char goal repeated. verbose=true (or the
6838
- // explicit compact=false) restores full goals. Verbose bypasses the
6839
- // shared (compact) aggregate cache so a verbose call never poisons
6840
- // the compact cache and vice versa.
6841
- const verboseMissions = args?.verbose === true || args?.compact === false;
6842
- // See (B3) below: scope the peek to this daemon when the
6843
- // caller doesn't tell us, otherwise scoped events look
6844
- // missing and we falsely return a stale cache.
6845
- const peekScope = typeof args?.coordinatorDaemonId === 'string' && args.coordinatorDaemonId.trim()
6846
- ? args.coordinatorDaemonId.trim()
6847
- : (this.deps.statusInstanceId || undefined);
6848
- const pendingCoordinatorEventCount = getPendingMeshCoordinatorEvents(meshId, peekScope).length;
6849
- const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
6850
- if (!refreshRequested && !verboseMissions && pendingCoordinatorEventCount === 0) {
6851
- const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
6852
- if (cachedStatus) {
6853
- logRepoMeshStatusDebug('return_cached', {
6854
- meshId,
6855
- command: 'mesh_status',
6856
- refreshRequested,
6857
- summary: summarizeRepoMeshStatusDebug(cachedStatus),
6858
- });
6859
- return cachedStatus;
6860
- }
6861
- }
6862
- const refreshReason = refreshRequested
6863
- ? 'explicit_refresh'
6864
- : pendingCoordinatorEventCount > 0
6865
- ? 'pending_coordinator_events'
6866
- : hadAggregateCache
6867
- ? 'stale_pending_cache_refresh'
6868
- : 'cold_cache_miss';
6869
-
6870
- const { getMeshQueueStats, getQueue } = await import('../mesh/mesh-work-queue.js');
6871
- const queue = getQueue(meshId);
6872
- const queueSummary = getMeshQueueStats(meshId);
6873
-
6874
- const { readLedgerEntries, getLedgerSummary } = await import('../mesh/mesh-ledger.js');
6875
- const ledgerEntries = readLedgerEntries(meshId, { tail: 20 });
6876
- const asyncRefineLedgerEntries = readLedgerEntries(meshId, { tail: 100 });
6877
- const ledgerSummary = getLedgerSummary(meshId);
6878
- const sessionHostRecords = this.deps.sessionHostControl?.listSessions
6879
- ? await this.deps.sessionHostControl.listSessions().catch(() => [])
6880
- : [];
6881
- const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
6882
-
6883
- const localMachineId = loadConfig().machineId || '';
6884
- const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
6885
- // Shared probe gate for this mesh_status call: the bootstrap
6886
- // hydrate below and the per-node render loop further down both
6887
- // probe the same peers — route both through this cache so they
6888
- // dedup within the call and reuse recent results across calls.
6889
- const meshGitProbeCache = this.meshGitProbeCache;
6890
- const directTruth = requireDirectPeerTruth
6891
- ? await hydrateInlineMeshDirectTruth({
6892
- mesh,
6893
- meshSource: meshRecord.source,
6894
- dispatchMeshCommand: this.deps.dispatchMeshCommand,
6895
- getMeshPeerConnectionStatus: this.deps.getMeshPeerConnectionStatus,
6896
- statusInstanceId: this.deps.statusInstanceId,
6897
- localMachineId,
6898
- // Standing-state model: only an explicit refresh fans
6899
- // out a blocking peer git probe. Default loads return
6900
- // held truth so one slow peer can't block the graph.
6901
- probeRemotePeers: refreshRequested,
6902
- probeCache: meshGitProbeCache,
6903
- })
6904
- : {
6905
- directEvidenceCount: 0,
6906
- localConfirmedCount: 0,
6907
- peerAttemptedCount: 0,
6908
- peerConfirmedCount: 0,
6909
- standingEvidenceCount: 0,
6910
- unavailableNodeIds: [] as string[],
6911
- deadNodeIds: [] as string[],
6912
- };
6913
- // Default/cached loads may not attempt a remote peer probe yet; do not surface that as
6914
- // a direct mesh truth failure until an explicit probe attempt actually fails.
6915
- const passivePeerTruthNotAttempted = requireDirectPeerTruth
6916
- && !refreshRequested
6917
- && directTruth.directEvidenceCount > 0
6918
- && directTruth.peerAttemptedCount === 0;
6919
- const effectiveDirectTruth = passivePeerTruthNotAttempted
6920
- ? { ...directTruth, unavailableNodeIds: [] as string[] }
6921
- : directTruth;
6922
- const unavailableDirectTruthNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
6923
- const unavailableNodesAreOnlyRemovedWorktrees = unavailableDirectTruthNodeIds.size > 0
6924
- && Array.isArray(mesh.nodes)
6925
- && mesh.nodes
6926
- .filter((node: any) => unavailableDirectTruthNodeIds.has(normalizeMeshNodeId(node) ?? ''))
6927
- .every((node: any) => node?.isLocalWorktree === true);
6928
- // Default (non-refresh) loads never hard-fail: held
6929
- // standing-state truth is returned and the graph renders
6930
- // immediately. The hard mesh_direct_peer_truth_unavailable
6931
- // failure is reserved for an explicit refresh that actually
6932
- // attempted a peer probe and could not confirm any evidence.
6933
- const directTruthSatisfied = !requireDirectPeerTruth
6934
- || !refreshRequested
6935
- || (effectiveDirectTruth.directEvidenceCount > 0 && (effectiveDirectTruth.unavailableNodeIds.length === 0 || unavailableNodesAreOnlyRemovedWorktrees));
6936
- if (requireDirectPeerTruth && refreshRequested && !directTruthSatisfied) {
6937
- const failureResult = {
6938
- success: false,
6939
- code: 'mesh_direct_peer_truth_unavailable',
6940
- error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.',
6941
- sourceOfTruth: {
6942
- membership: meshRecord.source === 'inline_cache'
6943
- ? 'coordinator_inline_mesh_cache'
6944
- : meshRecord.source === 'local_config'
6945
- ? 'local_mesh_config'
6946
- : 'inline_bootstrap_snapshot',
6947
- coordinatorOwnsLiveTruth: false,
6948
- currentStatus: 'direct_peer_truth_unavailable',
6949
- directPeerTruth: {
6950
- required: true,
6951
- satisfied: false,
6952
- directEvidenceCount: directTruth.directEvidenceCount,
6953
- localConfirmedCount: directTruth.localConfirmedCount,
6954
- peerAttemptedCount: directTruth.peerAttemptedCount,
6955
- peerConfirmedCount: directTruth.peerConfirmedCount,
6956
- unavailableNodeIds: directTruth.unavailableNodeIds,
6957
- },
6958
- },
6959
- };
6960
- logRepoMeshStatusDebug('direct_truth_unavailable', {
6961
- meshId,
6962
- command: 'mesh_status',
6963
- refreshRequested,
6964
- meshSource: meshRecord.source,
6965
- directTruth,
6966
- });
6967
- return failureResult;
6968
- }
6969
- const directTruthUnavailableNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
6970
- const coordinatorHostname = osHostname();
6971
- const selectedCoordinatorNodeId = readStringValue(
6972
- mesh.coordinator?.preferredNodeId,
6973
- normalizeMeshNodeId(mesh.nodes?.[0] as any),
6974
- );
6975
- const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes)
6976
- ? selectedCoordinatorNodeId
6977
- : undefined;
6978
- const refreshedAt = new Date().toISOString();
6979
- const nodeStatuses = [];
6980
- for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
6981
- const nodeId = normalizeMeshNodeId(node) ?? '';
6982
- const daemonId = readStringValue(node.daemonId);
6983
- const nodeMachineId = readMeshNodeMachineId(node as Record<string, unknown>);
6984
- const nodeHostname = readMeshNodeHostname(node as Record<string, unknown>);
6985
- const providerPriority = readProviderPriorityFromPolicy(node.policy);
6986
- const configuredCoordinatorNode = Boolean(
6987
- nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
6988
- );
6989
- const sparseConfiguredCoordinatorNode = configuredCoordinatorNode
6990
- && !daemonId
6991
- && !nodeMachineId
6992
- && !nodeHostname;
6993
- const isSelfNode = Boolean(
6994
- nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
6995
- ) || Boolean(
6996
- daemonId && (daemonIdsEquivalent(daemonId, localMachineId) || daemonIdsEquivalent(daemonId, this.deps.statusInstanceId)),
6997
- ) || Boolean(meshRecord?.inline && nodeIndex === 0)
6998
- || sparseConfiguredCoordinatorNode;
6999
- const machineIdentity = buildMeshNodeMachineIdentity(node as Record<string, unknown>, {
7000
- localMachineId,
7001
- localDaemonId: this.deps.statusInstanceId,
7002
- coordinatorHostname,
7003
- isSelfNode,
7004
- });
7005
- const status: Record<string, unknown> = {
7006
- nodeId,
7007
- machineLabel: buildMeshNodeDisplayLabel(node as Record<string, unknown>, nodeId, providerPriority),
7008
- labelSource: readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias)
7009
- ? 'explicit_metadata'
7010
- : 'workspace_host_provider_context',
7011
- workspace: node.workspace,
7012
- repoRoot: node.repoRoot,
7013
- isLocalWorktree: node.isLocalWorktree,
7014
- worktreeBranch: node.worktreeBranch,
7015
- role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? 'host' : undefined),
7016
- daemonId,
7017
- machineId: nodeMachineId || node.machineId,
7018
- machine: machineIdentity,
7019
- machineStatus: node.machineStatus,
7020
- health: 'unknown',
7021
- providers: node.providers || [],
7022
- providerPriority,
7023
- activeSessions: [],
7024
- activeSessionDetails: [],
7025
- launchReady: false,
7026
- };
7027
- if (isSelfNode) {
7028
- status.connection = {
7029
- perspective: 'selected_coordinator',
7030
- source: 'mesh_peer_status',
7031
- state: 'self',
7032
- transport: 'local',
7033
- reported: true,
7034
- reason: 'Selected coordinator daemon',
7035
- lastStateChangeAt: refreshedAt,
7036
- };
7037
- } else if (daemonId) {
7038
- const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
7039
- status.connection = connection ?? {
7040
- perspective: 'selected_coordinator',
7041
- source: 'not_reported',
7042
- state: 'unknown',
7043
- transport: 'unknown',
7044
- reported: false,
7045
- reason: 'No live mesh peer telemetry reported by the selected coordinator yet.',
7046
- };
7047
- } else {
7048
- status.connection = {
7049
- perspective: 'selected_coordinator',
7050
- source: 'not_reported',
7051
- state: 'unknown',
7052
- transport: 'unknown',
7053
- reported: false,
7054
- reason: 'Node has no daemon id, so mesh transport cannot be reported from the selected coordinator.',
7055
- };
7056
- }
7057
- const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
7058
- meshId,
7059
- node,
7060
- nodeId,
7061
- liveSessionRecords: liveMeshSessions,
7062
- allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
7063
- });
7064
- const workspace = readLiveMeshNodeWorkspace({
7065
- meshId,
7066
- nodeId,
7067
- liveSessionRecords: matchedLiveSessionRecords,
7068
- allowCoordinatorSession: nodeId === selectedCoordinatorNodeId,
7069
- }) || (typeof node.workspace === 'string' ? node.workspace : '');
7070
- status.workspace = workspace || node.workspace;
7071
- if (matchedLiveSessionRecords.length > 0) {
7072
- const sessionIds = matchedLiveSessionRecords
7073
- .map((record: any) => typeof record?.sessionId === 'string' ? record.sessionId : '')
7074
- .filter(Boolean);
7075
- const providerTypes = matchedLiveSessionRecords
7076
- .map((record: any) => readStringValue(record?.providerType))
7077
- .filter(Boolean) as string[];
7078
- status.activeSessions = sessionIds;
7079
- status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
7080
- if (providerTypes.length > 0) {
7081
- status.providers = Array.from(new Set([...(Array.isArray(status.providers) ? status.providers as string[] : []), ...providerTypes]));
7082
- }
7083
- }
7084
- if (workspace) {
7085
- if (!fs.existsSync(workspace)) {
7086
- // Workspace not local — prefer direct live inline truth, then attempt a P2P git probe.
7087
- const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
7088
- let remoteProbeApplied = false;
7089
- if (inlineTransitGit) {
7090
- status.git = inlineTransitGit;
7091
- status.health = inlineTransitGit.isGitRepo
7092
- ? deriveMeshNodeHealthFromGit(inlineTransitGit as unknown as Record<string, unknown>)
7093
- : 'degraded';
7094
- const connection = readObjectRecord(status.connection);
7095
- const connectionState = readStringValue(connection.state);
7096
- const connectionReported = readBooleanValue(connection.reported) ?? false;
7097
- if (!connectionReported || connectionState === 'unknown') {
7098
- status.connection = buildLivePeerGitConnection(connection, refreshedAt);
7099
- }
7100
- remoteProbeApplied = true;
7101
- } else if (refreshRequested && !isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
7102
- // Only an explicit refresh fans out a blocking
7103
- // per-node git probe. On the default load a peer
7104
- // with no held truth falls through to
7105
- // gitProbePending below — the graph still renders.
7106
- // Bounded retry (shared with the bootstrap hydrate
7107
- // path), gated on the peer staying connected, so a
7108
- // slow TURN-relayed peer is recovered rather than
7109
- // dropped after a single timeout.
7110
- const runNodeProbe = () => probeRemoteMeshGitStatusWithRetry({
7111
- dispatchMeshCommand: this.deps.dispatchMeshCommand,
7112
- daemonId,
7113
- workspace,
7114
- timeoutMs: MESH_DIRECT_PROBE_TIMEOUT_MS,
7115
- retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
7116
- getConnection: this.deps.getMeshPeerConnectionStatus,
7117
- onConnection: connection => { status.connection = connection; },
7118
- });
7119
- // Same shared cache as the bootstrap hydrate path: within one
7120
- // mesh_status call this dedups the bootstrap probe against this
7121
- // per-node probe for the same peer, and across calls it reuses a
7122
- // recent result so the dashboard auto-retry loop can't restart a
7123
- // fresh refreshUpstream probe seconds apart.
7124
- const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
7125
- if (remoteGit) {
7126
- status.git = remoteGit;
7127
- status.health = remoteGit.isGitRepo
7128
- ? deriveMeshNodeHealthFromGit(remoteGit as unknown as Record<string, unknown>)
7129
- : 'degraded';
7130
- const connection = readObjectRecord(status.connection);
7131
- const connectionState = readStringValue(connection.state);
7132
- const connectionReported = readBooleanValue(connection.reported) ?? false;
7133
- if (!connectionReported || connectionState === 'unknown') {
7134
- status.connection = buildLivePeerGitConnection(connection, refreshedAt);
7135
- }
7136
- const reporter = recordInlineMeshDirectGitTruth(node, remoteGit, 'selected_coordinator_mesh_p2p_git');
7137
- persistNodeReporterPlatform(meshRecord.source, mesh, nodeId, reporter);
7138
- remoteProbeApplied = true;
7139
- }
7140
- }
7141
- if (!remoteProbeApplied) {
7142
- const connectionState = readStringValue((status.connection as any)?.state);
7143
- const pendingPeerGitProbe = !inlineTransitGit
7144
- && !isSelfNode
7145
- && !!daemonId
7146
- && (
7147
- readStringValue(status.machineStatus) === 'online'
7148
- || readStringValue(status.health) === 'online'
7149
- || connectionState === 'connecting'
7150
- || connectionState === 'connected'
7151
- || connectionState === 'unknown'
7152
- );
7153
- if (pendingPeerGitProbe) {
7154
- status.gitProbePending = true;
7155
- status.health = 'unknown';
7156
- }
7157
- if (applyCachedInlineMeshNodeStatus(
7158
- status,
7159
- node,
7160
- pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : undefined,
7161
- )) {
7162
- applyInlineMeshBranchConvergence(mesh, node, status);
7163
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
7164
- nodeStatuses.push(status);
7165
- continue;
7166
- }
7167
- if (meshRecord?.source === 'inline_cache' && !isSelfNode) {
7168
- applyInlineMeshBranchConvergence(mesh, node, status);
7169
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
7170
- nodeStatuses.push(status);
7171
- continue;
7172
- }
7173
- }
7174
- } else {
7175
- try {
7176
- const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 10_000, refreshUpstream: true });
7177
- status.git = gitStatus;
7178
- const reporter = recordInlineMeshDirectGitTruth(node, gitStatus as unknown as Record<string, unknown>, 'selected_coordinator_local_git');
7179
- persistNodeReporterPlatform(meshRecord.source, mesh, nodeId, reporter);
7180
- if (gitStatus.isGitRepo) {
7181
- status.health = deriveMeshNodeHealthFromGit(gitStatus as unknown as Record<string, unknown>);
7182
- } else {
7183
- status.health = 'degraded';
7184
- if (gitStatus.error && !status.error) status.error = gitStatus.error;
7185
- }
7186
- } catch {
7187
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
7188
- status.health = 'degraded';
7189
- }
7190
- }
7191
- }
7192
- } else {
7193
- applyCachedInlineMeshNodeStatus(status, node);
7194
- }
7195
- applyInlineMeshBranchConvergence(mesh, node, status);
7196
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
7197
- nodeStatuses.push(status);
7198
- }
7199
-
7200
- // (B3) Resolve the coordinator daemon scope for the peek.
7201
- // mesh_status is a read-only status query — it must not consume
7202
- // (drain) pending events as a side effect. Coordinators that see
7203
- // pendingCoordinatorEvents in the response are expected to call
7204
- // get_pending_mesh_events to explicitly drain them after processing.
7205
- const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === 'string' && args.coordinatorDaemonId.trim()
7206
- ? args.coordinatorDaemonId.trim()
7207
- : (this.deps.statusInstanceId || undefined);
7208
- const pendingCoordinatorEvents = getPendingMeshCoordinatorEvents(meshId, callerCoordinatorDaemonId);
7209
- // R4: surface recent fail-loud routing drops so a coordinator/operator can see
7210
- // that a worker completion was lost (envelope present, mesh unresolved) instead
7211
- // of it vanishing silently. Diagnostic-only — never cached (see omit below).
7212
- const unroutableDeliveries = getRecentUnroutableDeliveries();
7213
- const previewFreshness = (() => {
7214
- const localRepoRoot = nodeStatuses
7215
- .map((node: any) => readStringValue(node?.git?.repoRoot, node?.repoRoot, node?.workspace))
7216
- .find((candidate: string | undefined) => !!candidate && fs.existsSync(candidate));
7217
- return localRepoRoot ? buildPreviewFreshness(localRepoRoot) : undefined;
7218
- })();
7219
- const asyncRefineJobs = buildMeshAsyncRefineJobs({
7220
- meshId,
7221
- ledgerEntries: asyncRefineLedgerEntries,
7222
- pendingEvents: [...pendingCoordinatorEvents],
7223
- });
7224
- const historicalSessions = buildHistoricalMeshSessions({
7225
- meshId,
7226
- nodes: mesh.nodes || [],
7227
- liveSessionRecords: liveMeshSessions,
7228
- });
7229
- const { getMeshStatusMissionSummaries } = await import('../mesh/mesh-missions.js');
7230
- // withStats opts in to per-mission operational rollups (durations /
7231
- // retries) for the dashboard mission detail. The rollup scans a
7232
- // bounded ledger tail per mission, but only over the bounded set
7233
- // returned here (live + capped history), so the cost stays linear
7234
- // in visible missions rather than the whole mesh history.
7235
- const missions = getMeshStatusMissionSummaries(meshId, { verbose: verboseMissions, withStats: true });
7236
- const statusResult = {
7237
- success: true,
7238
- meshId: mesh.id,
7239
- meshName: mesh.name,
7240
- repoIdentity: mesh.repoIdentity,
7241
- defaultBranch: mesh.defaultBranch,
7242
- refreshedAt,
7243
- meshHost,
7244
- sourceOfTruth: {
7245
- membership: meshRecord?.source === 'inline_cache'
7246
- ? 'coordinator_inline_mesh_cache'
7247
- : meshRecord?.source === 'local_config'
7248
- ? 'local_mesh_config'
7249
- : 'inline_bootstrap_snapshot',
7250
- coordinatorOwnsLiveTruth: directTruthSatisfied,
7251
- meshHost: {
7252
- owner: 'mesh_host_daemon',
7253
- localRole: meshHost.role,
7254
- hostDaemonId: meshHost.hostDaemonId,
7255
- hostNodeId: meshHost.hostNodeId,
7256
- hostAddress: meshHost.hostAddress,
7257
- },
7258
- ...(requireDirectPeerTruth ? {
7259
- currentStatus: directTruthSatisfied ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
7260
- directPeerTruth: {
7261
- required: true,
7262
- satisfied: directTruthSatisfied,
7263
- directEvidenceCount: effectiveDirectTruth.directEvidenceCount,
7264
- localConfirmedCount: effectiveDirectTruth.localConfirmedCount,
7265
- peerAttemptedCount: effectiveDirectTruth.peerAttemptedCount,
7266
- peerConfirmedCount: effectiveDirectTruth.peerConfirmedCount,
7267
- unavailableNodeIds: effectiveDirectTruth.unavailableNodeIds,
7268
- partialNodeFailures: effectiveDirectTruth.unavailableNodeIds,
7269
- },
7270
- } : {}),
7271
- historicalEvidenceOnly: ['recoveryHints', 'ledger.summary', 'queue.summary', 'historicalSessions'],
7272
- },
7273
- branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodeStatuses),
7274
- ...(previewFreshness ? { previewFreshness, deployFreshness: previewFreshness } : {}),
7275
- nodes: nodeStatuses,
7276
- queue: { tasks: queue, summary: queueSummary },
7277
- ledger: { entries: ledgerEntries, summary: ledgerSummary },
7278
- ...(missions.length > 0 ? { missions } : {}),
7279
- ...(asyncRefineJobs.length > 0 ? { asyncRefineJobs } : {}),
7280
- ...(historicalSessions ? { historicalSessions } : {}),
7281
- ...(pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {}),
7282
- ...(unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {}),
7283
- activeRefineJobs: Array.from(this.runningRefineJobs.values())
7284
- .filter(job => job.meshId === meshId)
7285
- .map(job => ({
7286
- jobId: job.jobId,
7287
- nodeId: job.targetNodeId,
7288
- workspace: job.workspace,
7289
- startedAt: job.startedAt,
7290
- status: job.status,
7291
- targetCoordinatorDaemonId: job.targetCoordinatorDaemonId,
7292
- })),
7293
- };
7294
- const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, ...cacheableStatusResult } = statusResult as any;
7295
- // Verbose carries full mission goals; never store it in the shared
7296
- // (compact) aggregate cache or a later compact poll would return the
7297
- // heavy goals from cache. Return it without caching.
7298
- const rememberedStatus = verboseMissions
7299
- ? cacheableStatusResult
7300
- : this.rememberAggregateMeshStatus(meshId, cacheableStatusResult, refreshReason);
7301
- const returnedStatus = {
7302
- ...rememberedStatus,
7303
- ...(pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {}),
7304
- ...(unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {}),
7305
- };
7306
- logRepoMeshStatusDebug('return_live', {
7307
- meshId,
7308
- command: 'mesh_status',
7309
- refreshRequested,
7310
- refreshReason,
7311
- meshSource: meshRecord.source,
7312
- directTruth,
7313
- summary: summarizeRepoMeshStatusDebug(returnedStatus),
7314
- });
7315
- return returnedStatus;
7316
- } catch (e: any) {
7317
- return { success: false, error: e.message };
7318
- }
7319
- }
7320
-
7321
- case 'get_mesh_review_inbox': {
7322
- const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
7323
- if (!meshId) return { success: false, error: 'meshId required' };
7324
- try {
7325
- const { deriveMeshReviewInboxItems } = await import('../mesh/mesh-review-inbox.js');
7326
- const { readLedgerEntries } = await import('../mesh/mesh-ledger.js');
7327
- const { getGitDiffSummary } = await import('../git/git-diff.js');
7328
- const { existsSync } = await import('node:fs');
7329
-
7330
- const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
7331
- const mesh = meshRecord?.mesh;
7332
- if (!mesh) return { success: false, error: 'Mesh not found' };
7333
-
7334
- // Ensure we have a fresh aggregate status so nodeStatuses carry
7335
- // computed fields (connection.state, branchConvergence, isLocalWorktree)
7336
- // that the raw mesh.nodes config objects don't have.
7337
- // When the caller provides an inlineMesh, prefer its nodes directly
7338
- // (they already carry the computed fields from the coordinator).
7339
- const inlineNodes = args?.inlineMesh && Array.isArray((args.inlineMesh as any)?.nodes)
7340
- ? (args.inlineMesh as any).nodes as Record<string, unknown>[]
7341
- : null;
7342
- let cachedStatus = !inlineNodes ? this.getCachedAggregateMeshStatus(meshId, mesh, {}) : null;
7343
- if (!cachedStatus && !inlineNodes) {
7344
- const freshStatus = await this.execute('mesh_status', {
7345
- meshId,
7346
- inlineMesh: args?.inlineMesh,
7347
- refresh: true,
7348
- }, 'get_mesh_review_inbox');
7349
- cachedStatus = (freshStatus?.success !== false) ? freshStatus : null;
7350
- }
7351
- const nodeStatuses: Record<string, unknown>[] = inlineNodes
7352
- ? inlineNodes
7353
- : Array.isArray(cachedStatus?.nodes)
7354
- ? cachedStatus.nodes as Record<string, unknown>[]
7355
- : Array.isArray(mesh.nodes)
7356
- ? mesh.nodes as Record<string, unknown>[]
7357
- : [];
7358
-
7359
- const ledgerEntries = readLedgerEntries(meshId, { tail: 300 });
7360
- const derivation = deriveMeshReviewInboxItems({ nodes: nodeStatuses, ledgerEntries });
7361
-
7362
- for (const item of derivation.items) {
7363
- const workspace = item.workspace;
7364
- if (!workspace || !existsSync(workspace)) continue;
7365
- const baseRef = item.defaultBranch
7366
- ? `origin/${item.defaultBranch}`
7367
- : 'origin/main';
7368
- try {
7369
- const diffResult = await getGitDiffSummary(workspace, { baseRef, maxFiles: 100 });
7370
- if (diffResult.isGitRepo) {
7371
- item.diffSummary = {
7372
- baseRef,
7373
- files: diffResult.files.map(f => ({
7374
- path: f.path,
7375
- status: f.status,
7376
- insertions: f.insertions,
7377
- deletions: f.deletions,
7378
- binary: f.binary,
7379
- oldPath: f.oldPath,
7380
- })),
7381
- totalFiles: diffResult.files.length,
7382
- totalInsertions: diffResult.totalInsertions,
7383
- totalDeletions: diffResult.totalDeletions,
7384
- truncated: diffResult.truncated,
7385
- ...(diffResult.error ? { error: diffResult.error } : {}),
7386
- };
7387
- }
7388
- } catch {
7389
- item.diffSummary = null;
7390
- }
7391
- }
7392
-
7393
- return {
7394
- success: true,
7395
- meshId,
7396
- inbox: derivation.items,
7397
- remoteNodesExcluded: derivation.remoteNodesExcluded,
7398
- excludedRemoteNodeIds: derivation.excludedRemoteNodeIds,
7399
- };
7400
- } catch (e: any) {
7401
- return { success: false, error: e.message };
7402
- }
7403
- }
7404
-
7405
- default:
7406
- break;
6420
+ // RF-ROUTER HIGH family: high-coupling commands (mesh coordinator-event
6421
+ // relay + interactive prompt, mesh coordinator launch, mesh aggregate
6422
+ // status + review inbox) are handled by the registry after the LOW and
6423
+ // MED families and before the (now empty) switch. HIGH handlers reach the
6424
+ // most router-owned state — the aggregate-status memory cache and the
6425
+ // running-refine-job table — so the context carries those plus bound
6426
+ // read/write helpers and the router's own `execute` (the
6427
+ // get_mesh_review_inbox mesh_status re-entry). A hit returns the same
6428
+ // CommandRouterResult the inlined case used to; a miss falls through to
6429
+ // CommandHandler delegation.
6430
+ const highFamilyHandler = highFamilyRegistry.get(cmd);
6431
+ if (highFamilyHandler) {
6432
+ return await highFamilyHandler(this.buildHighFamilyContext(), args);
7407
6433
  }
7408
6434
 
7409
6435
  return null; // Not handled at this level → delegate to CommandHandler