@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
|
@@ -39,6 +39,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
39
39
|
private providerSessionId;
|
|
40
40
|
private responseTimeout;
|
|
41
41
|
private ready;
|
|
42
|
+
private firstTurnSent;
|
|
42
43
|
private startupBuffer;
|
|
43
44
|
private startupParseGate;
|
|
44
45
|
private startupSettleTimer;
|
|
@@ -178,6 +179,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
178
179
|
private commitSendUserTurn;
|
|
179
180
|
private armResponseTimeout;
|
|
180
181
|
private writeSubmitKeyForRetry;
|
|
182
|
+
private writeStuckRetry;
|
|
181
183
|
private retrySubmitIfStuck;
|
|
182
184
|
private retryImmediateSubmitIfStuck;
|
|
183
185
|
private submitSendKey;
|
package/dist/index.js
CHANGED
|
@@ -383,10 +383,10 @@ function readInjected(value) {
|
|
|
383
383
|
}
|
|
384
384
|
function getDaemonBuildInfo() {
|
|
385
385
|
if (cached) return cached;
|
|
386
|
-
const commit = readInjected(true ? "
|
|
387
|
-
const commitShort = readInjected(true ? "
|
|
388
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
389
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
386
|
+
const commit = readInjected(true ? "5e6a6bcc5ba51a08f3941445f431cb0411217547" : void 0) ?? "unknown";
|
|
387
|
+
const commitShort = readInjected(true ? "5e6a6bcc" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
388
|
+
const version = readInjected(true ? "0.9.82-rc.397" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
389
|
+
const builtAt = readInjected(true ? "2026-06-27T08:32:58.380Z" : void 0);
|
|
390
390
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
391
391
|
return cached;
|
|
392
392
|
}
|
|
@@ -5197,6 +5197,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
5197
5197
|
init_load_better_sqlite3();
|
|
5198
5198
|
init_mesh_ledger();
|
|
5199
5199
|
init_mesh_work_queue();
|
|
5200
|
+
init_dist();
|
|
5200
5201
|
loggedMigrationFailure = false;
|
|
5201
5202
|
MeshRuntimeStore = class _MeshRuntimeStore {
|
|
5202
5203
|
static instance;
|
|
@@ -5729,6 +5730,8 @@ var init_mesh_runtime_store = __esm({
|
|
|
5729
5730
|
if (providerType && typeof providerMaxParallel === "number" && Number.isFinite(providerMaxParallel) && providerMaxParallel >= 0 && this.activeProviderAssignmentCount(meshId, nodeId, providerType) >= providerMaxParallel) {
|
|
5730
5731
|
return null;
|
|
5731
5732
|
}
|
|
5733
|
+
const nodeIdForms = expandDaemonIdForms(nodeId);
|
|
5734
|
+
const nodePinnedPlaceholders = nodeIdForms.map(() => "?").join(", ");
|
|
5732
5735
|
const rows = [
|
|
5733
5736
|
...this.db.prepare(`
|
|
5734
5737
|
SELECT payload FROM mesh_queue
|
|
@@ -5737,9 +5740,9 @@ var init_mesh_runtime_store = __esm({
|
|
|
5737
5740
|
`).all(meshId, sessionId),
|
|
5738
5741
|
...this.db.prepare(`
|
|
5739
5742
|
SELECT payload FROM mesh_queue
|
|
5740
|
-
WHERE mesh_id = ? AND status = 'pending' AND target_node_id
|
|
5743
|
+
WHERE mesh_id = ? AND status = 'pending' AND target_node_id IN (${nodePinnedPlaceholders}) AND target_session_id IS NULL
|
|
5741
5744
|
ORDER BY created_at ASC
|
|
5742
|
-
`).all(meshId,
|
|
5745
|
+
`).all(meshId, ...nodeIdForms),
|
|
5743
5746
|
...this.db.prepare(`
|
|
5744
5747
|
SELECT payload FROM mesh_queue
|
|
5745
5748
|
WHERE mesh_id = ? AND status = 'pending' AND target_node_id IS NULL AND target_session_id IS NULL
|
|
@@ -5769,7 +5772,9 @@ var init_mesh_runtime_store = __esm({
|
|
|
5769
5772
|
const convergenceAllows = (candidate) => candidate.taskMode !== "convergence" || !nodeIsWorktree;
|
|
5770
5773
|
const targetMatches = (candidate) => {
|
|
5771
5774
|
if (candidate.targetSessionId && candidate.targetSessionId !== sessionId) return false;
|
|
5772
|
-
if (candidate.targetNodeId && candidate.targetNodeId
|
|
5775
|
+
if (candidate.targetNodeId && !daemonIdsEquivalent(candidate.targetNodeId, nodeId) && !meshNodeIdMatches({ id: candidate.targetNodeId }, nodeId)) {
|
|
5776
|
+
return false;
|
|
5777
|
+
}
|
|
5773
5778
|
return true;
|
|
5774
5779
|
};
|
|
5775
5780
|
const entry = candidates.find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags) && dependenciesSatisfied(candidate) && convergenceAllows(candidate) && targetMatches(candidate) && nodeConflictAllows(candidate));
|
|
@@ -9866,27 +9871,32 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
|
|
|
9866
9871
|
updateDirectDispatchStatus(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed", args.taskId);
|
|
9867
9872
|
markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === "task_completed" ? "completed" : "failed");
|
|
9868
9873
|
setImmediate(() => cleanupTerminalDirectDispatches());
|
|
9874
|
+
const eventName = kind === "task_completed" ? "agent:generating_completed" : "agent:stopped";
|
|
9875
|
+
const nodeLabel = nodeId ? `Node '${nodeId}'` : "Remote agent";
|
|
9876
|
+
const metadataEvent = {
|
|
9877
|
+
targetSessionId: args.sessionId,
|
|
9878
|
+
providerType: providerType || void 0,
|
|
9879
|
+
providerSessionId: readNonEmptyString2(args.providerSessionId),
|
|
9880
|
+
finalSummary,
|
|
9881
|
+
taskId: args.taskId,
|
|
9882
|
+
workerResult,
|
|
9883
|
+
completionDiagnostic: {
|
|
9884
|
+
reason: "direct_task_transcript_reconciliation",
|
|
9885
|
+
terminalLedgerKind: kind,
|
|
9886
|
+
terminalLedgerId: entry.id
|
|
9887
|
+
}
|
|
9888
|
+
};
|
|
9889
|
+
const targetCoordinatorSessionId = readNonEmptyString2(args.targetCoordinatorSessionId) || readNonEmptyString2(dispatch?.payload?.coordinatorSessionId);
|
|
9869
9890
|
queuePendingMeshCoordinatorEvent({
|
|
9870
|
-
event:
|
|
9891
|
+
event: eventName,
|
|
9871
9892
|
meshId: args.meshId,
|
|
9872
|
-
nodeLabel
|
|
9893
|
+
nodeLabel,
|
|
9873
9894
|
nodeId: nodeId || void 0,
|
|
9874
|
-
metadataEvent
|
|
9875
|
-
|
|
9876
|
-
providerType: providerType || void 0,
|
|
9877
|
-
providerSessionId: readNonEmptyString2(args.providerSessionId),
|
|
9878
|
-
finalSummary,
|
|
9879
|
-
taskId: args.taskId,
|
|
9880
|
-
workerResult,
|
|
9881
|
-
completionDiagnostic: {
|
|
9882
|
-
reason: "direct_task_transcript_reconciliation",
|
|
9883
|
-
terminalLedgerKind: kind,
|
|
9884
|
-
terminalLedgerId: entry.id
|
|
9885
|
-
}
|
|
9886
|
-
},
|
|
9887
|
-
coordinatorMessage: void 0,
|
|
9895
|
+
metadataEvent,
|
|
9896
|
+
coordinatorMessage: buildMeshSystemMessage({ event: eventName, nodeLabel, metadataEvent }),
|
|
9888
9897
|
queuedAt: Date.now(),
|
|
9889
|
-
...readNonEmptyString2(args.targetCoordinatorDaemonId) ? { targetCoordinatorDaemonId: readNonEmptyString2(args.targetCoordinatorDaemonId) } : {}
|
|
9898
|
+
...readNonEmptyString2(args.targetCoordinatorDaemonId) ? { targetCoordinatorDaemonId: readNonEmptyString2(args.targetCoordinatorDaemonId) } : {},
|
|
9899
|
+
...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
|
|
9890
9900
|
});
|
|
9891
9901
|
return { reconciled: true, kind, workerResult, ledgerEntryId: entry.id };
|
|
9892
9902
|
}
|
|
@@ -10778,7 +10788,7 @@ function deliverTaskToSession(dispatchThunk, ctx, warmup) {
|
|
|
10778
10788
|
}
|
|
10779
10789
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
10780
10790
|
const mesh = getMeshWithCache(components, meshId);
|
|
10781
|
-
const node = mesh?.nodes.find((n) =>
|
|
10791
|
+
const node = mesh?.nodes.find((n) => meshNodeIdMatches(n, nodeId));
|
|
10782
10792
|
const gateNode = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
|
|
10783
10793
|
if (gateNode?.worktreeBootstrap?.status === "running") {
|
|
10784
10794
|
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)`);
|
|
@@ -11387,7 +11397,7 @@ async function triggerMeshQueue(components, meshId) {
|
|
|
11387
11397
|
const providerType = state.type || readNonEmptyString2(settings.providerType);
|
|
11388
11398
|
if (providerType) {
|
|
11389
11399
|
localIdleSessionsChecked += 1;
|
|
11390
|
-
localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) =>
|
|
11400
|
+
localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => meshNodeIdMatches(n, nodeId)) });
|
|
11391
11401
|
} else {
|
|
11392
11402
|
skippedSessions.push({
|
|
11393
11403
|
nodeId,
|
|
@@ -15222,7 +15232,19 @@ function shouldHoldPendingDrainForBusyLocalCoordinator(components, meshId, reque
|
|
|
15222
15232
|
return localIds.some((id) => daemonIdsEquivalent(id, requested));
|
|
15223
15233
|
}
|
|
15224
15234
|
function injectPendingIntoCoordinator(coordinator, pending) {
|
|
15225
|
-
if (!coordinator
|
|
15235
|
+
if (!coordinator) return;
|
|
15236
|
+
let coordinatorMessage = pending.coordinatorMessage;
|
|
15237
|
+
if (!coordinatorMessage) {
|
|
15238
|
+
if (!shouldForceInjectMeshEvent(pending.event)) return;
|
|
15239
|
+
const metadataEvent = pending.metadataEvent && typeof pending.metadataEvent === "object" ? pending.metadataEvent : {};
|
|
15240
|
+
coordinatorMessage = buildMeshSystemMessage({
|
|
15241
|
+
event: pending.event,
|
|
15242
|
+
nodeLabel: pending.nodeLabel,
|
|
15243
|
+
metadataEvent
|
|
15244
|
+
});
|
|
15245
|
+
if (!coordinatorMessage) return;
|
|
15246
|
+
LOG.warn("MeshReconcile", `Lazily synthesized missing coordinatorMessage for ${pending.event} (mesh ${pending.meshId}) at inject time \u2014 a queued terminal event arrived message-less`);
|
|
15247
|
+
}
|
|
15226
15248
|
const force = shouldForceInjectMeshEvent(pending.event);
|
|
15227
15249
|
traceMeshEventStage("surfaced", {
|
|
15228
15250
|
taskId: pending.metadataEvent?.taskId,
|
|
@@ -15232,7 +15254,7 @@ function injectPendingIntoCoordinator(coordinator, pending) {
|
|
|
15232
15254
|
event: pending.event
|
|
15233
15255
|
}, force ? "force-inject" : "inject");
|
|
15234
15256
|
coordinator.onEvent("send_message", {
|
|
15235
|
-
input: { text:
|
|
15257
|
+
input: { text: coordinatorMessage, textFallback: coordinatorMessage },
|
|
15236
15258
|
...force ? { force: true } : {}
|
|
15237
15259
|
});
|
|
15238
15260
|
}
|
|
@@ -19579,6 +19601,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
19579
19601
|
providerSessionId = null;
|
|
19580
19602
|
responseTimeout = null;
|
|
19581
19603
|
ready = false;
|
|
19604
|
+
// WIN32-READY-HOLD: the ready barrier can release on screen/spec-FSM grace
|
|
19605
|
+
// before win32 ConPTY's input layer is live. The first split write (text, then a
|
|
19606
|
+
// separate trailing CR via waitForEchoAndSubmit) then has its submit CR swallowed,
|
|
19607
|
+
// and every CR-only retry re-sends a bare CR the input layer keeps dropping — the
|
|
19608
|
+
// first message is typed-but-never-submitted and lost. Routing only the FIRST turn
|
|
19609
|
+
// through the atomic content+sendKey single write (submitImmediatePrompt) keeps the
|
|
19610
|
+
// Enter in the same PTY write unit as the text, the invariant win32 ConPTY needs to
|
|
19611
|
+
// recognize a submit, so the swallow is bypassed. Subsequent turns (input layer now
|
|
19612
|
+
// proven live) keep the normal echo-gated path. Flips true on first committed turn.
|
|
19613
|
+
firstTurnSent = false;
|
|
19582
19614
|
startupBuffer = "";
|
|
19583
19615
|
startupParseGate = false;
|
|
19584
19616
|
startupSettleTimer = null;
|
|
@@ -19865,6 +19897,7 @@ ${lastSnapshot}`;
|
|
|
19865
19897
|
this.resetTerminalScreen(import_session_host_core5.DEFAULT_SESSION_HOST_ROWS, import_session_host_core5.DEFAULT_SESSION_HOST_COLS);
|
|
19866
19898
|
this.pendingTerminalQueryTail = "";
|
|
19867
19899
|
this.ready = false;
|
|
19900
|
+
this.firstTurnSent = false;
|
|
19868
19901
|
await this.ptyProcess.ready;
|
|
19869
19902
|
this.engine.onSpawnReady();
|
|
19870
19903
|
this.scheduleStartupSettleCheck();
|
|
@@ -20329,6 +20362,7 @@ ${lastSnapshot}`;
|
|
|
20329
20362
|
commitSendUserTurn(state) {
|
|
20330
20363
|
if (state.didCommitUserTurn) return;
|
|
20331
20364
|
state.didCommitUserTurn = true;
|
|
20365
|
+
this.firstTurnSent = true;
|
|
20332
20366
|
}
|
|
20333
20367
|
armResponseTimeout() {
|
|
20334
20368
|
if (this.responseTimeout) clearTimeout(this.responseTimeout);
|
|
@@ -20351,12 +20385,30 @@ ${lastSnapshot}`;
|
|
|
20351
20385
|
LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
|
|
20352
20386
|
});
|
|
20353
20387
|
}
|
|
20388
|
+
// WIN32-READY-HOLD: choose the retry write for a stuck prompt. When the FIRST turn
|
|
20389
|
+
// is stuck on win32 — the premature-ready swallow window — the prompt text itself
|
|
20390
|
+
// may have been partially eaten by a not-yet-live ConPTY input layer, so re-sending
|
|
20391
|
+
// a bare CR keeps hitting nothing. Re-type the whole `text + sendKey` atomically
|
|
20392
|
+
// once so the input layer (now live) receives a self-contained, submit-coupled
|
|
20393
|
+
// write. All other cases keep the cheap bare-CR retry (the prompt is fully echoed
|
|
20394
|
+
// and only the Enter is missing).
|
|
20395
|
+
writeStuckRetry(state, mode) {
|
|
20396
|
+
const retypeFirstTurn = process.platform === "win32" && state.isFirstTurn;
|
|
20397
|
+
if (retypeFirstTurn) {
|
|
20398
|
+
LOG.info("CLI", `[${this.cliType}] ${mode}: re-typing full prompt atomically (win32 first-turn swallow recovery)`);
|
|
20399
|
+
void this.writeToPty(state.text + this.sendKey).catch((error) => {
|
|
20400
|
+
LOG.warn("CLI", `[${this.cliType}] ${mode} re-type write failed: ${error?.message || error}`);
|
|
20401
|
+
});
|
|
20402
|
+
return;
|
|
20403
|
+
}
|
|
20404
|
+
this.writeSubmitKeyForRetry(mode);
|
|
20405
|
+
}
|
|
20354
20406
|
retrySubmitIfStuck(state, attempt) {
|
|
20355
20407
|
this.submitRetryTimer = null;
|
|
20356
20408
|
if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
|
|
20357
20409
|
this.engine.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
20358
20410
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
20359
|
-
this.
|
|
20411
|
+
this.writeStuckRetry(state, "submit_retry");
|
|
20360
20412
|
if (attempt >= 3) {
|
|
20361
20413
|
this.engine.submitRetryUsed = true;
|
|
20362
20414
|
return;
|
|
@@ -20368,7 +20420,7 @@ ${lastSnapshot}`;
|
|
|
20368
20420
|
if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
|
|
20369
20421
|
this.engine.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
20370
20422
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
20371
|
-
this.
|
|
20423
|
+
this.writeStuckRetry(state, "immediate_retry");
|
|
20372
20424
|
this.engine.submitRetryUsed = true;
|
|
20373
20425
|
}
|
|
20374
20426
|
submitSendKey(state, completion) {
|
|
@@ -20621,7 +20673,9 @@ ${lastSnapshot}`;
|
|
|
20621
20673
|
submitDelayMs,
|
|
20622
20674
|
maxEchoWaitMs,
|
|
20623
20675
|
retryDelayMs,
|
|
20624
|
-
didCommitUserTurn: false
|
|
20676
|
+
didCommitUserTurn: false,
|
|
20677
|
+
// Capture BEFORE the send commits — commitSendUserTurn flips firstTurnSent.
|
|
20678
|
+
isFirstTurn: !this.firstTurnSent
|
|
20625
20679
|
};
|
|
20626
20680
|
this.engine.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
20627
20681
|
await new Promise((resolve24, reject) => {
|
|
@@ -20639,7 +20693,8 @@ ${lastSnapshot}`;
|
|
|
20639
20693
|
reject(error);
|
|
20640
20694
|
}
|
|
20641
20695
|
};
|
|
20642
|
-
|
|
20696
|
+
const useAtomicFirstTurn = this.submitStrategy === "immediate" || process.platform === "win32" && sendState.isFirstTurn;
|
|
20697
|
+
if (useAtomicFirstTurn) {
|
|
20643
20698
|
this.submitImmediatePrompt(sendState, completion);
|
|
20644
20699
|
return;
|
|
20645
20700
|
}
|
|
@@ -54389,7 +54444,14 @@ ${hintLines.join("\n")}` : "",
|
|
|
54389
54444
|
nodeId,
|
|
54390
54445
|
sessionCleanupMode: refineSessionCleanupMode,
|
|
54391
54446
|
...refineSessionIds && refineSessionIds.length > 0 ? { sessionIds: refineSessionIds } : {},
|
|
54392
|
-
inlineMesh: args?.inlineMesh
|
|
54447
|
+
inlineMesh: args?.inlineMesh,
|
|
54448
|
+
// REFINE-CLEANUP: refine reaches cleanup only AFTER a verified merge
|
|
54449
|
+
// convergence, so any residual worktree dirtiness here is incidental
|
|
54450
|
+
// (e.g. a bootstrap lockfile rewrite) — never unmerged work. `force`
|
|
54451
|
+
// sets requireClean=false so a plain-dirty worktree no longer aborts
|
|
54452
|
+
// removal with merged_cleanup_failed. Branch-ref deletion still keys off
|
|
54453
|
+
// mergeConvergence (NOT the force flag), so no merged work can be lost.
|
|
54454
|
+
force: true
|
|
54393
54455
|
});
|
|
54394
54456
|
recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
|
|
54395
54457
|
removed: removeResult?.removed,
|