@adhdev/daemon-core 0.9.82-rc.424 → 0.9.82-rc.425
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 +25 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +25 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/mesh/mesh-reconcile-loop.ts +47 -1
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.425",
|
|
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.425",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -44,7 +44,7 @@ import type { DaemonComponents } from '../boot/daemon-lifecycle.js';
|
|
|
44
44
|
import type { LocalMeshEntry } from '../repo-mesh-types.js';
|
|
45
45
|
import { loadConfig } from '../config/config.js';
|
|
46
46
|
import { listMeshes } from '../config/mesh-config.js';
|
|
47
|
-
import { LOG } from '../logging/logger.js';
|
|
47
|
+
import { LOG, getLogLevel } from '../logging/logger.js';
|
|
48
48
|
import { drainPendingMeshCoordinatorEvents, getPendingMeshCoordinatorEvents, buildPendingEventFingerprint, queuePendingMeshCoordinatorEvent } from './mesh-events-pending.js';
|
|
49
49
|
import type { PendingMeshCoordinatorEvent } from './mesh-events-pending.js';
|
|
50
50
|
import { appendLedgerEntry } from './mesh-ledger.js';
|
|
@@ -319,6 +319,38 @@ function findLiveCoordinators(components: DaemonComponents): LiveCoordinator[] {
|
|
|
319
319
|
? (inst as any).isModalParked() === true
|
|
320
320
|
: (status === 'waiting_choice' || status === 'waiting_approval');
|
|
321
321
|
const sessionId = readNonEmptyString(state.instanceId);
|
|
322
|
+
// ── NOTIF (B) desync diagnostic (read-only, no behavior change) ───────────
|
|
323
|
+
// The confirmed (B) defect: a coordinator whose FSM is idle (status above ===
|
|
324
|
+
// 'idle' for minutes) is nonetheless classified busy here, so the generating/
|
|
325
|
+
// modal-park hold never drains and a worker completion is stranded until the
|
|
326
|
+
// user's next turn edge. Static analysis found no code path where getState()
|
|
327
|
+
// returns generating while lastStatus and the adapter raw are both idle — so the
|
|
328
|
+
// divergence is a runtime desync between the three status sources. Capture all
|
|
329
|
+
// three (plus the auto-approve mask state that getState() overlays at :803) for
|
|
330
|
+
// EVERY mesh-coordinator candidate on this tick, so the source that diverges from
|
|
331
|
+
// the others can be read directly against the same-tick "skip → generating"/
|
|
332
|
+
// "skip → modal-parked" hold logs below (pair by sessionId + timestamp).
|
|
333
|
+
//
|
|
334
|
+
// CRITICAL: reuse the `state` already fetched above (line ~301) — do NOT call
|
|
335
|
+
// getState() again. getState() runs maybeAutoApproveStatus() as a side effect,
|
|
336
|
+
// which would mutate the very auto-approve mask we are trying to observe. The
|
|
337
|
+
// adapter raw read uses allowParse:false, which only reads engine.activeModal and
|
|
338
|
+
// is side-effect-free.
|
|
339
|
+
if (getLogLevel() === 'debug') {
|
|
340
|
+
let adapterRaw = '?';
|
|
341
|
+
try {
|
|
342
|
+
const a = (inst as any).adapter;
|
|
343
|
+
if (a && typeof a.getStatus === 'function') {
|
|
344
|
+
adapterRaw = readNonEmptyString(a.getStatus({ allowParse: false })?.status) || '?';
|
|
345
|
+
}
|
|
346
|
+
} catch (e: any) {
|
|
347
|
+
adapterRaw = `err:${e?.message || e}`;
|
|
348
|
+
}
|
|
349
|
+
const lastStatus = readNonEmptyString((inst as any).lastStatus) || '?';
|
|
350
|
+
const autoApproveBusy = (inst as any).autoApproveBusy;
|
|
351
|
+
const maskSince = (inst as any).autoApproveMaskSince;
|
|
352
|
+
LOG.debug('MeshReconcile', `coordDiag sess=${sessionId || '?'} mesh=${meshId} getState=${status || '?'} lastStatus=${lastStatus} adapterRaw=${adapterRaw} autoApproveBusy=${autoApproveBusy === true} maskSince=${maskSince || 0}`);
|
|
353
|
+
}
|
|
322
354
|
// Modal-park transition observability: a coordinator entering modal-park is what
|
|
323
355
|
// begins holding completion events under `modal_parked`; one leaving it is what
|
|
324
356
|
// drains them. Both transitions were previously SILENT (the operator had no log
|
|
@@ -902,6 +934,12 @@ export async function runMeshReconcileTick(components: DaemonComponents): Promis
|
|
|
902
934
|
}
|
|
903
935
|
}
|
|
904
936
|
LOG.info('MeshReconcile', `Reconcile skip → modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued${orphanEscaped > 0 ? `; ${orphanEscaped} orphan-targeted event(s) routed to strict-route TTL` : ''})`);
|
|
937
|
+
// NOTIF (B) diagnostic: name the session(s) classified modal-parked so the
|
|
938
|
+
// same-tick coordDiag line (paired by sessionId) shows whether the modal-park
|
|
939
|
+
// overlay is a real human-await or an unreleased mask (the getState_overlay origin).
|
|
940
|
+
if (getLogLevel() === 'debug') {
|
|
941
|
+
LOG.debug('MeshReconcile', `coordHoldModalParked mesh=${meshId} heldFor=[${modalParkedCoordinators.map(c => c.sessionId || '?').join(',')}] (these were classified modal-parked; cross-ref same-tick coordDiag by sessionId)`);
|
|
942
|
+
}
|
|
905
943
|
// C1: mirror held terminal events into the ledger so a held completion's
|
|
906
944
|
// worker summary is auditable/recoverable even if the modal is never
|
|
907
945
|
// resolved, the coordinator restarts, or the pending file is later trimmed.
|
|
@@ -937,6 +975,14 @@ export async function runMeshReconcileTick(components: DaemonComponents): Promis
|
|
|
937
975
|
}
|
|
938
976
|
if (hasPending) {
|
|
939
977
|
LOG.info('MeshReconcile', `Reconcile skip → generating: holding pending event(s) for mesh ${meshId} (${generatingCoordinators.length} coordinator(s) busy; events left queued for the next idle tick)`);
|
|
978
|
+
// NOTIF (B) diagnostic: this is the hold that strands the completion. Name
|
|
979
|
+
// the sessionId(s) the loop just classified non-idle/non-modal so the
|
|
980
|
+
// same-tick coordDiag line above (paired by sessionId) reveals which status
|
|
981
|
+
// source diverged. If a coordDiag for one of these sessions shows getState
|
|
982
|
+
// (or lastStatus/adapterRaw) === idle, that is the runtime desync origin.
|
|
983
|
+
if (getLogLevel() === 'debug') {
|
|
984
|
+
LOG.debug('MeshReconcile', `coordHoldGenerating mesh=${meshId} heldFor=[${generatingCoordinators.map(c => c.sessionId || '?').join(',')}] (these were classified busy; cross-ref same-tick coordDiag by sessionId)`);
|
|
985
|
+
}
|
|
940
986
|
recordHeldTerminalEventsToLedger(
|
|
941
987
|
meshId,
|
|
942
988
|
drainDaemonIds.length > 0 ? drainDaemonIds : (localDaemonId ? [localDaemonId] : []),
|