@adhdev/daemon-core 0.9.82-rc.395 → 0.9.82-rc.397
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/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/index.js +95 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -33
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-stale.d.ts +9 -0
- package/package.json +2 -2
- package/src/cli-adapters/provider-cli-adapter.ts +52 -3
- package/src/commands/router.ts +7 -0
- package/src/mesh/mesh-events-stale.ts +43 -17
- package/src/mesh/mesh-queue-assignment.ts +5 -2
- package/src/mesh/mesh-reconcile-loop.ts +26 -3
- package/src/mesh/mesh-runtime-store.ts +25 -3
package/dist/index.mjs
CHANGED
|
@@ -378,10 +378,10 @@ function readInjected(value) {
|
|
|
378
378
|
}
|
|
379
379
|
function getDaemonBuildInfo() {
|
|
380
380
|
if (cached) return cached;
|
|
381
|
-
const commit = readInjected(true ? "
|
|
382
|
-
const commitShort = readInjected(true ? "
|
|
383
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
384
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
381
|
+
const commit = readInjected(true ? "5e6a6bcc5ba51a08f3941445f431cb0411217547" : void 0) ?? "unknown";
|
|
382
|
+
const commitShort = readInjected(true ? "5e6a6bcc" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
383
|
+
const version = readInjected(true ? "0.9.82-rc.397" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
384
|
+
const builtAt = readInjected(true ? "2026-06-27T08:32:58.380Z" : void 0);
|
|
385
385
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
386
386
|
return cached;
|
|
387
387
|
}
|
|
@@ -5190,6 +5190,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
5190
5190
|
init_load_better_sqlite3();
|
|
5191
5191
|
init_mesh_ledger();
|
|
5192
5192
|
init_mesh_work_queue();
|
|
5193
|
+
init_dist();
|
|
5193
5194
|
loggedMigrationFailure = false;
|
|
5194
5195
|
MeshRuntimeStore = class _MeshRuntimeStore {
|
|
5195
5196
|
static instance;
|
|
@@ -5722,6 +5723,8 @@ var init_mesh_runtime_store = __esm({
|
|
|
5722
5723
|
if (providerType && typeof providerMaxParallel === "number" && Number.isFinite(providerMaxParallel) && providerMaxParallel >= 0 && this.activeProviderAssignmentCount(meshId, nodeId, providerType) >= providerMaxParallel) {
|
|
5723
5724
|
return null;
|
|
5724
5725
|
}
|
|
5726
|
+
const nodeIdForms = expandDaemonIdForms(nodeId);
|
|
5727
|
+
const nodePinnedPlaceholders = nodeIdForms.map(() => "?").join(", ");
|
|
5725
5728
|
const rows = [
|
|
5726
5729
|
...this.db.prepare(`
|
|
5727
5730
|
SELECT payload FROM mesh_queue
|
|
@@ -5730,9 +5733,9 @@ var init_mesh_runtime_store = __esm({
|
|
|
5730
5733
|
`).all(meshId, sessionId),
|
|
5731
5734
|
...this.db.prepare(`
|
|
5732
5735
|
SELECT payload FROM mesh_queue
|
|
5733
|
-
WHERE mesh_id = ? AND status = 'pending' AND target_node_id
|
|
5736
|
+
WHERE mesh_id = ? AND status = 'pending' AND target_node_id IN (${nodePinnedPlaceholders}) AND target_session_id IS NULL
|
|
5734
5737
|
ORDER BY created_at ASC
|
|
5735
|
-
`).all(meshId,
|
|
5738
|
+
`).all(meshId, ...nodeIdForms),
|
|
5736
5739
|
...this.db.prepare(`
|
|
5737
5740
|
SELECT payload FROM mesh_queue
|
|
5738
5741
|
WHERE mesh_id = ? AND status = 'pending' AND target_node_id IS NULL AND target_session_id IS NULL
|
|
@@ -5762,7 +5765,9 @@ var init_mesh_runtime_store = __esm({
|
|
|
5762
5765
|
const convergenceAllows = (candidate) => candidate.taskMode !== "convergence" || !nodeIsWorktree;
|
|
5763
5766
|
const targetMatches = (candidate) => {
|
|
5764
5767
|
if (candidate.targetSessionId && candidate.targetSessionId !== sessionId) return false;
|
|
5765
|
-
if (candidate.targetNodeId && candidate.targetNodeId
|
|
5768
|
+
if (candidate.targetNodeId && !daemonIdsEquivalent(candidate.targetNodeId, nodeId) && !meshNodeIdMatches({ id: candidate.targetNodeId }, nodeId)) {
|
|
5769
|
+
return false;
|
|
5770
|
+
}
|
|
5766
5771
|
return true;
|
|
5767
5772
|
};
|
|
5768
5773
|
const entry = candidates.find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags) && dependenciesSatisfied(candidate) && convergenceAllows(candidate) && targetMatches(candidate) && nodeConflictAllows(candidate));
|
|
@@ -9859,27 +9864,32 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
|
|
|
9859
9864
|
updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed", args.taskId);
|
|
9860
9865
|
markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
|
|
9861
9866
|
setImmediate(() => cleanupTerminalDirectDispatches());
|
|
9867
|
+
const eventName = kind === "task_completed" ? "agent:generating_completed" : "agent:stopped";
|
|
9868
|
+
const nodeLabel = nodeId ? `Node '${nodeId}'` : "Remote agent";
|
|
9869
|
+
const metadataEvent = {
|
|
9870
|
+
targetSessionId: args.sessionId,
|
|
9871
|
+
providerType: providerType || void 0,
|
|
9872
|
+
providerSessionId: readNonEmptyString2(args.providerSessionId),
|
|
9873
|
+
finalSummary,
|
|
9874
|
+
taskId: args.taskId,
|
|
9875
|
+
workerResult,
|
|
9876
|
+
completionDiagnostic: {
|
|
9877
|
+
reason: "direct_task_transcript_reconciliation",
|
|
9878
|
+
terminalLedgerKind: kind,
|
|
9879
|
+
terminalLedgerId: entry.id
|
|
9880
|
+
}
|
|
9881
|
+
};
|
|
9882
|
+
const targetCoordinatorSessionId = readNonEmptyString2(args.targetCoordinatorSessionId) || readNonEmptyString2(dispatch?.payload?.coordinatorSessionId);
|
|
9862
9883
|
queuePendingMeshCoordinatorEvent({
|
|
9863
|
-
event:
|
|
9884
|
+
event: eventName,
|
|
9864
9885
|
meshId: args.meshId,
|
|
9865
|
-
nodeLabel
|
|
9886
|
+
nodeLabel,
|
|
9866
9887
|
nodeId: nodeId || void 0,
|
|
9867
|
-
metadataEvent
|
|
9868
|
-
|
|
9869
|
-
providerType: providerType || void 0,
|
|
9870
|
-
providerSessionId: readNonEmptyString2(args.providerSessionId),
|
|
9871
|
-
finalSummary,
|
|
9872
|
-
taskId: args.taskId,
|
|
9873
|
-
workerResult,
|
|
9874
|
-
completionDiagnostic: {
|
|
9875
|
-
reason: "direct_task_transcript_reconciliation",
|
|
9876
|
-
terminalLedgerKind: kind,
|
|
9877
|
-
terminalLedgerId: entry.id
|
|
9878
|
-
}
|
|
9879
|
-
},
|
|
9880
|
-
coordinatorMessage: void 0,
|
|
9888
|
+
metadataEvent,
|
|
9889
|
+
coordinatorMessage: buildMeshSystemMessage({ event: eventName, nodeLabel, metadataEvent }),
|
|
9881
9890
|
queuedAt: Date.now(),
|
|
9882
|
-
...readNonEmptyString2(args.targetCoordinatorDaemonId) ? { targetCoordinatorDaemonId: readNonEmptyString2(args.targetCoordinatorDaemonId) } : {}
|
|
9891
|
+
...readNonEmptyString2(args.targetCoordinatorDaemonId) ? { targetCoordinatorDaemonId: readNonEmptyString2(args.targetCoordinatorDaemonId) } : {},
|
|
9892
|
+
...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
|
|
9883
9893
|
});
|
|
9884
9894
|
return { reconciled: true, kind, workerResult, ledgerEntryId: entry.id };
|
|
9885
9895
|
}
|
|
@@ -10774,7 +10784,7 @@ function deliverTaskToSession(dispatchThunk, ctx, warmup) {
|
|
|
10774
10784
|
}
|
|
10775
10785
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
10776
10786
|
const mesh = getMeshWithCache(components, meshId);
|
|
10777
|
-
const node = mesh?.nodes.find((n) =>
|
|
10787
|
+
const node = mesh?.nodes.find((n) => meshNodeIdMatches(n, nodeId));
|
|
10778
10788
|
const gateNode = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
|
|
10779
10789
|
if (gateNode?.worktreeBootstrap?.status === "running") {
|
|
10780
10790
|
LOG.info("MeshQueue", `Gating queue claim for worktree node ${nodeId} (${sessionId}): worktree bootstrap still running \u2014 task left pending; claim re-fires once bootstrap reaches a terminal state (guards against dispatching into a half-built worktree \u2192 empty session)`);
|
|
@@ -11383,7 +11393,7 @@ async function triggerMeshQueue(components, meshId) {
|
|
|
11383
11393
|
const providerType = state.type || readNonEmptyString2(settings.providerType);
|
|
11384
11394
|
if (providerType) {
|
|
11385
11395
|
localIdleSessionsChecked += 1;
|
|
11386
|
-
localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) =>
|
|
11396
|
+
localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => meshNodeIdMatches(n, nodeId)) });
|
|
11387
11397
|
} else {
|
|
11388
11398
|
skippedSessions.push({
|
|
11389
11399
|
nodeId,
|
|
@@ -15217,7 +15227,19 @@ function shouldHoldPendingDrainForBusyLocalCoordinator(components, meshId, reque
|
|
|
15217
15227
|
return localIds.some((id) => daemonIdsEquivalent(id, requested));
|
|
15218
15228
|
}
|
|
15219
15229
|
function injectPendingIntoCoordinator(coordinator, pending) {
|
|
15220
|
-
if (!coordinator
|
|
15230
|
+
if (!coordinator) return;
|
|
15231
|
+
let coordinatorMessage = pending.coordinatorMessage;
|
|
15232
|
+
if (!coordinatorMessage) {
|
|
15233
|
+
if (!shouldForceInjectMeshEvent(pending.event)) return;
|
|
15234
|
+
const metadataEvent = pending.metadataEvent && typeof pending.metadataEvent === "object" ? pending.metadataEvent : {};
|
|
15235
|
+
coordinatorMessage = buildMeshSystemMessage({
|
|
15236
|
+
event: pending.event,
|
|
15237
|
+
nodeLabel: pending.nodeLabel,
|
|
15238
|
+
metadataEvent
|
|
15239
|
+
});
|
|
15240
|
+
if (!coordinatorMessage) return;
|
|
15241
|
+
LOG.warn("MeshReconcile", `Lazily synthesized missing coordinatorMessage for ${pending.event} (mesh ${pending.meshId}) at inject time \u2014 a queued terminal event arrived message-less`);
|
|
15242
|
+
}
|
|
15221
15243
|
const force = shouldForceInjectMeshEvent(pending.event);
|
|
15222
15244
|
traceMeshEventStage("surfaced", {
|
|
15223
15245
|
taskId: pending.metadataEvent?.taskId,
|
|
@@ -15227,7 +15249,7 @@ function injectPendingIntoCoordinator(coordinator, pending) {
|
|
|
15227
15249
|
event: pending.event
|
|
15228
15250
|
}, force ? "force-inject" : "inject");
|
|
15229
15251
|
coordinator.onEvent("send_message", {
|
|
15230
|
-
input: { text:
|
|
15252
|
+
input: { text: coordinatorMessage, textFallback: coordinatorMessage },
|
|
15231
15253
|
...force ? { force: true } : {}
|
|
15232
15254
|
});
|
|
15233
15255
|
}
|
|
@@ -19572,6 +19594,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
19572
19594
|
providerSessionId = null;
|
|
19573
19595
|
responseTimeout = null;
|
|
19574
19596
|
ready = false;
|
|
19597
|
+
// WIN32-READY-HOLD: the ready barrier can release on screen/spec-FSM grace
|
|
19598
|
+
// before win32 ConPTY's input layer is live. The first split write (text, then a
|
|
19599
|
+
// separate trailing CR via waitForEchoAndSubmit) then has its submit CR swallowed,
|
|
19600
|
+
// and every CR-only retry re-sends a bare CR the input layer keeps dropping — the
|
|
19601
|
+
// first message is typed-but-never-submitted and lost. Routing only the FIRST turn
|
|
19602
|
+
// through the atomic content+sendKey single write (submitImmediatePrompt) keeps the
|
|
19603
|
+
// Enter in the same PTY write unit as the text, the invariant win32 ConPTY needs to
|
|
19604
|
+
// recognize a submit, so the swallow is bypassed. Subsequent turns (input layer now
|
|
19605
|
+
// proven live) keep the normal echo-gated path. Flips true on first committed turn.
|
|
19606
|
+
firstTurnSent = false;
|
|
19575
19607
|
startupBuffer = "";
|
|
19576
19608
|
startupParseGate = false;
|
|
19577
19609
|
startupSettleTimer = null;
|
|
@@ -19858,6 +19890,7 @@ ${lastSnapshot}`;
|
|
|
19858
19890
|
this.resetTerminalScreen(DEFAULT_SESSION_HOST_ROWS4, DEFAULT_SESSION_HOST_COLS4);
|
|
19859
19891
|
this.pendingTerminalQueryTail = "";
|
|
19860
19892
|
this.ready = false;
|
|
19893
|
+
this.firstTurnSent = false;
|
|
19861
19894
|
await this.ptyProcess.ready;
|
|
19862
19895
|
this.engine.onSpawnReady();
|
|
19863
19896
|
this.scheduleStartupSettleCheck();
|
|
@@ -20322,6 +20355,7 @@ ${lastSnapshot}`;
|
|
|
20322
20355
|
commitSendUserTurn(state) {
|
|
20323
20356
|
if (state.didCommitUserTurn) return;
|
|
20324
20357
|
state.didCommitUserTurn = true;
|
|
20358
|
+
this.firstTurnSent = true;
|
|
20325
20359
|
}
|
|
20326
20360
|
armResponseTimeout() {
|
|
20327
20361
|
if (this.responseTimeout) clearTimeout(this.responseTimeout);
|
|
@@ -20344,12 +20378,30 @@ ${lastSnapshot}`;
|
|
|
20344
20378
|
LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
|
|
20345
20379
|
});
|
|
20346
20380
|
}
|
|
20381
|
+
// WIN32-READY-HOLD: choose the retry write for a stuck prompt. When the FIRST turn
|
|
20382
|
+
// is stuck on win32 — the premature-ready swallow window — the prompt text itself
|
|
20383
|
+
// may have been partially eaten by a not-yet-live ConPTY input layer, so re-sending
|
|
20384
|
+
// a bare CR keeps hitting nothing. Re-type the whole `text + sendKey` atomically
|
|
20385
|
+
// once so the input layer (now live) receives a self-contained, submit-coupled
|
|
20386
|
+
// write. All other cases keep the cheap bare-CR retry (the prompt is fully echoed
|
|
20387
|
+
// and only the Enter is missing).
|
|
20388
|
+
writeStuckRetry(state, mode) {
|
|
20389
|
+
const retypeFirstTurn = process.platform === "win32" && state.isFirstTurn;
|
|
20390
|
+
if (retypeFirstTurn) {
|
|
20391
|
+
LOG.info("CLI", `[${this.cliType}] ${mode}: re-typing full prompt atomically (win32 first-turn swallow recovery)`);
|
|
20392
|
+
void this.writeToPty(state.text + this.sendKey).catch((error) => {
|
|
20393
|
+
LOG.warn("CLI", `[${this.cliType}] ${mode} re-type write failed: ${error?.message || error}`);
|
|
20394
|
+
});
|
|
20395
|
+
return;
|
|
20396
|
+
}
|
|
20397
|
+
this.writeSubmitKeyForRetry(mode);
|
|
20398
|
+
}
|
|
20347
20399
|
retrySubmitIfStuck(state, attempt) {
|
|
20348
20400
|
this.submitRetryTimer = null;
|
|
20349
20401
|
if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
|
|
20350
20402
|
this.engine.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
20351
20403
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
20352
|
-
this.
|
|
20404
|
+
this.writeStuckRetry(state, "submit_retry");
|
|
20353
20405
|
if (attempt >= 3) {
|
|
20354
20406
|
this.engine.submitRetryUsed = true;
|
|
20355
20407
|
return;
|
|
@@ -20361,7 +20413,7 @@ ${lastSnapshot}`;
|
|
|
20361
20413
|
if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
|
|
20362
20414
|
this.engine.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
20363
20415
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
20364
|
-
this.
|
|
20416
|
+
this.writeStuckRetry(state, "immediate_retry");
|
|
20365
20417
|
this.engine.submitRetryUsed = true;
|
|
20366
20418
|
}
|
|
20367
20419
|
submitSendKey(state, completion) {
|
|
@@ -20614,7 +20666,9 @@ ${lastSnapshot}`;
|
|
|
20614
20666
|
submitDelayMs,
|
|
20615
20667
|
maxEchoWaitMs,
|
|
20616
20668
|
retryDelayMs,
|
|
20617
|
-
didCommitUserTurn: false
|
|
20669
|
+
didCommitUserTurn: false,
|
|
20670
|
+
// Capture BEFORE the send commits — commitSendUserTurn flips firstTurnSent.
|
|
20671
|
+
isFirstTurn: !this.firstTurnSent
|
|
20618
20672
|
};
|
|
20619
20673
|
this.engine.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
20620
20674
|
await new Promise((resolve24, reject) => {
|
|
@@ -20632,7 +20686,8 @@ ${lastSnapshot}`;
|
|
|
20632
20686
|
reject(error);
|
|
20633
20687
|
}
|
|
20634
20688
|
};
|
|
20635
|
-
|
|
20689
|
+
const useAtomicFirstTurn = this.submitStrategy === "immediate" || process.platform === "win32" && sendState.isFirstTurn;
|
|
20690
|
+
if (useAtomicFirstTurn) {
|
|
20636
20691
|
this.submitImmediatePrompt(sendState, completion);
|
|
20637
20692
|
return;
|
|
20638
20693
|
}
|
|
@@ -54013,7 +54068,14 @@ ${hintLines.join("\n")}` : "",
|
|
|
54013
54068
|
nodeId,
|
|
54014
54069
|
sessionCleanupMode: refineSessionCleanupMode,
|
|
54015
54070
|
...refineSessionIds && refineSessionIds.length > 0 ? { sessionIds: refineSessionIds } : {},
|
|
54016
|
-
inlineMesh: args?.inlineMesh
|
|
54071
|
+
inlineMesh: args?.inlineMesh,
|
|
54072
|
+
// REFINE-CLEANUP: refine reaches cleanup only AFTER a verified merge
|
|
54073
|
+
// convergence, so any residual worktree dirtiness here is incidental
|
|
54074
|
+
// (e.g. a bootstrap lockfile rewrite) — never unmerged work. `force`
|
|
54075
|
+
// sets requireClean=false so a plain-dirty worktree no longer aborts
|
|
54076
|
+
// removal with merged_cleanup_failed. Branch-ref deletion still keys off
|
|
54077
|
+
// mergeConvergence (NOT the force flag), so no merged work can be lost.
|
|
54078
|
+
force: true
|
|
54017
54079
|
});
|
|
54018
54080
|
recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
|
|
54019
54081
|
removed: removeResult?.removed,
|