pi-ui-extend 0.1.13 → 0.1.17
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 -1
- package/dist/app/app.d.ts +7 -0
- package/dist/app/app.js +102 -17
- package/dist/app/commands/command-controller.js +2 -0
- package/dist/app/commands/command-host.d.ts +5 -0
- package/dist/app/commands/command-model-actions.d.ts +2 -0
- package/dist/app/commands/command-model-actions.js +40 -4
- package/dist/app/commands/command-navigation-actions.d.ts +9 -0
- package/dist/app/commands/command-navigation-actions.js +62 -0
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +16 -0
- package/dist/app/constants.d.ts +0 -1
- package/dist/app/constants.js +0 -1
- package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
- package/dist/app/extensions/extension-ui-controller.js +99 -61
- package/dist/app/icons.d.ts +1 -0
- package/dist/app/icons.js +2 -0
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/logger.d.ts +25 -0
- package/dist/app/logger.js +90 -0
- package/dist/app/model/model-usage-status.js +30 -15
- package/dist/app/popup/menu-items-controller.d.ts +4 -0
- package/dist/app/popup/menu-items-controller.js +68 -6
- package/dist/app/popup/popup-action-controller.d.ts +2 -1
- package/dist/app/popup/popup-action-controller.js +7 -4
- package/dist/app/popup/popup-menu-controller.d.ts +36 -23
- package/dist/app/popup/popup-menu-controller.js +97 -326
- package/dist/app/rendering/conversation-entry-renderer.js +3 -3
- package/dist/app/rendering/conversation-viewport.d.ts +10 -2
- package/dist/app/rendering/conversation-viewport.js +157 -16
- package/dist/app/rendering/editor-panels.js +22 -9
- package/dist/app/rendering/popup-menu-renderer.d.ts +62 -0
- package/dist/app/rendering/popup-menu-renderer.js +405 -0
- package/dist/app/rendering/render-controller.js +30 -28
- package/dist/app/rendering/render-text.js +5 -2
- package/dist/app/rendering/status-line-renderer.d.ts +8 -1
- package/dist/app/rendering/status-line-renderer.js +217 -117
- package/dist/app/rendering/toast-controller.d.ts +12 -3
- package/dist/app/rendering/toast-controller.js +70 -12
- package/dist/app/runtime.d.ts +2 -1
- package/dist/app/runtime.js +20 -10
- package/dist/app/screen/mouse-controller.d.ts +2 -2
- package/dist/app/screen/mouse-controller.js +27 -48
- package/dist/app/screen/screen-styler.d.ts +1 -1
- package/dist/app/screen/screen-styler.js +9 -7
- package/dist/app/screen/scroll-controller.d.ts +12 -9
- package/dist/app/screen/scroll-controller.js +56 -45
- package/dist/app/screen/status-controller.js +2 -1
- package/dist/app/session/lazy-session-manager.d.ts +11 -0
- package/dist/app/session/lazy-session-manager.js +539 -0
- package/dist/app/session/pix-system-message.d.ts +16 -0
- package/dist/app/session/pix-system-message.js +64 -0
- package/dist/app/session/request-history.d.ts +4 -0
- package/dist/app/session/request-history.js +11 -0
- package/dist/app/session/session-event-controller.d.ts +11 -0
- package/dist/app/session/session-event-controller.js +58 -2
- package/dist/app/session/session-history.d.ts +18 -0
- package/dist/app/session/session-history.js +72 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
- package/dist/app/session/session-lifecycle-controller.js +7 -2
- package/dist/app/session/session-search.js +10 -0
- package/dist/app/session/tabs-controller.d.ts +17 -5
- package/dist/app/session/tabs-controller.js +308 -29
- package/dist/app/todo/todo-model.d.ts +4 -2
- package/dist/app/todo/todo-model.js +23 -13
- package/dist/app/types.d.ts +17 -6
- package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
- package/dist/app/workspace/workspace-actions-controller.js +12 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +82 -25
- package/dist/default-pix-config.js +4 -0
- package/dist/fuzzy.d.ts +2 -0
- package/dist/fuzzy.js +27 -7
- package/dist/input-editor.d.ts +9 -0
- package/dist/input-editor.js +52 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +1 -0
- package/dist/schemas/pi-tools-suite-schema.js +1 -0
- package/dist/schemas/pix-schema.d.ts +3 -1
- package/dist/schemas/pix-schema.js +6 -4
- package/dist/terminal-width.d.ts +2 -0
- package/dist/terminal-width.js +64 -3
- package/dist/theme.js +6 -6
- package/dist/ui.d.ts +8 -0
- package/external/pi-tools-suite/README.md +3 -2
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +52 -8
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +3 -41
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +0 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +11 -18
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +129 -61
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +82 -3
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +20 -7
- package/external/pi-tools-suite/src/antigravity-auth/types.ts +21 -0
- package/external/pi-tools-suite/src/config.ts +8 -0
- package/external/pi-tools-suite/src/dcp/index.ts +16 -1
- package/external/pi-tools-suite/src/dcp/state.ts +35 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +3 -0
- package/external/pi-tools-suite/src/todo/index.ts +123 -14
- package/external/pi-tools-suite/src/todo/state/persistence.ts +0 -1
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +26 -43
- package/external/pi-tools-suite/src/todo/todo.ts +12 -23
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +34 -16
- package/external/pi-tools-suite/src/todo/tool/types.ts +7 -28
- package/external/pi-tools-suite/src/todo/view/format.ts +2 -3
- package/external/pi-tools-suite/src/tool-descriptions.ts +6 -4
- package/external/pi-tools-suite/src/usage/index.ts +5 -2
- package/external/pi-tools-suite/src/usage/lib/google.ts +53 -40
- package/external/pi-tools-suite/src/usage/lib/types.ts +12 -2
- package/package.json +1 -1
- package/schemas/pi-tools-suite.json +4 -0
- package/schemas/pix.json +11 -2
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { AgentSessionEvent, AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { ConversationViewport } from "../rendering/conversation-viewport.js";
|
|
3
|
+
import { type LoadOlderSessionHistoryOptions } from "./session-history.js";
|
|
3
4
|
import type { Entry, SessionActivity } from "../types.js";
|
|
4
5
|
import type { WorkspaceMutation, WorkspaceMutationPreparation } from "../workspace/workspace-undo.js";
|
|
5
6
|
export type AppSessionEventControllerHost = {
|
|
6
7
|
readonly entries: Entry[];
|
|
7
8
|
runtime(): AgentSessionRuntime | undefined;
|
|
8
9
|
conversationViewport(): ConversationViewport;
|
|
10
|
+
conversationViewportColumns?(): number;
|
|
11
|
+
onHistoryWindowPruned?(edge: "top" | "bottom", lineCount: number): void;
|
|
9
12
|
isRunning(): boolean;
|
|
10
13
|
render(): void;
|
|
11
14
|
scheduleRender(): void;
|
|
@@ -36,6 +39,7 @@ export declare class AppSessionEventController {
|
|
|
36
39
|
readonly entryRenderVersions: Map<string, number>;
|
|
37
40
|
private readonly toolEntryIdsByCallId;
|
|
38
41
|
private readonly toolMutationPreparationsByCallId;
|
|
42
|
+
private olderHistoryLoader;
|
|
39
43
|
private currentUserEntryId;
|
|
40
44
|
private currentAssistantEntryId;
|
|
41
45
|
private currentThinkingEntryId;
|
|
@@ -46,7 +50,11 @@ export declare class AppSessionEventController {
|
|
|
46
50
|
loadSessionHistoryAsync(options: {
|
|
47
51
|
isCancelled: () => boolean;
|
|
48
52
|
render: () => void;
|
|
53
|
+
lazyOlderHistory?: boolean;
|
|
49
54
|
}): Promise<boolean>;
|
|
55
|
+
hasOlderSessionHistory(): boolean;
|
|
56
|
+
isLoadingOlderSessionHistory(): boolean;
|
|
57
|
+
loadOlderSessionHistory(options?: LoadOlderSessionHistoryOptions): Promise<boolean>;
|
|
50
58
|
handleSessionEvent(event: AgentSessionEvent): void;
|
|
51
59
|
addCustomMessageEntry(message: Record<string, unknown>): void;
|
|
52
60
|
findEntry(id: string): Entry | undefined;
|
|
@@ -56,6 +64,9 @@ export declare class AppSessionEventController {
|
|
|
56
64
|
touchEntry(entry: Entry): void;
|
|
57
65
|
addEntry(entry: Entry): void;
|
|
58
66
|
private prependEntries;
|
|
67
|
+
private pruneHistoryWindow;
|
|
68
|
+
private measuredLineCountForEntries;
|
|
69
|
+
private forgetEntry;
|
|
59
70
|
addSessionAbortedEntry(): void;
|
|
60
71
|
private handleMessageStart;
|
|
61
72
|
private handleMessageEnd;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { createId } from "../id.js";
|
|
2
2
|
import { extractImageContents, renderContent, renderUserMessageContent, stringifyUnknown } from "../rendering/message-content.js";
|
|
3
3
|
import { customMessageEntry, loadSessionHistoryEntries, loadSessionHistoryEntriesAsync } from "./session-history.js";
|
|
4
|
+
import { sessionHistoryDisplayMessages, sessionHistoryOlderMessagesReader } from "./pix-system-message.js";
|
|
4
5
|
import { isRecord } from "../guards.js";
|
|
5
6
|
const DCP_MESSAGE_REFERENCE_PREFIX = "[dcp-id]: # (m";
|
|
6
7
|
const DCP_BLOCK_REFERENCE_PREFIX = "[dcp-block-id]: # (b";
|
|
8
|
+
const MAX_HISTORY_WINDOW_ENTRIES = 360;
|
|
9
|
+
const HISTORY_WINDOW_TARGET_ENTRIES = 300;
|
|
7
10
|
export class AppSessionEventController {
|
|
8
11
|
host;
|
|
9
12
|
entryRenderVersions = new Map();
|
|
10
13
|
toolEntryIdsByCallId = new Map();
|
|
11
14
|
toolMutationPreparationsByCallId = new Map();
|
|
15
|
+
olderHistoryLoader;
|
|
12
16
|
currentUserEntryId;
|
|
13
17
|
currentAssistantEntryId;
|
|
14
18
|
currentThinkingEntryId;
|
|
@@ -24,13 +28,14 @@ export class AppSessionEventController {
|
|
|
24
28
|
this.currentAssistantEntryId = undefined;
|
|
25
29
|
this.currentThinkingEntryId = undefined;
|
|
26
30
|
this.assistantTextBuffer = "";
|
|
31
|
+
this.olderHistoryLoader = undefined;
|
|
27
32
|
}
|
|
28
33
|
loadSessionHistory() {
|
|
29
34
|
const runtime = this.host.runtime();
|
|
30
35
|
if (!runtime)
|
|
31
36
|
return;
|
|
32
37
|
loadSessionHistoryEntries({
|
|
33
|
-
messages: runtime.session
|
|
38
|
+
messages: sessionHistoryDisplayMessages(runtime.session),
|
|
34
39
|
addEntry: (entry) => this.addEntry(entry),
|
|
35
40
|
setToolEntryId: (toolCallId, entryId) => this.toolEntryIdsByCallId.set(toolCallId, entryId),
|
|
36
41
|
toolDefaultExpanded: (toolName) => this.host.toolDefaultExpanded(toolName),
|
|
@@ -42,8 +47,10 @@ export class AppSessionEventController {
|
|
|
42
47
|
const runtime = this.host.runtime();
|
|
43
48
|
if (!runtime)
|
|
44
49
|
return !options.isCancelled();
|
|
50
|
+
this.olderHistoryLoader = undefined;
|
|
45
51
|
return loadSessionHistoryEntriesAsync({
|
|
46
|
-
messages: runtime.session
|
|
52
|
+
messages: sessionHistoryDisplayMessages(runtime.session),
|
|
53
|
+
olderMessagesReader: sessionHistoryOlderMessagesReader(runtime.session),
|
|
47
54
|
addEntry: (entry) => this.addEntry(entry),
|
|
48
55
|
prependEntries: (entries) => this.prependEntries(entries),
|
|
49
56
|
setToolEntryId: (toolCallId, entryId) => this.toolEntryIdsByCallId.set(toolCallId, entryId),
|
|
@@ -52,8 +59,21 @@ export class AppSessionEventController {
|
|
|
52
59
|
observeTodoToolResult: (toolName, details, isError) => this.host.observeTodoToolResult(toolName, details, isError),
|
|
53
60
|
isCancelled: options.isCancelled,
|
|
54
61
|
render: options.render,
|
|
62
|
+
lazyOlderHistory: options.lazyOlderHistory === true,
|
|
63
|
+
onOlderLoaderReady: (loader) => {
|
|
64
|
+
this.olderHistoryLoader = loader;
|
|
65
|
+
},
|
|
55
66
|
});
|
|
56
67
|
}
|
|
68
|
+
hasOlderSessionHistory() {
|
|
69
|
+
return this.olderHistoryLoader?.hasOlder() === true;
|
|
70
|
+
}
|
|
71
|
+
isLoadingOlderSessionHistory() {
|
|
72
|
+
return this.olderHistoryLoader?.isLoading() === true;
|
|
73
|
+
}
|
|
74
|
+
async loadOlderSessionHistory(options = {}) {
|
|
75
|
+
return this.olderHistoryLoader?.loadOlder(options) ?? false;
|
|
76
|
+
}
|
|
57
77
|
handleSessionEvent(event) {
|
|
58
78
|
switch (event.type) {
|
|
59
79
|
case "session_info_changed":
|
|
@@ -173,6 +193,7 @@ export class AppSessionEventController {
|
|
|
173
193
|
this.host.entries.push(entry);
|
|
174
194
|
this.entryRenderVersions.set(entry.id, 1);
|
|
175
195
|
this.host.conversationViewport().deleteEntry(entry.id);
|
|
196
|
+
this.pruneHistoryWindow("top");
|
|
176
197
|
}
|
|
177
198
|
prependEntries(entries) {
|
|
178
199
|
this.host.entries.unshift(...entries);
|
|
@@ -180,6 +201,41 @@ export class AppSessionEventController {
|
|
|
180
201
|
this.entryRenderVersions.set(entry.id, 1);
|
|
181
202
|
this.host.conversationViewport().deleteEntry(entry.id);
|
|
182
203
|
}
|
|
204
|
+
this.pruneHistoryWindow("bottom");
|
|
205
|
+
}
|
|
206
|
+
pruneHistoryWindow(edge) {
|
|
207
|
+
const removeCount = this.host.entries.length - MAX_HISTORY_WINDOW_ENTRIES;
|
|
208
|
+
if (removeCount <= 0)
|
|
209
|
+
return;
|
|
210
|
+
const targetRemoveCount = Math.max(removeCount, this.host.entries.length - HISTORY_WINDOW_TARGET_ENTRIES);
|
|
211
|
+
const removedEntryIds = edge === "top"
|
|
212
|
+
? this.host.entries.slice(0, targetRemoveCount).map((entry) => entry.id)
|
|
213
|
+
: this.host.entries.slice(Math.max(0, this.host.entries.length - targetRemoveCount)).map((entry) => entry.id);
|
|
214
|
+
const removedLineCount = this.measuredLineCountForEntries(removedEntryIds);
|
|
215
|
+
const removed = edge === "top"
|
|
216
|
+
? this.host.entries.splice(0, targetRemoveCount)
|
|
217
|
+
: this.host.entries.splice(Math.max(0, this.host.entries.length - targetRemoveCount), targetRemoveCount);
|
|
218
|
+
for (const entry of removed)
|
|
219
|
+
this.forgetEntry(entry);
|
|
220
|
+
this.host.onHistoryWindowPruned?.(edge, removedLineCount);
|
|
221
|
+
}
|
|
222
|
+
measuredLineCountForEntries(entryIds) {
|
|
223
|
+
if (entryIds.length === 0)
|
|
224
|
+
return 0;
|
|
225
|
+
const viewport = this.host.conversationViewport();
|
|
226
|
+
if (typeof viewport.measuredLineCountForEntries !== "function")
|
|
227
|
+
return 0;
|
|
228
|
+
return viewport.measuredLineCountForEntries(this.host.conversationViewportColumns?.() ?? 80, entryIds);
|
|
229
|
+
}
|
|
230
|
+
forgetEntry(entry) {
|
|
231
|
+
this.entryRenderVersions.delete(entry.id);
|
|
232
|
+
this.host.conversationViewport().deleteEntry(entry.id);
|
|
233
|
+
if (entry.kind !== "tool")
|
|
234
|
+
return;
|
|
235
|
+
for (const [toolCallId, entryId] of this.toolEntryIdsByCallId) {
|
|
236
|
+
if (entryId === entry.id)
|
|
237
|
+
this.toolEntryIdsByCallId.delete(toolCallId);
|
|
238
|
+
}
|
|
183
239
|
}
|
|
184
240
|
addSessionAbortedEntry() {
|
|
185
241
|
this.finishCurrentThinkingEntry();
|
|
@@ -16,6 +16,24 @@ export type LoadSessionHistoryAsyncOptions = LoadSessionHistoryOptions & {
|
|
|
16
16
|
isCancelled: () => boolean;
|
|
17
17
|
chunkSize?: number;
|
|
18
18
|
tailMessageCount?: number;
|
|
19
|
+
lazyOlderHistory?: boolean;
|
|
20
|
+
olderMessagesReader?: OlderSessionHistoryMessagesReader | undefined;
|
|
21
|
+
onOlderLoaderReady?: (loader: SessionHistoryOlderLoader | undefined) => void;
|
|
22
|
+
};
|
|
23
|
+
export type LoadOlderSessionHistoryOptions = {
|
|
24
|
+
render?: boolean;
|
|
25
|
+
onPrependedEntries?: (entries: readonly Entry[]) => void;
|
|
26
|
+
};
|
|
27
|
+
export type SessionHistoryOlderLoader = {
|
|
28
|
+
hasOlder(): boolean;
|
|
29
|
+
isLoading(): boolean;
|
|
30
|
+
loadedMessageCount(): number;
|
|
31
|
+
totalMessageCount(): number;
|
|
32
|
+
loadOlder(options?: LoadOlderSessionHistoryOptions): Promise<boolean>;
|
|
33
|
+
};
|
|
34
|
+
export type OlderSessionHistoryMessagesReader = {
|
|
35
|
+
hasOlder(): boolean;
|
|
36
|
+
readOlder(limit: number): Promise<readonly unknown[]>;
|
|
19
37
|
};
|
|
20
38
|
export declare function loadSessionHistoryEntries(options: LoadSessionHistoryOptions): void;
|
|
21
39
|
export declare function loadSessionHistoryEntriesAsync(options: LoadSessionHistoryAsyncOptions): Promise<boolean>;
|
|
@@ -2,7 +2,7 @@ import { isRecord } from "../guards.js";
|
|
|
2
2
|
import { createId } from "../id.js";
|
|
3
3
|
import { isOnlyHiddenMetadata } from "../../markdown-format.js";
|
|
4
4
|
import { extractImageContents, renderContent, renderUserMessageContent, stringifyUnknown } from "../rendering/message-content.js";
|
|
5
|
-
|
|
5
|
+
import { PIX_SESSION_ENTRY_ID_FIELD, PIX_SYSTEM_MESSAGE_CUSTOM_TYPE } from "./pix-system-message.js";
|
|
6
6
|
const HISTORICAL_SUBAGENTS_OBSERVATION = { showSnapshot: false };
|
|
7
7
|
const DEFAULT_HISTORY_CHUNK_SIZE = 50;
|
|
8
8
|
const DEFAULT_HISTORY_TAIL_MESSAGE_COUNT = 80;
|
|
@@ -27,6 +27,11 @@ export async function loadSessionHistoryEntriesAsync(options) {
|
|
|
27
27
|
addSessionHistoryRangeEntries(messages, tailStart, messages.length, toolResults, options.addEntry, options);
|
|
28
28
|
options.render();
|
|
29
29
|
await yieldToEventLoop();
|
|
30
|
+
if (options.lazyOlderHistory) {
|
|
31
|
+
const loader = createOlderHistoryLoader(messages, options, chunkSize, toolResults, tailStart, options.olderMessagesReader);
|
|
32
|
+
options.onOlderLoaderReady?.(loader.hasOlder() ? loader : undefined);
|
|
33
|
+
return !options.isCancelled();
|
|
34
|
+
}
|
|
30
35
|
for (let end = tailStart; end > 0; end -= chunkSize) {
|
|
31
36
|
if (options.isCancelled())
|
|
32
37
|
return false;
|
|
@@ -41,6 +46,69 @@ export async function loadSessionHistoryEntriesAsync(options) {
|
|
|
41
46
|
}
|
|
42
47
|
return !options.isCancelled();
|
|
43
48
|
}
|
|
49
|
+
function createOlderHistoryLoader(messages, options, chunkSize, toolResults, initialEnd, olderMessagesReader) {
|
|
50
|
+
let nextEnd = initialEnd;
|
|
51
|
+
let loading = false;
|
|
52
|
+
let externallyLoadedMessageCount = 0;
|
|
53
|
+
return {
|
|
54
|
+
hasOlder: () => nextEnd > 0 || olderMessagesReader?.hasOlder() === true,
|
|
55
|
+
isLoading: () => loading,
|
|
56
|
+
loadedMessageCount: () => Math.max(0, messages.length - nextEnd) + externallyLoadedMessageCount,
|
|
57
|
+
totalMessageCount: () => messages.length + externallyLoadedMessageCount,
|
|
58
|
+
async loadOlder(loadOptions = {}) {
|
|
59
|
+
if (loading)
|
|
60
|
+
return !options.isCancelled();
|
|
61
|
+
if (nextEnd <= 0 && olderMessagesReader?.hasOlder() !== true)
|
|
62
|
+
return !options.isCancelled();
|
|
63
|
+
loading = true;
|
|
64
|
+
let didPrependEntries = false;
|
|
65
|
+
try {
|
|
66
|
+
while (!options.isCancelled() && (nextEnd > 0 || olderMessagesReader?.hasOlder() === true)) {
|
|
67
|
+
const batch = nextEnd > 0
|
|
68
|
+
? inMemoryOlderMessagesBatch(messages, chunkSize, nextEnd)
|
|
69
|
+
: await externalOlderMessagesBatch(olderMessagesReader, chunkSize);
|
|
70
|
+
if (!batch)
|
|
71
|
+
break;
|
|
72
|
+
const { batchMessages } = batch;
|
|
73
|
+
if (batch.external)
|
|
74
|
+
externallyLoadedMessageCount += batchMessages.length;
|
|
75
|
+
buildToolResults(batchMessages, options, 0, batchMessages.length, toolResults);
|
|
76
|
+
const entries = [];
|
|
77
|
+
addSessionHistoryRangeEntries(batchMessages, 0, batchMessages.length, toolResults, (entry) => entries.push(entry), options);
|
|
78
|
+
nextEnd = batch.nextEnd;
|
|
79
|
+
if (entries.length > 0) {
|
|
80
|
+
options.prependEntries(entries);
|
|
81
|
+
loadOptions.onPrependedEntries?.(entries);
|
|
82
|
+
didPrependEntries = true;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
await yieldToEventLoop();
|
|
86
|
+
}
|
|
87
|
+
if (options.isCancelled())
|
|
88
|
+
return false;
|
|
89
|
+
if (didPrependEntries && loadOptions.render !== false)
|
|
90
|
+
options.render();
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
loading = false;
|
|
95
|
+
if (nextEnd <= 0 && olderMessagesReader?.hasOlder() !== true)
|
|
96
|
+
options.onOlderLoaderReady?.(undefined);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function inMemoryOlderMessagesBatch(messages, chunkSize, nextEnd) {
|
|
102
|
+
const end = nextEnd;
|
|
103
|
+
const start = Math.max(0, end - chunkSize);
|
|
104
|
+
return { batchMessages: messages.slice(start, end), nextEnd: start, external: false };
|
|
105
|
+
}
|
|
106
|
+
async function externalOlderMessagesBatch(olderMessagesReader, chunkSize) {
|
|
107
|
+
if (!olderMessagesReader?.hasOlder())
|
|
108
|
+
return undefined;
|
|
109
|
+
const batchMessages = await olderMessagesReader.readOlder(chunkSize);
|
|
110
|
+
return batchMessages.length === 0 ? undefined : { batchMessages, nextEnd: 0, external: true };
|
|
111
|
+
}
|
|
44
112
|
function expandedTailStart(messages, initialStart) {
|
|
45
113
|
let start = initialStart;
|
|
46
114
|
while (start > 0) {
|
|
@@ -82,7 +150,8 @@ function addSessionHistoryRangeEntries(messages, start, end, toolResults, addEnt
|
|
|
82
150
|
const text = renderUserMessageContent(message.content);
|
|
83
151
|
if (text) {
|
|
84
152
|
const images = extractImageContents(message.content);
|
|
85
|
-
|
|
153
|
+
const sessionEntryId = typeof message[PIX_SESSION_ENTRY_ID_FIELD] === "string" ? message[PIX_SESSION_ENTRY_ID_FIELD] : undefined;
|
|
154
|
+
addEntry({ id: createId("user"), kind: "user", text, ...(sessionEntryId === undefined ? {} : { sessionEntryId }), ...(images.length === 0 ? {} : { images }) });
|
|
86
155
|
}
|
|
87
156
|
}
|
|
88
157
|
else if (message.role === "assistant") {
|
|
@@ -101,7 +170,7 @@ export function customMessageEntry(message) {
|
|
|
101
170
|
const text = renderUserMessageContent(message.content);
|
|
102
171
|
if (!text)
|
|
103
172
|
return undefined;
|
|
104
|
-
if (customType ===
|
|
173
|
+
if (customType === PIX_SYSTEM_MESSAGE_CUSTOM_TYPE)
|
|
105
174
|
return { id: createId("system"), kind: "system", text };
|
|
106
175
|
return { id: createId("custom"), kind: "custom", customType, text };
|
|
107
176
|
}
|
|
@@ -17,8 +17,10 @@ export type AppSessionLifecycleHost = {
|
|
|
17
17
|
loadRequestHistory(): Promise<void>;
|
|
18
18
|
startSubagentsPolling(): void;
|
|
19
19
|
closeSdkMenuForBind(): void;
|
|
20
|
-
clearExtensionWidgets(
|
|
21
|
-
|
|
20
|
+
clearExtensionWidgets(scopeKey?: string, options?: {
|
|
21
|
+
cancelCustomUi?: boolean;
|
|
22
|
+
}): void;
|
|
23
|
+
createExtensionUIContext(scopeKey?: string): PixExtensionUIContext;
|
|
22
24
|
extensionShutdownHandler(): () => void;
|
|
23
25
|
createExtensionCommandContextActions(runtime: AgentSessionRuntime): ExtensionCommandContextActions;
|
|
24
26
|
handleExtensionError(error: ExtensionError): void;
|
|
@@ -40,6 +42,7 @@ export type AppSessionLifecycleHost = {
|
|
|
40
42
|
loadSessionHistoryEntriesAsync(options: {
|
|
41
43
|
isCancelled: () => boolean;
|
|
42
44
|
render: () => void;
|
|
45
|
+
lazyOlderHistory?: boolean;
|
|
43
46
|
}): Promise<boolean>;
|
|
44
47
|
syncUserSessionEntryMetadata(): void;
|
|
45
48
|
restoreTabsAfterStartup(): Promise<void>;
|
|
@@ -57,4 +60,5 @@ export declare class AppSessionLifecycleController {
|
|
|
57
60
|
resetSessionView(): void;
|
|
58
61
|
loadSessionHistory(): void;
|
|
59
62
|
requireRuntime(): AgentSessionRuntime;
|
|
63
|
+
private extensionUiScope;
|
|
60
64
|
}
|
|
@@ -71,9 +71,10 @@ export class AppSessionLifecycleController {
|
|
|
71
71
|
this.host.handleSessionEvent(event);
|
|
72
72
|
});
|
|
73
73
|
this.host.closeSdkMenuForBind();
|
|
74
|
-
this.
|
|
74
|
+
const extensionUiScope = this.extensionUiScope(runtime.session);
|
|
75
|
+
this.host.clearExtensionWidgets(extensionUiScope, { cancelCustomUi: false });
|
|
75
76
|
await runtime.session.bindExtensions({
|
|
76
|
-
uiContext: this.host.createExtensionUIContext(),
|
|
77
|
+
uiContext: this.host.createExtensionUIContext(extensionUiScope),
|
|
77
78
|
commandContextActions: this.host.createExtensionCommandContextActions(runtime),
|
|
78
79
|
shutdownHandler: this.host.extensionShutdownHandler(),
|
|
79
80
|
onError: (error) => this.host.handleExtensionError(error),
|
|
@@ -91,6 +92,7 @@ export class AppSessionLifecycleController {
|
|
|
91
92
|
await this.host.loadSessionHistoryEntriesAsync({
|
|
92
93
|
isCancelled: () => !this.host.isRunning(),
|
|
93
94
|
render: () => this.host.render(),
|
|
95
|
+
lazyOlderHistory: true,
|
|
94
96
|
});
|
|
95
97
|
this.host.syncUserSessionEntryMetadata();
|
|
96
98
|
if (message)
|
|
@@ -121,4 +123,7 @@ export class AppSessionLifecycleController {
|
|
|
121
123
|
throw new Error("Runtime is not initialized");
|
|
122
124
|
return runtime;
|
|
123
125
|
}
|
|
126
|
+
extensionUiScope(session) {
|
|
127
|
+
return session.sessionFile ?? session.sessionId;
|
|
128
|
+
}
|
|
124
129
|
}
|
|
@@ -38,6 +38,7 @@ export function createSessionSearchMenuItems(results) {
|
|
|
38
38
|
value: result,
|
|
39
39
|
label: title,
|
|
40
40
|
description: `${result.snippet} · ${date} ${time} · ${messages} · ${result.session.id.slice(0, 8)}`,
|
|
41
|
+
descriptionHighlightRanges: sessionSearchSnippetHighlightRanges(result),
|
|
41
42
|
keywords: [
|
|
42
43
|
result.session.id,
|
|
43
44
|
result.session.name ?? "",
|
|
@@ -48,6 +49,15 @@ export function createSessionSearchMenuItems(results) {
|
|
|
48
49
|
};
|
|
49
50
|
});
|
|
50
51
|
}
|
|
52
|
+
function sessionSearchSnippetHighlightRanges(result) {
|
|
53
|
+
const needle = result.query.trim();
|
|
54
|
+
if (!needle)
|
|
55
|
+
return [];
|
|
56
|
+
const start = result.snippet.toLocaleLowerCase().indexOf(needle.toLocaleLowerCase());
|
|
57
|
+
if (start < 0)
|
|
58
|
+
return [];
|
|
59
|
+
return [{ start, end: start + needle.length }];
|
|
60
|
+
}
|
|
51
61
|
export function searchResultTargetEntry(entries, result) {
|
|
52
62
|
const targetSessionEntryId = result.match.sessionEntryId;
|
|
53
63
|
if (targetSessionEntryId) {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type AgentSession, type AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { InputEditorDraftState } from "../../input-editor.js";
|
|
2
3
|
import type { AppBlinkController } from "../screen/blink-controller.js";
|
|
3
4
|
import type { AppOptions, Entry, SessionActivity, SessionTab, SubmittedUserMessage } from "../types.js";
|
|
4
|
-
export type TabInputState =
|
|
5
|
-
text: string;
|
|
6
|
-
cursor: number;
|
|
7
|
-
};
|
|
5
|
+
export type TabInputState = InputEditorDraftState;
|
|
8
6
|
export type AppTabsControllerHost = {
|
|
9
7
|
readonly options: AppOptions;
|
|
8
|
+
readonly maxProjectSessions?: number;
|
|
10
9
|
readonly blinkController: AppBlinkController;
|
|
11
10
|
runtime(): AgentSessionRuntime | undefined;
|
|
12
11
|
createRuntimeForNewSession(): Promise<AgentSessionRuntime>;
|
|
@@ -22,10 +21,12 @@ export type AppTabsControllerHost = {
|
|
|
22
21
|
loadSessionHistoryAsync(options: {
|
|
23
22
|
isCancelled: () => boolean;
|
|
24
23
|
render: () => void;
|
|
24
|
+
lazyOlderHistory?: boolean;
|
|
25
25
|
}): Promise<boolean>;
|
|
26
26
|
syncUserSessionEntryMetadata(): void;
|
|
27
27
|
captureInputState(): TabInputState;
|
|
28
28
|
restoreInputState(state: TabInputState): void;
|
|
29
|
+
closeMenusForTabSwitch?(): void;
|
|
29
30
|
captureDeferredUserMessages?(): readonly SubmittedUserMessage[];
|
|
30
31
|
restoreDeferredUserMessages?(messages: readonly SubmittedUserMessage[]): void;
|
|
31
32
|
addEntry(entry: Entry): void;
|
|
@@ -43,6 +44,8 @@ export declare class AppTabsController {
|
|
|
43
44
|
private pendingActiveTabId;
|
|
44
45
|
private historyLoadGeneration;
|
|
45
46
|
private restored;
|
|
47
|
+
private retentionCleanupRunning;
|
|
48
|
+
private retentionCleanupScheduled;
|
|
46
49
|
constructor(host: AppTabsControllerHost);
|
|
47
50
|
tabs(): readonly SessionTab[];
|
|
48
51
|
isSwitching(): boolean;
|
|
@@ -61,6 +64,7 @@ export declare class AppTabsController {
|
|
|
61
64
|
restoreAfterStartup(): Promise<void>;
|
|
62
65
|
openNewTab(): Promise<void>;
|
|
63
66
|
openSessionInNewTab(sessionPath: string): Promise<boolean>;
|
|
67
|
+
forkSessionEntryInNewTab(entryId: string): Promise<boolean>;
|
|
64
68
|
switchToTab(tabId: string): Promise<void>;
|
|
65
69
|
closeTab(tabId: string): Promise<void>;
|
|
66
70
|
private replaceLastTabWithNewSession;
|
|
@@ -70,6 +74,7 @@ export declare class AppTabsController {
|
|
|
70
74
|
private runtimeForCommand;
|
|
71
75
|
private idleRuntime;
|
|
72
76
|
private activeTab;
|
|
77
|
+
private settleStartupTabPlaceholders;
|
|
73
78
|
private storeActiveRuntime;
|
|
74
79
|
private setRuntimeForTab;
|
|
75
80
|
private deleteRuntimeForTab;
|
|
@@ -80,6 +85,7 @@ export declare class AppTabsController {
|
|
|
80
85
|
private storeActiveInputState;
|
|
81
86
|
private storeActiveDeferredUserMessages;
|
|
82
87
|
private restoreInputState;
|
|
88
|
+
private inputStateFromText;
|
|
83
89
|
private restoreDeferredUserMessages;
|
|
84
90
|
private cloneSubmittedUserMessage;
|
|
85
91
|
private runtimeForTab;
|
|
@@ -94,16 +100,22 @@ export declare class AppTabsController {
|
|
|
94
100
|
private updateTabFromSession;
|
|
95
101
|
private sessionPath;
|
|
96
102
|
private sessionTitle;
|
|
103
|
+
private sessionTitleFromParts;
|
|
97
104
|
private sessionActivity;
|
|
98
105
|
private tabActivity;
|
|
99
106
|
private clearTabAttention;
|
|
100
107
|
private startAttentionBlink;
|
|
101
108
|
private stopAttentionBlinkIfIdle;
|
|
102
|
-
private loadSessionTitles;
|
|
103
109
|
private restoredTabs;
|
|
110
|
+
private defaultSessionTitleFromPath;
|
|
104
111
|
private loadTabs;
|
|
105
112
|
private parsePersistedInputState;
|
|
106
113
|
private parsePersistedSubmittedUserMessages;
|
|
107
114
|
private saveTabs;
|
|
108
115
|
private filePath;
|
|
116
|
+
private sessionDir;
|
|
117
|
+
private scheduleProjectSessionRetention;
|
|
118
|
+
private cleanupOldProjectSessions;
|
|
119
|
+
private preservedSessionPaths;
|
|
120
|
+
private maxProjectSessions;
|
|
109
121
|
}
|