@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.js CHANGED
@@ -682,7 +682,7 @@ var init_config = __esm({
682
682
  function readObject(value) {
683
683
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
684
684
  }
685
- function readString(value) {
685
+ function readString2(value) {
686
686
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
687
687
  }
688
688
  function normalizeMeshDaemonRole(value) {
@@ -699,9 +699,9 @@ function resolveMeshHostStatus(mesh) {
699
699
  canOwnQueue: role === "host",
700
700
  defaulted: !raw
701
701
  };
702
- const hostDaemonId = readString(raw?.hostDaemonId);
703
- const hostNodeId = readString(raw?.hostNodeId);
704
- const hostAddress = readString(raw?.hostAddress);
702
+ const hostDaemonId = readString2(raw?.hostDaemonId);
703
+ const hostNodeId = readString2(raw?.hostNodeId);
704
+ const hostAddress = readString2(raw?.hostAddress);
705
705
  if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
706
706
  if (hostNodeId) normalized.hostNodeId = hostNodeId;
707
707
  if (hostAddress) normalized.hostAddress = hostAddress;
@@ -709,11 +709,11 @@ function resolveMeshHostStatus(mesh) {
709
709
  const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
710
710
  normalized.pairing = {
711
711
  status,
712
- ...readString(pairing.tokenId) ? { tokenId: readString(pairing.tokenId) } : {},
713
- ...readString(pairing.joinedAt) ? { joinedAt: readString(pairing.joinedAt) } : {},
714
- ...readString(pairing.lastPairedAt) ? { lastPairedAt: readString(pairing.lastPairedAt) } : {},
715
- ...readString(pairing.lastRejectedAt) ? { lastRejectedAt: readString(pairing.lastRejectedAt) } : {},
716
- ...readString(pairing.expiresAt) ? { expiresAt: readString(pairing.expiresAt) } : {}
712
+ ...readString2(pairing.tokenId) ? { tokenId: readString2(pairing.tokenId) } : {},
713
+ ...readString2(pairing.joinedAt) ? { joinedAt: readString2(pairing.joinedAt) } : {},
714
+ ...readString2(pairing.lastPairedAt) ? { lastPairedAt: readString2(pairing.lastPairedAt) } : {},
715
+ ...readString2(pairing.lastRejectedAt) ? { lastRejectedAt: readString2(pairing.lastRejectedAt) } : {},
716
+ ...readString2(pairing.expiresAt) ? { expiresAt: readString2(pairing.expiresAt) } : {}
717
717
  };
718
718
  }
719
719
  return normalized;
@@ -2115,7 +2115,7 @@ function compactLedger(meshId) {
2115
2115
  function readNonEmptyString(value) {
2116
2116
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2117
2117
  }
2118
- function readStringArray(value) {
2118
+ function readStringArray2(value) {
2119
2119
  if (!Array.isArray(value)) return [];
2120
2120
  return value.map((item) => readNonEmptyString(item)).filter(Boolean);
2121
2121
  }
@@ -2175,11 +2175,11 @@ function normalizeMeshWorkerResult(input, source = "explicit_metadata") {
2175
2175
  return {
2176
2176
  status,
2177
2177
  ...readNonEmptyString(raw.classification) ? { classification: readNonEmptyString(raw.classification) } : {},
2178
- changedFiles: readStringArray(raw.changedFiles),
2178
+ changedFiles: readStringArray2(raw.changedFiles),
2179
2179
  validationResults: normalizeValidationResults(raw.validationResults),
2180
2180
  ...gitStatus ? { gitStatus } : {},
2181
2181
  processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
2182
- errors: readStringArray(raw.errors),
2182
+ errors: readStringArray2(raw.errors),
2183
2183
  ...readNonEmptyString(raw.nextAction) ? { nextAction: readNonEmptyString(raw.nextAction) } : {},
2184
2184
  requiresUserAction: raw.requiresUserAction === true,
2185
2185
  source
@@ -3665,6 +3665,21 @@ function isDuplicateMeshCompletionEvent(args) {
3665
3665
  recordFingerprintSeen(fingerprint);
3666
3666
  return false;
3667
3667
  }
3668
+ function isDuplicateMeshApprovalEvent(args) {
3669
+ const modalButtons = Array.isArray(args.modalButtons) ? args.modalButtons.map((button) => String(button).trim()).filter(Boolean) : [];
3670
+ const approvalIdentity = Number.isFinite(args.timestamp) ? String(args.timestamp) : JSON.stringify({ message: args.modalMessage || "", buttons: modalButtons });
3671
+ if (!approvalIdentity || approvalIdentity === '{"message":"","buttons":[]}') return false;
3672
+ const fingerprint = [
3673
+ args.meshId,
3674
+ "agent:waiting_approval",
3675
+ args.sessionId,
3676
+ args.providerType || "",
3677
+ approvalIdentity
3678
+ ].join("::");
3679
+ if (hasFingerprintSeen(fingerprint)) return true;
3680
+ recordFingerprintSeen(fingerprint);
3681
+ return false;
3682
+ }
3668
3683
  function isDuplicateRefineTerminalEvent(meshId, eventName, metadataEvent) {
3669
3684
  const fingerprint = buildRefineTerminalEventFingerprint(meshId, eventName, metadataEvent);
3670
3685
  if (!fingerprint) return false;
@@ -4225,6 +4240,20 @@ function injectMeshSystemMessage(components, args) {
4225
4240
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
4226
4241
  }
4227
4242
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
4243
+ if (args.event === "agent:waiting_approval" && eventSessionId) {
4244
+ const duplicateApproval = isDuplicateMeshApprovalEvent({
4245
+ meshId: args.meshId,
4246
+ sessionId: eventSessionId,
4247
+ providerType: readNonEmptyString2(args.metadataEvent.providerType) || void 0,
4248
+ timestamp: eventTimestamp,
4249
+ modalMessage: readNonEmptyString2(args.metadataEvent.modalMessage) || void 0,
4250
+ modalButtons: args.metadataEvent.modalButtons
4251
+ });
4252
+ if (duplicateApproval) {
4253
+ LOG.info("MeshEvents", `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
4254
+ return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
4255
+ }
4256
+ }
4228
4257
  if (args.event === "agent:generating_completed" && eventSessionId) {
4229
4258
  const terminal = findRecentTerminalLedgerEvidence({
4230
4259
  meshId: args.meshId,
@@ -8794,6 +8823,8 @@ ${lastSnapshot}`;
8794
8823
  }
8795
8824
  await this.sendMessage(promptText);
8796
8825
  }
8826
+ async setInteractivePromptResponse(_response) {
8827
+ }
8797
8828
  isSubmitStuck(normalizedPromptSnippet) {
8798
8829
  if (!this.ptyProcess || !this.engine.isWaitingForResponse || this.engine.submitRetryUsed) return false;
8799
8830
  if (this.engine.hasActionableApproval()) return false;
@@ -12101,6 +12132,7 @@ __export(index_exports, {
12101
12132
  buildChatMessage: () => buildChatMessage,
12102
12133
  buildChatMessageSignature: () => buildChatMessageSignature,
12103
12134
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
12135
+ buildClaudeInteractiveToolResult: () => buildClaudeInteractiveToolResult,
12104
12136
  buildCompactStaleDirectWorkSummary: () => buildCompactStaleDirectWorkSummary,
12105
12137
  buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
12106
12138
  buildIpcStatusHttpResponse: () => buildIpcStatusHttpResponse,
@@ -12147,6 +12179,7 @@ __export(index_exports, {
12147
12179
  deleteMesh: () => deleteMesh,
12148
12180
  detectAllVersions: () => detectAllVersions,
12149
12181
  detectCLIs: () => detectCLIs,
12182
+ detectClaudeAskUserQuestionPromptFromJson: () => detectClaudeAskUserQuestionPromptFromJson,
12150
12183
  detectIDEs: () => detectIDEs,
12151
12184
  drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
12152
12185
  enqueueTask: () => enqueueTask,
@@ -12201,6 +12234,7 @@ __export(index_exports, {
12201
12234
  insertDirectDispatch: () => insertDirectDispatch,
12202
12235
  installExtensions: () => installExtensions,
12203
12236
  installGlobalInterceptor: () => installGlobalInterceptor,
12237
+ interactivePromptFromClaudeAskUserQuestion: () => interactivePromptFromClaudeAskUserQuestion,
12204
12238
  isActivityChatMessage: () => isActivityChatMessage,
12205
12239
  isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
12206
12240
  isCdpConnected: () => isCdpConnected,
@@ -12244,6 +12278,8 @@ __export(index_exports, {
12244
12278
  normalizeGitOutput: () => normalizeGitOutput,
12245
12279
  normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams,
12246
12280
  normalizeInputEnvelope: () => normalizeInputEnvelope,
12281
+ normalizeInteractivePrompt: () => normalizeInteractivePrompt,
12282
+ normalizeInteractivePromptResponse: () => normalizeInteractivePromptResponse,
12247
12283
  normalizeManagedStatus: () => normalizeManagedStatus,
12248
12284
  normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
12249
12285
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
@@ -12320,6 +12356,228 @@ __export(index_exports, {
12320
12356
  withRawTerminalAttachment: () => withRawTerminalAttachment
12321
12357
  });
12322
12358
  module.exports = __toCommonJS(index_exports);
12359
+
12360
+ // src/providers/types/interactive-prompt.ts
12361
+ function readString(value) {
12362
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
12363
+ }
12364
+ function readStringArray(value) {
12365
+ return Array.isArray(value) ? value.map((item) => readString(item)).filter((item) => !!item) : [];
12366
+ }
12367
+ function normalizeOption(raw) {
12368
+ if (typeof raw === "string") {
12369
+ const label2 = raw.trim();
12370
+ return label2 ? { label: label2 } : null;
12371
+ }
12372
+ if (!raw || typeof raw !== "object") return null;
12373
+ const record = raw;
12374
+ const label = readString(record.label);
12375
+ if (!label) return null;
12376
+ const description = readString(record.description);
12377
+ const preview = readString(record.preview);
12378
+ return {
12379
+ label,
12380
+ ...description ? { description } : {},
12381
+ ...preview ? { preview } : {}
12382
+ };
12383
+ }
12384
+ function normalizeQuestion(raw, index) {
12385
+ if (!raw || typeof raw !== "object") return null;
12386
+ const record = raw;
12387
+ const question = readString(record.question);
12388
+ if (!question) return null;
12389
+ const questionId = readString(record.questionId) || readString(record.id) || `q${index + 1}`;
12390
+ const options = Array.isArray(record.options) ? record.options.map(normalizeOption).filter((item) => !!item) : [];
12391
+ const header = readString(record.header);
12392
+ return {
12393
+ questionId,
12394
+ question,
12395
+ ...header ? { header } : {},
12396
+ multiSelect: record.multiSelect === true,
12397
+ options,
12398
+ ...record.allowFreeform === true ? { allowFreeform: true } : {}
12399
+ };
12400
+ }
12401
+ function normalizeInteractivePrompt(raw) {
12402
+ if (!raw || typeof raw !== "object") return null;
12403
+ const record = raw;
12404
+ const promptId = readString(record.promptId);
12405
+ const providerType = readString(record.providerType);
12406
+ const origin = record.origin === "mcp" || record.origin === "agent" ? record.origin : "cli";
12407
+ const questions = Array.isArray(record.questions) ? record.questions.map(normalizeQuestion).filter((item) => !!item) : [];
12408
+ if (!promptId || !providerType || questions.length === 0) return null;
12409
+ const createdAt = typeof record.createdAt === "number" && Number.isFinite(record.createdAt) ? record.createdAt : Date.now();
12410
+ return { promptId, origin, providerType, createdAt, questions };
12411
+ }
12412
+ function normalizeInteractivePromptResponse(raw) {
12413
+ if (!raw || typeof raw !== "object") throw new Error("Interactive prompt response must be an object");
12414
+ const record = raw;
12415
+ const promptId = readString(record.promptId);
12416
+ if (!promptId) throw new Error("promptId must be a non-empty string");
12417
+ if (!record.answers || typeof record.answers !== "object" || Array.isArray(record.answers)) {
12418
+ throw new Error("answers must be an object");
12419
+ }
12420
+ const answers = {};
12421
+ for (const [questionId, answerRaw] of Object.entries(record.answers)) {
12422
+ if (!answerRaw || typeof answerRaw !== "object" || Array.isArray(answerRaw)) continue;
12423
+ const answer = answerRaw;
12424
+ const selectedLabels = readStringArray(answer.selectedLabels);
12425
+ const freeformText = readString(answer.freeformText);
12426
+ answers[questionId] = {
12427
+ selectedLabels,
12428
+ ...freeformText ? { freeformText } : {}
12429
+ };
12430
+ }
12431
+ return { promptId, answers };
12432
+ }
12433
+ function buildClaudeInteractiveToolResult(response) {
12434
+ return JSON.stringify({
12435
+ type: "user",
12436
+ message: {
12437
+ role: "user",
12438
+ content: [{
12439
+ type: "tool_result",
12440
+ tool_use_id: response.promptId,
12441
+ content: JSON.stringify({ answers: response.answers }),
12442
+ is_error: false
12443
+ }]
12444
+ }
12445
+ });
12446
+ }
12447
+ function claudeTuiQuestionHeaders(screenText) {
12448
+ const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
12449
+ if (!navLine) return [];
12450
+ const headers = [];
12451
+ const pattern = /[☐☒]\s+(.+?)(?=\s+[☐☒]|\s+✔\s+Submit)/g;
12452
+ for (const match of navLine.matchAll(pattern)) {
12453
+ const header = readString(match[1]);
12454
+ if (header) headers.push(header);
12455
+ }
12456
+ return headers;
12457
+ }
12458
+ function parseClaudeInteractiveTuiQuestion(page, index) {
12459
+ const lines = page.screenText.split(/\r?\n/);
12460
+ let navIndex = -1;
12461
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
12462
+ if (lines[i].includes("\u2714 Submit") && /[☐☒]/.test(lines[i])) {
12463
+ navIndex = i;
12464
+ break;
12465
+ }
12466
+ }
12467
+ if (navIndex < 0 || !page.screenText.includes("Enter to select")) return null;
12468
+ let question = "";
12469
+ let questionLineIndex = -1;
12470
+ for (let i = navIndex + 1; i < lines.length; i += 1) {
12471
+ const candidate = lines[i].trim();
12472
+ if (!candidate || /^─+$/.test(candidate)) continue;
12473
+ if (candidate === "Review your answers" || candidate === "Ready to submit your answers?") return null;
12474
+ question = candidate;
12475
+ questionLineIndex = i;
12476
+ break;
12477
+ }
12478
+ if (!question) return null;
12479
+ const options = [];
12480
+ let allowFreeform = false;
12481
+ const optionPattern = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
12482
+ for (let i = questionLineIndex + 1; i < lines.length; i += 1) {
12483
+ const match = lines[i].match(optionPattern);
12484
+ if (!match) continue;
12485
+ const label = match[2].trim();
12486
+ if (/^Type something\.?$/i.test(label)) {
12487
+ allowFreeform = true;
12488
+ continue;
12489
+ }
12490
+ if (/^Chat about this$/i.test(label)) continue;
12491
+ let description;
12492
+ const nextLine = lines[i + 1]?.trim();
12493
+ if (nextLine && !optionPattern.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
12494
+ description = nextLine;
12495
+ }
12496
+ options.push({ label, ...description ? { description } : {} });
12497
+ }
12498
+ if (options.length === 0) return null;
12499
+ const header = readString(page.header);
12500
+ return {
12501
+ questionId: `q${index + 1}`,
12502
+ question,
12503
+ ...header ? { header } : {},
12504
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
12505
+ options,
12506
+ ...allowFreeform ? { allowFreeform: true } : {}
12507
+ };
12508
+ }
12509
+ function detectClaudeAskUserQuestionPromptFromTuiPages(pages, options) {
12510
+ if (pages.length === 0) return null;
12511
+ const headers = claudeTuiQuestionHeaders(pages[0].screenText);
12512
+ const questions = pages.map((page, index) => parseClaudeInteractiveTuiQuestion({
12513
+ ...page,
12514
+ header: page.header || headers[index]
12515
+ }, index)).filter((question) => !!question);
12516
+ if (questions.length !== pages.length) return null;
12517
+ return {
12518
+ promptId: options.promptId,
12519
+ origin: "cli",
12520
+ providerType: options.providerType || "claude-cli",
12521
+ createdAt: options.createdAt || Date.now(),
12522
+ questions
12523
+ };
12524
+ }
12525
+ function buildClaudeInteractiveTuiAnswerSteps(prompt, response) {
12526
+ if (response.promptId !== prompt.promptId) throw new Error("Interactive prompt response does not match active prompt");
12527
+ const steps = [];
12528
+ for (const question of prompt.questions) {
12529
+ if (question.multiSelect) throw new Error("Claude TUI multi-select prompts are not supported yet");
12530
+ const answer = response.answers[question.questionId];
12531
+ if (!answer) throw new Error(`Missing answer for ${question.questionId}`);
12532
+ if (answer.freeformText) throw new Error("Claude TUI freeform answers are not supported yet");
12533
+ if (answer.selectedLabels.length !== 1) throw new Error(`Expected one selected label for ${question.questionId}`);
12534
+ const selectedIndex = question.options.findIndex((option) => option.label === answer.selectedLabels[0]);
12535
+ if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${answer.selectedLabels[0]}`);
12536
+ steps.push(`${"\x1B[B".repeat(selectedIndex)}\r`);
12537
+ }
12538
+ steps.push("\r");
12539
+ return steps;
12540
+ }
12541
+ function interactivePromptFromClaudeAskUserQuestion(input, options) {
12542
+ if (!input || typeof input !== "object") return null;
12543
+ const record = input;
12544
+ const questions = Array.isArray(record.questions) ? record.questions.map(normalizeQuestion).filter((item) => !!item) : [];
12545
+ if (questions.length === 0) return null;
12546
+ return {
12547
+ promptId: options.promptId,
12548
+ origin: options.origin || "cli",
12549
+ providerType: options.providerType,
12550
+ createdAt: options.createdAt || Date.now(),
12551
+ questions
12552
+ };
12553
+ }
12554
+ function detectClaudeAskUserQuestionPromptFromJson(value, providerType = "claude-cli") {
12555
+ if (!value || typeof value !== "object") return null;
12556
+ const record = value;
12557
+ const blocks = [];
12558
+ if (Array.isArray(record.content)) blocks.push(...record.content);
12559
+ const message = record.message;
12560
+ if (message && typeof message === "object" && Array.isArray(message.content)) {
12561
+ blocks.push(...message.content);
12562
+ }
12563
+ if (record.type === "tool_use") blocks.push(record);
12564
+ for (const block2 of blocks) {
12565
+ if (!block2 || typeof block2 !== "object") continue;
12566
+ const b = block2;
12567
+ const name = readString(b.name);
12568
+ if (b.type !== "tool_use" || name !== "AskUserQuestion") continue;
12569
+ const id = readString(b.id) || readString(record.id) || `ask-user-${Date.now()}`;
12570
+ const prompt = interactivePromptFromClaudeAskUserQuestion(b.input, {
12571
+ promptId: id,
12572
+ providerType,
12573
+ origin: "cli"
12574
+ });
12575
+ if (prompt) return prompt;
12576
+ }
12577
+ return null;
12578
+ }
12579
+
12580
+ // src/index.ts
12323
12581
  init_repo_mesh_types();
12324
12582
 
12325
12583
  // src/git/index.ts
@@ -15066,7 +15324,7 @@ init_mesh_work_queue();
15066
15324
  // src/mesh/mesh-active-work.ts
15067
15325
  var DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
15068
15326
  var TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
15069
- function readString2(value) {
15327
+ function readString3(value) {
15070
15328
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
15071
15329
  }
15072
15330
  function summarizeMessage(message) {
@@ -15081,7 +15339,7 @@ function elapsedSince(value, now) {
15081
15339
  function sessionStatusFromNodes(nodes, nodeId, sessionId) {
15082
15340
  if (!Array.isArray(nodes)) return {};
15083
15341
  if (!nodeId) return { staleReason: "direct task has no node id" };
15084
- const node = nodes.find((item) => readString2(item?.id) === nodeId || readString2(item?.nodeId) === nodeId || readString2(item?.node_id) === nodeId);
15342
+ const node = nodes.find((item) => readString3(item?.id) === nodeId || readString3(item?.nodeId) === nodeId || readString3(item?.node_id) === nodeId);
15085
15343
  if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
15086
15344
  if (!sessionId) return {};
15087
15345
  const candidates = [];
@@ -15105,12 +15363,12 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
15105
15363
  }
15106
15364
  const session = candidates.find((item) => {
15107
15365
  if (typeof item === "string") return item === sessionId;
15108
- const id = readString2(item?.id) || readString2(item?.sessionId) || readString2(item?.session_id) || readString2(item?.runtimeSessionId) || readString2(item?.instanceId);
15366
+ const id = readString3(item?.id) || readString3(item?.sessionId) || readString3(item?.session_id) || readString3(item?.runtimeSessionId) || readString3(item?.instanceId);
15109
15367
  return id === sessionId;
15110
15368
  });
15111
15369
  if (!session) return { staleReason: "direct task session is not present in live session records" };
15112
15370
  if (typeof session === "string") return {};
15113
- const raw = `${readString2(session.status) || ""} ${readString2(session.lifecycle) || ""} ${readString2(session.state) || ""} ${readString2(session.activeChat?.status) || ""}`.toLowerCase();
15371
+ const raw = `${readString3(session.status) || ""} ${readString3(session.lifecycle) || ""} ${readString3(session.state) || ""} ${readString3(session.activeChat?.status) || ""}`.toLowerCase();
15114
15372
  if (raw.includes("approval")) return { status: "awaiting_approval" };
15115
15373
  if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
15116
15374
  if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
@@ -15121,14 +15379,14 @@ function isDirectDispatch(entry) {
15121
15379
  if (entry.kind !== "task_dispatched") return false;
15122
15380
  const payload = entry.payload || {};
15123
15381
  if (payload.source === "direct") return true;
15124
- const via = readString2(payload.via);
15382
+ const via = readString3(payload.via);
15125
15383
  return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
15126
15384
  }
15127
15385
  function directDispatchTaskId(entry) {
15128
- return readString2(entry.payload?.taskId) || entry.id;
15386
+ return readString3(entry.payload?.taskId) || entry.id;
15129
15387
  }
15130
15388
  function terminalMatchesDispatch(terminal, dispatch, taskId) {
15131
- const terminalTaskId = readString2(terminal.payload?.taskId);
15389
+ const terminalTaskId = readString3(terminal.payload?.taskId);
15132
15390
  if (terminalTaskId && terminalTaskId === taskId) return true;
15133
15391
  if (terminalTaskId && terminalTaskId !== taskId) return false;
15134
15392
  if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
@@ -15251,7 +15509,7 @@ function buildMeshActiveWork(opts) {
15251
15509
  const isNoTransition = !terminalStatus && !live.status;
15252
15510
  const isIdleUnacknowledged = status === "idle";
15253
15511
  const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
15254
- const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
15512
+ const message = readString3(dispatch.payload?.message) || readString3(dispatch.payload?.summary) || "";
15255
15513
  const { title, summary: summary2 } = summarizeMessage(message);
15256
15514
  const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
15257
15515
  const record = {
@@ -15260,11 +15518,11 @@ function buildMeshActiveWork(opts) {
15260
15518
  status,
15261
15519
  nodeId: dispatch.nodeId,
15262
15520
  sessionId: dispatch.sessionId,
15263
- providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
15264
- taskTitle: readString2(dispatch.payload?.taskTitle) || title,
15265
- taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
15521
+ providerType: dispatch.providerType || readString3(dispatch.payload?.providerType),
15522
+ taskTitle: readString3(dispatch.payload?.taskTitle) || title,
15523
+ taskSummary: readString3(dispatch.payload?.taskSummary) || summary2,
15266
15524
  message,
15267
- taskMode: readString2(dispatch.payload?.taskMode),
15525
+ taskMode: readString3(dispatch.payload?.taskMode),
15268
15526
  createdAt: dispatch.timestamp,
15269
15527
  updatedAt: terminal?.timestamp || dispatch.timestamp,
15270
15528
  dispatchedAt: dispatch.timestamp,
@@ -15299,7 +15557,7 @@ function buildMeshActiveWork(opts) {
15299
15557
  const isNoTransition = !terminalStatus && !live.status;
15300
15558
  const isIdleUnacknowledged = status === "idle";
15301
15559
  const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
15302
- const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
15560
+ const message = readString3(dispatch.payload?.message) || readString3(dispatch.payload?.summary) || "";
15303
15561
  const { title, summary: summary2 } = summarizeMessage(message);
15304
15562
  const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
15305
15563
  const record = {
@@ -15308,11 +15566,11 @@ function buildMeshActiveWork(opts) {
15308
15566
  status,
15309
15567
  nodeId: dispatch.nodeId,
15310
15568
  sessionId: dispatch.sessionId,
15311
- providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
15312
- taskTitle: readString2(dispatch.payload?.taskTitle) || title,
15313
- taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
15569
+ providerType: dispatch.providerType || readString3(dispatch.payload?.providerType),
15570
+ taskTitle: readString3(dispatch.payload?.taskTitle) || title,
15571
+ taskSummary: readString3(dispatch.payload?.taskSummary) || summary2,
15314
15572
  message,
15315
- taskMode: readString2(dispatch.payload?.taskMode),
15573
+ taskMode: readString3(dispatch.payload?.taskMode),
15316
15574
  createdAt: dispatch.timestamp,
15317
15575
  updatedAt: terminal?.timestamp || dispatch.timestamp,
15318
15576
  dispatchedAt: dispatch.timestamp,
@@ -15375,7 +15633,7 @@ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
15375
15633
  }
15376
15634
 
15377
15635
  // src/mesh/mesh-refine-status.ts
15378
- function readString3(value) {
15636
+ function readString4(value) {
15379
15637
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
15380
15638
  }
15381
15639
  function readRecord(value) {
@@ -15401,7 +15659,7 @@ function instructionForStatus(status) {
15401
15659
  return "Refine job failed; inspect result/finalBranchConvergenceState in mesh_task_history, fix the blocker, then rerun mesh_refine_node when ready.";
15402
15660
  }
15403
15661
  function mergeJob(jobs, patch) {
15404
- const jobId = readString3(patch.jobId);
15662
+ const jobId = readString4(patch.jobId);
15405
15663
  if (!jobId) return;
15406
15664
  const previous = jobs.get(jobId);
15407
15665
  const status = patch.status || previous?.status || "running";
@@ -15424,23 +15682,23 @@ function buildMeshAsyncRefineJobs(args) {
15424
15682
  const refineJob = readRecord(payload.refineJob);
15425
15683
  const result = readRecord(payload.result);
15426
15684
  const finalState = readRecord(payload.finalBranchConvergenceState) || readRecord(result?.finalBranchConvergenceState);
15427
- const jobId = readString3(refineJob?.jobId);
15685
+ const jobId = readString4(refineJob?.jobId);
15428
15686
  if (!jobId) continue;
15429
- const status = ledgerStatus(entry.kind, readString3(refineJob?.status));
15687
+ const status = ledgerStatus(entry.kind, readString4(refineJob?.status));
15430
15688
  mergeJob(jobs, {
15431
15689
  jobId,
15432
- interactionId: readString3(refineJob?.interactionId),
15690
+ interactionId: readString4(refineJob?.interactionId),
15433
15691
  status,
15434
- meshId: readString3(refineJob?.meshId) || args.meshId,
15435
- nodeId: readString3(refineJob?.nodeId) || entry.nodeId,
15436
- targetNodeId: readString3(refineJob?.nodeId) || entry.nodeId,
15437
- targetDaemonId: readString3(refineJob?.targetDaemonId),
15438
- workspace: readString3(refineJob?.workspace),
15439
- branch: readString3(result?.branch) || readString3(finalState?.branch),
15440
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
15441
- startedAt: readString3(refineJob?.startedAt),
15442
- completedAt: readString3(refineJob?.completedAt),
15443
- retryOfJobId: readString3(refineJob?.retryOfJobId) || readString3(payload.retryOfJobId),
15692
+ meshId: readString4(refineJob?.meshId) || args.meshId,
15693
+ nodeId: readString4(refineJob?.nodeId) || entry.nodeId,
15694
+ targetNodeId: readString4(refineJob?.nodeId) || entry.nodeId,
15695
+ targetDaemonId: readString4(refineJob?.targetDaemonId),
15696
+ workspace: readString4(refineJob?.workspace),
15697
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
15698
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
15699
+ startedAt: readString4(refineJob?.startedAt),
15700
+ completedAt: readString4(refineJob?.completedAt),
15701
+ retryOfJobId: readString4(refineJob?.retryOfJobId) || readString4(payload.retryOfJobId),
15444
15702
  lastLedgerKind: entry.kind,
15445
15703
  lastUpdatedAt: entry.timestamp
15446
15704
  });
@@ -15450,23 +15708,23 @@ function buildMeshAsyncRefineJobs(args) {
15450
15708
  if (metadata?.source !== "refine_mesh_node_async_job") continue;
15451
15709
  const result = readRecord(metadata.result);
15452
15710
  const finalState = readRecord(result?.finalBranchConvergenceState);
15453
- const jobId = readString3(metadata.jobId);
15711
+ const jobId = readString4(metadata.jobId);
15454
15712
  if (!jobId) continue;
15455
- const status = eventStatus(event.event, readString3(metadata.status));
15713
+ const status = eventStatus(event.event, readString4(metadata.status));
15456
15714
  mergeJob(jobs, {
15457
15715
  jobId,
15458
- interactionId: readString3(metadata.interactionId),
15716
+ interactionId: readString4(metadata.interactionId),
15459
15717
  ...status ? { status } : {},
15460
- meshId: readString3(metadata.meshId) || event.meshId || args.meshId,
15461
- nodeId: readString3(metadata.nodeId) || event.nodeId,
15462
- targetNodeId: readString3(metadata.nodeId) || event.nodeId,
15463
- targetDaemonId: readString3(metadata.targetDaemonId),
15464
- workspace: readString3(metadata.workspace) || event.workspace,
15465
- branch: readString3(result?.branch) || readString3(finalState?.branch),
15466
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
15467
- startedAt: readString3(metadata.startedAt),
15468
- completedAt: readString3(metadata.completedAt),
15469
- retryOfJobId: readString3(metadata.retryOfJobId),
15718
+ meshId: readString4(metadata.meshId) || event.meshId || args.meshId,
15719
+ nodeId: readString4(metadata.nodeId) || event.nodeId,
15720
+ targetNodeId: readString4(metadata.nodeId) || event.nodeId,
15721
+ targetDaemonId: readString4(metadata.targetDaemonId),
15722
+ workspace: readString4(metadata.workspace) || event.workspace,
15723
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
15724
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
15725
+ startedAt: readString4(metadata.startedAt),
15726
+ completedAt: readString4(metadata.completedAt),
15727
+ retryOfJobId: readString4(metadata.retryOfJobId),
15470
15728
  lastEvent: event.event,
15471
15729
  lastUpdatedAt: new Date(event.queuedAt).toISOString()
15472
15730
  });
@@ -21643,6 +21901,7 @@ function buildCliSession(state, options) {
21643
21901
  mode: state.mode,
21644
21902
  resume: state.resume,
21645
21903
  activeChat,
21904
+ ...state.activeInteractivePrompt ? { activeInteractivePrompt: state.activeInteractivePrompt } : {},
21646
21905
  ...summaryMetadata && { summaryMetadata },
21647
21906
  ...includeSessionMetadata && {
21648
21907
  capabilities: state.mode === "terminal" ? PTY_SESSION_CAPABILITIES : CLI_CHAT_SESSION_CAPABILITIES,
@@ -27395,6 +27654,9 @@ var SpecDriver = class {
27395
27654
  return;
27396
27655
  }
27397
27656
  }
27657
+ snapshot() {
27658
+ return this.adapter.snapshot();
27659
+ }
27398
27660
  shutdown() {
27399
27661
  for (const t of this.delegateTimers.values()) clearTimeout(t);
27400
27662
  this.delegateTimers.clear();
@@ -27674,6 +27936,10 @@ var SpecCliAdapter = class {
27674
27936
  statusCallback = null;
27675
27937
  ptyDataCallback = null;
27676
27938
  partialResponse = "";
27939
+ activeInteractivePrompt = null;
27940
+ interactivePromptTransport = null;
27941
+ claudeTuiPromptCaptureInFlight = false;
27942
+ jsonLineTail = "";
27677
27943
  exited = false;
27678
27944
  spawned = false;
27679
27945
  providerSessionId;
@@ -27723,11 +27989,11 @@ var SpecCliAdapter = class {
27723
27989
  this.driver.dispatch({ kind: "send_message", text });
27724
27990
  }
27725
27991
  getStatus() {
27726
- if (this.exited) return { status: "stopped", messages: [], activeModal: null };
27727
- if (!this.spawned) return { status: "starting", messages: [], activeModal: null };
27992
+ if (this.exited) return { status: "stopped", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27993
+ if (!this.spawned) return { status: "starting", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27728
27994
  this.maybeRefreshNativeHistory();
27729
27995
  const state = this.latestState;
27730
- if (!state) return { status: "starting", messages: [], activeModal: null };
27996
+ if (!state) return { status: "starting", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27731
27997
  const modal = this.latestModal;
27732
27998
  const lc = state.id.toLowerCase();
27733
27999
  if (modal) {
@@ -27737,13 +28003,14 @@ var SpecCliAdapter = class {
27737
28003
  activeModal: {
27738
28004
  message: modal.title ?? state.label,
27739
28005
  buttons: modal.buttons.map((b) => b.label)
27740
- }
28006
+ },
28007
+ activeInteractivePrompt: this.activeInteractivePrompt
27741
28008
  };
27742
28009
  }
27743
28010
  if (/busy|generating|working|running|thinking/i.test(lc + " " + state.label)) {
27744
- return { status: "generating", messages: [], activeModal: null };
28011
+ return { status: "generating", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27745
28012
  }
27746
- return { status: "idle", messages: [], activeModal: null };
28013
+ return { status: "idle", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt };
27747
28014
  }
27748
28015
  maybeRefreshNativeHistory() {
27749
28016
  }
@@ -27806,6 +28073,24 @@ var SpecCliAdapter = class {
27806
28073
  }
27807
28074
  this.resolveModal(target);
27808
28075
  }
28076
+ async setInteractivePromptResponse(response) {
28077
+ const prompt = this.activeInteractivePrompt;
28078
+ if (!prompt || prompt.promptId !== response.promptId) throw new Error("Interactive prompt response does not match active prompt");
28079
+ if (this.cliType !== "claude-cli") return;
28080
+ if (this.interactivePromptTransport === "tui") {
28081
+ const steps = buildClaudeInteractiveTuiAnswerSteps(prompt, response);
28082
+ for (const step of steps) {
28083
+ this.driver.dispatch({ kind: "pty_write", data: step });
28084
+ await new Promise((resolve23) => setTimeout(resolve23, 180));
28085
+ }
28086
+ } else {
28087
+ this.driver.dispatch({ kind: "pty_write", data: `${buildClaudeInteractiveToolResult(response)}
28088
+ ` });
28089
+ }
28090
+ this.activeInteractivePrompt = null;
28091
+ this.interactivePromptTransport = null;
28092
+ this.statusCallback?.();
28093
+ }
27809
28094
  isApprovalRecentlyResolved() {
27810
28095
  return false;
27811
28096
  }
@@ -27861,6 +28146,7 @@ var SpecCliAdapter = class {
27861
28146
  spec_id: this.spec.id,
27862
28147
  current_state: this.latestState,
27863
28148
  current_modal: this.latestModal,
28149
+ activeInteractivePrompt: this.activeInteractivePrompt,
27864
28150
  exited: this.exited
27865
28151
  };
27866
28152
  }
@@ -27890,9 +28176,12 @@ var SpecCliAdapter = class {
27890
28176
  if (ev.state.title) {
27891
28177
  LOG.debug("SpecAdapter", `[${this.cliType}] state.title=${JSON.stringify(ev.state.title)}`);
27892
28178
  }
28179
+ this.maybeCaptureClaudeTuiPrompt();
27893
28180
  this.statusCallback?.();
27894
28181
  return;
27895
28182
  case "pty_data":
28183
+ this.detectInteractivePromptFromPtyChunk(ev.chunk);
28184
+ this.maybeCaptureClaudeTuiPrompt();
27896
28185
  try {
27897
28186
  this.ptyDataCallback?.(ev.chunk);
27898
28187
  } catch {
@@ -27909,6 +28198,66 @@ var SpecCliAdapter = class {
27909
28198
  return;
27910
28199
  }
27911
28200
  }
28201
+ detectInteractivePromptFromPtyChunk(chunk) {
28202
+ if (this.cliType !== "claude-cli" || !chunk) return;
28203
+ this.jsonLineTail += chunk;
28204
+ if (this.jsonLineTail.length > 64 * 1024) this.jsonLineTail = this.jsonLineTail.slice(-64 * 1024);
28205
+ const lines = this.jsonLineTail.split(/\r?\n/);
28206
+ this.jsonLineTail = lines.pop() || "";
28207
+ for (const line of lines) {
28208
+ const trimmed = line.trim();
28209
+ if (!trimmed.startsWith("{") || !trimmed.includes("AskUserQuestion")) continue;
28210
+ try {
28211
+ const parsed = JSON.parse(trimmed);
28212
+ const prompt = detectClaudeAskUserQuestionPromptFromJson(parsed, this.cliType);
28213
+ if (!prompt) continue;
28214
+ this.activeInteractivePrompt = prompt;
28215
+ this.interactivePromptTransport = "stream-json";
28216
+ this.statusCallback?.();
28217
+ } catch {
28218
+ }
28219
+ }
28220
+ }
28221
+ maybeCaptureClaudeTuiPrompt() {
28222
+ if (this.cliType !== "claude-cli" || this.activeInteractivePrompt || this.claudeTuiPromptCaptureInFlight) return;
28223
+ const screenText = this.driver.snapshot();
28224
+ const headers = this.readClaudeTuiHeaders(screenText);
28225
+ if (headers.length === 0 || !screenText.includes("Enter to select")) return;
28226
+ this.claudeTuiPromptCaptureInFlight = true;
28227
+ void this.captureClaudeTuiPrompt(screenText, headers).finally(() => {
28228
+ this.claudeTuiPromptCaptureInFlight = false;
28229
+ });
28230
+ }
28231
+ readClaudeTuiHeaders(screenText) {
28232
+ const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
28233
+ if (!navLine) return [];
28234
+ const headers = [];
28235
+ for (const match of navLine.matchAll(/[☐☒]\s+(.+?)(?=\s+[☐☒]|\s+✔\s+Submit)/g)) {
28236
+ const header = match[1]?.trim();
28237
+ if (header) headers.push(header);
28238
+ }
28239
+ return headers;
28240
+ }
28241
+ async captureClaudeTuiPrompt(firstScreen, headers) {
28242
+ const pages = [{ screenText: firstScreen, header: headers[0] }];
28243
+ for (let index = 1; index < headers.length; index += 1) {
28244
+ this.driver.dispatch({ kind: "pty_write", data: " " });
28245
+ await new Promise((resolve23) => setTimeout(resolve23, 120));
28246
+ pages.push({ screenText: this.driver.snapshot(), header: headers[index] });
28247
+ }
28248
+ for (let index = headers.length - 1; index > 0; index -= 1) {
28249
+ this.driver.dispatch({ kind: "pty_write", data: "\x1B[Z" });
28250
+ await new Promise((resolve23) => setTimeout(resolve23, 80));
28251
+ }
28252
+ const prompt = detectClaudeAskUserQuestionPromptFromTuiPages(pages, {
28253
+ promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
28254
+ providerType: this.cliType
28255
+ });
28256
+ if (!prompt) return;
28257
+ this.activeInteractivePrompt = prompt;
28258
+ this.interactivePromptTransport = "tui";
28259
+ this.statusCallback?.();
28260
+ }
27912
28261
  };
27913
28262
 
27914
28263
  // src/providers/spec/route.ts
@@ -28177,7 +28526,7 @@ var CliProviderInstance = class {
28177
28526
  monitor;
28178
28527
  generatingDebounceTimer = null;
28179
28528
  generatingDebouncePending = null;
28180
- lastApprovalEventAt = 0;
28529
+ lastApprovalEventFingerprint = "";
28181
28530
  autoApproveBusy = false;
28182
28531
  autoApproveBusyTimer = null;
28183
28532
  lastAutoApprovalSignature = "";
@@ -28196,6 +28545,7 @@ var CliProviderInstance = class {
28196
28545
  suppressIdleHistoryReplay = false;
28197
28546
  errorMessage = void 0;
28198
28547
  errorReason = void 0;
28548
+ activeInteractivePrompt = null;
28199
28549
  presentationMode;
28200
28550
  providerSessionId;
28201
28551
  launchMode;
@@ -28288,6 +28638,9 @@ var CliProviderInstance = class {
28288
28638
  }
28289
28639
  getState() {
28290
28640
  const adapterStatus = this.adapter.getStatus();
28641
+ if (Object.prototype.hasOwnProperty.call(adapterStatus, "activeInteractivePrompt")) {
28642
+ this.activeInteractivePrompt = adapterStatus.activeInteractivePrompt ?? null;
28643
+ }
28291
28644
  let parsedStatus = null;
28292
28645
  let parseErrorMessage;
28293
28646
  if (typeof this.adapter.getScriptParsedStatus === "function") {
@@ -28404,8 +28757,10 @@ var CliProviderInstance = class {
28404
28757
  status: activeChatStatus,
28405
28758
  messages: statusMessages,
28406
28759
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
28760
+ activeInteractivePrompt: this.activeInteractivePrompt,
28407
28761
  inputContent: ""
28408
28762
  },
28763
+ activeInteractivePrompt: this.activeInteractivePrompt,
28409
28764
  workspace: this.workingDir,
28410
28765
  instanceId: this.instanceId,
28411
28766
  providerSessionId: this.providerSessionId,
@@ -28495,6 +28850,32 @@ var CliProviderInstance = class {
28495
28850
  void this.adapter.resolveAction(data).catch((e) => {
28496
28851
  LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
28497
28852
  });
28853
+ } else if (event === "interactive_prompt" && data) {
28854
+ const prompt = normalizeInteractivePrompt(data);
28855
+ if (prompt) {
28856
+ this.activeInteractivePrompt = prompt;
28857
+ this.events.push({
28858
+ event: "interactive_prompt",
28859
+ timestamp: Date.now(),
28860
+ promptId: prompt.promptId
28861
+ });
28862
+ }
28863
+ } else if (event === "interactive_prompt_response" && data) {
28864
+ try {
28865
+ const response = normalizeInteractivePromptResponse(data);
28866
+ if (this.activeInteractivePrompt?.promptId === response.promptId) {
28867
+ this.activeInteractivePrompt = null;
28868
+ }
28869
+ if (typeof this.adapter.setInteractivePromptResponse !== "function") {
28870
+ LOG.warn("CLI", `[${this.type}] interactive_prompt_response ignored: adapter does not support interactive prompts`);
28871
+ return;
28872
+ }
28873
+ void this.adapter.setInteractivePromptResponse(response).catch((e) => {
28874
+ LOG.warn("CLI", `[${this.type}] interactive_prompt_response failed: ${e?.message || e}`);
28875
+ });
28876
+ } catch (e) {
28877
+ LOG.warn("CLI", `[${this.type}] invalid interactive_prompt_response: ${e?.message || e}`);
28878
+ }
28498
28879
  } else if (event === "provider_state_patch" && data && typeof data === "object") {
28499
28880
  this.applyProviderResponse(data, { phase: "immediate" });
28500
28881
  }
@@ -28628,12 +29009,15 @@ var CliProviderInstance = class {
28628
29009
  if (typeof adapterAny?.responseBuffer === "string" && adapterAny.responseBuffer.trim()) return false;
28629
29010
  return true;
28630
29011
  }
28631
- getCompletedFinalizationBlock(latestVisibleStatus) {
29012
+ getCompletedFinalizationBlock(latestVisibleStatus, pending) {
28632
29013
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
28633
29014
  const adapterAny = this.adapter;
28634
- if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
28635
- if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
28636
- if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
29015
+ const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
29016
+ if (!approvalResolvedIdle) {
29017
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
29018
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
29019
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
29020
+ }
28637
29021
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
28638
29022
  if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28639
29023
  let parsed;
@@ -28658,7 +29042,7 @@ var CliProviderInstance = class {
28658
29042
  if (screenText) {
28659
29043
  const tailLines = screenText.split(/\r?\n/).slice(-16).join("\n");
28660
29044
  if (looksLikeActiveApprovalPromptText(tailLines)) {
28661
- return { reason: "screen_shows_approval_prompt", terminal: false };
29045
+ return { reason: "screen_shows_approval_prompt", terminal: approvalResolvedIdle };
28662
29046
  }
28663
29047
  }
28664
29048
  } catch {
@@ -28684,7 +29068,7 @@ var CliProviderInstance = class {
28684
29068
  this.completedDebounceTimer = null;
28685
29069
  return;
28686
29070
  }
28687
- const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus);
29071
+ const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending);
28688
29072
  if (block2) {
28689
29073
  const blockReason = block2.reason;
28690
29074
  const waitedMs = Date.now() - pending.firstObservedAt;
@@ -28716,6 +29100,7 @@ var CliProviderInstance = class {
28716
29100
  this.completedDebouncePending = null;
28717
29101
  this.completedDebounceTimer = null;
28718
29102
  this.generatingStartedAt = 0;
29103
+ this.lastApprovalEventFingerprint = "";
28719
29104
  return;
28720
29105
  }
28721
29106
  LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
@@ -28729,6 +29114,7 @@ var CliProviderInstance = class {
28729
29114
  this.completedDebouncePending = null;
28730
29115
  this.completedDebounceTimer = null;
28731
29116
  this.generatingStartedAt = 0;
29117
+ this.lastApprovalEventFingerprint = "";
28732
29118
  }
28733
29119
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
28734
29120
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
@@ -28825,9 +29211,12 @@ var CliProviderInstance = class {
28825
29211
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
28826
29212
  const modal = adapterStatus.activeModal;
28827
29213
  LOG.info("CLI", `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? "none"}"`);
28828
- const approvalCooldown = 5e3;
28829
- if (this.lastStatus !== "waiting_approval" && (!this.lastApprovalEventAt || now - this.lastApprovalEventAt > approvalCooldown)) {
28830
- this.lastApprovalEventAt = now;
29214
+ const approvalFingerprint = JSON.stringify({
29215
+ message: typeof modal?.message === "string" ? modal.message.trim() : "",
29216
+ buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button) => String(button).trim()) : []
29217
+ });
29218
+ if (this.lastStatus !== "waiting_approval" && approvalFingerprint !== this.lastApprovalEventFingerprint) {
29219
+ this.lastApprovalEventFingerprint = approvalFingerprint;
28831
29220
  this.appendRuntimeSystemMessage(
28832
29221
  this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
28833
29222
  `approval_request:${now}`,
@@ -28869,7 +29258,13 @@ var CliProviderInstance = class {
28869
29258
  }
28870
29259
  });
28871
29260
  } else {
28872
- this.completedDebouncePending = { chatTitle, duration, timestamp: now, firstObservedAt: now };
29261
+ this.completedDebouncePending = {
29262
+ chatTitle,
29263
+ duration,
29264
+ timestamp: now,
29265
+ firstObservedAt: now,
29266
+ previousStatus: this.lastStatus
29267
+ };
28873
29268
  this.scheduleCompletedDebounceFlush(3e3);
28874
29269
  }
28875
29270
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -48952,6 +49347,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
48952
49347
  buildChatMessage,
48953
49348
  buildChatMessageSignature,
48954
49349
  buildChatTailDeliverySignature,
49350
+ buildClaudeInteractiveToolResult,
48955
49351
  buildCompactStaleDirectWorkSummary,
48956
49352
  buildCoordinatorSystemPrompt,
48957
49353
  buildIpcStatusHttpResponse,
@@ -48998,6 +49394,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
48998
49394
  deleteMesh,
48999
49395
  detectAllVersions,
49000
49396
  detectCLIs,
49397
+ detectClaudeAskUserQuestionPromptFromJson,
49001
49398
  detectIDEs,
49002
49399
  drainPendingMeshCoordinatorEvents,
49003
49400
  enqueueTask,
@@ -49052,6 +49449,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
49052
49449
  insertDirectDispatch,
49053
49450
  installExtensions,
49054
49451
  installGlobalInterceptor,
49452
+ interactivePromptFromClaudeAskUserQuestion,
49055
49453
  isActivityChatMessage,
49056
49454
  isBuiltinChatMessageKind,
49057
49455
  isCdpConnected,
@@ -49095,6 +49493,8 @@ var V1_CONTRACT_VERSION = "1.0.0";
49095
49493
  normalizeGitOutput,
49096
49494
  normalizeGitWorkspaceSubscriptionParams,
49097
49495
  normalizeInputEnvelope,
49496
+ normalizeInteractivePrompt,
49497
+ normalizeInteractivePromptResponse,
49098
49498
  normalizeManagedStatus,
49099
49499
  normalizeMeshDaemonRole,
49100
49500
  normalizeMeshTaskMode,