@adhdev/daemon-core 0.9.82-rc.505 → 0.9.82-rc.507
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/index.js +52 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +52 -11
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +21 -0
- package/package.json +3 -3
- package/src/commands/high-family/mesh-coordinator-launch.ts +15 -2
- package/src/mesh/coordinator-prompt.ts +66 -0
- package/src/mesh/mesh-event-forwarding.ts +10 -2
- package/src/mesh/mesh-queue-assignment.ts +11 -3
- package/src/mesh/mesh-reconcile-loop.ts +10 -2
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* a static prompt, which is also fine.
|
|
22
22
|
*/
|
|
23
23
|
import type { LocalMeshEntry, RepoMeshStatus } from '../repo-mesh-types.js';
|
|
24
|
+
import type { MagiKindPanelMap } from '@adhdev/mesh-shared';
|
|
24
25
|
/**
|
|
25
26
|
* Cheap, locally-derived "what just happened" snapshot for the coordinator
|
|
26
27
|
* prompt. Built at launch from the local ledger + work-queue stats — no remote
|
|
@@ -91,5 +92,25 @@ export interface CoordinatorPromptContext {
|
|
|
91
92
|
* section.
|
|
92
93
|
*/
|
|
93
94
|
operatingNotes?: CoordinatorOperatingNote[];
|
|
95
|
+
/**
|
|
96
|
+
* Machine-local MAGI kind-panel bindings (`~/.adhdev/meshes.json`
|
|
97
|
+
* `magiKindPanels`), read live at launch. Omitted / empty / all-empty →
|
|
98
|
+
* no "## Configured MAGI panels" section, so a mesh with no MAGI configured
|
|
99
|
+
* renders identically to before. Threaded in the same systematic way as the
|
|
100
|
+
* brain presets: read machine-local config at launch, render a pure section.
|
|
101
|
+
*/
|
|
102
|
+
magiKindPanels?: MagiKindPanelMap;
|
|
94
103
|
}
|
|
95
104
|
export declare function buildCoordinatorSystemPrompt(ctx: CoordinatorPromptContext): string;
|
|
105
|
+
/**
|
|
106
|
+
* Render the machine-local MAGI kind-panel bindings so the coordinator KNOWS
|
|
107
|
+
* which cross-verification panels (rca / design / claim_audit / freeform) are
|
|
108
|
+
* actually configured on this machine. Without this the coordinator only sees
|
|
109
|
+
* the `mesh_magi_*` tools in the static table and has no idea MAGI is set up.
|
|
110
|
+
*
|
|
111
|
+
* Pure — takes the panels map (read live at launch, mirroring how brain presets
|
|
112
|
+
* read getDifficultyBrains). Returns null (section OMITTED) when nothing usable
|
|
113
|
+
* is configured: undefined/null map, or every kind maps to an empty slot list.
|
|
114
|
+
* That keeps a MAGI-less mesh's prompt byte-identical to before.
|
|
115
|
+
*/
|
|
116
|
+
export declare function buildMagiKindPanelsSection(panels: MagiKindPanelMap | undefined | null): string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.507",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"author": "vilmire",
|
|
48
48
|
"license": "AGPL-3.0-or-later",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
51
|
-
"@adhdev/session-host-core": "0.9.82-rc.
|
|
50
|
+
"@adhdev/mesh-shared": "0.9.82-rc.507",
|
|
51
|
+
"@adhdev/session-host-core": "0.9.82-rc.507",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
|
@@ -138,6 +138,19 @@ export const meshCoordinatorLaunchHandlers: Record<string, HighFamilyHandler> =
|
|
|
138
138
|
} catch { return undefined; }
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
// MAGI panels: load the machine-local kind-panel bindings so the
|
|
142
|
+
// coordinator prompt auto-lists which cross-verification panels
|
|
143
|
+
// (rca / design / claim_audit / freeform) are configured. Same
|
|
144
|
+
// systematic pattern as the brain presets — read machine-local
|
|
145
|
+
// config at launch. Best-effort: a read failure or empty map just
|
|
146
|
+
// omits the "## Configured MAGI panels" section.
|
|
147
|
+
const loadMagiKindPanelsBestEffort = async () => {
|
|
148
|
+
try {
|
|
149
|
+
const { listMagiKindPanels } = await import('../../config/mesh-config.js');
|
|
150
|
+
return listMagiKindPanels();
|
|
151
|
+
} catch { return undefined; }
|
|
152
|
+
};
|
|
153
|
+
|
|
141
154
|
// Support inline mesh data from cloud (bypasses local meshes.json lookup)
|
|
142
155
|
let mesh: any;
|
|
143
156
|
if (args?.inlineMesh && typeof args.inlineMesh === 'object') {
|
|
@@ -264,7 +277,7 @@ export const meshCoordinatorLaunchHandlers: Record<string, HighFamilyHandler> =
|
|
|
264
277
|
// Build coordinator prompt first — fail closed on errors.
|
|
265
278
|
let cliCmdSystemPrompt = '';
|
|
266
279
|
try {
|
|
267
|
-
cliCmdSystemPrompt = buildCoordinatorSystemPrompt({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id) });
|
|
280
|
+
cliCmdSystemPrompt = buildCoordinatorSystemPrompt({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort() });
|
|
268
281
|
} catch (error: any) {
|
|
269
282
|
const message = error?.message || String(error);
|
|
270
283
|
LOG.error('MeshCoordinator', `Failed to build coordinator prompt: ${message}`);
|
|
@@ -484,7 +497,7 @@ export const meshCoordinatorLaunchHandlers: Record<string, HighFamilyHandler> =
|
|
|
484
497
|
// broken mesh state is visible instead of silently launching with weaker rules.
|
|
485
498
|
let systemPrompt = '';
|
|
486
499
|
try {
|
|
487
|
-
systemPrompt = buildCoordinatorSystemPrompt({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id) });
|
|
500
|
+
systemPrompt = buildCoordinatorSystemPrompt({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || undefined, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort() });
|
|
488
501
|
} catch (error: any) {
|
|
489
502
|
const message = error?.message || String(error);
|
|
490
503
|
LOG.error('MeshCoordinator', `Failed to build coordinator prompt: ${message}`);
|
|
@@ -34,6 +34,7 @@ import type {
|
|
|
34
34
|
import { mergeAndNormalizePolicy } from '../repo-mesh-types.js';
|
|
35
35
|
import { getDifficultyBrains } from '../config/mesh-config.js';
|
|
36
36
|
import { MESH_TASK_DIFFICULTIES } from '@adhdev/mesh-shared';
|
|
37
|
+
import type { MagiKindPanelMap, MagiSlot, MagiTaskKind } from '@adhdev/mesh-shared';
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* Cheap, locally-derived "what just happened" snapshot for the coordinator
|
|
@@ -109,6 +110,14 @@ export interface CoordinatorPromptContext {
|
|
|
109
110
|
* section.
|
|
110
111
|
*/
|
|
111
112
|
operatingNotes?: CoordinatorOperatingNote[];
|
|
113
|
+
/**
|
|
114
|
+
* Machine-local MAGI kind-panel bindings (`~/.adhdev/meshes.json`
|
|
115
|
+
* `magiKindPanels`), read live at launch. Omitted / empty / all-empty →
|
|
116
|
+
* no "## Configured MAGI panels" section, so a mesh with no MAGI configured
|
|
117
|
+
* renders identically to before. Threaded in the same systematic way as the
|
|
118
|
+
* brain presets: read machine-local config at launch, render a pure section.
|
|
119
|
+
*/
|
|
120
|
+
magiKindPanels?: MagiKindPanelMap;
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/**
|
|
@@ -284,6 +293,11 @@ Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `\nDefault branch: \`
|
|
|
284
293
|
// ── Brain presets (difficulty → model/thinking) ──
|
|
285
294
|
sections.push(buildBrainPresetsSection());
|
|
286
295
|
|
|
296
|
+
// ── Configured MAGI panels (machine-local magiKindPanels) — only present
|
|
297
|
+
// when at least one task_kind has a non-empty slot list. ──
|
|
298
|
+
const magiSection = buildMagiKindPanelsSection(ctx.magiKindPanels);
|
|
299
|
+
if (magiSection) sections.push(magiSection);
|
|
300
|
+
|
|
287
301
|
// ── Tools ──
|
|
288
302
|
sections.push(TOOLS_SECTION);
|
|
289
303
|
|
|
@@ -634,6 +648,58 @@ function buildBrainPresetsSection(): string {
|
|
|
634
648
|
return lines.join('\n');
|
|
635
649
|
}
|
|
636
650
|
|
|
651
|
+
/**
|
|
652
|
+
* Render the machine-local MAGI kind-panel bindings so the coordinator KNOWS
|
|
653
|
+
* which cross-verification panels (rca / design / claim_audit / freeform) are
|
|
654
|
+
* actually configured on this machine. Without this the coordinator only sees
|
|
655
|
+
* the `mesh_magi_*` tools in the static table and has no idea MAGI is set up.
|
|
656
|
+
*
|
|
657
|
+
* Pure — takes the panels map (read live at launch, mirroring how brain presets
|
|
658
|
+
* read getDifficultyBrains). Returns null (section OMITTED) when nothing usable
|
|
659
|
+
* is configured: undefined/null map, or every kind maps to an empty slot list.
|
|
660
|
+
* That keeps a MAGI-less mesh's prompt byte-identical to before.
|
|
661
|
+
*/
|
|
662
|
+
export function buildMagiKindPanelsSection(panels: MagiKindPanelMap | undefined | null): string | null {
|
|
663
|
+
if (!panels) return null;
|
|
664
|
+
// Keep only kinds with a non-empty slot list; drop empty/undefined bindings.
|
|
665
|
+
const configured = (Object.entries(panels) as Array<[MagiTaskKind, MagiSlot[] | undefined]>)
|
|
666
|
+
.filter(([, slots]) => Array.isArray(slots) && slots.length > 0) as Array<[MagiTaskKind, MagiSlot[]]>;
|
|
667
|
+
if (configured.length === 0) return null;
|
|
668
|
+
|
|
669
|
+
const lines = [
|
|
670
|
+
'## Configured MAGI panels',
|
|
671
|
+
'',
|
|
672
|
+
'These machine-local MAGI kind-panels are configured on this mesh — read-only cross-verification quorums:',
|
|
673
|
+
'',
|
|
674
|
+
];
|
|
675
|
+
|
|
676
|
+
for (const [kind, slots] of configured) {
|
|
677
|
+
const replicaCount = slots.reduce((sum, s) => sum + (s.n && s.n > 0 ? s.n : 1), 0);
|
|
678
|
+
const label = replicaCount === slots.length
|
|
679
|
+
? `${slots.length} ${slots.length === 1 ? 'slot' : 'slots'}`
|
|
680
|
+
: `${replicaCount} replicas`;
|
|
681
|
+
const rendered = slots.map(renderMagiSlot).join(', ');
|
|
682
|
+
lines.push(`- **${kind}** (${label}): ${rendered}`);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
lines.push('');
|
|
686
|
+
lines.push('Use these via `mesh_magi_review` (the `task_kind` is REQUIRED — it selects BOTH the output schema and the panel). The live authoritative slot list is `mesh_magi_kind_panel_list`. MAGI worker replicas are read-only and typically do NOT have mesh MCP tools exposed, so for live timing / tool-behavior claims you MUST gather the primary evidence yourself and use MAGI only for independent source-level corroboration.');
|
|
687
|
+
|
|
688
|
+
return lines.join('\n');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/** Render one MAGI slot as `provider[@nodeId][ (model, tags…, xN)]`. */
|
|
692
|
+
function renderMagiSlot(slot: MagiSlot): string {
|
|
693
|
+
let s = slot.provider;
|
|
694
|
+
if (slot.nodeId) s += `@${slot.nodeId}`;
|
|
695
|
+
const extra: string[] = [];
|
|
696
|
+
if (slot.model) extra.push(`model: ${slot.model}`);
|
|
697
|
+
if (slot.capabilityTags && slot.capabilityTags.length) extra.push(`tags: ${slot.capabilityTags.join('+')}`);
|
|
698
|
+
if (slot.n && slot.n > 1) extra.push(`×${slot.n}`);
|
|
699
|
+
if (extra.length) s += ` (${extra.join(', ')})`;
|
|
700
|
+
return s;
|
|
701
|
+
}
|
|
702
|
+
|
|
637
703
|
function buildPolicySection(policy: RepoMeshPolicy): string {
|
|
638
704
|
const rules: string[] = [];
|
|
639
705
|
if (policy.requirePreTaskCheckpoint) rules.push('- Create a git checkpoint **before** starting each task');
|
|
@@ -16,7 +16,7 @@ import { enqueueUnresolvedDelegateForward, nudgeUnresolvedForwardRetry } from '.
|
|
|
16
16
|
import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
|
|
17
17
|
import { getLastDisplayMessage } from '../status/snapshot.js';
|
|
18
18
|
import { resolveDelegatedWorkerAutoApprove } from '../repo-mesh-types.js';
|
|
19
|
-
import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent, type MeshNodeIdentified } from '@adhdev/mesh-shared';
|
|
19
|
+
import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent, withStatusProbeMarker, type MeshNodeIdentified } from '@adhdev/mesh-shared';
|
|
20
20
|
import {
|
|
21
21
|
findRecentTerminalLedgerEvidence,
|
|
22
22
|
findTerminalLedgerEvidenceForTask,
|
|
@@ -760,7 +760,15 @@ function stopStaleMeshWorker(
|
|
|
760
760
|
} catch { /* best-effort */ }
|
|
761
761
|
}
|
|
762
762
|
if (daemonId && components.dispatchMeshCommand) {
|
|
763
|
-
|
|
763
|
+
// OFFLINE-NODE-BLOCKING: this stop is fire-and-forget. Without a short connect-wait,
|
|
764
|
+
// a stop_cli to a worker node whose daemon is powered off leaves a pending request
|
|
765
|
+
// hanging for the full 90s connect deadline before its .catch fires (a leaked
|
|
766
|
+
// pending per stale worker). Stamp the status-origin marker so the daemon-cloud relay
|
|
767
|
+
// grants the SHORT connect-wait budget — an offline node rejects in ~2s and the .catch
|
|
768
|
+
// logs it immediately. The marker only affects the connect wait and is stripped before
|
|
769
|
+
// stop_cli executes, so a live worker is stopped identically. (Best-effort by design:
|
|
770
|
+
// a failed stop only loses the belt-and-suspenders stop; the ack was already rejected.)
|
|
771
|
+
Promise.resolve(components.dispatchMeshCommand(daemonId, 'stop_cli', withStatusProbeMarker(stopArgs)))
|
|
764
772
|
.catch((e: any) => LOG.warn('MeshQueue', `Remote stop of stale worker ${sessionId} on daemon ${daemonId} failed: ${e?.message || e}`));
|
|
765
773
|
} else {
|
|
766
774
|
LOG.warn('MeshQueue', `Cannot stop stale worker ${sessionId}: no local adapter and no resolvable remote daemon id (node ${args.nodeId ?? '?'}). Ack already rejected — task will re-strand-and-fail if the worker completes.`);
|
|
@@ -15,7 +15,7 @@ import { traceMeshEventDrop } from './mesh-event-trace.js';
|
|
|
15
15
|
import { awaitWithWarmupDeadline, resolveWarmupDeadlineOpts } from './mesh-warmup-deadline.js';
|
|
16
16
|
import { resolveDelegatedWorkerAutoApprove, resolveProviderMaxParallel, resolveNodeSchedulingPriority, normalizeMeshSchedulingStrategy, resolveMaxParallelTasks, resolveMaxReadonlyParallelTasks } from '../repo-mesh-types.js';
|
|
17
17
|
import type { RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
|
|
18
|
-
import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, sessionIdsEquivalent, deriveSlotsFromLegacy, normalizeNodeCapabilitySlots, isMeshTaskDifficulty, type MeshNodeIdentified, type NodeCapabilitySlot, type MeshTaskDifficulty } from '@adhdev/mesh-shared';
|
|
18
|
+
import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, sessionIdsEquivalent, deriveSlotsFromLegacy, normalizeNodeCapabilitySlots, isMeshTaskDifficulty, withStatusProbeMarker, type MeshNodeIdentified, type NodeCapabilitySlot, type MeshTaskDifficulty } from '@adhdev/mesh-shared';
|
|
19
19
|
import { findTerminalLedgerEvidenceForTask, hasUnterminalDirectDispatchLedgerEntry } from './mesh-events-stale.js';
|
|
20
20
|
import { readNonEmptyString } from './mesh-events-utils.js';
|
|
21
21
|
import { readMeshNodeDaemonId } from './mesh-node-identity.js';
|
|
@@ -2100,7 +2100,15 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
2100
2100
|
markAutoLaunch(meshId, task.id, { status: 'started', nodeId, providerType: resolved.providerType });
|
|
2101
2101
|
let launchResult: any;
|
|
2102
2102
|
try {
|
|
2103
|
-
|
|
2103
|
+
// OFFLINE-NODE-BLOCKING: no peer-connected pre-check before this remote
|
|
2104
|
+
// launch_cli meant an OFFLINE target node sank the dispatch into the 90s
|
|
2105
|
+
// connect deadline, stalling the 4s auto-launch loop for a full 90s. Stamp
|
|
2106
|
+
// the status-origin marker so the daemon-cloud relay grants the SHORT
|
|
2107
|
+
// connect-wait budget — an offline node throws in ~2s, the catch below sets
|
|
2108
|
+
// the 25s cooldown (autoLaunchCooldownUntil) that already gates retries, so
|
|
2109
|
+
// the loop moves on. The marker only affects the connect wait and is
|
|
2110
|
+
// stripped before launch_cli executes, so a live node spawns identically.
|
|
2111
|
+
launchResult = await components.dispatchMeshCommand!(launchTarget.daemonId!, 'launch_cli', withStatusProbeMarker({
|
|
2104
2112
|
cliType: resolved.providerType,
|
|
2105
2113
|
dir: node.workspace,
|
|
2106
2114
|
settings: remoteSettings,
|
|
@@ -2110,7 +2118,7 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
2110
2118
|
...(effectiveModel ? { initialModel: effectiveModel } : {}),
|
|
2111
2119
|
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
2112
2120
|
...(effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}),
|
|
2113
|
-
});
|
|
2121
|
+
}));
|
|
2114
2122
|
} catch (e: any) {
|
|
2115
2123
|
markAutoLaunch(meshId, task.id, { status: 'failed', reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
|
|
2116
2124
|
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS); sweepExpiredCooldowns();
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
} from './mesh-unresolved-forward-outbox.js';
|
|
60
60
|
import { readNonEmptyString, readMeshCompletionSummary, buildMeshSystemMessage } from './mesh-events-utils.js';
|
|
61
61
|
import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
|
|
62
|
-
import { expandDaemonIdForms, daemonIdsEquivalent, sessionIdsEquivalent, meshNodeIdMatches } from '@adhdev/mesh-shared';
|
|
62
|
+
import { expandDaemonIdForms, daemonIdsEquivalent, sessionIdsEquivalent, meshNodeIdMatches, withStatusProbeMarker } from '@adhdev/mesh-shared';
|
|
63
63
|
import { getQueue, reclaimStrandedAssignedTask, updateTaskStatus } from './mesh-work-queue.js';
|
|
64
64
|
import { resolveSessionBusyVerdict } from './mesh-queue-assignment.js';
|
|
65
65
|
import { readLedgerEntries } from './mesh-ledger.js';
|
|
@@ -1637,7 +1637,15 @@ async function retryUnresolvedDelegateForwards(components: DaemonComponents): Pr
|
|
|
1637
1637
|
let result: any;
|
|
1638
1638
|
try {
|
|
1639
1639
|
traceMeshEventStage('forward_send', entryTraceCtx, `retry → ${entry.coordinatorDaemonId}`);
|
|
1640
|
-
|
|
1640
|
+
// OFFLINE-NODE-BLOCKING: stamp the status-origin marker so the daemon-cloud relay
|
|
1641
|
+
// grants the SHORT connect-wait budget. Without it, a retry to a coordinator whose
|
|
1642
|
+
// daemon is powered off sinks into the 90s connect deadline per entry, serializing
|
|
1643
|
+
// the whole unresolved-forward outbox behind one dead coordinator. With it, the
|
|
1644
|
+
// dispatch throws in ~2s and the entry is left queued for the next tick — the
|
|
1645
|
+
// existing retry-backoff and age-expiry below are unchanged. The marker only
|
|
1646
|
+
// affects the connect wait and is stripped before mesh_forward_event executes, so
|
|
1647
|
+
// delivery semantics are identical.
|
|
1648
|
+
result = await dispatchMeshCommand(entry.coordinatorDaemonId, 'mesh_forward_event', withStatusProbeMarker(pushPayload));
|
|
1641
1649
|
} catch (e: any) {
|
|
1642
1650
|
// Coordinator unreachable (transport threw) — keep the entry queued and try again
|
|
1643
1651
|
// next tick. This is NOT a hard rejection, so it does not count toward the cap;
|