pi-ui-extend 0.1.9 → 0.1.13
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 +23 -2
- package/dist/app/app.d.ts +4 -0
- package/dist/app/app.js +76 -7
- package/dist/app/cli/install.d.ts +16 -0
- package/dist/app/cli/install.js +34 -7
- package/dist/app/cli/startup-info.js +5 -2
- package/dist/app/cli/update.d.ts +7 -0
- package/dist/app/cli/update.js +11 -3
- package/dist/app/commands/command-controller.js +4 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-model-actions.d.ts +5 -0
- package/dist/app/commands/command-model-actions.js +104 -0
- package/dist/app/commands/command-navigation-actions.d.ts +6 -1
- package/dist/app/commands/command-navigation-actions.js +37 -14
- package/dist/app/commands/command-registry.d.ts +4 -0
- package/dist/app/commands/command-registry.js +32 -0
- package/dist/app/commands/command-session-actions.d.ts +1 -0
- package/dist/app/commands/command-session-actions.js +15 -5
- package/dist/app/commands/shell-command.d.ts +7 -0
- package/dist/app/commands/shell-command.js +12 -4
- package/dist/app/commands/shell-controller.d.ts +1 -0
- package/dist/app/commands/shell-controller.js +1 -1
- package/dist/app/constants.d.ts +1 -1
- package/dist/app/constants.js +1 -1
- package/dist/app/icons.d.ts +1 -0
- package/dist/app/icons.js +3 -1
- package/dist/app/input/autocomplete-controller.d.ts +52 -0
- package/dist/app/input/autocomplete-controller.js +352 -0
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +21 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +2 -0
- package/dist/app/input/input-paste-handler.d.ts +1 -0
- package/dist/app/input/input-paste-handler.js +22 -18
- package/dist/app/input/prompt-enhancer-controller.d.ts +7 -1
- package/dist/app/input/prompt-enhancer-controller.js +12 -3
- package/dist/app/input/voice-controller.d.ts +51 -1
- package/dist/app/input/voice-controller.js +42 -19
- package/dist/app/model/model-usage-status.d.ts +9 -0
- package/dist/app/model/model-usage-status.js +124 -34
- package/dist/app/popup/popup-action-controller.js +1 -1
- package/dist/app/process.d.ts +17 -0
- package/dist/app/process.js +68 -0
- package/dist/app/rendering/conversation-entry-renderer.js +8 -6
- package/dist/app/rendering/conversation-tool-renderer.js +3 -2
- package/dist/app/rendering/editor-layout-renderer.d.ts +1 -0
- package/dist/app/rendering/editor-layout-renderer.js +11 -1
- package/dist/app/rendering/message-content.js +65 -7
- package/dist/app/rendering/render-controller.js +6 -1
- package/dist/app/rendering/render-text.d.ts +3 -0
- package/dist/app/rendering/render-text.js +51 -3
- package/dist/app/rendering/status-line-renderer.d.ts +5 -1
- package/dist/app/rendering/status-line-renderer.js +61 -25
- package/dist/app/rendering/toast-renderer.js +10 -13
- package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
- package/dist/app/rendering/tool-block-renderer.js +16 -33
- package/dist/app/runtime.d.ts +6 -1
- package/dist/app/runtime.js +35 -2
- package/dist/app/screen/clipboard.d.ts +11 -2
- package/dist/app/screen/clipboard.js +29 -21
- package/dist/app/screen/file-link-opener.d.ts +8 -0
- package/dist/app/screen/file-link-opener.js +11 -3
- package/dist/app/screen/file-links.js +3 -3
- package/dist/app/screen/image-opener.d.ts +12 -0
- package/dist/app/screen/image-opener.js +13 -5
- package/dist/app/screen/mouse-controller.d.ts +5 -2
- package/dist/app/screen/mouse-controller.js +16 -1
- package/dist/app/screen/screen-styler.d.ts +4 -1
- package/dist/app/screen/screen-styler.js +3 -2
- package/dist/app/screen/status-controller.d.ts +3 -0
- package/dist/app/screen/status-controller.js +23 -8
- package/dist/app/session/queued-message-controller.d.ts +7 -1
- package/dist/app/session/queued-message-controller.js +36 -21
- package/dist/app/session/resume-session-loader.d.ts +15 -0
- package/dist/app/session/resume-session-loader.js +204 -0
- package/dist/app/session/session-event-controller.d.ts +5 -1
- package/dist/app/session/session-event-controller.js +72 -5
- package/dist/app/session/session-history.js +4 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +5 -0
- package/dist/app/session/session-lifecycle-controller.js +9 -1
- package/dist/app/session/tabs-controller.d.ts +10 -1
- package/dist/app/session/tabs-controller.js +101 -5
- package/dist/app/terminal/nerd-font-controller.d.ts +16 -0
- package/dist/app/terminal/nerd-font-controller.js +30 -23
- package/dist/app/terminal/terminal-controller.d.ts +1 -0
- package/dist/app/terminal/terminal-controller.js +1 -0
- package/dist/app/types.d.ts +14 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -1
- package/dist/app/workspace/workspace-actions-controller.js +3 -3
- package/dist/app/workspace/workspace-undo.d.ts +1 -1
- package/dist/app/workspace/workspace-undo.js +22 -20
- package/dist/config.d.ts +27 -0
- package/dist/config.js +174 -1
- package/dist/default-pix-config.js +39 -353
- package/dist/input-editor.d.ts +7 -1
- package/dist/input-editor.js +47 -6
- package/dist/markdown-format.d.ts +1 -0
- package/dist/markdown-format.js +26 -1
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +177 -0
- package/dist/schemas/pi-tools-suite-schema.js +218 -0
- package/dist/schemas/pix-schema.d.ts +65 -0
- package/dist/schemas/pix-schema.js +91 -0
- package/dist/terminal-width.js +73 -56
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +3 -0
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +46 -195
- package/external/pi-tools-suite/src/lib/lsp.ts +2 -1
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +8 -7
- package/external/pi-tools-suite/src/lsp/manager.ts +4 -4
- package/external/pi-tools-suite/src/repo-discovery/index.ts +49 -2
- package/external/pi-tools-suite/src/todo/index.ts +4 -2
- package/external/pi-tools-suite/src/todo/state/selectors.ts +4 -0
- package/external/pi-tools-suite/src/todo/todo.ts +2 -6
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +9 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +1 -1
- package/package.json +12 -3
- package/schemas/pi-tools-suite.json +881 -0
- package/schemas/pix.json +298 -0
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { isRecord } from "../guards.js";
|
|
2
|
+
const MAX_FORMAT_STRING_CHARS = 256 * 1024;
|
|
3
|
+
const MAX_RENDERED_CONTENT_CHARS = 512 * 1024;
|
|
4
|
+
const MAX_STRUCTURED_DEPTH = 8;
|
|
5
|
+
const MAX_STRUCTURED_ARRAY_ITEMS = 200;
|
|
6
|
+
const MAX_STRUCTURED_OBJECT_KEYS = 200;
|
|
7
|
+
const TRUNCATED_MARKER = "\n[… truncated …]";
|
|
2
8
|
export function stringifyUnknown(value) {
|
|
3
9
|
if (typeof value === "string")
|
|
4
10
|
return value;
|
|
@@ -13,7 +19,7 @@ export function stringifyUnknown(value) {
|
|
|
13
19
|
return name;
|
|
14
20
|
}
|
|
15
21
|
try {
|
|
16
|
-
return JSON.stringify(value, null, 2);
|
|
22
|
+
return JSON.stringify(normalizeStructuredValue(value), null, 2);
|
|
17
23
|
}
|
|
18
24
|
catch {
|
|
19
25
|
return String(value);
|
|
@@ -24,8 +30,10 @@ export function formatStructuredText(value) {
|
|
|
24
30
|
const trimmed = value.trim();
|
|
25
31
|
if (!trimmed)
|
|
26
32
|
return "(empty)";
|
|
33
|
+
if (trimmed.length > MAX_FORMAT_STRING_CHARS)
|
|
34
|
+
return truncateText(value, MAX_FORMAT_STRING_CHARS);
|
|
27
35
|
try {
|
|
28
|
-
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
36
|
+
return JSON.stringify(normalizeStructuredValue(JSON.parse(trimmed)), null, 2);
|
|
29
37
|
}
|
|
30
38
|
catch {
|
|
31
39
|
return value;
|
|
@@ -36,25 +44,40 @@ export function formatStructuredText(value) {
|
|
|
36
44
|
export function renderContent(content) {
|
|
37
45
|
const parts = [];
|
|
38
46
|
let imageCount = 0;
|
|
47
|
+
let renderedChars = 0;
|
|
48
|
+
const pushPart = (part) => {
|
|
49
|
+
const remaining = MAX_RENDERED_CONTENT_CHARS - renderedChars;
|
|
50
|
+
if (remaining <= 0)
|
|
51
|
+
return false;
|
|
52
|
+
const next = part.length > remaining ? truncateText(part, remaining) : part;
|
|
53
|
+
parts.push(next);
|
|
54
|
+
renderedChars += next.length;
|
|
55
|
+
return part.length <= remaining;
|
|
56
|
+
};
|
|
39
57
|
for (const item of content) {
|
|
40
58
|
if (!isRecord(item)) {
|
|
41
|
-
|
|
59
|
+
if (!pushPart(stringifyUnknown(item)))
|
|
60
|
+
break;
|
|
42
61
|
continue;
|
|
43
62
|
}
|
|
44
63
|
if (isImageContent(item)) {
|
|
45
64
|
imageCount += 1;
|
|
46
|
-
|
|
65
|
+
if (!pushPart(imageContentLabel(item, imageCount)))
|
|
66
|
+
break;
|
|
47
67
|
continue;
|
|
48
68
|
}
|
|
49
69
|
if (typeof item.text === "string") {
|
|
50
|
-
|
|
70
|
+
if (!pushPart(item.text))
|
|
71
|
+
break;
|
|
51
72
|
continue;
|
|
52
73
|
}
|
|
53
74
|
if (typeof item.thinking === "string") {
|
|
54
|
-
|
|
75
|
+
if (!pushPart(item.thinking))
|
|
76
|
+
break;
|
|
55
77
|
continue;
|
|
56
78
|
}
|
|
57
|
-
|
|
79
|
+
if (!pushPart(stringifyUnknown(item)))
|
|
80
|
+
break;
|
|
58
81
|
}
|
|
59
82
|
return parts.join("\n");
|
|
60
83
|
}
|
|
@@ -113,3 +136,38 @@ export function submittedUserDisplayText(displayText, promptText, images) {
|
|
|
113
136
|
return userImageLabels(images.length);
|
|
114
137
|
return promptText.trimEnd();
|
|
115
138
|
}
|
|
139
|
+
function truncateText(text, maxChars) {
|
|
140
|
+
if (text.length <= maxChars)
|
|
141
|
+
return text;
|
|
142
|
+
return `${text.slice(0, Math.max(0, maxChars))}${TRUNCATED_MARKER}`;
|
|
143
|
+
}
|
|
144
|
+
function normalizeStructuredValue(value, depth = 0, seen = new WeakSet()) {
|
|
145
|
+
if (typeof value === "string")
|
|
146
|
+
return truncateText(value, MAX_FORMAT_STRING_CHARS);
|
|
147
|
+
if (!value || typeof value !== "object")
|
|
148
|
+
return value;
|
|
149
|
+
if (depth >= MAX_STRUCTURED_DEPTH)
|
|
150
|
+
return "[… truncated: depth limit …]";
|
|
151
|
+
if (seen.has(value))
|
|
152
|
+
return "[… circular …]";
|
|
153
|
+
seen.add(value);
|
|
154
|
+
if (Array.isArray(value)) {
|
|
155
|
+
const items = value.slice(0, MAX_STRUCTURED_ARRAY_ITEMS).map((item) => normalizeStructuredValue(item, depth + 1, seen));
|
|
156
|
+
if (value.length > MAX_STRUCTURED_ARRAY_ITEMS)
|
|
157
|
+
items.push(`[… ${value.length - MAX_STRUCTURED_ARRAY_ITEMS} more items …]`);
|
|
158
|
+
return items;
|
|
159
|
+
}
|
|
160
|
+
if (value instanceof Error)
|
|
161
|
+
return value.message || value.name;
|
|
162
|
+
const output = {};
|
|
163
|
+
let count = 0;
|
|
164
|
+
for (const [key, child] of Object.entries(value)) {
|
|
165
|
+
if (count >= MAX_STRUCTURED_OBJECT_KEYS) {
|
|
166
|
+
output["…"] = "truncated: object key limit";
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
output[key] = normalizeStructuredValue(child, depth + 1, seen);
|
|
170
|
+
count += 1;
|
|
171
|
+
}
|
|
172
|
+
return output;
|
|
173
|
+
}
|
|
@@ -68,8 +68,11 @@ export class AppRenderController {
|
|
|
68
68
|
this.deps.mouseController.statusThinkingTarget = undefined;
|
|
69
69
|
this.deps.mouseController.statusContextTarget = undefined;
|
|
70
70
|
this.deps.mouseController.statusModelUsageTarget = undefined;
|
|
71
|
+
this.deps.mouseController.statusDraftQueueTarget = undefined;
|
|
72
|
+
this.deps.mouseController.statusUserJumpTarget = undefined;
|
|
71
73
|
this.deps.mouseController.statusThinkingExpandTarget = undefined;
|
|
72
74
|
this.deps.mouseController.statusCompactToolsTarget = undefined;
|
|
75
|
+
this.deps.mouseController.statusTerminalBellSoundTarget = undefined;
|
|
73
76
|
this.deps.mouseController.statusSessionTarget = undefined;
|
|
74
77
|
this.deps.mouseController.statusPromptEnhancerTarget = undefined;
|
|
75
78
|
this.deps.mouseController.statusVoiceMicTarget = undefined;
|
|
@@ -165,10 +168,11 @@ export class AppRenderController {
|
|
|
165
168
|
for (let index = 0; index < renderedInput.lines.length; index += 1) {
|
|
166
169
|
const inputLine = renderedInput.lines[index] ?? "";
|
|
167
170
|
const tagSpans = renderedInput.tagSpans[index];
|
|
171
|
+
const suggestionSpans = renderedInput.suggestionSpans?.[index] ?? [];
|
|
168
172
|
const row = toScreenRow(inputStartRow + index);
|
|
169
173
|
this.deps.mouseController.renderedRowTexts.set(row, inputLine);
|
|
170
174
|
const tagColor = this.deps.theme.colors.accent;
|
|
171
|
-
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, columns, tagColor, this.deps.theme.colors.inputBorder);
|
|
175
|
+
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, suggestionSpans, columns, tagColor, this.deps.theme.colors.muted, this.deps.theme.colors.inputBorder);
|
|
172
176
|
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, styledLine));
|
|
173
177
|
}
|
|
174
178
|
if (renderedInput.scrollBar && columns > 0) {
|
|
@@ -291,6 +295,7 @@ export class AppRenderController {
|
|
|
291
295
|
this.deps.mouseController.statusThinkingTarget = this.deps.statusLineRenderer.thinkingTarget(statusLayout.text, statusRow);
|
|
292
296
|
this.deps.mouseController.statusContextTarget = this.deps.statusLineRenderer.contextTarget(statusLayout.text, statusRow, statusLayout);
|
|
293
297
|
this.deps.mouseController.statusModelUsageTarget = this.deps.statusLineRenderer.modelUsageTarget(statusLayout.text, statusRow, statusLayout);
|
|
298
|
+
this.deps.mouseController.statusDraftQueueTarget = this.deps.statusLineRenderer.draftQueueTarget?.(statusLayout, statusRow);
|
|
294
299
|
this.deps.mouseController.statusUserJumpTarget = this.deps.statusLineRenderer.userJumpTarget?.(statusLayout, statusRow);
|
|
295
300
|
this.deps.mouseController.statusThinkingExpandTarget = this.deps.statusLineRenderer.thinkingExpandTarget?.(statusLayout, statusRow);
|
|
296
301
|
this.deps.mouseController.statusCompactToolsTarget = this.deps.statusLineRenderer.compactToolsTarget?.(statusLayout, statusRow);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Theme } from "../../theme.js";
|
|
2
2
|
import type { ToolStatusEntry } from "../types.js";
|
|
3
3
|
export declare function sanitizeText(text: string): string;
|
|
4
|
+
export declare function alertIconPrefixLength(text: string): number | undefined;
|
|
4
5
|
export declare function normalizePastedTextForDuplicateKey(text: string): string;
|
|
5
6
|
export declare function shortHash(text: string): string;
|
|
6
7
|
export declare function hasLspDiagnosticsAfterMutation(output: string): boolean;
|
|
7
8
|
export declare function hasToolLspDiagnosticsAfterMutation(entry: ToolStatusEntry): boolean;
|
|
9
|
+
export declare function lspDiagnosticSeverityForLine(line: string): "error" | "warning" | "hint" | undefined;
|
|
10
|
+
export declare function toolLspDiagnosticsAfterMutationSeverity(entry: ToolStatusEntry): "error" | "warning" | undefined;
|
|
8
11
|
export declare function toolStatusIcon(entry: ToolStatusEntry): string;
|
|
9
12
|
export declare function toolStatusIconColor(entry: ToolStatusEntry, colors: Theme["colors"]): string;
|
|
10
13
|
export declare function wrapLine(text: string, width: number): string[];
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { expandTabs, padOrTrimDisplay, sliceByDisplayWidth, stringDisplayWidth, wrapDisplayLine } from "../../terminal-width.js";
|
|
3
3
|
import { APP_ICONS } from "../icons.js";
|
|
4
|
+
const LSP_DIAGNOSTIC_ICON = "\u{f0026}";
|
|
4
5
|
export function sanitizeText(text) {
|
|
5
|
-
return expandTabs(text.replace(
|
|
6
|
+
return expandTabs(text.replace(/⚠️?|\u{f0026}/gu, APP_ICONS.alert).replace(/\x1b/g, "␛").replace(/\r/g, ""));
|
|
7
|
+
}
|
|
8
|
+
export function alertIconPrefixLength(text) {
|
|
9
|
+
if (text.startsWith(APP_ICONS.alert))
|
|
10
|
+
return APP_ICONS.alert.length;
|
|
11
|
+
if (text.startsWith(LSP_DIAGNOSTIC_ICON))
|
|
12
|
+
return LSP_DIAGNOSTIC_ICON.length;
|
|
13
|
+
return /^⚠️?/u.exec(text)?.[0].length;
|
|
6
14
|
}
|
|
7
15
|
export function normalizePastedTextForDuplicateKey(text) {
|
|
8
16
|
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -17,12 +25,49 @@ const LSP_DIAGNOSTIC_MUTATION_TOOLS = new Set(["apply_patch", "ast_apply"]);
|
|
|
17
25
|
export function hasToolLspDiagnosticsAfterMutation(entry) {
|
|
18
26
|
return LSP_DIAGNOSTIC_MUTATION_TOOLS.has(entry.toolName.toLowerCase()) && hasLspDiagnosticsAfterMutation(entry.output);
|
|
19
27
|
}
|
|
28
|
+
export function lspDiagnosticSeverityForLine(line) {
|
|
29
|
+
const counts = lspDiagnosticCounts(line);
|
|
30
|
+
const countSeverity = lspDiagnosticCountSeverity(counts);
|
|
31
|
+
if (countSeverity)
|
|
32
|
+
return countSeverity;
|
|
33
|
+
if (counts.length > 0)
|
|
34
|
+
return undefined;
|
|
35
|
+
const severityMatch = /(?:^|[^\p{L}\p{N}_])(?:diagnosticseverity\.)?(errors?|warnings?|warn|hints?)(?=$|[^\p{L}\p{N}_])/iu.exec(line);
|
|
36
|
+
const severity = severityMatch?.[1]?.toLowerCase();
|
|
37
|
+
if (!severity)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (severity.startsWith("error"))
|
|
40
|
+
return "error";
|
|
41
|
+
if (severity.startsWith("warn"))
|
|
42
|
+
return "warning";
|
|
43
|
+
return "hint";
|
|
44
|
+
}
|
|
45
|
+
function lspDiagnosticCounts(line) {
|
|
46
|
+
return [...line.matchAll(/\b(\d+)\s+(errors?|warnings?|hints?)\b/giu)];
|
|
47
|
+
}
|
|
48
|
+
function lspDiagnosticCountSeverity(counts) {
|
|
49
|
+
for (const severity of ["error", "warning", "hint"]) {
|
|
50
|
+
if (counts.some((match) => Number(match[1]) > 0 && match[2]?.toLowerCase().startsWith(severity)))
|
|
51
|
+
return severity;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
export function toolLspDiagnosticsAfterMutationSeverity(entry) {
|
|
56
|
+
if (!hasToolLspDiagnosticsAfterMutation(entry))
|
|
57
|
+
return undefined;
|
|
58
|
+
if (/\blsp\s+errors?\s+after\s+mutation\b/i.test(entry.output))
|
|
59
|
+
return "error";
|
|
60
|
+
const diagnosticLines = entry.output.split("\n").map((line) => line.trim());
|
|
61
|
+
if (diagnosticLines.some((line) => lspDiagnosticSeverityForLine(line) === "error"))
|
|
62
|
+
return "error";
|
|
63
|
+
return "warning";
|
|
64
|
+
}
|
|
20
65
|
export function toolStatusIcon(entry) {
|
|
21
66
|
if (entry.status === "running")
|
|
22
67
|
return APP_ICONS.timerSand;
|
|
23
68
|
if (entry.isError)
|
|
24
69
|
return APP_ICONS.closeCircle;
|
|
25
|
-
if (
|
|
70
|
+
if (toolLspDiagnosticsAfterMutationSeverity(entry))
|
|
26
71
|
return APP_ICONS.alert;
|
|
27
72
|
return APP_ICONS.checkCircle;
|
|
28
73
|
}
|
|
@@ -31,7 +76,10 @@ export function toolStatusIconColor(entry, colors) {
|
|
|
31
76
|
return colors.muted;
|
|
32
77
|
if (entry.isError)
|
|
33
78
|
return colors.error;
|
|
34
|
-
|
|
79
|
+
const lspSeverity = toolLspDiagnosticsAfterMutationSeverity(entry);
|
|
80
|
+
if (lspSeverity === "error")
|
|
81
|
+
return colors.error;
|
|
82
|
+
if (lspSeverity === "warning")
|
|
35
83
|
return colors.warning;
|
|
36
84
|
return colors.success;
|
|
37
85
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { Theme } from "../../theme.js";
|
|
3
|
-
import type { SessionActivity, StatusCompactToolsTarget, StatusContextTarget, StatusLineLayout, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
3
|
+
import type { SessionActivity, StatusCompactToolsTarget, StatusContextTarget, StatusDraftQueueTarget, StatusLineLayout, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
4
4
|
import type { ScreenStyler } from "../screen/screen-styler.js";
|
|
5
5
|
import { type ModelColorsConfig } from "../../config.js";
|
|
6
6
|
export type StatusLineRendererHost = {
|
|
@@ -26,6 +26,7 @@ export type StatusLineRendererHost = {
|
|
|
26
26
|
terminalBellSoundStatusWidgetEnabled(): boolean;
|
|
27
27
|
voiceStatusWidgetText(): string;
|
|
28
28
|
voiceStatusWidgetActive(): boolean;
|
|
29
|
+
queueableInputActive?(): boolean;
|
|
29
30
|
userMessageJumpMenuActive?(): boolean;
|
|
30
31
|
allThinkingExpandedActive?(): boolean;
|
|
31
32
|
superCompactToolsActive?(): boolean;
|
|
@@ -43,6 +44,7 @@ export declare class StatusLineRenderer {
|
|
|
43
44
|
voiceMicTarget(layout: StatusLineLayout, row: number): StatusVoiceMicTarget | undefined;
|
|
44
45
|
voiceLanguageTarget(layout: StatusLineLayout, row: number): StatusVoiceLanguageTarget | undefined;
|
|
45
46
|
userJumpTarget(layout: StatusLineLayout, row: number): StatusUserJumpTarget | undefined;
|
|
47
|
+
draftQueueTarget(layout: StatusLineLayout, row: number): StatusDraftQueueTarget | undefined;
|
|
46
48
|
thinkingExpandTarget(layout: StatusLineLayout, row: number): StatusThinkingExpandTarget | undefined;
|
|
47
49
|
compactToolsTarget(layout: StatusLineLayout, row: number): StatusCompactToolsTarget | undefined;
|
|
48
50
|
terminalBellSoundTarget(layout: StatusLineLayout, row: number): StatusTerminalBellSoundTarget | undefined;
|
|
@@ -50,6 +52,8 @@ export declare class StatusLineRenderer {
|
|
|
50
52
|
private segments;
|
|
51
53
|
private pushPromptEnhancerWidgetSegment;
|
|
52
54
|
private pushUserJumpWidgetSegment;
|
|
55
|
+
private pushDraftQueueWidgetSegment;
|
|
56
|
+
private draftQueueWidgetText;
|
|
53
57
|
private pushThinkingExpandWidgetSegment;
|
|
54
58
|
private pushCompactToolsWidgetSegment;
|
|
55
59
|
private pushTerminalBellSoundWidgetSegment;
|
|
@@ -13,13 +13,15 @@ export class StatusLineRenderer {
|
|
|
13
13
|
const contentWidth = Math.max(1, width);
|
|
14
14
|
const left = 0;
|
|
15
15
|
const statusDot = APP_ICONS.record;
|
|
16
|
+
const draftQueueButton = this.draftQueueWidgetText();
|
|
16
17
|
const userJumpButton = APP_ICONS.user;
|
|
17
18
|
const thinkingExpandButton = APP_ICONS.thinkingExpanded;
|
|
18
19
|
const compactToolsButton = APP_ICONS.compactTools;
|
|
19
20
|
const terminalBellSoundWidgetText = this.host.terminalBellSoundStatusWidgetText();
|
|
20
21
|
const promptEnhancerWidgetText = this.host.promptEnhancerStatusWidgetText();
|
|
21
22
|
const voiceWidgetText = this.host.voiceStatusWidgetText();
|
|
22
|
-
const
|
|
23
|
+
const rightWidgetParts = [draftQueueButton, promptEnhancerWidgetText, userJumpButton, terminalBellSoundWidgetText, thinkingExpandButton, compactToolsButton, voiceWidgetText];
|
|
24
|
+
const rightWidgetText = rightWidgetParts.filter((text) => text.length > 0).join(" ");
|
|
23
25
|
const rightWidgetWidth = stringDisplayWidth(rightWidgetText);
|
|
24
26
|
const leftWidth = rightWidgetWidth > 0 && contentWidth > rightWidgetWidth + 1 ? contentWidth - rightWidgetWidth - 1 : contentWidth;
|
|
25
27
|
const baseStatus = this.host.currentStatus();
|
|
@@ -34,37 +36,54 @@ export class StatusLineRenderer {
|
|
|
34
36
|
const innerText = leftWidth < contentWidth ? `${leftText} ${rightWidgetText}` : padOrTrimPlain(leftText, contentWidth);
|
|
35
37
|
const text = padOrTrimPlain(innerText, width);
|
|
36
38
|
let nextWidgetStartColumn = left + leftWidth + 2;
|
|
37
|
-
|
|
38
|
-
? this.widgetLayout(nextWidgetStartColumn,
|
|
39
|
+
let draftQueueWidget = leftWidth < contentWidth && draftQueueButton.length > 0
|
|
40
|
+
? this.widgetLayout(nextWidgetStartColumn, draftQueueButton)
|
|
39
41
|
: undefined;
|
|
40
|
-
if (
|
|
41
|
-
nextWidgetStartColumn =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
if (draftQueueWidget)
|
|
43
|
+
nextWidgetStartColumn = draftQueueWidget.endColumn + 1;
|
|
44
|
+
let promptEnhancerWidget;
|
|
45
|
+
let userJumpWidget;
|
|
46
|
+
let terminalBellSoundWidget;
|
|
47
|
+
let thinkingExpandWidget;
|
|
48
|
+
let compactToolsWidget;
|
|
49
|
+
const appendPromptEnhancerWidget = () => {
|
|
50
|
+
promptEnhancerWidget = leftWidth < contentWidth && promptEnhancerWidgetText.length > 0
|
|
51
|
+
? this.widgetLayout(nextWidgetStartColumn, promptEnhancerWidgetText)
|
|
52
|
+
: undefined;
|
|
53
|
+
if (promptEnhancerWidget)
|
|
54
|
+
nextWidgetStartColumn = promptEnhancerWidget.endColumn + 1;
|
|
55
|
+
};
|
|
56
|
+
const appendCoreStatusWidgets = () => {
|
|
57
|
+
userJumpWidget = leftWidth < contentWidth
|
|
58
|
+
? this.widgetLayout(nextWidgetStartColumn, userJumpButton)
|
|
59
|
+
: undefined;
|
|
60
|
+
if (userJumpWidget)
|
|
61
|
+
nextWidgetStartColumn = userJumpWidget.endColumn + 1;
|
|
62
|
+
terminalBellSoundWidget = leftWidth < contentWidth && terminalBellSoundWidgetText.length > 0
|
|
63
|
+
? this.widgetLayout(nextWidgetStartColumn, terminalBellSoundWidgetText)
|
|
64
|
+
: undefined;
|
|
65
|
+
if (terminalBellSoundWidget)
|
|
66
|
+
nextWidgetStartColumn = terminalBellSoundWidget.endColumn + 1;
|
|
67
|
+
thinkingExpandWidget = leftWidth < contentWidth
|
|
68
|
+
? this.widgetLayout(nextWidgetStartColumn, thinkingExpandButton)
|
|
69
|
+
: undefined;
|
|
70
|
+
if (thinkingExpandWidget)
|
|
71
|
+
nextWidgetStartColumn = thinkingExpandWidget.endColumn + 1;
|
|
72
|
+
compactToolsWidget = leftWidth < contentWidth
|
|
73
|
+
? this.widgetLayout(nextWidgetStartColumn, compactToolsButton)
|
|
74
|
+
: undefined;
|
|
75
|
+
if (compactToolsWidget)
|
|
76
|
+
nextWidgetStartColumn = compactToolsWidget.endColumn + 1;
|
|
77
|
+
};
|
|
78
|
+
appendPromptEnhancerWidget();
|
|
79
|
+
appendCoreStatusWidgets();
|
|
62
80
|
const voiceWidget = leftWidth < contentWidth && voiceWidgetText.length > 0 ? this.voiceWidgetLayout(nextWidgetStartColumn, voiceWidgetText) : undefined;
|
|
63
81
|
return {
|
|
64
82
|
details,
|
|
65
83
|
text,
|
|
66
84
|
sessionLabel,
|
|
67
85
|
workspaceLabel,
|
|
86
|
+
...(draftQueueWidget ? { draftQueueWidget } : {}),
|
|
68
87
|
...(userJumpWidget ? { userJumpWidget } : {}),
|
|
69
88
|
...(thinkingExpandWidget ? { thinkingExpandWidget } : {}),
|
|
70
89
|
...(compactToolsWidget ? { compactToolsWidget } : {}),
|
|
@@ -157,6 +176,12 @@ export class StatusLineRenderer {
|
|
|
157
176
|
return undefined;
|
|
158
177
|
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn };
|
|
159
178
|
}
|
|
179
|
+
draftQueueTarget(layout, row) {
|
|
180
|
+
const widget = layout.draftQueueWidget;
|
|
181
|
+
if (!widget)
|
|
182
|
+
return undefined;
|
|
183
|
+
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn };
|
|
184
|
+
}
|
|
160
185
|
thinkingExpandTarget(layout, row) {
|
|
161
186
|
const widget = layout.thinkingExpandWidget;
|
|
162
187
|
if (!widget)
|
|
@@ -195,6 +220,7 @@ export class StatusLineRenderer {
|
|
|
195
220
|
end: statusDotStart + APP_ICONS.record.length,
|
|
196
221
|
foreground: this.statusDotColor(),
|
|
197
222
|
}] : [];
|
|
223
|
+
this.pushDraftQueueWidgetSegment(segments, statusText);
|
|
198
224
|
this.pushUserJumpWidgetSegment(segments, statusText);
|
|
199
225
|
this.pushThinkingExpandWidgetSegment(segments, statusText);
|
|
200
226
|
this.pushCompactToolsWidgetSegment(segments, statusText);
|
|
@@ -248,6 +274,16 @@ export class StatusLineRenderer {
|
|
|
248
274
|
: this.host.theme.colors.muted;
|
|
249
275
|
this.pushSegment(segments, start, buttonText.length, foreground);
|
|
250
276
|
}
|
|
277
|
+
pushDraftQueueWidgetSegment(segments, statusText) {
|
|
278
|
+
const buttonText = this.draftQueueWidgetText();
|
|
279
|
+
const start = statusText.indexOf(buttonText);
|
|
280
|
+
if (start < 0 || buttonText.length <= 0)
|
|
281
|
+
return;
|
|
282
|
+
this.pushSegment(segments, start, buttonText.length, this.host.theme.colors.info);
|
|
283
|
+
}
|
|
284
|
+
draftQueueWidgetText() {
|
|
285
|
+
return this.host.queueableInputActive?.() ? APP_ICONS.timerSand : "";
|
|
286
|
+
}
|
|
251
287
|
pushThinkingExpandWidgetSegment(segments, statusText) {
|
|
252
288
|
const buttonText = APP_ICONS.thinkingExpanded;
|
|
253
289
|
const start = statusText.indexOf(buttonText);
|
|
@@ -64,11 +64,9 @@ function renderDialogToastOverlay(state, width, maxRows, theme, rowOffset) {
|
|
|
64
64
|
if (maxRows <= 0 || width <= 0)
|
|
65
65
|
return [];
|
|
66
66
|
const maxDialogWidth = Math.max(1, Math.min(width - 4, 72));
|
|
67
|
-
const icon = toastKindIcon(state.kind);
|
|
68
67
|
const closeLabel = `[${APP_ICONS.close}]`;
|
|
69
68
|
const wrappedLines = dialogMessageLines(state.message, Math.max(1, maxDialogWidth - 4));
|
|
70
|
-
const
|
|
71
|
-
const requiredWidth = Math.max(16, stringDisplayWidth(` ${title} ${closeLabel} `) + 2, ...wrappedLines.map((line) => stringDisplayWidth(line) + 4));
|
|
69
|
+
const requiredWidth = Math.max(16, stringDisplayWidth(closeLabel) + 4, ...wrappedLines.map((line) => stringDisplayWidth(line) + 4));
|
|
72
70
|
const dialogWidth = Math.min(maxDialogWidth, Math.max(16, requiredWidth));
|
|
73
71
|
const bodyWidth = Math.max(1, dialogWidth - 4);
|
|
74
72
|
const bodyLines = dialogMessageLines(state.message, bodyWidth);
|
|
@@ -76,14 +74,14 @@ function renderDialogToastOverlay(state, width, maxRows, theme, rowOffset) {
|
|
|
76
74
|
const visibleBodyLines = bodyLines.slice(0, bodyRows);
|
|
77
75
|
const includeBottom = maxRows > 1;
|
|
78
76
|
const dialogRows = [
|
|
79
|
-
dialogTopLine(
|
|
77
|
+
dialogTopLine(closeLabel, dialogWidth),
|
|
80
78
|
...visibleBodyLines.map((line) => `│ ${padOrTrimPlain(line, bodyWidth)} │`),
|
|
81
79
|
...(includeBottom ? [`╰${"─".repeat(Math.max(0, dialogWidth - 2))}╯`] : []),
|
|
82
80
|
].slice(0, maxRows);
|
|
83
81
|
const leftWidth = Math.max(0, width - dialogWidth - 2);
|
|
84
82
|
const column = leftWidth + 1;
|
|
85
83
|
const style = toastKindStyle(state.kind, theme);
|
|
86
|
-
const closeStartColumn = column + 1 + dialogTopCloseOffset(
|
|
84
|
+
const closeStartColumn = column + 1 + dialogTopCloseOffset(closeLabel, dialogWidth);
|
|
87
85
|
const closeEndColumn = closeStartColumn + stringDisplayWidth(closeLabel);
|
|
88
86
|
return dialogRows.map((text, index) => ({
|
|
89
87
|
id: state.id,
|
|
@@ -101,18 +99,17 @@ function dialogMessageLines(message, maxWidth) {
|
|
|
101
99
|
const lines = sanitizeText(message).split("\n").flatMap((line) => wrapDisplayLine(line, safeMaxWidth));
|
|
102
100
|
return lines.length > 0 ? lines : [""];
|
|
103
101
|
}
|
|
104
|
-
function dialogTopLine(
|
|
102
|
+
function dialogTopLine(closeLabel, width) {
|
|
105
103
|
const innerWidth = Math.max(0, width - 2);
|
|
106
|
-
const closeOffset = dialogTopCloseOffset(
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
return `╭${
|
|
104
|
+
const closeOffset = dialogTopCloseOffset(closeLabel, width);
|
|
105
|
+
const closeWidth = stringDisplayWidth(closeLabel);
|
|
106
|
+
const rightWidth = Math.max(0, innerWidth - closeOffset - closeWidth);
|
|
107
|
+
return `╭${"─".repeat(closeOffset)}${padOrTrimPlain(closeLabel, Math.min(closeWidth, innerWidth - closeOffset))}${"─".repeat(rightWidth)}╮`;
|
|
110
108
|
}
|
|
111
|
-
function dialogTopCloseOffset(
|
|
109
|
+
function dialogTopCloseOffset(closeLabel, width) {
|
|
112
110
|
const innerWidth = Math.max(0, width - 2);
|
|
113
|
-
const leftLabel = ` ${title} `;
|
|
114
111
|
const closeWidth = stringDisplayWidth(closeLabel);
|
|
115
|
-
return Math.max(
|
|
112
|
+
return Math.max(0, innerWidth - closeWidth - 1);
|
|
116
113
|
}
|
|
117
114
|
function toastKindIcon(kind) {
|
|
118
115
|
switch (kind) {
|
|
@@ -6,6 +6,7 @@ import type { ToolBodyLineStyle, ToolHeaderSegment } from "../../tool-renderers/
|
|
|
6
6
|
export type ToolBlockEntry = {
|
|
7
7
|
id: string;
|
|
8
8
|
toolName: string;
|
|
9
|
+
headerLabel?: string | undefined;
|
|
9
10
|
headerArgs?: string | undefined;
|
|
10
11
|
headerArgsSegments?: readonly ToolHeaderSegment[] | undefined;
|
|
11
12
|
bodyLineStyles?: readonly ToolBodyLineStyle[] | undefined;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { resolveColor } from "../../config.js";
|
|
2
2
|
import { expandTabs, sliceByDisplayWidth, stringDisplayWidth, wrapDisplayLineByWords } from "../../terminal-width.js";
|
|
3
|
-
import { hasToolLspDiagnosticsAfterMutation, sanitizeText, toolStatusIcon, toolStatusIconColor, wrapLine } from "./render-text.js";
|
|
4
|
-
const TRUNCATED_PREVIEW_MARKER = "
|
|
3
|
+
import { alertIconPrefixLength, hasToolLspDiagnosticsAfterMutation, lspDiagnosticSeverityForLine, sanitizeText, toolStatusIcon, toolStatusIconColor, wrapLine } from "./render-text.js";
|
|
4
|
+
const TRUNCATED_PREVIEW_MARKER = "▶ ";
|
|
5
5
|
export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
6
6
|
if (rule.hidden)
|
|
7
7
|
return [];
|
|
8
8
|
const hasLspDiagnostics = hasToolLspDiagnosticsAfterMutation(entry);
|
|
9
|
-
const expanded = entry.expanded
|
|
9
|
+
const expanded = entry.expanded;
|
|
10
10
|
const stateIcon = toolStatusIcon(entry);
|
|
11
11
|
const toolColor = resolveColor(rule.color, colors);
|
|
12
12
|
const toolOutputColor = colors.statusForeground;
|
|
13
|
-
const
|
|
13
|
+
const headerLabel = entry.headerLabel ?? entry.toolName;
|
|
14
|
+
const headerPrefix = headerLabel ? `${stateIcon} ${headerLabel}` : stateIcon;
|
|
14
15
|
const headerArgs = formatToolHeaderArgs(entry.headerArgs);
|
|
15
16
|
const headerArgsWidth = width - stringDisplayWidth(headerPrefix) - 1;
|
|
16
17
|
const clippedHeaderArgs = headerArgsWidth > 0 ? sliceByDisplayWidth(headerArgs, headerArgsWidth) : "";
|
|
@@ -31,7 +32,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
31
32
|
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi));
|
|
32
33
|
return headerLines;
|
|
33
34
|
}
|
|
34
|
-
if (rule.compactHidden || rule.defaultExpanded === true)
|
|
35
|
+
if (rule.compactHidden || (rule.defaultExpanded === true && !options.superCompact))
|
|
35
36
|
return headerLines;
|
|
36
37
|
const body = entry.collapsedBody.trimEnd();
|
|
37
38
|
if (!body || rule.previewLines === 0)
|
|
@@ -111,7 +112,10 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
111
112
|
segment.bold = diffStyle.bold;
|
|
112
113
|
line.segments = [segment];
|
|
113
114
|
}
|
|
114
|
-
else if (lspDiagnosticStyle) {
|
|
115
|
+
else if (lspDiagnosticStyle?.kind === "alert" && wrapIndex === 0) {
|
|
116
|
+
line.segments = [{ start: 2, end: 2 + lspDiagnosticStyle.length, foreground: colors.warning, bold: true }];
|
|
117
|
+
}
|
|
118
|
+
else if (lspDiagnosticStyle?.kind === "severity") {
|
|
115
119
|
line.segments = [{ start: 2, end: line.text.length, foreground: lspDiagnosticStyle.foreground }];
|
|
116
120
|
}
|
|
117
121
|
else if (bodyLineStyle && line.text.length > 2) {
|
|
@@ -348,37 +352,16 @@ function bodyLineStyleForLine(styles, lineIndex, colors) {
|
|
|
348
352
|
return resolvedForeground ? { ...segment, foreground: resolvedForeground } : segment;
|
|
349
353
|
}
|
|
350
354
|
function lspDiagnosticLineStyle(line, colors) {
|
|
355
|
+
const alertLength = alertIconPrefixLength(line);
|
|
356
|
+
if (alertLength != null)
|
|
357
|
+
return { kind: "alert", length: alertLength };
|
|
351
358
|
const severity = lspDiagnosticSeverityForLine(line);
|
|
352
359
|
if (severity === "error")
|
|
353
|
-
return { foreground: colors.error };
|
|
360
|
+
return { kind: "severity", foreground: colors.error };
|
|
354
361
|
if (severity === "warning")
|
|
355
|
-
return { foreground: colors.warning };
|
|
362
|
+
return { kind: "severity", foreground: colors.warning };
|
|
356
363
|
if (severity === "hint")
|
|
357
|
-
return { foreground: colors.muted };
|
|
358
|
-
return undefined;
|
|
359
|
-
}
|
|
360
|
-
function lspDiagnosticSeverityForLine(line) {
|
|
361
|
-
const countSeverity = lspDiagnosticCountSeverity(line);
|
|
362
|
-
if (countSeverity)
|
|
363
|
-
return countSeverity;
|
|
364
|
-
const severityMatch = /(?:^|[^\p{L}\p{N}_])(?:diagnosticseverity\.)?(errors?|warnings?|warn|hints?)(?=$|[^\p{L}\p{N}_])/iu.exec(line);
|
|
365
|
-
const severity = severityMatch?.[1]?.toLowerCase();
|
|
366
|
-
if (!severity)
|
|
367
|
-
return undefined;
|
|
368
|
-
if (severity.startsWith("error"))
|
|
369
|
-
return "error";
|
|
370
|
-
if (severity.startsWith("warn"))
|
|
371
|
-
return "warning";
|
|
372
|
-
return "hint";
|
|
373
|
-
}
|
|
374
|
-
function lspDiagnosticCountSeverity(line) {
|
|
375
|
-
const counts = [...line.matchAll(/\b(\d+)\s+(errors?|warnings?|hints?)\b/giu)];
|
|
376
|
-
if (counts.length === 0)
|
|
377
|
-
return undefined;
|
|
378
|
-
for (const severity of ["error", "warning", "hint"]) {
|
|
379
|
-
if (counts.some((match) => Number(match[1]) > 0 && match[2]?.toLowerCase().startsWith(severity)))
|
|
380
|
-
return severity;
|
|
381
|
-
}
|
|
364
|
+
return { kind: "severity", foreground: colors.muted };
|
|
382
365
|
return undefined;
|
|
383
366
|
}
|
|
384
367
|
function syntaxHighlightForLine(highlights, lineIndex) {
|
package/dist/app/runtime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type EventBus, type AgentSessionRuntime, type LoadExtensionsResult } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { SessionManager, type EventBus, type AgentSessionRuntime, type LoadExtensionsResult, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type PixConfig } from "../config.js";
|
|
2
3
|
import type { AppOptions } from "./types.js";
|
|
3
4
|
export type PiToolsSuiteInstallAction = "installed" | "already-installed" | "existing-kept" | "missing-source";
|
|
4
5
|
export type PiToolsSuiteInstallResult = {
|
|
@@ -36,4 +37,8 @@ export declare function prioritizeBundledQuestionExtension(base: LoadExtensionsR
|
|
|
36
37
|
export type CreatePixRuntimeOptions = {
|
|
37
38
|
eventBus?: EventBus;
|
|
38
39
|
};
|
|
40
|
+
type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
|
|
41
|
+
export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
|
|
42
|
+
export declare function resolveSessionModelRefFromTail(entries: readonly SessionEntry[]): string | undefined;
|
|
39
43
|
export declare function createPixRuntime(options: AppOptions, runtimeOptions?: CreatePixRuntimeOptions): Promise<AgentSessionRuntime>;
|
|
44
|
+
export {};
|
package/dist/app/runtime.js
CHANGED
|
@@ -4,8 +4,9 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { loadPixConfig, resolveDefaultModelRef } from "../config.js";
|
|
7
8
|
import { PI_FAVORITE_MODEL_REFS } from "./constants.js";
|
|
8
|
-
import { parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
9
|
+
import { isThinkingLevel, parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
9
10
|
const BUNDLED_QUESTION_EXTENSION_NAME = "question";
|
|
10
11
|
const PI_TOOLS_SUITE_EXTENSION_NAME = "pi-tools-suite";
|
|
11
12
|
const BUNDLED_EXTENSIONS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../..", "extensions");
|
|
@@ -131,10 +132,42 @@ function isBundledQuestionConflict(error, bundledExtensionPaths) {
|
|
|
131
132
|
}
|
|
132
133
|
return false;
|
|
133
134
|
}
|
|
135
|
+
export function resolvePixRuntimeModelRef(options, sessionManager, config = loadPixConfig()) {
|
|
136
|
+
if (options.modelRef)
|
|
137
|
+
return options.modelRef;
|
|
138
|
+
const existingEntryCount = sessionManager.getEntries().length;
|
|
139
|
+
if (existingEntryCount > 0)
|
|
140
|
+
return resolveSessionModelRefFromTail(sessionManager.getBranch());
|
|
141
|
+
return resolveDefaultModelRef(config);
|
|
142
|
+
}
|
|
143
|
+
export function resolveSessionModelRefFromTail(entries) {
|
|
144
|
+
let modelRef;
|
|
145
|
+
let thinkingLevel;
|
|
146
|
+
for (let index = entries.length - 1; index >= 0 && (modelRef === undefined || thinkingLevel === undefined); index--) {
|
|
147
|
+
const entry = entries[index];
|
|
148
|
+
if (!entry)
|
|
149
|
+
continue;
|
|
150
|
+
if (thinkingLevel === undefined && entry.type === "thinking_level_change" && isThinkingLevel(entry.thinkingLevel)) {
|
|
151
|
+
thinkingLevel = entry.thinkingLevel;
|
|
152
|
+
}
|
|
153
|
+
if (modelRef !== undefined)
|
|
154
|
+
continue;
|
|
155
|
+
if (entry.type === "model_change") {
|
|
156
|
+
modelRef = `${entry.provider}/${entry.modelId}`;
|
|
157
|
+
}
|
|
158
|
+
else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
159
|
+
modelRef = `${entry.message.provider}/${entry.message.model}`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!modelRef)
|
|
163
|
+
return undefined;
|
|
164
|
+
return thinkingLevel ? `${modelRef}:${thinkingLevel}` : modelRef;
|
|
165
|
+
}
|
|
134
166
|
export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
135
|
-
const parsedModel = options.modelRef ? parseModelRef(options.modelRef) : undefined;
|
|
136
167
|
const agentDir = getAgentDir();
|
|
137
168
|
const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
169
|
+
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager);
|
|
170
|
+
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
138
171
|
await ensureBundledSkillsInstalled();
|
|
139
172
|
await ensurePiToolsSuiteExtensionInstalled({ agentDir });
|
|
140
173
|
const bundledExtensionPaths = getBundledExtensionPaths();
|