@adhdev/daemon-core 0.9.82-rc.368 → 0.9.82-rc.369

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.
@@ -81,6 +81,7 @@ export interface DaemonComponents {
81
81
  };
82
82
  refreshProviderAvailability: (providerType?: string) => Promise<void>;
83
83
  dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
84
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
84
85
  onMeshCoordinatorEventForwarded?: (payload: Record<string, unknown>) => void;
85
86
  meshReconcileLoop?: {
86
87
  stop(): void;
@@ -14,6 +14,7 @@ import { DaemonCliManager } from './cli-manager.js';
14
14
  import type { ProviderLoader } from '../providers/provider-loader.js';
15
15
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
16
16
  import { SessionRegistry } from '../sessions/registry.js';
17
+ import { awaitWithWarmupDeadline } from '../mesh/mesh-warmup-deadline.js';
17
18
  export declare function readProviderPriorityFromPolicy(policy: unknown): string[];
18
19
  /**
19
20
  * Normalize a providerRoles array (RepoMeshNodePolicy.providerRoles) from raw
@@ -78,11 +79,46 @@ export declare function resolveMeshNodeAttribution(node: unknown): {
78
79
  machineName?: string;
79
80
  };
80
81
  export declare function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>>;
82
+ /**
83
+ * Transient per-node marker the mesh_status render loop stamps onto a node
84
+ * `status` at the two sites that obtain git truth from a FRESH probe this call
85
+ * (a successful local `getGitRepoStatus`, or a successful P2P `git_status`
86
+ * round-trip). finalizeMeshNodeStatus consumes and deletes it. Held/standing
87
+ * truth (node.lastGit / cachedStatus / inline transit) is deliberately NOT
88
+ * stamped — its absence is exactly how the freshness marker tells "live" apart
89
+ * from "cached". Internal only; never serialized in the response.
90
+ */
91
+ export declare const MESH_NODE_LIVE_TRUTH_MARKER = "__liveTruthProbed";
92
+ /**
93
+ * Build the additive per-node `dataFreshness` marker. This NEVER mutates any
94
+ * existing field — it only adds an explicit, machine-readable answer to the
95
+ * question the legacy fields blurred: is this node's data live (just probed),
96
+ * cached (held truth, maybe old), or absent because the peer was unreachable?
97
+ *
98
+ * The crucial separation: an UNREACHABLE peer (P2P probe failed / not connected)
99
+ * is no longer indistinguishable from an idle/EMPTY node. Both used to render as
100
+ * `health:'unknown'` with no sessions; now `dataFreshness.dataSource` and
101
+ * `reachable` tell them apart so a coordinator never reads a dead peer as "online
102
+ * but doing nothing".
103
+ */
104
+ export declare function buildMeshNodeDataFreshness(args: {
105
+ status: Record<string, unknown>;
106
+ node?: any;
107
+ isSelfNode: boolean;
108
+ daemonId?: string;
109
+ /** True when this node was stamped with a fresh live git probe this call. */
110
+ liveTruthProbed: boolean;
111
+ /** True when direct-peer-truth accounting classified this node unavailable. */
112
+ directTruthUnavailable?: boolean;
113
+ now?: () => number;
114
+ }): Record<string, unknown>;
81
115
  export declare function finalizeMeshNodeStatus(args: {
82
116
  status: Record<string, unknown>;
83
117
  node: any;
84
118
  daemonId?: string;
85
119
  isSelfNode: boolean;
120
+ /** True when direct-peer-truth accounting classified this node unavailable. */
121
+ directTruthUnavailable?: boolean;
86
122
  }): void;
87
123
  export declare const MESH_DIRECT_PROBE_TIMEOUT_MS: number;
88
124
  export declare const MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS: number;
@@ -117,37 +153,7 @@ export declare class MeshGitProbeCache {
117
153
  */
118
154
  probe(daemonId: string, workspace: string, probe: () => Promise<Record<string, unknown> | null>): Promise<Record<string, unknown> | null>;
119
155
  }
120
- /**
121
- * Await `work` under a warmup-aware deadline so a cold-open DataChannel handshake
122
- * is NOT charged against the command response budget — the root cause of the
123
- * "first mesh probe to a cold peer false-times-out, the warm retry succeeds"
124
- * signature. Two budgets, switched by the live peer connection state:
125
- *
126
- * - While `isConnected()` returns false the peer's channel is still opening; the
127
- * cold-open `connectTimeoutMs` budget applies. This phase is deliberately
128
- * generous because a TURN-relayed cross-machine handshake legitimately needs
129
- * many seconds — but a genuine connect *failure* is surfaced by `work`
130
- * rejecting on its own (the mesh manager fails the peer the instant its
131
- * PeerConnection state goes terminal), so a real failure is never masked for
132
- * the whole window.
133
- * - The first time `isConnected()` returns true the channel is warm; from that
134
- * instant the tight `responseTimeoutMs` governs how long the handler may take.
135
- * Warm-channel callers therefore see behavior identical to the old single
136
- * `Promise.race(work, responseTimeoutMs)`.
137
- *
138
- * Rejects with `Error('timeout')` when either budget is exhausted, mirroring the
139
- * previous single-race contract. Pure except for timers + the injected
140
- * `isConnected` probe, so it is unit-testable under fake timers without any real
141
- * WebRTC. When no connection getter is wired `isConnected` should be `() => true`
142
- * (the caller's choice) so the response deadline governs from t0 — the legacy
143
- * single-budget behavior, never a combined connect+response window.
144
- */
145
- export declare function awaitWithWarmupDeadline<T>(work: Promise<T>, opts: {
146
- isConnected: () => boolean;
147
- connectTimeoutMs: number;
148
- responseTimeoutMs: number;
149
- pollIntervalMs?: number;
150
- }): Promise<T>;
156
+ export { awaitWithWarmupDeadline };
151
157
  /**
152
158
  * Probe a remote peer's git_status with a bounded retry budget, but only while
153
159
  * the peer is reported `connected`. A single slow (often TURN-relayed) peer can
@@ -781,4 +787,3 @@ export declare class DaemonCommandRouter {
781
787
  */
782
788
  private stopIde;
783
789
  }
