@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,994 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { redactSensitiveText } from "@di-code/plugin-runtime";
3
+ import { RPC_PROTOCOL_VERSION, RpcProtocolError, } from "./protocol.js";
4
+ function transcriptEntryIds(tree, leafId) {
5
+ if (!leafId)
6
+ return [];
7
+ const visit = (nodes, parents) => {
8
+ for (const node of nodes) {
9
+ const branch = [...parents, node];
10
+ if (node.entry.id === leafId)
11
+ return branch;
12
+ const found = visit(node.children, branch);
13
+ if (found)
14
+ return found;
15
+ }
16
+ return undefined;
17
+ };
18
+ return (visit(tree, []) ?? []).filter((node) => node.entry.type === "message").map((node) => node.entry.id);
19
+ }
20
+ class MemoryAttachmentStore {
21
+ attachments = new Map();
22
+ ttlMs;
23
+ maxCount;
24
+ maxBytes;
25
+ constructor(options) {
26
+ this.ttlMs = options.ttlMs;
27
+ this.maxCount = options.maxCount;
28
+ this.maxBytes = options.maxBytes;
29
+ }
30
+ async create(input) {
31
+ this.prune();
32
+ const currentBytes = [...this.attachments.values()].reduce((total, item) => total + item.info.bytes, 0);
33
+ if (this.attachments.size >= this.maxCount || currentBytes + input.bytes > this.maxBytes)
34
+ throw new RpcProtocolError("BUSY", "Attachment storage is full; consume or retry later.");
35
+ const info = {
36
+ id: input.id,
37
+ name: input.name,
38
+ contentType: input.contentType,
39
+ bytes: input.bytes,
40
+ };
41
+ this.attachments.set(input.id, {
42
+ info,
43
+ image: { type: "image", data: input.data, mimeType: input.contentType },
44
+ createdAt: Date.now(),
45
+ });
46
+ return info;
47
+ }
48
+ async take(ids) {
49
+ this.prune();
50
+ const result = [];
51
+ for (const id of ids) {
52
+ const attachment = this.attachments.get(id);
53
+ if (!attachment)
54
+ throw new RpcProtocolError("NOT_FOUND", "RPC attachment was not found.");
55
+ this.attachments.delete(id);
56
+ result.push(structuredClone(attachment.image));
57
+ }
58
+ return result;
59
+ }
60
+ async discard(ids) {
61
+ for (const id of ids)
62
+ this.attachments.delete(id);
63
+ }
64
+ async dispose() {
65
+ this.attachments.clear();
66
+ }
67
+ prune() {
68
+ const cutoff = Date.now() - this.ttlMs;
69
+ for (const [id, attachment] of this.attachments)
70
+ if (attachment.createdAt <= cutoff)
71
+ this.attachments.delete(id);
72
+ }
73
+ }
74
+ function errorFrom(cause) {
75
+ return cause instanceof Error ? cause : new Error(String(cause));
76
+ }
77
+ function sessionHost(value) {
78
+ return "state" in value && typeof value.state === "function";
79
+ }
80
+ function errorCode(cause) {
81
+ if (cause instanceof RpcProtocolError)
82
+ return cause.code;
83
+ if (cause instanceof Error && "code" in cause) {
84
+ const code = cause.code;
85
+ if (code === "BUSY" ||
86
+ code === "NOT_FOUND" ||
87
+ code === "CANCELLED" ||
88
+ code === "DISPOSED" ||
89
+ code === "INVALID_INPUT" ||
90
+ code === "SESSION_IN_USE" ||
91
+ code === "INVALID_WORKSPACE")
92
+ return code === "INVALID_INPUT" ? "INVALID_PARAMS" : code;
93
+ }
94
+ return "INTERNAL_ERROR";
95
+ }
96
+ function operationErrorMessage(cause, code) {
97
+ if (code === "CANCELLED")
98
+ return "The RPC operation was cancelled.";
99
+ const message = redactSensitiveText(errorFrom(cause).message).trim();
100
+ return message ? message.slice(0, 1_000) : "The RPC operation failed.";
101
+ }
102
+ /**
103
+ * Transport-free protocol dispatcher. It owns request idempotency, operation state, event ordering,
104
+ * and the bounded replay buffer; callers decide whether events go to JSONL, SSE, or another adapter.
105
+ */
106
+ export class RpcDispatcher {
107
+ session;
108
+ methods;
109
+ onError;
110
+ approvals = new Map();
111
+ maxEvents;
112
+ productState;
113
+ productHost;
114
+ attachmentTtlMs;
115
+ commandRegistry;
116
+ attachmentMaxCount;
117
+ attachmentMaxBytes;
118
+ operationTtlMs;
119
+ operationMaxCount;
120
+ listeners = new Set();
121
+ operations = new Map();
122
+ events = [];
123
+ attachments;
124
+ negotiatedEvents = new Set();
125
+ unsubscribe;
126
+ unsubscribeProduct;
127
+ disposed = false;
128
+ sequence = 0;
129
+ constructor(options) {
130
+ this.session = options.session;
131
+ this.methods = options.methods;
132
+ this.onError = options.onError ?? (() => undefined);
133
+ this.maxEvents = options.eventBufferSize ?? 256;
134
+ this.productState = options.productState ?? { projectTrusted: false };
135
+ this.productHost = options.productHost;
136
+ this.commandRegistry = options.commandRegistry;
137
+ this.attachmentTtlMs = options.attachmentTtlMs ?? 10 * 60 * 1000;
138
+ this.attachmentMaxCount = options.attachmentMaxCount ?? 32;
139
+ this.attachmentMaxBytes = options.attachmentMaxBytes ?? 64 * 1024 * 1024;
140
+ this.operationTtlMs = options.operationTtlMs ?? 10 * 60 * 1000;
141
+ this.operationMaxCount = options.operationMaxCount ?? 256;
142
+ this.attachments =
143
+ options.attachmentStore ??
144
+ new MemoryAttachmentStore({
145
+ ttlMs: this.attachmentTtlMs,
146
+ maxCount: this.attachmentMaxCount,
147
+ maxBytes: this.attachmentMaxBytes,
148
+ });
149
+ this.unsubscribe = sessionHost(options.session)
150
+ ? options.session.subscribe((event) => this.onSessionEvent(event))
151
+ : options.session.subscribeSession((event) => this.onSessionEvent(event));
152
+ if (this.productHost) {
153
+ this.unsubscribeProduct = this.productHost.subscribe((event) => {
154
+ if (!this.negotiatedEvents.has("product_audit"))
155
+ return;
156
+ this.emit({
157
+ version: RPC_PROTOCOL_VERSION,
158
+ kind: "event",
159
+ requestId: "product",
160
+ sessionId: this.activeSessionId(),
161
+ sequence: ++this.sequence,
162
+ event,
163
+ });
164
+ });
165
+ }
166
+ }
167
+ subscribe(listener) {
168
+ this.listeners.add(listener);
169
+ return () => this.listeners.delete(listener);
170
+ }
171
+ async dispatch(request) {
172
+ this.pruneOperations();
173
+ if (this.disposed)
174
+ return this.failure(request.id, "DISPOSED", "RPC dispatcher has been disposed.");
175
+ if (this.methods && !this.methods.has(request.method))
176
+ return this.failure(request.id, "METHOD_NOT_FOUND", "RPC method is not registered for this server.");
177
+ const existing = this.operations.get(request.id);
178
+ if (existing?.promise)
179
+ return await existing.promise;
180
+ if (request.method === "prompt" &&
181
+ [...this.operations.values()].some((operation) => operation.kind === "prompt" && operation.status === "running"))
182
+ return this.failure(request.id, "BUSY", "The RPC session is already processing a prompt.");
183
+ if (this.isOperation(request.method)) {
184
+ const operation = {
185
+ requestId: request.id,
186
+ kind: request.method,
187
+ controller: new AbortController(),
188
+ sessionId: this.activeSessionId(),
189
+ status: "queued",
190
+ };
191
+ this.operations.set(request.id, operation);
192
+ operation.promise = this.runOperation(operation, request);
193
+ return await operation.promise;
194
+ }
195
+ return await this.execute(request);
196
+ }
197
+ async dispose() {
198
+ if (this.disposed)
199
+ return;
200
+ this.disposed = true;
201
+ this.unsubscribe();
202
+ this.unsubscribeProduct?.();
203
+ for (const operation of this.operations.values())
204
+ if (operation.status === "queued" || operation.status === "running")
205
+ operation.controller.abort();
206
+ await Promise.allSettled([...this.operations.values()].map((operation) => operation.promise));
207
+ await this.attachments.dispose();
208
+ this.listeners.clear();
209
+ }
210
+ async execute(request) {
211
+ try {
212
+ switch (request.method) {
213
+ case "get_state":
214
+ return this.success(request.id, { method: "get_state", state: this.state() });
215
+ case "get_capabilities":
216
+ return this.capabilities(request);
217
+ case "resume_events":
218
+ return this.resumeEvents(request);
219
+ case "cancel":
220
+ return this.cancel(request);
221
+ case "get_operation":
222
+ return this.getOperation(request);
223
+ case "list_sessions":
224
+ return this.listSessions(request);
225
+ case "new_session":
226
+ return this.newSession(request);
227
+ case "open_session":
228
+ return this.openSession(request);
229
+ case "inspect_session":
230
+ return this.inspectSession(request);
231
+ case "rename_session":
232
+ return this.renameSession(request);
233
+ case "delete_session":
234
+ return this.deleteSession(request);
235
+ case "branch_session":
236
+ return this.branchSession(request);
237
+ case "get_transcript":
238
+ return this.transcript(request);
239
+ case "get_tree":
240
+ return this.success(request.id, { method: "get_tree", tree: this.host().tree() });
241
+ case "get_models":
242
+ return this.success(request.id, { method: "get_models", models: this.host().ui().availableModels });
243
+ case "get_runtime": {
244
+ const ui = this.host().ui();
245
+ return this.success(request.id, {
246
+ method: "get_runtime",
247
+ providerId: ui.providerId,
248
+ modelId: ui.modelId,
249
+ thinkingLevel: ui.thinkingLevel,
250
+ });
251
+ }
252
+ case "set_compaction_enabled":
253
+ return this.success(request.id, {
254
+ method: "set_compaction_enabled",
255
+ enabled: this.host().setCompactionEnabled(request.params.enabled),
256
+ });
257
+ case "get_usage":
258
+ return this.success(request.id, { method: "get_usage", usage: this.host().usage() });
259
+ case "list_skills":
260
+ return this.success(request.id, {
261
+ method: "list_skills",
262
+ skills: this.host()
263
+ .ui()
264
+ .availableSkills.map(({ name, description, scope }) => ({ name, description, scope })),
265
+ });
266
+ case "get_resources":
267
+ return this.success(request.id, {
268
+ method: "get_resources",
269
+ models: this.host().ui().availableModels,
270
+ skills: this.host()
271
+ .ui()
272
+ .availableSkills.map(({ name, description, scope }) => ({ name, description, scope })),
273
+ });
274
+ case "get_product_state":
275
+ return this.success(request.id, {
276
+ method: "get_product_state",
277
+ state: this.productHost ? this.productHost.state() : this.productState,
278
+ });
279
+ case "get_project_trust":
280
+ return this.success(request.id, {
281
+ method: "get_project_trust",
282
+ trusted: this.productHost?.getProjectTrust() ?? this.productState.projectTrusted,
283
+ });
284
+ case "create_attachment":
285
+ return await this.createAttachment(request);
286
+ case "list_providers":
287
+ return this.success(request.id, {
288
+ method: "list_providers",
289
+ providers: this.productHost?.listProviders() ?? [],
290
+ });
291
+ case "get_settings":
292
+ return this.success(request.id, { method: "get_settings", settings: this.requireProduct().getSettings() });
293
+ case "list_plugins":
294
+ return this.success(request.id, {
295
+ method: "list_plugins",
296
+ plugins: await this.requireProduct().listPlugins(),
297
+ });
298
+ case "list_web_contributions":
299
+ return this.success(request.id, {
300
+ method: "list_web_contributions",
301
+ manifest: await this.requireProduct().getWebContributions(),
302
+ });
303
+ case "list_commands":
304
+ return this.success(request.id, { method: "list_commands", commands: this.listCommands() });
305
+ case "list_context_files":
306
+ case "list_mcp_servers":
307
+ case "login":
308
+ case "logout":
309
+ case "set_default_provider":
310
+ case "set_default_model":
311
+ case "set_locale":
312
+ case "set_permission_mode":
313
+ case "configure_custom_provider":
314
+ case "set_project_trust":
315
+ case "configure_mcp_server":
316
+ case "remove_mcp_server":
317
+ case "reconnect_mcp_server":
318
+ case "set_plugin_enabled":
319
+ return this.failure(request.id, "METHOD_NOT_FOUND", "Product configuration requests require operation dispatch.");
320
+ case "approve_tool":
321
+ {
322
+ const approvalId = request.params.approvalId;
323
+ const pending = this.approvals.get(approvalId);
324
+ if (pending) {
325
+ this.approvals.delete(approvalId);
326
+ pending.resolve(request.params.approved === true);
327
+ }
328
+ }
329
+ return this.success(request.id, {
330
+ method: "approve_tool",
331
+ approvalId: request.params.approvalId,
332
+ approved: request.params.approved,
333
+ });
334
+ case "run_command":
335
+ return this.failure(request.id, "METHOD_NOT_FOUND", "Command execution requires operation dispatch.");
336
+ default:
337
+ return this.failure(request.id, "METHOD_NOT_FOUND", "RPC method is unavailable for this Host.");
338
+ }
339
+ }
340
+ catch (cause) {
341
+ return this.failure(request.id, errorCode(cause), "RPC request failed.");
342
+ }
343
+ }
344
+ /** Waits for the browser to approve a specific tool invocation. */
345
+ async requestToolApproval(toolName, parameters, signal) {
346
+ if (!this.negotiatedEvents.has("tool_approval"))
347
+ return true;
348
+ const approvalId = randomUUID();
349
+ const requestId = [...this.operations.values()].find((operation) => operation.status === "running")?.requestId ?? approvalId;
350
+ return await new Promise((resolve, reject) => {
351
+ const abort = () => {
352
+ this.approvals.delete(approvalId);
353
+ reject(new Error("Tool approval cancelled."));
354
+ };
355
+ if (signal?.aborted)
356
+ return abort();
357
+ signal?.addEventListener("abort", abort, { once: true });
358
+ this.approvals.set(approvalId, {
359
+ resolve: (approved) => {
360
+ signal?.removeEventListener("abort", abort);
361
+ resolve(approved);
362
+ },
363
+ });
364
+ this.emit({
365
+ version: RPC_PROTOCOL_VERSION,
366
+ kind: "event",
367
+ requestId,
368
+ sessionId: this.activeSessionId(),
369
+ sequence: ++this.sequence,
370
+ event: {
371
+ type: "tool_approval",
372
+ approvalId,
373
+ toolName,
374
+ arguments: typeof parameters === "object" && parameters !== null ? parameters : {},
375
+ },
376
+ });
377
+ });
378
+ }
379
+ async runOperation(operation, request) {
380
+ operation.status = "running";
381
+ this.emitOperation(operation);
382
+ try {
383
+ let result;
384
+ switch (request.method) {
385
+ case "prompt": {
386
+ const message = await this.prompt(request.params.message, request.params, operation);
387
+ operation.message = message;
388
+ result = { method: "prompt", message };
389
+ break;
390
+ }
391
+ case "steer": {
392
+ const images = await this.takeAttachments(request.params);
393
+ if (images.length === 0) {
394
+ await this.host().steer({ text: request.params.message, requestId: request.id }, operation.controller.signal);
395
+ }
396
+ else {
397
+ await this.host().steerWithImages(request.params.message, images, operation.controller.signal);
398
+ }
399
+ result = { method: "steer" };
400
+ break;
401
+ }
402
+ case "retry": {
403
+ const message = await this.host().retry({ targetRequestId: request.params.targetRequestId }, operation.controller.signal);
404
+ operation.message = message;
405
+ result = { method: "retry", message };
406
+ break;
407
+ }
408
+ case "compact":
409
+ await this.host().compact(operation.controller.signal);
410
+ result = { method: "compact" };
411
+ break;
412
+ case "run_command":
413
+ result = await this.runCommand(request, operation.controller.signal);
414
+ break;
415
+ case "navigate_tree":
416
+ result = {
417
+ method: "navigate_tree",
418
+ navigation: await this.host().navigateTree(request.params.entryId),
419
+ };
420
+ break;
421
+ case "set_model":
422
+ result = { method: "set_model", model: this.host().setModel(request.params.modelId) };
423
+ break;
424
+ case "set_runtime": {
425
+ this.assertProductIdle();
426
+ const providerId = request.params.providerId;
427
+ const modelId = request.params.modelId;
428
+ await this.requireProduct().setRuntimePreference(providerId, modelId);
429
+ const runtime = this.host().ui();
430
+ const model = runtime.availableModels.find((candidate) => candidate.id === runtime.modelId);
431
+ if (!model)
432
+ throw new Error("The selected runtime was not applied to the active Session.");
433
+ result = {
434
+ method: "set_runtime",
435
+ model,
436
+ };
437
+ break;
438
+ }
439
+ case "set_thinking_level": {
440
+ this.assertProductIdle();
441
+ const ui = this.host().ui();
442
+ const model = ui.availableModels.find((candidate) => candidate.id === ui.modelId);
443
+ const efforts = model?.reasoningEfforts ?? [];
444
+ const requested = request.params.level;
445
+ const next = requested ??
446
+ (efforts.length === 0
447
+ ? undefined
448
+ : efforts[((ui.thinkingLevel === undefined ? -1 : efforts.indexOf(ui.thinkingLevel)) + 1) % efforts.length]);
449
+ if (next !== undefined)
450
+ await this.requireProduct().setThinkingLevelPreference(ui.providerId, ui.modelId, next);
451
+ result = {
452
+ method: "set_thinking_level",
453
+ level: next === undefined ? this.host().cycleThinkingLevel() : this.host().setThinkingLevel(next),
454
+ };
455
+ break;
456
+ }
457
+ case "list_context_files":
458
+ result = {
459
+ method: "list_context_files",
460
+ files: await this.requireProduct().listContextFiles(operation.controller.signal),
461
+ };
462
+ break;
463
+ case "list_mcp_servers":
464
+ result = {
465
+ method: "list_mcp_servers",
466
+ servers: await this.requireProduct().listMcpServers(operation.controller.signal),
467
+ };
468
+ break;
469
+ case "login":
470
+ this.assertProductIdle();
471
+ result = {
472
+ method: "login",
473
+ provider: await this.requireProduct().login({
474
+ providerId: request.params.providerId,
475
+ apiKey: request.params.apiKey,
476
+ modelId: request.params.modelId,
477
+ api: request.params.api,
478
+ }, operation.controller.signal),
479
+ };
480
+ break;
481
+ case "logout":
482
+ this.assertProductIdle();
483
+ await this.requireProduct().logout(request.params.providerId, operation.controller.signal);
484
+ result = { method: "logout" };
485
+ break;
486
+ case "set_default_provider":
487
+ this.assertProductIdle();
488
+ await this.requireProduct().setDefaultProvider(request.params.providerId);
489
+ result = { method: "set_default_provider" };
490
+ break;
491
+ case "set_default_model":
492
+ this.assertProductIdle();
493
+ await this.requireProduct().setDefaultModel(request.params.modelId);
494
+ result = { method: "set_default_model" };
495
+ break;
496
+ case "set_locale":
497
+ this.assertProductIdle();
498
+ await this.requireProduct().setLocale(request.params.locale);
499
+ result = { method: "set_locale" };
500
+ break;
501
+ case "set_permission_mode":
502
+ this.assertProductIdle();
503
+ await this.requireProduct().setPermissionMode(request.params.permissionMode);
504
+ result = { method: "set_permission_mode" };
505
+ break;
506
+ case "configure_custom_provider":
507
+ this.assertProductIdle();
508
+ result = {
509
+ method: "configure_custom_provider",
510
+ provider: await this.requireProduct().configureCustomProvider({
511
+ api: request.params.api,
512
+ baseUrl: request.params.baseUrl,
513
+ apiKey: request.params.apiKey,
514
+ modelId: request.params.modelId,
515
+ }, operation.controller.signal),
516
+ };
517
+ break;
518
+ case "set_project_trust": {
519
+ this.assertProductIdle();
520
+ const trusted = await this.requireProduct().setProjectTrust(request.params.trusted, operation.controller.signal);
521
+ this.productState = { projectTrusted: trusted };
522
+ result = { method: "set_project_trust", trusted };
523
+ break;
524
+ }
525
+ case "configure_mcp_server":
526
+ this.assertProductIdle();
527
+ result = {
528
+ method: "configure_mcp_server",
529
+ server: await this.requireProduct().configureMcpServer({
530
+ serverId: request.params.serverId,
531
+ scope: request.params.scope,
532
+ config: request.params.config,
533
+ }, operation.controller.signal),
534
+ };
535
+ break;
536
+ case "remove_mcp_server":
537
+ this.assertProductIdle();
538
+ await this.requireProduct().removeMcpServer(request.params.serverId, request.params.scope ?? "project", operation.controller.signal);
539
+ result = { method: "remove_mcp_server" };
540
+ break;
541
+ case "reconnect_mcp_server":
542
+ this.assertProductIdle();
543
+ result = {
544
+ method: "reconnect_mcp_server",
545
+ server: await this.requireProduct().reconnectMcpServer(request.params.serverId, operation.controller.signal),
546
+ };
547
+ break;
548
+ case "set_plugin_enabled":
549
+ this.assertProductIdle();
550
+ result = {
551
+ method: "set_plugin_enabled",
552
+ plugin: await this.requireProduct().setPluginEnabled(request.params.pluginId, request.params.enabled, operation.controller.signal),
553
+ };
554
+ break;
555
+ default:
556
+ return this.failure(request.id, "METHOD_NOT_FOUND", "RPC method is unavailable for this Host.");
557
+ }
558
+ operation.status = "completed";
559
+ operation.completedAt = Date.now();
560
+ this.emitOperation(operation);
561
+ this.pruneOperations();
562
+ return this.success(request.id, result);
563
+ }
564
+ catch (cause) {
565
+ const code = operation.controller.signal.aborted ? "CANCELLED" : errorCode(cause);
566
+ operation.status = code === "CANCELLED" ? "cancelled" : "failed";
567
+ operation.completedAt = Date.now();
568
+ operation.error = {
569
+ code,
570
+ message: operationErrorMessage(cause, code),
571
+ };
572
+ this.emitOperation(operation);
573
+ this.pruneOperations();
574
+ if (code === "INTERNAL_ERROR")
575
+ this.onError(errorFrom(cause));
576
+ return this.failure(request.id, operation.error.code, operation.error.message);
577
+ }
578
+ finally {
579
+ await this.discardAttachments(request.params);
580
+ }
581
+ }
582
+ transcript(request) {
583
+ const all = this.host().transcript();
584
+ const entryIds = transcriptEntryIds(this.host().tree(), this.host().ui().sessionLeafId);
585
+ if (entryIds.length !== all.length)
586
+ throw new RpcProtocolError("INTERNAL_ERROR", "Session transcript entries are inconsistent.");
587
+ const start = request.params.pageToken === undefined ? 0 : Number.parseInt(request.params.pageToken, 10);
588
+ if (!Number.isSafeInteger(start) || start < 0 || start > all.length)
589
+ return this.failure(request.id, "INVALID_PARAMS", "Invalid transcript page token.");
590
+ const pageSize = request.params.pageSize ?? 100;
591
+ const maxBytes = request.params.maxBytes ?? 1_000_000;
592
+ const transcript = [];
593
+ const pageEntryIds = [];
594
+ let bytes = 2;
595
+ for (let index = start; index < all.length && transcript.length < pageSize; index++) {
596
+ const value = all[index];
597
+ if (value === undefined)
598
+ break;
599
+ const size = Buffer.byteLength(JSON.stringify(value), "utf8");
600
+ if (size > maxBytes && transcript.length === 0)
601
+ return this.failure(request.id, "INVALID_PARAMS", "Transcript entry exceeds maxBytes.");
602
+ if (bytes + size > maxBytes)
603
+ break;
604
+ transcript.push(value);
605
+ pageEntryIds.push(entryIds[index] ?? "");
606
+ bytes += size;
607
+ }
608
+ const next = start + transcript.length < all.length ? String(start + transcript.length) : undefined;
609
+ return this.success(request.id, {
610
+ method: "get_transcript",
611
+ transcript,
612
+ entryIds: pageEntryIds,
613
+ ...(next ? { nextPageToken: next } : {}),
614
+ });
615
+ }
616
+ requireProduct() {
617
+ if (!this.productHost)
618
+ throw new RpcProtocolError("METHOD_NOT_FOUND", "ProductHost is unavailable.");
619
+ return this.productHost;
620
+ }
621
+ assertProductIdle() {
622
+ if (sessionHost(this.session) && this.session.state().busy)
623
+ throw new RpcProtocolError("BUSY", "Product configuration cannot change while the Session is busy.");
624
+ }
625
+ async discardAttachments(params) {
626
+ const ids = params.attachmentIds;
627
+ if (!Array.isArray(ids))
628
+ return;
629
+ await this.attachments.discard(ids.filter((id) => typeof id === "string"));
630
+ }
631
+ async prompt(message, params, operation) {
632
+ const images = await this.takeAttachments(params);
633
+ if (sessionHost(this.session)) {
634
+ if (images.length > 0)
635
+ return await this.session.promptWithImages(message, images, operation.controller.signal);
636
+ return await this.session.prompt({ text: message, requestId: operation.requestId }, operation.controller.signal);
637
+ }
638
+ if (images.length > 0)
639
+ throw new RpcProtocolError("METHOD_NOT_FOUND", "RPC attachments require a SessionHost.");
640
+ return await this.session.prompt(message, operation.controller.signal);
641
+ }
642
+ async createAttachment(request) {
643
+ const data = request.params.data;
644
+ if (Buffer.from(data, "base64").toString("base64") !== data)
645
+ return this.failure(request.id, "INVALID_PARAMS", "Attachment data must be canonical base64.");
646
+ const bytes = Buffer.byteLength(data, "base64");
647
+ if (bytes === 0 || bytes > 5 * 1024 * 1024)
648
+ return this.failure(request.id, "INVALID_PARAMS", "Attachment data exceeds the permitted size.");
649
+ const contentType = request.params.contentType;
650
+ const info = await this.attachments.create({
651
+ id: randomUUID(),
652
+ name: request.params.name,
653
+ contentType,
654
+ data,
655
+ bytes,
656
+ });
657
+ return this.success(request.id, { method: "create_attachment", attachment: info });
658
+ }
659
+ async takeAttachments(params) {
660
+ const ids = params.attachmentIds;
661
+ if (!ids || ids.length === 0)
662
+ return [];
663
+ return await this.attachments.take(ids);
664
+ }
665
+ host() {
666
+ if (!sessionHost(this.session))
667
+ throw new RpcProtocolError("METHOD_NOT_FOUND", "RPC method requires a SessionHost.");
668
+ return this.session;
669
+ }
670
+ state() {
671
+ if (sessionHost(this.session)) {
672
+ const state = this.session.state();
673
+ const ui = state.activeSession ? this.session.ui() : undefined;
674
+ return {
675
+ sessionId: state.activeSession?.id ?? "uninitialized",
676
+ modelId: ui?.modelId ?? "uninitialized",
677
+ isStreaming: state.busy,
678
+ messageCount: state.activeSession ? this.session.transcript().length : 0,
679
+ sequence: this.sequence,
680
+ };
681
+ }
682
+ return {
683
+ sessionId: this.session.sessionId,
684
+ modelId: this.session.modelId,
685
+ isStreaming: this.session.isStreaming,
686
+ messageCount: this.session.transcript.length,
687
+ sequence: this.sequence,
688
+ };
689
+ }
690
+ activeSessionId() {
691
+ return sessionHost(this.session) ? this.session.state().activeSession?.id : this.session.sessionId;
692
+ }
693
+ isOperation(method) {
694
+ return [
695
+ "prompt",
696
+ "steer",
697
+ "retry",
698
+ "compact",
699
+ "run_command",
700
+ "navigate_tree",
701
+ "set_model",
702
+ "set_runtime",
703
+ "set_thinking_level",
704
+ "list_context_files",
705
+ "list_mcp_servers",
706
+ "login",
707
+ "logout",
708
+ "set_default_provider",
709
+ "set_default_model",
710
+ "set_locale",
711
+ "set_permission_mode",
712
+ "configure_custom_provider",
713
+ "set_project_trust",
714
+ "configure_mcp_server",
715
+ "remove_mcp_server",
716
+ "reconnect_mcp_server",
717
+ "set_plugin_enabled",
718
+ ].includes(method);
719
+ }
720
+ listCommands() {
721
+ const locale = this.productHost?.getSettings().locale ?? "en";
722
+ const commands = (this.commandRegistry?.list() ?? []).map((command) => ({
723
+ name: command.name,
724
+ description: typeof command.description === "function" ? command.description(locale) : command.description,
725
+ kind: "command",
726
+ }));
727
+ const skills = (sessionHost(this.session) ? this.session.ui().availableSkills : []).map((skill) => ({
728
+ name: `skill:${skill.name}`,
729
+ description: skill.description,
730
+ kind: "skill",
731
+ }));
732
+ return [...commands, ...skills].sort((left, right) => left.name.localeCompare(right.name));
733
+ }
734
+ async runCommand(request, signal) {
735
+ const name = request.params.name;
736
+ const args = request.params.args ?? "";
737
+ if (!this.commandRegistry?.list().some((command) => command.name === name))
738
+ throw new RpcProtocolError("NOT_FOUND", `Unknown command: /${name}`, request.id);
739
+ let action;
740
+ await this.commandRegistry.execute(name, {
741
+ args,
742
+ host: {
743
+ runCommand: (command, commandArgs) => {
744
+ action = { command, args: commandArgs };
745
+ return 0;
746
+ },
747
+ },
748
+ }, signal);
749
+ return { method: "run_command", command: name, ...(action ? { action } : {}) };
750
+ }
751
+ success(id, result) {
752
+ return { version: RPC_PROTOCOL_VERSION, kind: "response", id, ok: true, result };
753
+ }
754
+ failure(id, code, message) {
755
+ return { version: RPC_PROTOCOL_VERSION, kind: "response", id, ok: false, error: { code, message } };
756
+ }
757
+ capabilities(request) {
758
+ const events = request.params.events;
759
+ if (Array.isArray(events))
760
+ for (const event of events)
761
+ if (typeof event === "string" &&
762
+ [
763
+ "sequence",
764
+ "operation_update",
765
+ "snapshot_required",
766
+ "session_changed",
767
+ "tool_approval",
768
+ "product_audit",
769
+ ].includes(event))
770
+ this.negotiatedEvents.add(event);
771
+ return this.success(request.id, {
772
+ method: "get_capabilities",
773
+ methods: RPC_METHODS_FOR_CAPABILITIES,
774
+ events: [...this.negotiatedEvents],
775
+ eventBufferSize: this.maxEvents,
776
+ });
777
+ }
778
+ resumeEvents(request) {
779
+ const last = request.params.lastSequence;
780
+ if (!this.negotiatedEvents.has("sequence"))
781
+ return this.failure(request.id, "INVALID_PARAMS", "Extended events require capability negotiation.");
782
+ if (last !== undefined && this.events.length > 0 && last < (this.events[0]?.sequence ?? 0) - 1) {
783
+ this.emit({
784
+ version: RPC_PROTOCOL_VERSION,
785
+ kind: "event",
786
+ requestId: request.id,
787
+ sessionId: this.activeSessionId(),
788
+ sequence: ++this.sequence,
789
+ event: { type: "snapshot_required" },
790
+ });
791
+ return this.success(request.id, { method: "resume_events", snapshotRequired: true });
792
+ }
793
+ for (const event of this.events)
794
+ if ((event.sequence ?? 0) > (last ?? 0))
795
+ this.emit(event);
796
+ return this.success(request.id, { method: "resume_events", snapshotRequired: false });
797
+ }
798
+ cancel(request) {
799
+ const target = request.params.requestId;
800
+ const operation = this.operations.get(target);
801
+ const cancelled = operation !== undefined && (operation.status === "queued" || operation.status === "running");
802
+ if (cancelled) {
803
+ operation.controller.abort();
804
+ if (sessionHost(this.session))
805
+ this.session.cancel(target);
806
+ }
807
+ return this.success(request.id, { method: "cancel", cancelled });
808
+ }
809
+ getOperation(request) {
810
+ this.pruneOperations();
811
+ const operation = this.operations.get(request.params.requestId);
812
+ return operation
813
+ ? this.success(request.id, { method: "get_operation", operation: this.operationState(operation) })
814
+ : this.failure(request.id, "NOT_FOUND", "RPC operation was not found.");
815
+ }
816
+ async listSessions(request) {
817
+ return this.success(request.id, {
818
+ method: "list_sessions",
819
+ sessions: await this.host()
820
+ .listSessions()
821
+ .then((items) => items.map(({ id, label, modifiedAt, stats }) => ({ id, label, modifiedAt, ...(stats ? { stats } : {}) }))),
822
+ });
823
+ }
824
+ async newSession(request) {
825
+ const session = await this.host().createSession();
826
+ return this.success(request.id, {
827
+ method: "new_session",
828
+ session: {
829
+ id: session.id,
830
+ label: session.label,
831
+ modifiedAt: session.modifiedAt,
832
+ ...(session.stats ? { stats: session.stats } : {}),
833
+ },
834
+ });
835
+ }
836
+ async openSession(request) {
837
+ const session = await this.host().openSession(request.params.sessionId);
838
+ return this.success(request.id, {
839
+ method: "open_session",
840
+ session: {
841
+ id: session.id,
842
+ label: session.label,
843
+ modifiedAt: session.modifiedAt,
844
+ ...(session.stats ? { stats: session.stats } : {}),
845
+ },
846
+ });
847
+ }
848
+ async inspectSession(request) {
849
+ const snapshot = await this.host().inspectSession(request.params.sessionId);
850
+ return this.success(request.id, {
851
+ method: "inspect_session",
852
+ snapshot: {
853
+ session: {
854
+ id: snapshot.session.id,
855
+ label: snapshot.session.label,
856
+ modifiedAt: snapshot.session.modifiedAt,
857
+ ...(snapshot.session.stats ? { stats: snapshot.session.stats } : {}),
858
+ },
859
+ transcript: snapshot.transcript,
860
+ tree: snapshot.tree,
861
+ stats: snapshot.stats,
862
+ readOnly: true,
863
+ },
864
+ });
865
+ }
866
+ async renameSession(request) {
867
+ const session = await this.host().renameSession(request.params.sessionId, request.params.label);
868
+ return this.success(request.id, { method: "rename_session", session });
869
+ }
870
+ async deleteSession(request) {
871
+ await this.host().deleteSession(request.params.sessionId, request.params.confirmation);
872
+ return this.success(request.id, { method: "delete_session" });
873
+ }
874
+ async branchSession(request) {
875
+ const session = await this.host().branchSession(request.params.sessionId, request.params.entryId);
876
+ return this.success(request.id, { method: "branch_session", session });
877
+ }
878
+ operationState(operation) {
879
+ return {
880
+ requestId: operation.requestId,
881
+ kind: operation.kind,
882
+ status: operation.status,
883
+ ...(operation.sessionId ? { sessionId: operation.sessionId } : {}),
884
+ ...(operation.message ? { message: operation.message } : {}),
885
+ ...(operation.error ? { error: operation.error } : {}),
886
+ };
887
+ }
888
+ pruneOperations() {
889
+ const cutoff = Date.now() - this.operationTtlMs;
890
+ for (const [id, operation] of this.operations)
891
+ if (operation.completedAt !== undefined && operation.completedAt <= cutoff)
892
+ this.operations.delete(id);
893
+ const retained = [...this.operations.values()]
894
+ .filter((operation) => operation.completedAt !== undefined)
895
+ .sort((left, right) => (left.completedAt ?? 0) - (right.completedAt ?? 0));
896
+ while (retained.length > this.operationMaxCount) {
897
+ const operation = retained.shift();
898
+ if (operation)
899
+ this.operations.delete(operation.requestId);
900
+ }
901
+ }
902
+ emitOperation(operation) {
903
+ if (!this.negotiatedEvents.has("operation_update"))
904
+ return;
905
+ this.emit({
906
+ version: RPC_PROTOCOL_VERSION,
907
+ kind: "event",
908
+ requestId: operation.requestId,
909
+ sessionId: operation.sessionId,
910
+ sequence: ++this.sequence,
911
+ event: { type: "operation_update", operation: this.operationState(operation) },
912
+ });
913
+ }
914
+ onSessionEvent(event) {
915
+ const operation = [...this.operations.values()].find((item) => item.status === "running");
916
+ const extended = event.type === "session_changed";
917
+ if (extended && !this.negotiatedEvents.has("session_changed"))
918
+ return;
919
+ const record = {
920
+ version: RPC_PROTOCOL_VERSION,
921
+ kind: "event",
922
+ requestId: operation?.requestId ?? "session",
923
+ event,
924
+ ...(this.negotiatedEvents.has("sequence")
925
+ ? { sessionId: this.activeSessionId(), sequence: ++this.sequence }
926
+ : {}),
927
+ };
928
+ if (this.negotiatedEvents.has("sequence")) {
929
+ this.events.push(record);
930
+ if (this.events.length > this.maxEvents)
931
+ this.events.shift();
932
+ }
933
+ this.emit(record);
934
+ }
935
+ emit(message) {
936
+ for (const listener of this.listeners) {
937
+ try {
938
+ listener(structuredClone(message));
939
+ }
940
+ catch (cause) {
941
+ this.onError(errorFrom(cause));
942
+ }
943
+ }
944
+ }
945
+ }
946
+ const RPC_METHODS_FOR_CAPABILITIES = [
947
+ "get_state",
948
+ "get_capabilities",
949
+ "resume_events",
950
+ "list_sessions",
951
+ "new_session",
952
+ "open_session",
953
+ "get_transcript",
954
+ "get_tree",
955
+ "navigate_tree",
956
+ "inspect_session",
957
+ "rename_session",
958
+ "delete_session",
959
+ "branch_session",
960
+ "prompt",
961
+ "steer",
962
+ "retry",
963
+ "cancel",
964
+ "get_operation",
965
+ "get_models",
966
+ "set_model",
967
+ "get_runtime",
968
+ "set_runtime",
969
+ "set_thinking_level",
970
+ "compact",
971
+ "set_compaction_enabled",
972
+ "get_usage",
973
+ "list_skills",
974
+ "get_resources",
975
+ "get_product_state",
976
+ "list_providers",
977
+ "get_settings",
978
+ "set_default_provider",
979
+ "set_default_model",
980
+ "set_locale",
981
+ "set_permission_mode",
982
+ "login",
983
+ "logout",
984
+ "get_project_trust",
985
+ "set_project_trust",
986
+ "list_context_files",
987
+ "list_mcp_servers",
988
+ "configure_mcp_server",
989
+ "remove_mcp_server",
990
+ "reconnect_mcp_server",
991
+ "create_attachment",
992
+ "approve_tool",
993
+ ];
994
+ //# sourceMappingURL=dispatcher.js.map