@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.
@@ -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 ? "91500e054cf2b6258f6041f90c18be03ad05a8ad" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "91500e05" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.356" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-22T22:47:29.082Z" : void 0);
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
- if (localMesh) return localMesh;
12677
- return components.router?.getCachedInlineMesh(meshId);
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
- updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
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
- buttons.sort((a, b) => a.index - b.index);
19775
- return buttons;
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
- modal: next.modal ? { title: next.modal.title, buttons: next.modal.buttons.map((b) => ({ index: b.index, label: b.label })) } : null,
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
- activeModal: modal ? { message: modal.title ?? state.label, buttons: modal.buttons.map((b) => b.label) } : null,
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 out = [];
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) || seen.has(idx)) continue;
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
- seen.add(idx);
35407
- out.push({ index: idx, label, current });
35457
+ all.push({ index: idx, label, current });
35408
35458
  }
35409
- return out;
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 { index: buttonIndex, label: buttonLabel } = pickAutoApprovalButton(buttons);
37089
- if (buttonIndex < 0) {
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 = [