@adhdev/daemon-core 0.9.82-rc.366 → 0.9.82-rc.367
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.
- package/dist/commands/chat-commands.d.ts +27 -0
- package/dist/commands/router.d.ts +34 -0
- package/dist/index.js +167 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +167 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/commands/chat-commands.ts +96 -2
- package/src/commands/router.ts +135 -8
- package/src/mesh/mesh-events-coordinator.ts +43 -12
- package/src/mesh/mesh-reconcile-loop.ts +38 -0
|
@@ -6,6 +6,33 @@ import type { CommandResult, CommandHelpers } from './handler.js';
|
|
|
6
6
|
import { type InputEnvelope } from '../providers/contracts.js';
|
|
7
7
|
export declare const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25000;
|
|
8
8
|
export declare function buildSendInputSignature(input: InputEnvelope): string;
|
|
9
|
+
/**
|
|
10
|
+
* read_chat node scope verdict. One physical daemon hosts a base node plus several
|
|
11
|
+
* worktree nodes; mesh_read_chat always dispatches read_chat with the requested
|
|
12
|
+
* node's workspace (`args.workspace`). When the resolved target session actually
|
|
13
|
+
* lives in a DIFFERENT worktree, returning its transcript — or worse, letting the
|
|
14
|
+
* native-history-by-workspace fallback splice sibling worktree turns into the
|
|
15
|
+
* reply — makes the coordinator believe one session received every worktree's
|
|
16
|
+
* work. This guard refuses a CONFIRMED cross-workspace read instead of mixing.
|
|
17
|
+
*
|
|
18
|
+
* Conservative by design (mirrors the WTCLAIM fix-B "unknown → allow" rule): only
|
|
19
|
+
* a session id that resolves to a known workspace which is unequal to a known
|
|
20
|
+
* intended workspace blocks. When either side is unknown — no targetSessionId, no
|
|
21
|
+
* args.workspace, an unregistered session, the coordinator self-session, or a
|
|
22
|
+
* plain dashboard read that never passes a node workspace — the read proceeds
|
|
23
|
+
* untouched, so base-node and same-daemon coordinator reads never regress.
|
|
24
|
+
*/
|
|
25
|
+
export declare function evaluateReadChatNodeWorkspaceScope(args: {
|
|
26
|
+
targetSessionId?: string;
|
|
27
|
+
intendedWorkspace?: string;
|
|
28
|
+
sessionWorkspace?: string;
|
|
29
|
+
}): {
|
|
30
|
+
scoped: false;
|
|
31
|
+
} | {
|
|
32
|
+
scoped: true;
|
|
33
|
+
intended: string;
|
|
34
|
+
actual: string;
|
|
35
|
+
};
|
|
9
36
|
interface DebugSanitizeOptions {
|
|
10
37
|
maxDepth?: number;
|
|
11
38
|
maxArrayLength?: number;
|
|
@@ -86,6 +86,7 @@ export declare function finalizeMeshNodeStatus(args: {
|
|
|
86
86
|
}): void;
|
|
87
87
|
export declare const MESH_DIRECT_PROBE_TIMEOUT_MS: number;
|
|
88
88
|
export declare const MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS: number;
|
|
89
|
+
export declare const MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS: number;
|
|
89
90
|
/**
|
|
90
91
|
* De-duplicates and rate-limits per-peer git_status probes so a single mesh
|
|
91
92
|
* refresh — or a burst of refreshes from the dashboard auto-retry loop — cannot
|
|
@@ -116,6 +117,37 @@ export declare class MeshGitProbeCache {
|
|
|
116
117
|
*/
|
|
117
118
|
probe(daemonId: string, workspace: string, probe: () => Promise<Record<string, unknown> | null>): Promise<Record<string, unknown> | null>;
|
|
118
119
|
}
|
|
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>;
|
|
119
151
|
/**
|
|
120
152
|
* Probe a remote peer's git_status with a bounded retry budget, but only while
|
|
121
153
|
* the peer is reported `connected`. A single slow (often TURN-relayed) peer can
|
|
@@ -136,6 +168,8 @@ export declare function probeRemoteMeshGitStatusWithRetry(args: {
|
|
|
136
168
|
timeoutMs: number;
|
|
137
169
|
/** Per-attempt timeout for retries (attempts > 0); defaults to timeoutMs. */
|
|
138
170
|
retryTimeoutMs?: number;
|
|
171
|
+
/** Cold-open warmup budget per attempt; defaults to MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS. */
|
|
172
|
+
connectTimeoutMs?: number;
|
|
139
173
|
getConnection?: (daemonId: string) => Record<string, unknown> | null;
|
|
140
174
|
onConnection?: (connection: Record<string, unknown>) => void;
|
|
141
175
|
}): Promise<Record<string, unknown> | null>;
|
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 ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "bb7e66dbc483232a27d1de42cf8ecba3eb0a818c" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "bb7e66db" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.367" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-24T04:45:13.384Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -2903,6 +2903,16 @@ function meshNodeIdMatches(node, candidateId) {
|
|
|
2903
2903
|
if (!trimmed) return false;
|
|
2904
2904
|
return normalizeMeshNodeId(node) === trimmed;
|
|
2905
2905
|
}
|
|
2906
|
+
function normalizeMeshWorkspaceForCompare(dir) {
|
|
2907
|
+
if (typeof dir !== "string") return "";
|
|
2908
|
+
return dir.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
2909
|
+
}
|
|
2910
|
+
function meshWorkspacesEquivalent(a, b) {
|
|
2911
|
+
const left = normalizeMeshWorkspaceForCompare(a);
|
|
2912
|
+
const right = normalizeMeshWorkspaceForCompare(b);
|
|
2913
|
+
if (!left || !right) return false;
|
|
2914
|
+
return left === right;
|
|
2915
|
+
}
|
|
2906
2916
|
function machineCoreFromDaemonId(id) {
|
|
2907
2917
|
const trimmed = readString3(id);
|
|
2908
2918
|
if (!trimmed) return void 0;
|
|
@@ -12856,10 +12866,6 @@ function deliverTaskToSession(dispatchThunk, ctx) {
|
|
|
12856
12866
|
}
|
|
12857
12867
|
});
|
|
12858
12868
|
}
|
|
12859
|
-
function normalizeMeshWorkspaceForCompare(dir) {
|
|
12860
|
-
if (typeof dir !== "string") return "";
|
|
12861
|
-
return dir.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
12862
|
-
}
|
|
12863
12869
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12864
12870
|
const mesh = getMeshWithCache(components, meshId);
|
|
12865
12871
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
@@ -13711,7 +13717,10 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13711
13717
|
if (terminal?.kind === "task_completed" && !sessionHasActiveAssignment(args.meshId, eventSessionId)) {
|
|
13712
13718
|
const newDispatchAfterTerminal = hasDispatchAfterTerminal(args.meshId, eventSessionId, terminal.id);
|
|
13713
13719
|
const supersedesWeakTerminal = isWeakTerminalLedgerPayload(terminal.payload) && isGenuineCompletionEvidence(args.metadataEvent);
|
|
13714
|
-
|
|
13720
|
+
const terminalTaskId = readNonEmptyString2(terminal.payload.taskId);
|
|
13721
|
+
const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId);
|
|
13722
|
+
const distinctTaskCompletion = !!eventTaskId && !!terminalTaskId && eventTaskId !== terminalTaskId;
|
|
13723
|
+
if (!newDispatchAfterTerminal && !supersedesWeakTerminal && !distinctTaskCompletion) {
|
|
13715
13724
|
const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
|
|
13716
13725
|
const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
|
|
13717
13726
|
const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
|
|
@@ -14157,6 +14166,16 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
|
14157
14166
|
nodeId: readNonEmptyString2(routing.nodeId) || readNonEmptyString2(event.meshNodeId) || void 0,
|
|
14158
14167
|
workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
|
|
14159
14168
|
};
|
|
14169
|
+
const selfDaemonIds = resolveCoordinatorDrainDaemonIds(components);
|
|
14170
|
+
if (selfDaemonIds.some((self) => daemonIdsEquivalent(self, coordinatorDaemonId))) {
|
|
14171
|
+
try {
|
|
14172
|
+
handleMeshForwardEvent(components, payload);
|
|
14173
|
+
LOG.info("MeshEvents", `Self-addressed unresolved-delegate ${eventName} routed via local router (coordinator ${coordinatorDaemonId} is self) \u2014 outbox skipped`);
|
|
14174
|
+
} catch (e) {
|
|
14175
|
+
LOG.warn("MeshEvents", `Local route of self-addressed unresolved-delegate ${eventName} failed: ${e?.message || e}`);
|
|
14176
|
+
}
|
|
14177
|
+
return true;
|
|
14178
|
+
}
|
|
14160
14179
|
const persisted = enqueueUnresolvedDelegateForward(coordinatorDaemonId, eventName, payload);
|
|
14161
14180
|
const fwdTraceCtx = {
|
|
14162
14181
|
taskId: payload.taskId,
|
|
@@ -14665,6 +14684,8 @@ async function retryUnresolvedDelegateForwards(components) {
|
|
|
14665
14684
|
expireStaleUnresolvedDelegateForwards();
|
|
14666
14685
|
const entries = peekUnresolvedDelegateForwards();
|
|
14667
14686
|
if (entries.length === 0) return;
|
|
14687
|
+
const selfIds = resolveCoordinatorDaemonIds(components);
|
|
14688
|
+
const isSelfCoordinatorId = (id) => selfIds.some((self) => daemonIdsEquivalent(self, id));
|
|
14668
14689
|
for (const entry of entries) {
|
|
14669
14690
|
const entryTraceCtx = {
|
|
14670
14691
|
taskId: entry.payload.taskId,
|
|
@@ -14672,6 +14693,23 @@ async function retryUnresolvedDelegateForwards(components) {
|
|
|
14672
14693
|
nodeId: readNonEmptyString2(entry.payload.nodeId),
|
|
14673
14694
|
event: readNonEmptyString2(entry.payload.event)
|
|
14674
14695
|
};
|
|
14696
|
+
if (isSelfCoordinatorId(entry.coordinatorDaemonId)) {
|
|
14697
|
+
let localResult;
|
|
14698
|
+
try {
|
|
14699
|
+
traceMeshEventStage("forward_send", entryTraceCtx, `self \u2192 local router (${entry.coordinatorDaemonId})`);
|
|
14700
|
+
localResult = handleMeshForwardEvent(components, entry.payload);
|
|
14701
|
+
} catch (e) {
|
|
14702
|
+
LOG.warn("MeshReconcile", `Local route of self-addressed forward to ${entry.coordinatorDaemonId} threw: ${e?.message || e} \u2014 draining anyway to break the retry loop`);
|
|
14703
|
+
}
|
|
14704
|
+
ackUnresolvedDelegateForward(entry.id);
|
|
14705
|
+
if (localResult && localResult.success === false) {
|
|
14706
|
+
LOG.warn("MeshReconcile", `Self-addressed unresolved-delegate ${readNonEmptyString2(entry.payload.event)} rejected by local router (${readNonEmptyString2(localResult.error) || "no reason"}) \u2014 drained to break the self-forward retry loop`);
|
|
14707
|
+
traceMeshEventDrop("self_forward_local_rejected", entryTraceCtx, readNonEmptyString2(localResult.error) || "no reason");
|
|
14708
|
+
} else {
|
|
14709
|
+
LOG.info("MeshReconcile", `Self-addressed unresolved-delegate ${readNonEmptyString2(entry.payload.event)} routed via local router (coordinator ${entry.coordinatorDaemonId} is self) \u2014 drained`);
|
|
14710
|
+
}
|
|
14711
|
+
continue;
|
|
14712
|
+
}
|
|
14675
14713
|
let result;
|
|
14676
14714
|
try {
|
|
14677
14715
|
traceMeshEventStage("forward_send", entryTraceCtx, `retry \u2192 ${entry.coordinatorDaemonId}`);
|
|
@@ -28227,6 +28265,7 @@ var CHAT_SOURCE_REGISTRY = new ChatSourceRegistry();
|
|
|
28227
28265
|
|
|
28228
28266
|
// src/commands/chat-commands.ts
|
|
28229
28267
|
init_chat_message_normalization();
|
|
28268
|
+
init_dist();
|
|
28230
28269
|
var RECENT_SEND_WINDOW_MS = 1200;
|
|
28231
28270
|
var READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25e3;
|
|
28232
28271
|
var HOT_TAIL_MIN_LIMIT = 60;
|
|
@@ -28788,6 +28827,24 @@ function normalizeComparableWorkspace(value) {
|
|
|
28788
28827
|
if (!text) return "";
|
|
28789
28828
|
return path16.resolve(text);
|
|
28790
28829
|
}
|
|
28830
|
+
function evaluateReadChatNodeWorkspaceScope(args) {
|
|
28831
|
+
const targetSessionId = typeof args.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
28832
|
+
if (!targetSessionId) return { scoped: false };
|
|
28833
|
+
const intended = normalizeMeshWorkspaceForCompare(args.intendedWorkspace);
|
|
28834
|
+
const actual = normalizeMeshWorkspaceForCompare(args.sessionWorkspace);
|
|
28835
|
+
if (!intended || !actual) return { scoped: false };
|
|
28836
|
+
if (intended === actual) return { scoped: false };
|
|
28837
|
+
return { scoped: true, intended, actual };
|
|
28838
|
+
}
|
|
28839
|
+
function resolveTargetSessionActualWorkspace(h, targetSessionId) {
|
|
28840
|
+
const registryWorkspace = h.ctx?.sessionRegistry?.get?.(targetSessionId)?.workspace;
|
|
28841
|
+
if (typeof registryWorkspace === "string" && registryWorkspace.trim()) return registryWorkspace;
|
|
28842
|
+
const adapter = h.getCliAdapter?.(targetSessionId);
|
|
28843
|
+
if (adapter && typeof adapter.workingDir === "string" && adapter.workingDir.trim()) return adapter.workingDir;
|
|
28844
|
+
const instanceWorkspace = getTargetInstance(h, { targetSessionId })?.getState?.()?.workspace;
|
|
28845
|
+
if (typeof instanceWorkspace === "string" && instanceWorkspace.trim()) return instanceWorkspace;
|
|
28846
|
+
return "";
|
|
28847
|
+
}
|
|
28791
28848
|
function isCurrentRuntimePtySafelyAttributed(args) {
|
|
28792
28849
|
if (args.adapter.cliType !== "codex-cli") return false;
|
|
28793
28850
|
if (!Array.isArray(args.ptyMessages) || args.ptyMessages.length === 0) return false;
|
|
@@ -29602,6 +29659,24 @@ async function handleChatHistory(h, args) {
|
|
|
29602
29659
|
}
|
|
29603
29660
|
}
|
|
29604
29661
|
async function handleReadChat(h, args) {
|
|
29662
|
+
{
|
|
29663
|
+
const guardSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
29664
|
+
if (guardSessionId && typeof args?.workspace === "string" && args.workspace.trim()) {
|
|
29665
|
+
const verdict = evaluateReadChatNodeWorkspaceScope({
|
|
29666
|
+
targetSessionId: guardSessionId,
|
|
29667
|
+
intendedWorkspace: args.workspace,
|
|
29668
|
+
sessionWorkspace: resolveTargetSessionActualWorkspace(h, guardSessionId)
|
|
29669
|
+
});
|
|
29670
|
+
if (verdict.scoped) {
|
|
29671
|
+
LOG.info("Command", `[read_chat] node scope mismatch: session ${guardSessionId} workspace "${verdict.actual}" \u2260 requested node workspace "${verdict.intended}" \u2014 refusing cross-worktree transcript`);
|
|
29672
|
+
return {
|
|
29673
|
+
success: false,
|
|
29674
|
+
code: "read_chat_session_node_scope_mismatch",
|
|
29675
|
+
error: `Session ${guardSessionId} belongs to a different worktree (workspace "${verdict.actual}") than the requested node (workspace "${verdict.intended}"). Refusing to return a cross-worktree transcript \u2014 target the node that owns this session.`
|
|
29676
|
+
};
|
|
29677
|
+
}
|
|
29678
|
+
}
|
|
29679
|
+
}
|
|
29605
29680
|
let providerHint = args?.agentType || args?.providerType;
|
|
29606
29681
|
if (!providerHint) {
|
|
29607
29682
|
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
@@ -29990,10 +30065,19 @@ async function handleReadChat(h, args) {
|
|
|
29990
30065
|
ptyStatusApprovalOnly: false
|
|
29991
30066
|
});
|
|
29992
30067
|
if (supportsNative && !decision.nativeSelected) {
|
|
30068
|
+
LOG.debug("Command", `[read_chat] soft pending: no live adapter and native history not safely mappable target=${String(args?.targetSessionId || "")} provider=${agentStr} reason=native_history_not_safely_available`);
|
|
29993
30069
|
return {
|
|
29994
|
-
success:
|
|
30070
|
+
success: true,
|
|
30071
|
+
pending: true,
|
|
30072
|
+
// Both signals are true here: we reached the history-only path
|
|
30073
|
+
// because no live adapter was found (`live_adapter_not_found`),
|
|
30074
|
+
// and native history is not safely mappable
|
|
30075
|
+
// (`native_history_not_safely_available`).
|
|
30076
|
+
reason: "native_history_not_safely_available",
|
|
30077
|
+
reasons: ["live_adapter_not_found", "native_history_not_safely_available"],
|
|
29995
30078
|
code: "native_history_not_safely_available",
|
|
29996
|
-
|
|
30079
|
+
messages: [],
|
|
30080
|
+
status: "idle",
|
|
29997
30081
|
providerSessionId: historyProviderSessionId,
|
|
29998
30082
|
messageSource: decision.messageSource,
|
|
29999
30083
|
transcriptProvenance: decision.messageSource
|
|
@@ -49493,6 +49577,7 @@ function readMeshTimeoutEnvMs(name, defaultMs) {
|
|
|
49493
49577
|
}
|
|
49494
49578
|
var MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_TIMEOUT_MS", 25e3);
|
|
49495
49579
|
var MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS", 25e3);
|
|
49580
|
+
var MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS", 45e3);
|
|
49496
49581
|
var MESH_DIRECT_PROBE_REUSE_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_REUSE_MS", 12e3);
|
|
49497
49582
|
var MeshGitProbeCache = class {
|
|
49498
49583
|
constructor(reuseMs, now = Date.now) {
|
|
@@ -49530,12 +49615,73 @@ var MeshGitProbeCache = class {
|
|
|
49530
49615
|
}
|
|
49531
49616
|
}
|
|
49532
49617
|
};
|
|
49618
|
+
function awaitWithWarmupDeadline(work, opts) {
|
|
49619
|
+
const pollMs = Math.max(1, Math.min(opts.pollIntervalMs ?? 200, opts.connectTimeoutMs));
|
|
49620
|
+
return new Promise((resolve24, reject) => {
|
|
49621
|
+
let done = false;
|
|
49622
|
+
let poll;
|
|
49623
|
+
let responseTimer;
|
|
49624
|
+
const startedAt = Date.now();
|
|
49625
|
+
const cleanup = () => {
|
|
49626
|
+
if (poll) {
|
|
49627
|
+
clearInterval(poll);
|
|
49628
|
+
poll = void 0;
|
|
49629
|
+
}
|
|
49630
|
+
if (responseTimer) {
|
|
49631
|
+
clearTimeout(responseTimer);
|
|
49632
|
+
responseTimer = void 0;
|
|
49633
|
+
}
|
|
49634
|
+
};
|
|
49635
|
+
const settle = (fn) => {
|
|
49636
|
+
if (done) return;
|
|
49637
|
+
done = true;
|
|
49638
|
+
cleanup();
|
|
49639
|
+
fn();
|
|
49640
|
+
};
|
|
49641
|
+
const armResponse = () => {
|
|
49642
|
+
if (responseTimer || done) return;
|
|
49643
|
+
responseTimer = setTimeout(
|
|
49644
|
+
() => settle(() => reject(new Error("timeout"))),
|
|
49645
|
+
opts.responseTimeoutMs
|
|
49646
|
+
);
|
|
49647
|
+
if (typeof responseTimer.unref === "function") responseTimer.unref();
|
|
49648
|
+
};
|
|
49649
|
+
const onPoll = () => {
|
|
49650
|
+
if (done) return;
|
|
49651
|
+
if (opts.isConnected()) {
|
|
49652
|
+
if (poll) {
|
|
49653
|
+
clearInterval(poll);
|
|
49654
|
+
poll = void 0;
|
|
49655
|
+
}
|
|
49656
|
+
armResponse();
|
|
49657
|
+
return;
|
|
49658
|
+
}
|
|
49659
|
+
if (Date.now() - startedAt >= opts.connectTimeoutMs) {
|
|
49660
|
+
settle(() => reject(new Error("timeout")));
|
|
49661
|
+
}
|
|
49662
|
+
};
|
|
49663
|
+
if (opts.isConnected()) {
|
|
49664
|
+
armResponse();
|
|
49665
|
+
} else {
|
|
49666
|
+
poll = setInterval(onPoll, pollMs);
|
|
49667
|
+
if (typeof poll.unref === "function") poll.unref();
|
|
49668
|
+
}
|
|
49669
|
+
work.then(
|
|
49670
|
+
(val) => settle(() => resolve24(val)),
|
|
49671
|
+
(err) => settle(() => reject(err))
|
|
49672
|
+
);
|
|
49673
|
+
});
|
|
49674
|
+
}
|
|
49533
49675
|
async function probeRemoteMeshGitStatus(args) {
|
|
49534
49676
|
if (!args.dispatchMeshCommand) return null;
|
|
49535
|
-
const
|
|
49536
|
-
|
|
49537
|
-
|
|
49538
|
-
|
|
49677
|
+
const dispatch = args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace, refreshUpstream: true });
|
|
49678
|
+
const getConnection = args.getConnection;
|
|
49679
|
+
const isConnected = getConnection ? () => readMeshConnectionState(getConnection(args.daemonId)) === "connected" : () => true;
|
|
49680
|
+
const remoteResult = await awaitWithWarmupDeadline(dispatch, {
|
|
49681
|
+
isConnected,
|
|
49682
|
+
connectTimeoutMs: args.connectTimeoutMs,
|
|
49683
|
+
responseTimeoutMs: args.responseTimeoutMs
|
|
49684
|
+
});
|
|
49539
49685
|
const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
|
|
49540
49686
|
if (!remoteGit || typeof remoteGit !== "object" || typeof remoteGit.isGitRepo !== "boolean") return null;
|
|
49541
49687
|
const reporterPlatform = readStringValue(remoteResult?.reporterPlatform);
|
|
@@ -49570,7 +49716,9 @@ async function probeRemoteMeshGitStatusWithRetry(args) {
|
|
|
49570
49716
|
dispatchMeshCommand: args.dispatchMeshCommand,
|
|
49571
49717
|
daemonId: args.daemonId,
|
|
49572
49718
|
workspace: args.workspace,
|
|
49573
|
-
|
|
49719
|
+
responseTimeoutMs: attempt === 0 ? args.timeoutMs : args.retryTimeoutMs ?? args.timeoutMs,
|
|
49720
|
+
connectTimeoutMs: args.connectTimeoutMs ?? MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS,
|
|
49721
|
+
getConnection: args.getConnection
|
|
49574
49722
|
});
|
|
49575
49723
|
if (remoteGit) return remoteGit;
|
|
49576
49724
|
} catch {
|
|
@@ -49653,6 +49801,7 @@ async function hydrateInlineMeshDirectTruth(args) {
|
|
|
49653
49801
|
workspace,
|
|
49654
49802
|
timeoutMs: MESH_DIRECT_PROBE_TIMEOUT_MS,
|
|
49655
49803
|
retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
|
|
49804
|
+
connectTimeoutMs: MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS,
|
|
49656
49805
|
getConnection: args.getMeshPeerConnectionStatus
|
|
49657
49806
|
});
|
|
49658
49807
|
const remoteGit = args.probeCache ? await args.probeCache.probe(daemonId, workspace, runProbe) : await runProbe();
|
|
@@ -49704,13 +49853,13 @@ function liveSessionRecordMatchesMeshNode(record, meshId, nodeId, nodeWorkspace
|
|
|
49704
49853
|
if (!recordNodeId || recordNodeId !== nodeId) return false;
|
|
49705
49854
|
if (nodeIsMissingLocalWorktree) return false;
|
|
49706
49855
|
const recordWorkspace = readStringValue(record?.workspace);
|
|
49707
|
-
if (nodeWorkspace && recordWorkspace && recordWorkspace
|
|
49856
|
+
if (nodeWorkspace && recordWorkspace && !meshWorkspacesEquivalent(recordWorkspace, nodeWorkspace)) return false;
|
|
49708
49857
|
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
49709
49858
|
return !recordMeshId || recordMeshId === meshId;
|
|
49710
49859
|
}
|
|
49711
49860
|
function liveSessionRecordMatchesMeshWorkspace(record, meshId, workspace) {
|
|
49712
49861
|
const recordWorkspace = readStringValue(record?.workspace);
|
|
49713
|
-
if (!recordWorkspace || !workspace || recordWorkspace
|
|
49862
|
+
if (!recordWorkspace || !workspace || !meshWorkspacesEquivalent(recordWorkspace, workspace)) return false;
|
|
49714
49863
|
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
49715
49864
|
if (recordMeshId) return recordMeshId === meshId;
|
|
49716
49865
|
return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
|