@di-code/coding-agent 0.1.7 → 0.1.9

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 (333) hide show
  1. package/README.md +54 -567
  2. package/compositions/base.yml +54 -0
  3. package/compositions/interactive.yml +14 -0
  4. package/compositions/json.yml +6 -0
  5. package/compositions/print.yml +5 -0
  6. package/compositions/rpc.yml +12 -0
  7. package/compositions/webui.yml +9 -0
  8. package/dist/cli.d.ts +22 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +97 -3
  11. package/dist/cli.js.map +1 -1
  12. package/dist/composition-loader-entry.d.ts +11 -0
  13. package/dist/composition-loader-entry.d.ts.map +1 -0
  14. package/dist/composition-loader-entry.js +12 -0
  15. package/dist/composition-loader-entry.js.map +1 -0
  16. package/dist/compositions.d.ts +32 -0
  17. package/dist/compositions.d.ts.map +1 -0
  18. package/dist/compositions.js +173 -0
  19. package/dist/compositions.js.map +1 -0
  20. package/dist/core/session/session-manager.d.ts +9 -2
  21. package/dist/core/session/session-manager.d.ts.map +1 -1
  22. package/dist/core/session/session-manager.js +28 -0
  23. package/dist/core/session/session-manager.js.map +1 -1
  24. package/dist/core/session/session-storage.d.ts.map +1 -1
  25. package/dist/core/session/session-storage.js +20 -3
  26. package/dist/core/session/session-storage.js.map +1 -1
  27. package/dist/core/session/types.d.ts +11 -2
  28. package/dist/core/session/types.d.ts.map +1 -1
  29. package/dist/core/session/types.js.map +1 -1
  30. package/dist/core/session-tools.d.ts +5 -0
  31. package/dist/core/session-tools.d.ts.map +1 -0
  32. package/dist/core/session-tools.js +17 -0
  33. package/dist/core/session-tools.js.map +1 -0
  34. package/dist/core/session.d.ts +19 -7
  35. package/dist/core/session.d.ts.map +1 -1
  36. package/dist/core/session.js +71 -39
  37. package/dist/core/session.js.map +1 -1
  38. package/dist/core/tools/bash.d.ts +1 -38
  39. package/dist/core/tools/bash.d.ts.map +1 -1
  40. package/dist/core/tools/bash.js +1 -207
  41. package/dist/core/tools/bash.js.map +1 -1
  42. package/dist/core/tools/edit-diff.d.ts +1 -24
  43. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  44. package/dist/core/tools/edit-diff.js +1 -135
  45. package/dist/core/tools/edit-diff.js.map +1 -1
  46. package/dist/core/tools/edit.d.ts +1 -26
  47. package/dist/core/tools/edit.d.ts.map +1 -1
  48. package/dist/core/tools/edit.js +1 -140
  49. package/dist/core/tools/edit.js.map +1 -1
  50. package/dist/core/tools/file-mutation-queue.d.ts +1 -1
  51. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  52. package/dist/core/tools/file-mutation-queue.js +1 -23
  53. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  54. package/dist/core/tools/file-search.d.ts +1 -13
  55. package/dist/core/tools/file-search.d.ts.map +1 -1
  56. package/dist/core/tools/file-search.js +1 -98
  57. package/dist/core/tools/file-search.js.map +1 -1
  58. package/dist/core/tools/glob.d.ts +1 -10
  59. package/dist/core/tools/glob.d.ts.map +1 -1
  60. package/dist/core/tools/glob.js +1 -43
  61. package/dist/core/tools/glob.js.map +1 -1
  62. package/dist/core/tools/grep.d.ts +1 -12
  63. package/dist/core/tools/grep.d.ts.map +1 -1
  64. package/dist/core/tools/grep.js +1 -66
  65. package/dist/core/tools/grep.js.map +1 -1
  66. package/dist/core/tools/load-skill.d.ts +1 -9
  67. package/dist/core/tools/load-skill.d.ts.map +1 -1
  68. package/dist/core/tools/load-skill.js +1 -21
  69. package/dist/core/tools/load-skill.js.map +1 -1
  70. package/dist/core/tools/path-boundary.d.ts +1 -4
  71. package/dist/core/tools/path-boundary.d.ts.map +1 -1
  72. package/dist/core/tools/path-boundary.js +1 -65
  73. package/dist/core/tools/path-boundary.js.map +1 -1
  74. package/dist/core/tools/read.d.ts +1 -16
  75. package/dist/core/tools/read.d.ts.map +1 -1
  76. package/dist/core/tools/read.js +1 -131
  77. package/dist/core/tools/read.js.map +1 -1
  78. package/dist/core/tools/write.d.ts +1 -16
  79. package/dist/core/tools/write.d.ts.map +1 -1
  80. package/dist/core/tools/write.js +1 -44
  81. package/dist/core/tools/write.js.map +1 -1
  82. package/dist/entry.js +30 -28
  83. package/dist/entry.js.map +1 -1
  84. package/dist/index.d.ts +6 -3
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +6 -3
  87. package/dist/index.js.map +1 -1
  88. package/dist/interactive/builtin.d.ts +17 -0
  89. package/dist/interactive/builtin.d.ts.map +1 -0
  90. package/dist/interactive/builtin.js +47 -0
  91. package/dist/interactive/builtin.js.map +1 -0
  92. package/dist/interactive/controller.d.ts +85 -0
  93. package/dist/interactive/controller.d.ts.map +1 -0
  94. package/dist/interactive/controller.js +211 -0
  95. package/dist/interactive/controller.js.map +1 -0
  96. package/dist/interactive/index.d.ts +3 -0
  97. package/dist/interactive/index.d.ts.map +1 -0
  98. package/dist/interactive/index.js +3 -0
  99. package/dist/interactive/index.js.map +1 -0
  100. package/dist/interactive-host-entry.d.ts +7 -0
  101. package/dist/interactive-host-entry.d.ts.map +1 -0
  102. package/dist/interactive-host-entry.js +123 -0
  103. package/dist/interactive-host-entry.js.map +1 -0
  104. package/dist/interactive-profile.d.ts +18 -0
  105. package/dist/interactive-profile.d.ts.map +1 -0
  106. package/dist/interactive-profile.js +290 -0
  107. package/dist/interactive-profile.js.map +1 -0
  108. package/dist/interactive-resources-entry.d.ts +7 -0
  109. package/dist/interactive-resources-entry.d.ts.map +1 -0
  110. package/dist/interactive-resources-entry.js +42 -0
  111. package/dist/interactive-resources-entry.js.map +1 -0
  112. package/dist/legacy-main.d.ts +39 -0
  113. package/dist/legacy-main.d.ts.map +1 -0
  114. package/dist/legacy-main.js +611 -0
  115. package/dist/legacy-main.js.map +1 -0
  116. package/dist/main.d.ts +15 -34
  117. package/dist/main.d.ts.map +1 -1
  118. package/dist/main.js +130 -518
  119. package/dist/main.js.map +1 -1
  120. package/dist/mcp/entries.d.ts +42 -0
  121. package/dist/mcp/entries.d.ts.map +1 -0
  122. package/dist/mcp/entries.js +98 -0
  123. package/dist/mcp/entries.js.map +1 -0
  124. package/dist/mcp-client-entry.d.ts +5 -0
  125. package/dist/mcp-client-entry.d.ts.map +1 -0
  126. package/dist/mcp-client-entry.js +6 -0
  127. package/dist/mcp-client-entry.js.map +1 -0
  128. package/dist/mcp-config-entry.d.ts +5 -0
  129. package/dist/mcp-config-entry.d.ts.map +1 -0
  130. package/dist/mcp-config-entry.js +6 -0
  131. package/dist/mcp-config-entry.js.map +1 -0
  132. package/dist/mcp-tools-entry.d.ts +5 -0
  133. package/dist/mcp-tools-entry.d.ts.map +1 -0
  134. package/dist/mcp-tools-entry.js +6 -0
  135. package/dist/mcp-tools-entry.js.map +1 -0
  136. package/dist/mcp-transport-entry.d.ts +5 -0
  137. package/dist/mcp-transport-entry.d.ts.map +1 -0
  138. package/dist/mcp-transport-entry.js +6 -0
  139. package/dist/mcp-transport-entry.js.map +1 -0
  140. package/dist/modes/interactive-entry.d.ts +18 -0
  141. package/dist/modes/interactive-entry.d.ts.map +1 -0
  142. package/dist/modes/interactive-entry.js +21 -0
  143. package/dist/modes/interactive-entry.js.map +1 -0
  144. package/dist/modes/interactive.d.ts +16 -6
  145. package/dist/modes/interactive.d.ts.map +1 -1
  146. package/dist/modes/interactive.js +81 -32
  147. package/dist/modes/interactive.js.map +1 -1
  148. package/dist/modes/json-entry.d.ts +13 -0
  149. package/dist/modes/json-entry.d.ts.map +1 -0
  150. package/dist/modes/json-entry.js +14 -0
  151. package/dist/modes/json-entry.js.map +1 -0
  152. package/dist/modes/json.d.ts +4 -1
  153. package/dist/modes/json.d.ts.map +1 -1
  154. package/dist/modes/json.js +3 -3
  155. package/dist/modes/json.js.map +1 -1
  156. package/dist/modes/print-entry.d.ts +10 -0
  157. package/dist/modes/print-entry.d.ts.map +1 -0
  158. package/dist/modes/print-entry.js +11 -0
  159. package/dist/modes/print-entry.js.map +1 -0
  160. package/dist/modes/tree-selector.d.ts.map +1 -1
  161. package/dist/modes/tree-selector.js +4 -0
  162. package/dist/modes/tree-selector.js.map +1 -1
  163. package/dist/plugin-dump-composition.d.ts +6 -0
  164. package/dist/plugin-dump-composition.d.ts.map +1 -0
  165. package/dist/plugin-dump-composition.js +7 -0
  166. package/dist/plugin-dump-composition.js.map +1 -0
  167. package/dist/plugin-inventory.d.ts +7 -0
  168. package/dist/plugin-inventory.d.ts.map +1 -0
  169. package/dist/plugin-inventory.js +8 -0
  170. package/dist/plugin-inventory.js.map +1 -0
  171. package/dist/plugin-manager.d.ts +6 -0
  172. package/dist/plugin-manager.d.ts.map +1 -0
  173. package/dist/plugin-manager.js +7 -0
  174. package/dist/plugin-manager.js.map +1 -0
  175. package/dist/plugin-trace.d.ts +6 -0
  176. package/dist/plugin-trace.d.ts.map +1 -0
  177. package/dist/plugin-trace.js +7 -0
  178. package/dist/plugin-trace.js.map +1 -0
  179. package/dist/plugins/dynamic-broker.d.ts +76 -0
  180. package/dist/plugins/dynamic-broker.d.ts.map +1 -0
  181. package/dist/plugins/dynamic-broker.js +852 -0
  182. package/dist/plugins/dynamic-broker.js.map +1 -0
  183. package/dist/plugins/profile.d.ts +15 -0
  184. package/dist/plugins/profile.d.ts.map +1 -0
  185. package/dist/plugins/profile.js +34 -0
  186. package/dist/plugins/profile.js.map +1 -0
  187. package/dist/plugins/runtime-host.d.ts +37 -0
  188. package/dist/plugins/runtime-host.d.ts.map +1 -0
  189. package/dist/plugins/runtime-host.js +95 -0
  190. package/dist/plugins/runtime-host.js.map +1 -0
  191. package/dist/plugins/trust.d.ts +8 -0
  192. package/dist/plugins/trust.d.ts.map +1 -0
  193. package/dist/plugins/trust.js +59 -0
  194. package/dist/plugins/trust.js.map +1 -0
  195. package/dist/project-trust-entry.d.ts +15 -0
  196. package/dist/project-trust-entry.d.ts.map +1 -0
  197. package/dist/project-trust-entry.js +15 -0
  198. package/dist/project-trust-entry.js.map +1 -0
  199. package/dist/rpc/client.d.ts +78 -6
  200. package/dist/rpc/client.d.ts.map +1 -1
  201. package/dist/rpc/client.js +172 -4
  202. package/dist/rpc/client.js.map +1 -1
  203. package/dist/rpc/dispatcher.d.ts +121 -0
  204. package/dist/rpc/dispatcher.d.ts.map +1 -0
  205. package/dist/rpc/dispatcher.js +994 -0
  206. package/dist/rpc/dispatcher.js.map +1 -0
  207. package/dist/rpc/index.d.ts +2 -0
  208. package/dist/rpc/index.d.ts.map +1 -1
  209. package/dist/rpc/index.js +2 -0
  210. package/dist/rpc/index.js.map +1 -1
  211. package/dist/rpc/lifecycle.d.ts +6 -0
  212. package/dist/rpc/lifecycle.d.ts.map +1 -0
  213. package/dist/rpc/lifecycle.js +24 -0
  214. package/dist/rpc/lifecycle.js.map +1 -0
  215. package/dist/rpc/protocol.d.ts +112 -31
  216. package/dist/rpc/protocol.d.ts.map +1 -1
  217. package/dist/rpc/protocol.js +569 -117
  218. package/dist/rpc/protocol.js.map +1 -1
  219. package/dist/rpc/server-service.d.ts +7 -0
  220. package/dist/rpc/server-service.d.ts.map +1 -0
  221. package/dist/rpc/server-service.js +3 -0
  222. package/dist/rpc/server-service.js.map +1 -0
  223. package/dist/rpc/server.d.ts +16 -19
  224. package/dist/rpc/server.d.ts.map +1 -1
  225. package/dist/rpc/server.js +59 -115
  226. package/dist/rpc/server.js.map +1 -1
  227. package/dist/rpc-client-sdk-entry.d.ts +12 -0
  228. package/dist/rpc-client-sdk-entry.d.ts.map +1 -0
  229. package/dist/rpc-client-sdk-entry.js +11 -0
  230. package/dist/rpc-client-sdk-entry.js.map +1 -0
  231. package/dist/rpc-entry.js +50 -18
  232. package/dist/rpc-entry.js.map +1 -1
  233. package/dist/rpc-server-entry.d.ts +8 -0
  234. package/dist/rpc-server-entry.d.ts.map +1 -0
  235. package/dist/rpc-server-entry.js +67 -0
  236. package/dist/rpc-server-entry.js.map +1 -0
  237. package/dist/runtime/attachment-store.d.ts +13 -0
  238. package/dist/runtime/attachment-store.d.ts.map +1 -0
  239. package/dist/runtime/attachment-store.js +93 -0
  240. package/dist/runtime/attachment-store.js.map +1 -0
  241. package/dist/runtime/bootstrap.d.ts +14 -0
  242. package/dist/runtime/bootstrap.d.ts.map +1 -0
  243. package/dist/runtime/bootstrap.js +29 -0
  244. package/dist/runtime/bootstrap.js.map +1 -0
  245. package/dist/runtime/interactive-host-service.d.ts +22 -0
  246. package/dist/runtime/interactive-host-service.d.ts.map +1 -0
  247. package/dist/runtime/interactive-host-service.js +2 -0
  248. package/dist/runtime/interactive-host-service.js.map +1 -0
  249. package/dist/runtime/interactive-resource-service.d.ts +11 -0
  250. package/dist/runtime/interactive-resource-service.d.ts.map +1 -0
  251. package/dist/runtime/interactive-resource-service.js +3 -0
  252. package/dist/runtime/interactive-resource-service.js.map +1 -0
  253. package/dist/runtime/plugin-inventory-entry.d.ts +5 -0
  254. package/dist/runtime/plugin-inventory-entry.d.ts.map +1 -0
  255. package/dist/runtime/plugin-inventory-entry.js +18 -0
  256. package/dist/runtime/plugin-inventory-entry.js.map +1 -0
  257. package/dist/runtime/plugin-inventory-service.d.ts +8 -0
  258. package/dist/runtime/plugin-inventory-service.d.ts.map +1 -0
  259. package/dist/runtime/plugin-inventory-service.js +3 -0
  260. package/dist/runtime/plugin-inventory-service.js.map +1 -0
  261. package/dist/runtime/plugin-manager-entry.d.ts +20 -0
  262. package/dist/runtime/plugin-manager-entry.d.ts.map +1 -0
  263. package/dist/runtime/plugin-manager-entry.js +96 -0
  264. package/dist/runtime/plugin-manager-entry.js.map +1 -0
  265. package/dist/runtime/plugin-observability-entry.d.ts +12 -0
  266. package/dist/runtime/plugin-observability-entry.d.ts.map +1 -0
  267. package/dist/runtime/plugin-observability-entry.js +73 -0
  268. package/dist/runtime/plugin-observability-entry.js.map +1 -0
  269. package/dist/runtime/product-host.d.ts +91 -0
  270. package/dist/runtime/product-host.d.ts.map +1 -0
  271. package/dist/runtime/product-host.js +529 -0
  272. package/dist/runtime/product-host.js.map +1 -0
  273. package/dist/runtime/session-factory.d.ts +17 -0
  274. package/dist/runtime/session-factory.d.ts.map +1 -0
  275. package/dist/runtime/session-factory.js +75 -0
  276. package/dist/runtime/session-factory.js.map +1 -0
  277. package/dist/runtime/session-host.d.ts +152 -0
  278. package/dist/runtime/session-host.d.ts.map +1 -0
  279. package/dist/runtime/session-host.js +895 -0
  280. package/dist/runtime/session-host.js.map +1 -0
  281. package/dist/runtime-core-entry.d.ts +11 -0
  282. package/dist/runtime-core-entry.d.ts.map +1 -0
  283. package/dist/runtime-core-entry.js +9 -0
  284. package/dist/runtime-core-entry.js.map +1 -0
  285. package/dist/session-factory-entry.d.ts +7 -0
  286. package/dist/session-factory-entry.d.ts.map +1 -0
  287. package/dist/session-factory-entry.js +9 -0
  288. package/dist/session-factory-entry.js.map +1 -0
  289. package/dist/session-store-jsonl-entry.d.ts +7 -0
  290. package/dist/session-store-jsonl-entry.d.ts.map +1 -0
  291. package/dist/session-store-jsonl-entry.js +33 -0
  292. package/dist/session-store-jsonl-entry.js.map +1 -0
  293. package/dist/startup.d.ts +12 -1
  294. package/dist/startup.d.ts.map +1 -1
  295. package/dist/startup.js +106 -15
  296. package/dist/startup.js.map +1 -1
  297. package/dist/subagents/index.d.ts +2 -0
  298. package/dist/subagents/index.d.ts.map +1 -0
  299. package/dist/subagents/index.js +2 -0
  300. package/dist/subagents/index.js.map +1 -0
  301. package/dist/subagents/service.d.ts +50 -0
  302. package/dist/subagents/service.d.ts.map +1 -0
  303. package/dist/subagents/service.js +521 -0
  304. package/dist/subagents/service.js.map +1 -0
  305. package/dist/ui-host.d.ts +125 -0
  306. package/dist/ui-host.d.ts.map +1 -0
  307. package/dist/ui-host.js +275 -0
  308. package/dist/ui-host.js.map +1 -0
  309. package/dist/web/assets/index-BwfuGMvv.css +1 -0
  310. package/dist/web/assets/index-DPUaJqFJ.js +41 -0
  311. package/dist/web/assets/index-DPUaJqFJ.js.map +1 -0
  312. package/dist/web/index.html +14 -0
  313. package/dist/web-command.d.ts +11 -0
  314. package/dist/web-command.d.ts.map +1 -0
  315. package/dist/web-command.js +114 -0
  316. package/dist/web-command.js.map +1 -0
  317. package/dist/web-frontend.d.ts +73 -0
  318. package/dist/web-frontend.d.ts.map +1 -0
  319. package/dist/web-frontend.js +249 -0
  320. package/dist/web-frontend.js.map +1 -0
  321. package/dist/web.d.ts +254 -0
  322. package/dist/web.d.ts.map +1 -0
  323. package/dist/web.js +740 -0
  324. package/dist/web.js.map +1 -0
  325. package/dist/webui-entry.d.ts +3 -0
  326. package/dist/webui-entry.d.ts.map +1 -0
  327. package/dist/webui-entry.js +73 -0
  328. package/dist/webui-entry.js.map +1 -0
  329. package/dist/webui.d.ts +91 -0
  330. package/dist/webui.d.ts.map +1 -0
  331. package/dist/webui.js +882 -0
  332. package/dist/webui.js.map +1 -0
  333. package/package.json +89 -8
