@adhdev/daemon-core 0.9.82-rc.390 → 0.9.82-rc.392
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 +24 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/mesh/mesh-event-forwarding.ts +39 -11
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.392",
|
|
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",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.392",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -927,21 +927,49 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
927
927
|
}
|
|
928
928
|
|
|
929
929
|
if (sessionId && nodeId && providerType) {
|
|
930
|
+
// WORKTREE-BOOTSTRAP-DISPATCH-RACE: a freshly cloned worktree session emits
|
|
931
|
+
// agent:ready as soon as the CLI process reaches its idle prompt — which happens
|
|
932
|
+
// BEFORE the worktree bootstrap (npm install + native-addon repair: node-datachannel,
|
|
933
|
+
// better-sqlite3, ghostty-vt-node) has finished. Claiming a queued task on that early
|
|
934
|
+
// ready dispatches work into a session whose runtime is half-built: the child daemon
|
|
935
|
+
// dies loading the absent native addon → the worker session boots empty (no task ever
|
|
936
|
+
// injected), the queue row reports assigned-but-dead, and the work silently leaks to /
|
|
937
|
+
// gets re-routed onto the base node. Live evidence (the OPSRULES dispatch): node
|
|
938
|
+
// node_6df455dd emitted agent:ready at 10:36:28 but worktree_bootstrap_complete only at
|
|
939
|
+
// 10:36:56 — a 28s window in which a claim produced an empty session.
|
|
940
|
+
//
|
|
941
|
+
// Gate the claim on bootstrap state: if this node is a worktree whose bootstrap is still
|
|
942
|
+
// 'running', register the idle session (so it stays a claim candidate) but DEFER the
|
|
943
|
+
// claim. The claim re-fires on the next agent:ready / reconcile drain tick once bootstrap
|
|
944
|
+
// reaches a terminal state. 'failed' is left to flow through unchanged — a failed
|
|
945
|
+
// bootstrap surfaces its own coordinator event and a dispatch there fails loudly rather
|
|
946
|
+
// than silently, which is the correct, visible behavior (deferring forever would hide it).
|
|
947
|
+
let worktreeBootstrapPending = false;
|
|
948
|
+
try {
|
|
949
|
+
const mesh = getMeshWithCache(components, args.meshId);
|
|
950
|
+
const node = mesh?.nodes?.find((n: any) => meshNodeIdMatches(n, nodeId)) as { worktreeBootstrap?: { status?: string } } | undefined;
|
|
951
|
+
worktreeBootstrapPending = node?.worktreeBootstrap?.status === 'running';
|
|
952
|
+
} catch { /* best-effort: unknown bootstrap state → do not defer (prior behavior) */ }
|
|
953
|
+
|
|
930
954
|
sweepExpiredRemoteIdleSessions();
|
|
931
955
|
try {
|
|
932
956
|
MeshRuntimeStore.getInstance().setRemoteIdleSession(args.meshId, nodeId, sessionId, providerType, Date.now() + REMOTE_IDLE_SESSION_TTL_MS);
|
|
933
957
|
} catch { /* best-effort */ }
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
958
|
+
if (worktreeBootstrapPending) {
|
|
959
|
+
LOG.info('MeshQueue', `Deferring queue claim for worktree node ${nodeId} (${sessionId}): worktree bootstrap still running — early agent:ready precedes bootstrap completion; the idle session is registered and the claim will re-fire once bootstrap finishes (guards against dispatching into a half-built worktree → empty session / work leaking to the base node)`);
|
|
960
|
+
} else {
|
|
961
|
+
setImmediate(() => {
|
|
962
|
+
maybeAutoFastForwardIdleNode(components, { meshId: args.meshId, nodeId, sessionId, providerType })
|
|
963
|
+
.finally(() => {
|
|
964
|
+
try {
|
|
965
|
+
const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
966
|
+
if (assigned) MeshRuntimeStore.getInstance().deleteRemoteIdleSession(args.meshId, nodeId, sessionId);
|
|
967
|
+
} catch (e: any) {
|
|
968
|
+
LOG.warn('MeshQueue', `Failed to assign idle queue task after maintenance for ${nodeId}: ${e?.message || e}`);
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
});
|
|
972
|
+
}
|
|
945
973
|
}
|
|
946
974
|
} else if (args.event === 'agent:generating_started') {
|
|
947
975
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|