pi-ui-extend 0.1.35 → 0.1.36
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/dist/app/app.d.ts +1 -0
- package/dist/app/app.js +8 -0
- package/dist/app/commands/command-host.d.ts +1 -0
- package/dist/app/commands/command-model-actions.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +32 -0
- package/dist/app/commands/command-navigation-actions.js +3 -0
- package/dist/app/commands/command-session-actions.js +2 -0
- package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
- package/dist/app/extensions/extension-actions-controller.js +4 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +28 -1
- package/dist/app/input/terminal-edit-shortcuts.d.ts +1 -0
- package/dist/app/input/terminal-edit-shortcuts.js +42 -0
- package/dist/app/popup/popup-action-controller.d.ts +1 -0
- package/dist/app/popup/popup-action-controller.js +1 -0
- package/dist/app/session/session-lifecycle-controller.d.ts +1 -0
- package/dist/app/session/session-lifecycle-controller.js +7 -0
- package/dist/app/session/tabs-controller.d.ts +1 -0
- package/dist/app/session/tabs-controller.js +1 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -0
- package/dist/app/workspace/workspace-actions-controller.js +1 -0
- package/external/pi-tools-suite/package.json +2 -2
- package/package.json +1 -1
package/dist/app/app.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ export declare class PiUiExtendApp {
|
|
|
66
66
|
start(): Promise<void>;
|
|
67
67
|
private checkPixUpdateOnStartup;
|
|
68
68
|
private bindCurrentSession;
|
|
69
|
+
private awaitCurrentSessionExtensions;
|
|
69
70
|
private activateRuntime;
|
|
70
71
|
private createExtensionEventBus;
|
|
71
72
|
private handleTerminalBellAttention;
|
package/dist/app/app.js
CHANGED
|
@@ -172,6 +172,7 @@ export class PiUiExtendApp {
|
|
|
172
172
|
noSession: false,
|
|
173
173
|
sessionPath,
|
|
174
174
|
}),
|
|
175
|
+
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
175
176
|
activateRuntime: (runtime, options) => this.activateRuntime(runtime, options),
|
|
176
177
|
disposeRuntime: (runtime) => this.terminalController.disposeRuntime(runtime),
|
|
177
178
|
isRunning: () => this.running,
|
|
@@ -317,6 +318,7 @@ export class PiUiExtendApp {
|
|
|
317
318
|
isRunning: () => this.running,
|
|
318
319
|
getInput: () => this.input,
|
|
319
320
|
setInput: (value) => this.setInput(value),
|
|
321
|
+
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
320
322
|
resetSessionView: () => this.resetSessionView(),
|
|
321
323
|
loadSessionHistory: () => this.loadSessionHistory(),
|
|
322
324
|
afterSessionReplacement: (message) => this.afterSessionReplacement(message),
|
|
@@ -341,6 +343,7 @@ export class PiUiExtendApp {
|
|
|
341
343
|
this.workspaceActions = new AppWorkspaceActionsController({
|
|
342
344
|
entries: this.entries,
|
|
343
345
|
runtime: () => this.runtime,
|
|
346
|
+
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
344
347
|
findUserEntry: (entryId) => this.findUserEntry(entryId),
|
|
345
348
|
touchEntry: (entry) => this.touchEntry(entry),
|
|
346
349
|
resetSessionView: () => this.resetSessionView(),
|
|
@@ -471,6 +474,7 @@ export class PiUiExtendApp {
|
|
|
471
474
|
showMenu: (items, options) => this.popupMenus.menuController.show(items, options),
|
|
472
475
|
getModelMenuItems: (query) => this.menuItems.getModelMenuItems(query),
|
|
473
476
|
getThinkingMenuItems: (query) => this.menuItems.getThinkingMenuItems(query),
|
|
477
|
+
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
474
478
|
modelRef: (model) => this.menuItems.modelRef(model),
|
|
475
479
|
getFavoriteScopedModels: () => this.menuItems.getFavoriteScopedModels(),
|
|
476
480
|
setSessionStatus: (session) => this.setSessionStatus(session),
|
|
@@ -519,6 +523,7 @@ export class PiUiExtendApp {
|
|
|
519
523
|
showToast: (message, kind) => this.showToast(message, kind),
|
|
520
524
|
render: () => this.render(),
|
|
521
525
|
resetSessionView: () => this.resetSessionView(),
|
|
526
|
+
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
522
527
|
bindCurrentSession: () => this.bindCurrentSession(),
|
|
523
528
|
loadSessionHistory: () => this.loadSessionHistory(),
|
|
524
529
|
scrollToConversationEntry: (entryId) => this.scrollController.scrollToConversationEntry(entryId),
|
|
@@ -824,6 +829,9 @@ export class PiUiExtendApp {
|
|
|
824
829
|
async bindCurrentSession(options) {
|
|
825
830
|
await this.sessionLifecycle.bindCurrentSession(options);
|
|
826
831
|
}
|
|
832
|
+
async awaitCurrentSessionExtensions(runtime) {
|
|
833
|
+
await this.sessionLifecycle.awaitCurrentSessionExtensions(runtime);
|
|
834
|
+
}
|
|
827
835
|
async activateRuntime(runtime, options) {
|
|
828
836
|
this.runtime = runtime;
|
|
829
837
|
runtime.setRebindSession(async () => {
|
|
@@ -7,6 +7,7 @@ export type DirectPopupMenu = Exclude<ActivePopupMenu, "slash">;
|
|
|
7
7
|
export type CommandControllerHost = {
|
|
8
8
|
readonly options: AppOptions;
|
|
9
9
|
runtime(): AgentSessionRuntime | undefined;
|
|
10
|
+
awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
|
|
10
11
|
requestHistory(): AppRequestHistory;
|
|
11
12
|
getInput(): string;
|
|
12
13
|
setInput(value: string): void;
|
|
@@ -14,6 +14,7 @@ export declare class ModelCommandActions {
|
|
|
14
14
|
runModelCommand(model: SessionModel): Promise<void>;
|
|
15
15
|
runThinkingCommand(level: ThinkingLevel): Promise<void>;
|
|
16
16
|
private addPersistentSystemEntry;
|
|
17
|
+
private reloadAfterModelChange;
|
|
17
18
|
private saveDefaultModel;
|
|
18
19
|
private saveDefaultThinking;
|
|
19
20
|
}
|
|
@@ -269,6 +269,17 @@ export class ModelCommandActions {
|
|
|
269
269
|
this.host.render();
|
|
270
270
|
await runtime.session.setModel(model);
|
|
271
271
|
this.host.addEntry({ id: createId("system"), kind: "system", text: `Selected model ${ref}` });
|
|
272
|
+
if (runtime.session.isStreaming) {
|
|
273
|
+
this.host.addEntry({
|
|
274
|
+
id: createId("system"),
|
|
275
|
+
kind: "system",
|
|
276
|
+
text: "Skipped reload because the agent is still running. Run /reload when idle to refresh model-specific tools.",
|
|
277
|
+
});
|
|
278
|
+
this.host.toast.warning("Model changed; reload skipped while the agent is running");
|
|
279
|
+
this.host.setSessionStatus(runtime.session);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
await this.reloadAfterModelChange(runtime.session, ref);
|
|
272
283
|
this.host.setSessionStatus(runtime.session);
|
|
273
284
|
}
|
|
274
285
|
async runThinkingCommand(level) {
|
|
@@ -285,6 +296,27 @@ export class ModelCommandActions {
|
|
|
285
296
|
appendPixSystemDisplayEntry(session, text);
|
|
286
297
|
this.host.addEntry({ id: createId("system"), kind: "system", text });
|
|
287
298
|
}
|
|
299
|
+
async reloadAfterModelChange(session, ref) {
|
|
300
|
+
this.host.setStatus(`reloading resources for ${ref}`);
|
|
301
|
+
this.host.render();
|
|
302
|
+
try {
|
|
303
|
+
await session.reload();
|
|
304
|
+
this.host.addEntry({
|
|
305
|
+
id: createId("system"),
|
|
306
|
+
kind: "system",
|
|
307
|
+
text: `Reloaded resources after model change to ${ref}`,
|
|
308
|
+
});
|
|
309
|
+
this.host.toast.success("Model changed and resources reloaded");
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
this.host.addEntry({
|
|
313
|
+
id: createId("error"),
|
|
314
|
+
kind: "error",
|
|
315
|
+
text: `Model changed to ${ref}, but reload failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
316
|
+
});
|
|
317
|
+
this.host.toast.error("Model changed, but reload failed");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
288
320
|
saveDefaultModel(modelRef) {
|
|
289
321
|
const saved = savePixDefaultModel(modelRef);
|
|
290
322
|
if (!saved)
|
|
@@ -50,6 +50,7 @@ export class NavigationCommandActions {
|
|
|
50
50
|
throw new Error("No user messages to fork from");
|
|
51
51
|
this.host.setStatus("forking session");
|
|
52
52
|
this.host.render();
|
|
53
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
53
54
|
const result = await runtime.fork(entryId);
|
|
54
55
|
if (result.cancelled) {
|
|
55
56
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Fork cancelled." });
|
|
@@ -74,6 +75,7 @@ export class NavigationCommandActions {
|
|
|
74
75
|
}
|
|
75
76
|
this.host.setStatus("cloning session");
|
|
76
77
|
this.host.render();
|
|
78
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
77
79
|
const result = await runtime.fork(leafId, { position: "at" });
|
|
78
80
|
if (result.cancelled) {
|
|
79
81
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Clone cancelled." });
|
|
@@ -228,6 +230,7 @@ export class NavigationCommandActions {
|
|
|
228
230
|
const resolvedSessionPath = resolve(runtime.cwd, sessionPath);
|
|
229
231
|
this.host.setStatus("switching session");
|
|
230
232
|
this.host.render();
|
|
233
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
231
234
|
const result = await runtime.switchSession(resolvedSessionPath);
|
|
232
235
|
if (result.cancelled) {
|
|
233
236
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Resume cancelled." });
|
|
@@ -260,6 +260,7 @@ export class SessionCommandActions {
|
|
|
260
260
|
this.host.setStatus("reloading");
|
|
261
261
|
this.host.render();
|
|
262
262
|
try {
|
|
263
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
263
264
|
await runtime.session.reload();
|
|
264
265
|
this.host.setSessionStatus(runtime.session);
|
|
265
266
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Reloaded keybindings, extensions, skills, prompts, themes" });
|
|
@@ -277,6 +278,7 @@ export class SessionCommandActions {
|
|
|
277
278
|
return;
|
|
278
279
|
this.host.setStatus("starting new session");
|
|
279
280
|
this.host.render();
|
|
281
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
280
282
|
const result = await runtime.newSession();
|
|
281
283
|
if (result.cancelled) {
|
|
282
284
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "New session cancelled." });
|
|
@@ -4,6 +4,7 @@ export type AppExtensionActionsHost = {
|
|
|
4
4
|
isRunning(): boolean;
|
|
5
5
|
getInput(): string;
|
|
6
6
|
setInput(value: string): void;
|
|
7
|
+
awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
|
|
7
8
|
resetSessionView(): void;
|
|
8
9
|
loadSessionHistory(): void;
|
|
9
10
|
afterSessionReplacement(message?: string): void;
|
|
@@ -8,12 +8,14 @@ export class AppExtensionActionsController {
|
|
|
8
8
|
return {
|
|
9
9
|
waitForIdle: () => this.waitForSessionIdle(runtime),
|
|
10
10
|
newSession: async (options) => {
|
|
11
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
11
12
|
const result = await runtime.newSession(options);
|
|
12
13
|
if (!result.cancelled)
|
|
13
14
|
this.host.afterSessionReplacement("Started a new session.");
|
|
14
15
|
return result;
|
|
15
16
|
},
|
|
16
17
|
fork: async (entryId, options) => {
|
|
18
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
17
19
|
const result = await runtime.fork(entryId, options);
|
|
18
20
|
if (!result.cancelled)
|
|
19
21
|
this.host.afterSessionReplacement("Forked to a new session.");
|
|
@@ -32,12 +34,14 @@ export class AppExtensionActionsController {
|
|
|
32
34
|
return result;
|
|
33
35
|
},
|
|
34
36
|
switchSession: async (sessionPath, options) => {
|
|
37
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
35
38
|
const result = await runtime.switchSession(sessionPath, options);
|
|
36
39
|
if (!result.cancelled)
|
|
37
40
|
this.host.afterSessionReplacement(`Switched session: ${sessionPath}`);
|
|
38
41
|
return result;
|
|
39
42
|
},
|
|
40
43
|
reload: async () => {
|
|
44
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
41
45
|
await runtime.session.reload();
|
|
42
46
|
this.host.setSessionStatus(runtime.session);
|
|
43
47
|
this.host.showToast("Reloaded resources", "success");
|
|
@@ -46,6 +46,7 @@ export declare class AppInputController {
|
|
|
46
46
|
private consumeCommandArrowPageMatch;
|
|
47
47
|
private consumeTerminalEditShortcutSequence;
|
|
48
48
|
private consumeIgnoredModifiedKeySequence;
|
|
49
|
+
private consumeModifiedArrowKeySequence;
|
|
49
50
|
private consumeClipboardImagePasteSequence;
|
|
50
51
|
private consumeShiftEnterSequence;
|
|
51
52
|
private consumeTerminalInterruptSequence;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InputPasteHandler } from "./input-paste-handler.js";
|
|
2
2
|
import { hasTerminalCommandModifier, isNativeCommandPressed, isNativeShiftPressed } from "./native-modifiers.js";
|
|
3
|
-
import { parseTerminalEditShortcutSequence, parseTerminalInterruptSequence, parseTerminalModifiedKeySequence, terminalEditShortcutForControlChar, terminalKeyIsClipboardImagePaste, terminalKeyIsShiftEnter, terminalKeyShouldIgnore, } from "./terminal-edit-shortcuts.js";
|
|
3
|
+
import { parseTerminalEditShortcutSequence, parseTerminalInterruptSequence, parseTerminalModifiedKeySequence, terminalKeyArrowDirection, terminalEditShortcutForControlChar, terminalKeyIsClipboardImagePaste, terminalKeyIsShiftEnter, terminalKeyShouldIgnore, } from "./terminal-edit-shortcuts.js";
|
|
4
4
|
const SHIFT_ENTER_ESCAPE_SEQUENCES = ["\x1b\r", "\x1b\n"];
|
|
5
5
|
export class AppInputController {
|
|
6
6
|
host;
|
|
@@ -112,6 +112,11 @@ export class AppInputController {
|
|
|
112
112
|
continue;
|
|
113
113
|
if (terminalEditShortcutSequence === "pending")
|
|
114
114
|
return;
|
|
115
|
+
const modifiedArrowKeySequence = this.consumeModifiedArrowKeySequence();
|
|
116
|
+
if (modifiedArrowKeySequence === "consumed")
|
|
117
|
+
continue;
|
|
118
|
+
if (modifiedArrowKeySequence === "pending")
|
|
119
|
+
return;
|
|
115
120
|
const ignoredModifiedKeySequence = this.consumeIgnoredModifiedKeySequence();
|
|
116
121
|
if (ignoredModifiedKeySequence === "consumed")
|
|
117
122
|
continue;
|
|
@@ -277,6 +282,28 @@ export class AppInputController {
|
|
|
277
282
|
this.inputBuffer = this.inputBuffer.slice(result.key.length);
|
|
278
283
|
return "consumed";
|
|
279
284
|
}
|
|
285
|
+
consumeModifiedArrowKeySequence() {
|
|
286
|
+
const result = parseTerminalModifiedKeySequence(this.inputBuffer);
|
|
287
|
+
if (result.kind === "pending")
|
|
288
|
+
return "pending";
|
|
289
|
+
if (result.kind === "none")
|
|
290
|
+
return "none";
|
|
291
|
+
const direction = terminalKeyArrowDirection(result.key);
|
|
292
|
+
if (!direction)
|
|
293
|
+
return "none";
|
|
294
|
+
this.inputBuffer = this.inputBuffer.slice(result.key.length);
|
|
295
|
+
if (terminalKeyShouldIgnore(result.key))
|
|
296
|
+
return "consumed";
|
|
297
|
+
if (direction === "up")
|
|
298
|
+
this.handleArrowUp();
|
|
299
|
+
else if (direction === "down")
|
|
300
|
+
this.handleArrowDown();
|
|
301
|
+
else if (direction === "right")
|
|
302
|
+
this.handleArrowRight();
|
|
303
|
+
else
|
|
304
|
+
this.handleArrowLeft();
|
|
305
|
+
return "consumed";
|
|
306
|
+
}
|
|
280
307
|
consumeClipboardImagePasteSequence() {
|
|
281
308
|
const result = parseTerminalModifiedKeySequence(this.inputBuffer);
|
|
282
309
|
if (result.kind === "pending")
|
|
@@ -39,5 +39,6 @@ export declare function parseTerminalInterruptSequence(input: string): {
|
|
|
39
39
|
export declare function terminalKeyIsShiftEnter(key: ParsedModifiedKey): boolean;
|
|
40
40
|
export declare function terminalKeyIsClipboardImagePaste(key: ParsedModifiedKey): boolean;
|
|
41
41
|
export declare function terminalKeyShouldIgnore(key: ParsedModifiedKey): boolean;
|
|
42
|
+
export declare function terminalKeyArrowDirection(key: ParsedModifiedKey): "up" | "down" | "right" | "left" | undefined;
|
|
42
43
|
export declare function terminalEditShortcutForControlChar(char: string, shiftPressed: boolean): TerminalEditShortcut | undefined;
|
|
43
44
|
export {};
|
|
@@ -11,12 +11,22 @@ const CYRILLIC_SMALL_ES_CODE = 1089;
|
|
|
11
11
|
const CYRILLIC_CAPITAL_ES_CODE = 1057;
|
|
12
12
|
const CYRILLIC_SMALL_EM_CODE = 1084;
|
|
13
13
|
const CYRILLIC_CAPITAL_EM_CODE = 1052;
|
|
14
|
+
const KITTY_ARROW_CODEPOINTS = {
|
|
15
|
+
A: -1,
|
|
16
|
+
B: -2,
|
|
17
|
+
C: -3,
|
|
18
|
+
D: -4,
|
|
19
|
+
};
|
|
14
20
|
const KITTY_CSI_U_SEQUENCE = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u/;
|
|
21
|
+
const KITTY_ARROW_SEQUENCE = /^\x1b\[1;(\d+)(?::(\d+))?([ABCD])/;
|
|
15
22
|
const XTERM_MODIFY_OTHER_KEYS_SEQUENCE = /^\x1b\[27;(\d+);(\d+)~/;
|
|
16
23
|
export function parseTerminalModifiedKeySequence(input) {
|
|
17
24
|
const kitty = parseKittyCsiUSequence(input);
|
|
18
25
|
if (kitty)
|
|
19
26
|
return { kind: "key", key: kitty };
|
|
27
|
+
const kittyArrow = parseKittyArrowSequence(input);
|
|
28
|
+
if (kittyArrow)
|
|
29
|
+
return { kind: "key", key: kittyArrow };
|
|
20
30
|
const xterm = parseXtermModifyOtherKeysSequence(input);
|
|
21
31
|
if (xterm)
|
|
22
32
|
return { kind: "key", key: xterm };
|
|
@@ -57,6 +67,20 @@ export function terminalKeyIsClipboardImagePaste(key) {
|
|
|
57
67
|
export function terminalKeyShouldIgnore(key) {
|
|
58
68
|
return key.eventType === 3;
|
|
59
69
|
}
|
|
70
|
+
export function terminalKeyArrowDirection(key) {
|
|
71
|
+
const effectiveModifier = key.modifier & ~LOCK_MODIFIER_MASK;
|
|
72
|
+
if (effectiveModifier !== 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
if (key.codepoint === KITTY_ARROW_CODEPOINTS.A)
|
|
75
|
+
return "up";
|
|
76
|
+
if (key.codepoint === KITTY_ARROW_CODEPOINTS.B)
|
|
77
|
+
return "down";
|
|
78
|
+
if (key.codepoint === KITTY_ARROW_CODEPOINTS.C)
|
|
79
|
+
return "right";
|
|
80
|
+
if (key.codepoint === KITTY_ARROW_CODEPOINTS.D)
|
|
81
|
+
return "left";
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
60
84
|
export function terminalEditShortcutForControlChar(char, shiftPressed) {
|
|
61
85
|
if (char === "\u001a")
|
|
62
86
|
return shiftPressed ? "redo" : "undo";
|
|
@@ -82,6 +106,24 @@ function parseKittyCsiUSequence(input) {
|
|
|
82
106
|
length: match[0].length,
|
|
83
107
|
};
|
|
84
108
|
}
|
|
109
|
+
function parseKittyArrowSequence(input) {
|
|
110
|
+
const match = KITTY_ARROW_SEQUENCE.exec(input);
|
|
111
|
+
if (!match)
|
|
112
|
+
return undefined;
|
|
113
|
+
const modifierValue = Number.parseInt(match[1] ?? "", 10);
|
|
114
|
+
const eventType = match[2] ? Number.parseInt(match[2], 10) : undefined;
|
|
115
|
+
const arrow = match[3];
|
|
116
|
+
const codepoint = arrow ? KITTY_ARROW_CODEPOINTS[arrow] : undefined;
|
|
117
|
+
if (!Number.isFinite(modifierValue) || codepoint === undefined)
|
|
118
|
+
return undefined;
|
|
119
|
+
return {
|
|
120
|
+
codepoint,
|
|
121
|
+
baseLayoutKey: undefined,
|
|
122
|
+
modifier: modifierValue - 1,
|
|
123
|
+
eventType: Number.isFinite(eventType) ? eventType : undefined,
|
|
124
|
+
length: match[0].length,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
85
127
|
function parseXtermModifyOtherKeysSequence(input) {
|
|
86
128
|
const match = XTERM_MODIFY_OTHER_KEYS_SEQUENCE.exec(input);
|
|
87
129
|
if (!match)
|
|
@@ -7,6 +7,7 @@ import type { Entry, SlashCommand, UserMessageJumpMenuValue } from "../types.js"
|
|
|
7
7
|
import type { AppWorkspaceActionsController } from "../workspace/workspace-actions-controller.js";
|
|
8
8
|
export type AppPopupActionControllerHost = {
|
|
9
9
|
runtime(): AgentSessionRuntime | undefined;
|
|
10
|
+
awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
|
|
10
11
|
getBuiltinSlashCommands(): readonly SlashCommand[];
|
|
11
12
|
isRunning(): boolean;
|
|
12
13
|
setInput(value: string): void;
|
|
@@ -228,6 +228,7 @@ export class AppPopupActionController {
|
|
|
228
228
|
this.host.setStatus("switching session");
|
|
229
229
|
this.host.render();
|
|
230
230
|
try {
|
|
231
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
231
232
|
const result = await runtime.switchSession(session.path);
|
|
232
233
|
if (result.cancelled) {
|
|
233
234
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Resume cancelled." });
|
|
@@ -61,6 +61,7 @@ export declare class AppSessionLifecycleController {
|
|
|
61
61
|
constructor(host: AppSessionLifecycleHost);
|
|
62
62
|
start(): Promise<void>;
|
|
63
63
|
bindCurrentSession(options?: BindCurrentSessionOptions): Promise<void>;
|
|
64
|
+
awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime | undefined): Promise<void>;
|
|
64
65
|
unsubscribeSession(): void;
|
|
65
66
|
afterSessionReplacement(message?: string): void;
|
|
66
67
|
private loadReplacementHistory;
|
|
@@ -85,6 +85,13 @@ export class AppSessionLifecycleController {
|
|
|
85
85
|
}
|
|
86
86
|
await bindPromise;
|
|
87
87
|
}
|
|
88
|
+
async awaitCurrentSessionExtensions(runtime = this.host.runtime()) {
|
|
89
|
+
if (!runtime)
|
|
90
|
+
return;
|
|
91
|
+
if (this.extensionBindRuntime !== runtime || this.extensionBindSession !== runtime.session)
|
|
92
|
+
return;
|
|
93
|
+
await this.extensionBindPromise;
|
|
94
|
+
}
|
|
88
95
|
unsubscribeSession() {
|
|
89
96
|
this.unsubscribe?.();
|
|
90
97
|
}
|
|
@@ -18,6 +18,7 @@ export type AppTabsControllerHost = {
|
|
|
18
18
|
runtime(): AgentSessionRuntime | undefined;
|
|
19
19
|
createRuntimeForNewSession(): Promise<AgentSessionRuntime>;
|
|
20
20
|
createRuntimeForSession(sessionPath: string): Promise<AgentSessionRuntime>;
|
|
21
|
+
awaitCurrentSessionExtensions?(runtime?: AgentSessionRuntime): Promise<void>;
|
|
21
22
|
activateRuntime(runtime: AgentSessionRuntime, options?: BindCurrentSessionOptions): Promise<void>;
|
|
22
23
|
disposeRuntime(runtime: AgentSessionRuntime): Promise<void>;
|
|
23
24
|
isRunning(): boolean;
|
|
@@ -730,6 +730,7 @@ export class AppTabsController {
|
|
|
730
730
|
this.activeTabId = tab.id;
|
|
731
731
|
this.host.setStatus("starting new session");
|
|
732
732
|
this.host.render();
|
|
733
|
+
await this.host.awaitCurrentSessionExtensions?.(runtime);
|
|
733
734
|
const result = await runtime.newSession();
|
|
734
735
|
if (result.cancelled) {
|
|
735
736
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "New session cancelled." });
|
|
@@ -4,6 +4,7 @@ import { type WorkspaceMutation, type WorkspaceMutationFromToolInput, type Works
|
|
|
4
4
|
export type AppWorkspaceActionsControllerHost = {
|
|
5
5
|
readonly entries: Entry[];
|
|
6
6
|
runtime(): AgentSessionRuntime | undefined;
|
|
7
|
+
awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
|
|
7
8
|
findUserEntry(entryId: string): Extract<Entry, {
|
|
8
9
|
kind: "user";
|
|
9
10
|
}> | undefined;
|
|
@@ -94,6 +94,7 @@ export class AppWorkspaceActionsController {
|
|
|
94
94
|
throw new Error("Session entry for this message is not available yet");
|
|
95
95
|
this.host.setStatus("forking session");
|
|
96
96
|
this.host.render();
|
|
97
|
+
await this.host.awaitCurrentSessionExtensions(runtime);
|
|
97
98
|
const result = await runtime.fork(sessionEntryId);
|
|
98
99
|
if (result.cancelled) {
|
|
99
100
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Fork cancelled." });
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"smoke:tools": "PI_OFFLINE=1 pi --no-session -p \"ping\"",
|
|
23
23
|
"smoke": "npm run smoke:explicit && npm run smoke:auto && npm run smoke:tools",
|
|
24
24
|
"test": "bun test test",
|
|
25
|
-
"test:async-subagents-e2e": "ASYNC_SUBAGENTS_E2E=1 ASYNC_SUBAGENTS_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=30 test/async-subagents",
|
|
25
|
+
"test:async-subagents-e2e": "ASYNC_SUBAGENTS_E2E=1 ASYNC_SUBAGENTS_DEBUG_LOGS=1 ASYNC_SUBAGENTS_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=30 test/async-subagents",
|
|
26
26
|
"test:async-subagents-selection-e2e": "ASYNC_SUBAGENTS_SELECTION_E2E=1 ASYNC_SUBAGENTS_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=30 test/async-subagents/selection-e2e.test.ts",
|
|
27
27
|
"bench:locate": "PI_LOCATE_BENCH_ITERATIONS=5 PI_LOCATE_BENCH_FAKE_IDX=0 PI_LOCATE_BENCH_MODEL=zai/glm-5-turbo PI_LOCATE_BENCH_MODES=direct-read-grep,ast-structural,repo-search-hybrid,repo-discovery,subagent-search,unrestricted-suite node test/fixtures/hard-to-find-project/benchmark/run-locate-benchmark.mjs",
|
|
28
28
|
"bench:locate:analyze": "node test/fixtures/hard-to-find-project/benchmark/analyze-locate-benchmark.mjs",
|
|
29
29
|
"test:locate-benchmark-e2e": "PI_LOCATE_BENCH_E2E=1 PI_LOCATE_BENCH_MODEL=zai/glm-5-turbo bun test test/locate-benchmark-e2e.test.ts",
|
|
30
30
|
"test:tool-selection-e2e": "TOOL_SELECTION_E2E=1 TOOL_SELECTION_E2E_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=30 test/tool-selection-e2e.test.ts",
|
|
31
|
-
"test:e2e": "TOOL_SELECTION_E2E=1 TOOL_SELECTION_E2E_MODEL=zai/glm-5-turbo ASYNC_SUBAGENTS_E2E=1 ASYNC_SUBAGENTS_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=5 test/tool-selection-e2e.test.ts test/async-subagents/e2e.test.ts test/async-subagents/selection-e2e.test.ts test/todo-persistence-e2e.test.ts",
|
|
31
|
+
"test:e2e": "TOOL_SELECTION_E2E=1 TOOL_SELECTION_E2E_MODEL=zai/glm-5-turbo ASYNC_SUBAGENTS_E2E=1 ASYNC_SUBAGENTS_DEBUG_LOGS=1 ASYNC_SUBAGENTS_MODEL=zai/glm-5-turbo bun test --concurrent --max-concurrency=5 test/tool-selection-e2e.test.ts test/async-subagents/e2e.test.ts test/async-subagents/selection-e2e.test.ts test/todo-persistence-e2e.test.ts",
|
|
32
32
|
"typecheck:async-subagents": "bash scripts/typecheck-source.sh",
|
|
33
33
|
"check": "npm run smoke"
|
|
34
34
|
},
|