@adhdev/daemon-core 0.9.82-rc.536 → 0.9.82-rc.538
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 +344 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +344 -17
- 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/cli-adapters/provider-cli-adapter.ts +7 -0
- 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/detect-status.ts +108 -1
- 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.538" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
420
|
+
const builtAt = readInjected(true ? "2026-07-15T15:51:55.785Z" : 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
|
}
|
|
@@ -24552,6 +24695,70 @@ function modalMatches(spec, input) {
|
|
|
24552
24695
|
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
24553
24696
|
return false;
|
|
24554
24697
|
}
|
|
24698
|
+
function lastModalCueLine(spec, screenText) {
|
|
24699
|
+
if (!screenText) return -1;
|
|
24700
|
+
const lines = screenText.split("\n");
|
|
24701
|
+
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
24702
|
+
const variants = (spec.questionVariants ?? []).map((v) => compile2(v.regex, v.flags ?? "i"));
|
|
24703
|
+
const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
24704
|
+
const buttonRe = compile2(spec.buttonPattern, buttonFlags);
|
|
24705
|
+
let last = -1;
|
|
24706
|
+
for (let i = 0; i < lines.length; i++) {
|
|
24707
|
+
const line = lines[i];
|
|
24708
|
+
question.lastIndex = 0;
|
|
24709
|
+
if (question.test(line)) {
|
|
24710
|
+
last = i;
|
|
24711
|
+
continue;
|
|
24712
|
+
}
|
|
24713
|
+
if (variants.some((re) => {
|
|
24714
|
+
re.lastIndex = 0;
|
|
24715
|
+
return re.test(line);
|
|
24716
|
+
})) {
|
|
24717
|
+
last = i;
|
|
24718
|
+
continue;
|
|
24719
|
+
}
|
|
24720
|
+
buttonRe.lastIndex = 0;
|
|
24721
|
+
if (buttonRe.test(line)) {
|
|
24722
|
+
last = i;
|
|
24723
|
+
continue;
|
|
24724
|
+
}
|
|
24725
|
+
}
|
|
24726
|
+
return last;
|
|
24727
|
+
}
|
|
24728
|
+
function modalSupersededBySettledPrompt(modalSpec, settledSpec, settled, input) {
|
|
24729
|
+
if (!settled || !settledSpec) return false;
|
|
24730
|
+
if (settledSpec.scope === "whole-screen") return false;
|
|
24731
|
+
const screenText = input.screenText ?? "";
|
|
24732
|
+
if (!screenText) return false;
|
|
24733
|
+
const modalLine = lastModalCueLine(modalSpec, screenText);
|
|
24734
|
+
if (modalLine < 0) return false;
|
|
24735
|
+
const lines = screenText.split("\n");
|
|
24736
|
+
const below = lines.slice(modalLine + 1);
|
|
24737
|
+
if (below.length === 0) return false;
|
|
24738
|
+
const belowText = below.join("\n");
|
|
24739
|
+
if (!settled.prompt.test(belowText)) return false;
|
|
24740
|
+
if (settled.footers.length > 0 && !settled.footers.every((f) => f.test(belowText))) return false;
|
|
24741
|
+
const question = compile2(modalSpec.questionPattern, modalSpec.questionFlags ?? "i");
|
|
24742
|
+
const variants = (modalSpec.questionVariants ?? []).map((v) => compile2(v.regex, v.flags ?? "i"));
|
|
24743
|
+
const buttonFlags = modalSpec.buttonFlags && modalSpec.buttonFlags.includes("m") ? modalSpec.buttonFlags : `${modalSpec.buttonFlags ?? ""}m`;
|
|
24744
|
+
const buttonRe = compile2(modalSpec.buttonPattern, buttonFlags);
|
|
24745
|
+
const isModalCueLine = (line) => {
|
|
24746
|
+
question.lastIndex = 0;
|
|
24747
|
+
if (question.test(line)) return true;
|
|
24748
|
+
if (variants.some((re) => {
|
|
24749
|
+
re.lastIndex = 0;
|
|
24750
|
+
return re.test(line);
|
|
24751
|
+
})) return true;
|
|
24752
|
+
buttonRe.lastIndex = 0;
|
|
24753
|
+
return buttonRe.test(line);
|
|
24754
|
+
};
|
|
24755
|
+
const settledPromptLineRe = compile2(settledSpec.regex, (settledSpec.flags ?? "m").includes("m") ? settledSpec.flags ?? "m" : `${settledSpec.flags ?? ""}m`);
|
|
24756
|
+
const isSettledLine = (line) => {
|
|
24757
|
+
settledPromptLineRe.lastIndex = 0;
|
|
24758
|
+
return settledPromptLineRe.test(line);
|
|
24759
|
+
};
|
|
24760
|
+
return below.some((line) => line.trim() !== "" && !isModalCueLine(line) && !isSettledLine(line));
|
|
24761
|
+
}
|
|
24555
24762
|
function evaluateGroup(group, spec, input, compiled) {
|
|
24556
24763
|
switch (group) {
|
|
24557
24764
|
case "spinner": {
|
|
@@ -24561,7 +24768,9 @@ function evaluateGroup(group, spec, input, compiled) {
|
|
|
24561
24768
|
}
|
|
24562
24769
|
case "modal": {
|
|
24563
24770
|
if (!spec.modal) return null;
|
|
24564
|
-
|
|
24771
|
+
if (!modalMatches(spec.modal, input)) return null;
|
|
24772
|
+
if (modalSupersededBySettledPrompt(spec.modal, spec.settledPrompt, compiled.settled, input)) return null;
|
|
24773
|
+
return "waiting_approval";
|
|
24565
24774
|
}
|
|
24566
24775
|
case "settled-prompt": {
|
|
24567
24776
|
if (!spec.settledPrompt || !compiled.settled) return null;
|
|
@@ -24619,6 +24828,21 @@ function compile3(re, flags) {
|
|
|
24619
24828
|
}
|
|
24620
24829
|
function findQuestionLineIndex(spec, lines) {
|
|
24621
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
|
+
}
|
|
24622
24846
|
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24623
24847
|
if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
|
|
24624
24848
|
}
|
|
@@ -25348,6 +25572,7 @@ var init_cli_state_engine = __esm({
|
|
|
25348
25572
|
* paint and made the engine type "1" repeatedly into the prompt.
|
|
25349
25573
|
*/
|
|
25350
25574
|
modalLostAt = 0;
|
|
25575
|
+
modalLostRecheckTimer = null;
|
|
25351
25576
|
approvalExitTimeout = null;
|
|
25352
25577
|
// ── Response tracking ────────────────────────────
|
|
25353
25578
|
responseEpoch = 0;
|
|
@@ -25399,6 +25624,10 @@ var init_cli_state_engine = __esm({
|
|
|
25399
25624
|
setStatus(status, trigger) {
|
|
25400
25625
|
const prev = this.currentStatus;
|
|
25401
25626
|
if (prev === status) return;
|
|
25627
|
+
if (prev === "waiting_approval" && this.modalLostRecheckTimer) {
|
|
25628
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25629
|
+
this.modalLostRecheckTimer = null;
|
|
25630
|
+
}
|
|
25402
25631
|
this.currentStatus = status;
|
|
25403
25632
|
this.statusHistory.push({ status, at: Date.now(), trigger });
|
|
25404
25633
|
if (this.statusHistory.length > 50) this.statusHistory.shift();
|
|
@@ -25559,6 +25788,10 @@ var init_cli_state_engine = __esm({
|
|
|
25559
25788
|
clearTimeout(this.approvalExitTimeout);
|
|
25560
25789
|
this.approvalExitTimeout = null;
|
|
25561
25790
|
}
|
|
25791
|
+
if (this.modalLostRecheckTimer) {
|
|
25792
|
+
clearTimeout(this.modalLostRecheckTimer);
|
|
25793
|
+
this.modalLostRecheckTimer = null;
|
|
25794
|
+
}
|
|
25562
25795
|
if (this.finishRetryTimer) {
|
|
25563
25796
|
clearTimeout(this.finishRetryTimer);
|
|
25564
25797
|
this.finishRetryTimer = null;
|
|
@@ -25870,7 +26103,7 @@ var init_cli_state_engine = __esm({
|
|
|
25870
26103
|
if (!inCooldown || modal) {
|
|
25871
26104
|
if (!modal) {
|
|
25872
26105
|
LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
25873
|
-
if (this.currentStatus === "waiting_approval"
|
|
26106
|
+
if (this.currentStatus === "waiting_approval") {
|
|
25874
26107
|
const lostAt = this.modalLostAt || Date.now();
|
|
25875
26108
|
if (!this.modalLostAt) this.modalLostAt = lostAt;
|
|
25876
26109
|
if (Date.now() - lostAt >= this.timeouts.approvalCooldown) {
|
|
@@ -25878,6 +26111,8 @@ var init_cli_state_engine = __esm({
|
|
|
25878
26111
|
this.modalLostAt = 0;
|
|
25879
26112
|
this.setStatus("generating", "approval_lost_modal");
|
|
25880
26113
|
this.callbacks.onStatusChange();
|
|
26114
|
+
} else {
|
|
26115
|
+
this.armModalLostRecheck();
|
|
25881
26116
|
}
|
|
25882
26117
|
}
|
|
25883
26118
|
return;
|
|
@@ -26139,6 +26374,25 @@ var init_cli_state_engine = __esm({
|
|
|
26139
26374
|
this.recordTrace("idle_finish_cancelled", { trigger: reason });
|
|
26140
26375
|
}
|
|
26141
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
|
+
}
|
|
26142
26396
|
armApprovalExitTimeout() {
|
|
26143
26397
|
if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
|
|
26144
26398
|
this.approvalExitTimeout = setTimeout(() => {
|
|
@@ -26731,6 +26985,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
26731
26985
|
const staleSnapshotLooksActive = activeScreenPattern.test(lastSnapshot);
|
|
26732
26986
|
const currentScreenLooksIdle = /(?:^|\n|\r)\s*[❯›>]\s*(?:Try\s+["“][^\n\r"”]+["”])?\s*(?:\n|\r|$)/.test(screenText) && !activeScreenPattern.test(screenText);
|
|
26733
26987
|
if (staleSnapshotLooksActive && currentScreenLooksIdle) return screenText;
|
|
26988
|
+
if (this.runDetectStatus(screenText) === "idle") return screenText;
|
|
26734
26989
|
if (currentSnapshot.length >= lastSnapshot.length) return screenText;
|
|
26735
26990
|
return `${screenText}
|
|
26736
26991
|
${lastSnapshot}`;
|
|
@@ -35551,6 +35806,33 @@ var ChatSourceRegistry = class {
|
|
|
35551
35806
|
getState(key2) {
|
|
35552
35807
|
return this.records.get(key2)?.state ?? INITIAL_CHAT_SOURCE_STATE;
|
|
35553
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
|
+
}
|
|
35554
35836
|
/** Recent transitions, newest last. Empty array when nothing has happened. */
|
|
35555
35837
|
getTransitions(key2) {
|
|
35556
35838
|
return this.records.get(key2)?.transitions ?? [];
|
|
@@ -35974,7 +36256,52 @@ function decideCliReadChatSource(args) {
|
|
|
35974
36256
|
const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
|
|
35975
36257
|
const observation = buildObservationForCli(args, supportsNative);
|
|
35976
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");
|
|
35977
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
|
+
}
|
|
35978
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) {
|
|
35979
36306
|
CHAT_SOURCE_REGISTRY.clear(sessionKey);
|
|
35980
36307
|
decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
|
|
@@ -36294,7 +36621,7 @@ function sessionSpawnEnvFromAdapter(h, targetSessionId) {
|
|
|
36294
36621
|
function readCliProviderNativeHistory(agentStr, args) {
|
|
36295
36622
|
const canBindFromLiveSession = !args.historySessionId && typeof args.sessionStartedAtMs === "number" && args.sessionStartedAtMs > 0 && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36296
36623
|
const pinnedProviderSessionId = typeof args.pinnedProviderSessionId === "string" ? args.pinnedProviderSessionId.trim() : "";
|
|
36297
|
-
const effectiveHistorySessionId = args.historySessionId ||
|
|
36624
|
+
const effectiveHistorySessionId = args.historySessionId || pinnedProviderSessionId || "";
|
|
36298
36625
|
const workspaceLatestFallback = !effectiveHistorySessionId && !canBindFromLiveSession && !pinnedProviderSessionId && args.allowWorkspaceLatestFallback === true && typeof args.workspace === "string" && args.workspace.trim().length > 0;
|
|
36299
36626
|
if (!effectiveHistorySessionId && !canBindFromLiveSession && !workspaceLatestFallback) {
|
|
36300
36627
|
return {
|