@adhdev/daemon-core 0.9.82-rc.356 → 0.9.82-rc.358
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-adapter-types.d.ts +9 -0
- package/dist/index.js +135 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -81
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-coordinator.d.ts +1 -0
- package/dist/providers/spec/evaluator.d.ts +21 -0
- package/dist/providers/spec/fsm-driver.d.ts +1 -0
- package/dist/providers/spec/fsm-types.d.ts +30 -0
- package/package.json +2 -2
- package/src/cli-adapter-types.ts +9 -0
- package/src/mesh/mesh-events-coordinator.ts +147 -65
- package/src/providers/cli-provider-instance.ts +29 -5
- package/src/providers/spec/cli-adapter.ts +17 -8
- package/src/providers/spec/evaluator.ts +41 -4
- package/src/providers/spec/fsm-driver.ts +8 -3
- package/src/providers/spec/fsm-types.ts +35 -0
|
@@ -12,6 +12,15 @@ export interface CliAdapterStatus {
|
|
|
12
12
|
activeModal?: {
|
|
13
13
|
message: string;
|
|
14
14
|
buttons: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Semantic modal class, when the adapter knows it (spec/FSM path):
|
|
17
|
+
* 'approval' = tool/command/trust consent (auto-approve may fire);
|
|
18
|
+
* 'picker' = a selection menu the user opened (/model, /mode — must NOT
|
|
19
|
+
* be auto-answered); 'confirm' = a yes/no left to the user. Absent/null
|
|
20
|
+
* for adapters that don't classify modals — the auto-approve gate then
|
|
21
|
+
* falls back to its structural heuristic.
|
|
22
|
+
*/
|
|
23
|
+
kind?: 'approval' | 'picker' | 'confirm' | null;
|
|
15
24
|
} | null;
|
|
16
25
|
activeInteractivePrompt?: InteractivePrompt | null;
|
|
17
26
|
providerSessionId?: string;
|
package/dist/index.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "7210e80591d8d9ab378ee786ffb233027437461a" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "7210e805" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.358" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-23T01:40:53.249Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -12673,8 +12673,22 @@ function sweepExpiredRemoteIdleSessions() {
|
|
|
12673
12673
|
}
|
|
12674
12674
|
function getMeshWithCache(components, meshId) {
|
|
12675
12675
|
const localMesh = getMesh(meshId);
|
|
12676
|
-
|
|
12677
|
-
|
|
12676
|
+
const cachedMesh = components.router?.getCachedInlineMesh(meshId);
|
|
12677
|
+
if (!localMesh) return cachedMesh;
|
|
12678
|
+
if (!cachedMesh) return localMesh;
|
|
12679
|
+
return mergeInlineCacheOnlyNodes(localMesh, cachedMesh);
|
|
12680
|
+
}
|
|
12681
|
+
function mergeInlineCacheOnlyNodes(localMesh, cachedMesh) {
|
|
12682
|
+
const localNodes = Array.isArray(localMesh?.nodes) ? localMesh.nodes : [];
|
|
12683
|
+
const cachedNodes = Array.isArray(cachedMesh?.nodes) ? cachedMesh.nodes : [];
|
|
12684
|
+
if (!cachedNodes.length) return localMesh;
|
|
12685
|
+
const cacheOnly = cachedNodes.filter((cachedNode) => {
|
|
12686
|
+
const cachedId = readMeshNodeId(cachedNode);
|
|
12687
|
+
if (!cachedId) return false;
|
|
12688
|
+
return !localNodes.some((localNode) => meshNodeIdMatches(localNode, cachedId));
|
|
12689
|
+
});
|
|
12690
|
+
if (!cacheOnly.length) return localMesh;
|
|
12691
|
+
return { ...localMesh, nodes: [...localNodes, ...cacheOnly] };
|
|
12678
12692
|
}
|
|
12679
12693
|
function isIntentionalCleanupStopMetadata(event) {
|
|
12680
12694
|
return event.intentional === true || event.intentionalStop === true || event.operatorCleanup === true || event.reason === "operator_cleanup" || event.stopReason === "operator_cleanup" || event.cleanupReason === "operator_cleanup" || event.source === "mesh_cleanup_sessions" || event.source === "mesh_remove_node";
|
|
@@ -13737,7 +13751,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13737
13751
|
occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
|
|
13738
13752
|
taskId: eventTaskId
|
|
13739
13753
|
});
|
|
13740
|
-
const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
|
|
13754
|
+
const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true || !eventTaskId && !sessionHasActiveAssignment(args.meshId, sessionId);
|
|
13741
13755
|
if (!leaveDirectDispatchActive) {
|
|
13742
13756
|
updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
|
|
13743
13757
|
}
|
|
@@ -13836,7 +13850,9 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13836
13850
|
}
|
|
13837
13851
|
if (sessionId) {
|
|
13838
13852
|
const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
|
|
13839
|
-
|
|
13853
|
+
if (startedTaskId || sessionHasActiveAssignment(args.meshId, sessionId)) {
|
|
13854
|
+
updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
|
|
13855
|
+
}
|
|
13840
13856
|
const activeDeliveries = (() => {
|
|
13841
13857
|
try {
|
|
13842
13858
|
return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(args.meshId, sessionId);
|
|
@@ -14011,6 +14027,76 @@ function injectMeshSystemMessage(components, args) {
|
|
|
14011
14027
|
}
|
|
14012
14028
|
return { success: true, forwarded: 0 };
|
|
14013
14029
|
}
|
|
14030
|
+
function buildRelayMetadataEvent(payload) {
|
|
14031
|
+
const relayModalMessage = readNonEmptyString2(payload.modalMessage);
|
|
14032
|
+
const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
|
|
14033
|
+
return {
|
|
14034
|
+
// Preserve the dispatch task id across the machine boundary. The `received` trace
|
|
14035
|
+
// stage reads payload.taskId; without mirroring it here the rebuilt metadataEvent
|
|
14036
|
+
// loses it, so injectMeshSystemMessage's traceCtx.taskId and the
|
|
14037
|
+
// updateDirectDispatchStatus(eventTaskId) call go undefined — the EvtTrace
|
|
14038
|
+
// queued/surfaced stages show task=- and the direct-dispatch ledger falls back to a
|
|
14039
|
+
// session_id match (which can flip a sibling row). The local in-process forward path
|
|
14040
|
+
// keeps event.taskId/meshActiveTaskId for free; this mirrors it for the remote relay.
|
|
14041
|
+
// Same taskId/meshActiveTaskId ordering the local unroutable trace uses.
|
|
14042
|
+
taskId: readNonEmptyString2(payload.taskId) || readNonEmptyString2(payload.meshActiveTaskId),
|
|
14043
|
+
targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
|
|
14044
|
+
providerType: readNonEmptyString2(payload.providerType),
|
|
14045
|
+
providerSessionId: readNonEmptyString2(payload.providerSessionId),
|
|
14046
|
+
// Preserve the originating coordinator SESSION id across the machine boundary so
|
|
14047
|
+
// the completion routes back to the exact coordinator session (multi-coordinator).
|
|
14048
|
+
// buildForwardPayloadFromPending spreads the worker event's metadata, so the id
|
|
14049
|
+
// arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
|
|
14050
|
+
// is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
|
|
14051
|
+
// anchors from this. Absent → daemon-level fallback (version-skew safe).
|
|
14052
|
+
meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
|
|
14053
|
+
// Carry the session identity fields the worker provider event emits so the
|
|
14054
|
+
// coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
|
|
14055
|
+
// settings. Without these the remote-relay hop reconstructs metadataEvent with
|
|
14056
|
+
// an empty workspace, and the dashboard flaps to the generic
|
|
14057
|
+
// "Terminal (Mesh Node)" title (and degrades the provider label) between live
|
|
14058
|
+
// events and the periodic get_status_metadata snapshot. The local in-process
|
|
14059
|
+
// forward path (onMeshCoordinatorEventForwarded) already preserves these; this
|
|
14060
|
+
// mirrors them for the remote-only relay path.
|
|
14061
|
+
workspace: readNonEmptyString2(payload.workspace) || readNonEmptyString2(payload.workspaceName),
|
|
14062
|
+
workspaceName: readNonEmptyString2(payload.workspaceName) || readNonEmptyString2(payload.workspace),
|
|
14063
|
+
sessionTitle: readNonEmptyString2(payload.sessionTitle),
|
|
14064
|
+
sessionStatus: readNonEmptyString2(payload.sessionStatus),
|
|
14065
|
+
sessionChatStatus: readNonEmptyString2(payload.sessionChatStatus),
|
|
14066
|
+
providerName: readNonEmptyString2(payload.providerName),
|
|
14067
|
+
...payload.sessionSettings && typeof payload.sessionSettings === "object" && !Array.isArray(payload.sessionSettings) ? { sessionSettings: payload.sessionSettings } : {},
|
|
14068
|
+
finalSummary: readNonEmptyString2(payload.finalSummary) || readNonEmptyString2(payload.summary),
|
|
14069
|
+
// T2: carry the worker's status-snapshot last-message preview across the machine
|
|
14070
|
+
// boundary so a summary-less completion still surfaces the assistant reply in the
|
|
14071
|
+
// coordinator's inbox mirror. resolveMeshSurfacedSessionPreview reads these
|
|
14072
|
+
// (assistant-role only) when finalSummary is absent.
|
|
14073
|
+
lastMessagePreview: readNonEmptyString2(payload.lastMessagePreview),
|
|
14074
|
+
lastMessageRole: readNonEmptyString2(payload.lastMessageRole),
|
|
14075
|
+
...payload.lastMessageAt !== void 0 ? { lastMessageAt: payload.lastMessageAt } : {},
|
|
14076
|
+
jobId: readNonEmptyString2(payload.jobId),
|
|
14077
|
+
interactionId: readNonEmptyString2(payload.interactionId),
|
|
14078
|
+
status: readNonEmptyString2(payload.status),
|
|
14079
|
+
targetDaemonId: readNonEmptyString2(payload.targetDaemonId),
|
|
14080
|
+
startedAt: readNonEmptyString2(payload.startedAt),
|
|
14081
|
+
completedAt: readNonEmptyString2(payload.completedAt),
|
|
14082
|
+
retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
|
|
14083
|
+
...relayModalMessage ? { modalMessage: relayModalMessage } : {},
|
|
14084
|
+
...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
|
|
14085
|
+
...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
|
|
14086
|
+
...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
|
|
14087
|
+
...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
|
|
14088
|
+
...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
|
|
14089
|
+
...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
|
|
14090
|
+
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
14091
|
+
intentional: payload.intentional === true,
|
|
14092
|
+
intentionalStop: payload.intentionalStop === true,
|
|
14093
|
+
operatorCleanup: payload.operatorCleanup === true,
|
|
14094
|
+
reason: readNonEmptyString2(payload.reason),
|
|
14095
|
+
stopReason: readNonEmptyString2(payload.stopReason),
|
|
14096
|
+
cleanupReason: readNonEmptyString2(payload.cleanupReason),
|
|
14097
|
+
source: readNonEmptyString2(payload.source)
|
|
14098
|
+
};
|
|
14099
|
+
}
|
|
14014
14100
|
function handleMeshForwardEvent(components, payload) {
|
|
14015
14101
|
const eventName = readNonEmptyString2(payload.event);
|
|
14016
14102
|
if (!isMeshCoordinatorEvent(eventName)) {
|
|
@@ -14036,70 +14122,12 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
14036
14122
|
event: eventName
|
|
14037
14123
|
});
|
|
14038
14124
|
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
|
|
14039
|
-
const relayModalMessage = readNonEmptyString2(payload.modalMessage);
|
|
14040
|
-
const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
|
|
14041
14125
|
return injectMeshSystemMessage(components, {
|
|
14042
14126
|
meshId,
|
|
14043
14127
|
nodeId,
|
|
14044
14128
|
nodeLabel,
|
|
14045
14129
|
event: eventName,
|
|
14046
|
-
metadataEvent:
|
|
14047
|
-
targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
|
|
14048
|
-
providerType: readNonEmptyString2(payload.providerType),
|
|
14049
|
-
providerSessionId: readNonEmptyString2(payload.providerSessionId),
|
|
14050
|
-
// Preserve the originating coordinator SESSION id across the machine boundary so
|
|
14051
|
-
// the completion routes back to the exact coordinator session (multi-coordinator).
|
|
14052
|
-
// buildForwardPayloadFromPending spreads the worker event's metadata, so the id
|
|
14053
|
-
// arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
|
|
14054
|
-
// is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
|
|
14055
|
-
// anchors from this. Absent → daemon-level fallback (version-skew safe).
|
|
14056
|
-
meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
|
|
14057
|
-
// Carry the session identity fields the worker provider event emits so the
|
|
14058
|
-
// coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
|
|
14059
|
-
// settings. Without these the remote-relay hop reconstructs metadataEvent with
|
|
14060
|
-
// an empty workspace, and the dashboard flaps to the generic
|
|
14061
|
-
// "Terminal (Mesh Node)" title (and degrades the provider label) between live
|
|
14062
|
-
// events and the periodic get_status_metadata snapshot. The local in-process
|
|
14063
|
-
// forward path (onMeshCoordinatorEventForwarded) already preserves these; this
|
|
14064
|
-
// mirrors them for the remote-only relay path.
|
|
14065
|
-
workspace: readNonEmptyString2(payload.workspace) || readNonEmptyString2(payload.workspaceName),
|
|
14066
|
-
workspaceName: readNonEmptyString2(payload.workspaceName) || readNonEmptyString2(payload.workspace),
|
|
14067
|
-
sessionTitle: readNonEmptyString2(payload.sessionTitle),
|
|
14068
|
-
sessionStatus: readNonEmptyString2(payload.sessionStatus),
|
|
14069
|
-
sessionChatStatus: readNonEmptyString2(payload.sessionChatStatus),
|
|
14070
|
-
providerName: readNonEmptyString2(payload.providerName),
|
|
14071
|
-
...payload.sessionSettings && typeof payload.sessionSettings === "object" && !Array.isArray(payload.sessionSettings) ? { sessionSettings: payload.sessionSettings } : {},
|
|
14072
|
-
finalSummary: readNonEmptyString2(payload.finalSummary) || readNonEmptyString2(payload.summary),
|
|
14073
|
-
// T2: carry the worker's status-snapshot last-message preview across the machine
|
|
14074
|
-
// boundary so a summary-less completion still surfaces the assistant reply in the
|
|
14075
|
-
// coordinator's inbox mirror. resolveMeshSurfacedSessionPreview reads these
|
|
14076
|
-
// (assistant-role only) when finalSummary is absent.
|
|
14077
|
-
lastMessagePreview: readNonEmptyString2(payload.lastMessagePreview),
|
|
14078
|
-
lastMessageRole: readNonEmptyString2(payload.lastMessageRole),
|
|
14079
|
-
...payload.lastMessageAt !== void 0 ? { lastMessageAt: payload.lastMessageAt } : {},
|
|
14080
|
-
jobId: readNonEmptyString2(payload.jobId),
|
|
14081
|
-
interactionId: readNonEmptyString2(payload.interactionId),
|
|
14082
|
-
status: readNonEmptyString2(payload.status),
|
|
14083
|
-
targetDaemonId: readNonEmptyString2(payload.targetDaemonId),
|
|
14084
|
-
startedAt: readNonEmptyString2(payload.startedAt),
|
|
14085
|
-
completedAt: readNonEmptyString2(payload.completedAt),
|
|
14086
|
-
retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
|
|
14087
|
-
...relayModalMessage ? { modalMessage: relayModalMessage } : {},
|
|
14088
|
-
...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
|
|
14089
|
-
...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
|
|
14090
|
-
...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
|
|
14091
|
-
...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
|
|
14092
|
-
...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
|
|
14093
|
-
...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
|
|
14094
|
-
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
14095
|
-
intentional: payload.intentional === true,
|
|
14096
|
-
intentionalStop: payload.intentionalStop === true,
|
|
14097
|
-
operatorCleanup: payload.operatorCleanup === true,
|
|
14098
|
-
reason: readNonEmptyString2(payload.reason),
|
|
14099
|
-
stopReason: readNonEmptyString2(payload.stopReason),
|
|
14100
|
-
cleanupReason: readNonEmptyString2(payload.cleanupReason),
|
|
14101
|
-
source: readNonEmptyString2(payload.source)
|
|
14102
|
-
}
|
|
14130
|
+
metadataEvent: buildRelayMetadataEvent(payload)
|
|
14103
14131
|
});
|
|
14104
14132
|
}
|
|
14105
14133
|
function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
@@ -19514,6 +19542,7 @@ __export(evaluator_exports, {
|
|
|
19514
19542
|
evaluateCondition: () => evaluateCondition,
|
|
19515
19543
|
extractButtonsFromRule: () => extractButtonsFromRule,
|
|
19516
19544
|
extractTitle: () => extractTitle,
|
|
19545
|
+
lastContiguousNumberedBlock: () => lastContiguousNumberedBlock,
|
|
19517
19546
|
resolveSections: () => resolveSections,
|
|
19518
19547
|
sectionText: () => sectionText
|
|
19519
19548
|
});
|
|
@@ -19754,7 +19783,6 @@ function extractButtonsFromRule(rule, hay) {
|
|
|
19754
19783
|
label += " " + next.trim();
|
|
19755
19784
|
j += 1;
|
|
19756
19785
|
}
|
|
19757
|
-
if (buttons.some((b) => b.index === idx)) continue;
|
|
19758
19786
|
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19759
19787
|
buttons.push({ index: idx, label, key, current });
|
|
19760
19788
|
i = j - 1;
|
|
@@ -19766,13 +19794,22 @@ function extractButtonsFromRule(rule, hay) {
|
|
|
19766
19794
|
const idx = Number(m[1]);
|
|
19767
19795
|
const label = String(m[2] ?? "").trim();
|
|
19768
19796
|
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19769
|
-
if (buttons.some((b) => b.index === idx)) continue;
|
|
19770
19797
|
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19771
19798
|
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
19772
19799
|
}
|
|
19773
19800
|
}
|
|
19774
|
-
|
|
19775
|
-
|
|
19801
|
+
const block2 = lastContiguousNumberedBlock(buttons);
|
|
19802
|
+
block2.sort((a, b) => a.index - b.index);
|
|
19803
|
+
return block2;
|
|
19804
|
+
}
|
|
19805
|
+
function lastContiguousNumberedBlock(entries) {
|
|
19806
|
+
if (entries.length <= 1) return entries.slice();
|
|
19807
|
+
let start = entries.length - 1;
|
|
19808
|
+
for (let i = entries.length - 1; i > 0; i -= 1) {
|
|
19809
|
+
if (entries[i - 1].index === entries[i].index - 1) start = i - 1;
|
|
19810
|
+
else break;
|
|
19811
|
+
}
|
|
19812
|
+
return entries.slice(start);
|
|
19776
19813
|
}
|
|
19777
19814
|
function hasCursorMarker(text) {
|
|
19778
19815
|
return /^\s*[❯›>]/.test(text);
|
|
@@ -19807,6 +19844,11 @@ function statusForState(state) {
|
|
|
19807
19844
|
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
19808
19845
|
return "idle";
|
|
19809
19846
|
}
|
|
19847
|
+
function modalKindForState(state) {
|
|
19848
|
+
if (state.modal_kind) return state.modal_kind;
|
|
19849
|
+
if (state.modal) return "approval";
|
|
19850
|
+
return null;
|
|
19851
|
+
}
|
|
19810
19852
|
var init_fsm_types = __esm({
|
|
19811
19853
|
"src/providers/spec/fsm-types.ts"() {
|
|
19812
19854
|
"use strict";
|
|
@@ -33698,7 +33740,12 @@ var FsmDriver = class {
|
|
|
33698
33740
|
this.emit({
|
|
33699
33741
|
kind: "state_changed",
|
|
33700
33742
|
state: next.state,
|
|
33701
|
-
|
|
33743
|
+
// kind is the SEMANTIC modal class (approval vs picker/confirm)
|
|
33744
|
+
// derived from the FSM state, NOT from the parsed buttons — the
|
|
33745
|
+
// status field already collapsed it to 'approval' so the modal is
|
|
33746
|
+
// surfaced. The auto-approve worker needs the distinction back to
|
|
33747
|
+
// avoid answering a /model picker on the user's behalf.
|
|
33748
|
+
modal: next.modal ? { title: next.modal.title, buttons: next.modal.buttons.map((b) => ({ index: b.index, label: b.label })), kind: modalKindForState(state) } : null,
|
|
33702
33749
|
controls: next.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
|
|
33703
33750
|
});
|
|
33704
33751
|
this.fireNotifications(state.id, title);
|
|
@@ -34180,6 +34227,9 @@ function collectStableSizes(when, sizes) {
|
|
|
34180
34227
|
}
|
|
34181
34228
|
}
|
|
34182
34229
|
|
|
34230
|
+
// src/providers/spec/cli-adapter.ts
|
|
34231
|
+
init_evaluator();
|
|
34232
|
+
|
|
34183
34233
|
// src/providers/spec/native-history-executor.ts
|
|
34184
34234
|
var fs13 = __toESM(require("fs"));
|
|
34185
34235
|
var os18 = __toESM(require("os"));
|
|
@@ -35118,7 +35168,9 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
35118
35168
|
messages: [],
|
|
35119
35169
|
// Surface buttons when we have them; an approval state with no parsed
|
|
35120
35170
|
// modal this frame still stays waiting_approval (no activeModal yet).
|
|
35121
|
-
|
|
35171
|
+
// `kind` carries the semantic modal class through to the auto-approve
|
|
35172
|
+
// gate so a /model picker (kind='picker') is never auto-answered.
|
|
35173
|
+
activeModal: modal ? { message: modal.title ?? state.label, buttons: modal.buttons.map((b) => b.label), kind: modal.kind ?? null } : null,
|
|
35122
35174
|
activeInteractivePrompt: this.activeInteractivePrompt,
|
|
35123
35175
|
...sessionFields
|
|
35124
35176
|
};
|
|
@@ -35392,21 +35444,19 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
35392
35444
|
const ec = action.extract_choices;
|
|
35393
35445
|
if (!ec?.pattern) return [];
|
|
35394
35446
|
const text = this.readScreenSectionText(ec.section);
|
|
35395
|
-
const
|
|
35396
|
-
const seen = /* @__PURE__ */ new Set();
|
|
35447
|
+
const all = [];
|
|
35397
35448
|
for (const rawLine of text.split("\n")) {
|
|
35398
35449
|
const line = rawLine.replace(/\r$/, "");
|
|
35399
35450
|
const m = new RegExp(ec.pattern, ec.flags ?? "").exec(line);
|
|
35400
35451
|
if (!m) continue;
|
|
35401
35452
|
const idx = Number(m[1]);
|
|
35402
|
-
if (!Number.isFinite(idx) ||
|
|
35453
|
+
if (!Number.isFinite(idx) || idx <= 0) continue;
|
|
35403
35454
|
const label = (m[2] ?? "").replace(/\s+/g, " ").trim();
|
|
35404
35455
|
if (!label) continue;
|
|
35405
35456
|
const current = /^\s*[❯›>]/.test(line) || /[✔✓●]\s*$/.test(label);
|
|
35406
|
-
|
|
35407
|
-
out.push({ index: idx, label, current });
|
|
35457
|
+
all.push({ index: idx, label, current });
|
|
35408
35458
|
}
|
|
35409
|
-
return
|
|
35459
|
+
return lastContiguousNumberedBlock(all);
|
|
35410
35460
|
}
|
|
35411
35461
|
/** Live text of a named screen section (or the whole screen when no
|
|
35412
35462
|
* section is named), resolved from the driver's current sections. */
|
|
@@ -37085,8 +37135,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
37085
37135
|
if (!modal || buttons.length === 0) {
|
|
37086
37136
|
return autoApproveActive;
|
|
37087
37137
|
}
|
|
37088
|
-
const
|
|
37089
|
-
if (
|
|
37138
|
+
const modalKind = typeof modal?.kind === "string" ? modal.kind : "approval";
|
|
37139
|
+
if (modalKind !== "approval") {
|
|
37140
|
+
return autoApproveActive;
|
|
37141
|
+
}
|
|
37142
|
+
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
|
|
37143
|
+
if (buttonIndex < 0 || !hasNegativeApprovalOption(buttons)) {
|
|
37090
37144
|
return autoApproveActive;
|
|
37091
37145
|
}
|
|
37092
37146
|
const modalSignature = [
|