@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
package/dist/index.mjs
CHANGED
|
@@ -414,10 +414,10 @@ function readInjected(value) {
|
|
|
414
414
|
}
|
|
415
415
|
function getDaemonBuildInfo() {
|
|
416
416
|
if (cached) return cached;
|
|
417
|
-
const commit = readInjected(true ? "
|
|
418
|
-
const commitShort = readInjected(true ? "
|
|
419
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
420
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
417
|
+
const commit = readInjected(true ? "35ac849eca76162066561b27633e11827fa19a73" : void 0) ?? "unknown";
|
|
418
|
+
const commitShort = readInjected(true ? "35ac849e" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
419
|
+
const version = readInjected(true ? "0.9.82-rc.539" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
420
|
+
const builtAt = readInjected(true ? "2026-07-15T15:55:08.195Z" : void 0);
|
|
421
421
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
422
422
|
return cached;
|
|
423
423
|
}
|
|
@@ -5705,6 +5705,8 @@ __export(mesh_work_queue_exports, {
|
|
|
5705
5705
|
MESH_TASK_MODES: () => MESH_TASK_MODES,
|
|
5706
5706
|
MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
|
|
5707
5707
|
NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
|
|
5708
|
+
REDRIVE_RECLAIM_REASONS: () => REDRIVE_RECLAIM_REASONS,
|
|
5709
|
+
REDRIVE_SUPERSEDE_WINDOW_MS: () => REDRIVE_SUPERSEDE_WINDOW_MS,
|
|
5708
5710
|
__clearDirectDispatchesForTests: () => __clearDirectDispatchesForTests,
|
|
5709
5711
|
__clearMeshQueueForTests: () => __clearMeshQueueForTests,
|
|
5710
5712
|
__replaceMeshQueueForTests: () => __replaceMeshQueueForTests,
|
|
@@ -6001,10 +6003,21 @@ function normalizeMeshCapabilityTags(value) {
|
|
|
6001
6003
|
return true;
|
|
6002
6004
|
});
|
|
6003
6005
|
}
|
|
6004
|
-
function
|
|
6005
|
-
const
|
|
6006
|
-
|
|
6007
|
-
|
|
6006
|
+
function readNodeProviderTypes(policy) {
|
|
6007
|
+
const record = policy && typeof policy === "object" && !Array.isArray(policy) ? policy : {};
|
|
6008
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6009
|
+
const out = [];
|
|
6010
|
+
const push = (type) => {
|
|
6011
|
+
const trimmed = typeof type === "string" ? type.trim() : "";
|
|
6012
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
6013
|
+
seen.add(trimmed);
|
|
6014
|
+
out.push(trimmed);
|
|
6015
|
+
};
|
|
6016
|
+
for (const slot of normalizeNodeCapabilitySlots(record.slots)) push(slot.provider);
|
|
6017
|
+
if (Array.isArray(record.providerPriority)) {
|
|
6018
|
+
for (const type of record.providerPriority) push(type);
|
|
6019
|
+
}
|
|
6020
|
+
return out;
|
|
6008
6021
|
}
|
|
6009
6022
|
function readNodeOverride(node, key2) {
|
|
6010
6023
|
const overrides = node?.userOverrides;
|
|
@@ -6017,7 +6030,8 @@ function readNodeReporter(node, key2) {
|
|
|
6017
6030
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
6018
6031
|
}
|
|
6019
6032
|
function buildMeshNodeCapabilityTags(node, providerType) {
|
|
6020
|
-
const
|
|
6033
|
+
const pinnedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : void 0;
|
|
6034
|
+
const providerTags = pinnedProvider ? [pinnedProvider] : readNodeProviderTypes(node?.policy);
|
|
6021
6035
|
const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
|
|
6022
6036
|
const os32 = readNodeOverride(node, "platform") ?? readNodeReporter(node, "platform") ?? process.platform;
|
|
6023
6037
|
const arch2 = readNodeOverride(node, "arch") ?? readNodeReporter(node, "arch") ?? process.arch;
|
|
@@ -6025,7 +6039,7 @@ function buildMeshNodeCapabilityTags(node, providerType) {
|
|
|
6025
6039
|
...Array.isArray(node?.capabilities) ? node.capabilities : [],
|
|
6026
6040
|
`os=${os32}`,
|
|
6027
6041
|
`arch=${arch2}`,
|
|
6028
|
-
...
|
|
6042
|
+
...providerTags.map((p) => `provider=${p}`),
|
|
6029
6043
|
// Worktree nodes automatically expose a "worktree=<branch>" tag so that
|
|
6030
6044
|
// mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
|
|
6031
6045
|
// only to the matching worktree node.
|
|
@@ -6528,7 +6542,7 @@ function recordMeshToolCall(opts) {
|
|
|
6528
6542
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
6529
6543
|
}
|
|
6530
6544
|
}
|
|
6531
|
-
var 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;
|
|
6545
|
+
var 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;
|
|
6532
6546
|
var init_mesh_work_queue = __esm({
|
|
6533
6547
|
"src/mesh/mesh-work-queue.ts"() {
|
|
6534
6548
|
"use strict";
|
|
@@ -6596,6 +6610,12 @@ var init_mesh_work_queue = __esm({
|
|
|
6596
6610
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
6597
6611
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
6598
6612
|
MAX_STRANDED_RECLAIMS = 3;
|
|
6613
|
+
REDRIVE_RECLAIM_REASONS = /* @__PURE__ */ new Set([
|
|
6614
|
+
"delivered_no_turn_deadline",
|
|
6615
|
+
"reclaim_after_unknown_grace",
|
|
6616
|
+
"delivered_not_consumed_redrive"
|
|
6617
|
+
]);
|
|
6618
|
+
REDRIVE_SUPERSEDE_WINDOW_MS = 5 * 6e4;
|
|
6599
6619
|
}
|
|
6600
6620
|
});
|
|
6601
6621
|
|
|
@@ -16844,7 +16864,8 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16844
16864
|
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
16845
16865
|
if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
|
|
16846
16866
|
if (task.requiredTags?.length) {
|
|
16847
|
-
const
|
|
16867
|
+
const slotProviders = resolveNodeCapabilitySlots(node).map((s2) => s2.provider).filter(Boolean);
|
|
16868
|
+
const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
|
|
16848
16869
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
16849
16870
|
return providerCandidates.some(
|
|
16850
16871
|
(p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
|
|
@@ -20432,6 +20453,52 @@ function stopStaleMeshWorker(components, args) {
|
|
|
20432
20453
|
LOG.warn("MeshQueue", `stopStaleMeshWorker error for ${sessionId}: ${e?.message || e}`);
|
|
20433
20454
|
}
|
|
20434
20455
|
}
|
|
20456
|
+
function supersedeRedriveReclaimForLateCompletion(components, meshId, row, completingSessionId, outcome, args) {
|
|
20457
|
+
if (!row.requeueReason || !REDRIVE_RECLAIM_REASONS.has(row.requeueReason)) return false;
|
|
20458
|
+
if (row.status === "completed" || row.status === "failed" || row.status === "cancelled") return false;
|
|
20459
|
+
const requeuedAtMs = Date.parse(row.requeuedAt ?? "");
|
|
20460
|
+
if (!Number.isFinite(requeuedAtMs)) return false;
|
|
20461
|
+
if (Date.now() - requeuedAtMs > REDRIVE_SUPERSEDE_WINDOW_MS) return false;
|
|
20462
|
+
const reDispatchedSessionId = row.assignedSessionId;
|
|
20463
|
+
if (row.status === "assigned" && reDispatchedSessionId && !sessionIdsEquivalent(reDispatchedSessionId, completingSessionId)) {
|
|
20464
|
+
stopStaleMeshWorker(components, {
|
|
20465
|
+
meshId,
|
|
20466
|
+
sessionId: reDispatchedSessionId,
|
|
20467
|
+
nodeId: row.assignedNodeId,
|
|
20468
|
+
providerType: row.assignedProviderType
|
|
20469
|
+
});
|
|
20470
|
+
}
|
|
20471
|
+
endTaskDispatchInFlight(meshId, row.id);
|
|
20472
|
+
updateTaskStatus(meshId, row.id, outcome === "completed" ? "completed" : "failed");
|
|
20473
|
+
if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
|
|
20474
|
+
try {
|
|
20475
|
+
appendLedgerEntry(meshId, {
|
|
20476
|
+
kind: outcome === "completed" ? "task_completed" : "task_failed",
|
|
20477
|
+
sessionId: completingSessionId,
|
|
20478
|
+
nodeId: readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId) || void 0,
|
|
20479
|
+
providerType: readNonEmptyString2(args.metadataEvent.providerType) || void 0,
|
|
20480
|
+
payload: {
|
|
20481
|
+
taskId: row.id,
|
|
20482
|
+
event: args.event,
|
|
20483
|
+
source: "redrive_late_completion_supersede",
|
|
20484
|
+
reclaimReason: row.requeueReason,
|
|
20485
|
+
reclaimAgeMs: Date.now() - requeuedAtMs,
|
|
20486
|
+
finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0
|
|
20487
|
+
}
|
|
20488
|
+
});
|
|
20489
|
+
} catch {
|
|
20490
|
+
}
|
|
20491
|
+
}
|
|
20492
|
+
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}` : ""}`);
|
|
20493
|
+
traceMeshEventDrop("redrive_late_completion_supersede", {
|
|
20494
|
+
taskId: row.id,
|
|
20495
|
+
sessionId: completingSessionId,
|
|
20496
|
+
nodeId: row.assignedNodeId ?? args.nodeId,
|
|
20497
|
+
meshId,
|
|
20498
|
+
event: args.event
|
|
20499
|
+
}, `${row.requeueReason} ${Math.round((Date.now() - requeuedAtMs) / 1e3)}s \u2192 ${outcome}`);
|
|
20500
|
+
return true;
|
|
20501
|
+
}
|
|
20435
20502
|
function injectMeshSystemMessage(components, args) {
|
|
20436
20503
|
const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
20437
20504
|
const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
|
|
@@ -20543,6 +20610,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
20543
20610
|
}
|
|
20544
20611
|
});
|
|
20545
20612
|
}
|
|
20613
|
+
} else if (strandedRow && supersedeRedriveReclaimForLateCompletion(components, args.meshId, strandedRow, sessionId, outcome, args)) {
|
|
20546
20614
|
}
|
|
20547
20615
|
} catch {
|
|
20548
20616
|
}
|
|
@@ -21869,6 +21937,50 @@ async function autoPruneStaleDirectDispatches(components, mesh, selfIds, localDa
|
|
|
21869
21937
|
LOG.info("MeshReconcile", `Auto-pruned ${result.prunedCount} orphaned direct dispatch record(s) for mesh ${mesh.id}`);
|
|
21870
21938
|
}
|
|
21871
21939
|
}
|
|
21940
|
+
async function pollAssignedTaskTerminalEvidence(components, mesh, row) {
|
|
21941
|
+
const sessionId = readNonEmptyString2(row.assignedSessionId);
|
|
21942
|
+
const nodeId = readNonEmptyString2(row.assignedNodeId);
|
|
21943
|
+
if (!sessionId || !nodeId) return null;
|
|
21944
|
+
const node = (mesh.nodes ?? []).find((n) => n.id === nodeId);
|
|
21945
|
+
const nodeDaemonId = readNonEmptyString2(node?.daemonId);
|
|
21946
|
+
const localDaemonId = readNonEmptyString2(components.statusInstanceId);
|
|
21947
|
+
const isLocalNode = !nodeDaemonId || daemonIdsEquivalent(nodeDaemonId, localDaemonId) || !!components.instanceManager.getInstance(sessionId);
|
|
21948
|
+
const providerType = readNonEmptyString2(row.assignedProviderType);
|
|
21949
|
+
const readArgs = {
|
|
21950
|
+
sessionId,
|
|
21951
|
+
targetSessionId: sessionId,
|
|
21952
|
+
tailLimit: 10,
|
|
21953
|
+
...node?.workspace ? { workspace: node.workspace } : {},
|
|
21954
|
+
...providerType ? { agentType: providerType, providerType } : {}
|
|
21955
|
+
};
|
|
21956
|
+
let payload = null;
|
|
21957
|
+
try {
|
|
21958
|
+
if (isLocalNode) {
|
|
21959
|
+
const result = await components.commandHandler?.handle("read_chat", readArgs);
|
|
21960
|
+
if (result && result.success === false) return null;
|
|
21961
|
+
payload = unwrapReadChatPayload(result);
|
|
21962
|
+
} else if (components.dispatchMeshCommand) {
|
|
21963
|
+
const result = await components.dispatchMeshCommand(nodeDaemonId, "read_chat", readArgs);
|
|
21964
|
+
payload = unwrapReadChatPayload(result);
|
|
21965
|
+
if (payload && payload.success === false) return null;
|
|
21966
|
+
} else {
|
|
21967
|
+
return null;
|
|
21968
|
+
}
|
|
21969
|
+
} catch {
|
|
21970
|
+
return null;
|
|
21971
|
+
}
|
|
21972
|
+
if (!payload) return null;
|
|
21973
|
+
if (readChatPayloadStatus(payload) !== "idle") return null;
|
|
21974
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
21975
|
+
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
21976
|
+
if (!evidence.finalSummary) return null;
|
|
21977
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString2(row.dispatchTimestamp));
|
|
21978
|
+
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
21979
|
+
if (!(Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs >= dispatchedAtMs)) {
|
|
21980
|
+
return null;
|
|
21981
|
+
}
|
|
21982
|
+
return "completed";
|
|
21983
|
+
}
|
|
21872
21984
|
var init_mesh_completion_synthesis = __esm({
|
|
21873
21985
|
"src/mesh/mesh-completion-synthesis.ts"() {
|
|
21874
21986
|
"use strict";
|
|
@@ -22145,7 +22257,8 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
|
|
|
22145
22257
|
}
|
|
22146
22258
|
return delivered;
|
|
22147
22259
|
}
|
|
22148
|
-
function recoverStrandedAssignedDispatches(components,
|
|
22260
|
+
async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
22261
|
+
const meshId = mesh.id;
|
|
22149
22262
|
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
22150
22263
|
if (!assigned.length) return;
|
|
22151
22264
|
const nowMs = Date.now();
|
|
@@ -22252,6 +22365,36 @@ function recoverStrandedAssignedDispatches(components, meshId, store) {
|
|
|
22252
22365
|
}
|
|
22253
22366
|
reclaimReason = "reclaim_after_unknown_grace";
|
|
22254
22367
|
}
|
|
22368
|
+
const terminalEvidence = await pollAssignedTaskTerminalEvidence(components, mesh, row);
|
|
22369
|
+
if (terminalEvidence) {
|
|
22370
|
+
deliveredNoTurnUnknownStreak.delete(streakKey);
|
|
22371
|
+
updateTaskStatus(meshId, row.id, terminalEvidence);
|
|
22372
|
+
if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
|
|
22373
|
+
try {
|
|
22374
|
+
appendLedgerEntry(meshId, {
|
|
22375
|
+
kind: terminalEvidence === "completed" ? "task_completed" : "task_failed",
|
|
22376
|
+
nodeId: row.assignedNodeId,
|
|
22377
|
+
sessionId: row.assignedSessionId,
|
|
22378
|
+
providerType: row.assignedProviderType,
|
|
22379
|
+
payload: {
|
|
22380
|
+
taskId: row.id,
|
|
22381
|
+
event: "agent:generating_completed",
|
|
22382
|
+
source: "redrive_deadline_transcript_evidence"
|
|
22383
|
+
}
|
|
22384
|
+
});
|
|
22385
|
+
} catch {
|
|
22386
|
+
}
|
|
22387
|
+
}
|
|
22388
|
+
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`);
|
|
22389
|
+
traceMeshEventDrop("redrive_deadline_transcript_completed", {
|
|
22390
|
+
taskId: row.id,
|
|
22391
|
+
sessionId: row.assignedSessionId,
|
|
22392
|
+
nodeId: row.assignedNodeId,
|
|
22393
|
+
meshId,
|
|
22394
|
+
event: "agent:generating_completed"
|
|
22395
|
+
}, `${reclaimReason} \u2192 transcript ${terminalEvidence}`);
|
|
22396
|
+
continue;
|
|
22397
|
+
}
|
|
22255
22398
|
const reclaimedLost = reclaimStrandedAssignedTask(meshId, row.id, {
|
|
22256
22399
|
reason: reclaimReason,
|
|
22257
22400
|
ageMs: nowMs - dispatchedAtMs
|
|
@@ -22378,7 +22521,7 @@ async function runMeshReconcileTick(components) {
|
|
|
22378
22521
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
22379
22522
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
22380
22523
|
try {
|
|
22381
|
-
recoverStrandedAssignedDispatches(components, mesh
|
|
22524
|
+
await recoverStrandedAssignedDispatches(components, mesh, store);
|
|
22382
22525
|
} catch (e) {
|
|
22383
22526
|
LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
22384
22527
|
}
|
|
@@ -24685,6 +24828,21 @@ function compile3(re, flags) {
|
|
|
24685
24828
|
}
|
|
24686
24829
|
function findQuestionLineIndex(spec, lines) {
|
|
24687
24830
|
const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
|
|
24831
|
+
const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
24832
|
+
const buttonRe = compile3(spec.buttonPattern, buttonFlags);
|
|
24833
|
+
const isButtonLine = (line) => {
|
|
24834
|
+
buttonRe.lastIndex = 0;
|
|
24835
|
+
return buttonRe.test(line);
|
|
24836
|
+
};
|
|
24837
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24838
|
+
if (primary.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: "primary" };
|
|
24839
|
+
}
|
|
24840
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
24841
|
+
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
24842
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24843
|
+
if (re.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
24844
|
+
}
|
|
24845
|
+
}
|
|
24688
24846
|
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24689
24847
|
if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
|
|
24690
24848
|
}
|
|
@@ -25414,6 +25572,7 @@ var init_cli_state_engine = __esm({
|
|
|
25414
25572
|
* paint and made the engine type "1" repeatedly into the prompt.
|
|
25415
25573
|
*/
|
|
25416
25574
|
modalLostAt = 0;
|
|
25575
|
+
modalLostRecheckTimer = null;
|
|
25417
25576
|
approvalExitTimeout = null;
|
|
25418
25577
|
// ── Response tracking ────────────────────────────
|
|
25419
25578
|
responseEpoch = 0;
|
|
@@ -25465,6 +25624,10 @@ var init_cli_state_engine = __esm({
|
|
|
25465
25624
|
setStatus(status, trigger) {
|
|
25466
25625
|
const prev = this.currentStatus;
|
|
25467
25626
|
if (prev === status) return;
|
|
25627
|
+
if (prev === "waiting_approval" && this.modalLostRecheckTimer) {
|
|
25628
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25629
|
+
this.modalLostRecheckTimer = null;
|
|
25630
|
+
}
|
|
25468
25631
|
this.currentStatus = status;
|
|
25469
25632
|
this.statusHistory.push({ status, at: Date.now(), trigger });
|
|
25470
25633
|
if (this.statusHistory.length > 50) this.statusHistory.shift();
|
|
@@ -25625,6 +25788,10 @@ var init_cli_state_engine = __esm({
|
|
|
25625
25788
|
clearTimeout(this.approvalExitTimeout);
|
|
25626
25789
|
this.approvalExitTimeout = null;
|
|
25627
25790
|
}
|
|
25791
|
+
if (this.modalLostRecheckTimer) {
|
|
25792
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25793
|
+
this.modalLostRecheckTimer = null;
|
|
25794
|
+
}
|
|
25628
25795
|
if (this.finishRetryTimer) {
|
|
25629
25796
|
clearTimeout(this.finishRetryTimer);
|
|
25630
25797
|
this.finishRetryTimer = null;
|
|
@@ -25936,7 +26103,7 @@ var init_cli_state_engine = __esm({
|
|
|
25936
26103
|
if (!inCooldown || modal) {
|
|
25937
26104
|
if (!modal) {
|
|
25938
26105
|
LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
25939
|
-
if (this.currentStatus === "waiting_approval"
|
|
26106
|
+
if (this.currentStatus === "waiting_approval") {
|
|
25940
26107
|
const lostAt = this.modalLostAt || Date.now();
|
|
25941
26108
|
if (!this.modalLostAt) this.modalLostAt = lostAt;
|
|
25942
26109
|
if (Date.now() - lostAt >= this.timeouts.approvalCooldown) {
|
|
@@ -25944,6 +26111,8 @@ var init_cli_state_engine = __esm({
|
|
|
25944
26111
|
this.modalLostAt = 0;
|
|
25945
26112
|
this.setStatus("generating", "approval_lost_modal");
|
|
25946
26113
|
this.callbacks.onStatusChange();
|
|
26114
|
+
} else {
|
|
26115
|
+
this.armModalLostRecheck();
|
|
25947
26116
|
}
|
|
25948
26117
|
}
|
|
25949
26118
|
return;
|
|
@@ -26205,6 +26374,25 @@ var init_cli_state_engine = __esm({
|
|
|
26205
26374
|
this.recordTrace("idle_finish_cancelled", { trigger: reason });
|
|
26206
26375
|
}
|
|
26207
26376
|
// ─── Helpers ────────────────────────────────────────────────────────────
|
|
26377
|
+
/**
|
|
26378
|
+
* Schedule one more settled evaluation while pinned to `waiting_approval`
|
|
26379
|
+
* with no actionable modal. The settled FSM normally only re-runs on new
|
|
26380
|
+
* PTY output; a provider whose modal cue lingers in a form detectStatus
|
|
26381
|
+
* still matches (e.g. kimi's questionPattern hitting the user echo) but
|
|
26382
|
+
* whose PTY has gone quiet would never get another evaluation, latching
|
|
26383
|
+
* `waiting_approval` forever. This timer guarantees the modal-lost recovery
|
|
26384
|
+
* in `applyWaitingApproval` is reached even against a silent PTY. It is a
|
|
26385
|
+
* no-op once the FSM leaves `waiting_approval` (the re-evaluation itself
|
|
26386
|
+
* takes the recovery branch and clears the state).
|
|
26387
|
+
*/
|
|
26388
|
+
armModalLostRecheck() {
|
|
26389
|
+
if (this.modalLostRecheckTimer) return;
|
|
26390
|
+
this.modalLostRecheckTimer = setTimeout(() => {
|
|
26391
|
+
this.modalLostRecheckTimer = null;
|
|
26392
|
+
if (this.currentStatus !== "waiting_approval") return;
|
|
26393
|
+
this.evaluateSettled(this.transport.getSnapshot());
|
|
26394
|
+
}, this.timeouts.approvalCooldown);
|
|
26395
|
+
}
|
|
26208
26396
|
armApprovalExitTimeout() {
|
|
26209
26397
|
if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
|
|
26210
26398
|
this.approvalExitTimeout = setTimeout(() => {
|
|
@@ -35618,6 +35806,33 @@ var ChatSourceRegistry = class {
|
|
|
35618
35806
|
getState(key2) {
|
|
35619
35807
|
return this.records.get(key2)?.state ?? INITIAL_CHAT_SOURCE_STATE;
|
|
35620
35808
|
}
|
|
35809
|
+
/**
|
|
35810
|
+
* Opaque snapshot of a session's FULL record (state + lock + transitions),
|
|
35811
|
+
* for callers that need to speculatively `observe()` and then roll back if
|
|
35812
|
+
* the resulting decision is undesirable (STICKY-NATIVE hold: a trusted
|
|
35813
|
+
* exact-identity session must not flip to PTY on a transient native gap).
|
|
35814
|
+
* Returns undefined when the session has no record yet. The snapshot is a
|
|
35815
|
+
* shallow copy — transitions array is copied so a later append does not
|
|
35816
|
+
* mutate it.
|
|
35817
|
+
*/
|
|
35818
|
+
snapshotRecord(key2) {
|
|
35819
|
+
const rec = this.records.get(key2);
|
|
35820
|
+
if (!rec) return void 0;
|
|
35821
|
+
return { state: rec.state, lockedSince: rec.lockedSince, transitions: [...rec.transitions] };
|
|
35822
|
+
}
|
|
35823
|
+
/** Restore a previously snapshotted record, undoing a speculative observe.
|
|
35824
|
+
* Passing undefined clears the key (it had no record when snapshotted). */
|
|
35825
|
+
restoreRecord(key2, snapshot) {
|
|
35826
|
+
if (!snapshot) {
|
|
35827
|
+
this.records.delete(key2);
|
|
35828
|
+
return;
|
|
35829
|
+
}
|
|
35830
|
+
this.records.set(key2, {
|
|
35831
|
+
state: snapshot.state,
|
|
35832
|
+
lockedSince: snapshot.lockedSince,
|
|
35833
|
+
transitions: [...snapshot.transitions]
|
|
35834
|
+
});
|
|
35835
|
+
}
|
|
35621
35836
|
/** Recent transitions, newest last. Empty array when nothing has happened. */
|
|
35622
35837
|
getTransitions(key2) {
|
|
35623
35838
|
return this.records.get(key2)?.transitions ?? [];
|
|
@@ -36041,7 +36256,52 @@ function decideCliReadChatSource(args) {
|
|
|
36041
36256
|
const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
|
|
36042
36257
|
const observation = buildObservationForCli(args, supportsNative);
|
|
36043
36258
|
const sessionKey = chatSourceSessionKey(args.providerType, args.sessionId);
|
|
36259
|
+
const priorSnapshot = CHAT_SOURCE_REGISTRY.snapshotRecord(sessionKey);
|
|
36260
|
+
const priorState = priorSnapshot?.state ?? CHAT_SOURCE_REGISTRY.getState(sessionKey);
|
|
36261
|
+
const eligibleForStickyHold = args.trustedExactNativeIdentity === true && (priorState.name === "NativeLocked" || priorState.name === "Recovering");
|
|
36044
36262
|
let decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
|
|
36263
|
+
if (eligibleForStickyHold && decision.selected === "pty-parser") {
|
|
36264
|
+
CHAT_SOURCE_REGISTRY.restoreRecord(sessionKey, priorSnapshot);
|
|
36265
|
+
const heldNativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
|
|
36266
|
+
const messageSource2 = buildCliMessageSourceProvenance({
|
|
36267
|
+
selected: "native-history",
|
|
36268
|
+
provider: args.providerType,
|
|
36269
|
+
nativeHandle: typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0,
|
|
36270
|
+
sessionWorkspace: args.sessionWorkspace,
|
|
36271
|
+
intendedWorkspace: args.intendedWorkspace,
|
|
36272
|
+
transcriptWorkspace: void 0,
|
|
36273
|
+
fallbackReason: "native_history_transient_gap_held",
|
|
36274
|
+
nativeSource: "provider-native",
|
|
36275
|
+
sourcePath: typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0,
|
|
36276
|
+
sourceMtimeMs: typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0,
|
|
36277
|
+
nativeHistoryCoverage: void 0,
|
|
36278
|
+
partialReason: void 0,
|
|
36279
|
+
unavailableReason: observation.kind === "native_unavailable" ? observation.reason : void 0,
|
|
36280
|
+
nativeMessages: heldNativeMessages,
|
|
36281
|
+
ptyMessages: args.ptyMessages,
|
|
36282
|
+
returnedMessages: heldNativeMessages,
|
|
36283
|
+
safeMapping: args.safeMapping,
|
|
36284
|
+
freshEnough: true,
|
|
36285
|
+
ptyStatusApprovalOnly: true
|
|
36286
|
+
});
|
|
36287
|
+
return {
|
|
36288
|
+
decision: {
|
|
36289
|
+
selected: "native-history",
|
|
36290
|
+
nextState: priorState,
|
|
36291
|
+
transition: {
|
|
36292
|
+
fromState: priorState.name,
|
|
36293
|
+
toState: priorState.name,
|
|
36294
|
+
event: "NoOp",
|
|
36295
|
+
cause: decision.transition.cause,
|
|
36296
|
+
at: Date.now()
|
|
36297
|
+
},
|
|
36298
|
+
lockState: { locked: priorState.name === "NativeLocked" }
|
|
36299
|
+
},
|
|
36300
|
+
messageSource: messageSource2,
|
|
36301
|
+
nativeMessages: heldNativeMessages,
|
|
36302
|
+
nativeSelected: true
|
|
36303
|
+
};
|
|
36304
|
+
}
|
|
36045
36305
|
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) {
|
|
36046
36306
|
CHAT_SOURCE_REGISTRY.clear(sessionKey);
|
|
36047
36307
|
decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
|
|
@@ -36361,7 +36621,7 @@ function sessionSpawnEnvFromAdapter(h, targetSessionId) {
|
|
|
36361
36621
|
function readCliProviderNativeHistory(agentStr, args) {
|
|
36362
36622
|
const canBindFromLiveSession = !args.historySessionId && typeof args.sessionStartedAtMs === "number" && args.sessionStartedAtMs > 0 && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36363
36623
|
const pinnedProviderSessionId = typeof args.pinnedProviderSessionId === "string" ? args.pinnedProviderSessionId.trim() : "";
|
|
36364
|
-
const effectiveHistorySessionId = args.historySessionId ||
|
|
36624
|
+
const effectiveHistorySessionId = args.historySessionId || pinnedProviderSessionId || "";
|
|
36365
36625
|
const workspaceLatestFallback = !effectiveHistorySessionId && !canBindFromLiveSession && !pinnedProviderSessionId && args.allowWorkspaceLatestFallback === true && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36366
36626
|
if (!effectiveHistorySessionId && !canBindFromLiveSession && !workspaceLatestFallback) {
|
|
36367
36627
|
return {
|