@adhdev/daemon-core 0.9.82-rc.519 → 0.9.82-rc.520
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.d.ts +1 -0
- package/dist/index.js +3100 -3079
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3102 -3084
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-node-identity.d.ts +26 -0
- package/package.json +3 -3
- package/src/index.ts +3 -0
- package/src/mesh/mesh-node-identity.ts +42 -0
- package/src/mesh/mesh-queue-assignment.ts +5 -4
|
@@ -81,6 +81,32 @@ export declare function normalizeInlineMeshNodeIdentity(inlineMesh: any): any;
|
|
|
81
81
|
export declare function sanitizeInlineMesh(inlineMesh: any): any;
|
|
82
82
|
export declare function reconcileInlineMeshCache(cached: any, incoming: any): any;
|
|
83
83
|
export declare function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded';
|
|
84
|
+
/**
|
|
85
|
+
* Resolve a node's EFFECTIVE health from whatever telemetry the node object carries,
|
|
86
|
+
* in the same precedence the coordinator surfaces use (applyCachedInlineMeshNodeStatus):
|
|
87
|
+
* 1. an explicit `node.health` scalar (set by a fresh mesh_status probe / status report),
|
|
88
|
+
* 2. else the cached inline status health (`node.cachedStatus.health`),
|
|
89
|
+
* 3. else derived from the node's git telemetry (`node.git` / cachedStatus.git) via
|
|
90
|
+
* deriveMeshNodeHealthFromGit,
|
|
91
|
+
* 4. else 'unknown' (no telemetry — cannot prove unhealthy).
|
|
92
|
+
*
|
|
93
|
+
* This is the SINGLE source of truth for "what is this node's health right now" shared by
|
|
94
|
+
* the auto-launch gate (isMeshNodeHealthLaunchable → isLaunchableNode) and the MAGI fan-out
|
|
95
|
+
* planner, so the two never disagree about whether a degraded node is a viable target.
|
|
96
|
+
* Returns a lowercased string (empty string is normalized to 'unknown').
|
|
97
|
+
*/
|
|
98
|
+
export declare function resolveEffectiveMeshNodeHealth(node: any): string;
|
|
99
|
+
/**
|
|
100
|
+
* Whether a node's health permits launching / assigning a fresh worker session onto it.
|
|
101
|
+
* Mirrors the auto-launch gate in mesh-queue-assignment.isLaunchableNode: 'online' and
|
|
102
|
+
* 'unknown' (and an absent/empty health, treated as unknown) pass — we never block on
|
|
103
|
+
* missing telemetry; every other resolved health ('degraded', 'offline', 'dirty',
|
|
104
|
+
* 'wrong_branch') is NOT launchable. A task assigned to a non-launchable node parks in
|
|
105
|
+
* `pending` forever (isLaunchableNode skips it → node_not_launch_ready) with no
|
|
106
|
+
* re-assignment, so the MAGI planner must exclude such nodes UP FRONT rather than emit a
|
|
107
|
+
* replica that can never run.
|
|
108
|
+
*/
|
|
109
|
+
export declare function isMeshNodeHealthLaunchable(node: any): boolean;
|
|
84
110
|
export declare function applyInlineMeshBranchConvergence(mesh: any, node: any, status: Record<string, unknown>): void;
|
|
85
111
|
export declare function summarizeInlineMeshBranchConvergence(nodes: Array<Record<string, unknown>>): Record<string, unknown>;
|
|
86
112
|
export declare function readCachedInlineMeshActiveSessions(node: any): string[];
|
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.520",
|
|
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.520",
|
|
51
|
+
"@adhdev/session-host-core": "0.9.82-rc.520",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
package/src/index.ts
CHANGED
|
@@ -277,6 +277,9 @@ export type { AnyLedgerSlice, MeshLedgerReconciliationEvidence, MeshLedgerReplic
|
|
|
277
277
|
// ── Mesh Work Queue (GUPP) ──
|
|
278
278
|
export { enqueueTask, recordDirectDispatchTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats, getMeshQueueRevision, normalizeMeshTaskMode, validateMeshTaskModeRequest, isTaskReadonly, buildMeshNodeCapabilityTags, nodeSatisfiesRequiredTags, normalizeMeshCapabilityTags, resolveConvergeRequiredTags, insertDirectDispatch, getActiveDirectDispatches, updateDirectDispatchStatus, cleanupTerminalDirectDispatches, markStaleDirectDispatches, deleteDirectDispatchesByTaskId, recordMeshToolCall, assertNoDependencyCycle, hasPendingDependents, describeTaskDependencyState, taskDependenciesSatisfied, normalizeMeshTaskPriority, meshTaskPriorityRank, resolveNotBefore, meshTaskNotBeforeReady, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS } from './mesh/mesh-work-queue.js';
|
|
279
279
|
export type { MeshWorkQueueEntry, MeshTaskStatus, MeshTaskMode, MeshTaskPriority, MeshWorkQueueStats, MeshQueueMutationOptions, MeshTaskModeValidationResult, DirectDispatchRecord, MeshToolCallRateResult } from './mesh/mesh-work-queue.js';
|
|
280
|
+
// Shared node-health resolver + launch gate (single source of truth for the auto-launch
|
|
281
|
+
// gate AND the MAGI fan-out planner — they must agree on what "launchable health" means).
|
|
282
|
+
export { deriveMeshNodeHealthFromGit, resolveEffectiveMeshNodeHealth, isMeshNodeHealthLaunchable } from './mesh/mesh-node-identity.js';
|
|
280
283
|
export { buildCompactStaleDirectWorkSummary, buildMeshActiveWork, buildMeshActiveWorkSummary, collectPendingApprovals, classifyStaleDirectForPrune, pruneStaleDirectDispatches, PRUNABLE_ORPHAN_STALE_REASONS } from './mesh/mesh-active-work.js';
|
|
281
284
|
export type { StaleDirectPruneClassification, StaleDirectPruneResult, PruneStaleDirectDispatchesOptions } from './mesh/mesh-active-work.js';
|
|
282
285
|
export type { MeshActiveWorkRecord, MeshActiveWorkStatus, MeshActiveWorkSummary, MeshActiveWorkSource, MeshStaleDirectWorkSummary, MeshPendingApproval } from './mesh/mesh-active-work.js';
|
|
@@ -865,6 +865,48 @@ export function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null
|
|
|
865
865
|
return 'online';
|
|
866
866
|
}
|
|
867
867
|
|
|
868
|
+
/**
|
|
869
|
+
* Resolve a node's EFFECTIVE health from whatever telemetry the node object carries,
|
|
870
|
+
* in the same precedence the coordinator surfaces use (applyCachedInlineMeshNodeStatus):
|
|
871
|
+
* 1. an explicit `node.health` scalar (set by a fresh mesh_status probe / status report),
|
|
872
|
+
* 2. else the cached inline status health (`node.cachedStatus.health`),
|
|
873
|
+
* 3. else derived from the node's git telemetry (`node.git` / cachedStatus.git) via
|
|
874
|
+
* deriveMeshNodeHealthFromGit,
|
|
875
|
+
* 4. else 'unknown' (no telemetry — cannot prove unhealthy).
|
|
876
|
+
*
|
|
877
|
+
* This is the SINGLE source of truth for "what is this node's health right now" shared by
|
|
878
|
+
* the auto-launch gate (isMeshNodeHealthLaunchable → isLaunchableNode) and the MAGI fan-out
|
|
879
|
+
* planner, so the two never disagree about whether a degraded node is a viable target.
|
|
880
|
+
* Returns a lowercased string (empty string is normalized to 'unknown').
|
|
881
|
+
*/
|
|
882
|
+
export function resolveEffectiveMeshNodeHealth(node: any): string {
|
|
883
|
+
const explicit = (readStringValue(node?.health) ?? '').toLowerCase();
|
|
884
|
+
if (explicit) return explicit;
|
|
885
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
886
|
+
const cachedHealth = (readStringValue(cachedStatus.health) ?? '').toLowerCase();
|
|
887
|
+
if (cachedHealth) return cachedHealth;
|
|
888
|
+
const git = readObjectRecord(node?.git);
|
|
889
|
+
if (Object.keys(git).length > 0) return deriveMeshNodeHealthFromGit(git).toLowerCase();
|
|
890
|
+
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
891
|
+
if (Object.keys(cachedGit).length > 0) return deriveMeshNodeHealthFromGit(cachedGit).toLowerCase();
|
|
892
|
+
return 'unknown';
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Whether a node's health permits launching / assigning a fresh worker session onto it.
|
|
897
|
+
* Mirrors the auto-launch gate in mesh-queue-assignment.isLaunchableNode: 'online' and
|
|
898
|
+
* 'unknown' (and an absent/empty health, treated as unknown) pass — we never block on
|
|
899
|
+
* missing telemetry; every other resolved health ('degraded', 'offline', 'dirty',
|
|
900
|
+
* 'wrong_branch') is NOT launchable. A task assigned to a non-launchable node parks in
|
|
901
|
+
* `pending` forever (isLaunchableNode skips it → node_not_launch_ready) with no
|
|
902
|
+
* re-assignment, so the MAGI planner must exclude such nodes UP FRONT rather than emit a
|
|
903
|
+
* replica that can never run.
|
|
904
|
+
*/
|
|
905
|
+
export function isMeshNodeHealthLaunchable(node: any): boolean {
|
|
906
|
+
const health = resolveEffectiveMeshNodeHealth(node);
|
|
907
|
+
return health === 'online' || health === 'unknown';
|
|
908
|
+
}
|
|
909
|
+
|
|
868
910
|
function readMeshNodeLabel(status: Record<string, unknown>, node: any): string {
|
|
869
911
|
return readStringValue(status.nodeId, normalizeMeshNodeId(node)) ?? 'unknown';
|
|
870
912
|
}
|
|
@@ -19,7 +19,7 @@ import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalD
|
|
|
19
19
|
import { resolveNodeCapabilitySlots } from './mesh-node-slots.js';
|
|
20
20
|
import { findTerminalLedgerEvidenceForTask, hasUnterminalDirectDispatchLedgerEntry } from './mesh-events-stale.js';
|
|
21
21
|
import { readNonEmptyString } from './mesh-events-utils.js';
|
|
22
|
-
import { readMeshNodeDaemonId } from './mesh-node-identity.js';
|
|
22
|
+
import { readMeshNodeDaemonId, isMeshNodeHealthLaunchable } from './mesh-node-identity.js';
|
|
23
23
|
import { queuePendingMeshCoordinatorEvent, retractPendingDispatchBlockedEvent } from './mesh-events-pending.js';
|
|
24
24
|
import { isWorktreeBootstrapStaleRunning, shouldDeferDispatchForBootstrap } from './worktree-bootstrap-config.js';
|
|
25
25
|
import { isWithinCloneBootstrapGrace } from './mesh-clone-grace.js';
|
|
@@ -1047,9 +1047,10 @@ function nodeHasActiveMeshWork(components: DaemonComponents, meshId: string, nod
|
|
|
1047
1047
|
|
|
1048
1048
|
function isLaunchableNode(node: any): boolean {
|
|
1049
1049
|
if (!node || node.status === 'disabled' || node.status === 'removed') return false;
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1050
|
+
// Delegate the health gate to the shared resolver so the auto-launch gate and the
|
|
1051
|
+
// MAGI fan-out planner agree on exactly what "launchable health" means (online /
|
|
1052
|
+
// unknown / absent pass; degraded / offline / dirty / wrong_branch are blocked).
|
|
1053
|
+
return isMeshNodeHealthLaunchable(node);
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1055
1056
|
/** Whether a mesh node's daemon/machine identity resolves to THIS coordinator daemon
|