@adhdev/daemon-core 0.9.82-rc.537 → 0.9.82-rc.539
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/chat/source-resolver.d.ts +21 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +13 -0
- package/dist/index.js +276 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +276 -16
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-completion-synthesis.d.ts +14 -0
- package/dist/mesh/mesh-work-queue.d.ts +22 -0
- package/package.json +3 -3
- package/src/chat/source-resolver.ts +0 -0
- package/src/cli-adapters/cli-state-engine.ts +51 -1
- package/src/commands/chat-commands-read.ts +103 -7
- package/src/mesh/mesh-completion-synthesis.ts +85 -0
- package/src/mesh/mesh-event-forwarding.ts +96 -1
- package/src/mesh/mesh-queue-assignment.ts +7 -3
- package/src/mesh/mesh-reconcile-loop.ts +53 -2
- package/src/mesh/mesh-work-queue.ts +74 -4
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +33 -1
|
@@ -41,6 +41,27 @@ export declare class ChatSourceRegistry {
|
|
|
41
41
|
private readonly records;
|
|
42
42
|
/** Snapshot of current state for diagnostics. Does not mutate. */
|
|
43
43
|
getState(key: ChatSourceSessionKey): ChatSourceState;
|
|
44
|
+
/**
|
|
45
|
+
* Opaque snapshot of a session's FULL record (state + lock + transitions),
|
|
46
|
+
* for callers that need to speculatively `observe()` and then roll back if
|
|
47
|
+
* the resulting decision is undesirable (STICKY-NATIVE hold: a trusted
|
|
48
|
+
* exact-identity session must not flip to PTY on a transient native gap).
|
|
49
|
+
* Returns undefined when the session has no record yet. The snapshot is a
|
|
50
|
+
* shallow copy — transitions array is copied so a later append does not
|
|
51
|
+
* mutate it.
|
|
52
|
+
*/
|
|
53
|
+
snapshotRecord(key: ChatSourceSessionKey): {
|
|
54
|
+
state: ChatSourceState;
|
|
55
|
+
lockedSince: number | undefined;
|
|
56
|
+
transitions: ChatSourceTransition[];
|
|
57
|
+
} | undefined;
|
|
58
|
+
/** Restore a previously snapshotted record, undoing a speculative observe.
|
|
59
|
+
* Passing undefined clears the key (it had no record when snapshotted). */
|
|
60
|
+
restoreRecord(key: ChatSourceSessionKey, snapshot: {
|
|
61
|
+
state: ChatSourceState;
|
|
62
|
+
lockedSince: number | undefined;
|
|
63
|
+
transitions: ChatSourceTransition[];
|
|
64
|
+
} | undefined): void;
|
|
44
65
|
/** Recent transitions, newest last. Empty array when nothing has happened. */
|
|
45
66
|
getTransitions(key: ChatSourceSessionKey): ReadonlyArray<ChatSourceTransition>;
|
|
46
67
|
/** Drop a session. Caller should invoke this when the session is destroyed
|
|
@@ -105,6 +105,7 @@ export declare class CliStateEngine {
|
|
|
105
105
|
* paint and made the engine type "1" repeatedly into the prompt.
|
|
106
106
|
*/
|
|
107
107
|
modalLostAt: number;
|
|
108
|
+
private modalLostRecheckTimer;
|
|
108
109
|
private approvalExitTimeout;
|
|
109
110
|
responseEpoch: number;
|
|
110
111
|
submitPendingUntil: number;
|
|
@@ -230,6 +231,18 @@ export declare class CliStateEngine {
|
|
|
230
231
|
finishResponse(): void;
|
|
231
232
|
private scheduleIdleFinish;
|
|
232
233
|
private cancelPendingIdleFinish;
|
|
234
|
+
/**
|
|
235
|
+
* Schedule one more settled evaluation while pinned to `waiting_approval`
|
|
236
|
+
* with no actionable modal. The settled FSM normally only re-runs on new
|
|
237
|
+
* PTY output; a provider whose modal cue lingers in a form detectStatus
|
|
238
|
+
* still matches (e.g. kimi's questionPattern hitting the user echo) but
|
|
239
|
+
* whose PTY has gone quiet would never get another evaluation, latching
|
|
240
|
+
* `waiting_approval` forever. This timer guarantees the modal-lost recovery
|
|
241
|
+
* in `applyWaitingApproval` is reached even against a silent PTY. It is a
|
|
242
|
+
* no-op once the FSM leaves `waiting_approval` (the re-evaluation itself
|
|
243
|
+
* takes the recovery branch and clears the state).
|
|
244
|
+
*/
|
|
245
|
+
private armModalLostRecheck;
|
|
233
246
|
private armApprovalExitTimeout;
|
|
234
247
|
private armIdleFinishCandidate;
|
|
235
248
|
private shouldDeferIdleTimeoutFinish;
|
package/dist/index.js
CHANGED
|
@@ -419,10 +419,10 @@ function readInjected(value) {
|
|
|
419
419
|
}
|
|
420
420
|
function getDaemonBuildInfo() {
|
|
421
421
|
if (cached) return cached;
|
|
422
|
-
const commit = readInjected(true ? "
|
|
423
|
-
const commitShort = readInjected(true ? "
|
|
424
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
425
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
422
|
+
const commit = readInjected(true ? "35ac849eca76162066561b27633e11827fa19a73" : void 0) ?? "unknown";
|
|
423
|
+
const commitShort = readInjected(true ? "35ac849e" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
424
|
+
const version = readInjected(true ? "0.9.82-rc.539" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
425
|
+
const builtAt = readInjected(true ? "2026-07-15T15:55:08.195Z" : void 0);
|
|
426
426
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
427
427
|
return cached;
|
|
428
428
|
}
|
|
@@ -5712,6 +5712,8 @@ __export(mesh_work_queue_exports, {
|
|
|
5712
5712
|
MESH_TASK_MODES: () => MESH_TASK_MODES,
|
|
5713
5713
|
MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
|
|
5714
5714
|
NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
|
|
5715
|
+
REDRIVE_RECLAIM_REASONS: () => REDRIVE_RECLAIM_REASONS,
|
|
5716
|
+
REDRIVE_SUPERSEDE_WINDOW_MS: () => REDRIVE_SUPERSEDE_WINDOW_MS,
|
|
5715
5717
|
__clearDirectDispatchesForTests: () => __clearDirectDispatchesForTests,
|
|
5716
5718
|
__clearMeshQueueForTests: () => __clearMeshQueueForTests,
|
|
5717
5719
|
__replaceMeshQueueForTests: () => __replaceMeshQueueForTests,
|
|
@@ -6007,10 +6009,21 @@ function normalizeMeshCapabilityTags(value) {
|
|
|
6007
6009
|
return true;
|
|
6008
6010
|
});
|
|
6009
6011
|
}
|
|
6010
|
-
function
|
|
6011
|
-
const
|
|
6012
|
-
|
|
6013
|
-
|
|
6012
|
+
function readNodeProviderTypes(policy) {
|
|
6013
|
+
const record = policy && typeof policy === "object" && !Array.isArray(policy) ? policy : {};
|
|
6014
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6015
|
+
const out = [];
|
|
6016
|
+
const push = (type) => {
|
|
6017
|
+
const trimmed = typeof type === "string" ? type.trim() : "";
|
|
6018
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
6019
|
+
seen.add(trimmed);
|
|
6020
|
+
out.push(trimmed);
|
|
6021
|
+
};
|
|
6022
|
+
for (const slot of normalizeNodeCapabilitySlots(record.slots)) push(slot.provider);
|
|
6023
|
+
if (Array.isArray(record.providerPriority)) {
|
|
6024
|
+
for (const type of record.providerPriority) push(type);
|
|
6025
|
+
}
|
|
6026
|
+
return out;
|
|
6014
6027
|
}
|
|
6015
6028
|
function readNodeOverride(node, key2) {
|
|
6016
6029
|
const overrides = node?.userOverrides;
|
|
@@ -6023,7 +6036,8 @@ function readNodeReporter(node, key2) {
|
|
|
6023
6036
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
6024
6037
|
}
|
|
6025
6038
|
function buildMeshNodeCapabilityTags(node, providerType) {
|
|
6026
|
-
const
|
|
6039
|
+
const pinnedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : void 0;
|
|
6040
|
+
const providerTags = pinnedProvider ? [pinnedProvider] : readNodeProviderTypes(node?.policy);
|
|
6027
6041
|
const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
|
|
6028
6042
|
const os32 = readNodeOverride(node, "platform") ?? readNodeReporter(node, "platform") ?? process.platform;
|
|
6029
6043
|
const arch2 = readNodeOverride(node, "arch") ?? readNodeReporter(node, "arch") ?? process.arch;
|
|
@@ -6031,7 +6045,7 @@ function buildMeshNodeCapabilityTags(node, providerType) {
|
|
|
6031
6045
|
...Array.isArray(node?.capabilities) ? node.capabilities : [],
|
|
6032
6046
|
`os=${os32}`,
|
|
6033
6047
|
`arch=${arch2}`,
|
|
6034
|
-
...
|
|
6048
|
+
...providerTags.map((p) => `provider=${p}`),
|
|
6035
6049
|
// Worktree nodes automatically expose a "worktree=<branch>" tag so that
|
|
6036
6050
|
// mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
|
|
6037
6051
|
// only to the matching worktree node.
|
|
@@ -6534,7 +6548,7 @@ function recordMeshToolCall(opts) {
|
|
|
6534
6548
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
6535
6549
|
}
|
|
6536
6550
|
}
|
|
6537
|
-
var import_crypto6, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS;
|
|
6551
|
+
var import_crypto6, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS, REDRIVE_RECLAIM_REASONS, REDRIVE_SUPERSEDE_WINDOW_MS;
|
|
6538
6552
|
var init_mesh_work_queue = __esm({
|
|
6539
6553
|
"src/mesh/mesh-work-queue.ts"() {
|
|
6540
6554
|
"use strict";
|
|
@@ -6603,6 +6617,12 @@ var init_mesh_work_queue = __esm({
|
|
|
6603
6617
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
6604
6618
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
6605
6619
|
MAX_STRANDED_RECLAIMS = 3;
|
|
6620
|
+
REDRIVE_RECLAIM_REASONS = /* @__PURE__ */ new Set([
|
|
6621
|
+
"delivered_no_turn_deadline",
|
|
6622
|
+
"reclaim_after_unknown_grace",
|
|
6623
|
+
"delivered_not_consumed_redrive"
|
|
6624
|
+
]);
|
|
6625
|
+
REDRIVE_SUPERSEDE_WINDOW_MS = 5 * 6e4;
|
|
6606
6626
|
}
|
|
6607
6627
|
});
|
|
6608
6628
|
|
|
@@ -16841,7 +16861,8 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16841
16861
|
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
16842
16862
|
if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
|
|
16843
16863
|
if (task.requiredTags?.length) {
|
|
16844
|
-
const
|
|
16864
|
+
const slotProviders = resolveNodeCapabilitySlots(node).map((s2) => s2.provider).filter(Boolean);
|
|
16865
|
+
const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
|
|
16845
16866
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
16846
16867
|
return providerCandidates.some(
|
|
16847
16868
|
(p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
|
|
@@ -20430,6 +20451,52 @@ function stopStaleMeshWorker(components, args) {
|
|
|
20430
20451
|
LOG.warn("MeshQueue", `stopStaleMeshWorker error for ${sessionId}: ${e?.message || e}`);
|
|
20431
20452
|
}
|
|
20432
20453
|
}
|
|
20454
|
+
function supersedeRedriveReclaimForLateCompletion(components, meshId, row, completingSessionId, outcome, args) {
|
|
20455
|
+
if (!row.requeueReason || !REDRIVE_RECLAIM_REASONS.has(row.requeueReason)) return false;
|
|
20456
|
+
if (row.status === "completed" || row.status === "failed" || row.status === "cancelled") return false;
|
|
20457
|
+
const requeuedAtMs = Date.parse(row.requeuedAt ?? "");
|
|
20458
|
+
if (!Number.isFinite(requeuedAtMs)) return false;
|
|
20459
|
+
if (Date.now() - requeuedAtMs > REDRIVE_SUPERSEDE_WINDOW_MS) return false;
|
|
20460
|
+
const reDispatchedSessionId = row.assignedSessionId;
|
|
20461
|
+
if (row.status === "assigned" && reDispatchedSessionId && !sessionIdsEquivalent(reDispatchedSessionId, completingSessionId)) {
|
|
20462
|
+
stopStaleMeshWorker(components, {
|
|
20463
|
+
meshId,
|
|
20464
|
+
sessionId: reDispatchedSessionId,
|
|
20465
|
+
nodeId: row.assignedNodeId,
|
|
20466
|
+
providerType: row.assignedProviderType
|
|
20467
|
+
});
|
|
20468
|
+
}
|
|
20469
|
+
endTaskDispatchInFlight(meshId, row.id);
|
|
20470
|
+
updateTaskStatus(meshId, row.id, outcome === "completed" ? "completed" : "failed");
|
|
20471
|
+
if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
|
|
20472
|
+
try {
|
|
20473
|
+
appendLedgerEntry(meshId, {
|
|
20474
|
+
kind: outcome === "completed" ? "task_completed" : "task_failed",
|
|
20475
|
+
sessionId: completingSessionId,
|
|
20476
|
+
nodeId: readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId) || void 0,
|
|
20477
|
+
providerType: readNonEmptyString2(args.metadataEvent.providerType) || void 0,
|
|
20478
|
+
payload: {
|
|
20479
|
+
taskId: row.id,
|
|
20480
|
+
event: args.event,
|
|
20481
|
+
source: "redrive_late_completion_supersede",
|
|
20482
|
+
reclaimReason: row.requeueReason,
|
|
20483
|
+
reclaimAgeMs: Date.now() - requeuedAtMs,
|
|
20484
|
+
finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0
|
|
20485
|
+
}
|
|
20486
|
+
});
|
|
20487
|
+
} catch {
|
|
20488
|
+
}
|
|
20489
|
+
}
|
|
20490
|
+
LOG.warn("MeshQueue", `Late completion superseded re-drive for task ${row.id} on mesh ${meshId} (reclaimed '${row.requeueReason}' ${Math.round((Date.now() - requeuedAtMs) / 1e3)}s ago; completing session ${completingSessionId}) \u2192 flipped ${outcome}${row.status === "assigned" && reDispatchedSessionId && !sessionIdsEquivalent(reDispatchedSessionId, completingSessionId) ? `, stopped duplicate re-dispatch on ${reDispatchedSessionId}` : ""}`);
|
|
20491
|
+
traceMeshEventDrop("redrive_late_completion_supersede", {
|
|
20492
|
+
taskId: row.id,
|
|
20493
|
+
sessionId: completingSessionId,
|
|
20494
|
+
nodeId: row.assignedNodeId ?? args.nodeId,
|
|
20495
|
+
meshId,
|
|
20496
|
+
event: args.event
|
|
20497
|
+
}, `${row.requeueReason} ${Math.round((Date.now() - requeuedAtMs) / 1e3)}s \u2192 ${outcome}`);
|
|
20498
|
+
return true;
|
|
20499
|
+
}
|
|
20433
20500
|
function injectMeshSystemMessage(components, args) {
|
|
20434
20501
|
const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
20435
20502
|
const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
|
|
@@ -20541,6 +20608,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
20541
20608
|
}
|
|
20542
20609
|
});
|
|
20543
20610
|
}
|
|
20611
|
+
} else if (strandedRow && supersedeRedriveReclaimForLateCompletion(components, args.meshId, strandedRow, sessionId, outcome, args)) {
|
|
20544
20612
|
}
|
|
20545
20613
|
} catch {
|
|
20546
20614
|
}
|
|
@@ -21867,6 +21935,50 @@ async function autoPruneStaleDirectDispatches(components, mesh, selfIds, localDa
|
|
|
21867
21935
|
LOG.info("MeshReconcile", `Auto-pruned ${result.prunedCount} orphaned direct dispatch record(s) for mesh ${mesh.id}`);
|
|
21868
21936
|
}
|
|
21869
21937
|
}
|
|
21938
|
+
async function pollAssignedTaskTerminalEvidence(components, mesh, row) {
|
|
21939
|
+
const sessionId = readNonEmptyString2(row.assignedSessionId);
|
|
21940
|
+
const nodeId = readNonEmptyString2(row.assignedNodeId);
|
|
21941
|
+
if (!sessionId || !nodeId) return null;
|
|
21942
|
+
const node = (mesh.nodes ?? []).find((n) => n.id === nodeId);
|
|
21943
|
+
const nodeDaemonId = readNonEmptyString2(node?.daemonId);
|
|
21944
|
+
const localDaemonId = readNonEmptyString2(components.statusInstanceId);
|
|
21945
|
+
const isLocalNode = !nodeDaemonId || daemonIdsEquivalent(nodeDaemonId, localDaemonId) || !!components.instanceManager.getInstance(sessionId);
|
|
21946
|
+
const providerType = readNonEmptyString2(row.assignedProviderType);
|
|
21947
|
+
const readArgs = {
|
|
21948
|
+
sessionId,
|
|
21949
|
+
targetSessionId: sessionId,
|
|
21950
|
+
tailLimit: 10,
|
|
21951
|
+
...node?.workspace ? { workspace: node.workspace } : {},
|
|
21952
|
+
...providerType ? { agentType: providerType, providerType } : {}
|
|
21953
|
+
};
|
|
21954
|
+
let payload = null;
|
|
21955
|
+
try {
|
|
21956
|
+
if (isLocalNode) {
|
|
21957
|
+
const result = await components.commandHandler?.handle("read_chat", readArgs);
|
|
21958
|
+
if (result && result.success === false) return null;
|
|
21959
|
+
payload = unwrapReadChatPayload(result);
|
|
21960
|
+
} else if (components.dispatchMeshCommand) {
|
|
21961
|
+
const result = await components.dispatchMeshCommand(nodeDaemonId, "read_chat", readArgs);
|
|
21962
|
+
payload = unwrapReadChatPayload(result);
|
|
21963
|
+
if (payload && payload.success === false) return null;
|
|
21964
|
+
} else {
|
|
21965
|
+
return null;
|
|
21966
|
+
}
|
|
21967
|
+
} catch {
|
|
21968
|
+
return null;
|
|
21969
|
+
}
|
|
21970
|
+
if (!payload) return null;
|
|
21971
|
+
if (readChatPayloadStatus(payload) !== "idle") return null;
|
|
21972
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
21973
|
+
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
21974
|
+
if (!evidence.finalSummary) return null;
|
|
21975
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString2(row.dispatchTimestamp));
|
|
21976
|
+
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
21977
|
+
if (!(Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs >= dispatchedAtMs)) {
|
|
21978
|
+
return null;
|
|
21979
|
+
}
|
|
21980
|
+
return "completed";
|
|
21981
|
+
}
|
|
21870
21982
|
var init_mesh_completion_synthesis = __esm({
|
|
21871
21983
|
"src/mesh/mesh-completion-synthesis.ts"() {
|
|
21872
21984
|
"use strict";
|
|
@@ -22143,7 +22255,8 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
|
|
|
22143
22255
|
}
|
|
22144
22256
|
return delivered;
|
|
22145
22257
|
}
|
|
22146
|
-
function recoverStrandedAssignedDispatches(components,
|
|
22258
|
+
async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
22259
|
+
const meshId = mesh.id;
|
|
22147
22260
|
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
22148
22261
|
if (!assigned.length) return;
|
|
22149
22262
|
const nowMs = Date.now();
|
|
@@ -22250,6 +22363,36 @@ function recoverStrandedAssignedDispatches(components, meshId, store) {
|
|
|
22250
22363
|
}
|
|
22251
22364
|
reclaimReason = "reclaim_after_unknown_grace";
|
|
22252
22365
|
}
|
|
22366
|
+
const terminalEvidence = await pollAssignedTaskTerminalEvidence(components, mesh, row);
|
|
22367
|
+
if (terminalEvidence) {
|
|
22368
|
+
deliveredNoTurnUnknownStreak.delete(streakKey);
|
|
22369
|
+
updateTaskStatus(meshId, row.id, terminalEvidence);
|
|
22370
|
+
if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
|
|
22371
|
+
try {
|
|
22372
|
+
appendLedgerEntry(meshId, {
|
|
22373
|
+
kind: terminalEvidence === "completed" ? "task_completed" : "task_failed",
|
|
22374
|
+
nodeId: row.assignedNodeId,
|
|
22375
|
+
sessionId: row.assignedSessionId,
|
|
22376
|
+
providerType: row.assignedProviderType,
|
|
22377
|
+
payload: {
|
|
22378
|
+
taskId: row.id,
|
|
22379
|
+
event: "agent:generating_completed",
|
|
22380
|
+
source: "redrive_deadline_transcript_evidence"
|
|
22381
|
+
}
|
|
22382
|
+
});
|
|
22383
|
+
} catch {
|
|
22384
|
+
}
|
|
22385
|
+
}
|
|
22386
|
+
LOG.warn("MeshReconcile", `Skipped delivered-no-turn re-drive for task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}): worker transcript is idle with a final assistant message after dispatch \u2014 the completion event was lost/late, task is ${terminalEvidence}, NOT re-driving`);
|
|
22387
|
+
traceMeshEventDrop("redrive_deadline_transcript_completed", {
|
|
22388
|
+
taskId: row.id,
|
|
22389
|
+
sessionId: row.assignedSessionId,
|
|
22390
|
+
nodeId: row.assignedNodeId,
|
|
22391
|
+
meshId,
|
|
22392
|
+
event: "agent:generating_completed"
|
|
22393
|
+
}, `${reclaimReason} \u2192 transcript ${terminalEvidence}`);
|
|
22394
|
+
continue;
|
|
22395
|
+
}
|
|
22253
22396
|
const reclaimedLost = reclaimStrandedAssignedTask(meshId, row.id, {
|
|
22254
22397
|
reason: reclaimReason,
|
|
22255
22398
|
ageMs: nowMs - dispatchedAtMs
|
|
@@ -22376,7 +22519,7 @@ async function runMeshReconcileTick(components) {
|
|
|
22376
22519
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
22377
22520
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
22378
22521
|
try {
|
|
22379
|
-
recoverStrandedAssignedDispatches(components, mesh
|
|
22522
|
+
await recoverStrandedAssignedDispatches(components, mesh, store);
|
|
22380
22523
|
} catch (e) {
|
|
22381
22524
|
LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
22382
22525
|
}
|
|
@@ -24684,6 +24827,21 @@ function compile3(re, flags) {
|
|
|
24684
24827
|
}
|
|
24685
24828
|
function findQuestionLineIndex(spec, lines) {
|
|
24686
24829
|
const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
|
|
24830
|
+
const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
24831
|
+
const buttonRe = compile3(spec.buttonPattern, buttonFlags);
|
|
24832
|
+
const isButtonLine = (line) => {
|
|
24833
|
+
buttonRe.lastIndex = 0;
|
|
24834
|
+
return buttonRe.test(line);
|
|
24835
|
+
};
|
|
24836
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24837
|
+
if (primary.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: "primary" };
|
|
24838
|
+
}
|
|
24839
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
24840
|
+
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
24841
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24842
|
+
if (re.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
24843
|
+
}
|
|
24844
|
+
}
|
|
24687
24845
|
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24688
24846
|
if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
|
|
24689
24847
|
}
|
|
@@ -25413,6 +25571,7 @@ var init_cli_state_engine = __esm({
|
|
|
25413
25571
|
* paint and made the engine type "1" repeatedly into the prompt.
|
|
25414
25572
|
*/
|
|
25415
25573
|
modalLostAt = 0;
|
|
25574
|
+
modalLostRecheckTimer = null;
|
|
25416
25575
|
approvalExitTimeout = null;
|
|
25417
25576
|
// ── Response tracking ────────────────────────────
|
|
25418
25577
|
responseEpoch = 0;
|
|
@@ -25464,6 +25623,10 @@ var init_cli_state_engine = __esm({
|
|
|
25464
25623
|
setStatus(status, trigger) {
|
|
25465
25624
|
const prev = this.currentStatus;
|
|
25466
25625
|
if (prev === status) return;
|
|
25626
|
+
if (prev === "waiting_approval" && this.modalLostRecheckTimer) {
|
|
25627
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25628
|
+
this.modalLostRecheckTimer = null;
|
|
25629
|
+
}
|
|
25467
25630
|
this.currentStatus = status;
|
|
25468
25631
|
this.statusHistory.push({ status, at: Date.now(), trigger });
|
|
25469
25632
|
if (this.statusHistory.length > 50) this.statusHistory.shift();
|
|
@@ -25624,6 +25787,10 @@ var init_cli_state_engine = __esm({
|
|
|
25624
25787
|
clearTimeout(this.approvalExitTimeout);
|
|
25625
25788
|
this.approvalExitTimeout = null;
|
|
25626
25789
|
}
|
|
25790
|
+
if (this.modalLostRecheckTimer) {
|
|
25791
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25792
|
+
this.modalLostRecheckTimer = null;
|
|
25793
|
+
}
|
|
25627
25794
|
if (this.finishRetryTimer) {
|
|
25628
25795
|
clearTimeout(this.finishRetryTimer);
|
|
25629
25796
|
this.finishRetryTimer = null;
|
|
@@ -25935,7 +26102,7 @@ var init_cli_state_engine = __esm({
|
|
|
25935
26102
|
if (!inCooldown || modal) {
|
|
25936
26103
|
if (!modal) {
|
|
25937
26104
|
LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
25938
|
-
if (this.currentStatus === "waiting_approval"
|
|
26105
|
+
if (this.currentStatus === "waiting_approval") {
|
|
25939
26106
|
const lostAt = this.modalLostAt || Date.now();
|
|
25940
26107
|
if (!this.modalLostAt) this.modalLostAt = lostAt;
|
|
25941
26108
|
if (Date.now() - lostAt >= this.timeouts.approvalCooldown) {
|
|
@@ -25943,6 +26110,8 @@ var init_cli_state_engine = __esm({
|
|
|
25943
26110
|
this.modalLostAt = 0;
|
|
25944
26111
|
this.setStatus("generating", "approval_lost_modal");
|
|
25945
26112
|
this.callbacks.onStatusChange();
|
|
26113
|
+
} else {
|
|
26114
|
+
this.armModalLostRecheck();
|
|
25946
26115
|
}
|
|
25947
26116
|
}
|
|
25948
26117
|
return;
|
|
@@ -26204,6 +26373,25 @@ var init_cli_state_engine = __esm({
|
|
|
26204
26373
|
this.recordTrace("idle_finish_cancelled", { trigger: reason });
|
|
26205
26374
|
}
|
|
26206
26375
|
// ─── Helpers ────────────────────────────────────────────────────────────
|
|
26376
|
+
/**
|
|
26377
|
+
* Schedule one more settled evaluation while pinned to `waiting_approval`
|
|
26378
|
+
* with no actionable modal. The settled FSM normally only re-runs on new
|
|
26379
|
+
* PTY output; a provider whose modal cue lingers in a form detectStatus
|
|
26380
|
+
* still matches (e.g. kimi's questionPattern hitting the user echo) but
|
|
26381
|
+
* whose PTY has gone quiet would never get another evaluation, latching
|
|
26382
|
+
* `waiting_approval` forever. This timer guarantees the modal-lost recovery
|
|
26383
|
+
* in `applyWaitingApproval` is reached even against a silent PTY. It is a
|
|
26384
|
+
* no-op once the FSM leaves `waiting_approval` (the re-evaluation itself
|
|
26385
|
+
* takes the recovery branch and clears the state).
|
|
26386
|
+
*/
|
|
26387
|
+
armModalLostRecheck() {
|
|
26388
|
+
if (this.modalLostRecheckTimer) return;
|
|
26389
|
+
this.modalLostRecheckTimer = setTimeout(() => {
|
|
26390
|
+
this.modalLostRecheckTimer = null;
|
|
26391
|
+
if (this.currentStatus !== "waiting_approval") return;
|
|
26392
|
+
this.evaluateSettled(this.transport.getSnapshot());
|
|
26393
|
+
}, this.timeouts.approvalCooldown);
|
|
26394
|
+
}
|
|
26207
26395
|
armApprovalExitTimeout() {
|
|
26208
26396
|
if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
|
|
26209
26397
|
this.approvalExitTimeout = setTimeout(() => {
|
|
@@ -36046,6 +36234,33 @@ var ChatSourceRegistry = class {
|
|
|
36046
36234
|
getState(key2) {
|
|
36047
36235
|
return this.records.get(key2)?.state ?? INITIAL_CHAT_SOURCE_STATE;
|
|
36048
36236
|
}
|
|
36237
|
+
/**
|
|
36238
|
+
* Opaque snapshot of a session's FULL record (state + lock + transitions),
|
|
36239
|
+
* for callers that need to speculatively `observe()` and then roll back if
|
|
36240
|
+
* the resulting decision is undesirable (STICKY-NATIVE hold: a trusted
|
|
36241
|
+
* exact-identity session must not flip to PTY on a transient native gap).
|
|
36242
|
+
* Returns undefined when the session has no record yet. The snapshot is a
|
|
36243
|
+
* shallow copy — transitions array is copied so a later append does not
|
|
36244
|
+
* mutate it.
|
|
36245
|
+
*/
|
|
36246
|
+
snapshotRecord(key2) {
|
|
36247
|
+
const rec = this.records.get(key2);
|
|
36248
|
+
if (!rec) return void 0;
|
|
36249
|
+
return { state: rec.state, lockedSince: rec.lockedSince, transitions: [...rec.transitions] };
|
|
36250
|
+
}
|
|
36251
|
+
/** Restore a previously snapshotted record, undoing a speculative observe.
|
|
36252
|
+
* Passing undefined clears the key (it had no record when snapshotted). */
|
|
36253
|
+
restoreRecord(key2, snapshot) {
|
|
36254
|
+
if (!snapshot) {
|
|
36255
|
+
this.records.delete(key2);
|
|
36256
|
+
return;
|
|
36257
|
+
}
|
|
36258
|
+
this.records.set(key2, {
|
|
36259
|
+
state: snapshot.state,
|
|
36260
|
+
lockedSince: snapshot.lockedSince,
|
|
36261
|
+
transitions: [...snapshot.transitions]
|
|
36262
|
+
});
|
|
36263
|
+
}
|
|
36049
36264
|
/** Recent transitions, newest last. Empty array when nothing has happened. */
|
|
36050
36265
|
getTransitions(key2) {
|
|
36051
36266
|
return this.records.get(key2)?.transitions ?? [];
|
|
@@ -36469,7 +36684,52 @@ function decideCliReadChatSource(args) {
|
|
|
36469
36684
|
const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
|
|
36470
36685
|
const observation = buildObservationForCli(args, supportsNative);
|
|
36471
36686
|
const sessionKey = chatSourceSessionKey(args.providerType, args.sessionId);
|
|
36687
|
+
const priorSnapshot = CHAT_SOURCE_REGISTRY.snapshotRecord(sessionKey);
|
|
36688
|
+
const priorState = priorSnapshot?.state ?? CHAT_SOURCE_REGISTRY.getState(sessionKey);
|
|
36689
|
+
const eligibleForStickyHold = args.trustedExactNativeIdentity === true && (priorState.name === "NativeLocked" || priorState.name === "Recovering");
|
|
36472
36690
|
let decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
|
|
36691
|
+
if (eligibleForStickyHold && decision.selected === "pty-parser") {
|
|
36692
|
+
CHAT_SOURCE_REGISTRY.restoreRecord(sessionKey, priorSnapshot);
|
|
36693
|
+
const heldNativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
|
|
36694
|
+
const messageSource2 = buildCliMessageSourceProvenance({
|
|
36695
|
+
selected: "native-history",
|
|
36696
|
+
provider: args.providerType,
|
|
36697
|
+
nativeHandle: typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0,
|
|
36698
|
+
sessionWorkspace: args.sessionWorkspace,
|
|
36699
|
+
intendedWorkspace: args.intendedWorkspace,
|
|
36700
|
+
transcriptWorkspace: void 0,
|
|
36701
|
+
fallbackReason: "native_history_transient_gap_held",
|
|
36702
|
+
nativeSource: "provider-native",
|
|
36703
|
+
sourcePath: typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0,
|
|
36704
|
+
sourceMtimeMs: typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0,
|
|
36705
|
+
nativeHistoryCoverage: void 0,
|
|
36706
|
+
partialReason: void 0,
|
|
36707
|
+
unavailableReason: observation.kind === "native_unavailable" ? observation.reason : void 0,
|
|
36708
|
+
nativeMessages: heldNativeMessages,
|
|
36709
|
+
ptyMessages: args.ptyMessages,
|
|
36710
|
+
returnedMessages: heldNativeMessages,
|
|
36711
|
+
safeMapping: args.safeMapping,
|
|
36712
|
+
freshEnough: true,
|
|
36713
|
+
ptyStatusApprovalOnly: true
|
|
36714
|
+
});
|
|
36715
|
+
return {
|
|
36716
|
+
decision: {
|
|
36717
|
+
selected: "native-history",
|
|
36718
|
+
nextState: priorState,
|
|
36719
|
+
transition: {
|
|
36720
|
+
fromState: priorState.name,
|
|
36721
|
+
toState: priorState.name,
|
|
36722
|
+
event: "NoOp",
|
|
36723
|
+
cause: decision.transition.cause,
|
|
36724
|
+
at: Date.now()
|
|
36725
|
+
},
|
|
36726
|
+
lockState: { locked: priorState.name === "NativeLocked" }
|
|
36727
|
+
},
|
|
36728
|
+
messageSource: messageSource2,
|
|
36729
|
+
nativeMessages: heldNativeMessages,
|
|
36730
|
+
nativeSelected: true
|
|
36731
|
+
};
|
|
36732
|
+
}
|
|
36473
36733
|
if (decision.selected === "pty-parser" && args.trustedExactNativeIdentity === true && args.safeMapping && args.ptyMessages.length === 0 && observation.kind === "native_present" && observation.coverage !== "partial" && observation.messages.length > 0) {
|
|
36474
36734
|
CHAT_SOURCE_REGISTRY.clear(sessionKey);
|
|
36475
36735
|
decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
|
|
@@ -36789,7 +37049,7 @@ function sessionSpawnEnvFromAdapter(h, targetSessionId) {
|
|
|
36789
37049
|
function readCliProviderNativeHistory(agentStr, args) {
|
|
36790
37050
|
const canBindFromLiveSession = !args.historySessionId && typeof args.sessionStartedAtMs === "number" && args.sessionStartedAtMs > 0 && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36791
37051
|
const pinnedProviderSessionId = typeof args.pinnedProviderSessionId === "string" ? args.pinnedProviderSessionId.trim() : "";
|
|
36792
|
-
const effectiveHistorySessionId = args.historySessionId ||
|
|
37052
|
+
const effectiveHistorySessionId = args.historySessionId || pinnedProviderSessionId || "";
|
|
36793
37053
|
const workspaceLatestFallback = !effectiveHistorySessionId && !canBindFromLiveSession && !pinnedProviderSessionId && args.allowWorkspaceLatestFallback === true && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36794
37054
|
if (!effectiveHistorySessionId && !canBindFromLiveSession && !workspaceLatestFallback) {
|
|
36795
37055
|
return {
|