784
- export {};
package/dist/index.js CHANGED
@@ -316,10 +316,10 @@ function readInjected(value) {
316
316
  }
317
317
  function getDaemonBuildInfo() {
318
318
  if (cached) return cached;
319
- const commit = readInjected(true ? "8cb6cfc5399f55c6b625095bacc9a27e1741df17" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "8cb6cfc5" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.368" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-24T06:14:32.255Z" : void 0);
319
+ const commit = readInjected(true ? "4464cd9f1effac841aa1fbef3b1691d2f06d64e4" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "4464cd9f" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.369" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-24T08:22:14.501Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -10382,6 +10382,90 @@ var init_mesh_event_trace = __esm({
10382
10382
  }
10383
10383
  });
10384
10384
 
10385
+ // src/mesh/mesh-warmup-deadline.ts
10386
+ function awaitWithWarmupDeadline(work, opts) {
10387
+ const pollMs = Math.max(1, Math.min(opts.pollIntervalMs ?? 200, opts.connectTimeoutMs));
10388
+ return new Promise((resolve24, reject) => {
10389
+ let done = false;
10390
+ let poll;
10391
+ let responseTimer;
10392
+ const startedAt = Date.now();
10393
+ const cleanup = () => {
10394
+ if (poll) {
10395
+ clearInterval(poll);
10396
+ poll = void 0;
10397
+ }
10398
+ if (responseTimer) {
10399
+ clearTimeout(responseTimer);
10400
+ responseTimer = void 0;
10401
+ }
10402
+ };
10403
+ const settle = (fn) => {
10404
+ if (done) return;
10405
+ done = true;
10406
+ cleanup();
10407
+ fn();
10408
+ };
10409
+ const armResponse = () => {
10410
+ if (responseTimer || done) return;
10411
+ responseTimer = setTimeout(
10412
+ () => settle(() => reject(new Error("timeout"))),
10413
+ opts.responseTimeoutMs
10414
+ );
10415
+ if (typeof responseTimer.unref === "function") responseTimer.unref();
10416
+ };
10417
+ const onPoll = () => {
10418
+ if (done) return;
10419
+ if (opts.isConnected()) {
10420
+ if (poll) {
10421
+ clearInterval(poll);
10422
+ poll = void 0;
10423
+ }
10424
+ armResponse();
10425
+ return;
10426
+ }
10427
+ if (Date.now() - startedAt >= opts.connectTimeoutMs) {
10428
+ settle(() => reject(new Error("timeout")));
10429
+ }
10430
+ };
10431
+ if (opts.isConnected()) {
10432
+ armResponse();
10433
+ } else {
10434
+ poll = setInterval(onPoll, pollMs);
10435
+ if (typeof poll.unref === "function") poll.unref();
10436
+ }
10437
+ work.then(
10438
+ (val) => settle(() => resolve24(val)),
10439
+ (err) => settle(() => reject(err))
10440
+ );
10441
+ });
10442
+ }
10443
+ function readWarmupConnectionState(connection) {
10444
+ const state = connection?.state;
10445
+ return typeof state === "string" && state.length > 0 ? state : void 0;
10446
+ }
10447
+ function resolveWarmupDeadlineOpts(opts) {
10448
+ const { getConnection, daemonId, connectTimeoutMs, responseTimeoutMs } = opts;
10449
+ if (getConnection) {
10450
+ return {
10451
+ isConnected: () => readWarmupConnectionState(getConnection(daemonId)) === "connected",
10452
+ connectTimeoutMs,
10453
+ responseTimeoutMs
10454
+ };
10455
+ }
10456
+ opts.onMissingGetter?.(daemonId);
10457
+ return {
10458
+ isConnected: () => false,
10459
+ connectTimeoutMs: connectTimeoutMs + responseTimeoutMs,
10460
+ responseTimeoutMs
10461
+ };
10462
+ }
10463
+ var init_mesh_warmup_deadline = __esm({
10464
+ "src/mesh/mesh-warmup-deadline.ts"() {
10465
+ "use strict";
10466
+ }
10467
+ });
10468
+
10385
10469
  // src/config/state-store.ts
