@adhdev/daemon-core 0.9.82-rc.355 → 0.9.82-rc.357
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 +177 -76
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +177 -76
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-coordinator.d.ts +1 -0
- package/dist/providers/spec/cli-adapter.d.ts +6 -0
- package/dist/providers/spec/evaluator.d.ts +1 -0
- package/dist/providers/spec/fsm-driver.d.ts +1 -0
- package/dist/providers/spec/fsm-types.d.ts +30 -0
- package/dist/providers/spec/types.d.ts +38 -0
- package/package.json +2 -2
- package/src/cli-adapter-types.ts +9 -0
- package/src/mesh/mesh-events-coordinator.ts +128 -63
- package/src/providers/cli-provider-instance.ts +46 -5
- package/src/providers/spec/cli-adapter.ts +61 -9
- package/src/providers/spec/evaluator.ts +14 -4
- package/src/providers/spec/fsm-driver.ts +31 -4
- package/src/providers/spec/fsm-types.ts +35 -0
- package/src/providers/spec/types.ts +32 -0
package/dist/index.mjs
CHANGED
|
@@ -311,10 +311,10 @@ function readInjected(value) {
|
|
|
311
311
|
}
|
|
312
312
|
function getDaemonBuildInfo() {
|
|
313
313
|
if (cached) return cached;
|
|
314
|
-
const commit = readInjected(true ? "
|
|
315
|
-
const commitShort = readInjected(true ? "
|
|
316
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
317
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
314
|
+
const commit = readInjected(true ? "37b9d1d9e10336707dbc0861a96d5bcc55cc015e" : void 0) ?? "unknown";
|
|
315
|
+
const commitShort = readInjected(true ? "37b9d1d9" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
316
|
+
const version = readInjected(true ? "0.9.82-rc.357" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
317
|
+
const builtAt = readInjected(true ? "2026-06-23T00:25:21.599Z" : void 0);
|
|
318
318
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
319
319
|
return cached;
|
|
320
320
|
}
|
|
@@ -12670,8 +12670,22 @@ function sweepExpiredRemoteIdleSessions() {
|
|
|
12670
12670
|
}
|
|
12671
12671
|
function getMeshWithCache(components, meshId) {
|
|
12672
12672
|
const localMesh = getMesh(meshId);
|
|
12673
|
-
|
|
12674
|
-
|
|
12673
|
+
const cachedMesh = components.router?.getCachedInlineMesh(meshId);
|
|
12674
|
+
if (!localMesh) return cachedMesh;
|
|
12675
|
+
if (!cachedMesh) return localMesh;
|
|
12676
|
+
return mergeInlineCacheOnlyNodes(localMesh, cachedMesh);
|
|
12677
|
+
}
|
|
12678
|
+
function mergeInlineCacheOnlyNodes(localMesh, cachedMesh) {
|
|
12679
|
+
const localNodes = Array.isArray(localMesh?.nodes) ? localMesh.nodes : [];
|
|
12680
|
+
const cachedNodes = Array.isArray(cachedMesh?.nodes) ? cachedMesh.nodes : [];
|
|
12681
|
+
if (!cachedNodes.length) return localMesh;
|
|
12682
|
+
const cacheOnly = cachedNodes.filter((cachedNode) => {
|
|
12683
|
+
const cachedId = readMeshNodeId(cachedNode);
|
|
12684
|
+
if (!cachedId) return false;
|
|
12685
|
+
return !localNodes.some((localNode) => meshNodeIdMatches(localNode, cachedId));
|
|
12686
|
+
});
|
|
12687
|
+
if (!cacheOnly.length) return localMesh;
|
|
12688
|
+
return { ...localMesh, nodes: [...localNodes, ...cacheOnly] };
|
|
12675
12689
|
}
|
|
12676
12690
|
function isIntentionalCleanupStopMetadata(event) {
|
|
12677
12691
|
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";
|
|
@@ -14008,6 +14022,76 @@ function injectMeshSystemMessage(components, args) {
|
|
|
14008
14022
|
}
|
|
14009
14023
|
return { success: true, forwarded: 0 };
|
|
14010
14024
|
}
|
|
14025
|
+
function buildRelayMetadataEvent(payload) {
|
|
14026
|
+
const relayModalMessage = readNonEmptyString2(payload.modalMessage);
|
|
14027
|
+
const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
|
|
14028
|
+
return {
|
|
14029
|
+
// Preserve the dispatch task id across the machine boundary. The `received` trace
|
|
14030
|
+
// stage reads payload.taskId; without mirroring it here the rebuilt metadataEvent
|
|
14031
|
+
// loses it, so injectMeshSystemMessage's traceCtx.taskId and the
|
|
14032
|
+
// updateDirectDispatchStatus(eventTaskId) call go undefined — the EvtTrace
|
|
14033
|
+
// queued/surfaced stages show task=- and the direct-dispatch ledger falls back to a
|
|
14034
|
+
// session_id match (which can flip a sibling row). The local in-process forward path
|
|
14035
|
+
// keeps event.taskId/meshActiveTaskId for free; this mirrors it for the remote relay.
|
|
14036
|
+
// Same taskId/meshActiveTaskId ordering the local unroutable trace uses.
|
|
14037
|
+
taskId: readNonEmptyString2(payload.taskId) || readNonEmptyString2(payload.meshActiveTaskId),
|
|
14038
|
+
targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
|
|
14039
|
+
providerType: readNonEmptyString2(payload.providerType),
|
|
14040
|
+
providerSessionId: readNonEmptyString2(payload.providerSessionId),
|
|
14041
|
+
// Preserve the originating coordinator SESSION id across the machine boundary so
|
|
14042
|
+
// the completion routes back to the exact coordinator session (multi-coordinator).
|
|
14043
|
+
// buildForwardPayloadFromPending spreads the worker event's metadata, so the id
|
|
14044
|
+
// arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
|
|
14045
|
+
// is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
|
|
14046
|
+
// anchors from this. Absent → daemon-level fallback (version-skew safe).
|
|
14047
|
+
meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
|
|
14048
|
+
// Carry the session identity fields the worker provider event emits so the
|
|
14049
|
+
// coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
|
|
14050
|
+
// settings. Without these the remote-relay hop reconstructs metadataEvent with
|
|
14051
|
+
// an empty workspace, and the dashboard flaps to the generic
|
|
14052
|
+
// "Terminal (Mesh Node)" title (and degrades the provider label) between live
|
|
14053
|
+
// events and the periodic get_status_metadata snapshot. The local in-process
|
|
14054
|
+
// forward path (onMeshCoordinatorEventForwarded) already preserves these; this
|
|
14055
|
+
// mirrors them for the remote-only relay path.
|
|
14056
|
+
workspace: readNonEmptyString2(payload.workspace) || readNonEmptyString2(payload.workspaceName),
|
|
14057
|
+
workspaceName: readNonEmptyString2(payload.workspaceName) || readNonEmptyString2(payload.workspace),
|
|
14058
|
+
sessionTitle: readNonEmptyString2(payload.sessionTitle),
|
|
14059
|
+
sessionStatus: readNonEmptyString2(payload.sessionStatus),
|
|
14060
|
+
sessionChatStatus: readNonEmptyString2(payload.sessionChatStatus),
|
|
14061
|
+
providerName: readNonEmptyString2(payload.providerName),
|
|
14062
|
+
...payload.sessionSettings && typeof payload.sessionSettings === "object" && !Array.isArray(payload.sessionSettings) ? { sessionSettings: payload.sessionSettings } : {},
|
|
14063
|
+
finalSummary: readNonEmptyString2(payload.finalSummary) || readNonEmptyString2(payload.summary),
|
|
14064
|
+
// T2: carry the worker's status-snapshot last-message preview across the machine
|
|
14065
|
+
// boundary so a summary-less completion still surfaces the assistant reply in the
|
|
14066
|
+
// coordinator's inbox mirror. resolveMeshSurfacedSessionPreview reads these
|
|
14067
|
+
// (assistant-role only) when finalSummary is absent.
|
|
14068
|
+
lastMessagePreview: readNonEmptyString2(payload.lastMessagePreview),
|
|
14069
|
+
lastMessageRole: readNonEmptyString2(payload.lastMessageRole),
|
|
14070
|
+
...payload.lastMessageAt !== void 0 ? { lastMessageAt: payload.lastMessageAt } : {},
|
|
14071
|
+
jobId: readNonEmptyString2(payload.jobId),
|
|
14072
|
+
interactionId: readNonEmptyString2(payload.interactionId),
|
|
14073
|
+
status: readNonEmptyString2(payload.status),
|
|
14074
|
+
targetDaemonId: readNonEmptyString2(payload.targetDaemonId),
|
|
14075
|
+
startedAt: readNonEmptyString2(payload.startedAt),
|
|
14076
|
+
completedAt: readNonEmptyString2(payload.completedAt),
|
|
14077
|
+
retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
|
|
14078
|
+
...relayModalMessage ? { modalMessage: relayModalMessage } : {},
|
|
14079
|
+
...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
|
|
14080
|
+
...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
|
|
14081
|
+
...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
|
|
14082
|
+
...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
|
|
14083
|
+
...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
|
|
14084
|
+
...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
|
|
14085
|
+
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
14086
|
+
intentional: payload.intentional === true,
|
|
14087
|
+
intentionalStop: payload.intentionalStop === true,
|
|
14088
|
+
operatorCleanup: payload.operatorCleanup === true,
|
|
14089
|
+
reason: readNonEmptyString2(payload.reason),
|
|
14090
|
+
stopReason: readNonEmptyString2(payload.stopReason),
|
|
14091
|
+
cleanupReason: readNonEmptyString2(payload.cleanupReason),
|
|
14092
|
+
source: readNonEmptyString2(payload.source)
|
|
14093
|
+
};
|
|
14094
|
+
}
|
|
14011
14095
|
function handleMeshForwardEvent(components, payload) {
|
|
14012
14096
|
const eventName = readNonEmptyString2(payload.event);
|
|
14013
14097
|
if (!isMeshCoordinatorEvent(eventName)) {
|
|
@@ -14033,70 +14117,12 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
14033
14117
|
event: eventName
|
|
14034
14118
|
});
|
|
14035
14119
|
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
|
|
14036
|
-
const relayModalMessage = readNonEmptyString2(payload.modalMessage);
|
|
14037
|
-
const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
|
|
14038
14120
|
return injectMeshSystemMessage(components, {
|
|
14039
14121
|
meshId,
|
|
14040
14122
|
nodeId,
|
|
14041
14123
|
nodeLabel,
|
|
14042
14124
|
event: eventName,
|
|
14043
|
-
metadataEvent:
|
|
14044
|
-
targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
|
|
14045
|
-
providerType: readNonEmptyString2(payload.providerType),
|
|
14046
|
-
providerSessionId: readNonEmptyString2(payload.providerSessionId),
|
|
14047
|
-
// Preserve the originating coordinator SESSION id across the machine boundary so
|
|
14048
|
-
// the completion routes back to the exact coordinator session (multi-coordinator).
|
|
14049
|
-
// buildForwardPayloadFromPending spreads the worker event's metadata, so the id
|
|
14050
|
-
// arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
|
|
14051
|
-
// is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
|
|
14052
|
-
// anchors from this. Absent → daemon-level fallback (version-skew safe).
|
|
14053
|
-
meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
|
|
14054
|
-
// Carry the session identity fields the worker provider event emits so the
|
|
14055
|
-
// coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
|
|
14056
|
-
// settings. Without these the remote-relay hop reconstructs metadataEvent with
|
|
14057
|
-
// an empty workspace, and the dashboard flaps to the generic
|
|
14058
|
-
// "Terminal (Mesh Node)" title (and degrades the provider label) between live
|
|
14059
|
-
// events and the periodic get_status_metadata snapshot. The local in-process
|
|
14060
|
-
// forward path (onMeshCoordinatorEventForwarded) already preserves these; this
|
|
14061
|
-
// mirrors them for the remote-only relay path.
|
|
14062
|
-
workspace: readNonEmptyString2(payload.workspace) || readNonEmptyString2(payload.workspaceName),
|
|
14063
|
-
workspaceName: readNonEmptyString2(payload.workspaceName) || readNonEmptyString2(payload.workspace),
|
|
14064
|
-
sessionTitle: readNonEmptyString2(payload.sessionTitle),
|
|
14065
|
-
sessionStatus: readNonEmptyString2(payload.sessionStatus),
|
|
14066
|
-
sessionChatStatus: readNonEmptyString2(payload.sessionChatStatus),
|
|
14067
|
-
providerName: readNonEmptyString2(payload.providerName),
|
|
14068
|
-
...payload.sessionSettings && typeof payload.sessionSettings === "object" && !Array.isArray(payload.sessionSettings) ? { sessionSettings: payload.sessionSettings } : {},
|
|
14069
|
-
finalSummary: readNonEmptyString2(payload.finalSummary) || readNonEmptyString2(payload.summary),
|
|
14070
|
-
// T2: carry the worker's status-snapshot last-message preview across the machine
|
|
14071
|
-
// boundary so a summary-less completion still surfaces the assistant reply in the
|
|
14072
|
-
// coordinator's inbox mirror. resolveMeshSurfacedSessionPreview reads these
|
|
14073
|
-
// (assistant-role only) when finalSummary is absent.
|
|
14074
|
-
lastMessagePreview: readNonEmptyString2(payload.lastMessagePreview),
|
|
14075
|
-
lastMessageRole: readNonEmptyString2(payload.lastMessageRole),
|
|
14076
|
-
...payload.lastMessageAt !== void 0 ? { lastMessageAt: payload.lastMessageAt } : {},
|
|
14077
|
-
jobId: readNonEmptyString2(payload.jobId),
|
|
14078
|
-
interactionId: readNonEmptyString2(payload.interactionId),
|
|
14079
|
-
status: readNonEmptyString2(payload.status),
|
|
14080
|
-
targetDaemonId: readNonEmptyString2(payload.targetDaemonId),
|
|
14081
|
-
startedAt: readNonEmptyString2(payload.startedAt),
|
|
14082
|
-
completedAt: readNonEmptyString2(payload.completedAt),
|
|
14083
|
-
retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
|
|
14084
|
-
...relayModalMessage ? { modalMessage: relayModalMessage } : {},
|
|
14085
|
-
...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
|
|
14086
|
-
...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
|
|
14087
|
-
...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
|
|
14088
|
-
...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
|
|
14089
|
-
...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
|
|
14090
|
-
...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
|
|
14091
|
-
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
14092
|
-
intentional: payload.intentional === true,
|
|
14093
|
-
intentionalStop: payload.intentionalStop === true,
|
|
14094
|
-
operatorCleanup: payload.operatorCleanup === true,
|
|
14095
|
-
reason: readNonEmptyString2(payload.reason),
|
|
14096
|
-
stopReason: readNonEmptyString2(payload.stopReason),
|
|
14097
|
-
cleanupReason: readNonEmptyString2(payload.cleanupReason),
|
|
14098
|
-
source: readNonEmptyString2(payload.source)
|
|
14099
|
-
}
|
|
14125
|
+
metadataEvent: buildRelayMetadataEvent(payload)
|
|
14100
14126
|
});
|
|
14101
14127
|
}
|
|
14102
14128
|
function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
@@ -19739,6 +19765,7 @@ function extractButtonsFromRule(rule, hay) {
|
|
|
19739
19765
|
const idx = Number(m[1]);
|
|
19740
19766
|
let label = String(m[2] ?? "").trim();
|
|
19741
19767
|
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19768
|
+
const current = hasCursorMarker(lines[i]);
|
|
19742
19769
|
let j = i + 1;
|
|
19743
19770
|
while (j < lines.length) {
|
|
19744
19771
|
const next = lines[j];
|
|
@@ -19750,7 +19777,7 @@ function extractButtonsFromRule(rule, hay) {
|
|
|
19750
19777
|
}
|
|
19751
19778
|
if (buttons.some((b) => b.index === idx)) continue;
|
|
19752
19779
|
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19753
|
-
buttons.push({ index: idx, label, key });
|
|
19780
|
+
buttons.push({ index: idx, label, key, current });
|
|
19754
19781
|
i = j - 1;
|
|
19755
19782
|
}
|
|
19756
19783
|
} else {
|
|
@@ -19762,12 +19789,15 @@ function extractButtonsFromRule(rule, hay) {
|
|
|
19762
19789
|
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19763
19790
|
if (buttons.some((b) => b.index === idx)) continue;
|
|
19764
19791
|
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19765
|
-
buttons.push({ index: idx, label, key });
|
|
19792
|
+
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
19766
19793
|
}
|
|
19767
19794
|
}
|
|
19768
19795
|
buttons.sort((a, b) => a.index - b.index);
|
|
19769
19796
|
return buttons;
|
|
19770
19797
|
}
|
|
19798
|
+
function hasCursorMarker(text) {
|
|
19799
|
+
return /^\s*[❯›>]/.test(text);
|
|
19800
|
+
}
|
|
19771
19801
|
var init_evaluator = __esm({
|
|
19772
19802
|
"src/providers/spec/evaluator.ts"() {
|
|
19773
19803
|
"use strict";
|
|
@@ -19798,6 +19828,11 @@ function statusForState(state) {
|
|
|
19798
19828
|
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
19799
19829
|
return "idle";
|
|
19800
19830
|
}
|
|
19831
|
+
function modalKindForState(state) {
|
|
19832
|
+
if (state.modal_kind) return state.modal_kind;
|
|
19833
|
+
if (state.modal) return "approval";
|
|
19834
|
+
return null;
|
|
19835
|
+
}
|
|
19801
19836
|
var init_fsm_types = __esm({
|
|
19802
19837
|
"src/providers/spec/fsm-types.ts"() {
|
|
19803
19838
|
"use strict";
|
|
@@ -33326,7 +33361,12 @@ var FsmDriver = class {
|
|
|
33326
33361
|
this.emit({
|
|
33327
33362
|
kind: "state_changed",
|
|
33328
33363
|
state: next.state,
|
|
33329
|
-
|
|
33364
|
+
// kind is the SEMANTIC modal class (approval vs picker/confirm)
|
|
33365
|
+
// derived from the FSM state, NOT from the parsed buttons — the
|
|
33366
|
+
// status field already collapsed it to 'approval' so the modal is
|
|
33367
|
+
// surfaced. The auto-approve worker needs the distinction back to
|
|
33368
|
+
// avoid answering a /model picker on the user's behalf.
|
|
33369
|
+
modal: next.modal ? { title: next.modal.title, buttons: next.modal.buttons.map((b) => ({ index: b.index, label: b.label })), kind: modalKindForState(state) } : null,
|
|
33330
33370
|
controls: next.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
|
|
33331
33371
|
});
|
|
33332
33372
|
this.fireNotifications(state.id, title);
|
|
@@ -33681,6 +33721,19 @@ var FsmDriver = class {
|
|
|
33681
33721
|
if (!m) return;
|
|
33682
33722
|
const btn = m.buttons.find((b) => b.index === index);
|
|
33683
33723
|
if (!btn) return;
|
|
33724
|
+
const rule = stateById(this.spec, this.currentStateId)?.extract?.buttons;
|
|
33725
|
+
if (rule?.select_mode === "arrow_keys") {
|
|
33726
|
+
const from = m.buttons.find((b) => b.current)?.index ?? 1;
|
|
33727
|
+
const up = rule.cursor_keys?.up ?? "\x1B[A";
|
|
33728
|
+
const down = rule.cursor_keys?.down ?? "\x1B[B";
|
|
33729
|
+
const delta = btn.index - from;
|
|
33730
|
+
const step = delta >= 0 ? down : up;
|
|
33731
|
+
const nav = step.repeat(Math.abs(delta));
|
|
33732
|
+
const confirm = (rule.key_for_index || "\r").replace(/\{index\}/g, "") || "\r";
|
|
33733
|
+
if (nav) this.adapter.send_keys(nav);
|
|
33734
|
+
this.adapter.send_keys(confirm);
|
|
33735
|
+
return;
|
|
33736
|
+
}
|
|
33684
33737
|
this.adapter.send_keys(btn.key);
|
|
33685
33738
|
}
|
|
33686
33739
|
handleAttachImage(blob, mime) {
|
|
@@ -34733,7 +34786,9 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
34733
34786
|
messages: [],
|
|
34734
34787
|
// Surface buttons when we have them; an approval state with no parsed
|
|
34735
34788
|
// modal this frame still stays waiting_approval (no activeModal yet).
|
|
34736
|
-
|
|
34789
|
+
// `kind` carries the semantic modal class through to the auto-approve
|
|
34790
|
+
// gate so a /model picker (kind='picker') is never auto-answered.
|
|
34791
|
+
activeModal: modal ? { message: modal.title ?? state.label, buttons: modal.buttons.map((b) => b.label), kind: modal.kind ?? null } : null,
|
|
34737
34792
|
activeInteractivePrompt: this.activeInteractivePrompt,
|
|
34738
34793
|
...sessionFields
|
|
34739
34794
|
};
|
|
@@ -34918,9 +34973,12 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
34918
34973
|
* — not this code — decides how a selection is keyed for each CLI.
|
|
34919
34974
|
*/
|
|
34920
34975
|
async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
|
|
34921
|
-
|
|
34922
|
-
|
|
34923
|
-
|
|
34976
|
+
let options = this.extractPickerChoicesIfRendered(action);
|
|
34977
|
+
if (!options) {
|
|
34978
|
+
this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
|
|
34979
|
+
await this.waitForPickerRendered(action);
|
|
34980
|
+
options = this.extractPickerChoices(action);
|
|
34981
|
+
}
|
|
34924
34982
|
let index = choiceIndex;
|
|
34925
34983
|
if ((index == null || !Number.isFinite(index)) && choiceLabel) {
|
|
34926
34984
|
const needle = choiceLabel.trim().toLowerCase();
|
|
@@ -34933,8 +34991,27 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
34933
34991
|
if (index == null || !Number.isFinite(index)) {
|
|
34934
34992
|
return { ok: false, error: "choiceIndex or choiceLabel required to select" };
|
|
34935
34993
|
}
|
|
34936
|
-
|
|
34937
|
-
|
|
34994
|
+
if (action.select_mode === "arrow_keys") {
|
|
34995
|
+
const current = options.find((o) => o.current);
|
|
34996
|
+
if (current == null) {
|
|
34997
|
+
return {
|
|
34998
|
+
ok: false,
|
|
34999
|
+
error: "arrow-nav picker: current cursor row not detected on screen",
|
|
35000
|
+
controlResult: { options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })) }
|
|
35001
|
+
};
|
|
35002
|
+
}
|
|
35003
|
+
const up = action.cursor_keys?.up ?? "\x1B[A";
|
|
35004
|
+
const down = action.cursor_keys?.down ?? "\x1B[B";
|
|
35005
|
+
const delta = index - current.index;
|
|
35006
|
+
const step = delta >= 0 ? down : up;
|
|
35007
|
+
const nav = step.repeat(Math.abs(delta));
|
|
35008
|
+
const confirm = (action.submit_key || "\r").replace(/\{index\}/g, "") || "\r";
|
|
35009
|
+
if (nav) this.driver.dispatch({ kind: "pty_write", data: nav });
|
|
35010
|
+
this.driver.dispatch({ kind: "pty_write", data: confirm });
|
|
35011
|
+
} else {
|
|
35012
|
+
const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
|
|
35013
|
+
this.driver.dispatch({ kind: "pty_write", data: keys });
|
|
35014
|
+
}
|
|
34938
35015
|
const selected = options.find((o) => o.index === index);
|
|
34939
35016
|
return {
|
|
34940
35017
|
ok: true,
|
|
@@ -34946,6 +35023,20 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
34946
35023
|
}
|
|
34947
35024
|
};
|
|
34948
35025
|
}
|
|
35026
|
+
/** Parse the picker choices only if the picker already appears rendered on
|
|
35027
|
+
* the live screen (its `wait_for` condition currently matches and at least
|
|
35028
|
+
* one choice parses). Returns the parsed choices when open, else null so
|
|
35029
|
+
* the caller knows it must send the trigger to open it. Used to de-dup the
|
|
35030
|
+
* picker open in {@link selectPickerChoice}. */
|
|
35031
|
+
extractPickerChoicesIfRendered(action) {
|
|
35032
|
+
const wf = action.wait_for;
|
|
35033
|
+
if (wf?.regex) {
|
|
35034
|
+
const re = new RegExp(wf.regex, wf.flags ?? "i");
|
|
35035
|
+
if (!re.test(this.readScreenSectionText(wf.section))) return null;
|
|
35036
|
+
}
|
|
35037
|
+
const options = this.extractPickerChoices(action);
|
|
35038
|
+
return options.length > 0 ? options : null;
|
|
35039
|
+
}
|
|
34949
35040
|
/** Poll the live screen until the picker's `wait_for` condition matches,
|
|
34950
35041
|
* up to a short budget. Returns true if it rendered, false on timeout. */
|
|
34951
35042
|
async waitForPickerRendered(action) {
|
|
@@ -36664,8 +36755,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36664
36755
|
if (!modal || buttons.length === 0) {
|
|
36665
36756
|
return autoApproveActive;
|
|
36666
36757
|
}
|
|
36667
|
-
const
|
|
36668
|
-
if (
|
|
36758
|
+
const modalKind = typeof modal?.kind === "string" ? modal.kind : "approval";
|
|
36759
|
+
if (modalKind !== "approval") {
|
|
36760
|
+
return autoApproveActive;
|
|
36761
|
+
}
|
|
36762
|
+
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
|
|
36763
|
+
if (buttonIndex < 0 || !hasNegativeApprovalOption(buttons)) {
|
|
36669
36764
|
return autoApproveActive;
|
|
36670
36765
|
}
|
|
36671
36766
|
const modalSignature = [
|
|
@@ -36969,6 +37064,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36969
37064
|
workspace: typeof event.workspace === "string" && event.workspace.trim() ? event.workspace : this.workingDir,
|
|
36970
37065
|
providerSessionId: typeof event.providerSessionId === "string" && event.providerSessionId.trim() ? event.providerSessionId : this.providerSessionId
|
|
36971
37066
|
};
|
|
37067
|
+
if (this.isMeshWorkerSession() && this.settings.meshActiveTaskId) {
|
|
37068
|
+
const existingTaskId = typeof enrichedEvent.taskId === "string" && enrichedEvent.taskId.trim() ? enrichedEvent.taskId : void 0;
|
|
37069
|
+
if (!existingTaskId) {
|
|
37070
|
+
enrichedEvent.taskId = this.settings.meshActiveTaskId;
|
|
37071
|
+
}
|
|
37072
|
+
}
|
|
36972
37073
|
if (this.context?.emitProviderEvent) {
|
|
36973
37074
|
this.context.emitProviderEvent(enrichedEvent);
|
|
36974
37075
|
} else {
|