@mariozechner/pi-coding-agent 0.34.2 → 0.36.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 +210 -0
- package/README.md +246 -107
- 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/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -1
- package/dist/core/auth-storage.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/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.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 +36 -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
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Extension runner - executes extensions and manages their lifecycle.
|
|
3
3
|
*/
|
|
4
4
|
import { theme } from "../../modes/interactive/theme/theme.js";
|
|
5
|
-
// Re-export execCommand for backward compatibility
|
|
6
|
-
export { execCommand } from "../exec.js";
|
|
7
|
-
/** No-op UI context used when no UI is available */
|
|
8
5
|
const noOpUIContext = {
|
|
9
6
|
select: async () => undefined,
|
|
10
7
|
confirm: async () => false,
|
|
@@ -21,11 +18,8 @@ const noOpUIContext = {
|
|
|
21
18
|
return theme;
|
|
22
19
|
},
|
|
23
20
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
export class HookRunner {
|
|
28
|
-
hooks;
|
|
21
|
+
export class ExtensionRunner {
|
|
22
|
+
extensions;
|
|
29
23
|
uiContext;
|
|
30
24
|
hasUI;
|
|
31
25
|
cwd;
|
|
@@ -40,25 +34,20 @@ export class HookRunner {
|
|
|
40
34
|
newSessionHandler = async () => ({ cancelled: false });
|
|
41
35
|
branchHandler = async () => ({ cancelled: false });
|
|
42
36
|
navigateTreeHandler = async () => ({ cancelled: false });
|
|
43
|
-
constructor(
|
|
44
|
-
this.
|
|
37
|
+
constructor(extensions, cwd, sessionManager, modelRegistry) {
|
|
38
|
+
this.extensions = extensions;
|
|
45
39
|
this.uiContext = noOpUIContext;
|
|
46
40
|
this.hasUI = false;
|
|
47
41
|
this.cwd = cwd;
|
|
48
42
|
this.sessionManager = sessionManager;
|
|
49
43
|
this.modelRegistry = modelRegistry;
|
|
50
44
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Initialize HookRunner with all required context.
|
|
53
|
-
* Modes call this once the agent session is fully set up.
|
|
54
|
-
*/
|
|
55
45
|
initialize(options) {
|
|
56
46
|
this.getModel = options.getModel;
|
|
57
47
|
this.isIdleFn = options.isIdle ?? (() => true);
|
|
58
48
|
this.waitForIdleFn = options.waitForIdle ?? (async () => { });
|
|
59
49
|
this.abortFn = options.abort ?? (() => { });
|
|
60
50
|
this.hasPendingMessagesFn = options.hasPendingMessages ?? (() => false);
|
|
61
|
-
// Store session handlers for HookCommandContext
|
|
62
51
|
if (options.newSessionHandler) {
|
|
63
52
|
this.newSessionHandler = options.newSessionHandler;
|
|
64
53
|
}
|
|
@@ -68,58 +57,51 @@ export class HookRunner {
|
|
|
68
57
|
if (options.navigateTreeHandler) {
|
|
69
58
|
this.navigateTreeHandler = options.navigateTreeHandler;
|
|
70
59
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
hook.setSetActiveToolsHandler(options.setActiveToolsHandler);
|
|
60
|
+
for (const ext of this.extensions) {
|
|
61
|
+
ext.setSendMessageHandler(options.sendMessageHandler);
|
|
62
|
+
ext.setAppendEntryHandler(options.appendEntryHandler);
|
|
63
|
+
ext.setGetActiveToolsHandler(options.getActiveToolsHandler);
|
|
64
|
+
ext.setGetAllToolsHandler(options.getAllToolsHandler);
|
|
65
|
+
ext.setSetActiveToolsHandler(options.setActiveToolsHandler);
|
|
78
66
|
}
|
|
79
67
|
this.uiContext = options.uiContext ?? noOpUIContext;
|
|
80
68
|
this.hasUI = options.hasUI ?? false;
|
|
81
69
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Get the UI context (set by mode).
|
|
84
|
-
*/
|
|
85
70
|
getUIContext() {
|
|
86
71
|
return this.uiContext;
|
|
87
72
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Get whether UI is available.
|
|
90
|
-
*/
|
|
91
73
|
getHasUI() {
|
|
92
74
|
return this.hasUI;
|
|
93
75
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
getExtensionPaths() {
|
|
77
|
+
return this.extensions.map((e) => e.path);
|
|
78
|
+
}
|
|
79
|
+
/** Get all registered tools from all extensions. */
|
|
80
|
+
getAllRegisteredTools() {
|
|
81
|
+
const tools = [];
|
|
82
|
+
for (const ext of this.extensions) {
|
|
83
|
+
for (const tool of ext.tools.values()) {
|
|
84
|
+
tools.push(tool);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return tools;
|
|
99
88
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Get all CLI flags registered by hooks.
|
|
102
|
-
*/
|
|
103
89
|
getFlags() {
|
|
104
90
|
const allFlags = new Map();
|
|
105
|
-
for (const
|
|
106
|
-
for (const [name, flag] of
|
|
91
|
+
for (const ext of this.extensions) {
|
|
92
|
+
for (const [name, flag] of ext.flags) {
|
|
107
93
|
allFlags.set(name, flag);
|
|
108
94
|
}
|
|
109
95
|
}
|
|
110
96
|
return allFlags;
|
|
111
97
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Set a flag value (after CLI parsing).
|
|
114
|
-
*/
|
|
115
98
|
setFlagValue(name, value) {
|
|
116
|
-
for (const
|
|
117
|
-
if (
|
|
118
|
-
|
|
99
|
+
for (const ext of this.extensions) {
|
|
100
|
+
if (ext.flags.has(name)) {
|
|
101
|
+
ext.setFlagValue(name, value);
|
|
119
102
|
}
|
|
120
103
|
}
|
|
121
104
|
}
|
|
122
|
-
// Built-in shortcuts that hooks should not override
|
|
123
105
|
static RESERVED_SHORTCUTS = new Set([
|
|
124
106
|
"ctrl+c",
|
|
125
107
|
"ctrl+d",
|
|
@@ -136,105 +118,69 @@ export class HookRunner {
|
|
|
136
118
|
"escape",
|
|
137
119
|
"enter",
|
|
138
120
|
]);
|
|
139
|
-
/**
|
|
140
|
-
* Get all keyboard shortcuts registered by hooks.
|
|
141
|
-
* When multiple hooks register the same shortcut, the last one wins.
|
|
142
|
-
* Conflicts with built-in shortcuts are skipped with a warning.
|
|
143
|
-
* Conflicts between hooks are logged as warnings.
|
|
144
|
-
*/
|
|
145
121
|
getShortcuts() {
|
|
146
122
|
const allShortcuts = new Map();
|
|
147
|
-
for (const
|
|
148
|
-
for (const [key, shortcut] of
|
|
149
|
-
// Normalize to lowercase for comparison (KeyId is string at runtime)
|
|
123
|
+
for (const ext of this.extensions) {
|
|
124
|
+
for (const [key, shortcut] of ext.shortcuts) {
|
|
150
125
|
const normalizedKey = key.toLowerCase();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
console.warn(`Hook shortcut '${key}' from ${shortcut.hookPath} conflicts with built-in shortcut. Skipping.`);
|
|
126
|
+
if (ExtensionRunner.RESERVED_SHORTCUTS.has(normalizedKey)) {
|
|
127
|
+
console.warn(`Extension shortcut '${key}' from ${shortcut.extensionPath} conflicts with built-in shortcut. Skipping.`);
|
|
154
128
|
continue;
|
|
155
129
|
}
|
|
156
130
|
const existing = allShortcuts.get(normalizedKey);
|
|
157
131
|
if (existing) {
|
|
158
|
-
|
|
159
|
-
console.warn(`Hook shortcut conflict: '${key}' registered by both ${existing.hookPath} and ${shortcut.hookPath}. Using ${shortcut.hookPath}.`);
|
|
132
|
+
console.warn(`Extension shortcut conflict: '${key}' registered by both ${existing.extensionPath} and ${shortcut.extensionPath}. Using ${shortcut.extensionPath}.`);
|
|
160
133
|
}
|
|
161
134
|
allShortcuts.set(normalizedKey, shortcut);
|
|
162
135
|
}
|
|
163
136
|
}
|
|
164
137
|
return allShortcuts;
|
|
165
138
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Subscribe to hook errors.
|
|
168
|
-
* @returns Unsubscribe function
|
|
169
|
-
*/
|
|
170
139
|
onError(listener) {
|
|
171
140
|
this.errorListeners.add(listener);
|
|
172
141
|
return () => this.errorListeners.delete(listener);
|
|
173
142
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Emit an error to all listeners.
|
|
176
|
-
*/
|
|
177
|
-
/**
|
|
178
|
-
* Emit an error to all error listeners.
|
|
179
|
-
*/
|
|
180
143
|
emitError(error) {
|
|
181
144
|
for (const listener of this.errorListeners) {
|
|
182
145
|
listener(error);
|
|
183
146
|
}
|
|
184
147
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Check if any hooks have handlers for the given event type.
|
|
187
|
-
*/
|
|
188
148
|
hasHandlers(eventType) {
|
|
189
|
-
for (const
|
|
190
|
-
const handlers =
|
|
149
|
+
for (const ext of this.extensions) {
|
|
150
|
+
const handlers = ext.handlers.get(eventType);
|
|
191
151
|
if (handlers && handlers.length > 0) {
|
|
192
152
|
return true;
|
|
193
153
|
}
|
|
194
154
|
}
|
|
195
155
|
return false;
|
|
196
156
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Get a message renderer for the given customType.
|
|
199
|
-
* Returns the first renderer found across all hooks, or undefined if none.
|
|
200
|
-
*/
|
|
201
157
|
getMessageRenderer(customType) {
|
|
202
|
-
for (const
|
|
203
|
-
const renderer =
|
|
158
|
+
for (const ext of this.extensions) {
|
|
159
|
+
const renderer = ext.messageRenderers.get(customType);
|
|
204
160
|
if (renderer) {
|
|
205
161
|
return renderer;
|
|
206
162
|
}
|
|
207
163
|
}
|
|
208
164
|
return undefined;
|
|
209
165
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Get all registered commands from all hooks.
|
|
212
|
-
*/
|
|
213
166
|
getRegisteredCommands() {
|
|
214
167
|
const commands = [];
|
|
215
|
-
for (const
|
|
216
|
-
for (const command of
|
|
168
|
+
for (const ext of this.extensions) {
|
|
169
|
+
for (const command of ext.commands.values()) {
|
|
217
170
|
commands.push(command);
|
|
218
171
|
}
|
|
219
172
|
}
|
|
220
173
|
return commands;
|
|
221
174
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Get a registered command by name.
|
|
224
|
-
* Returns the first command found across all hooks, or undefined if none.
|
|
225
|
-
*/
|
|
226
175
|
getCommand(name) {
|
|
227
|
-
for (const
|
|
228
|
-
const command =
|
|
176
|
+
for (const ext of this.extensions) {
|
|
177
|
+
const command = ext.commands.get(name);
|
|
229
178
|
if (command) {
|
|
230
179
|
return command;
|
|
231
180
|
}
|
|
232
181
|
}
|
|
233
182
|
return undefined;
|
|
234
183
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Create the event context for handlers.
|
|
237
|
-
*/
|
|
238
184
|
createContext() {
|
|
239
185
|
return {
|
|
240
186
|
ui: this.uiContext,
|
|
@@ -248,10 +194,6 @@ export class HookRunner {
|
|
|
248
194
|
hasPendingMessages: () => this.hasPendingMessagesFn(),
|
|
249
195
|
};
|
|
250
196
|
}
|
|
251
|
-
/**
|
|
252
|
-
* Create the command context for slash command handlers.
|
|
253
|
-
* Extends HookContext with session control methods that are only safe in commands.
|
|
254
|
-
*/
|
|
255
197
|
createCommandContext() {
|
|
256
198
|
return {
|
|
257
199
|
...this.createContext(),
|
|
@@ -261,38 +203,28 @@ export class HookRunner {
|
|
|
261
203
|
navigateTree: (targetId, options) => this.navigateTreeHandler(targetId, options),
|
|
262
204
|
};
|
|
263
205
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Check if event type is a session "before_*" event that can be cancelled.
|
|
266
|
-
*/
|
|
267
206
|
isSessionBeforeEvent(type) {
|
|
268
207
|
return (type === "session_before_switch" ||
|
|
269
208
|
type === "session_before_branch" ||
|
|
270
209
|
type === "session_before_compact" ||
|
|
271
210
|
type === "session_before_tree");
|
|
272
211
|
}
|
|
273
|
-
/**
|
|
274
|
-
* Emit an event to all hooks.
|
|
275
|
-
* Returns the result from session before_* / tool_result events (if any handler returns one).
|
|
276
|
-
*/
|
|
277
212
|
async emit(event) {
|
|
278
213
|
const ctx = this.createContext();
|
|
279
214
|
let result;
|
|
280
|
-
for (const
|
|
281
|
-
const handlers =
|
|
215
|
+
for (const ext of this.extensions) {
|
|
216
|
+
const handlers = ext.handlers.get(event.type);
|
|
282
217
|
if (!handlers || handlers.length === 0)
|
|
283
218
|
continue;
|
|
284
219
|
for (const handler of handlers) {
|
|
285
220
|
try {
|
|
286
221
|
const handlerResult = await handler(event, ctx);
|
|
287
|
-
// For session before_* events, capture the result (for cancellation)
|
|
288
222
|
if (this.isSessionBeforeEvent(event.type) && handlerResult) {
|
|
289
223
|
result = handlerResult;
|
|
290
|
-
// If cancelled, stop processing further hooks
|
|
291
224
|
if (result.cancel) {
|
|
292
225
|
return result;
|
|
293
226
|
}
|
|
294
227
|
}
|
|
295
|
-
// For tool_result events, capture the result
|
|
296
228
|
if (event.type === "tool_result" && handlerResult) {
|
|
297
229
|
result = handlerResult;
|
|
298
230
|
}
|
|
@@ -301,7 +233,7 @@ export class HookRunner {
|
|
|
301
233
|
const message = err instanceof Error ? err.message : String(err);
|
|
302
234
|
const stack = err instanceof Error ? err.stack : undefined;
|
|
303
235
|
this.emitError({
|
|
304
|
-
|
|
236
|
+
extensionPath: ext.path,
|
|
305
237
|
event: event.type,
|
|
306
238
|
error: message,
|
|
307
239
|
stack,
|
|
@@ -311,24 +243,17 @@ export class HookRunner {
|
|
|
311
243
|
}
|
|
312
244
|
return result;
|
|
313
245
|
}
|
|
314
|
-
/**
|
|
315
|
-
* Emit a tool_call event to all hooks.
|
|
316
|
-
* No timeout - user prompts can take as long as needed.
|
|
317
|
-
* Errors are thrown (not swallowed) so caller can block on failure.
|
|
318
|
-
*/
|
|
319
246
|
async emitToolCall(event) {
|
|
320
247
|
const ctx = this.createContext();
|
|
321
248
|
let result;
|
|
322
|
-
for (const
|
|
323
|
-
const handlers =
|
|
249
|
+
for (const ext of this.extensions) {
|
|
250
|
+
const handlers = ext.handlers.get("tool_call");
|
|
324
251
|
if (!handlers || handlers.length === 0)
|
|
325
252
|
continue;
|
|
326
253
|
for (const handler of handlers) {
|
|
327
|
-
// No timeout - let user take their time
|
|
328
254
|
const handlerResult = await handler(event, ctx);
|
|
329
255
|
if (handlerResult) {
|
|
330
256
|
result = handlerResult;
|
|
331
|
-
// If blocked, stop processing further hooks
|
|
332
257
|
if (result.block) {
|
|
333
258
|
return result;
|
|
334
259
|
}
|
|
@@ -337,18 +262,11 @@ export class HookRunner {
|
|
|
337
262
|
}
|
|
338
263
|
return result;
|
|
339
264
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Emit a context event to all hooks.
|
|
342
|
-
* Handlers are chained - each gets the previous handler's output (if any).
|
|
343
|
-
* Returns the final modified messages, or the original if no modifications.
|
|
344
|
-
*
|
|
345
|
-
* Messages are deep-copied before passing to hooks, so mutations are safe.
|
|
346
|
-
*/
|
|
347
265
|
async emitContext(messages) {
|
|
348
266
|
const ctx = this.createContext();
|
|
349
267
|
let currentMessages = structuredClone(messages);
|
|
350
|
-
for (const
|
|
351
|
-
const handlers =
|
|
268
|
+
for (const ext of this.extensions) {
|
|
269
|
+
const handlers = ext.handlers.get("context");
|
|
352
270
|
if (!handlers || handlers.length === 0)
|
|
353
271
|
continue;
|
|
354
272
|
for (const handler of handlers) {
|
|
@@ -363,7 +281,7 @@ export class HookRunner {
|
|
|
363
281
|
const message = err instanceof Error ? err.message : String(err);
|
|
364
282
|
const stack = err instanceof Error ? err.stack : undefined;
|
|
365
283
|
this.emitError({
|
|
366
|
-
|
|
284
|
+
extensionPath: ext.path,
|
|
367
285
|
event: "context",
|
|
368
286
|
error: message,
|
|
369
287
|
stack,
|
|
@@ -373,16 +291,12 @@ export class HookRunner {
|
|
|
373
291
|
}
|
|
374
292
|
return currentMessages;
|
|
375
293
|
}
|
|
376
|
-
/**
|
|
377
|
-
* Emit before_agent_start event to all hooks.
|
|
378
|
-
* Returns combined result: all messages and all systemPromptAppend strings concatenated.
|
|
379
|
-
*/
|
|
380
294
|
async emitBeforeAgentStart(prompt, images) {
|
|
381
295
|
const ctx = this.createContext();
|
|
382
296
|
const messages = [];
|
|
383
297
|
const systemPromptAppends = [];
|
|
384
|
-
for (const
|
|
385
|
-
const handlers =
|
|
298
|
+
for (const ext of this.extensions) {
|
|
299
|
+
const handlers = ext.handlers.get("before_agent_start");
|
|
386
300
|
if (!handlers || handlers.length === 0)
|
|
387
301
|
continue;
|
|
388
302
|
for (const handler of handlers) {
|
|
@@ -391,11 +305,9 @@ export class HookRunner {
|
|
|
391
305
|
const handlerResult = await handler(event, ctx);
|
|
392
306
|
if (handlerResult) {
|
|
393
307
|
const result = handlerResult;
|
|
394
|
-
// Collect all messages
|
|
395
308
|
if (result.message) {
|
|
396
309
|
messages.push(result.message);
|
|
397
310
|
}
|
|
398
|
-
// Collect all systemPromptAppend strings
|
|
399
311
|
if (result.systemPromptAppend) {
|
|
400
312
|
systemPromptAppends.push(result.systemPromptAppend);
|
|
401
313
|
}
|
|
@@ -405,7 +317,7 @@ export class HookRunner {
|
|
|
405
317
|
const message = err instanceof Error ? err.message : String(err);
|
|
406
318
|
const stack = err instanceof Error ? err.stack : undefined;
|
|
407
319
|
this.emitError({
|
|
408
|
-
|
|
320
|
+
extensionPath: ext.path,
|
|
409
321
|
event: "before_agent_start",
|
|
410
322
|
error: message,
|
|
411
323
|
stack,
|
|
@@ -413,7 +325,6 @@ export class HookRunner {
|
|
|
413
325
|
}
|
|
414
326
|
}
|
|
415
327
|
}
|
|
416
|
-
// Return combined result
|
|
417
328
|
if (messages.length > 0 || systemPromptAppends.length > 0) {
|
|
418
329
|
return {
|
|
419
330
|
messages: messages.length > 0 ? messages : undefined,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/core/extensions/runner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extension runner - executes extensions and manages their lifecycle.\n */\n\nimport type { AgentMessage } from \"@mariozechner/pi-agent-core\";\nimport type { ImageContent, Model } from \"@mariozechner/pi-ai\";\nimport type { KeyId } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ModelRegistry } from \"../model-registry.js\";\nimport type { SessionManager } from \"../session-manager.js\";\nimport type {\n\tAppendEntryHandler,\n\tBeforeAgentStartEvent,\n\tBeforeAgentStartEventResult,\n\tContextEvent,\n\tContextEventResult,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFlag,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tGetActiveToolsHandler,\n\tGetAllToolsHandler,\n\tLoadedExtension,\n\tMessageRenderer,\n\tRegisteredCommand,\n\tRegisteredTool,\n\tSendMessageHandler,\n\tSessionBeforeCompactResult,\n\tSessionBeforeTreeResult,\n\tSetActiveToolsHandler,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEventResult,\n} from \"./types.js\";\n\n/** Combined result from all before_agent_start handlers */\ninterface BeforeAgentStartCombinedResult {\n\tmessages?: NonNullable<BeforeAgentStartEventResult[\"message\"]>[];\n\tsystemPromptAppend?: string;\n}\n\nexport type ExtensionErrorListener = (error: ExtensionError) => void;\n\nexport type NewSessionHandler = (options?: {\n\tparentSession?: string;\n\tsetup?: (sessionManager: SessionManager) => Promise<void>;\n}) => Promise<{ cancelled: boolean }>;\n\nexport type BranchHandler = (entryId: string) => Promise<{ cancelled: boolean }>;\n\nexport type NavigateTreeHandler = (\n\ttargetId: string,\n\toptions?: { summarize?: boolean },\n) => Promise<{ cancelled: boolean }>;\n\nconst noOpUIContext: ExtensionUIContext = {\n\tselect: async () => undefined,\n\tconfirm: async () => false,\n\tinput: async () => undefined,\n\tnotify: () => {},\n\tsetStatus: () => {},\n\tsetWidget: () => {},\n\tsetTitle: () => {},\n\tcustom: async () => undefined as never,\n\tsetEditorText: () => {},\n\tgetEditorText: () => \"\",\n\teditor: async () => undefined,\n\tget theme() {\n\t\treturn theme;\n\t},\n};\n\nexport class ExtensionRunner {\n\tprivate extensions: LoadedExtension[];\n\tprivate uiContext: ExtensionUIContext;\n\tprivate hasUI: boolean;\n\tprivate cwd: string;\n\tprivate sessionManager: SessionManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate errorListeners: Set<ExtensionErrorListener> = new Set();\n\tprivate getModel: () => Model<any> | undefined = () => undefined;\n\tprivate isIdleFn: () => boolean = () => true;\n\tprivate waitForIdleFn: () => Promise<void> = async () => {};\n\tprivate abortFn: () => void = () => {};\n\tprivate hasPendingMessagesFn: () => boolean = () => false;\n\tprivate newSessionHandler: NewSessionHandler = async () => ({ cancelled: false });\n\tprivate branchHandler: BranchHandler = async () => ({ cancelled: false });\n\tprivate navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false });\n\n\tconstructor(\n\t\textensions: LoadedExtension[],\n\t\tcwd: string,\n\t\tsessionManager: SessionManager,\n\t\tmodelRegistry: ModelRegistry,\n\t) {\n\t\tthis.extensions = extensions;\n\t\tthis.uiContext = noOpUIContext;\n\t\tthis.hasUI = false;\n\t\tthis.cwd = cwd;\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t}\n\n\tinitialize(options: {\n\t\tgetModel: () => Model<any> | undefined;\n\t\tsendMessageHandler: SendMessageHandler;\n\t\tappendEntryHandler: AppendEntryHandler;\n\t\tgetActiveToolsHandler: GetActiveToolsHandler;\n\t\tgetAllToolsHandler: GetAllToolsHandler;\n\t\tsetActiveToolsHandler: SetActiveToolsHandler;\n\t\tnewSessionHandler?: NewSessionHandler;\n\t\tbranchHandler?: BranchHandler;\n\t\tnavigateTreeHandler?: NavigateTreeHandler;\n\t\tisIdle?: () => boolean;\n\t\twaitForIdle?: () => Promise<void>;\n\t\tabort?: () => void;\n\t\thasPendingMessages?: () => boolean;\n\t\tuiContext?: ExtensionUIContext;\n\t\thasUI?: boolean;\n\t}): void {\n\t\tthis.getModel = options.getModel;\n\t\tthis.isIdleFn = options.isIdle ?? (() => true);\n\t\tthis.waitForIdleFn = options.waitForIdle ?? (async () => {});\n\t\tthis.abortFn = options.abort ?? (() => {});\n\t\tthis.hasPendingMessagesFn = options.hasPendingMessages ?? (() => false);\n\n\t\tif (options.newSessionHandler) {\n\t\t\tthis.newSessionHandler = options.newSessionHandler;\n\t\t}\n\t\tif (options.branchHandler) {\n\t\t\tthis.branchHandler = options.branchHandler;\n\t\t}\n\t\tif (options.navigateTreeHandler) {\n\t\t\tthis.navigateTreeHandler = options.navigateTreeHandler;\n\t\t}\n\n\t\tfor (const ext of this.extensions) {\n\t\t\text.setSendMessageHandler(options.sendMessageHandler);\n\t\t\text.setAppendEntryHandler(options.appendEntryHandler);\n\t\t\text.setGetActiveToolsHandler(options.getActiveToolsHandler);\n\t\t\text.setGetAllToolsHandler(options.getAllToolsHandler);\n\t\t\text.setSetActiveToolsHandler(options.setActiveToolsHandler);\n\t\t}\n\n\t\tthis.uiContext = options.uiContext ?? noOpUIContext;\n\t\tthis.hasUI = options.hasUI ?? false;\n\t}\n\n\tgetUIContext(): ExtensionUIContext | null {\n\t\treturn this.uiContext;\n\t}\n\n\tgetHasUI(): boolean {\n\t\treturn this.hasUI;\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn this.extensions.map((e) => e.path);\n\t}\n\n\t/** Get all registered tools from all extensions. */\n\tgetAllRegisteredTools(): RegisteredTool[] {\n\t\tconst tools: RegisteredTool[] = [];\n\t\tfor (const ext of this.extensions) {\n\t\t\tfor (const tool of ext.tools.values()) {\n\t\t\t\ttools.push(tool);\n\t\t\t}\n\t\t}\n\t\treturn tools;\n\t}\n\n\tgetFlags(): Map<string, ExtensionFlag> {\n\t\tconst allFlags = new Map<string, ExtensionFlag>();\n\t\tfor (const ext of this.extensions) {\n\t\t\tfor (const [name, flag] of ext.flags) {\n\t\t\t\tallFlags.set(name, flag);\n\t\t\t}\n\t\t}\n\t\treturn allFlags;\n\t}\n\n\tsetFlagValue(name: string, value: boolean | string): void {\n\t\tfor (const ext of this.extensions) {\n\t\t\tif (ext.flags.has(name)) {\n\t\t\t\text.setFlagValue(name, value);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static readonly RESERVED_SHORTCUTS = new Set([\n\t\t\"ctrl+c\",\n\t\t\"ctrl+d\",\n\t\t\"ctrl+z\",\n\t\t\"ctrl+k\",\n\t\t\"ctrl+p\",\n\t\t\"ctrl+l\",\n\t\t\"ctrl+o\",\n\t\t\"ctrl+t\",\n\t\t\"ctrl+g\",\n\t\t\"shift+tab\",\n\t\t\"shift+ctrl+p\",\n\t\t\"alt+enter\",\n\t\t\"escape\",\n\t\t\"enter\",\n\t]);\n\n\tgetShortcuts(): Map<KeyId, ExtensionShortcut> {\n\t\tconst allShortcuts = new Map<KeyId, ExtensionShortcut>();\n\t\tfor (const ext of this.extensions) {\n\t\t\tfor (const [key, shortcut] of ext.shortcuts) {\n\t\t\t\tconst normalizedKey = key.toLowerCase() as KeyId;\n\n\t\t\t\tif (ExtensionRunner.RESERVED_SHORTCUTS.has(normalizedKey)) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Extension shortcut '${key}' from ${shortcut.extensionPath} conflicts with built-in shortcut. Skipping.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst existing = allShortcuts.get(normalizedKey);\n\t\t\t\tif (existing) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Extension shortcut conflict: '${key}' registered by both ${existing.extensionPath} and ${shortcut.extensionPath}. Using ${shortcut.extensionPath}.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tallShortcuts.set(normalizedKey, shortcut);\n\t\t\t}\n\t\t}\n\t\treturn allShortcuts;\n\t}\n\n\tonError(listener: ExtensionErrorListener): () => void {\n\t\tthis.errorListeners.add(listener);\n\t\treturn () => this.errorListeners.delete(listener);\n\t}\n\n\temitError(error: ExtensionError): void {\n\t\tfor (const listener of this.errorListeners) {\n\t\t\tlistener(error);\n\t\t}\n\t}\n\n\thasHandlers(eventType: string): boolean {\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst handlers = ext.handlers.get(eventType);\n\t\t\tif (handlers && handlers.length > 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tgetMessageRenderer(customType: string): MessageRenderer | undefined {\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst renderer = ext.messageRenderers.get(customType);\n\t\t\tif (renderer) {\n\t\t\t\treturn renderer;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tgetRegisteredCommands(): RegisteredCommand[] {\n\t\tconst commands: RegisteredCommand[] = [];\n\t\tfor (const ext of this.extensions) {\n\t\t\tfor (const command of ext.commands.values()) {\n\t\t\t\tcommands.push(command);\n\t\t\t}\n\t\t}\n\t\treturn commands;\n\t}\n\n\tgetCommand(name: string): RegisteredCommand | undefined {\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst command = ext.commands.get(name);\n\t\t\tif (command) {\n\t\t\t\treturn command;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate createContext(): ExtensionContext {\n\t\treturn {\n\t\t\tui: this.uiContext,\n\t\t\thasUI: this.hasUI,\n\t\t\tcwd: this.cwd,\n\t\t\tsessionManager: this.sessionManager,\n\t\t\tmodelRegistry: this.modelRegistry,\n\t\t\tmodel: this.getModel(),\n\t\t\tisIdle: () => this.isIdleFn(),\n\t\t\tabort: () => this.abortFn(),\n\t\t\thasPendingMessages: () => this.hasPendingMessagesFn(),\n\t\t};\n\t}\n\n\tcreateCommandContext(): ExtensionCommandContext {\n\t\treturn {\n\t\t\t...this.createContext(),\n\t\t\twaitForIdle: () => this.waitForIdleFn(),\n\t\t\tnewSession: (options) => this.newSessionHandler(options),\n\t\t\tbranch: (entryId) => this.branchHandler(entryId),\n\t\t\tnavigateTree: (targetId, options) => this.navigateTreeHandler(targetId, options),\n\t\t};\n\t}\n\n\tprivate isSessionBeforeEvent(\n\t\ttype: string,\n\t): type is \"session_before_switch\" | \"session_before_branch\" | \"session_before_compact\" | \"session_before_tree\" {\n\t\treturn (\n\t\t\ttype === \"session_before_switch\" ||\n\t\t\ttype === \"session_before_branch\" ||\n\t\t\ttype === \"session_before_compact\" ||\n\t\t\ttype === \"session_before_tree\"\n\t\t);\n\t}\n\n\tasync emit(\n\t\tevent: ExtensionEvent,\n\t): Promise<SessionBeforeCompactResult | SessionBeforeTreeResult | ToolResultEventResult | undefined> {\n\t\tconst ctx = this.createContext();\n\t\tlet result: SessionBeforeCompactResult | SessionBeforeTreeResult | ToolResultEventResult | undefined;\n\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst handlers = ext.handlers.get(event.type);\n\t\t\tif (!handlers || handlers.length === 0) continue;\n\n\t\t\tfor (const handler of handlers) {\n\t\t\t\ttry {\n\t\t\t\t\tconst handlerResult = await handler(event, ctx);\n\n\t\t\t\t\tif (this.isSessionBeforeEvent(event.type) && handlerResult) {\n\t\t\t\t\t\tresult = handlerResult as SessionBeforeCompactResult | SessionBeforeTreeResult;\n\t\t\t\t\t\tif (result.cancel) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (event.type === \"tool_result\" && handlerResult) {\n\t\t\t\t\t\tresult = handlerResult as ToolResultEventResult;\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst stack = err instanceof Error ? err.stack : undefined;\n\t\t\t\t\tthis.emitError({\n\t\t\t\t\t\textensionPath: ext.path,\n\t\t\t\t\t\tevent: event.type,\n\t\t\t\t\t\terror: message,\n\t\t\t\t\t\tstack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync emitToolCall(event: ToolCallEvent): Promise<ToolCallEventResult | undefined> {\n\t\tconst ctx = this.createContext();\n\t\tlet result: ToolCallEventResult | undefined;\n\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst handlers = ext.handlers.get(\"tool_call\");\n\t\t\tif (!handlers || handlers.length === 0) continue;\n\n\t\t\tfor (const handler of handlers) {\n\t\t\t\tconst handlerResult = await handler(event, ctx);\n\n\t\t\t\tif (handlerResult) {\n\t\t\t\t\tresult = handlerResult as ToolCallEventResult;\n\t\t\t\t\tif (result.block) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync emitContext(messages: AgentMessage[]): Promise<AgentMessage[]> {\n\t\tconst ctx = this.createContext();\n\t\tlet currentMessages = structuredClone(messages);\n\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst handlers = ext.handlers.get(\"context\");\n\t\t\tif (!handlers || handlers.length === 0) continue;\n\n\t\t\tfor (const handler of handlers) {\n\t\t\t\ttry {\n\t\t\t\t\tconst event: ContextEvent = { type: \"context\", messages: currentMessages };\n\t\t\t\t\tconst handlerResult = await handler(event, ctx);\n\n\t\t\t\t\tif (handlerResult && (handlerResult as ContextEventResult).messages) {\n\t\t\t\t\t\tcurrentMessages = (handlerResult as ContextEventResult).messages!;\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst stack = err instanceof Error ? err.stack : undefined;\n\t\t\t\t\tthis.emitError({\n\t\t\t\t\t\textensionPath: ext.path,\n\t\t\t\t\t\tevent: \"context\",\n\t\t\t\t\t\terror: message,\n\t\t\t\t\t\tstack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn currentMessages;\n\t}\n\n\tasync emitBeforeAgentStart(\n\t\tprompt: string,\n\t\timages?: ImageContent[],\n\t): Promise<BeforeAgentStartCombinedResult | undefined> {\n\t\tconst ctx = this.createContext();\n\t\tconst messages: NonNullable<BeforeAgentStartEventResult[\"message\"]>[] = [];\n\t\tconst systemPromptAppends: string[] = [];\n\n\t\tfor (const ext of this.extensions) {\n\t\t\tconst handlers = ext.handlers.get(\"before_agent_start\");\n\t\t\tif (!handlers || handlers.length === 0) continue;\n\n\t\t\tfor (const handler of handlers) {\n\t\t\t\ttry {\n\t\t\t\t\tconst event: BeforeAgentStartEvent = { type: \"before_agent_start\", prompt, images };\n\t\t\t\t\tconst handlerResult = await handler(event, ctx);\n\n\t\t\t\t\tif (handlerResult) {\n\t\t\t\t\t\tconst result = handlerResult as BeforeAgentStartEventResult;\n\t\t\t\t\t\tif (result.message) {\n\t\t\t\t\t\t\tmessages.push(result.message);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (result.systemPromptAppend) {\n\t\t\t\t\t\t\tsystemPromptAppends.push(result.systemPromptAppend);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst stack = err instanceof Error ? err.stack : undefined;\n\t\t\t\t\tthis.emitError({\n\t\t\t\t\t\textensionPath: ext.path,\n\t\t\t\t\t\tevent: \"before_agent_start\",\n\t\t\t\t\t\terror: message,\n\t\t\t\t\t\tstack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (messages.length > 0 || systemPromptAppends.length > 0) {\n\t\t\treturn {\n\t\t\t\tmessages: messages.length > 0 ? messages : undefined,\n\t\t\t\tsystemPromptAppend: systemPromptAppends.length > 0 ? systemPromptAppends.join(\"\\n\\n\") : undefined,\n\t\t\t};\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n"]}
|