10386
10470
  function isPlainObject2(value) {
10387
10471
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -12817,7 +12901,12 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
12817
12901
  return void 0;
12818
12902
  }
12819
12903
  }
12820
- function deliverTaskToSession(dispatchThunk, ctx) {
12904
+ function warnDispatchWarmupGetterMissingOnce(daemonId) {
12905
+ if (dispatchWarmupGetterMissingWarned.has(daemonId)) return;
12906
+ dispatchWarmupGetterMissingWarned.add(daemonId);
12907
+ LOG.warn("MeshQueue", `Mesh peer connection getter unavailable for ${String(daemonId).slice(0, 12)}; remote task-dispatch warmup deadline degraded to the combined connect+response window. Avoids a cold-open false-timeout but loses warm/cold precision \u2014 wire getMeshPeerConnectionStatus on this daemon.`);
12908
+ }
12909
+ function deliverTaskToSession(dispatchThunk, ctx, warmup) {
12821
12910
  const delivery = createSessionDelivery({
12822
12911
  meshId: ctx.meshId,
12823
12912
  nodeId: ctx.nodeId,
@@ -12837,16 +12926,27 @@ function deliverTaskToSession(dispatchThunk, ctx) {
12837
12926
  dispatchPromise = Promise.reject(e);
12838
12927
  }
12839
12928
  let timer;
12840
- const guarded = Promise.race([
12841
- dispatchPromise,
12842
- new Promise((_, reject) => {
12843
- timer = setTimeout(
12844
- () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
12845
- DISPATCH_CONFIRM_TIMEOUT_MS
12846
- );
12847
- if (typeof timer?.unref === "function") timer.unref();
12848
- })
12849
- ]);
12929
+ let guarded;
12930
+ if (warmup) {
12931
+ guarded = awaitWithWarmupDeadline(dispatchPromise, resolveWarmupDeadlineOpts({
12932
+ getConnection: warmup.getConnection,
12933
+ daemonId: warmup.daemonId,
12934
+ connectTimeoutMs: DISPATCH_CONNECT_TIMEOUT_MS,
12935
+ responseTimeoutMs: DISPATCH_CONFIRM_TIMEOUT_MS,
12936
+ onMissingGetter: warnDispatchWarmupGetterMissingOnce
12937
+ }));
12938
+ } else {
12939
+ guarded = Promise.race([
12940
+ dispatchPromise,
12941
+ new Promise((_, reject) => {
12942
+ timer = setTimeout(
12943
+ () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
12944
+ DISPATCH_CONFIRM_TIMEOUT_MS
12945
+ );
12946
+ if (typeof timer?.unref === "function") timer.unref();
12947
+ })
12948
+ ]);
12949
+ }
12850
12950
  guarded.then(() => {
12851
12951
  if (timer) clearTimeout(timer);
12852
12952
  updateSessionDeliveryStatus(delivery.id, "delivered");
@@ -12930,7 +13030,11 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12930
13030
  transport: "remote",
12931
13031
  ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12932
13032
  ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12933
- }
13033
+ },
13034
+ // Warmup-aware deadline: this dispatch can be the FIRST command to a
13035
+ // peer whose mesh DataChannel is still opening — charge the cold-open
13036
+ // handshake to the connect budget, not the response budget.
13037
+ { daemonId: remoteDaemonId, getConnection: components.getMeshPeerConnectionStatus }
12934
13038
  );
12935
13039
  return true;
12936
13040
  }
@@ -14309,7 +14413,7 @@ function setupMeshEventForwarding(components) {
14309
14413
  });
14310
14414
  });
