pi-ui-extend 0.1.20 → 0.1.24
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 +1 -10
- package/bin/pix.mjs +11 -154
- package/dist/app/app.d.ts +4 -0
- package/dist/app/app.js +102 -17
- package/dist/app/cli/startup-info.d.ts +0 -1
- package/dist/app/cli/startup-info.js +0 -3
- package/dist/app/commands/command-session-actions.js +3 -0
- package/dist/app/extensions/extension-ui-controller.js +2 -2
- package/dist/app/input/voice-controller.d.ts +3 -2
- package/dist/app/input/voice-controller.js +9 -0
- package/dist/app/popup/popup-menu-controller.js +7 -1
- package/dist/app/rendering/conversation-entry-renderer.js +29 -10
- package/dist/app/rendering/conversation-tool-renderer.js +1 -1
- package/dist/app/rendering/conversation-viewport.d.ts +1 -5
- package/dist/app/rendering/conversation-viewport.js +9 -16
- package/dist/app/rendering/editor-layout-renderer.js +1 -1
- package/dist/app/rendering/render-text.d.ts +6 -0
- package/dist/app/rendering/render-text.js +9 -0
- package/dist/app/rendering/tab-line-renderer.js +1 -5
- package/dist/app/rendering/tool-block-renderer.d.ts +2 -0
- package/dist/app/rendering/tool-block-renderer.js +20 -2
- package/dist/app/runtime.d.ts +2 -0
- package/dist/app/runtime.js +27 -4
- package/dist/app/screen/mouse-controller.js +14 -6
- package/dist/app/screen/screen-styler.js +2 -2
- package/dist/app/session/session-event-controller.js +5 -4
- package/dist/app/session/session-lifecycle-controller.d.ts +2 -0
- package/dist/app/session/session-lifecycle-controller.js +43 -20
- package/dist/app/session/tabs-controller.d.ts +6 -2
- package/dist/app/session/tabs-controller.js +114 -30
- package/dist/app/types.d.ts +5 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +3 -0
- package/dist/app/workspace/workspace-actions-controller.js +71 -16
- package/dist/app/workspace/workspace-undo.js +41 -6
- package/dist/config.d.ts +1 -0
- package/dist/config.js +19 -7
- package/dist/markdown-format.d.ts +6 -0
- package/dist/markdown-format.js +11 -3
- package/dist/syntax-highlight.js +3 -1
- package/dist/theme.d.ts +3 -0
- package/dist/theme.js +53 -28
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +2 -1
- package/external/pi-tools-suite/src/telegram-mirror/README.md +81 -46
- package/external/pi-tools-suite/src/telegram-mirror/bot.ts +81 -10
- package/external/pi-tools-suite/src/telegram-mirror/events.ts +6 -38
- package/external/pi-tools-suite/src/telegram-mirror/index.ts +246 -40
- package/external/pi-tools-suite/src/telegram-mirror/ipc.ts +20 -0
- package/external/pi-tools-suite/src/telegram-mirror/multiplexer.ts +247 -17
- package/external/pi-tools-suite/src/telegram-mirror/renderer.ts +75 -78
- package/external/pi-tools-suite/src/todo/index.ts +7 -6
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +1 -1
- package/external/pi-tools-suite/src/web-search/index.ts +139 -2
- package/package.json +7 -7
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import { type PixConfig } from "../../config.js";
|
|
3
2
|
import type { Theme } from "../../theme.js";
|
|
4
3
|
import { type InlineUserMessageMenuContext } from "./conversation-entry-renderer.js";
|
|
5
|
-
import type { ConversationBlockCache, Entry, RenderedLine
|
|
4
|
+
import type { ConversationBlockCache, Entry, RenderedLine } from "../types.js";
|
|
6
5
|
export type ConversationViewportHost = {
|
|
7
6
|
readonly entries: readonly Entry[];
|
|
8
|
-
readonly session: AgentSession | undefined;
|
|
9
|
-
readonly deferredUserMessages: readonly SubmittedUserMessage[];
|
|
10
7
|
readonly entryRenderVersions: ReadonlyMap<string, number>;
|
|
11
8
|
readonly cwd: string;
|
|
12
9
|
readonly colors: Theme["colors"];
|
|
@@ -40,7 +37,6 @@ export declare class ConversationViewport {
|
|
|
40
37
|
blockForEntry(entry: Entry, width: number): ConversationBlockCache;
|
|
41
38
|
entryBlockPositions(width: number): ConversationEntryBlockPosition[];
|
|
42
39
|
measuredLineCountForEntries(width: number, entryIds: readonly string[]): number;
|
|
43
|
-
private queuedEntries;
|
|
44
40
|
private layoutForWidth;
|
|
45
41
|
private buildLayout;
|
|
46
42
|
private previousMeasuredLineCount;
|
|
@@ -2,7 +2,6 @@ import { resolveToolRule } from "../../config.js";
|
|
|
2
2
|
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
3
3
|
import { renderConversationEntry as renderConversationEntryLines } from "./conversation-entry-renderer.js";
|
|
4
4
|
import { horizontalPaddingLayout } from "./render-text.js";
|
|
5
|
-
import { sdkQueuedMessageEntries } from "../session/queued-message-entries.js";
|
|
6
5
|
export class ConversationViewport {
|
|
7
6
|
host;
|
|
8
7
|
blockCachesByWidth = new Map();
|
|
@@ -63,8 +62,7 @@ export class ConversationViewport {
|
|
|
63
62
|
return { lines: visible, changed: false };
|
|
64
63
|
}
|
|
65
64
|
entries() {
|
|
66
|
-
|
|
67
|
-
return queued.length === 0 ? [...this.host.entries] : [...this.host.entries, ...queued];
|
|
65
|
+
return [...this.host.entries];
|
|
68
66
|
}
|
|
69
67
|
blockForEntry(entry, width) {
|
|
70
68
|
const blockCache = this.blockCacheForWidth(width);
|
|
@@ -119,21 +117,16 @@ export class ConversationViewport {
|
|
|
119
117
|
}
|
|
120
118
|
return lineCount;
|
|
121
119
|
}
|
|
122
|
-
queuedEntries() {
|
|
123
|
-
return sdkQueuedMessageEntries(this.host.session);
|
|
124
|
-
}
|
|
125
120
|
layoutForWidth(width) {
|
|
126
|
-
const
|
|
127
|
-
const entries = queued.length === 0 ? this.host.entries : [...this.host.entries, ...queued];
|
|
128
|
-
const queuedSignature = queued.map((entry) => entry.id).join("\n");
|
|
121
|
+
const entries = this.host.entries;
|
|
129
122
|
const superCompactTools = Boolean(this.host.superCompactTools);
|
|
130
123
|
const allThinkingExpanded = Boolean(this.host.allThinkingExpanded);
|
|
131
124
|
let layout = this.layoutCachesByWidth.get(width);
|
|
132
|
-
if (!layout || this.layoutStructureChanged(layout, entries,
|
|
133
|
-
const previousLayout = layout && layout.
|
|
125
|
+
if (!layout || this.layoutStructureChanged(layout, entries, superCompactTools, allThinkingExpanded)) {
|
|
126
|
+
const previousLayout = layout && layout.superCompactTools === superCompactTools && layout.allThinkingExpanded === allThinkingExpanded
|
|
134
127
|
? layout
|
|
135
128
|
: undefined;
|
|
136
|
-
layout = this.buildLayout(entries, width,
|
|
129
|
+
layout = this.buildLayout(entries, width, superCompactTools, allThinkingExpanded, previousLayout);
|
|
137
130
|
this.layoutCachesByWidth.set(width, layout);
|
|
138
131
|
}
|
|
139
132
|
else {
|
|
@@ -144,7 +137,7 @@ export class ConversationViewport {
|
|
|
144
137
|
}
|
|
145
138
|
return layout;
|
|
146
139
|
}
|
|
147
|
-
buildLayout(entries, width,
|
|
140
|
+
buildLayout(entries, width, superCompactTools, allThinkingExpanded, previousLayout) {
|
|
148
141
|
const entryIds = [];
|
|
149
142
|
const lineCounts = [];
|
|
150
143
|
const measuredLineCounts = [];
|
|
@@ -163,7 +156,7 @@ export class ConversationViewport {
|
|
|
163
156
|
totalLineCount += lineCount;
|
|
164
157
|
}
|
|
165
158
|
offsets.push(totalLineCount);
|
|
166
|
-
return { entries, entryIds, lineCounts, measuredLineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount,
|
|
159
|
+
return { entries, entryIds, lineCounts, measuredLineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount, superCompactTools, allThinkingExpanded };
|
|
167
160
|
}
|
|
168
161
|
previousMeasuredLineCount(previousLayout, entries, index, entry) {
|
|
169
162
|
const previousIndex = previousLayout?.positions.get(entry.id);
|
|
@@ -179,8 +172,8 @@ export class ConversationViewport {
|
|
|
179
172
|
return undefined;
|
|
180
173
|
return previousLayout.lineCounts[previousIndex];
|
|
181
174
|
}
|
|
182
|
-
layoutStructureChanged(layout, entries,
|
|
183
|
-
if (layout.entries.length !== entries.length || layout.
|
|
175
|
+
layoutStructureChanged(layout, entries, superCompactTools, allThinkingExpanded) {
|
|
176
|
+
if (layout.entries.length !== entries.length || layout.superCompactTools !== superCompactTools || layout.allThinkingExpanded !== allThinkingExpanded)
|
|
184
177
|
return true;
|
|
185
178
|
if (layout.entries.length === 0)
|
|
186
179
|
return false;
|
|
@@ -111,7 +111,7 @@ export class EditorLayoutRenderer {
|
|
|
111
111
|
for (const [index, text] of wrapped.entries()) {
|
|
112
112
|
lines.push({
|
|
113
113
|
text: padHorizontalText(text, width),
|
|
114
|
-
colorOverride: this.host.theme.colors.
|
|
114
|
+
colorOverride: this.host.theme.colors.userForeground,
|
|
115
115
|
target: { kind: "queue-message", id: entry.id },
|
|
116
116
|
...(index === 0 ? { segments: [{ start: 0, end: icon.length, foreground: this.host.theme.colors.info }] } : {}),
|
|
117
117
|
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { Theme } from "../../theme.js";
|
|
2
2
|
import type { ToolStatusEntry } from "../types.js";
|
|
3
|
+
export type WrappedTextLine = {
|
|
4
|
+
text: string;
|
|
5
|
+
copyText: string;
|
|
6
|
+
continuesOnNextLine?: boolean;
|
|
7
|
+
};
|
|
3
8
|
export declare function sanitizeText(text: string): string;
|
|
4
9
|
export declare function alertIconPrefixLength(text: string): number | undefined;
|
|
5
10
|
export declare function normalizePastedTextForDuplicateKey(text: string): string;
|
|
@@ -12,6 +17,7 @@ export declare function toolStatusIcon(entry: ToolStatusEntry): string;
|
|
|
12
17
|
export declare function toolStatusIconColor(entry: ToolStatusEntry, colors: Theme["colors"]): string;
|
|
13
18
|
export declare function wrapLine(text: string, width: number): string[];
|
|
14
19
|
export declare function wrapText(text: string, width: number): string[];
|
|
20
|
+
export declare function wrapTextLines(text: string, width: number): WrappedTextLine[];
|
|
15
21
|
export declare function padOrTrimPlain(text: string, width: number): string;
|
|
16
22
|
export declare function horizontalPaddingLayout(width: number): {
|
|
17
23
|
left: number;
|
|
@@ -93,6 +93,15 @@ export function wrapText(text, width) {
|
|
|
93
93
|
const lines = sanitizeText(text).split("\n");
|
|
94
94
|
return lines.flatMap((line) => wrapLine(line, width));
|
|
95
95
|
}
|
|
96
|
+
export function wrapTextLines(text, width) {
|
|
97
|
+
return sanitizeText(text)
|
|
98
|
+
.split("\n")
|
|
99
|
+
.flatMap((line) => wrapDisplayLine(line, width).map((wrapped, index, wrappedLines) => ({
|
|
100
|
+
text: wrapped,
|
|
101
|
+
copyText: wrapped,
|
|
102
|
+
...(index < wrappedLines.length - 1 ? { continuesOnNextLine: true } : {}),
|
|
103
|
+
})));
|
|
104
|
+
}
|
|
96
105
|
export function padOrTrimPlain(text, width) {
|
|
97
106
|
return padOrTrimDisplay(text, width);
|
|
98
107
|
}
|
|
@@ -3,7 +3,6 @@ import { APP_ICONS } from "../icons.js";
|
|
|
3
3
|
import { ellipsizeDisplay } from "./render-text.js";
|
|
4
4
|
const TAB_SEPARATOR = " │ ";
|
|
5
5
|
const EMPTY_NEW_TAB_PREFIX = "│ ";
|
|
6
|
-
const DEFAULT_SESSION_TITLE_PATTERN = /^session [0-9a-f]{8}$/iu;
|
|
7
6
|
export const TAB_PANEL_ROWS = 2;
|
|
8
7
|
export function tabPanelRows(tabLineVisible, terminalRows, tabCount = TAB_PANEL_ROWS) {
|
|
9
8
|
if (!tabLineVisible)
|
|
@@ -158,10 +157,7 @@ export class TabLineRenderer {
|
|
|
158
157
|
return this.buttonLayoutFromText(`${prefix}${ellipsizeDisplay(title, titleWidth)}${suffix}`, 0, statusText.length);
|
|
159
158
|
}
|
|
160
159
|
displayTitle(tab) {
|
|
161
|
-
|
|
162
|
-
if (!DEFAULT_SESSION_TITLE_PATTERN.test(title))
|
|
163
|
-
return tab.title;
|
|
164
|
-
return tab.titlePlaceholder === "loading" ? "Loading…" : "New";
|
|
160
|
+
return tab.title;
|
|
165
161
|
}
|
|
166
162
|
buttonLayoutFromText(text, statusStart, statusLength) {
|
|
167
163
|
const closeStart = Math.max(0, text.lastIndexOf(APP_ICONS.close));
|
|
@@ -23,5 +23,7 @@ export type ToolBlockEntry = {
|
|
|
23
23
|
};
|
|
24
24
|
export type ToolBlockRenderOptions = {
|
|
25
25
|
superCompact?: boolean;
|
|
26
|
+
backgroundOverride?: string;
|
|
27
|
+
skipHeaderBackground?: boolean;
|
|
26
28
|
};
|
|
27
29
|
export declare function renderToolBlock(entry: ToolBlockEntry, rule: ResolvedToolRule, width: number, colors: Theme["colors"], options?: ToolBlockRenderOptions): RenderedLine[];
|
|
@@ -10,7 +10,10 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
10
10
|
const stateIcon = toolStatusIcon(entry);
|
|
11
11
|
const toolColor = resolveColor(rule.color, colors);
|
|
12
12
|
const toolOutputColor = colors.statusForeground;
|
|
13
|
-
const headerLabel = entry.headerLabel ?? entry.toolName;
|
|
13
|
+
const headerLabel = (entry.headerLabel ?? entry.toolName).toLowerCase();
|
|
14
|
+
const bg = options.backgroundOverride;
|
|
15
|
+
const applyBackground = bg ? (lines) => { for (const line of lines)
|
|
16
|
+
line.backgroundOverride = bg; } : (_lines) => { };
|
|
14
17
|
const headerPrefix = headerLabel ? `${stateIcon} ${headerLabel}` : stateIcon;
|
|
15
18
|
const headerArgs = formatToolHeaderArgs(entry.headerArgs);
|
|
16
19
|
const headerArgsWidth = width - stringDisplayWidth(headerPrefix) - 1;
|
|
@@ -22,14 +25,22 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
22
25
|
text: header,
|
|
23
26
|
target,
|
|
24
27
|
colorOverride: toolColor,
|
|
28
|
+
...(options.backgroundOverride && !options.skipHeaderBackground ? { backgroundOverride: options.backgroundOverride } : {}),
|
|
25
29
|
segments: [
|
|
26
30
|
{ start: 0, end: stateIcon.length, foreground: toolStatusIconColor(entry, colors), bold: true },
|
|
31
|
+
{ start: stateIcon.length, end: headerPrefix.length, bold: true },
|
|
27
32
|
...headerArgsStyledSegments(headerArgsStart, clippedHeaderArgs.length, entry.headerArgsSegments, colors),
|
|
28
33
|
],
|
|
29
34
|
};
|
|
30
35
|
const headerLines = [headerLine];
|
|
31
36
|
if (expanded) {
|
|
32
37
|
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi));
|
|
38
|
+
if (options.skipHeaderBackground && headerLines.length > 1) {
|
|
39
|
+
applyBackground(headerLines.slice(1));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
applyBackground(headerLines);
|
|
43
|
+
}
|
|
33
44
|
return headerLines;
|
|
34
45
|
}
|
|
35
46
|
if (rule.compactHidden || (rule.defaultExpanded === true && !options.superCompact))
|
|
@@ -39,6 +50,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
39
50
|
return headerLines;
|
|
40
51
|
if (!options.superCompact) {
|
|
41
52
|
headerLines.push(...renderCollapsedPreviewLines(entry, body, rule, width, target, toolOutputColor, colors, hasLspDiagnostics));
|
|
53
|
+
applyBackground(headerLines);
|
|
42
54
|
return headerLines;
|
|
43
55
|
}
|
|
44
56
|
const preview = collapsedInlinePreview(body, rule, entry.preserveAnsi);
|
|
@@ -105,7 +117,13 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
105
117
|
? wrapAnsiStyledDisplayLine(ansiLine, bodyWidth)
|
|
106
118
|
: wrapBodyLine(displayLine, bodyWidth).map((wrapped) => ({ text: wrapped, segments: [] }));
|
|
107
119
|
for (const [wrapIndex, wrapped] of wrappedLines.entries()) {
|
|
108
|
-
const line = {
|
|
120
|
+
const line = {
|
|
121
|
+
text: ` ${wrapped.text}`,
|
|
122
|
+
copyText: ` ${wrapped.text}`,
|
|
123
|
+
...(wrapIndex < wrappedLines.length - 1 ? { continuesOnNextLine: true } : {}),
|
|
124
|
+
target,
|
|
125
|
+
colorOverride: color,
|
|
126
|
+
};
|
|
109
127
|
if (diffStyle) {
|
|
110
128
|
const segment = { start: 2, end: line.text.length, foreground: diffStyle.foreground };
|
|
111
129
|
if (diffStyle.bold != null)
|
package/dist/app/runtime.d.ts
CHANGED
|
@@ -33,9 +33,11 @@ export declare function bundledSkillsInstallPath(homeDir?: string): string;
|
|
|
33
33
|
export declare function ensurePiToolsSuiteExtensionInstalled(options?: PiToolsSuiteInstallOptions): Promise<PiToolsSuiteInstallResult>;
|
|
34
34
|
export declare function ensureBundledSkillsInstalled(options?: BundledSkillsInstallOptions): Promise<BundledSkillsInstallResult>;
|
|
35
35
|
export declare function getBundledExtensionPaths(): string[];
|
|
36
|
+
export declare function getBundledExtensionPathsAsync(): Promise<string[]>;
|
|
36
37
|
export declare function prioritizeBundledQuestionExtension(base: LoadExtensionsResult, questionExtensionPath?: string): LoadExtensionsResult;
|
|
37
38
|
export type CreatePixRuntimeOptions = {
|
|
38
39
|
eventBus?: EventBus;
|
|
40
|
+
config?: PixConfig;
|
|
39
41
|
};
|
|
40
42
|
type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
|
|
41
43
|
export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
|
package/dist/app/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { cp, lstat, mkdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
2
|
+
import { access, cp, lstat, mkdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -40,7 +40,7 @@ export function bundledSkillsInstallPath(homeDir = homedir()) {
|
|
|
40
40
|
export async function ensurePiToolsSuiteExtensionInstalled(options = {}) {
|
|
41
41
|
const sourcePath = resolve(options.sourcePath ?? piToolsSuiteExtensionSourcePath());
|
|
42
42
|
const targetPath = resolve(options.targetPath ?? piToolsSuiteExtensionInstallPath(options.agentDir));
|
|
43
|
-
if (!
|
|
43
|
+
if (!(await extensionEntryExistsAsync(sourcePath))) {
|
|
44
44
|
return { action: "missing-source", sourcePath, targetPath };
|
|
45
45
|
}
|
|
46
46
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
@@ -84,6 +84,14 @@ export function getBundledExtensionPaths() {
|
|
|
84
84
|
bundledTerminalBellExtensionPath(),
|
|
85
85
|
].filter(extensionEntryExists);
|
|
86
86
|
}
|
|
87
|
+
export async function getBundledExtensionPathsAsync() {
|
|
88
|
+
const paths = await Promise.all([
|
|
89
|
+
bundledQuestionExtensionPath(),
|
|
90
|
+
bundledSessionTitleExtensionPath(),
|
|
91
|
+
bundledTerminalBellExtensionPath(),
|
|
92
|
+
].map(async (extensionPath) => await extensionEntryExistsAsync(extensionPath) ? extensionPath : undefined));
|
|
93
|
+
return paths.filter((path) => path !== undefined);
|
|
94
|
+
}
|
|
87
95
|
export function prioritizeBundledQuestionExtension(base, questionExtensionPath = bundledQuestionExtensionPath()) {
|
|
88
96
|
const bundledQuestionExtensions = base.extensions.filter((extension) => isBundledQuestionExtension(extension, questionExtensionPath));
|
|
89
97
|
if (bundledQuestionExtensions.length === 0)
|
|
@@ -101,6 +109,21 @@ export function prioritizeBundledQuestionExtension(base, questionExtensionPath =
|
|
|
101
109
|
function extensionEntryExists(extensionPath) {
|
|
102
110
|
return existsSync(join(extensionPath, "index.ts")) || existsSync(join(extensionPath, "index.js"));
|
|
103
111
|
}
|
|
112
|
+
async function extensionEntryExistsAsync(extensionPath) {
|
|
113
|
+
try {
|
|
114
|
+
await access(join(extensionPath, "index.ts"));
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
try {
|
|
119
|
+
await access(join(extensionPath, "index.js"));
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
104
127
|
function extensionSymlinkType() {
|
|
105
128
|
return process.platform === "win32" ? "junction" : "dir";
|
|
106
129
|
}
|
|
@@ -198,13 +221,13 @@ export function resolveSessionModelRefFromTail(entries) {
|
|
|
198
221
|
export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
199
222
|
const agentDir = getAgentDir();
|
|
200
223
|
const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
201
|
-
const config = loadPixConfig(cwd);
|
|
224
|
+
const config = runtimeOptions.config ?? loadPixConfig(cwd);
|
|
202
225
|
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager, config);
|
|
203
226
|
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
204
227
|
const initialThinkingLevel = resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config);
|
|
205
228
|
await ensureBundledSkillsInstalledOnce();
|
|
206
229
|
await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir });
|
|
207
|
-
const bundledExtensionPaths =
|
|
230
|
+
const bundledExtensionPaths = await getBundledExtensionPathsAsync();
|
|
208
231
|
const services = await createAgentSessionServices({
|
|
209
232
|
cwd,
|
|
210
233
|
agentDir,
|
|
@@ -799,17 +799,19 @@ export class AppMouseController {
|
|
|
799
799
|
const width = this.conversationArea()?.viewportColumns ?? this.host.terminalColumns();
|
|
800
800
|
const count = range.end.line - range.start.line + 1;
|
|
801
801
|
const renderedLines = this.host.conversationViewport().slice(width, range.start.line, count);
|
|
802
|
-
|
|
802
|
+
let copiedText = "";
|
|
803
803
|
for (let index = 0; index < count; index += 1) {
|
|
804
804
|
const rendered = renderedLines[index];
|
|
805
|
-
const
|
|
805
|
+
const lineText = rendered?.text ?? "";
|
|
806
806
|
const line = range.start.line + index;
|
|
807
807
|
const startColumn = line === range.start.line ? range.start.x : 1;
|
|
808
|
-
const endColumn = line === range.end.line ? range.end.x :
|
|
809
|
-
const
|
|
810
|
-
|
|
808
|
+
const endColumn = line === range.end.line ? range.end.x : lineText.length + 1;
|
|
809
|
+
const selectedLine = selectedConversationLineText(rendered, lineText, startColumn, endColumn);
|
|
810
|
+
copiedText += selectedLine;
|
|
811
|
+
if (!(rendered?.continuesOnNextLine))
|
|
812
|
+
copiedText += "\n";
|
|
811
813
|
}
|
|
812
|
-
return
|
|
814
|
+
return copiedText.replace(/\s+$/u, "");
|
|
813
815
|
}
|
|
814
816
|
conversationPointFromMouse(event, clampToViewport) {
|
|
815
817
|
const area = this.conversationArea();
|
|
@@ -930,6 +932,12 @@ export class AppMouseController {
|
|
|
930
932
|
selection.moved = true;
|
|
931
933
|
}
|
|
932
934
|
}
|
|
935
|
+
function selectedConversationLineText(rendered, text, startColumn, endColumn) {
|
|
936
|
+
const selectsWholeLine = startColumn <= 1 && endColumn >= text.length + 1;
|
|
937
|
+
if (selectsWholeLine && rendered?.copyText !== undefined)
|
|
938
|
+
return rendered.copyText;
|
|
939
|
+
return sliceByDisplayColumns(text, startColumn, endColumn).trimEnd();
|
|
940
|
+
}
|
|
933
941
|
function orderedConversationSelection(anchor, current) {
|
|
934
942
|
if (anchor.line < current.line)
|
|
935
943
|
return { start: anchor, end: current };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ANSI_RESET, colorize } from "../../theme.js";
|
|
1
|
+
import { ANSI_RESET, ansiStylePrefix, colorize } from "../../theme.js";
|
|
2
2
|
import { renderMarkdownLine } from "../../markdown-format.js";
|
|
3
3
|
import { syntaxHighlightSegmentsForLine } from "../../syntax-highlight.js";
|
|
4
4
|
import { displayIndexForColumn } from "../../terminal-width.js";
|
|
@@ -105,7 +105,7 @@ export class ScreenStyler {
|
|
|
105
105
|
return chunks.join("");
|
|
106
106
|
}
|
|
107
107
|
styleAnsiLine(text, options) {
|
|
108
|
-
const prefix =
|
|
108
|
+
const prefix = ansiStylePrefix(options);
|
|
109
109
|
if (!prefix)
|
|
110
110
|
return text;
|
|
111
111
|
return `${prefix}${text.replaceAll(ANSI_RESET, `${ANSI_RESET}${prefix}`)}${ANSI_RESET}`;
|
|
@@ -133,6 +133,8 @@ export class AppSessionEventController {
|
|
|
133
133
|
case "tool_execution_end":
|
|
134
134
|
this.host.setSessionActivity(this.host.runtime()?.session.isStreaming ? "running" : "idle");
|
|
135
135
|
this.recordToolWorkspaceMutation(event.toolCallId, event.toolName, event.result.details, event.isError);
|
|
136
|
+
if (this.currentUserEntryId)
|
|
137
|
+
this.host.scheduleUserSessionEntryMetadataSync();
|
|
136
138
|
this.host.observeSubagentsToolResult(event.toolName, isRecord(event.result) ? event.result.details : undefined);
|
|
137
139
|
this.host.observeTodoToolResult(event.toolName, isRecord(event.result) ? event.result.details : undefined, event.isError);
|
|
138
140
|
this.upsertToolEntry(event.toolCallId, {
|
|
@@ -274,7 +276,6 @@ export class AppSessionEventController {
|
|
|
274
276
|
this.finishCurrentThinkingEntry();
|
|
275
277
|
this.flushAssistantTextBuffer(true);
|
|
276
278
|
this.clearCurrentAssistantState();
|
|
277
|
-
this.currentUserEntryId = undefined;
|
|
278
279
|
}
|
|
279
280
|
}
|
|
280
281
|
prepareToolWorkspaceMutation(toolCallId, toolName, args) {
|
|
@@ -362,7 +363,7 @@ export class AppSessionEventController {
|
|
|
362
363
|
}
|
|
363
364
|
if (!this.assistantTextBuffer)
|
|
364
365
|
return visibleText;
|
|
365
|
-
if (shouldHoldAssistantStreamTail(this.assistantTextBuffer)) {
|
|
366
|
+
if (shouldHoldAssistantStreamTail(this.assistantTextBuffer, this.hasVisibleAssistantText(visibleText))) {
|
|
366
367
|
if (final)
|
|
367
368
|
this.assistantTextBuffer = "";
|
|
368
369
|
return visibleText;
|
|
@@ -439,9 +440,9 @@ function shouldDropAssistantStreamLine(line, hasVisibleText) {
|
|
|
439
440
|
return true;
|
|
440
441
|
return isHiddenMarkdownMetadataLine(line);
|
|
441
442
|
}
|
|
442
|
-
function shouldHoldAssistantStreamTail(text) {
|
|
443
|
+
function shouldHoldAssistantStreamTail(text, hasVisibleText) {
|
|
443
444
|
if (text.trim().length === 0)
|
|
444
|
-
return
|
|
445
|
+
return !hasVisibleText;
|
|
445
446
|
return isPotentialDcpMetadataLine(text);
|
|
446
447
|
}
|
|
447
448
|
function isHiddenMarkdownMetadataLine(line) {
|
|
@@ -14,6 +14,7 @@ export type AppSessionLifecycleHost = {
|
|
|
14
14
|
inputEditor(): InputEditor;
|
|
15
15
|
enableTerminal(): void;
|
|
16
16
|
disposeRuntimeForQuit(runtime: AgentSessionRuntime): Promise<void>;
|
|
17
|
+
loadStartupConfig(): Promise<void>;
|
|
17
18
|
loadRequestHistory(): Promise<void>;
|
|
18
19
|
startSubagentsPolling(): void;
|
|
19
20
|
closeSdkMenuForBind(): void;
|
|
@@ -67,6 +68,7 @@ export declare class AppSessionLifecycleController {
|
|
|
67
68
|
loadSessionHistory(): void;
|
|
68
69
|
requireRuntime(): AgentSessionRuntime;
|
|
69
70
|
private bindSessionExtensions;
|
|
71
|
+
private collectAvailabilityIssues;
|
|
70
72
|
private isCurrentRuntimeSession;
|
|
71
73
|
private extensionUiScope;
|
|
72
74
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createId } from "../id.js";
|
|
2
2
|
import { stringifyUnknown } from "../rendering/message-content.js";
|
|
3
3
|
import { collectStartupAvailabilityIssues } from "../cli/startup-checks.js";
|
|
4
|
-
import { createStartupInfoMessage, isEmptyStartupSession } from "../cli/startup-info.js";
|
|
5
4
|
export class AppSessionLifecycleController {
|
|
6
5
|
host;
|
|
7
6
|
unsubscribe;
|
|
@@ -16,11 +15,17 @@ export class AppSessionLifecycleController {
|
|
|
16
15
|
throw new Error("pi-ui-extend needs an interactive TTY");
|
|
17
16
|
}
|
|
18
17
|
this.host.enableTerminal();
|
|
19
|
-
await this.host.loadRequestHistory();
|
|
20
18
|
this.host.setRunning(true);
|
|
21
19
|
this.host.startSubagentsPolling();
|
|
22
20
|
this.host.render();
|
|
21
|
+
void this.host.loadRequestHistory().catch((error) => {
|
|
22
|
+
if (!this.host.isRunning())
|
|
23
|
+
return;
|
|
24
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Request history failed to load: ${stringifyUnknown(error)}` });
|
|
25
|
+
this.host.render();
|
|
26
|
+
});
|
|
23
27
|
try {
|
|
28
|
+
await this.host.loadStartupConfig();
|
|
24
29
|
const runtime = await this.host.createRuntime();
|
|
25
30
|
if (!this.host.isRunning()) {
|
|
26
31
|
await this.host.disposeRuntimeForQuit(runtime);
|
|
@@ -31,24 +36,6 @@ export class AppSessionLifecycleController {
|
|
|
31
36
|
await this.bindCurrentSession({ awaitExtensions: false });
|
|
32
37
|
});
|
|
33
38
|
await this.bindCurrentSession({ awaitExtensions: false });
|
|
34
|
-
if (isEmptyStartupSession(runtime)) {
|
|
35
|
-
this.host.addEntry({ id: createId("system"), kind: "system", text: createStartupInfoMessage(runtime) });
|
|
36
|
-
}
|
|
37
|
-
await this.host.restoreTabsAfterStartup();
|
|
38
|
-
const availabilityIssues = await collectStartupAvailabilityIssues(runtime);
|
|
39
|
-
for (const issue of availabilityIssues) {
|
|
40
|
-
this.host.addEntry({
|
|
41
|
-
id: createId(issue.kind),
|
|
42
|
-
kind: issue.kind === "error" ? "error" : "system",
|
|
43
|
-
text: issue.message,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
if (availabilityIssues.some((issue) => issue.kind === "error")) {
|
|
47
|
-
this.host.showToast("Startup dependency unavailable", "error");
|
|
48
|
-
}
|
|
49
|
-
else if (availabilityIssues.length > 0) {
|
|
50
|
-
this.host.showToast("Startup dependency warning", "warning");
|
|
51
|
-
}
|
|
52
39
|
if (runtime.modelFallbackMessage) {
|
|
53
40
|
this.host.addEntry({ id: createId("system"), kind: "system", text: runtime.modelFallbackMessage });
|
|
54
41
|
}
|
|
@@ -59,6 +46,14 @@ export class AppSessionLifecycleController {
|
|
|
59
46
|
this.host.setSessionStatus(runtime.session);
|
|
60
47
|
this.host.setSessionActivity(runtime.session.isStreaming ? "running" : "idle");
|
|
61
48
|
this.host.render();
|
|
49
|
+
void this.collectAvailabilityIssues(runtime);
|
|
50
|
+
void this.host.restoreTabsAfterStartup().catch((error) => {
|
|
51
|
+
if (!this.host.isRunning())
|
|
52
|
+
return;
|
|
53
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Tab restore failed: ${stringifyUnknown(error)}` });
|
|
54
|
+
this.host.showToast("Could not restore tabs", "warning");
|
|
55
|
+
this.host.render();
|
|
56
|
+
});
|
|
62
57
|
}
|
|
63
58
|
catch (error) {
|
|
64
59
|
this.host.addEntry({ id: createId("error"), kind: "error", text: stringifyUnknown(error) });
|
|
@@ -154,6 +149,34 @@ export class AppSessionLifecycleController {
|
|
|
154
149
|
this.extensionBindSession = session;
|
|
155
150
|
return promise;
|
|
156
151
|
}
|
|
152
|
+
async collectAvailabilityIssues(runtime) {
|
|
153
|
+
try {
|
|
154
|
+
const availabilityIssues = await collectStartupAvailabilityIssues(runtime);
|
|
155
|
+
if (!this.host.isRunning() || this.host.runtime() !== runtime)
|
|
156
|
+
return;
|
|
157
|
+
for (const issue of availabilityIssues) {
|
|
158
|
+
this.host.addEntry({
|
|
159
|
+
id: createId(issue.kind),
|
|
160
|
+
kind: issue.kind === "error" ? "error" : "system",
|
|
161
|
+
text: issue.message,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (availabilityIssues.some((issue) => issue.kind === "error")) {
|
|
165
|
+
this.host.showToast("Startup dependency unavailable", "error");
|
|
166
|
+
}
|
|
167
|
+
else if (availabilityIssues.length > 0) {
|
|
168
|
+
this.host.showToast("Startup dependency warning", "warning");
|
|
169
|
+
}
|
|
170
|
+
if (availabilityIssues.length > 0)
|
|
171
|
+
this.host.render();
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (!this.host.isRunning() || this.host.runtime() !== runtime)
|
|
175
|
+
return;
|
|
176
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Startup dependency check failed: ${stringifyUnknown(error)}` });
|
|
177
|
+
this.host.render();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
157
180
|
isCurrentRuntimeSession(runtime, session) {
|
|
158
181
|
return this.host.isRunning() && this.host.runtime() === runtime && runtime.session === session;
|
|
159
182
|
}
|
|
@@ -6,7 +6,7 @@ import type { AppOptions, Entry, SessionActivity, SessionTab, SubmittedUserMessa
|
|
|
6
6
|
export type TabInputState = InputEditorDraftState;
|
|
7
7
|
export type AppTabsControllerHost = {
|
|
8
8
|
readonly options: AppOptions;
|
|
9
|
-
readonly maxProjectSessions?: number;
|
|
9
|
+
readonly maxProjectSessions?: number | (() => number | undefined);
|
|
10
10
|
readonly blinkController: AppBlinkController;
|
|
11
11
|
runtime(): AgentSessionRuntime | undefined;
|
|
12
12
|
createRuntimeForNewSession(): Promise<AgentSessionRuntime>;
|
|
@@ -79,7 +79,7 @@ export declare class AppTabsController {
|
|
|
79
79
|
private runtimeForCommand;
|
|
80
80
|
private idleRuntime;
|
|
81
81
|
private activeTab;
|
|
82
|
-
private
|
|
82
|
+
private clearStartupTabPlaceholders;
|
|
83
83
|
private storeActiveRuntime;
|
|
84
84
|
private setRuntimeForTab;
|
|
85
85
|
private deleteRuntimeForTab;
|
|
@@ -109,6 +109,7 @@ export declare class AppTabsController {
|
|
|
109
109
|
private sessionPath;
|
|
110
110
|
private sessionTitle;
|
|
111
111
|
private sessionTitleFromParts;
|
|
112
|
+
private updatedSessionTitle;
|
|
112
113
|
private sessionActivity;
|
|
113
114
|
private tabActivity;
|
|
114
115
|
private clearTabAttention;
|
|
@@ -116,6 +117,9 @@ export declare class AppTabsController {
|
|
|
116
117
|
private stopAttentionBlinkIfIdle;
|
|
117
118
|
private restoredTabs;
|
|
118
119
|
private defaultSessionTitleFromPath;
|
|
120
|
+
private loadSessionTitles;
|
|
121
|
+
private scheduleRestoredTabTitleRefresh;
|
|
122
|
+
private refreshRestoredTabTitles;
|
|
119
123
|
private loadTabs;
|
|
120
124
|
private parsePersistedInputState;
|
|
121
125
|
private parsePersistedSubmittedUserMessages;
|