@@ -0,0 +1,852 @@
1
+ import { spawn } from "node:child_process";
2
+ import { StringDecoder } from "node:string_decoder";
3
+ import { Type } from "@di-code/ai";
4
+ import { DYNAMIC_PLUGIN_MAX_LINE_BYTES, DYNAMIC_PLUGIN_PROTOCOL_VERSION, DynamicPluginRuntime, parseDynamicPluginCapabilityRecord, parseDynamicPluginChildInvocationRecord, } from "@di-code/plugin-runtime";
5
+ let invocationSequence = 0;
6
+ const BOOTSTRAP = String.raw `
7
+ import { createInterface } from "node:readline";
8
+ const input = createInterface({ input: process.stdin });
9
+ let stopping = false;
10
+ let bootstrapped = false;
11
+ let sequence = 0;
12
+ let writes = Promise.resolve();
13
+ const handlers = new Map();
14
+ const active = new Map();
15
+ const pendingNext = new Map();
16
+ const write = (record) => {
17
+ writes = writes.then(() => new Promise((resolve, reject) => process.stdout.write(JSON.stringify(record) + "\n", (error) => error ? reject(error) : resolve())));
18
+ return writes;
19
+ };
20
+ const disposable = (send) => {
21
+ let disposed = false;
22
+ return { dispose: () => { if (disposed) return; disposed = true; void send().catch(() => undefined); } };
23
+ };
24
+ const capability = (pluginId, runId, value, handler) => {
25
+ const id = typeof value.id === "string" ? value.id : typeof value.name === "string" ? value.name : "capability-" + (++sequence);
26
+ handlers.set(id, { type: value.type, handler });
27
+ const record = { version: 1, id: "cap-" + (++sequence), method: "capability_register", params: { pluginId, runId, capability: { ...value, id } } };
28
+ const registered = write(record);
29
+ return { dispose: () => { handlers.delete(id); return registered.then(() => write({ version: 1, id: "cap-" + (++sequence), method: "capability_revoke", params: { pluginId, runId, capabilityId: id } })); } };
30
+ };
31
+ const createApi = (pluginId, runId) => ({
32
+ registerTool: (tool) => capability(pluginId, runId, { type: "tool", name: tool.name, description: tool.description, parameters: tool.parameters, id: tool.name }, tool.execute),
33
+ registerPromptSection: (section) => capability(pluginId, runId, { type: "prompt", id: section.id, order: section.order }, section.render),
34
+ useToolMiddleware: (middleware) => capability(pluginId, runId, { type: "middleware", id: middleware.id }, middleware.execute),
35
+ on: (event, handler) => capability(pluginId, runId, { type: "event", id: "event:" + event + ":" + (++sequence), event }, handler),
36
+ });
37
+ const contextFor = (record, controller) => ({ ...record.context, signal: controller.signal });
38
+ const rejectPending = (invokeId, error) => {
39
+ for (const [nextId, pending] of pendingNext) if (pending.invokeId === invokeId) { pendingNext.delete(nextId); pending.reject(error); }
40
+ };
41
+ const handle = async (record) => {
42
+ if (record.kind === "bootstrap") {
43
+ if (bootstrapped) throw new Error("duplicate bootstrap");
44
+ const source = Buffer.from(record.source, "base64url").toString("utf8");
45
+ const module = await import("data:text/javascript;base64," + Buffer.from(source).toString("base64"));
46
+ if (typeof module.default === "function") await module.default(createApi(record.pluginId, record.runId));
47
+ bootstrapped = true;
48
+ await writes;
49
+ await write({ kind: "ready" });
50
+ return;
51
+ }
52
+ if (record.kind === "stop" && !stopping) {
53
+ stopping = true;
54
+ for (const [invokeId, activeInvocation] of active) { activeInvocation.abort(); rejectPending(invokeId, new Error("Dynamic plugin run stopped.")); }
55
+ await write({ kind: "stopped" });
56
+ process.exit(0);
57
+ return;
58
+ }
59
+ if (record.kind === "cancel") {
60
+ active.get(record.invokeId)?.abort();
61
+ rejectPending(record.invokeId, new Error("Dynamic plugin invocation cancelled."));
62
+ return;
63
+ }
64
+ if (record.kind === "middleware_next_result") {
65
+ const pending = pendingNext.get(record.nextId);
66
+ if (!pending) return;
67
+ pendingNext.delete(record.nextId);
68
+ if (record.ok) pending.resolve(record.result); else pending.reject(new Error(record.error || "middleware next failed"));
69
+ return;
70
+ }
71
+ if (record.kind !== "invoke") throw new Error("unexpected dynamic plugin message");
72
+ const entry = handlers.get(record.capabilityId);
73
+ if (!entry) throw new Error("unknown dynamic capability");
74
+ const controller = new AbortController();
75
+ active.set(record.invokeId, controller);
76
+ try {
77
+ const context = contextFor(record, controller);
78
+ let result;
79
+ if (record.action === "tool") result = await entry.handler(record.payload.toolCallId, record.payload.parameters, controller.signal, context);
80
+ else if (record.action === "prompt") result = await entry.handler(context);
81
+ else if (record.action === "event") result = await entry.handler(record.payload.event, context);
82
+ else {
83
+ const next = (execution) => {
84
+ const nextId = "next-" + (++sequence);
85
+ const promise = new Promise((resolve, reject) => pendingNext.set(nextId, { invokeId: record.invokeId, resolve, reject }));
86
+ void write({ version: 1, kind: "middleware_next", invokeId: record.invokeId, nextId, execution });
87
+ return promise;
88
+ };
89
+ result = await entry.handler(record.payload.execution, next, context);
90
+ }
91
+ await write({ version: 1, kind: "invoke_result", invokeId: record.invokeId, ok: true, result });
92
+ } catch (error) {
93
+ await write({ version: 1, kind: "invoke_result", invokeId: record.invokeId, ok: false, error: String(error && error.message || error).slice(0, 500) });
94
+ } finally {
95
+ active.delete(record.invokeId);
96
+ rejectPending(record.invokeId, new Error("Dynamic plugin invocation finished."));
97
+ }
98
+ };
99
+ input.on("line", (line) => {
100
+ let record;
101
+ try { record = JSON.parse(line); } catch { void write({ kind: "error", message: "invalid bootstrap JSON" }); process.exitCode = 2; return; }
102
+ void handle(record).catch((error) => { void write({ kind: "error", message: String(error && error.message || error).slice(0, 500) }); process.exitCode = 2; });
103
+ });
104
+ input.on("close", () => { if (!stopping) process.exitCode = 0; });
105
+ `;
106
+ /** Owns dynamic plugin child processes and never executes dynamic source in the host process. */
107
+ export class DynamicPluginBroker {
108
+ runtime = new DynamicPluginRuntime();
109
+ options;
110
+ reservedToolNames;
111
+ runs = new Map();
112
+ current = new Map();
113
+ sessionStartEvent;
114
+ disposed = false;
115
+ constructor(options) {
116
+ if (!options.cwd.trim())
117
+ throw new Error("Dynamic plugin broker cwd must be non-empty");
118
+ this.options = {
119
+ ...options,
120
+ model: options.model ?? "",
121
+ projectTrusted: options.projectTrusted ?? false,
122
+ reservedToolNames: [...(options.reservedToolNames ?? [])],
123
+ killGraceMs: options.killGraceMs ?? 250,
124
+ };
125
+ this.reservedToolNames = [...(options.reservedToolNames ?? [])];
126
+ }
127
+ /** Adds host-owned tool names that dynamic capabilities must not shadow. */
128
+ setReservedToolNames(names) {
129
+ this.reservedToolNames = [...names];
130
+ }
131
+ define(definition) {
132
+ this.assertOpen();
133
+ return this.runtime.define(definition).snapshot();
134
+ }
135
+ inspect() {
136
+ return this.runtime.inspect();
137
+ }
138
+ async run(packageId, signal) {
139
+ this.assertOpen();
140
+ if (signal?.aborted)
141
+ throw abortError();
142
+ const pkg = this.runtime.getPackage(packageId);
143
+ if (!pkg)
144
+ throw new Error(`Unknown package: ${packageId}`);
145
+ await this.approve(pkg.snapshot());
146
+ if (signal?.aborted)
147
+ throw abortError();
148
+ const response = this.runtime.handle({
149
+ version: DYNAMIC_PLUGIN_PROTOCOL_VERSION,
150
+ id: `plugin-run-${Date.now()}`,
151
+ method: "plugin_run",
152
+ params: { packageId },
153
+ });
154
+ if (!response.ok)
155
+ throw new Error(response.error.message);
156
+ const runId = response.result.id;
157
+ const child = this.spawnChild();
158
+ const run = this.createRun(pkg, runId, child);
159
+ this.runs.set(runId, run);
160
+ const abort = () => void this.stop(runId).catch(() => undefined);
161
+ signal?.addEventListener("abort", abort, { once: true });
162
+ try {
163
+ await run.ready;
164
+ run.readyConsumed = true;
165
+ if (signal?.aborted)
166
+ throw abortError();
167
+ this.runtime.activateRun(runId);
168
+ this.current.set(pkg.pluginId, runId);
169
+ if (this.sessionStartEvent)
170
+ await this.deliverEvent(run, this.sessionStartEvent, signal);
171
+ return this.runtime.getRun(runId)?.snapshot() ?? runSnapshot(this.runtime, runId);
172
+ }
173
+ catch (cause) {
174
+ if (!run.cleanupPromise)
175
+ await this.fail(run, pkg.pluginId, cause);
176
+ throw cause;
177
+ }
178
+ finally {
179
+ signal?.removeEventListener("abort", abort);
180
+ }
181
+ }
182
+ async update(definition, signal) {
183
+ const next = this.define(definition);
184
+ const previous = this.current.get(definition.pluginId);
185
+ try {
186
+ const started = await this.run(next.id, signal);
187
+ if (previous !== undefined && previous !== started.id)
188
+ await this.stop(previous);
189
+ return started;
190
+ }
191
+ catch (cause) {
192
+ this.reportDiagnostic({ pluginId: definition.pluginId, stage: "cleanup", message: safeMessage(cause) });
193
+ throw cause;
194
+ }
195
+ }
196
+ async stop(runId) {
197
+ const run = this.runs.get(runId);
198
+ if (!run) {
199
+ const existing = this.runtime.getRun(runId);
200
+ if (!existing)
201
+ throw new Error(`Unknown run: ${runId}`);
202
+ return existing.snapshot();
203
+ }
204
+ if (run.cleanupPromise) {
205
+ await run.cleanupPromise;
206
+ return runSnapshot(this.runtime, runId);
207
+ }
208
+ const pkg = this.runtime.getPackage(run.packageId);
209
+ run.cleanupPromise = this.stopRun(run, pkg?.pluginId);
210
+ await run.cleanupPromise;
211
+ return runSnapshot(this.runtime, runId);
212
+ }
213
+ async remove(packageId) {
214
+ const pkg = this.runtime.getPackage(packageId);
215
+ if (!pkg)
216
+ throw new Error(`Unknown package: ${packageId}`);
217
+ for (const run of [...this.runs.values()])
218
+ if (run.packageId === packageId)
219
+ await this.stop(run.runId);
220
+ this.runtime.remove(packageId);
221
+ }
222
+ async dispose() {
223
+ if (this.disposed)
224
+ return;
225
+ this.disposed = true;
226
+ const failures = [];
227
+ for (const run of [...this.runs.values()]) {
228
+ try {
229
+ await this.stop(run.runId);
230
+ }
231
+ catch (cause) {
232
+ failures.push(cause);
233
+ }
234
+ }
235
+ if (failures.length > 0)
236
+ throw new AggregateError(failures, "Dynamic plugin broker disposal failed");
237
+ }
238
+ /** Exposes the broker as model tools; execution remains subject to approval and mode checks. */
239
+ createTools() {
240
+ return [
241
+ {
242
+ name: "plugin_inspect",
243
+ description: "Inspect dynamic plugin packages and runs.",
244
+ parameters: Type.Object({}),
245
+ execute: async () => jsonResult(this.inspect()),
246
+ },
247
+ {
248
+ name: "plugin_define",
249
+ description: "Define dynamic plugin source without executing it.",
250
+ parameters: definitionSchema(),
251
+ execute: async (_id, value) => jsonResult(this.define(value)),
252
+ },
253
+ {
254
+ name: "plugin_run",
255
+ description: "Run an approved dynamic plugin package.",
256
+ parameters: Type.Object({ packageId: Type.String({ minLength: 1, maxLength: 128 }) }),
257
+ execute: async (_id, value, signal) => jsonResult(await this.run(value.packageId, signal)),
258
+ },
259
+ {
260
+ name: "plugin_update",
261
+ description: "Define and run a replacement dynamic plugin; the old run stays active if it fails.",
262
+ parameters: definitionSchema(),
263
+ execute: async (_id, value, signal) => jsonResult(await this.update(value, signal)),
264
+ },
265
+ {
266
+ name: "plugin_stop",
267
+ description: "Stop a dynamic plugin run.",
268
+ parameters: Type.Object({ runId: Type.String({ minLength: 1, maxLength: 128 }) }),
269
+ execute: async (_id, value) => jsonResult(await this.stop(value.runId)),
270
+ },
271
+ {
272
+ name: "plugin_remove",
273
+ description: "Stop and remove a dynamic plugin package.",
274
+ parameters: Type.Object({ packageId: Type.String({ minLength: 1, maxLength: 128 }) }),
275
+ execute: async (_id, value) => {
276
+ await this.remove(value.packageId);
277
+ return [{ type: "text", text: "Dynamic plugin package removed." }];
278
+ },
279
+ },
280
+ ];
281
+ }
282
+ /** Resolves active dynamic capabilities into the Agent request snapshot. */
283
+ getContextProvider() {
284
+ return { resolve: (signal) => this.resolveContext(signal) };
285
+ }
286
+ async resolveContext(signal) {
287
+ const tools = [];
288
+ const middleware = [];
289
+ const prompts = [];
290
+ for (const run of this.activeRuns()) {
291
+ const snapshot = this.runtime.getRun(run.runId)?.snapshot();
292
+ for (const capability of snapshot?.capabilities ?? []) {
293
+ if (capability.type === "tool")
294
+ tools.push(this.createDynamicTool(run, capability));
295
+ else if (capability.type === "middleware")
296
+ middleware.push(this.createDynamicMiddleware(run, capability));
297
+ else if (capability.type === "prompt")
298
+ prompts.push({ order: capability.order, id: capability.id, run, capabilityId: capability.id });
299
+ }
300
+ }
301
+ prompts.sort((a, b) => a.order - b.order || a.run.runId.localeCompare(b.run.runId) || a.id.localeCompare(b.id));
302
+ const rendered = [];
303
+ for (const prompt of prompts) {
304
+ try {
305
+ const value = await this.invoke(prompt.run, prompt.capabilityId, "prompt", {}, signal);
306
+ if (typeof value === "string" && value)
307
+ rendered.push(value);
308
+ }
309
+ catch (cause) {
310
+ this.reportDiagnostic({
311
+ pluginId: prompt.run.pluginId,
312
+ runId: prompt.run.runId,
313
+ stage: "protocol",
314
+ message: safeMessage(cause),
315
+ });
316
+ throw new Error(`Dynamic plugin prompt section "${prompt.id}" failed`, { cause });
317
+ }
318
+ }
319
+ return {
320
+ systemPrompt: rendered.length > 0 ? rendered.join("\n\n") : undefined,
321
+ tools,
322
+ toolMiddleware: middleware,
323
+ };
324
+ }
325
+ /** Delivers an Agent lifecycle event to active dynamic event capabilities. */
326
+ async emit(event, signal) {
327
+ if (event.type === "session_start")
328
+ this.sessionStartEvent = { ...event };
329
+ if (event.type === "session_shutdown")
330
+ this.sessionStartEvent = undefined;
331
+ for (const run of this.activeRuns()) {
332
+ await this.deliverEvent(run, event, signal);
333
+ }
334
+ }
335
+ async deliverEvent(run, event, signal) {
336
+ const capabilities = this.runtime.getRun(run.runId)?.snapshot().capabilities ?? [];
337
+ for (const capability of capabilities) {
338
+ if (capability.type !== "event" || capability.event !== event.type)
339
+ continue;
340
+ try {
341
+ await this.invoke(run, capability.id, "event", { event }, signal);
342
+ }
343
+ catch (cause) {
344
+ this.reportDiagnostic({
345
+ pluginId: run.pluginId,
346
+ runId: run.runId,
347
+ stage: "protocol",
348
+ message: safeMessage(cause),
349
+ });
350
+ }
351
+ }
352
+ }
353
+ activeRuns() {
354
+ return [...this.runs.values()].filter((run) => this.runtime.getRun(run.runId)?.state === "active");
355
+ }
356
+ createDynamicTool(run, capability) {
357
+ if (this.reservedToolNames.includes(capability.name))
358
+ throw new Error(`Dynamic plugin tool conflicts with host tool: ${capability.name}`);
359
+ return {
360
+ name: capability.name,
361
+ description: capability.description,
362
+ parameters: capability.parameters,
363
+ execute: async (toolCallId, parameters, signal) => {
364
+ const result = await this.invoke(run, capability.id, "tool", { toolCallId, parameters }, signal);
365
+ return parseAgentToolResult(result);
366
+ },
367
+ };
368
+ }
369
+ createDynamicMiddleware(run, capability) {
370
+ return async (execution, next) => {
371
+ const invokeId = `invoke-${Date.now()}-${++invocationSequence}`;
372
+ run.nexts.set(invokeId, async (payload) => {
373
+ const parameters = payload.parameters === undefined ? execution.parameters : payload.parameters;
374
+ return next({ ...execution, parameters });
375
+ });
376
+ try {
377
+ const value = await this.invoke(run, capability.id, "middleware", {
378
+ execution: {
379
+ toolCallId: execution.toolCallId,
380
+ toolName: execution.tool.name,
381
+ parameters: execution.parameters,
382
+ },
383
+ }, execution.signal, invokeId);
384
+ return parseAgentToolResult(value);
385
+ }
386
+ finally {
387
+ run.nexts.delete(invokeId);
388
+ }
389
+ };
390
+ }
391
+ async invoke(run, capabilityId, action, payload, signal, invokeId = `invoke-${Date.now()}-${++invocationSequence}`) {
392
+ if (signal?.aborted)
393
+ throw abortError();
394
+ if (run.settled || this.runtime.getRun(run.runId)?.state !== "active")
395
+ throw new Error("Dynamic plugin run is not active.");
396
+ const context = {
397
+ cwd: this.options.cwd,
398
+ mode: this.options.mode,
399
+ isProjectTrusted: this.options.projectTrusted === true,
400
+ model: this.options.model ?? "",
401
+ };
402
+ let rejectInvocation;
403
+ const result = new Promise((resolve, reject) => {
404
+ rejectInvocation = reject;
405
+ run.pending.set(invokeId, { resolve, reject, signal });
406
+ });
407
+ void result.catch(() => undefined);
408
+ const abort = () => {
409
+ run.pending.delete(invokeId);
410
+ void writeChildRecord(run.child, { version: 1, kind: "cancel", invokeId }).catch(() => undefined);
411
+ queueMicrotask(() => rejectInvocation(abortError()));
412
+ };
413
+ signal?.addEventListener("abort", abort, { once: true });
414
+ try {
415
+ await writeChildRecord(run.child, {
416
+ version: 1,
417
+ kind: "invoke",
418
+ invokeId,
419
+ capabilityId,
420
+ action,
421
+ context,
422
+ payload,
423
+ });
424
+ return await result;
425
+ }
426
+ finally {
427
+ signal?.removeEventListener("abort", abort);
428
+ run.pending.delete(invokeId);
429
+ }
430
+ }
431
+ async approve(pkg) {
432
+ if (this.options.mode !== "interactive" || this.options.allowDynamicPlugins !== true) {
433
+ this.reportDiagnostic({
434
+ pluginId: pkg.pluginId,
435
+ stage: "approval",
436
+ message: "Dynamic plugin execution is disabled outside an approved interactive run.",
437
+ });
438
+ throw new Error("Dynamic plugin execution is disabled; use interactive mode with --allow-dynamic-plugins.");
439
+ }
440
+ if (!this.options.confirmRun)
441
+ throw new Error("Dynamic plugin execution requires explicit user approval.");
442
+ const approved = await this.options.confirmRun({
443
+ pluginId: pkg.pluginId,
444
+ version: pkg.version,
445
+ capabilities: pkg.capabilities,
446
+ sourceHash: pkg.sourceHash,
447
+ sourceBytes: pkg.sourceBytes,
448
+ impact: "executes-session-code",
449
+ });
450
+ if (!approved)
451
+ throw new Error("Dynamic plugin execution was not approved.");
452
+ }
453
+ spawnChild() {
454
+ try {
455
+ return spawn(process.execPath, ["--input-type=module", "-e", BOOTSTRAP], {
456
+ cwd: this.options.cwd,
457
+ stdio: ["pipe", "pipe", "pipe"],
458
+ env: minimalEnvironment(),
459
+ });
460
+ }
461
+ catch (cause) {
462
+ this.reportDiagnostic({ stage: "spawn", message: safeMessage(cause) });
463
+ throw cause;
464
+ }
465
+ }
466
+ createRun(pkg, runId, child) {
467
+ let resolveReady;
468
+ let rejectReady;
469
+ let readyState = "pending";
470
+ const ready = new Promise((resolve, reject) => {
471
+ resolveReady = () => {
472
+ if (readyState !== "pending")
473
+ return;
474
+ readyState = "resolved";
475
+ resolve();
476
+ };
477
+ rejectReady = (cause) => {
478
+ if (readyState !== "pending")
479
+ return;
480
+ readyState = "rejected";
481
+ reject(cause);
482
+ };
483
+ });
484
+ let buffer = "";
485
+ let outputBytes = 0;
486
+ const decoder = new StringDecoder("utf8");
487
+ const run = {
488
+ packageId: pkg.id,
489
+ pluginId: pkg.pluginId,
490
+ runId,
491
+ child,
492
+ ready,
493
+ rejectReady,
494
+ settled: false,
495
+ readyConsumed: false,
496
+ pending: new Map(),
497
+ nexts: new Map(),
498
+ };
499
+ const timeout = setTimeout(() => {
500
+ const error = new Error("Dynamic plugin run timed out.");
501
+ run.cleanupPromise ??= this.fail(run, pkg.pluginId, error, "timeout");
502
+ }, pkg.limits.timeoutMs);
503
+ child.stdout.on("data", (chunk) => {
504
+ const text = typeof chunk === "string" ? chunk : decoder.write(chunk);
505
+ outputBytes += Buffer.byteLength(text, "utf8");
506
+ if (outputBytes > pkg.limits.maxOutputBytes) {
507
+ const error = new Error(`Dynamic plugin output exceeds ${pkg.limits.maxOutputBytes} bytes.`);
508
+ this.reportDiagnostic({ pluginId: pkg.pluginId, runId, stage: "protocol", message: error.message });
509
+ rejectReady(error);
510
+ this.kill(child);
511
+ return;
512
+ }
513
+ buffer += text;
514
+ if (Buffer.byteLength(buffer, "utf8") > DYNAMIC_PLUGIN_MAX_LINE_BYTES) {
515
+ this.reportDiagnostic({
516
+ pluginId: pkg.pluginId,
517
+ runId,
518
+ stage: "protocol",
519
+ message: "Dynamic plugin output line exceeds the size limit.",
520
+ });
521
+ rejectReady(new Error("Dynamic plugin protocol line exceeds the size limit."));
522
+ this.kill(child);
523
+ return;
524
+ }
525
+ while (true) {
526
+ const index = buffer.indexOf("\n");
527
+ if (index < 0)
528
+ break;
529
+ const line = buffer.slice(0, index);
530
+ buffer = buffer.slice(index + 1);
531
+ this.acceptChildRecord(pkg.snapshot(), runId, line, resolveReady, rejectReady);
532
+ }
533
+ });
534
+ child.stderr.on("data", (chunk) => this.reportDiagnostic({ pluginId: pkg.pluginId, runId, stage: "stderr", message: safeMessage(chunk) }));
535
+ child.stdin.on("error", (cause) => {
536
+ const error = new Error(safeMessage(cause));
537
+ run.cleanupPromise ??= this.fail(run, pkg.pluginId, error, "protocol");
538
+ });
539
+ child.once("error", (cause) => {
540
+ const error = new Error(safeMessage(cause));
541
+ run.cleanupPromise ??= this.fail(run, pkg.pluginId, error, "exit");
542
+ });
543
+ child.once("exit", (code, signal) => {
544
+ clearTimeout(timeout);
545
+ if (!run.settled) {
546
+ const error = new Error(`Dynamic plugin process exited unexpectedly (${signal ?? code ?? "code 0"}).`);
547
+ run.cleanupPromise ??= this.fail(run, pkg.pluginId, error, "exit");
548
+ }
549
+ });
550
+ void writeChild(child, {
551
+ kind: "bootstrap",
552
+ pluginId: pkg.pluginId,
553
+ runId,
554
+ source: Buffer.from(pkg.source, "utf8").toString("base64url"),
555
+ }).catch((cause) => {
556
+ const error = new Error(safeMessage(cause));
557
+ run.cleanupPromise ??= this.fail(run, pkg.pluginId, error, "protocol");
558
+ });
559
+ return run;
560
+ }
561
+ acceptChildRecord(pkg, runId, line, resolveReady, rejectReady) {
562
+ let record;
563
+ try {
564
+ if (Buffer.byteLength(line, "utf8") > DYNAMIC_PLUGIN_MAX_LINE_BYTES)
565
+ throw new Error("line exceeds size limit");
566
+ const parsed = JSON.parse(line);
567
+ if (parsed && typeof parsed === "object" && "method" in parsed) {
568
+ const capability = parseDynamicPluginCapabilityRecord(parsed);
569
+ if (capability.params.runId !== runId || capability.params.pluginId !== pkg.pluginId)
570
+ throw new Error("dynamic capability ownership mismatch");
571
+ const requiredCapability = capability.method === "capability_register"
572
+ ? declaredCapability(capability.params.capability.type)
573
+ : undefined;
574
+ if (requiredCapability !== undefined &&
575
+ !declaredCapabilityAliases(requiredCapability).some((name) => pkg.capabilities.includes(name)))
576
+ throw new Error(`dynamic capability "${requiredCapability}" was not declared by the package`);
577
+ if (capability.method === "capability_register" &&
578
+ capability.params.capability.type === "tool" &&
579
+ this.reservedToolNames.includes(capability.params.capability.name))
580
+ throw new Error(`Dynamic plugin tool conflicts with host tool: ${capability.params.capability.name}`);
581
+ if (capability.method === "capability_register")
582
+ this.runtime.registerCapability(runId, capability.params.capability);
583
+ else
584
+ this.runtime.revokeCapability(runId, capability.params.capabilityId);
585
+ return;
586
+ }
587
+ if (parsed &&
588
+ typeof parsed === "object" &&
589
+ "kind" in parsed &&
590
+ (parsed.kind === "invoke_result" || parsed.kind === "middleware_next")) {
591
+ const invocation = parseDynamicPluginChildInvocationRecord(parsed);
592
+ if (invocation.kind === "invoke_result")
593
+ this.resolveInvocation(runId, invocation);
594
+ else
595
+ this.resolveMiddlewareNext(runId, invocation);
596
+ return;
597
+ }
598
+ record = parsed;
599
+ if (record.kind !== "ready" && record.kind !== "stopped" && record.kind !== "error")
600
+ throw new Error("unknown child record");
601
+ }
602
+ catch (cause) {
603
+ const error = new Error(`Invalid dynamic plugin protocol: ${safeMessage(cause)}`);
604
+ this.reportDiagnostic({ pluginId: pkg.pluginId, runId, stage: "protocol", message: error.message });
605
+ rejectReady(error);
606
+ this.kill(this.runs.get(runId)?.child);
607
+ return;
608
+ }
609
+ if (record.kind === "ready")
610
+ resolveReady();
611
+ if (record.kind === "error") {
612
+ const message = safeMessage(record.message ?? "Dynamic plugin child failed.");
613
+ this.reportDiagnostic({ pluginId: pkg.pluginId, runId, stage: "exit", message });
614
+ rejectReady(new Error(message));
615
+ }
616
+ }
617
+ resolveInvocation(runId, record) {
618
+ if (record.kind !== "invoke_result")
619
+ throw new Error("invalid invocation result");
620
+ const run = this.runs.get(runId);
621
+ if (!run || record.invokeId === undefined)
622
+ throw new Error("unknown dynamic invocation");
623
+ const pending = run.pending.get(record.invokeId);
624
+ if (!pending)
625
+ return;
626
+ run.pending.delete(record.invokeId);
627
+ if (record.ok)
628
+ pending.resolve(record.result);
629
+ else
630
+ pending.reject(new Error(safeMessage(record.error ?? "Dynamic plugin invocation failed.")));
631
+ }
632
+ resolveMiddlewareNext(runId, record) {
633
+ if (record.kind !== "middleware_next")
634
+ throw new Error("invalid middleware next request");
635
+ const run = this.runs.get(runId);
636
+ if (!run)
637
+ throw new Error("unknown dynamic run");
638
+ const next = run.nexts.get(record.nextId) ?? run.nexts.get(record.invokeId);
639
+ if (!next)
640
+ throw new Error("unknown middleware next callback");
641
+ run.nexts.delete(record.nextId);
642
+ void next(record.execution)
643
+ .then((result) => writeChildRecord(run.child, {
644
+ version: 1,
645
+ kind: "middleware_next_result",
646
+ nextId: record.nextId,
647
+ ok: true,
648
+ result,
649
+ }))
650
+ .catch((cause) => writeChildRecord(run.child, {
651
+ version: 1,
652
+ kind: "middleware_next_result",
653
+ nextId: record.nextId,
654
+ ok: false,
655
+ error: safeMessage(cause),
656
+ }))
657
+ .catch((cause) => this.reportDiagnostic({ runId, stage: "protocol", message: safeMessage(cause) }));
658
+ }
659
+ fail(run, pluginId, cause, stage = "cleanup") {
660
+ run.cleanupPromise = (async () => {
661
+ const message = safeMessage(cause);
662
+ const wasStarting = this.runtime.getRun(run.runId)?.state === "starting";
663
+ if (wasStarting && !run.readyConsumed)
664
+ run.rejectReady(new Error(message));
665
+ this.reportDiagnostic({ pluginId, runId: run.runId, stage, message });
666
+ try {
667
+ this.runtime.fail(run.runId, message);
668
+ }
669
+ catch {
670
+ // Preserve the original failure when the runtime is already terminal.
671
+ }
672
+ for (const pending of run.pending.values())
673
+ pending.reject(new Error(message));
674
+ run.pending.clear();
675
+ run.nexts.clear();
676
+ run.settled = true;
677
+ this.kill(run.child);
678
+ await waitForExit(run.child, this.options.killGraceMs ?? 250);
679
+ this.runs.delete(run.runId);
680
+ if (this.current.get(pluginId) === run.runId)
681
+ this.current.delete(pluginId);
682
+ })();
683
+ return run.cleanupPromise;
684
+ }
685
+ stopRun(run, pluginId) {
686
+ run.settled = true;
687
+ if (this.runtime.getRun(run.runId)?.state === "starting" && !run.readyConsumed)
688
+ run.rejectReady(abortError());
689
+ return (async () => {
690
+ try {
691
+ this.runtime.handle({
692
+ version: 1,
693
+ id: `plugin-stop-${Date.now()}`,
694
+ method: "plugin_stop",
695
+ params: { runId: run.runId },
696
+ });
697
+ await writeChild(run.child, { kind: "stop" });
698
+ await waitForExit(run.child, this.options.killGraceMs ?? 250);
699
+ this.runtime.stop(run.runId);
700
+ }
701
+ catch (cause) {
702
+ const message = safeMessage(cause);
703
+ this.reportDiagnostic({ pluginId, runId: run.runId, stage: "cleanup", message });
704
+ this.runtime.fail(run.runId, message);
705
+ this.kill(run.child);
706
+ }
707
+ finally {
708
+ for (const pending of run.pending.values())
709
+ pending.reject(abortError());
710
+ run.pending.clear();
711
+ run.nexts.clear();
712
+ this.runs.delete(run.runId);
713
+ if (pluginId && this.current.get(pluginId) === run.runId)
714
+ this.current.delete(pluginId);
715
+ }
716
+ })();
717
+ }
718
+ kill(child) {
719
+ if (!child || child.killed)
720
+ return;
721
+ try {
722
+ child.kill();
723
+ }
724
+ catch (cause) {
725
+ this.reportDiagnostic({ stage: "cleanup", message: safeMessage(cause) });
726
+ }
727
+ }
728
+ reportDiagnostic(diagnostic) {
729
+ this.options.onDiagnostic?.(diagnostic);
730
+ }
731
+ assertOpen() {
732
+ if (this.disposed)
733
+ throw new Error("Dynamic plugin broker is disposed.");
734
+ }
735
+ }
736
+ function definitionSchema() {
737
+ return Type.Object({
738
+ pluginId: Type.String({ minLength: 1, maxLength: 64 }),
739
+ version: Type.String({ minLength: 1, maxLength: 128 }),
740
+ runtimeVersion: Type.String({ minLength: 1, maxLength: 64 }),
741
+ source: Type.String({ minLength: 1, maxLength: 1_048_576 }),
742
+ capabilities: Type.Array(Type.String({ minLength: 1, maxLength: 64 }), { maxItems: 64 }),
743
+ limits: Type.Optional(Type.Object({
744
+ timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
745
+ maxOutputBytes: Type.Optional(Type.Integer({ minimum: 1 })),
746
+ })),
747
+ });
748
+ }
749
+ function jsonResult(value) {
750
+ return [{ type: "text", text: JSON.stringify(value) }];
751
+ }
752
+ function parseAgentToolResult(value) {
753
+ const content = Array.isArray(value)
754
+ ? value
755
+ : value && typeof value === "object" && "content" in value
756
+ ? value.content
757
+ : undefined;
758
+ if (!Array.isArray(content))
759
+ throw new Error("Dynamic plugin tool result must contain an array of content blocks.");
760
+ for (const block of content) {
761
+ if (!block || typeof block !== "object" || block.type === undefined)
762
+ throw new Error("Dynamic plugin tool result contains an invalid content block.");
763
+ const type = block.type;
764
+ if (type === "text" && typeof block.text !== "string")
765
+ throw new Error("Dynamic plugin text result must contain text.");
766
+ if (type === "image" &&
767
+ (typeof block.data !== "string" ||
768
+ typeof block.mimeType !== "string"))
769
+ throw new Error("Dynamic plugin image result must contain data and mimeType.");
770
+ if (type !== "text" && type !== "image")
771
+ throw new Error("Dynamic plugin result contains an unsupported content type.");
772
+ }
773
+ return content;
774
+ }
775
+ function runSnapshot(runtime, runId) {
776
+ const run = runtime.getRun(runId);
777
+ if (!run)
778
+ throw new Error(`Unknown run: ${runId}`);
779
+ return run.snapshot();
780
+ }
781
+ function safeMessage(cause) {
782
+ const value = cause instanceof Error ? cause.message : String(cause);
783
+ return value
784
+ .replace(/(api[_-]?key|token|secret|authorization|password)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]")
785
+ .slice(0, 500);
786
+ }
787
+ function declaredCapability(type) {
788
+ return type === "tool" ? "tools" : type === "prompt" ? "prompts" : type === "middleware" ? "middleware" : "events";
789
+ }
790
+ function declaredCapabilityAliases(value) {
791
+ return value === "tools"
792
+ ? ["tools", "tool"]
793
+ : value === "prompts"
794
+ ? ["prompts", "prompt"]
795
+ : value === "middleware"
796
+ ? ["middleware", "middlewares"]
797
+ : ["events", "event"];
798
+ }
799
+ function abortError() {
800
+ const error = new Error("Dynamic plugin run was cancelled.");
801
+ error.name = "AbortError";
802
+ return error;
803
+ }
804
+ function minimalEnvironment() {
805
+ const env = {};
806
+ for (const key of ["PATH", "Path", "SystemRoot", "COMSPEC", "TEMP", "TMP"])
807
+ if (process.env[key])
808
+ env[key] = process.env[key];
809
+ return env;
810
+ }
811
+ function writeChild(child, record) {
812
+ const line = `${JSON.stringify(record)}\n`;
813
+ return new Promise((resolve, reject) => {
814
+ try {
815
+ if (child.stdin.destroyed || child.stdin.writableEnded) {
816
+ reject(new Error("Dynamic plugin child stdin is closed."));
817
+ return;
818
+ }
819
+ child.stdin.write(line, (error) => (error ? reject(error) : resolve()));
820
+ }
821
+ catch (cause) {
822
+ reject(cause);
823
+ }
824
+ });
825
+ }
826
+ const writeChildRecord = writeChild;
827
+ function waitForExit(child, graceMs) {
828
+ if (child.exitCode !== null || child.signalCode !== null)
829
+ return Promise.resolve();
830
+ return new Promise((resolve) => {
831
+ let done = false;
832
+ let forceTimer;
833
+ const finish = () => {
834
+ if (done)
835
+ return;
836
+ done = true;
837
+ clearTimeout(timer);
838
+ if (forceTimer)
839
+ clearTimeout(forceTimer);
840
+ resolve();
841
+ };
842
+ const timer = setTimeout(() => {
843
+ try {
844
+ child.kill();
845
+ }
846
+ catch { }
847
+ forceTimer = setTimeout(finish, Math.max(100, graceMs * 4));
848
+ }, graceMs);
849
+ child.once("close", finish);
850
+ });
851
+ }
852
+ //# sourceMappingURL=dynamic-broker.js.map