@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/cli-adapter-types.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +474 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +467 -74
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +19 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/providers/provider-instance.d.ts +3 -0
- package/dist/providers/spec/cli-adapter.d.ts +10 -0
- package/dist/providers/spec/driver.d.ts +1 -0
- package/dist/providers/types/interactive-prompt.d.ts +48 -0
- package/dist/shared-types.d.ts +3 -0
- package/package.json +1 -1
- package/src/cli-adapter-types.ts +3 -0
- package/src/cli-adapters/provider-cli-adapter.ts +6 -0
- package/src/cli-adapters/provider-cli-shared.ts +2 -0
- package/src/index.ts +15 -0
- package/src/mesh/mesh-events.ts +41 -0
- package/src/providers/cli-provider-instance.ts +61 -12
- package/src/providers/provider-instance.ts +3 -0
- package/src/providers/spec/cli-adapter.ts +109 -6
- package/src/providers/spec/driver.ts +4 -0
- package/src/providers/types/interactive-prompt.ts +297 -0
- package/src/shared-types.ts +3 -0
- package/src/status/builders.ts +1 -0
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
|
|
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 =
|
|
698
|
-
const hostNodeId =
|
|
699
|
-
const 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
|
-
...
|
|
708
|
-
...
|
|
709
|
-
...
|
|
710
|
-
...
|
|
711
|
-
...
|
|
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
|
|
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:
|
|
2177
|
+
changedFiles: readStringArray2(raw.changedFiles),
|
|
2178
2178
|
validationResults: normalizeValidationResults(raw.validationResults),
|
|
2179
2179
|
...gitStatus ? { gitStatus } : {},
|
|
2180
2180
|
processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
|
|
2181
|
-
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
|
|
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) =>
|
|
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 =
|
|
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 = `${
|
|
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 =
|
|
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
|
|
15078
|
+
return readString3(entry.payload?.taskId) || entry.id;
|
|
14828
15079
|
}
|
|
14829
15080
|
function terminalMatchesDispatch(terminal, dispatch, taskId) {
|
|
14830
|
-
const terminalTaskId =
|
|
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 =
|
|
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 ||
|
|
14963
|
-
taskTitle:
|
|
14964
|
-
taskSummary:
|
|
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:
|
|
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 =
|
|
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 ||
|
|
15011
|
-
taskTitle:
|
|
15012
|
-
taskSummary:
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
15377
|
+
const jobId = readString4(refineJob?.jobId);
|
|
15127
15378
|
if (!jobId) continue;
|
|
15128
|
-
const status = ledgerStatus(entry.kind,
|
|
15379
|
+
const status = ledgerStatus(entry.kind, readString4(refineJob?.status));
|
|
15129
15380
|
mergeJob(jobs, {
|
|
15130
15381
|
jobId,
|
|
15131
|
-
interactionId:
|
|
15382
|
+
interactionId: readString4(refineJob?.interactionId),
|
|
15132
15383
|
status,
|
|
15133
|
-
meshId:
|
|
15134
|
-
nodeId:
|
|
15135
|
-
targetNodeId:
|
|
15136
|
-
targetDaemonId:
|
|
15137
|
-
workspace:
|
|
15138
|
-
branch:
|
|
15139
|
-
into:
|
|
15140
|
-
startedAt:
|
|
15141
|
-
completedAt:
|
|
15142
|
-
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 =
|
|
15403
|
+
const jobId = readString4(metadata.jobId);
|
|
15153
15404
|
if (!jobId) continue;
|
|
15154
|
-
const status = eventStatus(event.event,
|
|
15405
|
+
const status = eventStatus(event.event, readString4(metadata.status));
|
|
15155
15406
|
mergeJob(jobs, {
|
|
15156
15407
|
jobId,
|
|
15157
|
-
interactionId:
|
|
15408
|
+
interactionId: readString4(metadata.interactionId),
|
|
15158
15409
|
...status ? { status } : {},
|
|
15159
|
-
meshId:
|
|
15160
|
-
nodeId:
|
|
15161
|
-
targetNodeId:
|
|
15162
|
-
targetDaemonId:
|
|
15163
|
-
workspace:
|
|
15164
|
-
branch:
|
|
15165
|
-
into:
|
|
15166
|
-
startedAt:
|
|
15167
|
-
completedAt:
|
|
15168
|
-
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
|
-
|
|
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
|
-
|
|
28334
|
-
if (
|
|
28335
|
-
|
|
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:
|
|
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
|
|
28528
|
-
|
|
28529
|
-
|
|
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 = {
|
|
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,
|