14311
14415
  }
14312
- var import_fs13, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS, coordinatorForwardLanes;
14416
+ var import_fs13, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, DISPATCH_CONNECT_TIMEOUT_MS, dispatchWarmupGetterMissingWarned, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS, coordinatorForwardLanes;
14313
14417
  var init_mesh_events_coordinator = __esm({
14314
14418
  "src/mesh/mesh-events-coordinator.ts"() {
14315
14419
  "use strict";
@@ -14327,6 +14431,7 @@ var init_mesh_events_coordinator = __esm({
14327
14431
  init_mesh_routing();
14328
14432
  init_mesh_unresolved_forward_outbox();
14329
14433
  init_mesh_event_trace();
14434
+ init_mesh_warmup_deadline();
14330
14435
  init_snapshot();
14331
14436
  init_repo_mesh_types();
14332
14437
  init_dist();
@@ -14340,6 +14445,8 @@ var init_mesh_events_coordinator = __esm({
14340
14445
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
14341
14446
  RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
14342
14447
  DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
14448
+ DISPATCH_CONNECT_TIMEOUT_MS = 45e3;
14449
+ dispatchWarmupGetterMissingWarned = /* @__PURE__ */ new Set();
14343
14450
  autoLaunchInProgress = /* @__PURE__ */ new Set();
14344
14451
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
14345
14452
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -41849,6 +41956,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
41849
41956
  let restored = 0;
41850
41957
  const restoredBindings = /* @__PURE__ */ new Set();
41851
41958
  const managerTag = this.deps.hostedRuntimeManagerTag;
41959
+ const restoredRuntimeIds = /* @__PURE__ */ new Set();
41960
+ const workspaceTypeCounts = /* @__PURE__ */ new Map();
41961
+ for (const r of sessions) {
41962
+ if (!r?.runtimeId || !r?.cliType || !r?.workspace) continue;
41963
+ restoredRuntimeIds.add(r.runtimeId);
41964
+ const key = `${r.workspace}::${r.cliType}`;
41965
+ workspaceTypeCounts.set(key, (workspaceTypeCounts.get(key) || 0) + 1);
41966
+ }
41852
41967
  for (const record of sessions) {
41853
41968
  if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
41854
41969
  if (!shouldRestoreHostedRuntime(record, managerTag)) {
@@ -41886,11 +42001,21 @@ Run 'adhdev doctor' for detailed diagnostics.`
41886
42001
  if (!coordinatorEntry?.meshId && record.workspace) {
41887
42002
  const workspaceCoordinators = listCoordinatorsForWorkspace(record.workspace).filter((e) => e.meshId && (!e.cliType || e.cliType === record.cliType));
41888
42003
  if (workspaceCoordinators.length === 1) {
41889
- coordinatorEntry = workspaceCoordinators[0];
41890
- LOG.info(
41891
- "CLI",
41892
- `\u21BB Rebound coordinator mark by workspace for ${record.runtimeKey || record.runtimeId} (mesh ${coordinatorEntry.meshId} @ ${record.workspace}); registry key did not match runtimeId`
41893
- );
42004
+ const candidate = workspaceCoordinators[0];
42005
+ const coordinatorPresentById = !!candidate.sessionId && restoredRuntimeIds.has(candidate.sessionId);
42006
+ const siblingCount = workspaceTypeCounts.get(`${record.workspace}::${record.cliType}`) || 1;
42007
+ if (!coordinatorPresentById && siblingCount === 1) {
42008
+ coordinatorEntry = candidate;
42009
+ LOG.info(
42010
+ "CLI",
42011
+ `\u21BB Rebound coordinator mark by workspace for ${record.runtimeKey || record.runtimeId} (mesh ${candidate.meshId} @ ${record.workspace}); registry key did not match runtimeId`
42012
+ );
42013
+ } else {
42014
+ LOG.info(
42015
+ "CLI",
42016
+ `\u21B7 Skipping workspace coordinator rebind for ${record.runtimeKey || record.runtimeId} (${record.cliType} @ ${record.workspace}): ${coordinatorPresentById ? "registered coordinator is restoring under its own id \u2014 this is a delegated worker" : `ambiguous (${siblingCount} sessions share this workspace+cliType)`}`
42017
+ );
42018
+ }
41894
42019
  }
41895
42020
  }
41896
42021
  if (coordinatorEntry?.meshId) {
@@ -48372,6 +48497,7 @@ var meshStatusHandlers = {
48372
48497
  const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
48373
48498
  if (remoteGit) {
48374
48499
  status.git = remoteGit;
48500
+ status[MESH_NODE_LIVE_TRUTH_MARKER] = true;
48375
48501
  status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
48376
48502
  const connection = readObjectRecord(status.connection);
48377
48503
  const connectionState = readStringValue(connection.state);
@@ -48397,13 +48523,13 @@ var meshStatusHandlers = {
48397
48523
  pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
48398
48524
  )) {
48399
48525
  applyInlineMeshBranchConvergence(mesh, node, status);
48400
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
48526
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode, directTruthUnavailable: directTruthUnavailableNodeIds.has(nodeId) });
48401
48527
  nodeStatuses.push(status);
48402
48528
  continue;
48403
48529
  }
48404
48530
  if (meshRecord?.source === "inline_cache" && !isSelfNode) {
48405
48531
  applyInlineMeshBranchConvergence(mesh, node, status);
48406
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
48532
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode, directTruthUnavailable: directTruthUnavailableNodeIds.has(nodeId) });
48407
48533
  nodeStatuses.push(status);
48408
48534
  continue;
48409
48535
  }
@@ -48412,6 +48538,7 @@ var meshStatusHandlers = {
48412
48538
  try {
48413
48539
  const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
48414
48540
  status.git = gitStatus;
48541
+ status[MESH_NODE_LIVE_TRUTH_MARKER] = true;
48415
48542
  const reporter = recordInlineMeshDirectGitTruth(node, gitStatus, "selected_coordinator_local_git");
48416
48543
  persistNodeReporterPlatform(meshRecord.source, mesh, nodeId, reporter);
48417
48544
  if (gitStatus.isGitRepo) {
@@ -48430,7 +48557,7 @@ var meshStatusHandlers = {
48430
48557
  applyCachedInlineMeshNodeStatus(status, node);
48431
48558
  }
48432
48559
  applyInlineMeshBranchConvergence(mesh, node, status);
48433
- finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
48560
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode, directTruthUnavailable: directTruthUnavailableNodeIds.has(nodeId) });
48434
48561
  nodeStatuses.push(status);
48435
48562
  }
48436
48563
  const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : ctx.deps.statusInstanceId || void 0;
@@ -48844,6 +48971,7 @@ function orderMeshRefineBatchNodes(changeAreas) {
48844
48971
 
48845
48972
  // src/commands/router.ts
48846
48973
  init_mesh_work_queue();
48974
+ init_mesh_warmup_deadline();
48847
48975
  init_repo_mesh_types();
48848
48976
  var import_os4 = require("os");
48849
48977
  var import_path13 = require("path");
@@ -49585,14 +49713,92 @@ function synthesizeMeshNodeFreshnessFromConnection(status) {
49585
49713
  status.updatedAt = gitCheckedAt ?? connectionFreshAt;
49586
49714
  }
49587
49715
  }
49716
+ var MESH_NODE_LIVE_TRUTH_MARKER = "__liveTruthProbed";
49717
+ var MESH_FRESHNESS_FRESH_MS = 3e4;
49718
+ var MESH_FRESHNESS_RECENT_MS = 3e5;
49719
+ function classifyMeshNodeStaleness(dataSource, ageMs) {
49720
+ if (dataSource === "self" || dataSource === "live") return "fresh";
49721
+ if (ageMs === null) return "unknown";
49722
+ if (ageMs < MESH_FRESHNESS_FRESH_MS) return "fresh";
49723
+ if (ageMs < MESH_FRESHNESS_RECENT_MS) return "recent";
49724
+ return "stale";
49725
+ }
49726
+ function buildMeshNodeDataFreshness(args) {
49727
+ const { status, node, isSelfNode, daemonId, liveTruthProbed, directTruthUnavailable } = args;
49728
+ const now = args.now ?? Date.now;
49729
+ const connection = readObjectRecord(status.connection);
49730
+ const connectionState = readStringValue(connection.state);
49731
+ const git = readObjectRecord(status.git);
49732
+ const hasGit = readBooleanValue(git.isGitRepo) === true || !!readStringValue(git.branch, git.headCommit, git.head, git.upstream);
49733
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
49734
+ const liveGitCheckedAt = liveTruthProbed ? toIsoTimestamp(git.lastCheckedAt) : null;
49735
+ const heldGit = readObjectRecord(node?.lastGit ?? node?.last_git);
49736
+ const heldCheckedAt = toIsoTimestamp(heldGit.checkedAt ?? heldGit.checked_at);
49737
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
49738
+ const cachedGitCheckedAt = toIsoTimestamp(readObjectRecord(cachedStatus.git).lastCheckedAt);
49739
+ const lastProbeAt = liveGitCheckedAt ?? heldCheckedAt ?? cachedGitCheckedAt ?? toIsoTimestamp(git.lastCheckedAt) ?? connectionFreshAt ?? toIsoTimestamp(status.updatedAt) ?? toIsoTimestamp(status.lastSeenAt);
49740
+ const connectionReachable = connectionState === "connected" ? true : !connectionState || connectionState === "unknown" || connectionState === "connecting" ? connectionState === "connecting" ? true : null : false;
49741
+ let dataSource;
49742
+ let reachable;
49743
+ if (isSelfNode) {
49744
+ dataSource = "self";
49745
+ reachable = true;
49746
+ } else if (liveTruthProbed) {
49747
+ dataSource = "live";
49748
+ reachable = true;
49749
+ } else if (readBooleanValue(status.gitProbePending) === true) {
49750
+ dataSource = "pending";
49751
+ reachable = connectionReachable;
49752
+ } else if (directTruthUnavailable) {
49753
+ dataSource = "unreachable";
49754
+ reachable = false;
49755
+ } else if (hasGit) {
49756
+ dataSource = "cached";
49757
+ reachable = connectionReachable;
49758
+ } else if (!daemonId) {
49759
+ dataSource = "unconfigured";
49760
+ reachable = null;
49761
+ } else if (connectionState === "connected") {
49762
+ dataSource = "empty";
49763
+ reachable = true;
49764
+ } else {
49765
+ dataSource = "unreachable";
49766
+ reachable = false;
49767
+ }
49768
+ const probeOk = dataSource === "live" || dataSource === "self";
49769
+ let ageMs = null;
49770
+ if (lastProbeAt) {
49771
+ const parsed = Date.parse(lastProbeAt);
49772
+ if (Number.isFinite(parsed)) ageMs = Math.max(0, now() - parsed);
49773
+ }
49774
+ const staleness = classifyMeshNodeStaleness(dataSource, ageMs);
49775
+ return {
49776
+ dataSource,
49777
+ probeOk,
49778
+ reachable,
49779
+ lastProbeAt: lastProbeAt ?? null,
49780
+ ageMs,
49781
+ staleness
49782
+ };
49783
+ }
49588
49784
  function finalizeMeshNodeStatus(args) {
49589
- const { status, node, daemonId, isSelfNode } = args;
49785
+ const { status, node, daemonId, isSelfNode, directTruthUnavailable } = args;
49590
49786
  if (!readStringValue(status.machineStatus)) {
49591
49787
  const cachedStatus = readObjectRecord(node?.cachedStatus);
49592
49788
  const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
49593
49789
  if (machineStatus) status.machineStatus = machineStatus;
49594
49790
  }
49595
49791
  synthesizeMeshNodeFreshnessFromConnection(status);
49792
+ const liveTruthProbed = readBooleanValue(status[MESH_NODE_LIVE_TRUTH_MARKER]) === true;
49793
+ delete status[MESH_NODE_LIVE_TRUTH_MARKER];
49794
+ status.dataFreshness = buildMeshNodeDataFreshness({
49795
+ status,
49796
+ node,
49797
+ isSelfNode,
49798
+ daemonId,
49799
+ liveTruthProbed,
49800
+ directTruthUnavailable
49801
+ });
49596
49802
  const bootstrap = readObjectRecord(node?.worktreeBootstrap);
49597
49803
  if (node?.isLocalWorktree && readStringValue(bootstrap.status)) {
49598
49804
  status.worktreeBootstrap = bootstrap;
@@ -49660,73 +49866,16 @@ var MeshGitProbeCache = class {
49660
49866
  }
49661
49867
  }
49662
49868
  };
49663
- function awaitWithWarmupDeadline(work, opts) {
49664
- const pollMs = Math.max(1, Math.min(opts.pollIntervalMs ?? 200, opts.connectTimeoutMs));
49665
- return new Promise((resolve24, reject) => {
49666
- let done = false;
49667
- let poll;
49668
- let responseTimer;
49669
- const startedAt = Date.now();
49670
- const cleanup = () => {
49671
- if (poll) {
49672
- clearInterval(poll);
49673
- poll = void 0;
49674
- }
49675
- if (responseTimer) {
49676
- clearTimeout(responseTimer);
49677
- responseTimer = void 0;
49678
- }
49679
- };
49680
- const settle = (fn) => {
49681
- if (done) return;
49682
- done = true;
49683
- cleanup();
49684
- fn();
49685
- };
49686
- const armResponse = () => {
49687
- if (responseTimer || done) return;
49688
- responseTimer = setTimeout(
49689
- () => settle(() => reject(new Error("timeout"))),
49690
- opts.responseTimeoutMs
49691
- );
49692
- if (typeof responseTimer.unref === "function") responseTimer.unref();
49693
- };
49694
- const onPoll = () => {
49695
- if (done) return;
49696
- if (opts.isConnected()) {
49697
- if (poll) {
49698
- clearInterval(poll);
49699
- poll = void 0;
49700
- }
49701
- armResponse();
49702
- return;
49703
- }
49704
- if (Date.now() - startedAt >= opts.connectTimeoutMs) {
49705
- settle(() => reject(new Error("timeout")));
49706
- }
49707
- };
49708
- if (opts.isConnected()) {
49709
- armResponse();
49710
- } else {
49711
- poll = setInterval(onPoll, pollMs);
49712
- if (typeof poll.unref === "function") poll.unref();
49713
- }
49714
- work.then(
49715
- (val) => settle(() => resolve24(val)),
49716
- (err) => settle(() => reject(err))
49717
- );
49718
- });
49719
- }
49720
49869
  async function probeRemoteMeshGitStatus(args) {
49721
49870
  if (!args.dispatchMeshCommand) return null;
49722
49871
  const dispatch = args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace, refreshUpstream: true });
49723
- const getConnection = args.getConnection;
49724
- const isConnected = getConnection ? () => readMeshConnectionState(getConnection(args.daemonId)) === "connected" : () => true;
49725
- const remoteResult = await awaitWithWarmupDeadline(dispatch, {
49726
- isConnected,
49872
+ const remoteResult = await awaitWithWarmupDeadline(dispatch, resolveWarmupDeadlineOpts({
49873
+ getConnection: args.getConnection,
49874
+ daemonId: args.daemonId,
49727
49875
  connectTimeoutMs: args.connectTimeoutMs,
49728
- responseTimeoutMs: args.responseTimeoutMs
49729
- });
49876
+ responseTimeoutMs: args.responseTimeoutMs,
49877
+ onMissingGetter: warnMeshWarmupGetterMissingOnce
49878
+ }));
49730
49879
  const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
49731
49880
  if (!remoteGit || typeof remoteGit !== "object" || typeof remoteGit.isGitRepo !== "boolean") return null;
49732
49881
  const reporterPlatform = readStringValue(remoteResult?.reporterPlatform);
@@ -49740,6 +49889,12 @@ var MESH_DIRECT_PROBE_MAX_RETRIES = 2;
49740
49889
  function readMeshConnectionState(connection) {
49741
49890
  return readStringValue(connection?.state);
49742
49891
  }
49892
+ var meshWarmupGetterMissingWarned = /* @__PURE__ */ new Set();
49893
+ function warnMeshWarmupGetterMissingOnce(daemonId) {
49894
+ if (meshWarmupGetterMissingWarned.has(daemonId)) return;
49895
+ meshWarmupGetterMissingWarned.add(daemonId);
49896
+ LOG.warn("Mesh", `Mesh peer connection getter unavailable for ${String(daemonId).slice(0, 12)}; warmup deadline degraded to the combined connect+response window (cannot observe DataChannel open). This avoids a cold-open false-timeout but loses warm/cold precision \u2014 wire getMeshPeerConnectionStatus on this daemon.`);
49897
+ }
49743
49898
  function isMeshConnectionDefinitivelyDown(connection) {
49744
49899
  if (!connection) return true;
49745
49900
  const state = readMeshConnectionState(connection);
@@ -61816,6 +61971,7 @@ async function initDaemonComponents(config) {
61816
61971
  detectedIdes: detectedIdesRef,
61817
61972
  refreshProviderAvailability,
61818
61973
  dispatchMeshCommand: config.dispatchMeshCommand,
61974
+ getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
61819
61975
  onMeshCoordinatorEventForwarded: config.onMeshCoordinatorEventForwarded,
61820
61976
  statusInstanceId: config.statusInstanceId
61821
61977
  };