@mariozechner/pi-coding-agent 0.34.2 → 0.35.0
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/CHANGELOG.md +204 -0
- package/README.md +233 -105
- package/dist/cli/args.d.ts +3 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -18
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +39 -50
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +166 -197
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -5
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +10 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +21 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +400 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +88 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/runner.js +52 -141
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +461 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/types.js +7 -4
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +25 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +7 -7
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +4 -4
- package/dist/core/messages.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +40 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/sdk.d.ts +29 -52
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +111 -211
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -17
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +25 -10
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -6
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +4 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +36 -33
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +7 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +93 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +4 -4
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +18 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +2 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +3 -3
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +9 -9
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +37 -44
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +143 -189
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +10 -33
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -3
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +3 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +33 -57
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +16 -16
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/extensions.md +1053 -0
- package/docs/rpc.md +4 -4
- package/docs/sdk.md +62 -93
- package/docs/session.md +22 -19
- package/docs/skills.md +1 -1
- package/docs/tui.md +1 -1
- package/examples/README.md +9 -15
- package/examples/extensions/README.md +141 -0
- package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
- package/examples/extensions/chalk-logger.ts +26 -0
- package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
- package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
- package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
- package/examples/{hooks → extensions}/file-trigger.ts +3 -3
- package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
- package/examples/{hooks → extensions}/handoff.ts +3 -3
- package/examples/extensions/hello.ts +25 -0
- package/examples/{hooks → extensions}/permission-gate.ts +3 -3
- package/examples/{hooks → extensions}/pirate.ts +5 -5
- package/examples/{hooks → extensions}/plan-mode.ts +6 -6
- package/examples/{hooks → extensions}/protected-paths.ts +3 -3
- package/examples/{hooks → extensions}/qna.ts +3 -3
- package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
- package/examples/{hooks → extensions}/snake.ts +3 -3
- package/examples/{hooks → extensions}/status-line.ts +3 -3
- package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
- package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
- package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
- package/examples/{hooks → extensions}/tools.ts +5 -5
- package/examples/extensions/with-deps/index.ts +40 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/05-tools.ts +7 -41
- package/examples/sdk/06-extensions.ts +81 -0
- package/examples/sdk/08-prompt-templates.ts +42 -0
- package/examples/sdk/12-full-control.ts +10 -29
- package/examples/sdk/README.md +5 -5
- package/package.json +4 -4
- package/dist/core/custom-tools/index.d.ts +0 -7
- package/dist/core/custom-tools/index.d.ts.map +0 -1
- package/dist/core/custom-tools/index.js +0 -6
- package/dist/core/custom-tools/index.js.map +0 -1
- package/dist/core/custom-tools/loader.d.ts +0 -30
- package/dist/core/custom-tools/loader.d.ts.map +0 -1
- package/dist/core/custom-tools/loader.js +0 -276
- package/dist/core/custom-tools/loader.js.map +0 -1
- package/dist/core/custom-tools/types.d.ts +0 -144
- package/dist/core/custom-tools/types.d.ts.map +0 -1
- package/dist/core/custom-tools/types.js +0 -8
- package/dist/core/custom-tools/types.js.map +0 -1
- package/dist/core/custom-tools/wrapper.d.ts +0 -15
- package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
- package/dist/core/custom-tools/wrapper.js +0 -23
- package/dist/core/custom-tools/wrapper.js.map +0 -1
- package/dist/core/hooks/index.d.ts +0 -6
- package/dist/core/hooks/index.d.ts.map +0 -1
- package/dist/core/hooks/index.js +0 -6
- package/dist/core/hooks/index.js.map +0 -1
- package/dist/core/hooks/loader.d.ts +0 -146
- package/dist/core/hooks/loader.d.ts.map +0 -1
- package/dist/core/hooks/loader.js +0 -275
- package/dist/core/hooks/loader.js.map +0 -1
- package/dist/core/hooks/runner.d.ts +0 -173
- package/dist/core/hooks/runner.d.ts.map +0 -1
- package/dist/core/hooks/runner.js.map +0 -1
- package/dist/core/hooks/tool-wrapper.d.ts +0 -17
- package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
- package/dist/core/hooks/tool-wrapper.js.map +0 -1
- package/dist/core/hooks/types.d.ts +0 -767
- package/dist/core/hooks/types.d.ts.map +0 -1
- package/dist/core/hooks/types.js.map +0 -1
- package/dist/core/slash-commands.d.ts +0 -40
- package/dist/core/slash-commands.d.ts.map +0 -1
- package/dist/core/slash-commands.js.map +0 -1
- package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-editor.js.map +0 -1
- package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-input.js.map +0 -1
- package/dist/modes/interactive/components/hook-message.d.ts +0 -18
- package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-message.js.map +0 -1
- package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-selector.js.map +0 -1
- package/docs/custom-tools.md +0 -514
- package/docs/extension-loading.md +0 -1004
- package/docs/hooks.md +0 -979
- package/docs/session-tree-plan.md +0 -441
- package/examples/custom-tools/README.md +0 -114
- package/examples/custom-tools/hello/index.ts +0 -21
- package/examples/hooks/README.md +0 -60
- package/examples/hooks/todo/index.ts +0 -134
- package/examples/sdk/06-hooks.ts +0 -61
- package/examples/sdk/08-slash-commands.ts +0 -42
- /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
|
@@ -24,12 +24,12 @@ import { BorderedLoader } from "./components/bordered-loader.js";
|
|
|
24
24
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
25
25
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
26
26
|
import { CustomEditor } from "./components/custom-editor.js";
|
|
27
|
+
import { CustomMessageComponent } from "./components/custom-message.js";
|
|
27
28
|
import { DynamicBorder } from "./components/dynamic-border.js";
|
|
29
|
+
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
30
|
+
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
31
|
+
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
28
32
|
import { FooterComponent } from "./components/footer.js";
|
|
29
|
-
import { HookEditorComponent } from "./components/hook-editor.js";
|
|
30
|
-
import { HookInputComponent } from "./components/hook-input.js";
|
|
31
|
-
import { HookMessageComponent } from "./components/hook-message.js";
|
|
32
|
-
import { HookSelectorComponent } from "./components/hook-selector.js";
|
|
33
33
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
34
34
|
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
|
|
35
35
|
import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
@@ -43,7 +43,7 @@ function isExpandable(obj) {
|
|
|
43
43
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
44
44
|
}
|
|
45
45
|
export class InteractiveMode {
|
|
46
|
-
|
|
46
|
+
setExtensionUIContext;
|
|
47
47
|
session;
|
|
48
48
|
ui;
|
|
49
49
|
chatContainer;
|
|
@@ -86,15 +86,13 @@ export class InteractiveMode {
|
|
|
86
86
|
// Auto-retry state
|
|
87
87
|
retryLoader = undefined;
|
|
88
88
|
retryEscapeHandler;
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
89
|
+
// Extension UI state
|
|
90
|
+
extensionSelector = undefined;
|
|
91
|
+
extensionInput = undefined;
|
|
92
|
+
extensionEditor = undefined;
|
|
93
|
+
// Extension widgets (components rendered above the editor)
|
|
94
|
+
extensionWidgets = new Map();
|
|
95
95
|
widgetContainer;
|
|
96
|
-
// Custom tools for custom rendering
|
|
97
|
-
customTools;
|
|
98
96
|
// Convenience accessors
|
|
99
97
|
get agent() {
|
|
100
98
|
return this.session.agent;
|
|
@@ -105,12 +103,11 @@ export class InteractiveMode {
|
|
|
105
103
|
get settingsManager() {
|
|
106
104
|
return this.session.settingsManager;
|
|
107
105
|
}
|
|
108
|
-
constructor(session, version, changelogMarkdown = undefined,
|
|
109
|
-
this.
|
|
106
|
+
constructor(session, version, changelogMarkdown = undefined, _extensions = [], setExtensionUIContext = () => { }, fdPath = undefined) {
|
|
107
|
+
this.setExtensionUIContext = setExtensionUIContext;
|
|
110
108
|
this.session = session;
|
|
111
109
|
this.version = version;
|
|
112
110
|
this.changelogMarkdown = changelogMarkdown;
|
|
113
|
-
this.customTools = new Map(customTools.map((ct) => [ct.tool.name, ct]));
|
|
114
111
|
this.ui = new TUI(new ProcessTerminal());
|
|
115
112
|
this.chatContainer = new Container();
|
|
116
113
|
this.pendingMessagesContainer = new Container();
|
|
@@ -122,7 +119,7 @@ export class InteractiveMode {
|
|
|
122
119
|
this.editorContainer.addChild(this.editor);
|
|
123
120
|
this.footer = new FooterComponent(session);
|
|
124
121
|
this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
|
|
125
|
-
// Define
|
|
122
|
+
// Define commands for autocomplete
|
|
126
123
|
const slashCommands = [
|
|
127
124
|
{ name: "settings", description: "Open settings menu" },
|
|
128
125
|
{ name: "model", description: "Select model (opens selector UI)" },
|
|
@@ -142,18 +139,18 @@ export class InteractiveMode {
|
|
|
142
139
|
];
|
|
143
140
|
// Load hide thinking block setting
|
|
144
141
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
145
|
-
// Convert
|
|
146
|
-
const
|
|
142
|
+
// Convert prompt templates to SlashCommand format for autocomplete
|
|
143
|
+
const templateCommands = this.session.promptTemplates.map((cmd) => ({
|
|
147
144
|
name: cmd.name,
|
|
148
145
|
description: cmd.description,
|
|
149
146
|
}));
|
|
150
|
-
// Convert
|
|
151
|
-
const
|
|
147
|
+
// Convert extension commands to SlashCommand format
|
|
148
|
+
const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map((cmd) => ({
|
|
152
149
|
name: cmd.name,
|
|
153
|
-
description: cmd.description ?? "(
|
|
150
|
+
description: cmd.description ?? "(extension command)",
|
|
154
151
|
}));
|
|
155
152
|
// Setup autocomplete
|
|
156
|
-
const autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...
|
|
153
|
+
const autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands], process.cwd(), fdPath);
|
|
157
154
|
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
158
155
|
}
|
|
159
156
|
async init() {
|
|
@@ -268,8 +265,8 @@ export class InteractiveMode {
|
|
|
268
265
|
// Set terminal title
|
|
269
266
|
const cwdBasename = path.basename(process.cwd());
|
|
270
267
|
this.ui.terminal.setTitle(`pi - ${cwdBasename}`);
|
|
271
|
-
// Initialize
|
|
272
|
-
await this.
|
|
268
|
+
// Initialize extensions with TUI-based UI context
|
|
269
|
+
await this.initExtensions();
|
|
273
270
|
// Subscribe to agent events
|
|
274
271
|
this.subscribeToAgent();
|
|
275
272
|
// Set up theme file watcher
|
|
@@ -284,12 +281,12 @@ export class InteractiveMode {
|
|
|
284
281
|
});
|
|
285
282
|
}
|
|
286
283
|
// =========================================================================
|
|
287
|
-
//
|
|
284
|
+
// Extension System
|
|
288
285
|
// =========================================================================
|
|
289
286
|
/**
|
|
290
|
-
* Initialize the
|
|
287
|
+
* Initialize the extension system with TUI-based UI context.
|
|
291
288
|
*/
|
|
292
|
-
async
|
|
289
|
+
async initExtensions() {
|
|
293
290
|
// Show loaded project context files
|
|
294
291
|
const contextFiles = loadProjectContextFiles();
|
|
295
292
|
if (contextFiles.length > 0) {
|
|
@@ -315,32 +312,19 @@ export class InteractiveMode {
|
|
|
315
312
|
this.chatContainer.addChild(new Spacer(1));
|
|
316
313
|
}
|
|
317
314
|
}
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
315
|
+
// Create and set extension UI context
|
|
316
|
+
const uiContext = this.createExtensionUIContext();
|
|
317
|
+
this.setExtensionUIContext(uiContext, true);
|
|
318
|
+
const extensionRunner = this.session.extensionRunner;
|
|
319
|
+
if (!extensionRunner) {
|
|
320
|
+
return; // No extensions loaded
|
|
325
321
|
}
|
|
326
|
-
|
|
327
|
-
const uiContext = this.createHookUIContext();
|
|
328
|
-
this.setToolUIContext(uiContext, true);
|
|
329
|
-
// Notify custom tools of session start
|
|
330
|
-
await this.emitCustomToolSessionEvent({
|
|
331
|
-
reason: "start",
|
|
332
|
-
previousSessionFile: undefined,
|
|
333
|
-
});
|
|
334
|
-
const hookRunner = this.session.hookRunner;
|
|
335
|
-
if (!hookRunner) {
|
|
336
|
-
return; // No hooks loaded
|
|
337
|
-
}
|
|
338
|
-
hookRunner.initialize({
|
|
322
|
+
extensionRunner.initialize({
|
|
339
323
|
getModel: () => this.session.model,
|
|
340
324
|
sendMessageHandler: (message, options) => {
|
|
341
325
|
const wasStreaming = this.session.isStreaming;
|
|
342
326
|
this.session
|
|
343
|
-
.
|
|
327
|
+
.sendCustomMessage(message, options)
|
|
344
328
|
.then(() => {
|
|
345
329
|
// For non-streaming cases with display=true, update UI
|
|
346
330
|
// (streaming cases update via message_end event)
|
|
@@ -349,7 +333,7 @@ export class InteractiveMode {
|
|
|
349
333
|
}
|
|
350
334
|
})
|
|
351
335
|
.catch((err) => {
|
|
352
|
-
this.showError(`
|
|
336
|
+
this.showError(`Extension sendMessage failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
353
337
|
});
|
|
354
338
|
},
|
|
355
339
|
appendEntryHandler: (customType, data) => {
|
|
@@ -420,66 +404,42 @@ export class InteractiveMode {
|
|
|
420
404
|
uiContext,
|
|
421
405
|
hasUI: true,
|
|
422
406
|
});
|
|
423
|
-
// Subscribe to
|
|
424
|
-
|
|
425
|
-
this.
|
|
407
|
+
// Subscribe to extension errors
|
|
408
|
+
extensionRunner.onError((error) => {
|
|
409
|
+
this.showExtensionError(error.extensionPath, error.error, error.stack);
|
|
426
410
|
});
|
|
427
|
-
// Set up
|
|
428
|
-
this.
|
|
429
|
-
// Show loaded
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
432
|
-
const
|
|
433
|
-
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded
|
|
411
|
+
// Set up extension-registered shortcuts
|
|
412
|
+
this.setupExtensionShortcuts(extensionRunner);
|
|
413
|
+
// Show loaded extensions
|
|
414
|
+
const extensionPaths = extensionRunner.getExtensionPaths();
|
|
415
|
+
if (extensionPaths.length > 0) {
|
|
416
|
+
const extList = extensionPaths.map((p) => theme.fg("dim", ` ${p}`)).join("\n");
|
|
417
|
+
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded extensions:\n") + extList, 0, 0));
|
|
434
418
|
this.chatContainer.addChild(new Spacer(1));
|
|
435
419
|
}
|
|
436
420
|
// Emit session_start event
|
|
437
|
-
await
|
|
421
|
+
await extensionRunner.emit({
|
|
438
422
|
type: "session_start",
|
|
439
423
|
});
|
|
440
424
|
}
|
|
441
425
|
/**
|
|
442
|
-
*
|
|
426
|
+
* Get a registered tool definition by name (for custom rendering).
|
|
443
427
|
*/
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
await tool.onSession(event, {
|
|
449
|
-
sessionManager: this.session.sessionManager,
|
|
450
|
-
modelRegistry: this.session.modelRegistry,
|
|
451
|
-
model: this.session.model,
|
|
452
|
-
isIdle: () => !this.session.isStreaming,
|
|
453
|
-
hasPendingMessages: () => this.session.pendingMessageCount > 0,
|
|
454
|
-
abort: () => {
|
|
455
|
-
this.session.abort();
|
|
456
|
-
},
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
catch (err) {
|
|
460
|
-
this.showToolError(tool.name, err instanceof Error ? err.message : String(err));
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Show a tool error in the chat.
|
|
467
|
-
*/
|
|
468
|
-
showToolError(toolName, error) {
|
|
469
|
-
const errorText = new Text(theme.fg("error", `Tool "${toolName}" error: ${error}`), 1, 0);
|
|
470
|
-
this.chatContainer.addChild(errorText);
|
|
471
|
-
this.ui.requestRender();
|
|
428
|
+
getRegisteredToolDefinition(toolName) {
|
|
429
|
+
const tools = this.session.extensionRunner?.getAllRegisteredTools() ?? [];
|
|
430
|
+
const registeredTool = tools.find((t) => t.definition.name === toolName);
|
|
431
|
+
return registeredTool?.definition;
|
|
472
432
|
}
|
|
473
433
|
/**
|
|
474
|
-
* Set up keyboard shortcuts registered by
|
|
434
|
+
* Set up keyboard shortcuts registered by extensions.
|
|
475
435
|
*/
|
|
476
|
-
|
|
477
|
-
const shortcuts =
|
|
436
|
+
setupExtensionShortcuts(extensionRunner) {
|
|
437
|
+
const shortcuts = extensionRunner.getShortcuts();
|
|
478
438
|
if (shortcuts.size === 0)
|
|
479
439
|
return;
|
|
480
440
|
// Create a context for shortcut handlers
|
|
481
441
|
const createContext = () => ({
|
|
482
|
-
ui: this.
|
|
442
|
+
ui: this.createExtensionUIContext(),
|
|
483
443
|
hasUI: true,
|
|
484
444
|
cwd: process.cwd(),
|
|
485
445
|
sessionManager: this.sessionManager,
|
|
@@ -489,10 +449,10 @@ export class InteractiveMode {
|
|
|
489
449
|
abort: () => this.session.abort(),
|
|
490
450
|
hasPendingMessages: () => this.session.pendingMessageCount > 0,
|
|
491
451
|
});
|
|
492
|
-
// Set up the
|
|
493
|
-
this.editor.
|
|
452
|
+
// Set up the extension shortcut handler on the editor
|
|
453
|
+
this.editor.onExtensionShortcut = (data) => {
|
|
494
454
|
for (const [shortcutStr, shortcut] of shortcuts) {
|
|
495
|
-
// Cast to KeyId -
|
|
455
|
+
// Cast to KeyId - extension shortcuts use the same format
|
|
496
456
|
if (matchesKey(data, shortcutStr)) {
|
|
497
457
|
// Run handler async, don't block input
|
|
498
458
|
Promise.resolve(shortcut.handler(createContext())).catch((err) => {
|
|
@@ -505,22 +465,22 @@ export class InteractiveMode {
|
|
|
505
465
|
};
|
|
506
466
|
}
|
|
507
467
|
/**
|
|
508
|
-
* Set
|
|
468
|
+
* Set extension status text in the footer.
|
|
509
469
|
*/
|
|
510
|
-
|
|
511
|
-
this.footer.
|
|
470
|
+
setExtensionStatus(key, text) {
|
|
471
|
+
this.footer.setExtensionStatus(key, text);
|
|
512
472
|
this.ui.requestRender();
|
|
513
473
|
}
|
|
514
474
|
/**
|
|
515
|
-
* Set
|
|
475
|
+
* Set an extension widget (string array or custom component).
|
|
516
476
|
*/
|
|
517
|
-
|
|
477
|
+
setExtensionWidget(key, content) {
|
|
518
478
|
// Dispose and remove existing widget
|
|
519
|
-
const existing = this.
|
|
479
|
+
const existing = this.extensionWidgets.get(key);
|
|
520
480
|
if (existing?.dispose)
|
|
521
481
|
existing.dispose();
|
|
522
482
|
if (content === undefined) {
|
|
523
|
-
this.
|
|
483
|
+
this.extensionWidgets.delete(key);
|
|
524
484
|
}
|
|
525
485
|
else if (Array.isArray(content)) {
|
|
526
486
|
// Wrap string array in a Container with Text components
|
|
@@ -531,149 +491,149 @@ export class InteractiveMode {
|
|
|
531
491
|
if (content.length > InteractiveMode.MAX_WIDGET_LINES) {
|
|
532
492
|
container.addChild(new Text(theme.fg("muted", "... (widget truncated)"), 1, 0));
|
|
533
493
|
}
|
|
534
|
-
this.
|
|
494
|
+
this.extensionWidgets.set(key, container);
|
|
535
495
|
}
|
|
536
496
|
else {
|
|
537
497
|
// Factory function - create component
|
|
538
498
|
const component = content(this.ui, theme);
|
|
539
|
-
this.
|
|
499
|
+
this.extensionWidgets.set(key, component);
|
|
540
500
|
}
|
|
541
501
|
this.renderWidgets();
|
|
542
502
|
}
|
|
543
503
|
// Maximum total widget lines to prevent viewport overflow
|
|
544
504
|
static MAX_WIDGET_LINES = 10;
|
|
545
505
|
/**
|
|
546
|
-
* Render all
|
|
506
|
+
* Render all extension widgets to the widget container.
|
|
547
507
|
*/
|
|
548
508
|
renderWidgets() {
|
|
549
509
|
if (!this.widgetContainer)
|
|
550
510
|
return;
|
|
551
511
|
this.widgetContainer.clear();
|
|
552
|
-
if (this.
|
|
512
|
+
if (this.extensionWidgets.size === 0) {
|
|
553
513
|
this.ui.requestRender();
|
|
554
514
|
return;
|
|
555
515
|
}
|
|
556
|
-
for (const [_key, component] of this.
|
|
516
|
+
for (const [_key, component] of this.extensionWidgets) {
|
|
557
517
|
this.widgetContainer.addChild(component);
|
|
558
518
|
}
|
|
559
519
|
this.ui.requestRender();
|
|
560
520
|
}
|
|
561
521
|
/**
|
|
562
|
-
* Create the
|
|
522
|
+
* Create the ExtensionUIContext for extensions.
|
|
563
523
|
*/
|
|
564
|
-
|
|
524
|
+
createExtensionUIContext() {
|
|
565
525
|
return {
|
|
566
|
-
select: (title, options) => this.
|
|
567
|
-
confirm: (title, message) => this.
|
|
568
|
-
input: (title, placeholder) => this.
|
|
569
|
-
notify: (message, type) => this.
|
|
570
|
-
setStatus: (key, text) => this.
|
|
571
|
-
setWidget: (key, content) => this.
|
|
526
|
+
select: (title, options) => this.showExtensionSelector(title, options),
|
|
527
|
+
confirm: (title, message) => this.showExtensionConfirm(title, message),
|
|
528
|
+
input: (title, placeholder) => this.showExtensionInput(title, placeholder),
|
|
529
|
+
notify: (message, type) => this.showExtensionNotify(message, type),
|
|
530
|
+
setStatus: (key, text) => this.setExtensionStatus(key, text),
|
|
531
|
+
setWidget: (key, content) => this.setExtensionWidget(key, content),
|
|
572
532
|
setTitle: (title) => this.ui.terminal.setTitle(title),
|
|
573
|
-
custom: (factory) => this.
|
|
533
|
+
custom: (factory) => this.showExtensionCustom(factory),
|
|
574
534
|
setEditorText: (text) => this.editor.setText(text),
|
|
575
535
|
getEditorText: () => this.editor.getText(),
|
|
576
|
-
editor: (title, prefill) => this.
|
|
536
|
+
editor: (title, prefill) => this.showExtensionEditor(title, prefill),
|
|
577
537
|
get theme() {
|
|
578
538
|
return theme;
|
|
579
539
|
},
|
|
580
540
|
};
|
|
581
541
|
}
|
|
582
542
|
/**
|
|
583
|
-
* Show a selector for
|
|
543
|
+
* Show a selector for extensions.
|
|
584
544
|
*/
|
|
585
|
-
|
|
545
|
+
showExtensionSelector(title, options) {
|
|
586
546
|
return new Promise((resolve) => {
|
|
587
|
-
this.
|
|
588
|
-
this.
|
|
547
|
+
this.extensionSelector = new ExtensionSelectorComponent(title, options, (option) => {
|
|
548
|
+
this.hideExtensionSelector();
|
|
589
549
|
resolve(option);
|
|
590
550
|
}, () => {
|
|
591
|
-
this.
|
|
551
|
+
this.hideExtensionSelector();
|
|
592
552
|
resolve(undefined);
|
|
593
553
|
});
|
|
594
554
|
this.editorContainer.clear();
|
|
595
|
-
this.editorContainer.addChild(this.
|
|
596
|
-
this.ui.setFocus(this.
|
|
555
|
+
this.editorContainer.addChild(this.extensionSelector);
|
|
556
|
+
this.ui.setFocus(this.extensionSelector);
|
|
597
557
|
this.ui.requestRender();
|
|
598
558
|
});
|
|
599
559
|
}
|
|
600
560
|
/**
|
|
601
|
-
* Hide the
|
|
561
|
+
* Hide the extension selector.
|
|
602
562
|
*/
|
|
603
|
-
|
|
563
|
+
hideExtensionSelector() {
|
|
604
564
|
this.editorContainer.clear();
|
|
605
565
|
this.editorContainer.addChild(this.editor);
|
|
606
|
-
this.
|
|
566
|
+
this.extensionSelector = undefined;
|
|
607
567
|
this.ui.setFocus(this.editor);
|
|
608
568
|
this.ui.requestRender();
|
|
609
569
|
}
|
|
610
570
|
/**
|
|
611
|
-
* Show a confirmation dialog for
|
|
571
|
+
* Show a confirmation dialog for extensions.
|
|
612
572
|
*/
|
|
613
|
-
async
|
|
614
|
-
const result = await this.
|
|
573
|
+
async showExtensionConfirm(title, message) {
|
|
574
|
+
const result = await this.showExtensionSelector(`${title}\n${message}`, ["Yes", "No"]);
|
|
615
575
|
return result === "Yes";
|
|
616
576
|
}
|
|
617
577
|
/**
|
|
618
|
-
* Show a text input for
|
|
578
|
+
* Show a text input for extensions.
|
|
619
579
|
*/
|
|
620
|
-
|
|
580
|
+
showExtensionInput(title, placeholder) {
|
|
621
581
|
return new Promise((resolve) => {
|
|
622
|
-
this.
|
|
623
|
-
this.
|
|
582
|
+
this.extensionInput = new ExtensionInputComponent(title, placeholder, (value) => {
|
|
583
|
+
this.hideExtensionInput();
|
|
624
584
|
resolve(value);
|
|
625
585
|
}, () => {
|
|
626
|
-
this.
|
|
586
|
+
this.hideExtensionInput();
|
|
627
587
|
resolve(undefined);
|
|
628
588
|
});
|
|
629
589
|
this.editorContainer.clear();
|
|
630
|
-
this.editorContainer.addChild(this.
|
|
631
|
-
this.ui.setFocus(this.
|
|
590
|
+
this.editorContainer.addChild(this.extensionInput);
|
|
591
|
+
this.ui.setFocus(this.extensionInput);
|
|
632
592
|
this.ui.requestRender();
|
|
633
593
|
});
|
|
634
594
|
}
|
|
635
595
|
/**
|
|
636
|
-
* Hide the
|
|
596
|
+
* Hide the extension input.
|
|
637
597
|
*/
|
|
638
|
-
|
|
598
|
+
hideExtensionInput() {
|
|
639
599
|
this.editorContainer.clear();
|
|
640
600
|
this.editorContainer.addChild(this.editor);
|
|
641
|
-
this.
|
|
601
|
+
this.extensionInput = undefined;
|
|
642
602
|
this.ui.setFocus(this.editor);
|
|
643
603
|
this.ui.requestRender();
|
|
644
604
|
}
|
|
645
605
|
/**
|
|
646
|
-
* Show a multi-line editor for
|
|
606
|
+
* Show a multi-line editor for extensions (with Ctrl+G support).
|
|
647
607
|
*/
|
|
648
|
-
|
|
608
|
+
showExtensionEditor(title, prefill) {
|
|
649
609
|
return new Promise((resolve) => {
|
|
650
|
-
this.
|
|
651
|
-
this.
|
|
610
|
+
this.extensionEditor = new ExtensionEditorComponent(this.ui, title, prefill, (value) => {
|
|
611
|
+
this.hideExtensionEditor();
|
|
652
612
|
resolve(value);
|
|
653
613
|
}, () => {
|
|
654
|
-
this.
|
|
614
|
+
this.hideExtensionEditor();
|
|
655
615
|
resolve(undefined);
|
|
656
616
|
});
|
|
657
617
|
this.editorContainer.clear();
|
|
658
|
-
this.editorContainer.addChild(this.
|
|
659
|
-
this.ui.setFocus(this.
|
|
618
|
+
this.editorContainer.addChild(this.extensionEditor);
|
|
619
|
+
this.ui.setFocus(this.extensionEditor);
|
|
660
620
|
this.ui.requestRender();
|
|
661
621
|
});
|
|
662
622
|
}
|
|
663
623
|
/**
|
|
664
|
-
* Hide the
|
|
624
|
+
* Hide the extension editor.
|
|
665
625
|
*/
|
|
666
|
-
|
|
626
|
+
hideExtensionEditor() {
|
|
667
627
|
this.editorContainer.clear();
|
|
668
628
|
this.editorContainer.addChild(this.editor);
|
|
669
|
-
this.
|
|
629
|
+
this.extensionEditor = undefined;
|
|
670
630
|
this.ui.setFocus(this.editor);
|
|
671
631
|
this.ui.requestRender();
|
|
672
632
|
}
|
|
673
633
|
/**
|
|
674
|
-
* Show a notification for
|
|
634
|
+
* Show a notification for extensions.
|
|
675
635
|
*/
|
|
676
|
-
|
|
636
|
+
showExtensionNotify(message, type) {
|
|
677
637
|
if (type === "error") {
|
|
678
638
|
this.showError(message);
|
|
679
639
|
}
|
|
@@ -687,7 +647,7 @@ export class InteractiveMode {
|
|
|
687
647
|
/**
|
|
688
648
|
* Show a custom component with keyboard focus.
|
|
689
649
|
*/
|
|
690
|
-
async
|
|
650
|
+
async showExtensionCustom(factory) {
|
|
691
651
|
const savedText = this.editor.getText();
|
|
692
652
|
return new Promise((resolve) => {
|
|
693
653
|
let component;
|
|
@@ -710,10 +670,10 @@ export class InteractiveMode {
|
|
|
710
670
|
});
|
|
711
671
|
}
|
|
712
672
|
/**
|
|
713
|
-
* Show
|
|
673
|
+
* Show an extension error in the UI.
|
|
714
674
|
*/
|
|
715
|
-
|
|
716
|
-
const errorMsg = `
|
|
675
|
+
showExtensionError(extensionPath, error, stack) {
|
|
676
|
+
const errorMsg = `Extension "${extensionPath}" error: ${error}`;
|
|
717
677
|
const errorText = new Text(theme.fg("error", errorMsg), 1, 0);
|
|
718
678
|
this.chatContainer.addChild(errorText);
|
|
719
679
|
if (stack) {
|
|
@@ -729,10 +689,6 @@ export class InteractiveMode {
|
|
|
729
689
|
}
|
|
730
690
|
this.ui.requestRender();
|
|
731
691
|
}
|
|
732
|
-
/**
|
|
733
|
-
* Handle pi.send() from hooks.
|
|
734
|
-
* If streaming, queue the message. Otherwise, start a new agent loop.
|
|
735
|
-
*/
|
|
736
692
|
// =========================================================================
|
|
737
693
|
// Key Handlers
|
|
738
694
|
// =========================================================================
|
|
@@ -827,7 +783,7 @@ export class InteractiveMode {
|
|
|
827
783
|
text = text.trim();
|
|
828
784
|
if (!text)
|
|
829
785
|
return;
|
|
830
|
-
// Handle
|
|
786
|
+
// Handle commands
|
|
831
787
|
if (text === "/settings") {
|
|
832
788
|
this.showSettingsSelector();
|
|
833
789
|
this.editor.setText("");
|
|
@@ -947,7 +903,7 @@ export class InteractiveMode {
|
|
|
947
903
|
return;
|
|
948
904
|
}
|
|
949
905
|
// If streaming, use prompt() with steer behavior
|
|
950
|
-
// This handles
|
|
906
|
+
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
951
907
|
if (this.session.isStreaming) {
|
|
952
908
|
this.editor.addToHistory(text);
|
|
953
909
|
this.editor.setText("");
|
|
@@ -986,7 +942,7 @@ export class InteractiveMode {
|
|
|
986
942
|
this.ui.requestRender();
|
|
987
943
|
break;
|
|
988
944
|
case "message_start":
|
|
989
|
-
if (event.message.role === "
|
|
945
|
+
if (event.message.role === "custom") {
|
|
990
946
|
this.addMessageToChat(event.message);
|
|
991
947
|
this.ui.requestRender();
|
|
992
948
|
}
|
|
@@ -1014,7 +970,7 @@ export class InteractiveMode {
|
|
|
1014
970
|
this.chatContainer.addChild(new Text("", 0, 0));
|
|
1015
971
|
const component = new ToolExecutionComponent(content.name, content.arguments, {
|
|
1016
972
|
showImages: this.settingsManager.getShowImages(),
|
|
1017
|
-
}, this.
|
|
973
|
+
}, this.getRegisteredToolDefinition(content.name), this.ui);
|
|
1018
974
|
component.setExpanded(this.toolOutputExpanded);
|
|
1019
975
|
this.chatContainer.addChild(component);
|
|
1020
976
|
this.pendingTools.set(content.id, component);
|
|
@@ -1064,7 +1020,7 @@ export class InteractiveMode {
|
|
|
1064
1020
|
if (!this.pendingTools.has(event.toolCallId)) {
|
|
1065
1021
|
const component = new ToolExecutionComponent(event.toolName, event.args, {
|
|
1066
1022
|
showImages: this.settingsManager.getShowImages(),
|
|
1067
|
-
}, this.
|
|
1023
|
+
}, this.getRegisteredToolDefinition(event.toolName), this.ui);
|
|
1068
1024
|
component.setExpanded(this.toolOutputExpanded);
|
|
1069
1025
|
this.chatContainer.addChild(component);
|
|
1070
1026
|
this.pendingTools.set(event.toolCallId, component);
|
|
@@ -1231,10 +1187,10 @@ export class InteractiveMode {
|
|
|
1231
1187
|
this.chatContainer.addChild(component);
|
|
1232
1188
|
break;
|
|
1233
1189
|
}
|
|
1234
|
-
case "
|
|
1190
|
+
case "custom": {
|
|
1235
1191
|
if (message.display) {
|
|
1236
|
-
const renderer = this.session.
|
|
1237
|
-
this.chatContainer.addChild(new
|
|
1192
|
+
const renderer = this.session.extensionRunner?.getMessageRenderer(message.customType);
|
|
1193
|
+
this.chatContainer.addChild(new CustomMessageComponent(message, renderer));
|
|
1238
1194
|
}
|
|
1239
1195
|
break;
|
|
1240
1196
|
}
|
|
@@ -1296,7 +1252,7 @@ export class InteractiveMode {
|
|
|
1296
1252
|
// Render tool call components
|
|
1297
1253
|
for (const content of message.content) {
|
|
1298
1254
|
if (content.type === "toolCall") {
|
|
1299
|
-
const component = new ToolExecutionComponent(content.name, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.
|
|
1255
|
+
const component = new ToolExecutionComponent(content.name, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
|
|
1300
1256
|
component.setExpanded(this.toolOutputExpanded);
|
|
1301
1257
|
this.chatContainer.addChild(component);
|
|
1302
1258
|
if (message.stopReason === "aborted" || message.stopReason === "error") {
|
|
@@ -1372,18 +1328,16 @@ export class InteractiveMode {
|
|
|
1372
1328
|
}
|
|
1373
1329
|
/**
|
|
1374
1330
|
* Gracefully shutdown the agent.
|
|
1375
|
-
* Emits shutdown event to
|
|
1331
|
+
* Emits shutdown event to extensions, then exits.
|
|
1376
1332
|
*/
|
|
1377
1333
|
async shutdown() {
|
|
1378
|
-
// Emit shutdown event to
|
|
1379
|
-
const
|
|
1380
|
-
if (
|
|
1381
|
-
await
|
|
1334
|
+
// Emit shutdown event to extensions
|
|
1335
|
+
const extensionRunner = this.session.extensionRunner;
|
|
1336
|
+
if (extensionRunner?.hasHandlers("session_shutdown")) {
|
|
1337
|
+
await extensionRunner.emit({
|
|
1382
1338
|
type: "session_shutdown",
|
|
1383
1339
|
});
|
|
1384
1340
|
}
|
|
1385
|
-
// Emit shutdown event to custom tools
|
|
1386
|
-
await this.session.emitCustomToolSessionEvent("shutdown");
|
|
1387
1341
|
this.stop();
|
|
1388
1342
|
process.exit(0);
|
|
1389
1343
|
}
|
|
@@ -1403,7 +1357,7 @@ export class InteractiveMode {
|
|
|
1403
1357
|
if (!text)
|
|
1404
1358
|
return;
|
|
1405
1359
|
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
1406
|
-
// This handles
|
|
1360
|
+
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
1407
1361
|
if (this.session.isStreaming) {
|
|
1408
1362
|
this.editor.addToHistory(text);
|
|
1409
1363
|
this.editor.setText("");
|
|
@@ -1701,7 +1655,7 @@ export class InteractiveMode {
|
|
|
1701
1655
|
const selector = new UserMessageSelectorComponent(userMessages.map((m) => ({ id: m.entryId, text: m.text })), async (entryId) => {
|
|
1702
1656
|
const result = await this.session.branch(entryId);
|
|
1703
1657
|
if (result.cancelled) {
|
|
1704
|
-
//
|
|
1658
|
+
// Extension cancelled the branch
|
|
1705
1659
|
done();
|
|
1706
1660
|
this.ui.requestRender();
|
|
1707
1661
|
return;
|
|
@@ -1745,7 +1699,7 @@ export class InteractiveMode {
|
|
|
1745
1699
|
}
|
|
1746
1700
|
// Ask about summarization
|
|
1747
1701
|
done(); // Close selector first
|
|
1748
|
-
const wantsSummary = await this.
|
|
1702
|
+
const wantsSummary = await this.showExtensionConfirm("Summarize branch?", "Create a summary of the branch you're leaving?");
|
|
1749
1703
|
// Set up escape handler and loader if summarizing
|
|
1750
1704
|
let summaryLoader;
|
|
1751
1705
|
const originalOnEscape = this.editor.onEscape;
|
|
@@ -1825,7 +1779,7 @@ export class InteractiveMode {
|
|
|
1825
1779
|
this.streamingComponent = undefined;
|
|
1826
1780
|
this.streamingMessage = undefined;
|
|
1827
1781
|
this.pendingTools.clear();
|
|
1828
|
-
// Switch session via AgentSession (emits
|
|
1782
|
+
// Switch session via AgentSession (emits extension session events)
|
|
1829
1783
|
await this.session.switchSession(sessionPath);
|
|
1830
1784
|
// Clear and re-render the chat
|
|
1831
1785
|
this.chatContainer.clear();
|
|
@@ -2176,18 +2130,18 @@ export class InteractiveMode {
|
|
|
2176
2130
|
| \`/\` | Slash commands |
|
|
2177
2131
|
| \`!\` | Run bash command |
|
|
2178
2132
|
`;
|
|
2179
|
-
// Add
|
|
2180
|
-
const
|
|
2181
|
-
if (
|
|
2182
|
-
const shortcuts =
|
|
2133
|
+
// Add extension-registered shortcuts
|
|
2134
|
+
const extensionRunner = this.session.extensionRunner;
|
|
2135
|
+
if (extensionRunner) {
|
|
2136
|
+
const shortcuts = extensionRunner.getShortcuts();
|
|
2183
2137
|
if (shortcuts.size > 0) {
|
|
2184
2138
|
hotkeys += `
|
|
2185
|
-
**
|
|
2139
|
+
**Extensions**
|
|
2186
2140
|
| Key | Action |
|
|
2187
2141
|
|-----|--------|
|
|
2188
2142
|
`;
|
|
2189
2143
|
for (const [key, shortcut] of shortcuts) {
|
|
2190
|
-
const description = shortcut.description ?? shortcut.
|
|
2144
|
+
const description = shortcut.description ?? shortcut.extensionPath;
|
|
2191
2145
|
hotkeys += `| \`${key}\` | ${description} |\n`;
|
|
2192
2146
|
}
|
|
2193
2147
|
}
|
|
@@ -2207,7 +2161,7 @@ export class InteractiveMode {
|
|
|
2207
2161
|
this.loadingAnimation = undefined;
|
|
2208
2162
|
}
|
|
2209
2163
|
this.statusContainer.clear();
|
|
2210
|
-
// New session via session (emits
|
|
2164
|
+
// New session via session (emits extension session events)
|
|
2211
2165
|
await this.session.newSession();
|
|
2212
2166
|
// Clear UI state
|
|
2213
2167
|
this.chatContainer.clear();
|