@fadhelhaidar/pi-ask-user 1.0.3 → 1.1.0
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/README.md +4 -2
- package/dist/chunks/{index-x7fb685n.js → index-n7wxhdmh.js} +77 -1
- package/dist/chunks/index-pen3t92h.js +7 -0
- package/dist/chunks/{questionnaire-session-gdq5zqtt.js → questionnaire-session-m053x4ka.js} +238 -190
- package/dist/events.d.ts +78 -0
- package/dist/events.js +6 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +570 -74
- package/dist/locales/de.json +26 -29
- package/dist/locales/en.json +31 -27
- package/dist/locales/es.json +26 -29
- package/dist/locales/fr.json +26 -29
- package/dist/locales/pt-BR.json +26 -29
- package/dist/locales/pt.json +26 -29
- package/dist/locales/ru.json +26 -29
- package/dist/locales/uk.json +26 -29
- package/dist/locales/zh.json +28 -31
- package/docs/configuration.md +14 -0
- package/docs/hosts.md +48 -4
- package/docs/keyboard.md +33 -13
- package/docs/localization.md +7 -2
- package/docs/tool-schema.md +38 -6
- package/package.json +3 -3
- package/dist/chunks/index-bx2tbz93.js +0 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ASK_USER_PROMPT_EVENT2,
|
|
3
|
+
ASK_USER_BLOCKED_EVENT2,
|
|
4
|
+
ASK_USER_ANSWER_EVENT2,
|
|
5
|
+
ASK_USER_ANSWER_RESULT_EVENT2
|
|
6
|
+
} from "./chunks/index-pen3t92h.js";
|
|
1
7
|
import {
|
|
2
8
|
validateGuidanceFields,
|
|
3
9
|
COLLAPSE_KEY_OFF,
|
|
4
10
|
resolveCollapseKey,
|
|
11
|
+
resolveNotesKey,
|
|
5
12
|
formatKeySpecForDisplay,
|
|
6
13
|
loadConfig,
|
|
7
14
|
ROW_INTENT_META,
|
|
@@ -9,20 +16,199 @@ import {
|
|
|
9
16
|
I18N_NAMESPACE,
|
|
10
17
|
t,
|
|
11
18
|
displayLabel,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ASK_USER_BLOCKED_EVENT2
|
|
17
|
-
} from "./chunks/index-bx2tbz93.js";
|
|
19
|
+
NO_INPUT_PLACEHOLDER,
|
|
20
|
+
formatAnswerScalar,
|
|
21
|
+
stripFenceMarkers
|
|
22
|
+
} from "./chunks/index-n7wxhdmh.js";
|
|
18
23
|
|
|
19
24
|
// ask-user-question.ts
|
|
20
25
|
import { isKeyRelease, isKeyRepeat, matchesKey } from "@earendil-works/pi-tui";
|
|
21
26
|
|
|
27
|
+
// tool/external-answer.ts
|
|
28
|
+
var reject = (code, reason) => ({ ok: false, code, reason });
|
|
29
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
30
|
+
function noteOf(v) {
|
|
31
|
+
const trimmed = v?.trim();
|
|
32
|
+
return trimmed ? trimmed : undefined;
|
|
33
|
+
}
|
|
34
|
+
function resolveExternalAnswer(questions, payload) {
|
|
35
|
+
const { answers, globalNote, cancel } = payload;
|
|
36
|
+
if (cancel !== undefined && typeof cancel !== "boolean")
|
|
37
|
+
return reject("invalid_payload", "cancel must be a boolean");
|
|
38
|
+
if (globalNote !== undefined && typeof globalNote !== "string") {
|
|
39
|
+
return reject("invalid_payload", "globalNote must be a string");
|
|
40
|
+
}
|
|
41
|
+
const note = noteOf(globalNote);
|
|
42
|
+
const withNote = note ? { globalNote: note } : {};
|
|
43
|
+
if (cancel === true) {
|
|
44
|
+
if (answers !== undefined && !(Array.isArray(answers) && answers.length === 0)) {
|
|
45
|
+
return reject("invalid_payload", "cancel takes no answers");
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, result: { answers: [], cancelled: true, ...withNote } };
|
|
48
|
+
}
|
|
49
|
+
if (!Array.isArray(answers))
|
|
50
|
+
return reject("invalid_payload", "answers must be an array");
|
|
51
|
+
const byQuestion = new Map;
|
|
52
|
+
for (const entry of answers) {
|
|
53
|
+
if (!isRecord(entry))
|
|
54
|
+
return reject("invalid_payload", "each answer must be an object");
|
|
55
|
+
const index = entry.questionIndex;
|
|
56
|
+
if (!Number.isInteger(index))
|
|
57
|
+
return reject("invalid_payload", "questionIndex must be an integer");
|
|
58
|
+
const i = index;
|
|
59
|
+
if (i < 0 || i >= questions.length) {
|
|
60
|
+
return reject("invalid_answer", `questionIndex ${i} out of range (0..${questions.length - 1})`);
|
|
61
|
+
}
|
|
62
|
+
if (byQuestion.has(i))
|
|
63
|
+
return reject("invalid_answer", `question ${i} answered twice`);
|
|
64
|
+
byQuestion.set(i, entry);
|
|
65
|
+
}
|
|
66
|
+
const resolved = [];
|
|
67
|
+
for (let i = 0;i < questions.length; i++) {
|
|
68
|
+
const entry = byQuestion.get(i);
|
|
69
|
+
if (!entry)
|
|
70
|
+
return reject("invalid_answer", `no answer for question ${i} ("${questions[i].header}")`);
|
|
71
|
+
const answer = resolveEntry(questions[i], i, entry);
|
|
72
|
+
if (!answer.ok)
|
|
73
|
+
return answer;
|
|
74
|
+
resolved.push(answer.answer);
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, result: { answers: resolved, cancelled: false, ...withNote } };
|
|
77
|
+
}
|
|
78
|
+
function resolveEntry(q, i, entry) {
|
|
79
|
+
const { optionIndexes, text, notes } = entry;
|
|
80
|
+
if (optionIndexes !== undefined && (!Array.isArray(optionIndexes) || !optionIndexes.every(Number.isInteger))) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
code: "invalid_payload",
|
|
84
|
+
reason: `question ${i}: optionIndexes must be an array of integers`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (text !== undefined && typeof text !== "string") {
|
|
88
|
+
return { ok: false, code: "invalid_payload", reason: `question ${i}: text must be a string` };
|
|
89
|
+
}
|
|
90
|
+
if (notes !== undefined && typeof notes !== "string") {
|
|
91
|
+
return { ok: false, code: "invalid_payload", reason: `question ${i}: notes must be a string` };
|
|
92
|
+
}
|
|
93
|
+
const invalid = (reason) => ({
|
|
94
|
+
ok: false,
|
|
95
|
+
code: "invalid_answer",
|
|
96
|
+
reason: `question ${i}: ${reason}`
|
|
97
|
+
});
|
|
98
|
+
const picked = optionIndexes ?? [];
|
|
99
|
+
const out = picked.find((n) => n < 0 || n >= q.options.length);
|
|
100
|
+
if (out !== undefined)
|
|
101
|
+
return invalid(`option index ${out} out of range (0..${q.options.length - 1})`);
|
|
102
|
+
if (new Set(picked).size !== picked.length)
|
|
103
|
+
return invalid("option index repeated");
|
|
104
|
+
if (text !== undefined && text.trim().length === 0)
|
|
105
|
+
return invalid("text is blank");
|
|
106
|
+
if (picked.length === 0 && text === undefined)
|
|
107
|
+
return invalid("needs optionIndexes or text");
|
|
108
|
+
const base = { questionIndex: i, question: q.question };
|
|
109
|
+
const note = noteOf(notes);
|
|
110
|
+
const withNote = note ? { notes: note } : {};
|
|
111
|
+
if (q.multiSelect) {
|
|
112
|
+
const selected = q.options.filter((_o, n) => picked.includes(n)).map((o) => o.label);
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
answer: { ...base, kind: "multi", answer: text ?? null, selected, ...withNote }
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (picked.length > 0 && text !== undefined)
|
|
119
|
+
return invalid("single-select takes optionIndexes or text, not both");
|
|
120
|
+
if (picked.length > 1)
|
|
121
|
+
return invalid(`single-select takes one option, got ${picked.length}`);
|
|
122
|
+
if (text !== undefined)
|
|
123
|
+
return { ok: true, answer: { ...base, kind: "custom", answer: text, ...withNote } };
|
|
124
|
+
const option = q.options[picked[0]];
|
|
125
|
+
const withPreview = option.preview && option.preview.length > 0 ? { preview: option.preview } : {};
|
|
126
|
+
return { ok: true, answer: { ...base, kind: "option", answer: option.label, ...withPreview, ...withNote } };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// answer-channel.ts
|
|
130
|
+
class OpenQuestionnaire {
|
|
131
|
+
toolCallId;
|
|
132
|
+
questions;
|
|
133
|
+
host;
|
|
134
|
+
result;
|
|
135
|
+
deliver;
|
|
136
|
+
resolveEarly;
|
|
137
|
+
answeredEarly = new Promise((resolve) => {
|
|
138
|
+
this.resolveEarly = resolve;
|
|
139
|
+
});
|
|
140
|
+
constructor(toolCallId, questions, host) {
|
|
141
|
+
this.toolCallId = toolCallId;
|
|
142
|
+
this.questions = questions;
|
|
143
|
+
this.host = host;
|
|
144
|
+
}
|
|
145
|
+
get settled() {
|
|
146
|
+
return this.result;
|
|
147
|
+
}
|
|
148
|
+
attach(done) {
|
|
149
|
+
this.deliver = done;
|
|
150
|
+
}
|
|
151
|
+
settle(result) {
|
|
152
|
+
if (this.result)
|
|
153
|
+
return false;
|
|
154
|
+
this.result = result;
|
|
155
|
+
this.resolveEarly(result);
|
|
156
|
+
this.deliver?.(result);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
161
|
+
function registerAnswerChannel(pi) {
|
|
162
|
+
const open = new Map;
|
|
163
|
+
pi.events.on(ASK_USER_ANSWER_EVENT2, (data) => {
|
|
164
|
+
const payload = isRecord2(data) ? data : {};
|
|
165
|
+
const requestId = typeof payload.requestId === "string" ? payload.requestId : undefined;
|
|
166
|
+
const toolCallId = typeof payload.toolCallId === "string" ? payload.toolCallId : undefined;
|
|
167
|
+
const reply = (code, reason) => {
|
|
168
|
+
const verdict = {
|
|
169
|
+
ok: code === undefined,
|
|
170
|
+
...code ? { code, reason } : {},
|
|
171
|
+
...requestId !== undefined ? { requestId } : {},
|
|
172
|
+
...toolCallId !== undefined ? { toolCallId } : {}
|
|
173
|
+
};
|
|
174
|
+
pi.events.emit(ASK_USER_ANSWER_RESULT_EVENT2, verdict);
|
|
175
|
+
};
|
|
176
|
+
if (!isRecord2(data))
|
|
177
|
+
return reply("invalid_payload", "payload must be an object");
|
|
178
|
+
if (payload.requestId !== undefined && requestId === undefined) {
|
|
179
|
+
return reply("invalid_payload", "requestId must be a string");
|
|
180
|
+
}
|
|
181
|
+
if (toolCallId === undefined)
|
|
182
|
+
return reply("invalid_payload", "toolCallId must be a string");
|
|
183
|
+
const target = open.get(toolCallId);
|
|
184
|
+
if (!target || target.settled)
|
|
185
|
+
return reply("not_found", `no open questionnaire for tool call ${toolCallId}`);
|
|
186
|
+
if (target.host === "rpc")
|
|
187
|
+
return reply("unsupported_host", "this host shows the questions in its own dialogs");
|
|
188
|
+
const outcome = resolveExternalAnswer(target.questions, payload);
|
|
189
|
+
if (!outcome.ok)
|
|
190
|
+
return reply(outcome.code, outcome.reason);
|
|
191
|
+
if (!target.settle(outcome.result))
|
|
192
|
+
return reply("not_found", `tool call ${toolCallId} is already answered`);
|
|
193
|
+
reply();
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
open(toolCallId, questions, host) {
|
|
197
|
+
const questionnaire = new OpenQuestionnaire(toolCallId, questions, host);
|
|
198
|
+
open.set(toolCallId, questionnaire);
|
|
199
|
+
return questionnaire;
|
|
200
|
+
},
|
|
201
|
+
close(questionnaire) {
|
|
202
|
+
if (open.get(questionnaire.toolCallId) === questionnaire)
|
|
203
|
+
open.delete(questionnaire.toolCallId);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
22
208
|
// rpc-fallback.ts
|
|
23
|
-
var MULTI_SELECT_INSTRUCTIONS = 'Enter the numbers of all that apply, comma-separated (e.g. "1,3")
|
|
209
|
+
var MULTI_SELECT_INSTRUCTIONS = 'Enter the numbers of all that apply, comma-separated (e.g. "1,3"). Add "; custom answer" to include your own text, or type only custom text.';
|
|
24
210
|
var CUSTOM_ANSWER_TITLE = "Type your answer:";
|
|
25
|
-
var MULTI_SELECT_PLACEHOLDER = "1,3";
|
|
211
|
+
var MULTI_SELECT_PLACEHOLDER = "1,3; custom answer";
|
|
26
212
|
var MAX_PREVIEW_CHARS = 600;
|
|
27
213
|
function hasDialogUI(ui) {
|
|
28
214
|
const u = ui;
|
|
@@ -62,9 +248,12 @@ async function askSingleSelect(ui, q, questionIndex, header) {
|
|
|
62
248
|
const chosen = await ui.select(`${header}${q.question}${buildPreviewBlock(q)}`, options);
|
|
63
249
|
if (chosen == null)
|
|
64
250
|
return;
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
251
|
+
const reply = chosen.trim();
|
|
252
|
+
if (reply.length === 0)
|
|
67
253
|
return;
|
|
254
|
+
const idx = resolveSingleSelectReply(chosen, reply, options, q);
|
|
255
|
+
if (idx === undefined)
|
|
256
|
+
return { questionIndex, question: q.question, kind: "custom", answer: reply };
|
|
68
257
|
if (idx < q.options.length) {
|
|
69
258
|
const o = q.options[idx];
|
|
70
259
|
return {
|
|
@@ -77,11 +266,23 @@ async function askSingleSelect(ui, q, questionIndex, header) {
|
|
|
77
266
|
}
|
|
78
267
|
const typed = await ui.input(`${header}${q.question}
|
|
79
268
|
|
|
269
|
+
${q.options.map(formatOptionLine).join(`
|
|
270
|
+
`)}
|
|
271
|
+
|
|
80
272
|
${t("rpc.custom_answer_title", CUSTOM_ANSWER_TITLE)}`, "");
|
|
81
273
|
if (typed == null)
|
|
82
274
|
return;
|
|
83
275
|
return { questionIndex, question: q.question, kind: "custom", answer: typed };
|
|
84
276
|
}
|
|
277
|
+
function resolveSingleSelectReply(chosen, reply, lines, q) {
|
|
278
|
+
const line = lines.indexOf(chosen);
|
|
279
|
+
if (line >= 0)
|
|
280
|
+
return line;
|
|
281
|
+
if (/^\d+\.?$/.test(reply))
|
|
282
|
+
return parseIndex(reply, lines.length) ?? undefined;
|
|
283
|
+
const label = q.options.findIndex((o) => o.label === reply);
|
|
284
|
+
return label >= 0 ? label : undefined;
|
|
285
|
+
}
|
|
85
286
|
async function askMultiSelect(ui, q, questionIndex, header) {
|
|
86
287
|
const list = q.options.map(formatOptionLine).join(`
|
|
87
288
|
`);
|
|
@@ -96,18 +297,99 @@ ${t("rpc.multi_instructions", MULTI_SELECT_INSTRUCTIONS)}`, MULTI_SELECT_PLACEHO
|
|
|
96
297
|
if (trimmed.length === 0) {
|
|
97
298
|
return { questionIndex, question: q.question, kind: "multi", answer: null, selected: [] };
|
|
98
299
|
}
|
|
99
|
-
const
|
|
300
|
+
const [selectionPart = "", ...customParts] = trimmed.split(";");
|
|
301
|
+
const selection = selectionPart.trim();
|
|
302
|
+
const custom = customParts.join(";").trim();
|
|
303
|
+
const tokens = selection.split(/[,\s]+/).filter((tok) => tok.length > 0);
|
|
100
304
|
const indices = tokens.map((tok) => /^\d+\.?$/.test(tok) ? parseIndex(tok, q.options.length) : null);
|
|
101
|
-
if (indices.every((i) => i != null)) {
|
|
305
|
+
if (selection.length === 0 || indices.every((i) => i != null)) {
|
|
102
306
|
const selected = [];
|
|
103
307
|
for (const i of indices) {
|
|
308
|
+
if (i == null)
|
|
309
|
+
continue;
|
|
104
310
|
const label = q.options[i].label;
|
|
105
311
|
if (!selected.includes(label))
|
|
106
312
|
selected.push(label);
|
|
107
313
|
}
|
|
108
|
-
return {
|
|
314
|
+
return {
|
|
315
|
+
questionIndex,
|
|
316
|
+
question: q.question,
|
|
317
|
+
kind: "multi",
|
|
318
|
+
answer: custom.length > 0 ? custom : null,
|
|
319
|
+
selected
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
return { questionIndex, question: q.question, kind: "multi", answer: trimmed, selected: [] };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// presentation.ts
|
|
326
|
+
var INLINE_ROW_RATIO = 0.7;
|
|
327
|
+
var INLINE_MIN_ROWS = 12;
|
|
328
|
+
var fullRowBudget = (terminalRows) => terminalRows;
|
|
329
|
+
var HOST_ALLOCATED_ROWS = Symbol.for("rpiv-ask-user-question:host-allocated-rows");
|
|
330
|
+
function hostAllocatesRows(terminal) {
|
|
331
|
+
return typeof terminal === "object" && terminal !== null && terminal[HOST_ALLOCATED_ROWS] === true;
|
|
332
|
+
}
|
|
333
|
+
function rowBudgetForHost(terminal, budget) {
|
|
334
|
+
return hostAllocatesRows(terminal) ? fullRowBudget : budget;
|
|
335
|
+
}
|
|
336
|
+
var YIELD_TO_OVERLAY = Object.freeze({
|
|
337
|
+
answers: [],
|
|
338
|
+
cancelled: true
|
|
339
|
+
});
|
|
340
|
+
function mustYieldInlineSlot(tui) {
|
|
341
|
+
if (hostAllocatesRows(tui.terminal))
|
|
342
|
+
return false;
|
|
343
|
+
const probe = overlayProbe(tui);
|
|
344
|
+
if (probe)
|
|
345
|
+
return probe() !== undefined;
|
|
346
|
+
return typeof tui.hasOverlay === "function" && tui.hasOverlay();
|
|
347
|
+
}
|
|
348
|
+
function overlayProbe(tui) {
|
|
349
|
+
const { getTopmostVisibleOverlay } = tui;
|
|
350
|
+
return typeof getTopmostVisibleOverlay === "function" ? () => getTopmostVisibleOverlay.call(tui) : undefined;
|
|
351
|
+
}
|
|
352
|
+
function topOverlayComponent(tui) {
|
|
353
|
+
let top;
|
|
354
|
+
try {
|
|
355
|
+
top = overlayProbe(tui)?.()?.component;
|
|
356
|
+
} catch {
|
|
357
|
+
return;
|
|
109
358
|
}
|
|
110
|
-
return
|
|
359
|
+
return typeof top === "object" && top !== null ? top : undefined;
|
|
360
|
+
}
|
|
361
|
+
function focusOverlayComponent(tui, component) {
|
|
362
|
+
const { setFocus } = tui;
|
|
363
|
+
if (typeof setFocus === "function")
|
|
364
|
+
setFocus.call(tui, component);
|
|
365
|
+
}
|
|
366
|
+
var inlineRowBudget = (terminalRows) => Math.min(terminalRows, Math.max(INLINE_MIN_ROWS, Math.floor(terminalRows * INLINE_ROW_RATIO)));
|
|
367
|
+
var inlineSlotHeld = false;
|
|
368
|
+
function claimInlineSlot() {
|
|
369
|
+
if (inlineSlotHeld)
|
|
370
|
+
return;
|
|
371
|
+
inlineSlotHeld = true;
|
|
372
|
+
let released = false;
|
|
373
|
+
return () => {
|
|
374
|
+
if (released)
|
|
375
|
+
return;
|
|
376
|
+
released = true;
|
|
377
|
+
inlineSlotHeld = false;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
var OVERLAY_PRESENTATION = Object.freeze({
|
|
381
|
+
overlay: true,
|
|
382
|
+
rowBudget: fullRowBudget,
|
|
383
|
+
hideOnCollapse: true
|
|
384
|
+
});
|
|
385
|
+
var STACKED_OVERLAY_PRESENTATION = Object.freeze({
|
|
386
|
+
overlay: true,
|
|
387
|
+
rowBudget: fullRowBudget,
|
|
388
|
+
hideOnCollapse: false
|
|
389
|
+
});
|
|
390
|
+
function resolvePresentation() {
|
|
391
|
+
const release = claimInlineSlot();
|
|
392
|
+
return release ? { overlay: false, rowBudget: inlineRowBudget, release } : OVERLAY_PRESENTATION;
|
|
111
393
|
}
|
|
112
394
|
|
|
113
395
|
// tool/normalize-params.ts
|
|
@@ -133,6 +415,39 @@ function normalizeQuestionParams(params) {
|
|
|
133
415
|
}))
|
|
134
416
|
};
|
|
135
417
|
}
|
|
418
|
+
function prepareQuestionArguments(args) {
|
|
419
|
+
if (!isJsonRecord(args))
|
|
420
|
+
return args;
|
|
421
|
+
const questions = decodeArray(args.questions);
|
|
422
|
+
if (!questions)
|
|
423
|
+
return args;
|
|
424
|
+
const decoded = questions.map((q) => {
|
|
425
|
+
const question = decodeObject(q);
|
|
426
|
+
if (!question)
|
|
427
|
+
return q;
|
|
428
|
+
const options = decodeArray(question.options);
|
|
429
|
+
return options ? { ...question, options: options.map((o) => decodeObject(o) ?? o) } : question;
|
|
430
|
+
});
|
|
431
|
+
return { ...args, questions: decoded };
|
|
432
|
+
}
|
|
433
|
+
function isJsonRecord(value) {
|
|
434
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
435
|
+
}
|
|
436
|
+
function decodeArray(value) {
|
|
437
|
+
const parsed = typeof value === "string" ? parseJson(value) : value;
|
|
438
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
439
|
+
}
|
|
440
|
+
function decodeObject(value) {
|
|
441
|
+
const parsed = typeof value === "string" ? parseJson(value) : value;
|
|
442
|
+
return isJsonRecord(parsed) ? parsed : undefined;
|
|
443
|
+
}
|
|
444
|
+
function parseJson(text) {
|
|
445
|
+
try {
|
|
446
|
+
return JSON.parse(text);
|
|
447
|
+
} catch {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
136
451
|
|
|
137
452
|
// tool/response-envelope.ts
|
|
138
453
|
var DECLINE_MESSAGE = "User declined to answer questions";
|
|
@@ -147,18 +462,27 @@ function buildQuestionnaireResponse(result, params) {
|
|
|
147
462
|
});
|
|
148
463
|
}
|
|
149
464
|
const segments = [];
|
|
465
|
+
const unanswered = [];
|
|
150
466
|
for (let i = 0;i < params.questions.length; i++) {
|
|
151
467
|
const a = result.answers.find((x) => x.questionIndex === i);
|
|
152
468
|
if (a)
|
|
153
469
|
segments.push(buildAnswerSegment(a));
|
|
470
|
+
else
|
|
471
|
+
unanswered.push(i);
|
|
154
472
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
if (segments.length === 0) {
|
|
473
|
+
const hasGlobalNote = result.globalNote !== undefined && result.globalNote.length > 0;
|
|
474
|
+
if (segments.length === 0 && !hasGlobalNote) {
|
|
159
475
|
return buildToolResult(DECLINE_MESSAGE, { answers: result.answers, cancelled: true });
|
|
160
476
|
}
|
|
161
|
-
|
|
477
|
+
if (unanswered.length > 0)
|
|
478
|
+
segments.push(buildUnansweredSegment(unanswered, params));
|
|
479
|
+
if (hasGlobalNote)
|
|
480
|
+
segments.push(`global note: ${result.globalNote}.`);
|
|
481
|
+
const details = unanswered.length > 0 ? { ...result, unanswered } : result;
|
|
482
|
+
return buildToolResult(`${ENVELOPE_PREFIX} ${segments.join(" ")} ${ENVELOPE_SUFFIX}`, details);
|
|
483
|
+
}
|
|
484
|
+
function buildUnansweredSegment(indexes, params) {
|
|
485
|
+
return `unanswered: ${indexes.map((i) => `"${params.questions[i]?.question}"`).join(", ")}.`;
|
|
162
486
|
}
|
|
163
487
|
function buildAnswerSegment(a) {
|
|
164
488
|
const parts = [`"${a.question}"="${formatAnswerScalar(a, "envelope")}"`];
|
|
@@ -221,6 +545,12 @@ var QuestionsSchema = Type.Array(QuestionSchema, {
|
|
|
221
545
|
var QuestionParamsSchema = Type.Object({
|
|
222
546
|
questions: QuestionsSchema
|
|
223
547
|
});
|
|
548
|
+
function isQuestionnaireResult(value) {
|
|
549
|
+
if (!value || typeof value !== "object")
|
|
550
|
+
return false;
|
|
551
|
+
const v = value;
|
|
552
|
+
return Array.isArray(v.answers) && typeof v.cancelled === "boolean";
|
|
553
|
+
}
|
|
224
554
|
|
|
225
555
|
// tool/validate-questionnaire.ts
|
|
226
556
|
var ERROR_NO_QUESTIONS = "Error: At least one question is required";
|
|
@@ -266,9 +596,99 @@ function validateQuestionnaire(typed) {
|
|
|
266
596
|
return { ok: true };
|
|
267
597
|
}
|
|
268
598
|
|
|
599
|
+
// view/transcript.ts
|
|
600
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
601
|
+
var INDENT = " ";
|
|
602
|
+
var COLUMN_GAP = " ";
|
|
603
|
+
var RECOMMENDED_SUFFIX = /\s*\(Recommended\)$/;
|
|
604
|
+
|
|
605
|
+
class WidthLines {
|
|
606
|
+
layout;
|
|
607
|
+
constructor(layout) {
|
|
608
|
+
this.layout = layout;
|
|
609
|
+
}
|
|
610
|
+
render(width) {
|
|
611
|
+
return this.layout(width);
|
|
612
|
+
}
|
|
613
|
+
invalidate() {}
|
|
614
|
+
}
|
|
615
|
+
function renderAskCall(args, theme) {
|
|
616
|
+
const headers = (args?.questions ?? []).map((q) => q?.header).filter(Boolean);
|
|
617
|
+
const title = theme.fg("toolTitle", theme.bold("Ask"));
|
|
618
|
+
const text = headers.length > 0 ? `${title} ${theme.fg("accent", headers.join(" · "))}` : title;
|
|
619
|
+
return new WidthLines((width) => [truncateToWidth(text, width, "…")]);
|
|
620
|
+
}
|
|
621
|
+
function renderAskResult(result, args, theme, options) {
|
|
622
|
+
const details = isQuestionnaireResult(result.details) ? result.details : undefined;
|
|
623
|
+
if (options.isError || details?.error) {
|
|
624
|
+
return new WidthLines(() => [theme.fg("error", `✗ ${firstTextLine(result)}`)]);
|
|
625
|
+
}
|
|
626
|
+
if (!details)
|
|
627
|
+
return new WidthLines(() => [theme.fg("success", "✓")]);
|
|
628
|
+
if (details.cancelled) {
|
|
629
|
+
return new WidthLines(() => [theme.fg("dim", `✗ ${t("transcript.no_answer", "no answer")}`)]);
|
|
630
|
+
}
|
|
631
|
+
const questions = args?.questions ?? [];
|
|
632
|
+
return new WidthLines((width) => questions.length > 1 ? tableLines(details, args, theme, options.expanded, width) : singleLines(details, questions[0]?.question, theme, options.expanded, width));
|
|
633
|
+
}
|
|
634
|
+
function singleLines(details, questionText, theme, expanded, width) {
|
|
635
|
+
const answer = details.answers[0];
|
|
636
|
+
const lines = hanging(`${theme.fg("success", "✓")} `, answerText(answer, theme), width);
|
|
637
|
+
lines.push(...detailLines(answer, expanded ? questionText : undefined, INDENT, theme, width));
|
|
638
|
+
return [...lines, ...globalNoteLines(details, theme, width)];
|
|
639
|
+
}
|
|
640
|
+
function tableLines(details, args, theme, expanded, width) {
|
|
641
|
+
const headerWidth = Math.max(...args.questions.map((q) => visibleWidth(q.header)));
|
|
642
|
+
const answerIndent = " ".repeat(INDENT.length + headerWidth + COLUMN_GAP.length);
|
|
643
|
+
const lines = args.questions.flatMap((q, i) => {
|
|
644
|
+
const answer = details.answers.find((a) => a.questionIndex === i);
|
|
645
|
+
const header = q.header + " ".repeat(headerWidth - visibleWidth(q.header));
|
|
646
|
+
const row = hanging(`${INDENT}${theme.fg("muted", header)}${COLUMN_GAP}`, answerText(answer, theme), width);
|
|
647
|
+
return [...row, ...detailLines(answer, expanded ? q.question : undefined, answerIndent, theme, width)];
|
|
648
|
+
});
|
|
649
|
+
return [...lines, ...globalNoteLines(details, theme, width)];
|
|
650
|
+
}
|
|
651
|
+
function detailLines(answer, questionText, indent, theme, width) {
|
|
652
|
+
const lines = [];
|
|
653
|
+
if (questionText)
|
|
654
|
+
lines.push(...hanging(indent, theme.fg("dim", questionText), width));
|
|
655
|
+
if (questionText && answer?.preview) {
|
|
656
|
+
for (const line of stripFenceMarkers(answer.preview.split(`
|
|
657
|
+
`))) {
|
|
658
|
+
lines.push(truncateToWidth(`${indent}${theme.fg("dim", `│ ${line}`)}`, width, "…"));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (answer?.notes)
|
|
662
|
+
lines.push(...hanging(indent, theme.fg("dim", `✎ ${answer.notes}`), width));
|
|
663
|
+
return lines;
|
|
664
|
+
}
|
|
665
|
+
function globalNoteLines(details, theme, width) {
|
|
666
|
+
return details.globalNote ? hanging(INDENT, theme.fg("dim", `✎ ${details.globalNote}`), width) : [];
|
|
667
|
+
}
|
|
668
|
+
function answerText(answer, theme) {
|
|
669
|
+
if (!answer)
|
|
670
|
+
return theme.fg("dim", t("transcript.not_answered", "not answered"));
|
|
671
|
+
const typed = answer.answer ? `"${answer.answer}"` : undefined;
|
|
672
|
+
const picks = answer.kind === "option" ? [label(answer.answer ?? "")] : answer.kind === "custom" ? [typed] : [...(answer.selected ?? []).map(label), typed];
|
|
673
|
+
return theme.fg("toolOutput", picks.filter(Boolean).join(", ") || NO_INPUT_PLACEHOLDER);
|
|
674
|
+
}
|
|
675
|
+
function label(text) {
|
|
676
|
+
return text.replace(RECOMMENDED_SUFFIX, "");
|
|
677
|
+
}
|
|
678
|
+
function hanging(prefix, text, width) {
|
|
679
|
+
const indent = visibleWidth(prefix);
|
|
680
|
+
return wrapTextWithAnsi(text, Math.max(1, width - indent)).map((line, i) => (i === 0 ? prefix : " ".repeat(indent)) + line);
|
|
681
|
+
}
|
|
682
|
+
function firstTextLine(result) {
|
|
683
|
+
const text = result.content.find((c) => c.type === "text")?.text ?? "";
|
|
684
|
+
return text.split(`
|
|
685
|
+
`)[0] ?? "";
|
|
686
|
+
}
|
|
687
|
+
|
|
269
688
|
// ask-user-question.ts
|
|
270
|
-
function emitAskUserPromptEvent(pi, params) {
|
|
689
|
+
function emitAskUserPromptEvent(pi, params, toolCallId) {
|
|
271
690
|
const payload = {
|
|
691
|
+
toolCallId,
|
|
272
692
|
questions: params.questions.map((q) => ({
|
|
273
693
|
question: q.question,
|
|
274
694
|
header: q.header,
|
|
@@ -282,20 +702,20 @@ function emitAskUserPromptEvent(pi, params) {
|
|
|
282
702
|
};
|
|
283
703
|
pi.events.emit(ASK_USER_PROMPT_EVENT2, payload);
|
|
284
704
|
}
|
|
285
|
-
function emitAskUserBlockedEvent(pi, active) {
|
|
286
|
-
const payload = { active };
|
|
705
|
+
function emitAskUserBlockedEvent(pi, active, toolCallId) {
|
|
706
|
+
const payload = { active, toolCallId };
|
|
287
707
|
pi.events.emit(ASK_USER_BLOCKED_EVENT2, payload);
|
|
288
708
|
}
|
|
289
709
|
function rejectWithoutUi() {
|
|
290
710
|
return buildToolResult(ERROR_NO_UI, { answers: [], cancelled: true, error: "no_ui" });
|
|
291
711
|
}
|
|
292
|
-
async function runRpcPath(pi, ui, typed) {
|
|
293
|
-
emitAskUserBlockedEvent(pi, true);
|
|
712
|
+
async function runRpcPath(pi, ui, typed, toolCallId) {
|
|
713
|
+
emitAskUserBlockedEvent(pi, true, toolCallId);
|
|
294
714
|
try {
|
|
295
715
|
emitTerminalAttention();
|
|
296
716
|
return buildQuestionnaireResponse(await runRpcQuestionnaire(ui, typed), typed);
|
|
297
717
|
} finally {
|
|
298
|
-
emitAskUserBlockedEvent(pi, false);
|
|
718
|
+
emitAskUserBlockedEvent(pi, false, toolCallId);
|
|
299
719
|
}
|
|
300
720
|
}
|
|
301
721
|
var ASK_USER_QUESTION_TOOL_NAME = "ask_user_question";
|
|
@@ -314,7 +734,7 @@ var PREWARM_DELAY_MS = 2000;
|
|
|
314
734
|
async function loadQuestionnaireSession() {
|
|
315
735
|
let mod;
|
|
316
736
|
try {
|
|
317
|
-
mod = await import("./chunks/questionnaire-session-
|
|
737
|
+
mod = await import("./chunks/questionnaire-session-m053x4ka.js");
|
|
318
738
|
} catch (e) {
|
|
319
739
|
const cause = e instanceof Error ? e.message : String(e);
|
|
320
740
|
return { ok: false, error: "session_load_failed", message: `${ERROR_SESSION_LOAD_FAILED} (cause: ${cause})` };
|
|
@@ -352,14 +772,35 @@ function registerCollapseKeyListener(ctx, collapseKey, sessionRef, overlayHandle
|
|
|
352
772
|
});
|
|
353
773
|
}
|
|
354
774
|
function makeSessionFactory(config) {
|
|
355
|
-
const {
|
|
775
|
+
const {
|
|
776
|
+
ctx,
|
|
777
|
+
typed,
|
|
778
|
+
itemsByTab,
|
|
779
|
+
collapseKey,
|
|
780
|
+
notesKey,
|
|
781
|
+
canReopenWhileHidden,
|
|
782
|
+
presentation,
|
|
783
|
+
open,
|
|
784
|
+
sessionRef,
|
|
785
|
+
overlayHandleRef,
|
|
786
|
+
Session
|
|
787
|
+
} = config;
|
|
356
788
|
return (tui, theme, keybindings, done) => {
|
|
789
|
+
if (!presentation.overlay && mustYieldInlineSlot(tui)) {
|
|
790
|
+
done(YIELD_TO_OVERLAY);
|
|
791
|
+
return INERT_COMPONENT;
|
|
792
|
+
}
|
|
793
|
+
if (open.settled) {
|
|
794
|
+
if (!presentation.overlay)
|
|
795
|
+
done(open.settled);
|
|
796
|
+
return INERT_COMPONENT;
|
|
797
|
+
}
|
|
357
798
|
const session = new Session({
|
|
358
799
|
tui,
|
|
359
800
|
theme,
|
|
360
801
|
params: typed,
|
|
361
802
|
itemsByTab,
|
|
362
|
-
done,
|
|
803
|
+
done: (result) => open.settle(result),
|
|
363
804
|
keybindings,
|
|
364
805
|
editInput: async (value) => {
|
|
365
806
|
try {
|
|
@@ -380,12 +821,29 @@ function makeSessionFactory(config) {
|
|
|
380
821
|
}
|
|
381
822
|
},
|
|
382
823
|
collapseKey,
|
|
383
|
-
|
|
824
|
+
notesKey,
|
|
825
|
+
canReopenWhileHidden,
|
|
826
|
+
rowBudget: rowBudgetForHost(tui.terminal, presentation.rowBudget)
|
|
384
827
|
});
|
|
385
828
|
sessionRef.current = session;
|
|
829
|
+
open.attach((result) => {
|
|
830
|
+
if (presentation.overlay) {
|
|
831
|
+
overlayHandleRef.current?.hide();
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
done(result);
|
|
835
|
+
const top = topOverlayComponent(tui);
|
|
836
|
+
if (top !== undefined && top !== session.component)
|
|
837
|
+
focusOverlayComponent(tui, top);
|
|
838
|
+
});
|
|
386
839
|
return session.component;
|
|
387
840
|
};
|
|
388
841
|
}
|
|
842
|
+
var INERT_COMPONENT = {
|
|
843
|
+
render: () => [],
|
|
844
|
+
invalidate: () => {},
|
|
845
|
+
handleInput: () => {}
|
|
846
|
+
};
|
|
389
847
|
async function resolveUndefinedResult(ctx, typed) {
|
|
390
848
|
if (hasDialogUI(ctx.ui)) {
|
|
391
849
|
return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
|
|
@@ -437,6 +895,7 @@ Use the optional \`preview\` field on options when presenting concrete artifacts
|
|
|
437
895
|
Preview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect).`;
|
|
438
896
|
function registerAskUserQuestionTool(pi) {
|
|
439
897
|
const guidance = validateGuidanceFields(loadConfig().guidance);
|
|
898
|
+
const answers = registerAnswerChannel(pi);
|
|
440
899
|
pi.registerTool({
|
|
441
900
|
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
442
901
|
label: "Ask User Question",
|
|
@@ -444,7 +903,11 @@ function registerAskUserQuestionTool(pi) {
|
|
|
444
903
|
promptSnippet: guidance.promptSnippet ?? DEFAULT_PROMPT_SNIPPET,
|
|
445
904
|
promptGuidelines: guidance.promptGuidelines ?? DEFAULT_PROMPT_GUIDELINES,
|
|
446
905
|
parameters: QuestionParamsSchema,
|
|
447
|
-
|
|
906
|
+
prepareArguments: prepareQuestionArguments,
|
|
907
|
+
exposure: "model-only",
|
|
908
|
+
renderCall: (args, theme) => renderAskCall(args, theme),
|
|
909
|
+
renderResult: (result, { expanded }, theme, context) => renderAskResult(result, context?.args, theme, { expanded, isError: context?.isError ?? false }),
|
|
910
|
+
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
448
911
|
const typed = normalizeQuestionParams(params);
|
|
449
912
|
if (!ctx.hasUI)
|
|
450
913
|
return rejectWithoutUi();
|
|
@@ -456,57 +919,88 @@ function registerAskUserQuestionTool(pi) {
|
|
|
456
919
|
error: validation.error
|
|
457
920
|
});
|
|
458
921
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return runRpcPath(pi, ctx.ui, typed);
|
|
462
|
-
}
|
|
463
|
-
const itemsByTab = typed.questions.map((q) => buildItemsForQuestion(q));
|
|
464
|
-
const sessionLoad = await loadQuestionnaireSession();
|
|
465
|
-
if (!sessionLoad.ok) {
|
|
466
|
-
return buildToolResult(sessionLoad.message, { answers: [], cancelled: true, error: sessionLoad.error });
|
|
467
|
-
}
|
|
468
|
-
const { QuestionnaireSession } = sessionLoad.module;
|
|
469
|
-
const collapseKey = resolveCollapseKey(loadConfig());
|
|
470
|
-
const sessionRef = { current: null };
|
|
471
|
-
const overlayHandleRef = { current: undefined };
|
|
472
|
-
const removeOverlayInputListener = registerCollapseKeyListener(ctx, collapseKey, sessionRef, overlayHandleRef);
|
|
473
|
-
const canReopenWhileHidden = removeOverlayInputListener !== undefined;
|
|
474
|
-
emitAskUserBlockedEvent(pi, true);
|
|
922
|
+
const viaRpc = ctx.mode === "rpc" && hasDialogUI(ctx.ui);
|
|
923
|
+
const open = answers.open(toolCallId, typed.questions, viaRpc ? "rpc" : "tui");
|
|
475
924
|
try {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
ctx,
|
|
479
|
-
|
|
480
|
-
itemsByTab,
|
|
481
|
-
collapseKey,
|
|
482
|
-
canReopenWhileHidden,
|
|
483
|
-
sessionRef,
|
|
484
|
-
Session: QuestionnaireSession
|
|
485
|
-
}), {
|
|
486
|
-
overlay: true,
|
|
487
|
-
overlayOptions: {
|
|
488
|
-
anchor: "bottom-center",
|
|
489
|
-
width: "100%",
|
|
490
|
-
maxHeight: "100%",
|
|
491
|
-
margin: { left: 0, right: 0, bottom: 0 }
|
|
492
|
-
},
|
|
493
|
-
onHandle: (handle) => {
|
|
494
|
-
overlayHandleRef.current = handle;
|
|
495
|
-
sessionRef.current?.setOverlayHandle(handle);
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
if (result === undefined) {
|
|
499
|
-
return resolveUndefinedResult(ctx, typed);
|
|
500
|
-
}
|
|
501
|
-
return buildQuestionnaireResponse(result, typed);
|
|
925
|
+
emitAskUserPromptEvent(pi, typed, toolCallId);
|
|
926
|
+
if (viaRpc)
|
|
927
|
+
return await runRpcPath(pi, ctx.ui, typed, toolCallId);
|
|
928
|
+
return await runTuiPath(pi, ctx, typed, open, toolCallId);
|
|
502
929
|
} finally {
|
|
503
|
-
|
|
504
|
-
emitAskUserBlockedEvent(pi, false);
|
|
930
|
+
answers.close(open);
|
|
505
931
|
}
|
|
506
932
|
}
|
|
507
933
|
});
|
|
508
934
|
prewarmSessionGraph();
|
|
509
935
|
}
|
|
936
|
+
async function runTuiPath(pi, ctx, typed, open, toolCallId) {
|
|
937
|
+
const itemsByTab = typed.questions.map((q) => buildItemsForQuestion(q));
|
|
938
|
+
let presentation;
|
|
939
|
+
let removeOverlayInputListener;
|
|
940
|
+
emitAskUserBlockedEvent(pi, true, toolCallId);
|
|
941
|
+
try {
|
|
942
|
+
const sessionLoad = await loadQuestionnaireSession();
|
|
943
|
+
if (open.settled)
|
|
944
|
+
return buildQuestionnaireResponse(open.settled, typed);
|
|
945
|
+
if (!sessionLoad.ok) {
|
|
946
|
+
return buildToolResult(sessionLoad.message, { answers: [], cancelled: true, error: sessionLoad.error });
|
|
947
|
+
}
|
|
948
|
+
const { QuestionnaireSession } = sessionLoad.module;
|
|
949
|
+
const collapseKey = resolveCollapseKey(loadConfig());
|
|
950
|
+
const notesKey = resolveNotesKey(loadConfig());
|
|
951
|
+
const sessionRef = { current: null };
|
|
952
|
+
const overlayHandleRef = { current: undefined };
|
|
953
|
+
presentation = resolvePresentation();
|
|
954
|
+
removeOverlayInputListener = presentation.hideOnCollapse ? registerCollapseKeyListener(ctx, collapseKey, sessionRef, overlayHandleRef) : undefined;
|
|
955
|
+
const canReopenWhileHidden = removeOverlayInputListener !== undefined;
|
|
956
|
+
const mount = (p) => Promise.race([
|
|
957
|
+
ctx.ui.custom(makeSessionFactory({
|
|
958
|
+
ctx,
|
|
959
|
+
typed,
|
|
960
|
+
itemsByTab,
|
|
961
|
+
collapseKey,
|
|
962
|
+
notesKey,
|
|
963
|
+
canReopenWhileHidden,
|
|
964
|
+
presentation: p,
|
|
965
|
+
open,
|
|
966
|
+
sessionRef,
|
|
967
|
+
overlayHandleRef,
|
|
968
|
+
Session: QuestionnaireSession
|
|
969
|
+
}), p.overlay ? {
|
|
970
|
+
overlay: true,
|
|
971
|
+
overlayOptions: {
|
|
972
|
+
anchor: "bottom-center",
|
|
973
|
+
width: "100%",
|
|
974
|
+
maxHeight: "100%",
|
|
975
|
+
margin: { left: 0, right: 0, bottom: 0 }
|
|
976
|
+
},
|
|
977
|
+
onHandle: (handle) => {
|
|
978
|
+
overlayHandleRef.current = handle;
|
|
979
|
+
sessionRef.current?.setOverlayHandle(handle);
|
|
980
|
+
if (open.settled)
|
|
981
|
+
handle.hide();
|
|
982
|
+
}
|
|
983
|
+
} : { overlay: false }),
|
|
984
|
+
open.answeredEarly
|
|
985
|
+
]);
|
|
986
|
+
emitTerminalAttention();
|
|
987
|
+
let result = await mount(presentation);
|
|
988
|
+
if (result === YIELD_TO_OVERLAY) {
|
|
989
|
+
presentation.release?.();
|
|
990
|
+
presentation = STACKED_OVERLAY_PRESENTATION;
|
|
991
|
+
result = await mount(presentation);
|
|
992
|
+
}
|
|
993
|
+
if (result === undefined) {
|
|
994
|
+
open.host = "rpc";
|
|
995
|
+
return await resolveUndefinedResult(ctx, typed);
|
|
996
|
+
}
|
|
997
|
+
return buildQuestionnaireResponse(result, typed);
|
|
998
|
+
} finally {
|
|
999
|
+
presentation?.release?.();
|
|
1000
|
+
removeOverlayInputListener?.();
|
|
1001
|
+
emitAskUserBlockedEvent(pi, false, toolCallId);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
510
1004
|
|
|
511
1005
|
// reconcile.ts
|
|
512
1006
|
function reconcileAskUserQuestionTool(pi, ctx) {
|
|
@@ -532,6 +1026,8 @@ function pi_ask_user_default(pi) {
|
|
|
532
1026
|
registerAskUserQuestionReconciler(pi);
|
|
533
1027
|
}
|
|
534
1028
|
export {
|
|
1029
|
+
ASK_USER_ANSWER_EVENT2 as ASK_USER_ANSWER_EVENT,
|
|
1030
|
+
ASK_USER_ANSWER_RESULT_EVENT2 as ASK_USER_ANSWER_RESULT_EVENT,
|
|
535
1031
|
ASK_USER_BLOCKED_EVENT2 as ASK_USER_BLOCKED_EVENT,
|
|
536
1032
|
ASK_USER_PROMPT_EVENT2 as ASK_USER_PROMPT_EVENT,
|
|
537
1033
|
pi_ask_user_default as default
|