pi-ui-extend 0.1.13 → 0.1.15
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 +5 -0
- package/dist/app/app.js +82 -12
- package/dist/app/commands/command-controller.js +1 -0
- package/dist/app/commands/command-host.d.ts +3 -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.js +3 -0
- package/dist/app/commands/command-registry.d.ts +1 -0
- package/dist/app/commands/command-registry.js +8 -0
- package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
- package/dist/app/extensions/extension-ui-controller.js +99 -61
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +8 -2
- 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 +2 -0
- package/dist/app/popup/menu-items-controller.js +45 -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 +68 -322
- 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 +4 -2
- package/dist/app/rendering/popup-menu-renderer.d.ts +50 -0
- package/dist/app/rendering/popup-menu-renderer.js +307 -0
- package/dist/app/rendering/render-controller.js +5 -13
- package/dist/app/rendering/status-line-renderer.d.ts +1 -1
- package/dist/app/rendering/status-line-renderer.js +27 -24
- package/dist/app/rendering/toast-controller.d.ts +11 -3
- package/dist/app/rendering/toast-controller.js +53 -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 +11 -9
- package/dist/app/screen/scroll-controller.js +50 -45
- 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/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/tabs-controller.d.ts +13 -1
- package/dist/app/session/tabs-controller.js +248 -27
- package/dist/app/todo/todo-model.d.ts +3 -1
- package/dist/app/todo/todo-model.js +14 -2
- package/dist/app/types.d.ts +5 -2
- 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 +5 -1
- package/dist/config.js +73 -25
- package/dist/default-pix-config.js +2 -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 +2 -1
- package/dist/schemas/pix-schema.js +5 -4
- package/dist/terminal-width.d.ts +2 -0
- package/dist/terminal-width.js +64 -3
- package/external/pi-tools-suite/README.md +1 -0
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +12 -3
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +2 -4
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +2 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +8 -2
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +102 -50
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +81 -2
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +29 -8
- 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 +181 -11
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +23 -10
- package/external/pi-tools-suite/src/todo/todo.ts +10 -5
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +33 -6
- package/external/pi-tools-suite/src/todo/tool/types.ts +9 -1
- package/external/pi-tools-suite/src/todo/view/format.ts +2 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +2 -1
- package/external/pi-tools-suite/src/usage/index.ts +5 -2
- package/external/pi-tools-suite/src/usage/lib/google.ts +6 -13
- package/package.json +1 -1
- package/schemas/pi-tools-suite.json +4 -0
- package/schemas/pix.json +6 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentSession, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export declare const PIX_SYSTEM_MESSAGE_CUSTOM_TYPE = "pix-system";
|
|
3
|
+
export declare const PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE = "pix:system_message";
|
|
4
|
+
export declare const PIX_SESSION_ENTRY_ID_FIELD = "__pixSessionEntryId";
|
|
5
|
+
export declare function appendPixSystemDisplayEntry(session: AgentSession, text: string): void;
|
|
6
|
+
export declare function sessionHistoryDisplayMessages(session: AgentSession): readonly unknown[];
|
|
7
|
+
export type SessionHistoryOlderMessagesReader = {
|
|
8
|
+
hasOlder(): boolean;
|
|
9
|
+
readOlder(limit: number): Promise<readonly unknown[]>;
|
|
10
|
+
};
|
|
11
|
+
export declare function sessionHistoryOlderMessagesReader(session: AgentSession): SessionHistoryOlderMessagesReader | undefined;
|
|
12
|
+
export declare function sessionHistoryDisplayMessagesFromEntries(branch: readonly SessionEntry[]): readonly unknown[];
|
|
13
|
+
export type FullBranchSessionEntryReader = {
|
|
14
|
+
readFullBranchEntries(): Promise<readonly SessionEntry[]>;
|
|
15
|
+
};
|
|
16
|
+
export declare function sessionHistoryFullBranchEntries(session: AgentSession): Promise<readonly SessionEntry[]>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isRecord } from "../guards.js";
|
|
2
|
+
export const PIX_SYSTEM_MESSAGE_CUSTOM_TYPE = "pix-system";
|
|
3
|
+
export const PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE = "pix:system_message";
|
|
4
|
+
export const PIX_SESSION_ENTRY_ID_FIELD = "__pixSessionEntryId";
|
|
5
|
+
export function appendPixSystemDisplayEntry(session, text) {
|
|
6
|
+
const trimmed = text.trim();
|
|
7
|
+
if (!trimmed)
|
|
8
|
+
return;
|
|
9
|
+
session.sessionManager.appendCustomEntry(PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE, { text: trimmed });
|
|
10
|
+
}
|
|
11
|
+
export function sessionHistoryDisplayMessages(session) {
|
|
12
|
+
const branch = session.sessionManager.getBranch();
|
|
13
|
+
if (branch.length === 0)
|
|
14
|
+
return session.messages;
|
|
15
|
+
return sessionHistoryDisplayMessagesFromEntries(branch);
|
|
16
|
+
}
|
|
17
|
+
export function sessionHistoryOlderMessagesReader(session) {
|
|
18
|
+
const reader = session.sessionManager.createHistoryReader?.();
|
|
19
|
+
if (!reader)
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
hasOlder: () => reader.hasOlder(),
|
|
23
|
+
readOlder: async (limit) => sessionHistoryDisplayMessagesFromEntries(await reader.readOlder(limit)),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function sessionHistoryDisplayMessagesFromEntries(branch) {
|
|
27
|
+
const messages = [];
|
|
28
|
+
for (const entry of branch) {
|
|
29
|
+
if (entry.type === "message") {
|
|
30
|
+
messages.push(withSessionEntryId(entry.message, entry.id));
|
|
31
|
+
}
|
|
32
|
+
else if (entry.type === "custom_message") {
|
|
33
|
+
messages.push({
|
|
34
|
+
role: "custom",
|
|
35
|
+
customType: entry.customType,
|
|
36
|
+
content: entry.content,
|
|
37
|
+
display: entry.display,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else if (isPixSystemDisplayEntry(entry)) {
|
|
41
|
+
messages.push({
|
|
42
|
+
role: "custom",
|
|
43
|
+
customType: PIX_SYSTEM_MESSAGE_CUSTOM_TYPE,
|
|
44
|
+
content: entry.data.text,
|
|
45
|
+
display: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return messages;
|
|
50
|
+
}
|
|
51
|
+
export async function sessionHistoryFullBranchEntries(session) {
|
|
52
|
+
const reader = session.sessionManager;
|
|
53
|
+
return await reader.readFullBranchEntries?.() ?? session.sessionManager.getBranch();
|
|
54
|
+
}
|
|
55
|
+
function withSessionEntryId(message, entryId) {
|
|
56
|
+
return isRecord(message) ? { ...message, [PIX_SESSION_ENTRY_ID_FIELD]: entryId } : message;
|
|
57
|
+
}
|
|
58
|
+
function isPixSystemDisplayEntry(entry) {
|
|
59
|
+
return entry.type === "custom"
|
|
60
|
+
&& entry.customType === PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE
|
|
61
|
+
&& isRecord(entry.data)
|
|
62
|
+
&& typeof entry.data.text === "string"
|
|
63
|
+
&& entry.data.text.trim().length > 0;
|
|
64
|
+
}
|
|
@@ -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
|
}
|
|
@@ -22,10 +22,12 @@ export type AppTabsControllerHost = {
|
|
|
22
22
|
loadSessionHistoryAsync(options: {
|
|
23
23
|
isCancelled: () => boolean;
|
|
24
24
|
render: () => void;
|
|
25
|
+
lazyOlderHistory?: boolean;
|
|
25
26
|
}): Promise<boolean>;
|
|
26
27
|
syncUserSessionEntryMetadata(): void;
|
|
27
28
|
captureInputState(): TabInputState;
|
|
28
29
|
restoreInputState(state: TabInputState): void;
|
|
30
|
+
closeMenusForTabSwitch?(): void;
|
|
29
31
|
captureDeferredUserMessages?(): readonly SubmittedUserMessage[];
|
|
30
32
|
restoreDeferredUserMessages?(messages: readonly SubmittedUserMessage[]): void;
|
|
31
33
|
addEntry(entry: Entry): void;
|
|
@@ -43,6 +45,8 @@ export declare class AppTabsController {
|
|
|
43
45
|
private pendingActiveTabId;
|
|
44
46
|
private historyLoadGeneration;
|
|
45
47
|
private restored;
|
|
48
|
+
private retentionCleanupRunning;
|
|
49
|
+
private retentionCleanupScheduled;
|
|
46
50
|
constructor(host: AppTabsControllerHost);
|
|
47
51
|
tabs(): readonly SessionTab[];
|
|
48
52
|
isSwitching(): boolean;
|
|
@@ -61,6 +65,7 @@ export declare class AppTabsController {
|
|
|
61
65
|
restoreAfterStartup(): Promise<void>;
|
|
62
66
|
openNewTab(): Promise<void>;
|
|
63
67
|
openSessionInNewTab(sessionPath: string): Promise<boolean>;
|
|
68
|
+
forkSessionEntryInNewTab(entryId: string): Promise<boolean>;
|
|
64
69
|
switchToTab(tabId: string): Promise<void>;
|
|
65
70
|
closeTab(tabId: string): Promise<void>;
|
|
66
71
|
private replaceLastTabWithNewSession;
|
|
@@ -70,6 +75,7 @@ export declare class AppTabsController {
|
|
|
70
75
|
private runtimeForCommand;
|
|
71
76
|
private idleRuntime;
|
|
72
77
|
private activeTab;
|
|
78
|
+
private settleStartupTabPlaceholders;
|
|
73
79
|
private storeActiveRuntime;
|
|
74
80
|
private setRuntimeForTab;
|
|
75
81
|
private deleteRuntimeForTab;
|
|
@@ -80,6 +86,7 @@ export declare class AppTabsController {
|
|
|
80
86
|
private storeActiveInputState;
|
|
81
87
|
private storeActiveDeferredUserMessages;
|
|
82
88
|
private restoreInputState;
|
|
89
|
+
private inputStateFromText;
|
|
83
90
|
private restoreDeferredUserMessages;
|
|
84
91
|
private cloneSubmittedUserMessage;
|
|
85
92
|
private runtimeForTab;
|
|
@@ -94,16 +101,21 @@ export declare class AppTabsController {
|
|
|
94
101
|
private updateTabFromSession;
|
|
95
102
|
private sessionPath;
|
|
96
103
|
private sessionTitle;
|
|
104
|
+
private sessionTitleFromParts;
|
|
97
105
|
private sessionActivity;
|
|
98
106
|
private tabActivity;
|
|
99
107
|
private clearTabAttention;
|
|
100
108
|
private startAttentionBlink;
|
|
101
109
|
private stopAttentionBlinkIfIdle;
|
|
102
|
-
private loadSessionTitles;
|
|
103
110
|
private restoredTabs;
|
|
111
|
+
private defaultSessionTitleFromPath;
|
|
104
112
|
private loadTabs;
|
|
105
113
|
private parsePersistedInputState;
|
|
106
114
|
private parsePersistedSubmittedUserMessages;
|
|
107
115
|
private saveTabs;
|
|
108
116
|
private filePath;
|
|
117
|
+
private sessionDir;
|
|
118
|
+
private scheduleProjectSessionRetention;
|
|
119
|
+
private cleanupOldProjectSessions;
|
|
120
|
+
private preservedSessionPaths;
|
|
109
121
|
}
|