@adhdev/daemon-core 0.9.82-rc.169 → 0.9.82-rc.170

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/index.mjs CHANGED
@@ -677,7 +677,7 @@ var init_config = __esm({
677
677
  function readObject(value) {
678
678
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
679
679
  }
680
- function readString(value) {
680
+ function readString2(value) {
681
681
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
682
682
  }
683
683
  function normalizeMeshDaemonRole(value) {
@@ -694,9 +694,9 @@ function resolveMeshHostStatus(mesh) {
694
694
  canOwnQueue: role === "host",
695
695
  defaulted: !raw
696
696
  };
697
- const hostDaemonId = readString(raw?.hostDaemonId);
698
- const hostNodeId = readString(raw?.hostNodeId);
699
- const hostAddress = readString(raw?.hostAddress);
697
+ const hostDaemonId = readString2(raw?.hostDaemonId);
698
+ const hostNodeId = readString2(raw?.hostNodeId);
699
+ const hostAddress = readString2(raw?.hostAddress);
700
700
  if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
701
701
  if (hostNodeId) normalized.hostNodeId = hostNodeId;
702
702
  if (hostAddress) normalized.hostAddress = hostAddress;
@@ -704,11 +704,11 @@ function resolveMeshHostStatus(mesh) {
704
704
  const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
705
705
  normalized.pairing = {
706
706
  status,
707
- ...readString(pairing.tokenId) ? { tokenId: readString(pairing.tokenId) } : {},
708
- ...readString(pairing.joinedAt) ? { joinedAt: readString(pairing.joinedAt) } : {},
709
- ...readString(pairing.lastPairedAt) ? { lastPairedAt: readString(pairing.lastPairedAt) } : {},
710
- ...readString(pairing.lastRejectedAt) ? { lastRejectedAt: readString(pairing.lastRejectedAt) } : {},
711
- ...readString(pairing.expiresAt) ? { expiresAt: readString(pairing.expiresAt) } : {}
707
+ ...readString2(pairing.tokenId) ? { tokenId: readString2(pairing.tokenId) } : {},
708
+ ...readString2(pairing.joinedAt) ? { joinedAt: readString2(pairing.joinedAt) } : {},
709
+ ...readString2(pairing.lastPairedAt) ? { lastPairedAt: readString2(pairing.lastPairedAt) } : {},
710
+ ...readString2(pairing.lastRejectedAt) ? { lastRejectedAt: readString2(pairing.lastRejectedAt) } : {},
711
+ ...readString2(pairing.expiresAt) ? { expiresAt: readString2(pairing.expiresAt) } : {}
712
712
  };
713
713
  }
714
714
  return normalized;
@@ -2114,7 +2114,7 @@ function compactLedger(meshId) {
2114
2114
  function readNonEmptyString(value) {
2115
2115
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2116
2116
  }
2117
- function readStringArray(value) {
2117
+ function readStringArray2(value) {
2118
2118
  if (!Array.isArray(value)) return [];
2119
2119
  return value.map((item) => readNonEmptyString(item)).filter(Boolean);
2120
2120
  }
@@ -2174,11 +2174,11 @@ function normalizeMeshWorkerResult(input, source = "explicit_metadata") {
2174
2174
  return {
2175
2175
  status,
2176
2176
  ...readNonEmptyString(raw.classification) ? { classification: readNonEmptyString(raw.classification) } : {},
2177
- changedFiles: readStringArray(raw.changedFiles),
2177
+ changedFiles: readStringArray2(raw.changedFiles),
2178
2178
  validationResults: normalizeValidationResults(raw.validationResults),
2179
2179
  ...gitStatus ? { gitStatus } : {},
2180
2180
  processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
2181
- errors: readStringArray(raw.errors),
2181
+ errors: readStringArray2(raw.errors),
2182
2182
  ...readNonEmptyString(raw.nextAction) ? { nextAction: readNonEmptyString(raw.nextAction) } : {},
2183
2183
  requiresUserAction: raw.requiresUserAction === true,
2184
2184
  source
@@ -3660,6 +3660,21 @@ function isDuplicateMeshCompletionEvent(args) {
3660
3660
  recordFingerprintSeen(fingerprint);
3661
3661
  return false;
3662
3662
  }
3663
+ function isDuplicateMeshApprovalEvent(args) {
3664
+ const modalButtons = Array.isArray(args.modalButtons) ? args.modalButtons.map((button) => String(button).trim()).filter(Boolean) : [];
3665
+ const approvalIdentity = Number.isFinite(args.timestamp) ? String(args.timestamp) : JSON.stringify({ message: args.modalMessage || "", buttons: modalButtons });
3666
+ if (!approvalIdentity || approvalIdentity === '{"message":"","buttons":[]}') return false;
3667
+ const fingerprint = [
3668
+ args.meshId,
3669
+ "agent:waiting_approval",
3670
+ args.sessionId,
3671
+ args.providerType || "",
3672
+ approvalIdentity
3673
+ ].join("::");
3674
+ if (hasFingerprintSeen(fingerprint)) return true;
3675
+ recordFingerprintSeen(fingerprint);
3676
+ return false;
3677
+ }
3663
3678
  function isDuplicateRefineTerminalEvent(meshId, eventName, metadataEvent) {
3664
3679
  const fingerprint = buildRefineTerminalEventFingerprint(meshId, eventName, metadataEvent);
3665
3680
  if (!fingerprint) return false;
@@ -4220,6 +4235,20 @@ function injectMeshSystemMessage(components, args) {
4220
4235
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
4221
4236
  }
4222
4237
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
4238
+ if (args.event === "agent:waiting_approval" && eventSessionId) {
4239
+ const duplicateApproval = isDuplicateMeshApprovalEvent({
4240
+ meshId: args.meshId,
4241
+ sessionId: eventSessionId,
4242
+ providerType: readNonEmptyString2(args.metadataEvent.providerType) || void 0,
4243
+ timestamp: eventTimestamp,
4244
+ modalMessage: readNonEmptyString2(args.metadataEvent.modalMessage) || void 0,
4245
+ modalButtons: args.metadataEvent.modalButtons
4246
+ });
4247
+ if (duplicateApproval) {
4248
+ LOG.info("MeshEvents", `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
4249
+ return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
4250
+ }
4251
+ }
4223
4252
  if (args.event === "agent:generating_completed" && eventSessionId) {
4224
4253
  const terminal = findRecentTerminalLedgerEvidence({
4225
4254
  meshId: args.meshId,
@@ -8789,6 +8818,8 @@ ${lastSnapshot}`;
8789
8818
  }
8790
8819
  await this.sendMessage(promptText);
8791
8820
  }
8821
+ async setInteractivePromptResponse(_response) {
8822
+ }
8792
8823
  isSubmitStuck(normalizedPromptSnippet) {
8793
8824
  if (!this.ptyProcess || !this.engine.isWaitingForResponse || this.engine.submitRetryUsed) return false;
8794
8825
  if (this.engine.hasActionableApproval()) return false;
@@ -12018,6 +12049,226 @@ var init_dispatcher = __esm({
12018
12049
  }
12019
12050
  });
12020
12051
 
12052
+ // src/providers/types/interactive-prompt.ts
12053
+ function readString(value) {
12054
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
12055
+ }
12056
+ function readStringArray(value) {
12057
+ return Array.isArray(value) ? value.map((item) => readString(item)).filter((item) => !!item) : [];
12058
+ }
12059
+ function normalizeOption(raw) {
12060
+ if (typeof raw === "string") {
12061
+ const label2 = raw.trim();
12062
+ return label2 ? { label: label2 } : null;
12063
+ }
12064
+ if (!raw || typeof raw !== "object") return null;
12065
+ const record = raw;
12066
+ const label = readString(record.label);
12067
+ if (!label) return null;
12068
+ const description = readString(record.description);
12069
+ const preview = readString(record.preview);
12070
+ return {
12071
+ label,
12072
+ ...description ? { description } : {},
12073
+ ...preview ? { preview } : {}
12074
+ };
12075
+ }
12076
+ function normalizeQuestion(raw, index) {
12077
+ if (!raw || typeof raw !== "object") return null;
12078
+ const record = raw;
12079
+ const question = readString(record.question);
12080
+ if (!question) return null;
12081
+ const questionId = readString(record.questionId) || readString(record.id) || `q${index + 1}`;
12082
+ const options = Array.isArray(record.options) ? record.options.map(normalizeOption).filter((item) => !!item) : [];
12083
+ const header = readString(record.header);
12084
+ return {
12085
+ questionId,
12086
+ question,
12087
+ ...header ? { header } : {},
12088
+ multiSelect: record.multiSelect === true,
12089
+ options,
12090
+ ...record.allowFreeform === true ? { allowFreeform: true } : {}
12091
+ };
12092
+ }
12093
+ function normalizeInteractivePrompt(raw) {
12094
+ if (!raw || typeof raw !== "object") return null;
12095
+ const record = raw;
12096
+ const promptId = readString(record.promptId);
12097
+ const providerType = readString(record.providerType);
12098
+ const origin = record.origin === "mcp" || record.origin === "agent" ? record.origin : "cli";
12099
+ const questions = Array.isArray(record.questions) ? record.questions.map(normalizeQuestion).filter((item) => !!item) : [];
12100
+ if (!promptId || !providerType || questions.length === 0) return null;
12101
+ const createdAt = typeof record.createdAt === "number" && Number.isFinite(record.createdAt) ? record.createdAt : Date.now();
12102
+ return { promptId, origin, providerType, createdAt, questions };
12103
+ }
12104
+ function normalizeInteractivePromptResponse(raw) {
12105
+ if (!raw || typeof raw !== "object") throw new Error("Interactive prompt response must be an object");
12106
+ const record = raw;
12107
+ const promptId = readString(record.promptId);
12108
+ if (!promptId) throw new Error("promptId must be a non-empty string");
12109
+ if (!record.answers || typeof record.answers !== "object" || Array.isArray(record.answers)) {
12110
+ throw new Error("answers must be an object");
12111
+ }
12112
+ const answers = {};
12113
+ for (const [questionId, answerRaw] of Object.entries(record.answers)) {
12114
+ if (!answerRaw || typeof answerRaw !== "object" || Array.isArray(answerRaw)) continue;
12115
+ const answer = answerRaw;
12116
+ const selectedLabels = readStringArray(answer.selectedLabels);
12117
+ const freeformText = readString(answer.freeformText);
12118
+ answers[questionId] = {
12119
+ selectedLabels,
12120
+ ...freeformText ? { freeformText } : {}
12121
+ };
12122
+ }
12123
+ return { promptId, answers };
12124
+ }
12125
+ function buildClaudeInteractiveToolResult(response) {
12126
+ return JSON.stringify({
12127
+ type: "user",
12128
+ message: {
12129
+ role: "user",
12130
+ content: [{
12131
+ type: "tool_result",
12132
+ tool_use_id: response.promptId,
12133
+ content: JSON.stringify({ answers: response.answers }),
12134
+ is_error: false
12135
+ }]
12136
+ }
12137
+ });
12138
+ }
12139
+ function claudeTuiQuestionHeaders(screenText) {
12140
+ const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
12141
+ if (!navLine) return [];
12142
+ const headers = [];
12143
+ const pattern = /[☐☒]\s+(.+?)(?=\s+[☐☒]|\s+✔\s+Submit)/g;
12144
+ for (const match of navLine.matchAll(pattern)) {
12145
+ const header = readString(match[1]);
12146
+ if (header) headers.push(header);
12147
+ }
12148
+ return headers;
12149
+ }
12150
+ function parseClaudeInteractiveTuiQuestion(page, index) {
12151
+ const lines = page.screenText.split(/\r?\n/);
12152
+ let navIndex = -1;
12153
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
12154
+ if (lines[i].includes("\u2714 Submit") && /[☐☒]/.test(lines[i])) {
12155
+ navIndex = i;
12156
+ break;
12157
+ }
12158
+ }
12159
+ if (navIndex < 0 || !page.screenText.includes("Enter to select")) return null;
12160
+ let question = "";
12161
+ let questionLineIndex = -1;
12162
+ for (let i = navIndex + 1; i < lines.length; i += 1) {
12163
+ const candidate = lines[i].trim();
12164
+ if (!candidate || /^─+$/.test(candidate)) continue;
12165
+ if (candidate === "Review your answers" || candidate === "Ready to submit your answers?") return null;
12166
+ question = candidate;
12167
+ questionLineIndex = i;
12168
+ break;
12169
+ }
12170
+ if (!question) return null;
12171
+ const options = [];
12172
+ let allowFreeform = false;
12173
+ const optionPattern = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
12174
+ for (let i = questionLineIndex + 1; i < lines.length; i += 1) {
12175
+ const match = lines[i].match(optionPattern);
12176
+ if (!match) continue;
12177
+ const label = match[2].trim();
12178
+ if (/^Type something\.?$/i.test(label)) {
12179
+ allowFreeform = true;
12180
+ continue;
12181
+ }
12182
+ if (/^Chat about this$/i.test(label)) continue;
12183
+ let description;
12184
+ const nextLine = lines[i + 1]?.trim();
12185
+ if (nextLine && !optionPattern.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
12186
+ description = nextLine;
12187
+ }
12188
+ options.push({ label, ...description ? { description } : {} });
12189
+ }
12190
+ if (options.length === 0) return null;
12191
+ const header = readString(page.header);
12192
+ return {
12193
+ questionId: `q${index + 1}`,
12194
+ question,
12195
+ ...header ? { header } : {},
12196
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
12197
+ options,
12198
+ ...allowFreeform ? { allowFreeform: true } : {}
12199
+ };
12200
+ }
12201
+ function detectClaudeAskUserQuestionPromptFromTuiPages(pages, options) {
12202
+ if (pages.length === 0) return null;
12203
+ const headers = claudeTuiQuestionHeaders(pages[0].screenText);
12204
+ const questions = pages.map((page, index) => parseClaudeInteractiveTuiQuestion({
12205
+ ...page,
12206
+ header: page.header || headers[index]
12207
+ }, index)).filter((question) => !!question);
12208
+ if (questions.length !== pages.length) return null;
12209
+ return {
12210
+ promptId: options.promptId,
12211
+ origin: "cli",
12212
+ providerType: options.providerType || "claude-cli",
12213
+ createdAt: options.createdAt || Date.now(),
12214
+ questions
12215
+ };
12216
+ }
12217
+ function buildClaudeInteractiveTuiAnswerSteps(prompt, response) {
12218
+ if (response.promptId !== prompt.promptId) throw new Error("Interactive prompt response does not match active prompt");
12219
+ const steps = [];
12220
+ for (const question of prompt.questions) {
12221
+ if (question.multiSelect) throw new Error("Claude TUI multi-select prompts are not supported yet");
12222
+ const answer = response.answers[question.questionId];
12223
+ if (!answer) throw new Error(`Missing answer for ${question.questionId}`);
12224
+ if (answer.freeformText) throw new Error("Claude TUI freeform answers are not supported yet");
12225
+ if (answer.selectedLabels.length !== 1) throw new Error(`Expected one selected label for ${question.questionId}`);
12226
+ const selectedIndex = question.options.findIndex((option) => option.label === answer.selectedLabels[0]);
12227
+ if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${answer.selectedLabels[0]}`);
12228
+ steps.push(`${"\x1B[B".repeat(selectedIndex)}\r`);
12229
+ }
12230
+ steps.push("\r");
12231
+ return steps;
12232
+ }
12233
+ function interactivePromptFromClaudeAskUserQuestion(input, options) {
12234
+ if (!input || typeof input !== "object") return null;
12235
+ const record = input;
12236
+ const questions = Array.isArray(record.questions) ? record.questions.map(normalizeQuestion).filter((item) => !!item) : [];
12237
+ if (questions.length === 0) return null;
12238
+ return {
12239
+ promptId: options.promptId,
12240
+ origin: options.origin || "cli",
12241
+ providerType: options.providerType,
12242
+ createdAt: options.createdAt || Date.now(),
12243
+ questions
12244
+ };
12245
+ }
12246
+ function detectClaudeAskUserQuestionPromptFromJson(value, providerType = "claude-cli") {
12247
+ if (!value || typeof value !== "object") return null;
12248
+ const record = value;
12249
+ const blocks = [];
12250
+ if (Array.isArray(record.content)) blocks.push(...record.content);
12251
+ const message = record.message;
12252
+ if (message && typeof message === "object" && Array.isArray(message.content)) {
12253
+ blocks.push(...message.content);
12254
+ }
12255
+ if (record.type === "tool_use") blocks.push(record);
12256
+ for (const block2 of blocks) {
12257
+ if (!block2 || typeof block2 !== "object") continue;
12258
+ const b = block2;
12259
+ const name = readString(b.name);
12260
+ if (b.type !== "tool_use" || name !== "AskUserQuestion") continue;
12261
+ const id = readString(b.id) || readString(record.id) || `ask-user-${Date.now()}`;
12262
+ const prompt = interactivePromptFromClaudeAskUserQuestion(b.input, {
12263
+ promptId: id,
12264
+ providerType,
12265
+ origin: "cli"
12266
+ });
12267
+ if (prompt) return prompt;
12268
+ }
12269
+ return null;
12270
+ }
12271
+
12021
12272
  // src/index.ts
12022
12273
  init_repo_mesh_types();
12023
12274
 
@@ -14765,7 +15016,7 @@ init_mesh_work_queue();
14765
15016
  // src/mesh/mesh-active-work.ts
14766
15017
  var DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
14767
15018
  var TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
14768
- function readString2(value) {
15019
+ function readString3(value) {
14769
15020
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
14770
15021
  }
14771
15022
  function summarizeMessage(message) {
@@ -14780,7 +15031,7 @@ function elapsedSince(value, now) {
14780
15031
  function sessionStatusFromNodes(nodes, nodeId, sessionId) {
14781
15032
  if (!Array.isArray(nodes)) return {};
14782
15033
  if (!nodeId) return { staleReason: "direct task has no node id" };
14783
- const node = nodes.find((item) => readString2(item?.id) === nodeId || readString2(item?.nodeId) === nodeId || readString2(item?.node_id) === nodeId);
15034
+ const node = nodes.find((item) => readString3(item?.id) === nodeId || readString3(item?.nodeId) === nodeId || readString3(item?.node_id) === nodeId);
14784
15035
  if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
14785
15036
  if (!sessionId) return {};
14786
15037
  const candidates = [];
@@ -14804,12 +15055,12 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
14804
15055
  }
14805
15056
  const session = candidates.find((item) => {
14806
15057
  if (typeof item === "string") return item === sessionId;
14807
- const id = readString2(item?.id) || readString2(item?.sessionId) || readString2(item?.session_id) || readString2(item?.runtimeSessionId) || readString2(item?.instanceId);
15058
+ const id = readString3(item?.id) || readString3(item?.sessionId) || readString3(item?.session_id) || readString3(item?.runtimeSessionId) || readString3(item?.instanceId);
14808
15059
  return id === sessionId;
14809
15060
  });
14810
15061
  if (!session) return { staleReason: "direct task session is not present in live session records" };
14811
15062
  if (typeof session === "string") return {};
14812
- const raw = `${readString2(session.status) || ""} ${readString2(session.lifecycle) || ""} ${readString2(session.state) || ""} ${readString2(session.activeChat?.status) || ""}`.toLowerCase();
15063
+ const raw = `${readString3(session.status) || ""} ${readString3(session.lifecycle) || ""} ${readString3(session.state) || ""} ${readString3(session.activeChat?.status) || ""}`.toLowerCase();
14813
15064
  if (raw.includes("approval")) return { status: "awaiting_approval" };
14814
15065
  if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
14815
15066
  if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
@@ -14820,14 +15071,14 @@ function isDirectDispatch(entry) {
14820
15071
  if (entry.kind !== "task_dispatched") return false;
14821
15072
  const payload = entry.payload || {};
14822
15073
  if (payload.source === "direct") return true;
14823
- const via = readString2(payload.via);
15074
+ const via = readString3(payload.via);
14824
15075
  return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
14825
15076
  }
14826
15077
  function directDispatchTaskId(entry) {
14827
- return readString2(entry.payload?.taskId) || entry.id;
15078
+ return readString3(entry.payload?.taskId) || entry.id;
14828
15079
  }
14829
15080
  function terminalMatchesDispatch(terminal, dispatch, taskId) {
14830
- const terminalTaskId = readString2(terminal.payload?.taskId);
15081
+ const terminalTaskId = readString3(terminal.payload?.taskId);
14831
15082
  if (terminalTaskId && terminalTaskId === taskId) return true;
14832
15083
  if (terminalTaskId && terminalTaskId !== taskId) return false;
14833
15084
  if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
@@ -14950,7 +15201,7 @@ function buildMeshActiveWork(opts) {
14950
15201
  const isNoTransition = !terminalStatus && !live.status;
14951
15202
  const isIdleUnacknowledged = status === "idle";
14952
15203
  const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
14953
- const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
15204
+ const message = readString3(dispatch.payload?.message) || readString3(dispatch.payload?.summary) || "";
14954
15205
  const { title, summary: summary2 } = summarizeMessage(message);
14955
15206
  const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
14956
15207
  const record = {
@@ -14959,11 +15210,11 @@ function buildMeshActiveWork(opts) {
14959
15210
  status,
14960
15211
  nodeId: dispatch.nodeId,
14961
15212
  sessionId: dispatch.sessionId,
14962
- providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
14963
- taskTitle: readString2(dispatch.payload?.taskTitle) || title,
14964
- taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
15213
+ providerType: dispatch.providerType || readString3(dispatch.payload?.providerType),
15214
+ taskTitle: readString3(dispatch.payload?.taskTitle) || title,
15215
+ taskSummary: readString3(dispatch.payload?.taskSummary) || summary2,
14965
15216
  message,
14966
- taskMode: readString2(dispatch.payload?.taskMode),
15217
+ taskMode: readString3(dispatch.payload?.taskMode),
14967
15218
  createdAt: dispatch.timestamp,
14968
15219
  updatedAt: terminal?.timestamp || dispatch.timestamp,
14969
15220
  dispatchedAt: dispatch.timestamp,
@@ -14998,7 +15249,7 @@ function buildMeshActiveWork(opts) {
14998
15249
  const isNoTransition = !terminalStatus && !live.status;
14999
15250
  const isIdleUnacknowledged = status === "idle";
15000
15251
  const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
15001
- const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
15252
+ const message = readString3(dispatch.payload?.message) || readString3(dispatch.payload?.summary) || "";
15002
15253
  const { title, summary: summary2 } = summarizeMessage(message);
15003
15254
  const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
15004
15255
  const record = {
@@ -15007,11 +15258,11 @@ function buildMeshActiveWork(opts) {
15007
15258
  status,
15008
15259
  nodeId: dispatch.nodeId,
15009
15260
  sessionId: dispatch.sessionId,
15010
- providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
15011
- taskTitle: readString2(dispatch.payload?.taskTitle) || title,
15012
- taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
15261
+ providerType: dispatch.providerType || readString3(dispatch.payload?.providerType),
15262
+ taskTitle: readString3(dispatch.payload?.taskTitle) || title,
15263
+ taskSummary: readString3(dispatch.payload?.taskSummary) || summary2,
15013
15264
  message,
15014
- taskMode: readString2(dispatch.payload?.taskMode),
15265
+ taskMode: readString3(dispatch.payload?.taskMode),
15015
15266
  createdAt: dispatch.timestamp,
15016
15267
  updatedAt: terminal?.timestamp || dispatch.timestamp,
15017
15268
  dispatchedAt: dispatch.timestamp,
@@ -15074,7 +15325,7 @@ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
15074
15325
  }
15075
15326
 
15076
15327
  // src/mesh/mesh-refine-status.ts
15077
- function readString3(value) {
15328
+ function readString4(value) {
15078
15329
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
15079
15330
  }
15080
15331
  function readRecord(value) {
@@ -15100,7 +15351,7 @@ function instructionForStatus(status) {
15100
15351
  return "Refine job failed; inspect result/finalBranchConvergenceState in mesh_task_history, fix the blocker, then rerun mesh_refine_node when ready.";
15101
15352
  }
15102
15353
  function mergeJob(jobs, patch) {
15103
- const jobId = readString3(patch.jobId);
15354
+ const jobId = readString4(patch.jobId);
15104
15355
  if (!jobId) return;
15105
15356
  const previous = jobs.get(jobId);
15106
15357
  const status = patch.status || previous?.status || "running";
@@ -15123,23 +15374,23 @@ function buildMeshAsyncRefineJobs(args) {
15123
15374
  const refineJob = readRecord(payload.refineJob);
15124
15375
  const result = readRecord(payload.result);
15125
15376
  const finalState = readRecord(payload.finalBranchConvergenceState) || readRecord(result?.finalBranchConvergenceState);
15126
- const jobId = readString3(refineJob?.jobId);
15377
+ const jobId = readString4(refineJob?.jobId);
15127
15378
  if (!jobId) continue;
15128
- const status = ledgerStatus(entry.kind, readString3(refineJob?.status));
15379
+ const status = ledgerStatus(entry.kind, readString4(refineJob?.status));
15129
15380
  mergeJob(jobs, {
15130
15381
  jobId,
15131
- interactionId: readString3(refineJob?.interactionId),
15382
+ interactionId: readString4(refineJob?.interactionId),
15132
15383
  status,
15133
- meshId: readString3(refineJob?.meshId) || args.meshId,
15134
- nodeId: readString3(refineJob?.nodeId) || entry.nodeId,
15135
- targetNodeId: readString3(refineJob?.nodeId) || entry.nodeId,
15136
- targetDaemonId: readString3(refineJob?.targetDaemonId),
15137
- workspace: readString3(refineJob?.workspace),
15138
- branch: readString3(result?.branch) || readString3(finalState?.branch),
15139
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
15140
- startedAt: readString3(refineJob?.startedAt),
15141
- completedAt: readString3(refineJob?.completedAt),
15142
- retryOfJobId: readString3(refineJob?.retryOfJobId) || readString3(payload.retryOfJobId),
15384
+ meshId: readString4(refineJob?.meshId) || args.meshId,
15385
+ nodeId: readString4(refineJob?.nodeId) || entry.nodeId,
15386
+ targetNodeId: readString4(refineJob?.nodeId) || entry.nodeId,
15387
+ targetDaemonId: readString4(refineJob?.targetDaemonId),
15388
+ workspace: readString4(refineJob?.workspace),
15389
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
15390
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
15391
+ startedAt: readString4(refineJob?.startedAt),
15392
+ completedAt: readString4(refineJob?.completedAt),
15393
+ retryOfJobId: readString4(refineJob?.retryOfJobId) || readString4(payload.retryOfJobId),
15143
15394
  lastLedgerKind: entry.kind,
15144
15395
  lastUpdatedAt: entry.timestamp
15145
15396
  });
@@ -15149,23 +15400,23 @@ function buildMeshAsyncRefineJobs(args) {
15149
15400
  if (metadata?.source !== "refine_mesh_node_async_job") continue;
15150
15401
  const result = readRecord(metadata.result);
15151
15402
  const finalState = readRecord(result?.finalBranchConvergenceState);
15152
- const jobId = readString3(metadata.jobId);
15403
+ const jobId = readString4(metadata.jobId);
15153
15404
  if (!jobId) continue;
15154
- const status = eventStatus(event.event, readString3(metadata.status));
15405
+ const status = eventStatus(event.event, readString4(metadata.status));
15155
15406
  mergeJob(jobs, {
15156
15407
  jobId,
15157
- interactionId: readString3(metadata.interactionId),
15408
+ interactionId: readString4(metadata.interactionId),
15158
15409
  ...status ? { status } : {},
15159
- meshId: readString3(metadata.meshId) || event.meshId || args.meshId,
15160
- nodeId: readString3(metadata.nodeId) || event.nodeId,
15161
- targetNodeId: readString3(metadata.nodeId) || event.nodeId,
15162
- targetDaemonId: readString3(metadata.targetDaemonId),
15163
- workspace: readString3(metadata.workspace) || event.workspace,
15164
- branch: readString3(result?.branch) || readString3(finalState?.branch),
15165
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
15166
- startedAt: readString3(metadata.startedAt),
15167
- completedAt: readString3(metadata.completedAt),
15168
- retryOfJobId: readString3(metadata.retryOfJobId),
15410
+ meshId: readString4(metadata.meshId) || event.meshId || args.meshId,
15411
+ nodeId: readString4(metadata.nodeId) || event.nodeId,
15412
+ targetNodeId: readString4(metadata.nodeId) || event.nodeId,
15413
+ targetDaemonId: readString4(metadata.targetDaemonId),
15414
+ workspace: readString4(metadata.workspace) || event.workspace,
15415
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
15416
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
15417
+ startedAt: readString4(metadata.startedAt),
15418
+ completedAt: readString4(metadata.completedAt),
15419
+ retryOfJobId: readString4(metadata.retryOfJobId),
15169
15420
  lastEvent: event.event,
15170
15421
  lastUpdatedAt: new Date(event.queuedAt).toISOString()
15171
15422
  });
@@ -21342,6 +21593,7 @@ function buildCliSession(state, options) {
21342
21593
  mode: state.mode,
21343
21594
  resume: state.resume,
21344
21595
  activeChat,
21596
+ ...state.activeInteractivePrompt ? { activeInteractivePrompt: state.activeInteractivePrompt } : {},
21345
21597
  ...summaryMetadata && { summaryMetadata },
21346
21598
  ...includeSessionMetadata && {
21347
21599
  capabilities: state.mode === "terminal" ? PTY_SESSION_CAPABILITIES : CLI_CHAT_SESSION_CAPABILITIES,
@@ -27094,6 +27346,9 @@ var SpecDriver = class {
27094
27346
  return;
27095
27347
  }
27096
27348
  }
27349
+ snapshot() {
27350
+ return this.adapter.snapshot();
27351
+ }
27097
27352
  shutdown() {
27098
27353
  for (const t of this.delegateTimers.values()) clearTimeout(t);
27099
27354
  this.delegateTimers.clear();
@@ -27373,6 +27628,10 @@ var SpecCliAdapter = class {
27373
27628
  statusCallback = null;
27374
27629
  ptyDataCallback = null;
27375
27630
  partialResponse = "";
27631
+ activeInteractivePrompt = null;
27632
+ interactivePromptTransport = null;
27633
+ claudeTuiPromptCaptureInFlight = false;
27634
+ jsonLineTail = "";
27376
27635
  exited = false;
27377
27636
  spawned = false;
27378
27637
  providerSessionId;
@@ -27422,11 +27681,11 @@ var SpecCliAdapter = class {
27422
27681
  this.driver.dispatch({ kind: "send_message", text });
27423
27682
  }
27424
27683
  getStatus() {
27425
- if (this.exited) return { status: "stopped", messages: [], activeModal: null };
27426
- if (!this.spawned) return { status: "starting", messages: [], activeModal: null };
27684
+ if (this.exited) return { status: "stopped", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27685
+ if (!this.spawned) return { status: "starting", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27427
27686
  this.maybeRefreshNativeHistory();
27428
27687
  const state = this.latestState;
27429
- if (!state) return { status: "starting", messages: [], activeModal: null };
27688
+ if (!state) return { status: "starting", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27430
27689
  const modal = this.latestModal;
27431
27690
  const lc = state.id.toLowerCase();
27432
27691
  if (modal) {
@@ -27436,13 +27695,14 @@ var SpecCliAdapter = class {
27436
27695
  activeModal: {
27437
27696
  message: modal.title ?? state.label,
27438
27697
  buttons: modal.buttons.map((b) => b.label)
27439
- }
27698
+ },
27699
+ activeInteractivePrompt: this.activeInteractivePrompt
27440
27700
  };
27441
27701
  }
27442
27702
  if (/busy|generating|working|running|thinking/i.test(lc + " " + state.label)) {
27443
- return { status: "generating", messages: [], activeModal: null };
27703
+ return { status: "generating", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27444
27704
  }
27445
- return { status: "idle", messages: [], activeModal: null };
27705
+ return { status: "idle", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27446
27706
  }
27447
27707
  maybeRefreshNativeHistory() {
27448
27708
  }
@@ -27505,6 +27765,24 @@ var SpecCliAdapter = class {
27505
27765
  }
27506
27766
  this.resolveModal(target);
27507
27767
  }
27768
+ async setInteractivePromptResponse(response) {
27769
+ const prompt = this.activeInteractivePrompt;
27770
+ if (!prompt || prompt.promptId !== response.promptId) throw new Error("Interactive prompt response does not match active prompt");
27771
+ if (this.cliType !== "claude-cli") return;
27772
+ if (this.interactivePromptTransport === "tui") {
27773
+ const steps = buildClaudeInteractiveTuiAnswerSteps(prompt, response);
27774
+ for (const step of steps) {
27775
+ this.driver.dispatch({ kind: "pty_write", data: step });
27776
+ await new Promise((resolve23) => setTimeout(resolve23, 180));
27777
+ }
27778
+ } else {
27779
+ this.driver.dispatch({ kind: "pty_write", data: `${buildClaudeInteractiveToolResult(response)}
27780
+ ` });
27781
+ }
27782
+ this.activeInteractivePrompt = null;
27783
+ this.interactivePromptTransport = null;
27784
+ this.statusCallback?.();
27785
+ }
27508
27786
  isApprovalRecentlyResolved() {
27509
27787
  return false;
27510
27788
  }
@@ -27560,6 +27838,7 @@ var SpecCliAdapter = class {
27560
27838
  spec_id: this.spec.id,
27561
27839
  current_state: this.latestState,
27562
27840
  current_modal: this.latestModal,
27841
+ activeInteractivePrompt: this.activeInteractivePrompt,
27563
27842
  exited: this.exited
27564
27843
  };
27565
27844
  }
@@ -27589,9 +27868,12 @@ var SpecCliAdapter = class {
27589
27868
  if (ev.state.title) {
27590
27869
  LOG.debug("SpecAdapter", `[${this.cliType}] state.title=${JSON.stringify(ev.state.title)}`);
27591
27870
  }
27871
+ this.maybeCaptureClaudeTuiPrompt();
27592
27872
  this.statusCallback?.();
27593
27873
  return;
27594
27874
  case "pty_data":
27875
+ this.detectInteractivePromptFromPtyChunk(ev.chunk);
27876
+ this.maybeCaptureClaudeTuiPrompt();
27595
27877
  try {
27596
27878
  this.ptyDataCallback?.(ev.chunk);
27597
27879
  } catch {
@@ -27608,6 +27890,66 @@ var SpecCliAdapter = class {
27608
27890
  return;
27609
27891
  }
27610
27892
  }
27893
+ detectInteractivePromptFromPtyChunk(chunk) {
27894
+ if (this.cliType !== "claude-cli" || !chunk) return;
27895
+ this.jsonLineTail += chunk;
27896
+ if (this.jsonLineTail.length > 64 * 1024) this.jsonLineTail = this.jsonLineTail.slice(-64 * 1024);
27897
+ const lines = this.jsonLineTail.split(/\r?\n/);
27898
+ this.jsonLineTail = lines.pop() || "";
27899
+ for (const line of lines) {
27900
+ const trimmed = line.trim();
27901
+ if (!trimmed.startsWith("{") || !trimmed.includes("AskUserQuestion")) continue;
27902
+ try {
27903
+ const parsed = JSON.parse(trimmed);
27904
+ const prompt = detectClaudeAskUserQuestionPromptFromJson(parsed, this.cliType);
27905
+ if (!prompt) continue;
27906
+ this.activeInteractivePrompt = prompt;
27907
+ this.interactivePromptTransport = "stream-json";
27908
+ this.statusCallback?.();
27909
+ } catch {
27910
+ }
27911
+ }
27912
+ }
27913
+ maybeCaptureClaudeTuiPrompt() {
27914
+ if (this.cliType !== "claude-cli" || this.activeInteractivePrompt || this.claudeTuiPromptCaptureInFlight) return;
27915
+ const screenText = this.driver.snapshot();
27916
+ const headers = this.readClaudeTuiHeaders(screenText);
27917
+ if (headers.length === 0 || !screenText.includes("Enter to select")) return;
27918
+ this.claudeTuiPromptCaptureInFlight = true;
27919
+ void this.captureClaudeTuiPrompt(screenText, headers).finally(() => {
27920
+ this.claudeTuiPromptCaptureInFlight = false;
27921
+ });
27922
+ }
27923
+ readClaudeTuiHeaders(screenText) {
27924
+ const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
27925
+ if (!navLine) return [];
27926
+ const headers = [];
27927
+ for (const match of navLine.matchAll(/[☐☒]\s+(.+?)(?=\s+[☐☒]|\s+✔\s+Submit)/g)) {
27928
+ const header = match[1]?.trim();
27929
+ if (header) headers.push(header);
27930
+ }
27931
+ return headers;
27932
+ }
27933
+ async captureClaudeTuiPrompt(firstScreen, headers) {
27934
+ const pages = [{ screenText: firstScreen, header: headers[0] }];
27935
+ for (let index = 1; index < headers.length; index += 1) {
27936
+ this.driver.dispatch({ kind: "pty_write", data: " " });
27937
+ await new Promise((resolve23) => setTimeout(resolve23, 120));
27938
+ pages.push({ screenText: this.driver.snapshot(), header: headers[index] });
27939
+ }
27940
+ for (let index = headers.length - 1; index > 0; index -= 1) {
27941
+ this.driver.dispatch({ kind: "pty_write", data: "\x1B[Z" });
27942
+ await new Promise((resolve23) => setTimeout(resolve23, 80));
27943
+ }
27944
+ const prompt = detectClaudeAskUserQuestionPromptFromTuiPages(pages, {
27945
+ promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
27946
+ providerType: this.cliType
27947
+ });
27948
+ if (!prompt) return;
27949
+ this.activeInteractivePrompt = prompt;
27950
+ this.interactivePromptTransport = "tui";
27951
+ this.statusCallback?.();
27952
+ }
27611
27953
  };
27612
27954
 
27613
27955
  // src/providers/spec/route.ts
@@ -27876,7 +28218,7 @@ var CliProviderInstance = class {
27876
28218
  monitor;
27877
28219
  generatingDebounceTimer = null;
27878
28220
  generatingDebouncePending = null;
27879
- lastApprovalEventAt = 0;
28221
+ lastApprovalEventFingerprint = "";
27880
28222
  autoApproveBusy = false;
27881
28223
  autoApproveBusyTimer = null;
27882
28224
  lastAutoApprovalSignature = "";
@@ -27895,6 +28237,7 @@ var CliProviderInstance = class {
27895
28237
  suppressIdleHistoryReplay = false;
27896
28238
  errorMessage = void 0;
27897
28239
  errorReason = void 0;
28240
+ activeInteractivePrompt = null;
27898
28241
  presentationMode;
27899
28242
  providerSessionId;
27900
28243
  launchMode;
@@ -27987,6 +28330,9 @@ var CliProviderInstance = class {
27987
28330
  }
27988
28331
  getState() {
27989
28332
  const adapterStatus = this.adapter.getStatus();
28333
+ if (Object.prototype.hasOwnProperty.call(adapterStatus, "activeInteractivePrompt")) {
28334
+ this.activeInteractivePrompt = adapterStatus.activeInteractivePrompt ?? null;
28335
+ }
27990
28336
  let parsedStatus = null;
27991
28337
  let parseErrorMessage;
27992
28338
  if (typeof this.adapter.getScriptParsedStatus === "function") {
@@ -28103,8 +28449,10 @@ var CliProviderInstance = class {
28103
28449
  status: activeChatStatus,
28104
28450
  messages: statusMessages,
28105
28451
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
28452
+ activeInteractivePrompt: this.activeInteractivePrompt,
28106
28453
  inputContent: ""
28107
28454
  },
28455
+ activeInteractivePrompt: this.activeInteractivePrompt,
28108
28456
  workspace: this.workingDir,
28109
28457
  instanceId: this.instanceId,
28110
28458
  providerSessionId: this.providerSessionId,
@@ -28194,6 +28542,32 @@ var CliProviderInstance = class {
28194
28542
  void this.adapter.resolveAction(data).catch((e) => {
28195
28543
  LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
28196
28544
  });
28545
+ } else if (event === "interactive_prompt" && data) {
28546
+ const prompt = normalizeInteractivePrompt(data);
28547
+ if (prompt) {
28548
+ this.activeInteractivePrompt = prompt;
28549
+ this.events.push({
28550
+ event: "interactive_prompt",
28551
+ timestamp: Date.now(),
28552
+ promptId: prompt.promptId
28553
+ });
28554
+ }
28555
+ } else if (event === "interactive_prompt_response" && data) {
28556
+ try {
28557
+ const response = normalizeInteractivePromptResponse(data);
28558
+ if (this.activeInteractivePrompt?.promptId === response.promptId) {
28559
+ this.activeInteractivePrompt = null;
28560
+ }
28561
+ if (typeof this.adapter.setInteractivePromptResponse !== "function") {
28562
+ LOG.warn("CLI", `[${this.type}] interactive_prompt_response ignored: adapter does not support interactive prompts`);
28563
+ return;
28564
+ }
28565
+ void this.adapter.setInteractivePromptResponse(response).catch((e) => {
28566
+ LOG.warn("CLI", `[${this.type}] interactive_prompt_response failed: ${e?.message || e}`);
28567
+ });
28568
+ } catch (e) {
28569
+ LOG.warn("CLI", `[${this.type}] invalid interactive_prompt_response: ${e?.message || e}`);
28570
+ }
28197
28571
  } else if (event === "provider_state_patch" && data && typeof data === "object") {
28198
28572
  this.applyProviderResponse(data, { phase: "immediate" });
28199
28573
  }
@@ -28327,12 +28701,15 @@ var CliProviderInstance = class {
28327
28701
  if (typeof adapterAny?.responseBuffer === "string" && adapterAny.responseBuffer.trim()) return false;
28328
28702
  return true;
28329
28703
  }
28330
- getCompletedFinalizationBlock(latestVisibleStatus) {
28704
+ getCompletedFinalizationBlock(latestVisibleStatus, pending) {
28331
28705
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
28332
28706
  const adapterAny = this.adapter;
28333
- if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
28334
- if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
28335
- if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
28707
+ const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
28708
+ if (!approvalResolvedIdle) {
28709
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
28710
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
28711
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
28712
+ }
28336
28713
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
28337
28714
  if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28338
28715
  let parsed;
@@ -28357,7 +28734,7 @@ var CliProviderInstance = class {
28357
28734
  if (screenText) {
28358
28735
  const tailLines = screenText.split(/\r?\n/).slice(-16).join("\n");
28359
28736
  if (looksLikeActiveApprovalPromptText(tailLines)) {
28360
- return { reason: "screen_shows_approval_prompt", terminal: false };
28737
+ return { reason: "screen_shows_approval_prompt", terminal: approvalResolvedIdle };
28361
28738
  }
28362
28739
  }
28363
28740
  } catch {
@@ -28383,7 +28760,7 @@ var CliProviderInstance = class {
28383
28760
  this.completedDebounceTimer = null;
28384
28761
  return;
28385
28762
  }
28386
- const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus);
28763
+ const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending);
28387
28764
  if (block2) {
28388
28765
  const blockReason = block2.reason;
28389
28766
  const waitedMs = Date.now() - pending.firstObservedAt;
@@ -28415,6 +28792,7 @@ var CliProviderInstance = class {
28415
28792
  this.completedDebouncePending = null;
28416
28793
  this.completedDebounceTimer = null;
28417
28794
  this.generatingStartedAt = 0;
28795
+ this.lastApprovalEventFingerprint = "";
28418
28796
  return;
28419
28797
  }
28420
28798
  LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
@@ -28428,6 +28806,7 @@ var CliProviderInstance = class {
28428
28806
  this.completedDebouncePending = null;
28429
28807
  this.completedDebounceTimer = null;
28430
28808
  this.generatingStartedAt = 0;
28809
+ this.lastApprovalEventFingerprint = "";
28431
28810
  }
28432
28811
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
28433
28812
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
@@ -28524,9 +28903,12 @@ var CliProviderInstance = class {
28524
28903
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
28525
28904
  const modal = adapterStatus.activeModal;
28526
28905
  LOG.info("CLI", `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? "none"}"`);
28527
- const approvalCooldown = 5e3;
28528
- if (this.lastStatus !== "waiting_approval" && (!this.lastApprovalEventAt || now - this.lastApprovalEventAt > approvalCooldown)) {
28529
- this.lastApprovalEventAt = now;
28906
+ const approvalFingerprint = JSON.stringify({
28907
+ message: typeof modal?.message === "string" ? modal.message.trim() : "",
28908
+ buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button) => String(button).trim()) : []
28909
+ });
28910
+ if (this.lastStatus !== "waiting_approval" && approvalFingerprint !== this.lastApprovalEventFingerprint) {
28911
+ this.lastApprovalEventFingerprint = approvalFingerprint;
28530
28912
  this.appendRuntimeSystemMessage(
28531
28913
  this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
28532
28914
  `approval_request:${now}`,
@@ -28568,7 +28950,13 @@ var CliProviderInstance = class {
28568
28950
  }
28569
28951
  });
28570
28952
  } else {
28571
- this.completedDebouncePending = { chatTitle, duration, timestamp: now, firstObservedAt: now };
28953
+ this.completedDebouncePending = {
28954
+ chatTitle,
28955
+ duration,
28956
+ timestamp: now,
28957
+ firstObservedAt: now,
28958
+ previousStatus: this.lastStatus
28959
+ };
28572
28960
  this.scheduleCompletedDebounceFlush(3e3);
28573
28961
  }
28574
28962
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -48662,6 +49050,7 @@ export {
48662
49050
  buildChatMessage,
48663
49051
  buildChatMessageSignature,
48664
49052
  buildChatTailDeliverySignature,
49053
+ buildClaudeInteractiveToolResult,
48665
49054
  buildCompactStaleDirectWorkSummary,
48666
49055
  buildCoordinatorSystemPrompt,
48667
49056
  buildIpcStatusHttpResponse,
@@ -48708,6 +49097,7 @@ export {
48708
49097
  deleteMesh,
48709
49098
  detectAllVersions,
48710
49099
  detectCLIs,
49100
+ detectClaudeAskUserQuestionPromptFromJson,
48711
49101
  detectIDEs,
48712
49102
  drainPendingMeshCoordinatorEvents,
48713
49103
  enqueueTask,
@@ -48762,6 +49152,7 @@ export {
48762
49152
  insertDirectDispatch,
48763
49153
  installExtensions,
48764
49154
  installGlobalInterceptor,
49155
+ interactivePromptFromClaudeAskUserQuestion,
48765
49156
  isActivityChatMessage,
48766
49157
  isBuiltinChatMessageKind,
48767
49158
  isCdpConnected,
@@ -48805,6 +49196,8 @@ export {
48805
49196
  normalizeGitOutput,
48806
49197
  normalizeGitWorkspaceSubscriptionParams,
48807
49198
  normalizeInputEnvelope,
49199
+ normalizeInteractivePrompt,
49200
+ normalizeInteractivePromptResponse,
48808
49201
  normalizeManagedStatus,
48809
49202
  normalizeMeshDaemonRole,
48810
49203
  normalizeMeshTaskMode,