@astralyn/pi 0.82.5 → 0.82.7
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/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +4 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +11 -13
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +4 -22
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +32 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -5
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -41
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +42 -11
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +6 -7
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +17 -8
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +3 -3
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +3 -13
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +6 -36
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -4
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +7 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +11 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/extensions/deepwiki/render.d.ts.map +1 -1
- package/dist/extensions/deepwiki/render.js +34 -22
- package/dist/extensions/deepwiki/render.js.map +1 -1
- package/dist/extensions/plan/constants.d.ts +43 -6
- package/dist/extensions/plan/constants.d.ts.map +1 -1
- package/dist/extensions/plan/constants.js +51 -8
- package/dist/extensions/plan/constants.js.map +1 -1
- package/dist/extensions/plan/index.d.ts +12 -3
- package/dist/extensions/plan/index.d.ts.map +1 -1
- package/dist/extensions/plan/index.js +173 -35
- package/dist/extensions/plan/index.js.map +1 -1
- package/dist/extensions/plan/schema.d.ts +5 -0
- package/dist/extensions/plan/schema.d.ts.map +1 -1
- package/dist/extensions/plan/schema.js.map +1 -1
- package/dist/extensions/plan/state.d.ts +11 -2
- package/dist/extensions/plan/state.d.ts.map +1 -1
- package/dist/extensions/plan/state.js +31 -9
- package/dist/extensions/plan/state.js.map +1 -1
- package/dist/extensions/plan/storage.d.ts +11 -3
- package/dist/extensions/plan/storage.d.ts.map +1 -1
- package/dist/extensions/plan/storage.js +45 -20
- package/dist/extensions/plan/storage.js.map +1 -1
- package/dist/extensions/plan/view.d.ts +44 -0
- package/dist/extensions/plan/view.d.ts.map +1 -0
- package/dist/extensions/plan/view.js +149 -0
- package/dist/extensions/plan/view.js.map +1 -0
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +29 -10
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +7 -2
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/render.d.ts +4 -8
- package/dist/extensions/question/render.d.ts.map +1 -1
- package/dist/extensions/question/render.js +202 -29
- package/dist/extensions/question/render.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js +1 -0
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js +2 -2
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/types.d.ts +1 -0
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/question/types.js +1 -1
- package/dist/extensions/question/types.js.map +1 -1
- package/dist/extensions/router/stream.d.ts +1 -0
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js +75 -0
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +25 -4
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -0
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +1 -0
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +16 -0
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +352 -152
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +13 -9
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/retry.d.ts +17 -0
- package/dist/extensions/subagent/retry.d.ts.map +1 -0
- package/dist/extensions/subagent/retry.js +41 -0
- package/dist/extensions/subagent/retry.js.map +1 -0
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +37 -7
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +2 -2
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts +6 -0
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +46 -24
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +7 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +17 -6
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +22 -9
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts +0 -14
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js +13 -1
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +53 -2
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/schema.js +57 -8
- package/dist/extensions/todo/schema.js.map +1 -1
- package/dist/extensions/todo/state.d.ts +16 -5
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +874 -130
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts +16 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +428 -1
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +5 -0
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +13 -6
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +54 -17
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +6 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +23 -11
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +11 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +56 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +52 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +139 -18
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts +1 -0
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +5 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +10 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +55 -2
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +79 -19
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +3 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +8 -0
- package/dist/utils/paths.js.map +1 -1
- package/docs/bundled/README.md +1 -0
- package/docs/bundled/extensions/deepwiki.md +1 -1
- package/docs/bundled/extensions/plan.md +13 -10
- package/docs/bundled/extensions/question.md +7 -5
- package/docs/bundled/extensions/router.md +3 -4
- package/docs/bundled/extensions/statusline.md +0 -1
- package/docs/bundled/extensions/subagent.md +5 -4
- package/docs/bundled/extensions/todo.md +9 -7
- package/docs/bundled/tool-presentation.md +17 -16
- package/docs/extensions.md +34 -5
- package/docs/rpc.md +1 -1
- package/docs/usage.md +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +1 -1
- package/dist/utils/output-decoder.d.ts +0 -52
- package/dist/utils/output-decoder.d.ts.map +0 -1
- package/dist/utils/output-decoder.js +0 -134
- package/dist/utils/output-decoder.js.map +0 -1
|
@@ -1,47 +1,220 @@
|
|
|
1
1
|
import { Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { keyLabel } from "../../modes/interactive/components/keybinding-hints.js";
|
|
3
|
+
import { QUESTION_LIMITS } from "./limits.js";
|
|
2
4
|
import { answerScalar } from "./results.js";
|
|
5
|
+
const MAX_RENDERED_QUESTIONS = 4;
|
|
6
|
+
const MAX_RENDERED_ANSWERS = 4;
|
|
7
|
+
const MAX_RENDERED_SELECTIONS = 5;
|
|
8
|
+
const MAX_RENDERED_ANSWER_CHARS = 400;
|
|
9
|
+
const MAX_RENDERED_NOTE_CHARS = 400;
|
|
10
|
+
const MAX_RENDERED_ERROR_CHARS = 400;
|
|
11
|
+
const MAX_RENDERED_VALIDATION_SUMMARY_CHARS = 240;
|
|
12
|
+
const MAX_RENDERED_FALLBACK_CHARS = 1_000;
|
|
13
|
+
function safeRecord(value) {
|
|
14
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
15
|
+
? value
|
|
16
|
+
: undefined;
|
|
17
|
+
}
|
|
18
|
+
function safeValue(record, key) {
|
|
19
|
+
try {
|
|
20
|
+
return record[key];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function truncate(text, limit) {
|
|
27
|
+
return text.length > limit ? `${text.slice(0, Math.max(0, limit - 1))}…` : text;
|
|
28
|
+
}
|
|
29
|
+
function oneLine(value, limit) {
|
|
30
|
+
return typeof value === "string" ? truncate(value.replace(/\s+/gu, " ").trim(), limit) : "";
|
|
31
|
+
}
|
|
3
32
|
function questionsFromArgs(args) {
|
|
4
|
-
|
|
33
|
+
const record = safeRecord(args);
|
|
34
|
+
if (!record)
|
|
35
|
+
return [];
|
|
36
|
+
const rawQuestions = safeValue(record, "questions");
|
|
37
|
+
if (!Array.isArray(rawQuestions))
|
|
5
38
|
return [];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
39
|
+
const questions = [];
|
|
40
|
+
for (const rawQuestion of rawQuestions.slice(0, MAX_RENDERED_QUESTIONS)) {
|
|
41
|
+
const question = safeRecord(rawQuestion);
|
|
42
|
+
if (!question)
|
|
43
|
+
continue;
|
|
44
|
+
const rawHeader = safeValue(question, "header");
|
|
45
|
+
if (typeof rawHeader !== "string")
|
|
46
|
+
continue;
|
|
47
|
+
const header = oneLine(rawHeader, QUESTION_LIMITS.headerChars) || `Decision ${questions.length + 1}`;
|
|
48
|
+
questions.push({
|
|
49
|
+
header,
|
|
50
|
+
question: oneLine(safeValue(question, "question"), QUESTION_LIMITS.questionChars),
|
|
51
|
+
multiSelect: safeValue(question, "multiSelect") === true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return questions;
|
|
10
55
|
}
|
|
11
56
|
function resultFallback(result) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
57
|
+
if (!Array.isArray(result.content))
|
|
58
|
+
return "";
|
|
59
|
+
const blocks = [];
|
|
60
|
+
for (const rawBlock of result.content.slice(0, 8)) {
|
|
61
|
+
const block = safeRecord(rawBlock);
|
|
62
|
+
if (!block || safeValue(block, "type") !== "text")
|
|
63
|
+
continue;
|
|
64
|
+
const text = safeValue(block, "text");
|
|
65
|
+
if (typeof text === "string")
|
|
66
|
+
blocks.push(text);
|
|
67
|
+
}
|
|
68
|
+
return truncate(blocks.join("\n"), MAX_RENDERED_FALLBACK_CHARS);
|
|
69
|
+
}
|
|
70
|
+
function validationSummary(text) {
|
|
71
|
+
const match = /^Validation failed for tool\s+"question":\s*([\s\S]*?)(?:\r?\n\s*\r?\nReceived arguments:|$)/u.exec(text.trim());
|
|
72
|
+
if (!match)
|
|
73
|
+
return undefined;
|
|
74
|
+
const errors = match[1]
|
|
75
|
+
.split(/\r?\n/u)
|
|
76
|
+
.map((line) => line.trim().replace(/^-\s*/u, ""))
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
const firstError = oneLine(errors[0], MAX_RENDERED_VALIDATION_SUMMARY_CHARS) || "schema validation failed";
|
|
79
|
+
return {
|
|
80
|
+
summary: `Invalid arguments · ${firstError}`,
|
|
81
|
+
hiddenErrors: Math.max(0, errors.length - 1),
|
|
82
|
+
};
|
|
16
83
|
}
|
|
17
|
-
|
|
84
|
+
function renderFallbackResult(result, expanded, theme) {
|
|
85
|
+
const fallback = resultFallback(result);
|
|
86
|
+
if (!fallback)
|
|
87
|
+
return new Text("Question result unavailable", 0, 0);
|
|
88
|
+
const validation = validationSummary(fallback);
|
|
89
|
+
if (!validation || expanded)
|
|
90
|
+
return new Text(fallback, 0, 0);
|
|
91
|
+
const moreErrors = validation.hiddenErrors > 0 ? ` · +${validation.hiddenErrors} more` : "";
|
|
92
|
+
const expandKey = keyLabel("app.tools.expand");
|
|
93
|
+
const hint = theme.fg("muted", expandKey ? ` (${expandKey} to expand)` : " (more details available)");
|
|
94
|
+
return new Text(`${theme.fg("error", validation.summary)}${theme.fg("muted", moreErrors)}${hint}`, 0, 0);
|
|
95
|
+
}
|
|
96
|
+
function normalizeAnswer(value, index) {
|
|
97
|
+
const raw = safeRecord(value);
|
|
98
|
+
if (!raw)
|
|
99
|
+
return undefined;
|
|
100
|
+
const rawHeader = safeValue(raw, "header");
|
|
101
|
+
if (typeof rawHeader !== "string")
|
|
102
|
+
return undefined;
|
|
103
|
+
const header = oneLine(rawHeader, QUESTION_LIMITS.headerChars) || `Decision ${index + 1}`;
|
|
104
|
+
const kindValue = safeValue(raw, "kind");
|
|
105
|
+
const kind = kindValue === "multi" || kindValue === "custom" ? kindValue : "option";
|
|
106
|
+
const rawIndex = safeValue(raw, "questionIndex");
|
|
107
|
+
const questionIndex = typeof rawIndex === "number" && Number.isSafeInteger(rawIndex) && rawIndex >= 0 ? rawIndex : index;
|
|
108
|
+
const question = oneLine(safeValue(raw, "question"), QUESTION_LIMITS.questionChars);
|
|
109
|
+
const answer = oneLine(safeValue(raw, "answer"), MAX_RENDERED_ANSWER_CHARS);
|
|
110
|
+
const rawSelected = safeValue(raw, "selected");
|
|
111
|
+
const selected = Array.isArray(rawSelected)
|
|
112
|
+
? rawSelected
|
|
113
|
+
.slice(0, MAX_RENDERED_SELECTIONS)
|
|
114
|
+
.map((entry, selectedIndex) => {
|
|
115
|
+
if (typeof entry !== "string")
|
|
116
|
+
return "";
|
|
117
|
+
return oneLine(entry, MAX_RENDERED_ANSWER_CHARS) || `Option ${selectedIndex + 1}`;
|
|
118
|
+
})
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
: undefined;
|
|
121
|
+
const rawNotes = safeValue(raw, "notes");
|
|
122
|
+
const notes = Array.isArray(rawNotes)
|
|
123
|
+
? rawNotes
|
|
124
|
+
.slice(0, MAX_RENDERED_ANSWERS)
|
|
125
|
+
.map((entry) => {
|
|
126
|
+
const note = safeRecord(entry);
|
|
127
|
+
if (!note)
|
|
128
|
+
return undefined;
|
|
129
|
+
const option = oneLine(safeValue(note, "option"), QUESTION_LIMITS.optionLabelChars);
|
|
130
|
+
const text = oneLine(safeValue(note, "text"), MAX_RENDERED_NOTE_CHARS);
|
|
131
|
+
return option && text ? { option, text } : undefined;
|
|
132
|
+
})
|
|
133
|
+
.filter((note) => note !== undefined)
|
|
134
|
+
: undefined;
|
|
135
|
+
return {
|
|
136
|
+
questionIndex,
|
|
137
|
+
question,
|
|
138
|
+
header,
|
|
139
|
+
kind,
|
|
140
|
+
answer: answer || null,
|
|
141
|
+
...(kind === "multi" && selected?.length ? { selected } : {}),
|
|
142
|
+
...(notes?.length ? { notes } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function normalizeDetails(value) {
|
|
146
|
+
const raw = safeRecord(value);
|
|
147
|
+
if (!raw)
|
|
148
|
+
return undefined;
|
|
149
|
+
const outcome = safeValue(raw, "outcome");
|
|
150
|
+
if (outcome !== "answered" && outcome !== "cancelled" && outcome !== "needs_clarification" && outcome !== "error") {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const rawAnswers = safeValue(raw, "answers");
|
|
154
|
+
const answers = Array.isArray(rawAnswers)
|
|
155
|
+
? rawAnswers
|
|
156
|
+
.slice(0, MAX_RENDERED_ANSWERS)
|
|
157
|
+
.map((answer, index) => normalizeAnswer(answer, index))
|
|
158
|
+
.filter((answer) => answer !== undefined)
|
|
159
|
+
: [];
|
|
160
|
+
const message = oneLine(safeValue(raw, "message"), MAX_RENDERED_ERROR_CHARS);
|
|
161
|
+
return { answers, outcome, ...(message ? { message } : {}) };
|
|
162
|
+
}
|
|
163
|
+
function errorMessage(result, details) {
|
|
164
|
+
if (details.message)
|
|
165
|
+
return details.message;
|
|
166
|
+
const fallback = resultFallback(result).trim();
|
|
167
|
+
const match = /^Question tool error \([^)]*\):\s*([\s\S]+)$/u.exec(fallback);
|
|
168
|
+
return truncate(match?.[1]?.trim() || "The question tool could not continue", MAX_RENDERED_ERROR_CHARS);
|
|
169
|
+
}
|
|
170
|
+
function answerLines(answers, theme) {
|
|
171
|
+
return answers.map((answer) => {
|
|
172
|
+
const notes = answer.notes?.length
|
|
173
|
+
? `\n${answer.notes.map((note) => theme.fg("muted", ` Note for ${note.option}: ${note.text}`)).join("\n")}`
|
|
174
|
+
: "";
|
|
175
|
+
return `${theme.fg("accent", answer.header)}: ${theme.fg("text", answerScalar(answer))}${notes}`;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function renderOutcome(title, details, expanded, theme) {
|
|
179
|
+
if (!expanded || details.answers.length === 0)
|
|
180
|
+
return new Text(title, 0, 0);
|
|
181
|
+
return new Text(`${title}\n${answerLines(details.answers, theme).join("\n")}`, 0, 0);
|
|
182
|
+
}
|
|
183
|
+
function answerProgress(details, args) {
|
|
184
|
+
const total = questionsFromArgs(args).length;
|
|
185
|
+
return total > 0 ? `answered ${details.answers.length} of ${total}` : `answered ${details.answers.length}`;
|
|
186
|
+
}
|
|
187
|
+
export function renderQuestionCall(args, theme, expanded) {
|
|
18
188
|
const questions = questionsFromArgs(args);
|
|
19
189
|
const headers = questions.map((question) => question.header).join(", ");
|
|
20
190
|
const count = `${questions.length} decision${questions.length === 1 ? "" : "s"}`;
|
|
21
191
|
const summary = questions.length === 0 ? "asking for a decision" : headers ? `${count}: ${headers}` : count;
|
|
22
|
-
|
|
192
|
+
const title = `${theme.fg("toolTitle", theme.bold("question"))} ${theme.fg("muted", summary)}`;
|
|
193
|
+
if (!expanded || questions.length === 0)
|
|
194
|
+
return new Text(title, 0, 0);
|
|
195
|
+
const detailLines = questions.map((question) => {
|
|
196
|
+
const multi = question.multiSelect ? " (multi-select)" : "";
|
|
197
|
+
const body = question.question ? `: ${question.question}` : "";
|
|
198
|
+
return theme.fg("dim", `${question.header}${multi}${body}`);
|
|
199
|
+
});
|
|
200
|
+
return new Text(`${title}\n${detailLines.join("\n")}`, 0, 0);
|
|
23
201
|
}
|
|
24
|
-
export function renderQuestionResult(result, options, theme) {
|
|
25
|
-
const details = result.details;
|
|
202
|
+
export function renderQuestionResult(result, options, theme, args) {
|
|
203
|
+
const details = normalizeDetails(result.details);
|
|
26
204
|
if (!details)
|
|
27
|
-
return
|
|
28
|
-
if (details.outcome === "cancelled")
|
|
29
|
-
return new Text(theme.fg("warning", "Cancelled"), 0, 0);
|
|
30
|
-
if (details.outcome === "needs_clarification") {
|
|
31
|
-
return new Text(theme.fg("warning", "Wants to discuss the choices before answering"), 0, 0);
|
|
32
|
-
}
|
|
205
|
+
return renderFallbackResult(result, options.expanded, theme);
|
|
33
206
|
if (details.outcome === "error") {
|
|
34
|
-
return new Text(theme.fg("error", `Question error: ${details
|
|
207
|
+
return new Text(theme.fg("error", `Question error: ${errorMessage(result, details)}`), 0, 0);
|
|
208
|
+
}
|
|
209
|
+
if (details.outcome === "cancelled") {
|
|
210
|
+
const title = theme.fg("warning", `Cancelled · ${answerProgress(details, args)}`);
|
|
211
|
+
return renderOutcome(title, details, options.expanded, theme);
|
|
212
|
+
}
|
|
213
|
+
if (details.outcome === "needs_clarification") {
|
|
214
|
+
const title = theme.fg("warning", `Wants to discuss · ${answerProgress(details, args)}`);
|
|
215
|
+
return renderOutcome(title, details, options.expanded, theme);
|
|
35
216
|
}
|
|
36
217
|
const title = theme.fg("success", `✓ Answered ${details.answers.length} decision${details.answers.length === 1 ? "" : "s"}`);
|
|
37
|
-
|
|
38
|
-
return new Text(title, 0, 0);
|
|
39
|
-
const answers = details.answers.map((answer) => {
|
|
40
|
-
const notes = answer.notes?.length
|
|
41
|
-
? `\n${answer.notes.map((note) => theme.fg("muted", ` Note for ${note.option}: ${note.text}`)).join("\n")}`
|
|
42
|
-
: "";
|
|
43
|
-
return `${theme.fg("accent", answer.header)}: ${theme.fg("text", answerScalar(answer))}${notes}`;
|
|
44
|
-
});
|
|
45
|
-
return new Text(`${title}\n${answers.join("\n")}`, 0, 0);
|
|
218
|
+
return renderOutcome(title, details, options.expanded, theme);
|
|
46
219
|
}
|
|
47
220
|
//# sourceMappingURL=render.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/extensions/question/render.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { answerScalar } from \"./results.ts\";\nimport type { Question, QuestionToolDetails } from \"./types.ts\";\n\ntype RenderOptions = { expanded: boolean };\n\ntype ResultLike = {\n\tcontent: Array<{ type: string; text?: string }>;\n\tdetails?: QuestionToolDetails;\n};\n\nfunction questionsFromArgs(args: unknown): Question[] {\n\tif (!args || typeof args !== \"object\" || !(\"questions\" in args) || !Array.isArray(args.questions)) return [];\n\treturn args.questions.filter(\n\t\t(question): question is Question =>\n\t\t\tBoolean(question) &&\n\t\t\ttypeof question === \"object\" &&\n\t\t\t\"header\" in question &&\n\t\t\ttypeof question.header === \"string\",\n\t);\n}\n\nfunction resultFallback(result: ResultLike): string {\n\treturn result.content\n\t\t.filter(\n\t\t\t(block): block is { type: string; text: string } => block.type === \"text\" && typeof block.text === \"string\",\n\t\t)\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\nexport function renderQuestionCall(args: unknown, theme: Theme): Text {\n\tconst questions = questionsFromArgs(args);\n\tconst headers = questions.map((question) => question.header).join(\", \");\n\tconst count = `${questions.length} decision${questions.length === 1 ? \"\" : \"s\"}`;\n\tconst summary = questions.length === 0 ? \"asking for a decision\" : headers ? `${count}: ${headers}` : count;\n\treturn new Text(`${theme.fg(\"toolTitle\", theme.bold(\"question\"))} ${theme.fg(\"muted\", summary)}`, 0, 0);\n}\n\nexport function renderQuestionResult(result: ResultLike, options: RenderOptions, theme: Theme): Text {\n\tconst details = result.details;\n\tif (!details) return new Text(resultFallback(result), 0, 0);\n\n\tif (details.outcome === \"cancelled\") return new Text(theme.fg(\"warning\", \"Cancelled\"), 0, 0);\n\tif (details.outcome === \"needs_clarification\") {\n\t\treturn new Text(theme.fg(\"warning\", \"Wants to discuss the choices before answering\"), 0, 0);\n\t}\n\tif (details.outcome === \"error\") {\n\t\treturn new Text(theme.fg(\"error\", `Question error: ${details.error ?? \"unknown error\"}`), 0, 0);\n\t}\n\n\tconst title = theme.fg(\n\t\t\"success\",\n\t\t`✓ Answered ${details.answers.length} decision${details.answers.length === 1 ? \"\" : \"s\"}`,\n\t);\n\tif (!options.expanded) return new Text(title, 0, 0);\n\n\tconst answers = details.answers.map((answer) => {\n\t\tconst notes = answer.notes?.length\n\t\t\t? `\\n${answer.notes.map((note) => theme.fg(\"muted\", ` Note for ${note.option}: ${note.text}`)).join(\"\\n\")}`\n\t\t\t: \"\";\n\t\treturn `${theme.fg(\"accent\", answer.header)}: ${theme.fg(\"text\", answerScalar(answer))}${notes}`;\n\t});\n\treturn new Text(`${title}\\n${answers.join(\"\\n\")}`, 0, 0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/extensions/question/render.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport { keyLabel } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport { answerScalar } from \"./results.ts\";\nimport type { QuestionAnswer, QuestionOutcome } from \"./types.ts\";\n\ntype RenderOptions = { expanded: boolean };\n\ntype ResultLike = {\n\tcontent?: unknown;\n\tdetails?: unknown;\n};\n\ninterface RenderQuestion {\n\theader: string;\n\tquestion: string;\n\tmultiSelect: boolean;\n}\n\ninterface RenderDetails {\n\tanswers: QuestionAnswer[];\n\toutcome: QuestionOutcome;\n\tmessage?: string;\n}\n\nconst MAX_RENDERED_QUESTIONS = 4;\nconst MAX_RENDERED_ANSWERS = 4;\nconst MAX_RENDERED_SELECTIONS = 5;\nconst MAX_RENDERED_ANSWER_CHARS = 400;\nconst MAX_RENDERED_NOTE_CHARS = 400;\nconst MAX_RENDERED_ERROR_CHARS = 400;\nconst MAX_RENDERED_VALIDATION_SUMMARY_CHARS = 240;\nconst MAX_RENDERED_FALLBACK_CHARS = 1_000;\n\nfunction safeRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\nfunction safeValue(record: Record<string, unknown>, key: string): unknown {\n\ttry {\n\t\treturn record[key];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction truncate(text: string, limit: number): string {\n\treturn text.length > limit ? `${text.slice(0, Math.max(0, limit - 1))}…` : text;\n}\n\nfunction oneLine(value: unknown, limit: number): string {\n\treturn typeof value === \"string\" ? truncate(value.replace(/\\s+/gu, \" \").trim(), limit) : \"\";\n}\n\nfunction questionsFromArgs(args: unknown): RenderQuestion[] {\n\tconst record = safeRecord(args);\n\tif (!record) return [];\n\tconst rawQuestions = safeValue(record, \"questions\");\n\tif (!Array.isArray(rawQuestions)) return [];\n\tconst questions: RenderQuestion[] = [];\n\tfor (const rawQuestion of rawQuestions.slice(0, MAX_RENDERED_QUESTIONS)) {\n\t\tconst question = safeRecord(rawQuestion);\n\t\tif (!question) continue;\n\t\tconst rawHeader = safeValue(question, \"header\");\n\t\tif (typeof rawHeader !== \"string\") continue;\n\t\tconst header = oneLine(rawHeader, QUESTION_LIMITS.headerChars) || `Decision ${questions.length + 1}`;\n\t\tquestions.push({\n\t\t\theader,\n\t\t\tquestion: oneLine(safeValue(question, \"question\"), QUESTION_LIMITS.questionChars),\n\t\t\tmultiSelect: safeValue(question, \"multiSelect\") === true,\n\t\t});\n\t}\n\treturn questions;\n}\n\nfunction resultFallback(result: ResultLike): string {\n\tif (!Array.isArray(result.content)) return \"\";\n\tconst blocks: string[] = [];\n\tfor (const rawBlock of result.content.slice(0, 8)) {\n\t\tconst block = safeRecord(rawBlock);\n\t\tif (!block || safeValue(block, \"type\") !== \"text\") continue;\n\t\tconst text = safeValue(block, \"text\");\n\t\tif (typeof text === \"string\") blocks.push(text);\n\t}\n\treturn truncate(blocks.join(\"\\n\"), MAX_RENDERED_FALLBACK_CHARS);\n}\n\nfunction validationSummary(text: string): { summary: string; hiddenErrors: number } | undefined {\n\tconst match = /^Validation failed for tool\\s+\"question\":\\s*([\\s\\S]*?)(?:\\r?\\n\\s*\\r?\\nReceived arguments:|$)/u.exec(\n\t\ttext.trim(),\n\t);\n\tif (!match) return undefined;\n\tconst errors = match[1]\n\t\t.split(/\\r?\\n/u)\n\t\t.map((line) => line.trim().replace(/^-\\s*/u, \"\"))\n\t\t.filter(Boolean);\n\tconst firstError = oneLine(errors[0], MAX_RENDERED_VALIDATION_SUMMARY_CHARS) || \"schema validation failed\";\n\treturn {\n\t\tsummary: `Invalid arguments · ${firstError}`,\n\t\thiddenErrors: Math.max(0, errors.length - 1),\n\t};\n}\n\nfunction renderFallbackResult(result: ResultLike, expanded: boolean, theme: Theme): Text {\n\tconst fallback = resultFallback(result);\n\tif (!fallback) return new Text(\"Question result unavailable\", 0, 0);\n\tconst validation = validationSummary(fallback);\n\tif (!validation || expanded) return new Text(fallback, 0, 0);\n\n\tconst moreErrors = validation.hiddenErrors > 0 ? ` · +${validation.hiddenErrors} more` : \"\";\n\tconst expandKey = keyLabel(\"app.tools.expand\");\n\tconst hint = theme.fg(\"muted\", expandKey ? ` (${expandKey} to expand)` : \" (more details available)\");\n\treturn new Text(`${theme.fg(\"error\", validation.summary)}${theme.fg(\"muted\", moreErrors)}${hint}`, 0, 0);\n}\n\nfunction normalizeAnswer(value: unknown, index: number): QuestionAnswer | undefined {\n\tconst raw = safeRecord(value);\n\tif (!raw) return undefined;\n\tconst rawHeader = safeValue(raw, \"header\");\n\tif (typeof rawHeader !== \"string\") return undefined;\n\tconst header = oneLine(rawHeader, QUESTION_LIMITS.headerChars) || `Decision ${index + 1}`;\n\tconst kindValue = safeValue(raw, \"kind\");\n\tconst kind = kindValue === \"multi\" || kindValue === \"custom\" ? kindValue : \"option\";\n\tconst rawIndex = safeValue(raw, \"questionIndex\");\n\tconst questionIndex =\n\t\ttypeof rawIndex === \"number\" && Number.isSafeInteger(rawIndex) && rawIndex >= 0 ? rawIndex : index;\n\tconst question = oneLine(safeValue(raw, \"question\"), QUESTION_LIMITS.questionChars);\n\tconst answer = oneLine(safeValue(raw, \"answer\"), MAX_RENDERED_ANSWER_CHARS);\n\n\tconst rawSelected = safeValue(raw, \"selected\");\n\tconst selected = Array.isArray(rawSelected)\n\t\t? rawSelected\n\t\t\t\t.slice(0, MAX_RENDERED_SELECTIONS)\n\t\t\t\t.map((entry, selectedIndex) => {\n\t\t\t\t\tif (typeof entry !== \"string\") return \"\";\n\t\t\t\t\treturn oneLine(entry, MAX_RENDERED_ANSWER_CHARS) || `Option ${selectedIndex + 1}`;\n\t\t\t\t})\n\t\t\t\t.filter(Boolean)\n\t\t: undefined;\n\tconst rawNotes = safeValue(raw, \"notes\");\n\tconst notes = Array.isArray(rawNotes)\n\t\t? rawNotes\n\t\t\t\t.slice(0, MAX_RENDERED_ANSWERS)\n\t\t\t\t.map((entry) => {\n\t\t\t\t\tconst note = safeRecord(entry);\n\t\t\t\t\tif (!note) return undefined;\n\t\t\t\t\tconst option = oneLine(safeValue(note, \"option\"), QUESTION_LIMITS.optionLabelChars);\n\t\t\t\t\tconst text = oneLine(safeValue(note, \"text\"), MAX_RENDERED_NOTE_CHARS);\n\t\t\t\t\treturn option && text ? { option, text } : undefined;\n\t\t\t\t})\n\t\t\t\t.filter((note): note is { option: string; text: string } => note !== undefined)\n\t\t: undefined;\n\n\treturn {\n\t\tquestionIndex,\n\t\tquestion,\n\t\theader,\n\t\tkind,\n\t\tanswer: answer || null,\n\t\t...(kind === \"multi\" && selected?.length ? { selected } : {}),\n\t\t...(notes?.length ? { notes } : {}),\n\t};\n}\n\nfunction normalizeDetails(value: unknown): RenderDetails | undefined {\n\tconst raw = safeRecord(value);\n\tif (!raw) return undefined;\n\tconst outcome = safeValue(raw, \"outcome\");\n\tif (outcome !== \"answered\" && outcome !== \"cancelled\" && outcome !== \"needs_clarification\" && outcome !== \"error\") {\n\t\treturn undefined;\n\t}\n\tconst rawAnswers = safeValue(raw, \"answers\");\n\tconst answers = Array.isArray(rawAnswers)\n\t\t? rawAnswers\n\t\t\t\t.slice(0, MAX_RENDERED_ANSWERS)\n\t\t\t\t.map((answer, index) => normalizeAnswer(answer, index))\n\t\t\t\t.filter((answer): answer is QuestionAnswer => answer !== undefined)\n\t\t: [];\n\tconst message = oneLine(safeValue(raw, \"message\"), MAX_RENDERED_ERROR_CHARS);\n\treturn { answers, outcome, ...(message ? { message } : {}) };\n}\n\nfunction errorMessage(result: ResultLike, details: RenderDetails): string {\n\tif (details.message) return details.message;\n\tconst fallback = resultFallback(result).trim();\n\tconst match = /^Question tool error \\([^)]*\\):\\s*([\\s\\S]+)$/u.exec(fallback);\n\treturn truncate(match?.[1]?.trim() || \"The question tool could not continue\", MAX_RENDERED_ERROR_CHARS);\n}\n\nfunction answerLines(answers: QuestionAnswer[], theme: Theme): string[] {\n\treturn answers.map((answer) => {\n\t\tconst notes = answer.notes?.length\n\t\t\t? `\\n${answer.notes.map((note) => theme.fg(\"muted\", ` Note for ${note.option}: ${note.text}`)).join(\"\\n\")}`\n\t\t\t: \"\";\n\t\treturn `${theme.fg(\"accent\", answer.header)}: ${theme.fg(\"text\", answerScalar(answer))}${notes}`;\n\t});\n}\n\nfunction renderOutcome(title: string, details: RenderDetails, expanded: boolean, theme: Theme): Text {\n\tif (!expanded || details.answers.length === 0) return new Text(title, 0, 0);\n\treturn new Text(`${title}\\n${answerLines(details.answers, theme).join(\"\\n\")}`, 0, 0);\n}\n\nfunction answerProgress(details: RenderDetails, args: unknown): string {\n\tconst total = questionsFromArgs(args).length;\n\treturn total > 0 ? `answered ${details.answers.length} of ${total}` : `answered ${details.answers.length}`;\n}\n\nexport function renderQuestionCall(args: unknown, theme: Theme, expanded: boolean): Text {\n\tconst questions = questionsFromArgs(args);\n\tconst headers = questions.map((question) => question.header).join(\", \");\n\tconst count = `${questions.length} decision${questions.length === 1 ? \"\" : \"s\"}`;\n\tconst summary = questions.length === 0 ? \"asking for a decision\" : headers ? `${count}: ${headers}` : count;\n\tconst title = `${theme.fg(\"toolTitle\", theme.bold(\"question\"))} ${theme.fg(\"muted\", summary)}`;\n\tif (!expanded || questions.length === 0) return new Text(title, 0, 0);\n\tconst detailLines = questions.map((question) => {\n\t\tconst multi = question.multiSelect ? \" (multi-select)\" : \"\";\n\t\tconst body = question.question ? `: ${question.question}` : \"\";\n\t\treturn theme.fg(\"dim\", `${question.header}${multi}${body}`);\n\t});\n\treturn new Text(`${title}\\n${detailLines.join(\"\\n\")}`, 0, 0);\n}\n\nexport function renderQuestionResult(result: ResultLike, options: RenderOptions, theme: Theme, args?: unknown): Text {\n\tconst details = normalizeDetails(result.details);\n\tif (!details) return renderFallbackResult(result, options.expanded, theme);\n\n\tif (details.outcome === \"error\") {\n\t\treturn new Text(theme.fg(\"error\", `Question error: ${errorMessage(result, details)}`), 0, 0);\n\t}\n\tif (details.outcome === \"cancelled\") {\n\t\tconst title = theme.fg(\"warning\", `Cancelled · ${answerProgress(details, args)}`);\n\t\treturn renderOutcome(title, details, options.expanded, theme);\n\t}\n\tif (details.outcome === \"needs_clarification\") {\n\t\tconst title = theme.fg(\"warning\", `Wants to discuss · ${answerProgress(details, args)}`);\n\t\treturn renderOutcome(title, details, options.expanded, theme);\n\t}\n\n\tconst title = theme.fg(\n\t\t\"success\",\n\t\t`✓ Answered ${details.answers.length} decision${details.answers.length === 1 ? \"\" : \"s\"}`,\n\t);\n\treturn renderOutcome(title, details, options.expanded, theme);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAoBzF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAoBzF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAQ3G;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAG3D;AAiBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAS7F;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAc,EAAO,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAOjG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,cAAc,EAAO,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAYxG","sourcesContent":["import type { AgentToolResult } from \"../../core/extensions/types.ts\";\nimport { QUESTION_LIMITS, TRUNCATION_FOLLOW_UP } from \"./limits.ts\";\nimport type { QuestionAnswer, QuestionToolDetails, QuestionToolError } from \"./types.ts\";\n\nconst DECLINE_MESSAGE = \"User declined to answer the questions.\";\nconst CLARIFICATION_MESSAGE = \"The user wants to discuss these questions before choosing an answer.\";\nconst ENVELOPE_PREFIX = \"User decisions:\";\nconst ENVELOPE_SUFFIX = \"Continue with these decisions in mind.\";\n\nfunction buildToolResult(text: string, details: QuestionToolDetails): AgentToolResult<QuestionToolDetails> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails,\n\t};\n}\n\nfunction boundedText(text: string): string {\n\tif (text.length <= QUESTION_LIMITS.modelResultChars) return text;\n\tconst suffix = `\\n\\n[Question result truncated. ${TRUNCATION_FOLLOW_UP}]`;\n\treturn `${text.slice(0, Math.max(0, QUESTION_LIMITS.modelResultChars - suffix.length))}${suffix}`;\n}\n\nexport function errorResult(error: QuestionToolError, message: string): AgentToolResult<QuestionToolDetails> {\n\treturn buildToolResult(`Question tool error (${error}): ${message}`, {\n\t\tanswers: [],\n\t\toutcome: \"error\",\n\t\tcancelled: false,\n\t\terror,\n\t\tmessage,\n\t});\n}\n\nexport function answerScalar(answer: QuestionAnswer): string {\n\tif (answer.kind === \"multi\") return answer.selected?.length ? answer.selected.join(\", \") : \"(no input)\";\n\treturn answer.answer && answer.answer.length > 0 ? answer.answer : \"(no input)\";\n}\n\nfunction answerKindLabel(answer: QuestionAnswer): string {\n\tif (answer.kind === \"multi\") return \"Selections\";\n\treturn answer.kind === \"custom\" ? \"Custom answer\" : \"Selected option\";\n}\n\nfunction answerSegment(answer: QuestionAnswer): string {\n\tconst lines = [\n\t\t`${answer.questionIndex + 1}. [${answer.header}] ${answer.question}`,\n\t\t` ${answerKindLabel(answer)}: ${answerScalar(answer)}`,\n\t];\n\tif (answer.preview) lines.push(\" Preview: selected (kept in tool details)\");\n\tfor (const note of answer.notes ?? []) lines.push(` Note for ${note.option}: ${note.text}`);\n\treturn lines.join(\"\\n\");\n}\n\nexport function successResult(answers: QuestionAnswer[]): AgentToolResult<QuestionToolDetails> {\n\tconst text = answers.length\n\t\t? boundedText(`${ENVELOPE_PREFIX}\\n${answers.map(answerSegment).join(\"\\n\\n\")}\\n\\n${ENVELOPE_SUFFIX}`)\n\t\t: DECLINE_MESSAGE;\n\treturn buildToolResult(text, {\n\t\tanswers,\n\t\toutcome: answers.length ? \"answered\" : \"cancelled\",\n\t\tcancelled: answers.length === 0,\n\t});\n}\n\nexport function cancelResult(answers: QuestionAnswer[] = []): AgentToolResult<QuestionToolDetails> {\n\tconst answered = answers.length ? `\\n\\nPartial answers so far:\\n${answers.map(answerSegment).join(\"\\n\\n\")}` : \"\";\n\treturn buildToolResult(boundedText(`${DECLINE_MESSAGE}${answered}`), {\n\t\tanswers,\n\t\toutcome: \"cancelled\",\n\t\tcancelled: true,\n\t});\n}\n\nexport function clarificationResult(answers: QuestionAnswer[] = []): AgentToolResult<QuestionToolDetails> {\n\tconst answered = answers.length ? ` Partial answers so far:\\n${answers.map(answerSegment).join(\"\\n\\n\")}` : \"\";\n\treturn buildToolResult(\n\t\tboundedText(\n\t\t\t`${CLARIFICATION_MESSAGE}${answered}\\n\\nExplain the trade-offs or ask what the user would like clarified.`,\n\t\t),\n\t\t{\n\t\t\tanswers,\n\t\t\toutcome: \"needs_clarification\",\n\t\t\tcancelled: false,\n\t\t},\n\t);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results.js","sourceRoot":"","sources":["../../../src/extensions/question/results.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGpE,MAAM,eAAe,GAAG,wCAAwC,CAAC;AACjE,MAAM,qBAAqB,GAAG,sEAAsE,CAAC;AACrG,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAC1C,MAAM,eAAe,GAAG,wCAAwC,CAAC;AAEjE,SAAS,eAAe,CAAC,IAAY,EAAE,OAA4B,EAAwC;IAC1G,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACjC,OAAO;KACP,CAAC;AAAA,CACF;AAED,SAAS,WAAW,CAAC,IAAY,EAAU;IAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IACjE,MAAM,MAAM,GAAG,mCAAmC,oBAAoB,GAAG,CAAC;IAC1E,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,CAAC,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC;AAAA,CAClG;AAED,MAAM,UAAU,WAAW,CAAC,KAAwB,EAAE,OAAe,EAAwC;IAC5G,OAAO,eAAe,CAAC,wBAAwB,KAAK,MAAM,OAAO,EAAE,EAAE;QACpE,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,KAAK;QAChB,KAAK;
|
|
1
|
+
{"version":3,"file":"results.js","sourceRoot":"","sources":["../../../src/extensions/question/results.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolResult } from \"../../core/extensions/types.ts\";\nimport { QUESTION_LIMITS, TRUNCATION_FOLLOW_UP } from \"./limits.ts\";\nimport type { QuestionAnswer, QuestionToolDetails, QuestionToolError } from \"./types.ts\";\n\nconst DECLINE_MESSAGE = \"User declined to answer the questions.\";\nconst CLARIFICATION_MESSAGE = \"The user wants to discuss these questions before choosing an answer.\";\nconst ENVELOPE_PREFIX = \"User decisions:\";\nconst ENVELOPE_SUFFIX = \"Continue with these decisions in mind.\";\n\nfunction buildToolResult(text: string, details: QuestionToolDetails): AgentToolResult<QuestionToolDetails> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails,\n\t};\n}\n\nfunction boundedText(text: string): string {\n\tif (text.length <= QUESTION_LIMITS.modelResultChars) return text;\n\tconst suffix = `\\n\\n[Question result truncated. ${TRUNCATION_FOLLOW_UP}]`;\n\treturn `${text.slice(0, Math.max(0, QUESTION_LIMITS.modelResultChars - suffix.length))}${suffix}`;\n}\n\nexport function errorResult(error: QuestionToolError, message: string): AgentToolResult<QuestionToolDetails> {\n\treturn buildToolResult(`Question tool error (${error}): ${message}`, {\n\t\tanswers: [],\n\t\toutcome: \"error\",\n\t\tcancelled: false,\n\t\terror,\n\t\tmessage,\n\t});\n}\n\nexport function answerScalar(answer: QuestionAnswer): string {\n\tif (answer.kind === \"multi\") return answer.selected?.length ? answer.selected.join(\", \") : \"(no input)\";\n\treturn answer.answer && answer.answer.length > 0 ? answer.answer : \"(no input)\";\n}\n\nfunction answerKindLabel(answer: QuestionAnswer): string {\n\tif (answer.kind === \"multi\") return \"Selections\";\n\treturn answer.kind === \"custom\" ? \"Custom answer\" : \"Selected option\";\n}\n\nfunction answerSegment(answer: QuestionAnswer): string {\n\tconst lines = [\n\t\t`${answer.questionIndex + 1}. [${answer.header}] ${answer.question}`,\n\t\t` ${answerKindLabel(answer)}: ${answerScalar(answer)}`,\n\t];\n\tif (answer.preview) lines.push(\" Preview: selected (kept in tool details)\");\n\tfor (const note of answer.notes ?? []) lines.push(` Note for ${note.option}: ${note.text}`);\n\treturn lines.join(\"\\n\");\n}\n\nexport function successResult(answers: QuestionAnswer[]): AgentToolResult<QuestionToolDetails> {\n\tconst text = answers.length\n\t\t? boundedText(`${ENVELOPE_PREFIX}\\n${answers.map(answerSegment).join(\"\\n\\n\")}\\n\\n${ENVELOPE_SUFFIX}`)\n\t\t: DECLINE_MESSAGE;\n\treturn buildToolResult(text, {\n\t\tanswers,\n\t\toutcome: answers.length ? \"answered\" : \"cancelled\",\n\t\tcancelled: answers.length === 0,\n\t});\n}\n\nexport function cancelResult(answers: QuestionAnswer[] = []): AgentToolResult<QuestionToolDetails> {\n\tconst answered = answers.length ? `\\n\\nPartial answers so far:\\n${answers.map(answerSegment).join(\"\\n\\n\")}` : \"\";\n\treturn buildToolResult(boundedText(`${DECLINE_MESSAGE}${answered}`), {\n\t\tanswers,\n\t\toutcome: \"cancelled\",\n\t\tcancelled: true,\n\t});\n}\n\nexport function clarificationResult(answers: QuestionAnswer[] = []): AgentToolResult<QuestionToolDetails> {\n\tconst answered = answers.length ? ` Partial answers so far:\\n${answers.map(answerSegment).join(\"\\n\\n\")}` : \"\";\n\treturn buildToolResult(\n\t\tboundedText(\n\t\t\t`${CLARIFICATION_MESSAGE}${answered}\\n\\nExplain the trade-offs or ask what the user would like clarified.`,\n\t\t),\n\t\t{\n\t\t\tanswers,\n\t\t\toutcome: \"needs_clarification\",\n\t\t\tcancelled: false,\n\t\t},\n\t);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAoD9E,eAAO,MAAM,cAAc;;;;;;;;;;;EAMzB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,QAAQ,EAAE,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAwCzE","sourcesContent":["import { Type } from \"typebox\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport type { Question, QuestionOption, QuestionToolError } from \"./types.ts\";\n\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 4;\nconst MAX_QUESTIONS = 4;\nconst RESERVED_LABELS = [\"Other\", \"Type something.\", \"Chat about this\"] as const;\n\nconst normalizeLabel = (text: string) => text.trim().toLowerCase();\nconst RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));\n\nconst OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.optionLabelChars,\n\t\tdescription:\n\t\t\t\"Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAoD9E,eAAO,MAAM,cAAc;;;;;;;;;;;EAMzB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,QAAQ,EAAE,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAwCzE","sourcesContent":["import { Type } from \"typebox\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport type { Question, QuestionOption, QuestionToolError } from \"./types.ts\";\n\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 4;\nconst MAX_QUESTIONS = 4;\nconst RESERVED_LABELS = [\"Other\", \"Type something\", \"Type something.\", \"Chat about this\"] as const;\n\nconst normalizeLabel = (text: string) => text.trim().toLowerCase();\nconst RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));\n\nconst OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.optionLabelChars,\n\t\tdescription:\n\t\t\t\"Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something', 'Chat about this') are rejected — the UI adds the custom-answer path itself.\",\n\t}),\n\tdescription: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.optionDescriptionChars,\n\t\tdescription: \"One concise sentence explaining the option's meaning, consequence, or tradeoff.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: QUESTION_LIMITS.previewChars,\n\t\t\tdescription:\n\t\t\t\t\"Optional markdown preview shown only for focused single-select options; use for concrete snippets, layouts, copy, or config comparisons.\",\n\t\t}),\n\t),\n});\n\nconst QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.questionChars,\n\t\tdescription:\n\t\t\t\"One clear, specific decision or preference question ending in a question mark. Ask only what is needed to proceed.\",\n\t}),\n\theader: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.headerChars,\n\t\tdescription: \"Very short decision label shown as a chip; aim for ~12 chars, max 32. E.g. 'Auth method'.\",\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription: \"Mutually distinct options for this decision; do not include reserved custom-answer labels.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Allow selecting multiple options only when choices can be combined. Defaults to false for mutually exclusive decisions.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionParams = Type.Object({\n\tquestions: Type.Array(QuestionSchema, {\n\t\tminItems: 1,\n\t\tmaxItems: MAX_QUESTIONS,\n\t\tdescription: \"Related decisions to ask now (1-4); ask only what is needed to unblock progress.\",\n\t}),\n});\n\n/**\n * Checks the schema cannot express. Length and count limits are omitted:\n * the agent core validates params against `QuestionParams` before execute runs.\n */\nexport function validateQuestions(\n\tquestions: Question[],\n): { ok: true } | { ok: false; error: QuestionToolError; message: string } {\n\tconst seenQuestions = new Set<string>();\n\tfor (const q of questions) {\n\t\tconst questionKey = normalizeLabel(q.question);\n\t\tif (seenQuestions.has(questionKey)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: \"Question text must be unique\" };\n\t\t}\n\t\tseenQuestions.add(questionKey);\n\n\t\tif (q.multiSelect && q.options.some((option) => option.preview)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"preview_multiselect\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Option previews are not supported on multiSelect questions; drop the previews or make the question single-select\",\n\t\t\t};\n\t\t}\n\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (const option of q.options as QuestionOption[]) {\n\t\t\tconst labelKey = normalizeLabel(option.label);\n\t\t\tif (RESERVED_NORMALIZED.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"reserved_label\",\n\t\t\t\t\tmessage: `Option label is reserved (${RESERVED_LABELS.join(\", \")})`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seenLabels.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: \"Option labels must be unique within a question\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(labelKey);\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
|
|
@@ -3,13 +3,13 @@ import { QUESTION_LIMITS } from "./limits.js";
|
|
|
3
3
|
const MIN_OPTIONS = 2;
|
|
4
4
|
const MAX_OPTIONS = 4;
|
|
5
5
|
const MAX_QUESTIONS = 4;
|
|
6
|
-
const RESERVED_LABELS = ["Other", "Type something.", "Chat about this"];
|
|
6
|
+
const RESERVED_LABELS = ["Other", "Type something", "Type something.", "Chat about this"];
|
|
7
7
|
const normalizeLabel = (text) => text.trim().toLowerCase();
|
|
8
8
|
const RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));
|
|
9
9
|
const OptionSchema = Type.Object({
|
|
10
10
|
label: Type.String({
|
|
11
11
|
maxLength: QUESTION_LIMITS.optionLabelChars,
|
|
12
|
-
description: "Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something
|
|
12
|
+
description: "Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something', 'Chat about this') are rejected — the UI adds the custom-answer path itself.",
|
|
13
13
|
}),
|
|
14
14
|
description: Type.String({
|
|
15
15
|
maxLength: QUESTION_LIMITS.optionDescriptionChars,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type } from \"typebox\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport type { Question, QuestionOption, QuestionToolError } from \"./types.ts\";\n\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 4;\nconst MAX_QUESTIONS = 4;\nconst RESERVED_LABELS = [\"Other\", \"Type something\", \"Type something.\", \"Chat about this\"] as const;\n\nconst normalizeLabel = (text: string) => text.trim().toLowerCase();\nconst RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));\n\nconst OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.optionLabelChars,\n\t\tdescription:\n\t\t\t\"Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something', 'Chat about this') are rejected — the UI adds the custom-answer path itself.\",\n\t}),\n\tdescription: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.optionDescriptionChars,\n\t\tdescription: \"One concise sentence explaining the option's meaning, consequence, or tradeoff.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: QUESTION_LIMITS.previewChars,\n\t\t\tdescription:\n\t\t\t\t\"Optional markdown preview shown only for focused single-select options; use for concrete snippets, layouts, copy, or config comparisons.\",\n\t\t}),\n\t),\n});\n\nconst QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.questionChars,\n\t\tdescription:\n\t\t\t\"One clear, specific decision or preference question ending in a question mark. Ask only what is needed to proceed.\",\n\t}),\n\theader: Type.String({\n\t\tmaxLength: QUESTION_LIMITS.headerChars,\n\t\tdescription: \"Very short decision label shown as a chip; aim for ~12 chars, max 32. E.g. 'Auth method'.\",\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription: \"Mutually distinct options for this decision; do not include reserved custom-answer labels.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Allow selecting multiple options only when choices can be combined. Defaults to false for mutually exclusive decisions.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionParams = Type.Object({\n\tquestions: Type.Array(QuestionSchema, {\n\t\tminItems: 1,\n\t\tmaxItems: MAX_QUESTIONS,\n\t\tdescription: \"Related decisions to ask now (1-4); ask only what is needed to unblock progress.\",\n\t}),\n});\n\n/**\n * Checks the schema cannot express. Length and count limits are omitted:\n * the agent core validates params against `QuestionParams` before execute runs.\n */\nexport function validateQuestions(\n\tquestions: Question[],\n): { ok: true } | { ok: false; error: QuestionToolError; message: string } {\n\tconst seenQuestions = new Set<string>();\n\tfor (const q of questions) {\n\t\tconst questionKey = normalizeLabel(q.question);\n\t\tif (seenQuestions.has(questionKey)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: \"Question text must be unique\" };\n\t\t}\n\t\tseenQuestions.add(questionKey);\n\n\t\tif (q.multiSelect && q.options.some((option) => option.preview)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"preview_multiselect\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Option previews are not supported on multiSelect questions; drop the previews or make the question single-select\",\n\t\t\t};\n\t\t}\n\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (const option of q.options as QuestionOption[]) {\n\t\t\tconst labelKey = normalizeLabel(option.label);\n\t\t\tif (RESERVED_NORMALIZED.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"reserved_label\",\n\t\t\t\t\tmessage: `Option label is reserved (${RESERVED_LABELS.join(\", \")})`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seenLabels.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: \"Option labels must be unique within a question\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(labelKey);\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAC1B,OAAO,GACP,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,GAChB,qBAAqB,CAAC;AAEzB,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,qBAAqB,GAAG,OAAO,CAAC;AAEzF,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAC1B,OAAO,GACP,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,GAChB,qBAAqB,CAAC;AAEzB,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,qBAAqB,GAAG,OAAO,CAAC;AAEzF,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,MAAM,aAAa,GACtB,CAAC,cAAc,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAEnD,eAAO,MAAM,YAAY,EAAE,aAAyE,CAAC","sourcesContent":["export interface QuestionOption {\n\tlabel: string;\n\tdescription: string;\n\tpreview?: string;\n}\n\nexport interface Question {\n\tquestion: string;\n\theader: string;\n\toptions: QuestionOption[];\n\tmultiSelect?: boolean;\n}\n\nexport type InputMode = \"custom\" | \"notes\" | undefined;\n\nexport type QuestionToolError =\n\t| \"no_ui\"\n\t| \"duplicate_question\"\n\t| \"duplicate_option_label\"\n\t| \"reserved_label\"\n\t| \"preview_multiselect\";\n\nexport interface AnswerNote {\n\toption: string;\n\ttext: string;\n}\n\nexport interface QuestionAnswer {\n\tquestionIndex: number;\n\tquestion: string;\n\theader: string;\n\tkind: \"option\" | \"custom\" | \"multi\";\n\tanswer: string | null;\n\tselected?: string[];\n\tnotes?: AnswerNote[];\n\tpreview?: string;\n}\n\nexport type QuestionOutcome = \"answered\" | \"cancelled\" | \"needs_clarification\" | \"error\";\n\nexport interface QuestionToolDetails {\n\tanswers: QuestionAnswer[];\n\toutcome: QuestionOutcome;\n\tcancelled: boolean;\n\terror?: QuestionToolError;\n\tmessage?: string;\n}\n\nexport interface CustomAnswer {\n\ttext: string;\n\tselected: boolean;\n}\n\nexport interface QuestionState {\n\toptionIndex: number;\n\tsingleAnswer?: QuestionAnswer;\n\tmultiSelected: Set<number>;\n\tcustomAnswer?: CustomAnswer;\n\tnotesByOption: Map<string, string>;\n\twarning?: string;\n}\n\nexport interface DialogResult {\n\tanswers: QuestionAnswer[];\n\toutcome: QuestionOutcome;\n}\n\nexport type DisplayOption =\n\t| (QuestionOption & { kind: \"option\"; optionIndex: number })\n\t| { kind: \"other\"; label: string; isOther: true };\n\nexport const OTHER_OPTION: DisplayOption = { kind: \"other\", label: \"Type something\", isOther: true };\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const OTHER_OPTION = { kind: "other", label: "Type something
|
|
1
|
+
export const OTHER_OPTION = { kind: "other", label: "Type something", isOther: true };
|
|
2
2
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/extensions/question/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/extensions/question/types.ts"],"names":[],"mappings":"AAuEA,MAAM,CAAC,MAAM,YAAY,GAAkB,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC","sourcesContent":["export interface QuestionOption {\n\tlabel: string;\n\tdescription: string;\n\tpreview?: string;\n}\n\nexport interface Question {\n\tquestion: string;\n\theader: string;\n\toptions: QuestionOption[];\n\tmultiSelect?: boolean;\n}\n\nexport type InputMode = \"custom\" | \"notes\" | undefined;\n\nexport type QuestionToolError =\n\t| \"no_ui\"\n\t| \"duplicate_question\"\n\t| \"duplicate_option_label\"\n\t| \"reserved_label\"\n\t| \"preview_multiselect\";\n\nexport interface AnswerNote {\n\toption: string;\n\ttext: string;\n}\n\nexport interface QuestionAnswer {\n\tquestionIndex: number;\n\tquestion: string;\n\theader: string;\n\tkind: \"option\" | \"custom\" | \"multi\";\n\tanswer: string | null;\n\tselected?: string[];\n\tnotes?: AnswerNote[];\n\tpreview?: string;\n}\n\nexport type QuestionOutcome = \"answered\" | \"cancelled\" | \"needs_clarification\" | \"error\";\n\nexport interface QuestionToolDetails {\n\tanswers: QuestionAnswer[];\n\toutcome: QuestionOutcome;\n\tcancelled: boolean;\n\terror?: QuestionToolError;\n\tmessage?: string;\n}\n\nexport interface CustomAnswer {\n\ttext: string;\n\tselected: boolean;\n}\n\nexport interface QuestionState {\n\toptionIndex: number;\n\tsingleAnswer?: QuestionAnswer;\n\tmultiSelected: Set<number>;\n\tcustomAnswer?: CustomAnswer;\n\tnotesByOption: Map<string, string>;\n\twarning?: string;\n}\n\nexport interface DialogResult {\n\tanswers: QuestionAnswer[];\n\toutcome: QuestionOutcome;\n}\n\nexport type DisplayOption =\n\t| (QuestionOption & { kind: \"option\"; optionIndex: number })\n\t| { kind: \"other\"; label: string; isOther: true };\n\nexport const OTHER_OPTION: DisplayOption = { kind: \"other\", label: \"Type something\", isOther: true };\n"]}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { type Api, type Context, createAssistantMessageEventStream, type Model, type SimpleStreamOptions } from "@earendil-works/pi-ai/compat";
|
|
13
13
|
export declare function streamRouterCodex(model: Model<Api>, context: Context, options?: SimpleStreamOptions): ReturnType<typeof createAssistantMessageEventStream>;
|
|
14
|
+
export declare function sanitizeInputItemsForCodex(input: unknown): void;
|
|
14
15
|
export declare function resolveResponsesUrl(baseUrl: string): string;
|
|
15
16
|
export declare function isRouterApi(api: string): boolean;
|
|
16
17
|
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACN,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,iCAAiC,EACjC,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,MAAM,8BAA8B,CAAC;AAMtC,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,UAAU,CAAC,OAAO,iCAAiC,CAAC,CA0EtD;AA4HD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD","sourcesContent":["/**\n * Codex-oriented Responses client for API relays.\n *\n * Follows Pi's documented custom-provider pattern (see coding-agent docs\n * providers.md / custom-provider.md and examples/custom-provider-gitlab-duo):\n * wrap a built-in pi-ai stream API from `@earendil-works/pi-ai/compat` instead of\n * reimplementing SSE or deep-importing internal modules.\n *\n * We use openAIResponsesApi (works with relay sk- keys) and reshape the request\n * payload toward Codex CLI style so transparent gateways receive a friendlier body.\n */\n\nimport {\n\ttype Api,\n\ttype Context,\n\tcreateAssistantMessageEventStream,\n\ttype Model,\n\topenAIResponsesApi,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai/compat\";\n\nimport { DEFAULTS, formatError, ROUTER_API } from \"./constants.ts\";\n\nconst responsesApi = openAIResponsesApi();\n\nexport function streamRouterCodex(\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): ReturnType<typeof createAssistantMessageEventStream> {\n\tconst stream = createAssistantMessageEventStream();\n\n\tvoid (async () => {\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);\n\n\t\t\t// Built-in Responses stream expects api \"openai-responses\" on the model object.\n\t\t\tconst requestModel = {\n\t\t\t\t...model,\n\t\t\t\tapi: \"openai-responses\" as const,\n\t\t\t\tcompat: {\n\t\t\t\t\tsupportsDeveloperRole: true,\n\t\t\t\t\t// Avoid underscore session_id header that strict proxies reject.\n\t\t\t\t\tsessionAffinityFormat: \"openai-nosession\" as const,\n\t\t\t\t\t// Codex-style upstreams reject prompt_cache_retention: \"24h\".\n\t\t\t\t\tsupportsLongCacheRetention: false,\n\t\t\t\t\t...(model.compat ?? {}),\n\t\t\t\t},\n\t\t\t} as Model<\"openai-responses\">;\n\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\toriginator: DEFAULTS.originator,\n\t\t\t\t...(options?.headers as Record<string, string> | undefined),\n\t\t\t};\n\n\t\t\t// Prefer hyphenated Codex-style session affinity when we have a session id.\n\t\t\tconst sessionId = clampCacheKey(options?.sessionId);\n\t\t\tif (sessionId) {\n\t\t\t\tif (!headers[\"session-id\"]) headers[\"session-id\"] = sessionId;\n\t\t\t\tif (!headers[\"x-client-request-id\"]) headers[\"x-client-request-id\"] = sessionId;\n\t\t\t}\n\n\t\t\tconst inner = responsesApi.streamSimple(requestModel, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tonPayload: (payload) => reshapePayloadForRelay(payload, context, options?.onPayload, requestModel),\n\t\t\t});\n\n\t\t\tfor await (const event of inner) {\n\t\t\t\tstream.push(event);\n\t\t\t}\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Match examples/custom-provider-gitlab-duo error event shape.\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: options?.signal?.aborted ? \"aborted\" : \"error\",\n\t\t\t\terror: {\n\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\tcontent: [],\n\t\t\t\t\tapi: model.api,\n\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\tmodel: model.id,\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t},\n\t\t\t\t\tstopReason: options?.signal?.aborted ? \"aborted\" : \"error\",\n\t\t\t\t\terrorMessage: formatError(error),\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Nudge the OpenAI Responses payload toward Codex CLI request shape for\n * transparent relays: instructions + input, store:false, no max_output_tokens /\n * prompt_cache_retention, verbosity + parallel_tool_calls.\n */\nasync function reshapePayloadForRelay(\n\tpayload: unknown,\n\tcontext: Context,\n\tuserOnPayload: SimpleStreamOptions[\"onPayload\"],\n\tmodel: Model<\"openai-responses\">,\n): Promise<unknown> {\n\tconst base =\n\t\tpayload && typeof payload === \"object\" && !Array.isArray(payload)\n\t\t\t? { ...(payload as Record<string, unknown>) }\n\t\t\t: ({} as Record<string, unknown>);\n\n\t// Always safe for ChatGPT/Codex-style backends.\n\tbase.store = false;\n\tbase.stream = true;\n\n\t// Codex CLI sends system prompt as `instructions`, not as a role message in input.\n\tconst { instructions, input } = extractInstructions(base.input, context.systemPrompt);\n\tif (instructions) base.instructions = instructions;\n\tif (input !== undefined) base.input = input;\n\n\t// Fields common on Codex CLI / rejected by many transparent Codex upstreams.\n\tif (!base.text || typeof base.text !== \"object\") {\n\t\tbase.text = { verbosity: \"low\" };\n\t}\n\tif (base.tool_choice === undefined) base.tool_choice = \"auto\";\n\tif (base.parallel_tool_calls === undefined) base.parallel_tool_calls = true;\n\n\t// Prefer encrypted reasoning content for multi-turn store:false sessions.\n\tconst include = Array.isArray(base.include) ? [...(base.include as unknown[])] : [];\n\tif (!include.includes(\"reasoning.encrypted_content\")) {\n\t\tinclude.push(\"reasoning.encrypted_content\");\n\t}\n\tbase.include = include;\n\n\t// Drop Platform-only fields that Codex OAuth endpoints often 400 on.\n\tdelete base.prompt_cache_retention;\n\tdelete base.max_output_tokens;\n\tdelete base.temperature;\n\tdelete base.top_p;\n\tdelete base.user;\n\tdelete base.metadata;\n\tdelete base.service_tier;\n\tdelete base.truncation;\n\tdelete base.context_management;\n\tdelete base.safety_identifier;\n\tdelete base.stream_options;\n\n\tif (userOnPayload) {\n\t\tconst next = await userOnPayload(base, model);\n\t\tif (next !== undefined) return next;\n\t}\n\treturn base;\n}\n\nfunction extractInstructions(\n\tinput: unknown,\n\tsystemPrompt: string | undefined,\n): { instructions?: string; input: unknown } {\n\t// Prefer context.systemPrompt (matches Codex stream path).\n\tif (systemPrompt && systemPrompt.length > 0) {\n\t\tconst stripped = stripLeadingSystemRoles(input);\n\t\treturn { instructions: systemPrompt, input: stripped ?? input };\n\t}\n\n\tif (!Array.isArray(input) || input.length === 0) {\n\t\treturn { input };\n\t}\n\n\tconst first = input[0];\n\tif (\n\t\tfirst &&\n\t\ttypeof first === \"object\" &&\n\t\t!Array.isArray(first) &&\n\t\t\"role\" in first &&\n\t\t((first as { role?: string }).role === \"system\" || (first as { role?: string }).role === \"developer\")\n\t) {\n\t\tconst content = (first as { content?: unknown }).content;\n\t\tconst text =\n\t\t\ttypeof content === \"string\"\n\t\t\t\t? content\n\t\t\t\t: Array.isArray(content)\n\t\t\t\t\t? content\n\t\t\t\t\t\t\t.map((part) =>\n\t\t\t\t\t\t\t\tpart && typeof part === \"object\" && \"text\" in part\n\t\t\t\t\t\t\t\t\t? String((part as { text: unknown }).text)\n\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t: \"\";\n\t\treturn {\n\t\t\tinstructions: text || undefined,\n\t\t\tinput: input.slice(1),\n\t\t};\n\t}\n\n\treturn { input };\n}\n\nfunction stripLeadingSystemRoles(input: unknown): unknown {\n\tif (!Array.isArray(input) || input.length === 0) return input;\n\tconst first = input[0];\n\tif (\n\t\tfirst &&\n\t\ttypeof first === \"object\" &&\n\t\t!Array.isArray(first) &&\n\t\t((first as { role?: string }).role === \"system\" || (first as { role?: string }).role === \"developer\")\n\t) {\n\t\treturn input.slice(1);\n\t}\n\treturn input;\n}\n\nfunction clampCacheKey(sessionId: string | undefined): string | undefined {\n\tif (!sessionId) return undefined;\n\treturn sessionId.length <= 64 ? sessionId : sessionId.slice(0, 64);\n}\n\nexport function resolveResponsesUrl(baseUrl: string): string {\n\tconst normalized = baseUrl.trim().replace(/\\/+$/, \"\");\n\tif (!normalized) throw new Error(\"Model baseUrl is empty.\");\n\tif (normalized.endsWith(\"/responses\")) return normalized;\n\treturn `${normalized}/responses`;\n}\n\nexport function isRouterApi(api: string): boolean {\n\treturn api === ROUTER_API;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACN,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,iCAAiC,EACjC,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,MAAM,8BAA8B,CAAC;AAMtC,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,UAAU,CAAC,OAAO,iCAAiC,CAAC,CA0EtD;AAiHD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAyB/D;AAiED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD","sourcesContent":["/**\n * Codex-oriented Responses client for API relays.\n *\n * Follows Pi's documented custom-provider pattern (see coding-agent docs\n * providers.md / custom-provider.md and examples/custom-provider-gitlab-duo):\n * wrap a built-in pi-ai stream API from `@earendil-works/pi-ai/compat` instead of\n * reimplementing SSE or deep-importing internal modules.\n *\n * We use openAIResponsesApi (works with relay sk- keys) and reshape the request\n * payload toward Codex CLI style so transparent gateways receive a friendlier body.\n */\n\nimport {\n\ttype Api,\n\ttype Context,\n\tcreateAssistantMessageEventStream,\n\ttype Model,\n\topenAIResponsesApi,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai/compat\";\n\nimport { DEFAULTS, formatError, ROUTER_API } from \"./constants.ts\";\n\nconst responsesApi = openAIResponsesApi();\n\nexport function streamRouterCodex(\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): ReturnType<typeof createAssistantMessageEventStream> {\n\tconst stream = createAssistantMessageEventStream();\n\n\tvoid (async () => {\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);\n\n\t\t\t// Built-in Responses stream expects api \"openai-responses\" on the model object.\n\t\t\tconst requestModel = {\n\t\t\t\t...model,\n\t\t\t\tapi: \"openai-responses\" as const,\n\t\t\t\tcompat: {\n\t\t\t\t\tsupportsDeveloperRole: true,\n\t\t\t\t\t// Avoid underscore session_id header that strict proxies reject.\n\t\t\t\t\tsessionAffinityFormat: \"openai-nosession\" as const,\n\t\t\t\t\t// Codex-style upstreams reject prompt_cache_retention: \"24h\".\n\t\t\t\t\tsupportsLongCacheRetention: false,\n\t\t\t\t\t...(model.compat ?? {}),\n\t\t\t\t},\n\t\t\t} as Model<\"openai-responses\">;\n\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\toriginator: DEFAULTS.originator,\n\t\t\t\t...(options?.headers as Record<string, string> | undefined),\n\t\t\t};\n\n\t\t\t// Prefer hyphenated Codex-style session affinity when we have a session id.\n\t\t\tconst sessionId = clampCacheKey(options?.sessionId);\n\t\t\tif (sessionId) {\n\t\t\t\tif (!headers[\"session-id\"]) headers[\"session-id\"] = sessionId;\n\t\t\t\tif (!headers[\"x-client-request-id\"]) headers[\"x-client-request-id\"] = sessionId;\n\t\t\t}\n\n\t\t\tconst inner = responsesApi.streamSimple(requestModel, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tonPayload: (payload) => reshapePayloadForRelay(payload, context, options?.onPayload, requestModel),\n\t\t\t});\n\n\t\t\tfor await (const event of inner) {\n\t\t\t\tstream.push(event);\n\t\t\t}\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Match examples/custom-provider-gitlab-duo error event shape.\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: options?.signal?.aborted ? \"aborted\" : \"error\",\n\t\t\t\terror: {\n\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\tcontent: [],\n\t\t\t\t\tapi: model.api,\n\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\tmodel: model.id,\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t},\n\t\t\t\t\tstopReason: options?.signal?.aborted ? \"aborted\" : \"error\",\n\t\t\t\t\terrorMessage: formatError(error),\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Nudge the OpenAI Responses payload toward Codex CLI request shape for\n * transparent relays: instructions + input, store:false, no max_output_tokens /\n * prompt_cache_retention, verbosity + parallel_tool_calls.\n */\nasync function reshapePayloadForRelay(\n\tpayload: unknown,\n\tcontext: Context,\n\tuserOnPayload: SimpleStreamOptions[\"onPayload\"],\n\tmodel: Model<\"openai-responses\">,\n): Promise<unknown> {\n\tconst base =\n\t\tpayload && typeof payload === \"object\" && !Array.isArray(payload)\n\t\t\t? { ...(payload as Record<string, unknown>) }\n\t\t\t: ({} as Record<string, unknown>);\n\n\t// Always safe for ChatGPT/Codex-style backends.\n\tbase.store = false;\n\tbase.stream = true;\n\n\t// Codex CLI sends system prompt as `instructions`, not as a role message in input.\n\tconst { instructions, input } = extractInstructions(base.input, context.systemPrompt);\n\tif (instructions) base.instructions = instructions;\n\tif (input !== undefined) base.input = input;\n\n\t// Released Codex CLI defaults omit replayed ResponseItem ids from store:false\n\t// requests. It also never emits `status` on the item types below, and its\n\t// output_text content has no `annotations`.\n\tsanitizeInputItemsForCodex(base.input);\n\n\t// Fields common on Codex CLI / rejected by many transparent Codex upstreams.\n\tif (!base.text || typeof base.text !== \"object\") {\n\t\tbase.text = { verbosity: \"low\" };\n\t}\n\tif (base.tool_choice === undefined) base.tool_choice = \"auto\";\n\tif (base.parallel_tool_calls === undefined) base.parallel_tool_calls = true;\n\n\t// Prefer encrypted reasoning content for multi-turn store:false sessions.\n\tconst include = Array.isArray(base.include) ? [...(base.include as unknown[])] : [];\n\tif (!include.includes(\"reasoning.encrypted_content\")) {\n\t\tinclude.push(\"reasoning.encrypted_content\");\n\t}\n\tbase.include = include;\n\n\t// Drop Platform-only fields that Codex OAuth endpoints often 400 on.\n\tdelete base.prompt_cache_retention;\n\tdelete base.prompt_cache_options;\n\tdelete base.max_output_tokens;\n\tdelete base.temperature;\n\tdelete base.top_p;\n\tdelete base.user;\n\tdelete base.metadata;\n\tdelete base.service_tier;\n\tdelete base.truncation;\n\tdelete base.context_management;\n\tdelete base.safety_identifier;\n\tdelete base.stream_options;\n\n\tif (userOnPayload) {\n\t\tconst next = await userOnPayload(base, model);\n\t\tif (next !== undefined) return next;\n\t}\n\treturn base;\n}\n\n/**\n * Match the released Codex CLI's default stateless request preparation:\n * optional ResponseItem identity ids are omitted from store:false requests,\n * while semantic ids, call_id, and encrypted_content remain available for\n * reference, tool, and reasoning continuity.\n *\n * These are the tagged variants handled by ResponseItem::set_id in Codex CLI\n * 0.145. Unknown input variants must retain `id`: for example,\n * item_reference.id and local_shell_call_output.id are required references,\n * not optional ResponseItem identities.\n */\nconst RESPONSE_ITEM_ID_TYPES = new Set([\n\t\"additional_tools\",\n\t\"message\",\n\t\"agent_message\",\n\t\"reasoning\",\n\t\"local_shell_call\",\n\t\"function_call\",\n\t\"tool_search_call\",\n\t\"function_call_output\",\n\t\"custom_tool_call\",\n\t\"custom_tool_call_output\",\n\t\"tool_search_output\",\n\t\"web_search_call\",\n\t\"image_generation_call\",\n\t\"compaction\",\n\t\"compaction_summary\",\n\t\"context_compaction\",\n]);\n\n/**\n * Codex ResponseItem serialization has NO `status` field on Message,\n * Reasoning, FunctionCall, FunctionCallOutput, or CustomToolCallOutput\n * (codex-rs/protocol/src/models.rs). Other item types either require `status`\n * (local_shell_call, tool_search_output, image_generation_call) or accept it\n * optionally (tool_search_call, custom_tool_call, web_search_call), so we must\n * not strip it there.\n */\nconst STATUS_LESS_ITEM_TYPES = new Set([\n\t\"message\",\n\t\"reasoning\",\n\t\"function_call\",\n\t\"function_call_output\",\n\t\"custom_tool_call_output\",\n]);\n\nexport function sanitizeInputItemsForCodex(input: unknown): void {\n\tif (!Array.isArray(input)) return;\n\tfor (const item of input) {\n\t\tif (!item || typeof item !== \"object\" || Array.isArray(item)) continue;\n\t\tconst record = item as Record<string, unknown>;\n\t\tconst type = typeof record.type === \"string\" ? record.type : undefined;\n\t\tif (type && RESPONSE_ITEM_ID_TYPES.has(type)) {\n\t\t\tdelete record.id;\n\t\t}\n\t\tif (type && STATUS_LESS_ITEM_TYPES.has(type) && \"status\" in record) {\n\t\t\tdelete record.status;\n\t\t}\n\t\tif (Array.isArray(record.content)) {\n\t\t\tfor (const part of record.content) {\n\t\t\t\tif (\n\t\t\t\t\tpart &&\n\t\t\t\t\ttypeof part === \"object\" &&\n\t\t\t\t\t(part as { type?: string }).type === \"output_text\" &&\n\t\t\t\t\t\"annotations\" in part\n\t\t\t\t) {\n\t\t\t\t\tdelete (part as Record<string, unknown>).annotations;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction extractInstructions(\n\tinput: unknown,\n\tsystemPrompt: string | undefined,\n): { instructions?: string; input: unknown } {\n\t// Prefer context.systemPrompt (matches Codex stream path).\n\tif (systemPrompt && systemPrompt.length > 0) {\n\t\tconst stripped = stripLeadingSystemRoles(input);\n\t\treturn { instructions: systemPrompt, input: stripped ?? input };\n\t}\n\n\tif (!Array.isArray(input) || input.length === 0) {\n\t\treturn { input };\n\t}\n\n\tconst first = input[0];\n\tif (\n\t\tfirst &&\n\t\ttypeof first === \"object\" &&\n\t\t!Array.isArray(first) &&\n\t\t\"role\" in first &&\n\t\t((first as { role?: string }).role === \"system\" || (first as { role?: string }).role === \"developer\")\n\t) {\n\t\tconst content = (first as { content?: unknown }).content;\n\t\tconst text =\n\t\t\ttypeof content === \"string\"\n\t\t\t\t? content\n\t\t\t\t: Array.isArray(content)\n\t\t\t\t\t? content\n\t\t\t\t\t\t\t.map((part) =>\n\t\t\t\t\t\t\t\tpart && typeof part === \"object\" && \"text\" in part\n\t\t\t\t\t\t\t\t\t? String((part as { text: unknown }).text)\n\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t: \"\";\n\t\treturn {\n\t\t\tinstructions: text || undefined,\n\t\t\tinput: input.slice(1),\n\t\t};\n\t}\n\n\treturn { input };\n}\n\nfunction stripLeadingSystemRoles(input: unknown): unknown {\n\tif (!Array.isArray(input) || input.length === 0) return input;\n\tconst first = input[0];\n\tif (\n\t\tfirst &&\n\t\ttypeof first === \"object\" &&\n\t\t!Array.isArray(first) &&\n\t\t((first as { role?: string }).role === \"system\" || (first as { role?: string }).role === \"developer\")\n\t) {\n\t\treturn input.slice(1);\n\t}\n\treturn input;\n}\n\nfunction clampCacheKey(sessionId: string | undefined): string | undefined {\n\tif (!sessionId) return undefined;\n\treturn sessionId.length <= 64 ? sessionId : sessionId.slice(0, 64);\n}\n\nexport function resolveResponsesUrl(baseUrl: string): string {\n\tconst normalized = baseUrl.trim().replace(/\\/+$/, \"\");\n\tif (!normalized) throw new Error(\"Model baseUrl is empty.\");\n\tif (normalized.endsWith(\"/responses\")) return normalized;\n\treturn `${normalized}/responses`;\n}\n\nexport function isRouterApi(api: string): boolean {\n\treturn api === ROUTER_API;\n}\n"]}
|