@adhdev/daemon-core 0.9.82-rc.356 → 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.
@@ -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 ? "37b9d1d9e10336707dbc0861a96d5bcc55cc015e" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "37b9d1d9" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.357" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-23T00:25:21.599Z" : 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";
@@ -14011,6 +14025,76 @@ function injectMeshSystemMessage(components, args) {
14011
14025
  }
14012
14026
  return { success: true, forwarded: 0 };
14013
14027
  }
14028
+ function buildRelayMetadataEvent(payload) {
14029
+ const relayModalMessage = readNonEmptyString2(payload.modalMessage);
14030
+ const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
14031
+ return {
14032
+ // Preserve the dispatch task id across the machine boundary. The `received` trace
14033
+ // stage reads payload.taskId; without mirroring it here the rebuilt metadataEvent
14034
+ // loses it, so injectMeshSystemMessage's traceCtx.taskId and the
14035
+ // updateDirectDispatchStatus(eventTaskId) call go undefined — the EvtTrace
14036
+ // queued/surfaced stages show task=- and the direct-dispatch ledger falls back to a
14037
+ // session_id match (which can flip a sibling row). The local in-process forward path
14038
+ // keeps event.taskId/meshActiveTaskId for free; this mirrors it for the remote relay.
14039
+ // Same taskId/meshActiveTaskId ordering the local unroutable trace uses.
14040
+ taskId: readNonEmptyString2(payload.taskId) || readNonEmptyString2(payload.meshActiveTaskId),
14041
+ targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
14042
+ providerType: readNonEmptyString2(payload.providerType),
14043
+ providerSessionId: readNonEmptyString2(payload.providerSessionId),
14044
+ // Preserve the originating coordinator SESSION id across the machine boundary so
14045
+ // the completion routes back to the exact coordinator session (multi-coordinator).
14046
+ // buildForwardPayloadFromPending spreads the worker event's metadata, so the id
14047
+ // arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
14048
+ // is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
14049
+ // anchors from this. Absent → daemon-level fallback (version-skew safe).
14050
+ meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
14051
+ // Carry the session identity fields the worker provider event emits so the
14052
+ // coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
14053
+ // settings. Without these the remote-relay hop reconstructs metadataEvent with
14054
+ // an empty workspace, and the dashboard flaps to the generic
14055
+ // "Terminal (Mesh Node)" title (and degrades the provider label) between live
14056
+ // events and the periodic get_status_metadata snapshot. The local in-process
14057
+ // forward path (onMeshCoordinatorEventForwarded) already preserves these; this
14058
+ // mirrors them for the remote-only relay path.
14059
+ workspace: readNonEmptyString2(payload.workspace) || readNonEmptyString2(payload.workspaceName),
14060
+ workspaceName: readNonEmptyString2(payload.workspaceName) || readNonEmptyString2(payload.workspace),
14061
+ sessionTitle: readNonEmptyString2(payload.sessionTitle),
14062
+ sessionStatus: readNonEmptyString2(payload.sessionStatus),
14063
+ sessionChatStatus: readNonEmptyString2(payload.sessionChatStatus),
14064
+ providerName: readNonEmptyString2(payload.providerName),
14065
+ ...payload.sessionSettings && typeof payload.sessionSettings === "object" && !Array.isArray(payload.sessionSettings) ? { sessionSettings: payload.sessionSettings } : {},
14066
+ finalSummary: readNonEmptyString2(payload.finalSummary) || readNonEmptyString2(payload.summary),
14067
+ // T2: carry the worker's status-snapshot last-message preview across the machine
14068
+ // boundary so a summary-less completion still surfaces the assistant reply in the
14069
+ // coordinator's inbox mirror. resolveMeshSurfacedSessionPreview reads these
14070
+ // (assistant-role only) when finalSummary is absent.
14071
+ lastMessagePreview: readNonEmptyString2(payload.lastMessagePreview),
14072
+ lastMessageRole: readNonEmptyString2(payload.lastMessageRole),
14073
+ ...payload.lastMessageAt !== void 0 ? { lastMessageAt: payload.lastMessageAt } : {},
14074
+ jobId: readNonEmptyString2(payload.jobId),
14075
+ interactionId: readNonEmptyString2(payload.interactionId),
14076
+ status: readNonEmptyString2(payload.status),
14077
+ targetDaemonId: readNonEmptyString2(payload.targetDaemonId),
14078
+ startedAt: readNonEmptyString2(payload.startedAt),
14079
+ completedAt: readNonEmptyString2(payload.completedAt),
14080
+ retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
14081
+ ...relayModalMessage ? { modalMessage: relayModalMessage } : {},
14082
+ ...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
14083
+ ...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
14084
+ ...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
14085
+ ...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
14086
+ ...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
14087
+ ...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
14088
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
14089
+ intentional: payload.intentional === true,
14090
+ intentionalStop: payload.intentionalStop === true,
14091
+ operatorCleanup: payload.operatorCleanup === true,
14092
+ reason: readNonEmptyString2(payload.reason),
14093
+ stopReason: readNonEmptyString2(payload.stopReason),
14094
+ cleanupReason: readNonEmptyString2(payload.cleanupReason),
14095
+ source: readNonEmptyString2(payload.source)
14096
+ };
14097
+ }
14014
14098
  function handleMeshForwardEvent(components, payload) {
14015
14099
  const eventName = readNonEmptyString2(payload.event);
14016
14100
  if (!isMeshCoordinatorEvent(eventName)) {
@@ -14036,70 +14120,12 @@ function handleMeshForwardEvent(components, payload) {
14036
14120
  event: eventName
14037
14121
  });
14038
14122
  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
14123
  return injectMeshSystemMessage(components, {
14042
14124
  meshId,
14043
14125
  nodeId,
14044
14126
  nodeLabel,
14045
14127
  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
- }
14128
+ metadataEvent: buildRelayMetadataEvent(payload)
14103
14129
  });
