@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.
Files changed (260) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +246 -107
  3. package/dist/cli/args.d.ts +3 -4
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +13 -18
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +3 -3
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +39 -50
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +166 -197
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/auth-storage.d.ts.map +1 -1
  16. package/dist/core/auth-storage.js +4 -1
  17. package/dist/core/auth-storage.js.map +1 -1
  18. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  19. package/dist/core/compaction/branch-summarization.js +3 -3
  20. package/dist/core/compaction/branch-summarization.js.map +1 -1
  21. package/dist/core/compaction/compaction.d.ts +1 -1
  22. package/dist/core/compaction/compaction.d.ts.map +1 -1
  23. package/dist/core/compaction/compaction.js +6 -5
  24. package/dist/core/compaction/compaction.js.map +1 -1
  25. package/dist/core/event-bus.d.ts +9 -0
  26. package/dist/core/event-bus.d.ts.map +1 -0
  27. package/dist/core/event-bus.js +25 -0
  28. package/dist/core/event-bus.js.map +1 -0
  29. package/dist/core/exec.d.ts +1 -1
  30. package/dist/core/exec.d.ts.map +1 -1
  31. package/dist/core/exec.js +1 -1
  32. package/dist/core/exec.js.map +1 -1
  33. package/dist/core/extensions/index.d.ts +10 -0
  34. package/dist/core/extensions/index.d.ts.map +1 -0
  35. package/dist/core/extensions/index.js +9 -0
  36. package/dist/core/extensions/index.js.map +1 -0
  37. package/dist/core/extensions/loader.d.ts +21 -0
  38. package/dist/core/extensions/loader.d.ts.map +1 -0
  39. package/dist/core/extensions/loader.js +400 -0
  40. package/dist/core/extensions/loader.js.map +1 -0
  41. package/dist/core/extensions/runner.d.ts +88 -0
  42. package/dist/core/extensions/runner.d.ts.map +1 -0
  43. package/dist/core/{hooks → extensions}/runner.js +52 -141
  44. package/dist/core/extensions/runner.js.map +1 -0
  45. package/dist/core/extensions/types.d.ts +461 -0
  46. package/dist/core/extensions/types.d.ts.map +1 -0
  47. package/dist/core/{hooks → extensions}/types.js +7 -4
  48. package/dist/core/extensions/types.js.map +1 -0
  49. package/dist/core/extensions/wrapper.d.ts +25 -0
  50. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  51. package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
  52. package/dist/core/extensions/wrapper.js.map +1 -0
  53. package/dist/core/index.d.ts +2 -2
  54. package/dist/core/index.d.ts.map +1 -1
  55. package/dist/core/index.js +3 -2
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/core/messages.d.ts +7 -7
  58. package/dist/core/messages.d.ts.map +1 -1
  59. package/dist/core/messages.js +4 -4
  60. package/dist/core/messages.js.map +1 -1
  61. package/dist/core/model-registry.d.ts.map +1 -1
  62. package/dist/core/model-registry.js +2 -0
  63. package/dist/core/model-registry.js.map +1 -1
  64. package/dist/core/model-resolver.d.ts.map +1 -1
  65. package/dist/core/model-resolver.js +1 -0
  66. package/dist/core/model-resolver.js.map +1 -1
  67. package/dist/core/prompt-templates.d.ts +40 -0
  68. package/dist/core/prompt-templates.d.ts.map +1 -0
  69. package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
  70. package/dist/core/prompt-templates.js.map +1 -0
  71. package/dist/core/sdk.d.ts +29 -52
  72. package/dist/core/sdk.d.ts.map +1 -1
  73. package/dist/core/sdk.js +111 -211
  74. package/dist/core/sdk.js.map +1 -1
  75. package/dist/core/session-manager.d.ts +17 -17
  76. package/dist/core/session-manager.d.ts.map +1 -1
  77. package/dist/core/session-manager.js +25 -10
  78. package/dist/core/session-manager.js.map +1 -1
  79. package/dist/core/settings-manager.d.ts +3 -6
  80. package/dist/core/settings-manager.d.ts.map +1 -1
  81. package/dist/core/settings-manager.js +4 -11
  82. package/dist/core/settings-manager.js.map +1 -1
  83. package/dist/core/system-prompt.d.ts.map +1 -1
  84. package/dist/core/system-prompt.js +4 -2
  85. package/dist/core/system-prompt.js.map +1 -1
  86. package/dist/index.d.ts +4 -5
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +5 -6
  89. package/dist/index.js.map +1 -1
  90. package/dist/main.d.ts.map +1 -1
  91. package/dist/main.js +36 -33
  92. package/dist/main.js.map +1 -1
  93. package/dist/migrations.d.ts +7 -2
  94. package/dist/migrations.d.ts.map +1 -1
  95. package/dist/migrations.js +93 -4
  96. package/dist/migrations.js.map +1 -1
  97. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  98. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  100. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  101. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  102. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  104. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  105. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  106. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  108. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  109. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  110. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/custom-editor.js +4 -4
  112. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  113. package/dist/modes/interactive/components/custom-message.d.ts +18 -0
  114. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  115. package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
  116. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  117. package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
  118. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/dynamic-border.js +2 -2
  120. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  121. package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
  122. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  123. package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
  124. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  125. package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
  126. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  127. package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
  128. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  129. package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
  130. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  131. package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
  132. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  133. package/dist/modes/interactive/components/footer.d.ts +3 -3
  134. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  135. package/dist/modes/interactive/components/footer.js +8 -8
  136. package/dist/modes/interactive/components/footer.js.map +1 -1
  137. package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
  138. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/tool-execution.js +9 -9
  140. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  141. package/dist/modes/interactive/interactive-mode.d.ts +37 -44
  142. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  143. package/dist/modes/interactive/interactive-mode.js +143 -189
  144. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  145. package/dist/modes/print-mode.d.ts.map +1 -1
  146. package/dist/modes/print-mode.js +10 -33
  147. package/dist/modes/print-mode.js.map +1 -1
  148. package/dist/modes/rpc/rpc-client.d.ts +3 -3
  149. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  150. package/dist/modes/rpc/rpc-client.js +3 -3
  151. package/dist/modes/rpc/rpc-client.js.map +1 -1
  152. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  153. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  154. package/dist/modes/rpc/rpc-mode.js +33 -57
  155. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  156. package/dist/modes/rpc/rpc-types.d.ts +16 -16
  157. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  158. package/dist/modes/rpc/rpc-types.js.map +1 -1
  159. package/docs/extensions.md +1053 -0
  160. package/docs/rpc.md +4 -4
  161. package/docs/sdk.md +62 -93
  162. package/docs/session.md +22 -19
  163. package/docs/skills.md +1 -1
  164. package/docs/tui.md +1 -1
  165. package/examples/README.md +9 -15
  166. package/examples/extensions/README.md +141 -0
  167. package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
  168. package/examples/extensions/chalk-logger.ts +26 -0
  169. package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
  170. package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
  171. package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
  172. package/examples/{hooks → extensions}/file-trigger.ts +3 -3
  173. package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
  174. package/examples/{hooks → extensions}/handoff.ts +3 -3
  175. package/examples/extensions/hello.ts +25 -0
  176. package/examples/{hooks → extensions}/permission-gate.ts +3 -3
  177. package/examples/{hooks → extensions}/pirate.ts +5 -5
  178. package/examples/{hooks → extensions}/plan-mode.ts +6 -6
  179. package/examples/{hooks → extensions}/protected-paths.ts +3 -3
  180. package/examples/{hooks → extensions}/qna.ts +3 -3
  181. package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
  182. package/examples/{hooks → extensions}/snake.ts +3 -3
  183. package/examples/{hooks → extensions}/status-line.ts +3 -3
  184. package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
  185. package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
  186. package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
  187. package/examples/{hooks → extensions}/tools.ts +5 -5
  188. package/examples/extensions/with-deps/index.ts +36 -0
  189. package/examples/extensions/with-deps/package-lock.json +31 -0
  190. package/examples/extensions/with-deps/package.json +16 -0
  191. package/examples/sdk/01-minimal.ts +1 -1
  192. package/examples/sdk/05-tools.ts +7 -41
  193. package/examples/sdk/06-extensions.ts +81 -0
  194. package/examples/sdk/08-prompt-templates.ts +42 -0
  195. package/examples/sdk/12-full-control.ts +10 -29
  196. package/examples/sdk/README.md +5 -5
  197. package/package.json +4 -4
  198. package/dist/core/custom-tools/index.d.ts +0 -7
  199. package/dist/core/custom-tools/index.d.ts.map +0 -1
  200. package/dist/core/custom-tools/index.js +0 -6
  201. package/dist/core/custom-tools/index.js.map +0 -1
  202. package/dist/core/custom-tools/loader.d.ts +0 -30
  203. package/dist/core/custom-tools/loader.d.ts.map +0 -1
  204. package/dist/core/custom-tools/loader.js +0 -276
  205. package/dist/core/custom-tools/loader.js.map +0 -1
  206. package/dist/core/custom-tools/types.d.ts +0 -144
  207. package/dist/core/custom-tools/types.d.ts.map +0 -1
  208. package/dist/core/custom-tools/types.js +0 -8
  209. package/dist/core/custom-tools/types.js.map +0 -1
  210. package/dist/core/custom-tools/wrapper.d.ts +0 -15
  211. package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
  212. package/dist/core/custom-tools/wrapper.js +0 -23
  213. package/dist/core/custom-tools/wrapper.js.map +0 -1
  214. package/dist/core/hooks/index.d.ts +0 -6
  215. package/dist/core/hooks/index.d.ts.map +0 -1
  216. package/dist/core/hooks/index.js +0 -6
  217. package/dist/core/hooks/index.js.map +0 -1
  218. package/dist/core/hooks/loader.d.ts +0 -146
  219. package/dist/core/hooks/loader.d.ts.map +0 -1
  220. package/dist/core/hooks/loader.js +0 -275
  221. package/dist/core/hooks/loader.js.map +0 -1
  222. package/dist/core/hooks/runner.d.ts +0 -173
  223. package/dist/core/hooks/runner.d.ts.map +0 -1
  224. package/dist/core/hooks/runner.js.map +0 -1
  225. package/dist/core/hooks/tool-wrapper.d.ts +0 -17
  226. package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
  227. package/dist/core/hooks/tool-wrapper.js.map +0 -1
  228. package/dist/core/hooks/types.d.ts +0 -767
  229. package/dist/core/hooks/types.d.ts.map +0 -1
  230. package/dist/core/hooks/types.js.map +0 -1
  231. package/dist/core/slash-commands.d.ts +0 -40
  232. package/dist/core/slash-commands.d.ts.map +0 -1
  233. package/dist/core/slash-commands.js.map +0 -1
  234. package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
  235. package/dist/modes/interactive/components/hook-editor.js.map +0 -1
  236. package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
  237. package/dist/modes/interactive/components/hook-input.js.map +0 -1
  238. package/dist/modes/interactive/components/hook-message.d.ts +0 -18
  239. package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
  240. package/dist/modes/interactive/components/hook-message.js.map +0 -1
  241. package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
  242. package/dist/modes/interactive/components/hook-selector.js.map +0 -1
  243. package/docs/custom-tools.md +0 -514
  244. package/docs/extension-loading.md +0 -1004
  245. package/docs/hooks.md +0 -979
  246. package/docs/session-tree-plan.md +0 -441
  247. package/examples/custom-tools/README.md +0 -114
  248. package/examples/custom-tools/hello/index.ts +0 -21
  249. package/examples/hooks/README.md +0 -60
  250. package/examples/hooks/todo/index.ts +0 -134
  251. package/examples/sdk/06-hooks.ts +0 -61
  252. package/examples/sdk/08-slash-commands.ts +0 -42
  253. /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
  254. /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
  255. /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
  256. /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
  257. /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
  258. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
  259. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
  260. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
@@ -1,10 +1,7 @@
1
1
  /**
2
- * Hook runner - executes hooks and manages their lifecycle.
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
- * HookRunner executes hooks and manages event emission.
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(hooks, cwd, sessionManager, modelRegistry) {
44
- this.hooks = hooks;
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
- // Set per-hook handlers for pi.sendMessage(), pi.appendEntry(), pi.getActiveTools(), pi.getAllTools(), pi.setActiveTools()
72
- for (const hook of this.hooks) {
73
- hook.setSendMessageHandler(options.sendMessageHandler);
74
- hook.setAppendEntryHandler(options.appendEntryHandler);
75
- hook.setGetActiveToolsHandler(options.getActiveToolsHandler);
76
- hook.setGetAllToolsHandler(options.getAllToolsHandler);
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
- * Get the paths of all loaded hooks.
96
- */
97
- getHookPaths() {
98
- return this.hooks.map((h) => h.path);
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 hook of this.hooks) {
106
- for (const [name, flag] of hook.flags) {
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 hook of this.hooks) {
117
- if (hook.flags.has(name)) {
118
- hook.setFlagValue(name, value);
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 hook of this.hooks) {
148
- for (const [key, shortcut] of hook.shortcuts) {
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
- // Check for built-in shortcut conflicts
152
- if (HookRunner.RESERVED_SHORTCUTS.has(normalizedKey)) {
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
- // Log conflict between hooks - last one wins
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 hook of this.hooks) {
190
- const handlers = hook.handlers.get(eventType);
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 hook of this.hooks) {
203
- const renderer = hook.messageRenderers.get(customType);
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 hook of this.hooks) {
216
- for (const command of hook.commands.values()) {
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 hook of this.hooks) {
228
- const command = hook.commands.get(name);
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 hook of this.hooks) {
281
- const handlers = hook.handlers.get(event.type);
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
- hookPath: hook.path,
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 hook of this.hooks) {
323
- const handlers = hook.handlers.get("tool_call");
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 hook of this.hooks) {
351
- const handlers = hook.handlers.get("context");
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
- hookPath: hook.path,
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 hook of this.hooks) {
385
- const handlers = hook.handlers.get("before_agent_start");
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
- hookPath: hook.path,
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"]}