14104
14130
  }
14105
14131
  function forwardUnresolvedDelegateEvent(components, routing, event) {
@@ -19807,6 +19833,11 @@ function statusForState(state) {
19807
19833
  if (state.id === "busy" || state.id === "generating") return "generating";
19808
19834
  return "idle";
19809
19835
  }
19836
+ function modalKindForState(state) {
19837
+ if (state.modal_kind) return state.modal_kind;
19838
+ if (state.modal) return "approval";
19839
+ return null;
19840
+ }
19810
19841
  var init_fsm_types = __esm({
19811
19842
  "src/providers/spec/fsm-types.ts"() {
19812
19843
  "use strict";
@@ -33698,7 +33729,12 @@ var FsmDriver = class {
33698
33729
  this.emit({
33699
33730
  kind: "state_changed",
33700
33731
  state: next.state,
33701
- modal: next.modal ? { title: next.modal.title, buttons: next.modal.buttons.map((b) => ({ index: b.index, label: b.label })) } : null,
33732
+ // kind is the SEMANTIC modal class (approval vs picker/confirm)
33733
+ // derived from the FSM state, NOT from the parsed buttons — the
33734
+ // status field already collapsed it to 'approval' so the modal is
33735
+ // surfaced. The auto-approve worker needs the distinction back to
33736
+ // avoid answering a /model picker on the user's behalf.
33737
+ modal: next.modal ? { title: next.modal.title, buttons: next.modal.buttons.map((b) => ({ index: b.index, label: b.label })), kind: modalKindForState(state) } : null,
33702
33738
  controls: next.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
33703
33739
  });
33704
33740
  this.fireNotifications(state.id, title);
@@ -35118,7 +35154,9 @@ var SpecCliAdapter = class _SpecCliAdapter {
35118
35154
  messages: [],
35119
35155
  // Surface buttons when we have them; an approval state with no parsed
35120
35156
  // 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,
35157
+ // `kind` carries the semantic modal class through to the auto-approve
35158
+ // gate so a /model picker (kind='picker') is never auto-answered.
35159
+ activeModal: modal ? { message: modal.title ?? state.label, buttons: modal.buttons.map((b) => b.label), kind: modal.kind ?? null } : null,
35122
35160
  activeInteractivePrompt: this.activeInteractivePrompt,
35123
35161
  ...sessionFields
35124
35162
  };
@@ -37085,8 +37123,12 @@ var CliProviderInstance = class _CliProviderInstance {
37085
37123
  if (!modal || buttons.length === 0) {
37086
37124
  return autoApproveActive;
37087
37125
  }
37088
- const { index: buttonIndex, label: buttonLabel } = pickAutoApprovalButton(buttons);
37089
- if (buttonIndex < 0) {
37126
+ const modalKind = typeof modal?.kind === "string" ? modal.kind : "approval";
37127
+ if (modalKind !== "approval") {
37128
+ return autoApproveActive;
37129
+ }
37130
+ const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
37131
+ if (buttonIndex < 0 || !hasNegativeApprovalOption(buttons)) {
37090
37132
  return autoApproveActive;
37091
37133
  }
37092
37134
  const modalSignature = [