@gajae-code/coding-agent 0.10.2 → 0.11.1

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 (476) hide show
  1. package/CHANGELOG.md +58 -2
  2. package/dist/types/append-only-mode.d.ts +1 -1
  3. package/dist/types/async/job-manager.d.ts +46 -7
  4. package/dist/types/cli/args.d.ts +4 -5
  5. package/dist/types/cli/daemon-cli.d.ts +12 -1
  6. package/dist/types/cli/notify-cli.d.ts +21 -1
  7. package/dist/types/cli.d.ts +3 -0
  8. package/dist/types/commands/daemon.d.ts +39 -0
  9. package/dist/types/commands/launch.d.ts +3 -0
  10. package/dist/types/commands/notify.d.ts +28 -1
  11. package/dist/types/commands/sdk.d.ts +33 -0
  12. package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
  13. package/dist/types/config/model-profile-activation.d.ts +35 -15
  14. package/dist/types/config/model-profiles.d.ts +11 -5
  15. package/dist/types/config/model-registry.d.ts +3 -3
  16. package/dist/types/config/model-resolver.d.ts +39 -6
  17. package/dist/types/config/model-selector-value.d.ts +17 -0
  18. package/dist/types/config/models-config-schema.d.ts +10 -6
  19. package/dist/types/config/settings-schema.d.ts +133 -6
  20. package/dist/types/config/settings.d.ts +63 -35
  21. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
  22. package/dist/types/coordinator-mcp/server.d.ts +6 -49
  23. package/dist/types/daemon/builtin.d.ts +6 -5
  24. package/dist/types/daemon/control-types.d.ts +1 -1
  25. package/dist/types/discovery/mcp-json.d.ts +12 -1
  26. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  27. package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
  28. package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
  29. package/dist/types/extensibility/extensions/loader.d.ts +10 -1
  30. package/dist/types/extensibility/extensions/runner.d.ts +3 -3
  31. package/dist/types/extensibility/extensions/types.d.ts +141 -16
  32. package/dist/types/extensibility/hooks/types.d.ts +1 -1
  33. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
  34. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  35. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
  36. package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
  37. package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
  38. package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
  39. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
  40. package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
  41. package/dist/types/harness-control-plane/operate.d.ts +5 -5
  42. package/dist/types/harness-control-plane/owner.d.ts +18 -4
  43. package/dist/types/harness-control-plane/receipts.d.ts +0 -1
  44. package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
  45. package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
  46. package/dist/types/harness-control-plane/types.d.ts +3 -4
  47. package/dist/types/lsp/index.d.ts +1 -1
  48. package/dist/types/main.d.ts +32 -38
  49. package/dist/types/modes/acp/acp-agent.d.ts +224 -53
  50. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
  51. package/dist/types/modes/acp/acp-mode.d.ts +6 -15
  52. package/dist/types/modes/acp/permission-mode.d.ts +4 -0
  53. package/dist/types/modes/acp/startup-options.d.ts +6 -0
  54. package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
  55. package/dist/types/modes/components/model-selector.d.ts +1 -0
  56. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  57. package/dist/types/modes/components/settings-selector.d.ts +6 -4
  58. package/dist/types/modes/components/welcome.d.ts +1 -1
  59. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  60. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
  61. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  62. package/dist/types/modes/index.d.ts +0 -4
  63. package/dist/types/modes/interactive-mode.d.ts +5 -2
  64. package/dist/types/modes/runtime-init.d.ts +2 -2
  65. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  66. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  67. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  68. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  69. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  70. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +170 -30
  71. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  72. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  73. package/dist/types/modes/types.d.ts +7 -2
  74. package/dist/types/runtime-mcp/client.d.ts +2 -0
  75. package/dist/types/runtime-mcp/config.d.ts +5 -1
  76. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  77. package/dist/types/runtime-mcp/types.d.ts +4 -0
  78. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  79. package/dist/types/sdk/acp/index.d.ts +2 -0
  80. package/dist/types/sdk/broker/broker.d.ts +53 -0
  81. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  82. package/dist/types/sdk/broker/ensure.d.ts +42 -0
  83. package/dist/types/sdk/broker/identity.d.ts +3 -0
  84. package/dist/types/sdk/broker/index.d.ts +8 -0
  85. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  86. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  87. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  88. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  89. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  90. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  91. package/dist/types/sdk/broker/transport.d.ts +9 -0
  92. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  93. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  94. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  95. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  96. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  97. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  98. package/dist/types/sdk/bus/config.d.ts +274 -0
  99. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  100. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  101. package/dist/types/sdk/bus/discord-daemon.d.ts +50 -0
  102. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  103. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  104. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  105. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  106. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  107. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  108. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  109. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  110. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  111. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  112. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  113. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  114. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  115. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  116. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  117. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  118. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  119. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  120. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  121. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +78 -17
  122. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  123. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  124. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  125. package/dist/types/sdk/cli/index.d.ts +1 -0
  126. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  127. package/dist/types/sdk/client/client.d.ts +1 -0
  128. package/dist/types/sdk/client/discovery.d.ts +27 -0
  129. package/dist/types/sdk/client/index.d.ts +2 -0
  130. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  131. package/dist/types/sdk/host/control/index.d.ts +2 -0
  132. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  133. package/dist/types/sdk/host/events.d.ts +35 -0
  134. package/dist/types/sdk/host/host.d.ts +35 -0
  135. package/dist/types/sdk/host/index.d.ts +4 -0
  136. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  137. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  138. package/dist/types/sdk/host/query/index.d.ts +3 -0
  139. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  140. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  141. package/dist/types/sdk/host/types.d.ts +32 -0
  142. package/dist/types/sdk/index.d.ts +6 -0
  143. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  144. package/dist/types/sdk/mcp/index.d.ts +1 -0
  145. package/dist/types/sdk/mcp/server.d.ts +37 -0
  146. package/dist/types/sdk/models.d.ts +44 -0
  147. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  148. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  149. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  150. package/dist/types/sdk/startup-capability.d.ts +50 -0
  151. package/dist/types/session/agent-session.d.ts +114 -21
  152. package/dist/types/session/default-model-selection.d.ts +25 -0
  153. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  154. package/dist/types/session/session-manager.d.ts +58 -1
  155. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  156. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  157. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  158. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  159. package/dist/types/tools/browser/launch.d.ts +22 -0
  160. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  161. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  162. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  163. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  164. package/dist/types/tools/browser/registry.d.ts +19 -0
  165. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  166. package/dist/types/tools/index.d.ts +8 -8
  167. package/dist/types/tools/subagent-render.d.ts +1 -0
  168. package/dist/types/tools/subagent.d.ts +6 -1
  169. package/package.json +34 -13
  170. package/scripts/build-sdk-package-smoke.ts +99 -0
  171. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  172. package/scripts/compile-args.ts +4 -2
  173. package/scripts/g005-daemon-smoke.ts +4 -5
  174. package/scripts/g011-daemon-path-smoke.ts +5 -5
  175. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  176. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  177. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  178. package/scripts/manifest-schema.ts +124 -0
  179. package/scripts/run-test-manifest.ts +153 -0
  180. package/scripts/run-threat-model-check.ts +157 -0
  181. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  182. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  183. package/src/append-only-mode.ts +1 -1
  184. package/src/async/job-manager.ts +406 -63
  185. package/src/cli/args.ts +43 -12
  186. package/src/cli/auth-gateway-cli.ts +2 -2
  187. package/src/cli/config-cli.ts +2 -1
  188. package/src/cli/daemon-cli.ts +63 -14
  189. package/src/cli/list-models.ts +1 -1
  190. package/src/cli/notify-cli.ts +288 -286
  191. package/src/cli.ts +21 -2
  192. package/src/commands/daemon.ts +50 -6
  193. package/src/commands/harness.ts +93 -36
  194. package/src/commands/launch.ts +5 -2
  195. package/src/commands/mcp-serve.ts +19 -12
  196. package/src/commands/notify.ts +36 -3
  197. package/src/commands/sdk.ts +426 -0
  198. package/src/commit/agentic/agent.ts +1 -0
  199. package/src/config/atomic-yaml-patch.ts +423 -0
  200. package/src/config/model-profile-activation.ts +240 -89
  201. package/src/config/model-profiles.ts +18 -15
  202. package/src/config/model-registry.ts +51 -26
  203. package/src/config/model-resolver.ts +138 -51
  204. package/src/config/model-selector-value.ts +50 -0
  205. package/src/config/models-config-schema.ts +14 -21
  206. package/src/config/settings-schema.ts +128 -10
  207. package/src/config/settings.ts +1120 -158
  208. package/src/coordinator-mcp/model-preset.ts +74 -20
  209. package/src/coordinator-mcp/server.ts +1597 -3052
  210. package/src/daemon/builtin.ts +8 -5
  211. package/src/daemon/control-types.ts +1 -1
  212. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  213. package/src/discovery/mcp-json.ts +327 -20
  214. package/src/extensibility/custom-tools/types.ts +1 -1
  215. package/src/extensibility/extensions/compact-handler.ts +3 -3
  216. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  217. package/src/extensibility/extensions/loader.ts +69 -5
  218. package/src/extensibility/extensions/runner.ts +112 -4
  219. package/src/extensibility/extensions/types.ts +176 -17
  220. package/src/extensibility/hooks/types.ts +1 -1
  221. package/src/gjc-runtime/launch-tmux.ts +170 -86
  222. package/src/gjc-runtime/psmux-detect.ts +82 -4
  223. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  224. package/src/gjc-runtime/team-runtime.ts +54 -35
  225. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  226. package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
  227. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  228. package/src/gjc-runtime/ultragoal-guard.ts +108 -14
  229. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
  230. package/src/gjc-runtime/ultragoal-runtime.ts +227 -11
  231. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  232. package/src/harness-control-plane/operate.ts +9 -9
  233. package/src/harness-control-plane/owner.ts +283 -39
  234. package/src/harness-control-plane/receipts.ts +0 -1
  235. package/src/harness-control-plane/sdk-transport.ts +395 -0
  236. package/src/harness-control-plane/session-transport.ts +94 -0
  237. package/src/harness-control-plane/types.ts +1 -1
  238. package/src/internal-urls/docs-index.generated.ts +34 -35
  239. package/src/lsp/client.ts +42 -6
  240. package/src/lsp/index.ts +2 -2
  241. package/src/main.ts +299 -204
  242. package/src/modes/DESIGN.md +258 -0
  243. package/src/modes/acp/acp-agent.ts +1228 -2612
  244. package/src/modes/acp/acp-client-bridge.ts +2 -20
  245. package/src/modes/acp/acp-event-mapper.ts +18 -2
  246. package/src/modes/acp/acp-mode.ts +8 -36
  247. package/src/modes/acp/permission-mode.ts +25 -0
  248. package/src/modes/acp/startup-options.ts +6 -0
  249. package/src/modes/components/agent-dashboard.ts +37 -17
  250. package/src/modes/components/model-selector.ts +93 -35
  251. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  252. package/src/modes/components/settings-selector.ts +60 -11
  253. package/src/modes/components/welcome.ts +4 -4
  254. package/src/modes/controllers/command-controller.ts +10 -17
  255. package/src/modes/controllers/event-controller.ts +11 -16
  256. package/src/modes/controllers/extension-ui-controller.ts +396 -28
  257. package/src/modes/controllers/selector-controller.ts +674 -36
  258. package/src/modes/index.ts +0 -22
  259. package/src/modes/interactive-mode.ts +87 -36
  260. package/src/modes/runtime-init.ts +298 -6
  261. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  262. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  263. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  264. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  265. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  266. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1258 -87
  267. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  268. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  269. package/src/modes/types.ts +7 -2
  270. package/src/runtime-mcp/client.ts +94 -19
  271. package/src/runtime-mcp/config.ts +27 -15
  272. package/src/runtime-mcp/manager.ts +343 -105
  273. package/src/runtime-mcp/oauth-flow.ts +42 -12
  274. package/src/runtime-mcp/transports/http.ts +49 -19
  275. package/src/runtime-mcp/transports/stdio.ts +47 -28
  276. package/src/runtime-mcp/types.ts +10 -0
  277. package/src/sdk/acp/adapter.ts +418 -0
  278. package/src/sdk/acp/index.ts +2 -0
  279. package/src/sdk/broker/broker.ts +642 -0
  280. package/src/sdk/broker/discovery.ts +113 -0
  281. package/src/sdk/broker/ensure.ts +307 -0
  282. package/src/sdk/broker/identity.ts +42 -0
  283. package/src/sdk/broker/index.ts +8 -0
  284. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  285. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  286. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  287. package/src/sdk/broker/lifecycle.ts +2157 -0
  288. package/src/sdk/broker/process-incarnation.ts +159 -0
  289. package/src/sdk/broker/runtime.ts +164 -0
  290. package/src/sdk/broker/session-index.ts +318 -0
  291. package/src/sdk/broker/state-version.ts +23 -0
  292. package/src/sdk/broker/transport.ts +125 -0
  293. package/src/sdk/bus/chat-command-policy.ts +107 -0
  294. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  295. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  296. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  297. package/src/sdk/bus/chat-effect-journal.ts +355 -0
  298. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  299. package/src/sdk/bus/config.ts +433 -0
  300. package/src/sdk/bus/conversation-store.ts +391 -0
  301. package/src/sdk/bus/discord-conversation.ts +85 -0
  302. package/src/sdk/bus/discord-daemon.ts +2292 -0
  303. package/src/sdk/bus/discord-live-provider.ts +521 -0
  304. package/src/sdk/bus/discord-provider.ts +56 -0
  305. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  306. package/src/sdk/bus/index.ts +3979 -0
  307. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  308. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  309. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  310. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  311. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  312. package/src/sdk/bus/natives-augment.d.ts +38 -0
  313. package/src/sdk/bus/notification-orchestration.ts +535 -0
  314. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  315. package/src/sdk/bus/session-control.ts +333 -0
  316. package/src/sdk/bus/slack-conversation.ts +74 -0
  317. package/src/sdk/bus/slack-daemon.ts +2144 -0
  318. package/src/sdk/bus/slack-live-provider.ts +380 -0
  319. package/src/sdk/bus/slack-provider.ts +76 -0
  320. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  321. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  322. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  323. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  324. package/src/{notifications → sdk/bus}/telegram-daemon.ts +603 -63
  325. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  326. package/src/sdk/bus/telegram-setup.ts +663 -0
  327. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  328. package/src/sdk/cli/index.ts +1 -0
  329. package/src/sdk/cli/session-cli.ts +275 -0
  330. package/src/sdk/client/client.ts +1 -0
  331. package/src/sdk/client/discovery.ts +108 -0
  332. package/src/sdk/client/index.ts +2 -0
  333. package/src/sdk/host/control/dispatch.ts +367 -0
  334. package/src/sdk/host/control/index.ts +10 -0
  335. package/src/sdk/host/control/operations.ts +63 -0
  336. package/src/sdk/host/events.ts +91 -0
  337. package/src/sdk/host/host.ts +296 -0
  338. package/src/sdk/host/index.ts +4 -0
  339. package/src/sdk/host/query/cursor.ts +189 -0
  340. package/src/sdk/host/query/handlers.ts +533 -0
  341. package/src/sdk/host/query/index.ts +3 -0
  342. package/src/sdk/host/query/revision-store.ts +1126 -0
  343. package/src/sdk/host/reverse-leases.ts +325 -0
  344. package/src/sdk/host/types.ts +29 -0
  345. package/src/sdk/index.ts +13 -0
  346. package/src/sdk/lifecycle-session.ts +39 -0
  347. package/src/sdk/mcp/index.ts +1 -0
  348. package/src/sdk/mcp/server.ts +332 -0
  349. package/src/sdk/models.ts +168 -0
  350. package/src/sdk/protocol/adapter-validation.ts +48 -0
  351. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  352. package/src/sdk/protocol/operation-registry.ts +312 -0
  353. package/src/{sdk.ts → sdk/session.ts} +339 -212
  354. package/src/sdk/startup-capability.ts +189 -0
  355. package/src/session/agent-session.ts +2650 -944
  356. package/src/session/default-model-selection.ts +93 -0
  357. package/src/session/fallback-chain-controller.ts +136 -0
  358. package/src/session/session-manager.ts +438 -11
  359. package/src/session/session-storage.ts +8 -1
  360. package/src/setup/credential-auto-import.ts +290 -35
  361. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  362. package/src/slash-commands/builtin-registry.ts +2 -1
  363. package/src/task/executor.ts +62 -13
  364. package/src/task/index.ts +26 -16
  365. package/src/tools/ask-answer-registry.ts +1 -1
  366. package/src/tools/ask.ts +13 -8
  367. package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
  368. package/src/tools/browser/benchmark/detector-report.ts +157 -0
  369. package/src/tools/browser/benchmark/run-suite.ts +39 -0
  370. package/src/tools/browser/launch.ts +124 -19
  371. package/src/tools/browser/profile-discovery.ts +63 -0
  372. package/src/tools/browser/profile-posture.ts +59 -0
  373. package/src/tools/browser/profile-reuse.ts +102 -0
  374. package/src/tools/browser/profile-warmup.ts +109 -0
  375. package/src/tools/browser/registry.ts +87 -11
  376. package/src/tools/browser/tab-protocol.ts +1 -0
  377. package/src/tools/browser/tab-supervisor.ts +1 -0
  378. package/src/tools/browser/tab-worker.ts +1 -1
  379. package/src/tools/browser.ts +6 -0
  380. package/src/tools/index.ts +8 -8
  381. package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
  382. package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
  383. package/src/tools/subagent-render.ts +19 -8
  384. package/src/tools/subagent.ts +84 -32
  385. package/src/tools/telegram-send.ts +2 -2
  386. package/vendor/insane-search/MANIFEST.json +2 -1
  387. package/vendor/insane-search/engine/bias_check.py +4 -1
  388. package/vendor/insane-search/engine/phase0.py +26 -1
  389. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  390. package/dist/types/modes/bridge/auth.d.ts +0 -12
  391. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  392. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  393. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  394. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  395. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  396. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  397. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  398. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  399. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  400. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  401. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  402. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  403. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  404. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  405. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  406. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  407. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  408. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  409. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  410. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  411. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  412. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  413. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  414. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  415. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  416. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  417. package/dist/types/notifications/config.d.ts +0 -132
  418. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  419. package/src/modes/bridge/auth.ts +0 -41
  420. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  421. package/src/modes/bridge/bridge-mode.ts +0 -675
  422. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  423. package/src/modes/bridge/event-stream.ts +0 -70
  424. package/src/modes/rpc/host-tools.ts +0 -1
  425. package/src/modes/rpc/host-uris.ts +0 -1
  426. package/src/modes/rpc/rpc-client.ts +0 -1166
  427. package/src/modes/rpc/rpc-mode.ts +0 -1029
  428. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  429. package/src/modes/rpc/rpc-types.ts +0 -637
  430. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  431. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  432. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  433. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  434. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  435. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  436. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  437. package/src/modes/shared/agent-wire/responses.ts +0 -17
  438. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  439. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  440. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  441. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  442. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  443. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  444. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  445. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  446. package/src/notifications/config.ts +0 -219
  447. package/src/notifications/index.ts +0 -1835
  448. package/src/notifications/telegram-daemon-cli.ts +0 -243
  449. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  450. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  453. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  454. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  455. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  456. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  457. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  458. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  459. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  460. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  461. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  462. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  463. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  466. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  467. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  468. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  469. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  470. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  471. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  472. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  473. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  474. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  475. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  476. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -1,1166 +0,0 @@
1
- /**
2
- * RPC Client for programmatic access to the coding agent.
3
- *
4
- * Spawns the agent in RPC mode and provides a typed API for all operations.
5
- */
6
- import {
7
- type AgentEvent,
8
- type AgentMessage,
9
- type AgentToolResult,
10
- type ResolvedThinkingLevel,
11
- ThinkingLevel,
12
- } from "@gajae-code/agent-core";
13
- import type { CompactionResult } from "@gajae-code/agent-core/compaction";
14
- import type { ImageContent, Model } from "@gajae-code/ai";
15
- import { isRecord, ptree, readJsonl } from "@gajae-code/utils";
16
- import type { BashResult } from "../../exec/bash-executor";
17
- import type { AgentSessionEvent, SessionStats } from "../../session/agent-session";
18
- import { AGENT_WIRE_EVENT_TYPES, type AgentWireEventType } from "../shared/agent-wire/event-contract";
19
- import type {
20
- RpcCommand,
21
- RpcExtensionUIRequest,
22
- RpcGetStateInclude,
23
- RpcHandoffResult,
24
- RpcHostToolCallRequest,
25
- RpcHostToolCancelRequest,
26
- RpcHostToolDefinition,
27
- RpcHostToolResult,
28
- RpcHostToolUpdate,
29
- RpcResponse,
30
- RpcSessionState,
31
- RpcUnattendedAccepted,
32
- RpcUnattendedDeclaration,
33
- RpcWorkflowGate,
34
- RpcWorkflowGateResolution,
35
- RpcWorkflowGateResponse,
36
- } from "./rpc-types";
37
-
38
- /** Distributive Omit that works with union types */
39
- type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
40
-
41
- /** RpcCommand without the id field (for internal send) */
42
- type RpcCommandBody = DistributiveOmit<RpcCommand, "id">;
43
-
44
- export interface RpcClientOptions {
45
- /** Dial an existing Unix-domain socket instead of spawning a stdio child. */
46
- socketPath?: string;
47
- /** Explicit transport selector; defaults to uds when socketPath is set, otherwise stdio. */
48
- transport?: "stdio" | "uds";
49
- /** Observe transport close/error state. */
50
- onTransportClose?: (error?: Error) => void;
51
- /** Alias for transport close/error observation. */
52
- onTransportError?: (error: Error) => void;
53
- /** Path to the CLI entry point (default: searches for dist/cli.js) */
54
- cliPath?: string;
55
- /** Working directory for the agent */
56
- cwd?: string;
57
- /** Environment variables */
58
- env?: Record<string, string>;
59
- /** Provider to use */
60
- provider?: string;
61
- /** Model ID to use */
62
- model?: string;
63
- /** Session directory for the agent */
64
- sessionDir?: string;
65
- /** Additional CLI arguments */
66
- args?: string[];
67
- /** Custom tools owned by the embedding host and exposed over the RPC transport */
68
- customTools?: RpcClientCustomTool[];
69
- }
70
-
71
- export type ModelInfo = Pick<Model, "provider" | "id" | "contextWindow" | "reasoning" | "thinking">;
72
-
73
- export type RpcDefaultModelSelection = {
74
- readonly provider: string;
75
- readonly modelId: string;
76
- readonly thinkingLevel: ResolvedThinkingLevel;
77
- };
78
-
79
- export type RpcEventListener = (event: AgentEvent) => void;
80
- export type RpcSessionEventListener = (event: AgentSessionEvent) => void;
81
-
82
- export interface RpcClientToolContext<TDetails = unknown> {
83
- toolCallId: string;
84
- signal: AbortSignal;
85
- sendUpdate(partialResult: RpcClientToolResult<TDetails>): void;
86
- }
87
-
88
- export type RpcClientToolResult<TDetails = unknown> = AgentToolResult<TDetails> | string;
89
-
90
- export interface RpcClientCustomTool<
91
- TParams extends Record<string, unknown> = Record<string, unknown>,
92
- TDetails = unknown,
93
- > extends Omit<RpcHostToolDefinition, "parameters"> {
94
- parameters: Record<string, unknown>;
95
- execute(
96
- params: TParams,
97
- context: RpcClientToolContext<TDetails>,
98
- ): Promise<RpcClientToolResult<TDetails>> | RpcClientToolResult<TDetails>;
99
- }
100
-
101
- export function defineRpcClientTool<
102
- TParams extends Record<string, unknown> = Record<string, unknown>,
103
- TDetails = unknown,
104
- >(tool: RpcClientCustomTool<TParams, TDetails>): RpcClientCustomTool<TParams, TDetails> {
105
- return tool;
106
- }
107
-
108
- const agentWireEventTypes = new Set<AgentWireEventType>(AGENT_WIRE_EVENT_TYPES);
109
- const DEFAULT_MODEL_SELECTION_TIMEOUT_MS = 600_000;
110
- const coreAgentEventTypes = new Set<AgentEvent["type"]>([
111
- "agent_start",
112
- "agent_end",
113
- "turn_start",
114
- "turn_end",
115
- "message_start",
116
- "message_update",
117
- "message_end",
118
- "tool_execution_start",
119
- "tool_execution_update",
120
- "tool_execution_end",
121
- ]);
122
-
123
- function isResolvedThinkingLevel(value: unknown): value is ResolvedThinkingLevel {
124
- return (
125
- value === ThinkingLevel.Off ||
126
- value === ThinkingLevel.Minimal ||
127
- value === ThinkingLevel.Low ||
128
- value === ThinkingLevel.Medium ||
129
- value === ThinkingLevel.High ||
130
- value === ThinkingLevel.XHigh ||
131
- value === ThinkingLevel.Max
132
- );
133
- }
134
-
135
- function isDefaultModelSelection(value: unknown): value is RpcDefaultModelSelection {
136
- return (
137
- isRecord(value) &&
138
- typeof value.provider === "string" &&
139
- value.provider.trim().length > 0 &&
140
- typeof value.modelId === "string" &&
141
- value.modelId.trim().length > 0 &&
142
- isResolvedThinkingLevel(value.thinkingLevel)
143
- );
144
- }
145
-
146
- function isRpcResponse(value: unknown): value is RpcResponse {
147
- if (!isRecord(value)) return false;
148
- if (value.type !== "response") return false;
149
- if (typeof value.command !== "string") return false;
150
- if (typeof value.success !== "boolean") return false;
151
- if (value.id !== undefined && typeof value.id !== "string") return false;
152
- if (value.success === false) {
153
- return typeof value.error === "string" || isRecord(value.error);
154
- }
155
- return true;
156
- }
157
-
158
- function isAgentSessionEvent(value: unknown): value is AgentSessionEvent {
159
- if (!isRecord(value)) return false;
160
- const type = value.type;
161
- if (typeof type !== "string") return false;
162
- return agentWireEventTypes.has(type as AgentWireEventType);
163
- }
164
-
165
- function isCoreAgentEvent(value: AgentSessionEvent): value is AgentEvent {
166
- return coreAgentEventTypes.has(value.type as AgentEvent["type"]);
167
- }
168
- function isRpcHostToolCallRequest(value: unknown): value is RpcHostToolCallRequest {
169
- if (!isRecord(value)) return false;
170
- return (
171
- value.type === "host_tool_call" &&
172
- typeof value.id === "string" &&
173
- typeof value.toolCallId === "string" &&
174
- typeof value.toolName === "string" &&
175
- isRecord(value.arguments)
176
- );
177
- }
178
-
179
- function isRpcHostToolCancelRequest(value: unknown): value is RpcHostToolCancelRequest {
180
- if (!isRecord(value)) return false;
181
- return value.type === "host_tool_cancel" && typeof value.id === "string" && typeof value.targetId === "string";
182
- }
183
-
184
- function isRpcExtensionUiRequest(value: unknown): value is RpcExtensionUIRequest {
185
- if (!isRecord(value)) return false;
186
- return value.type === "extension_ui_request" && typeof value.id === "string" && typeof value.method === "string";
187
- }
188
-
189
- function isRpcWorkflowGate(value: unknown): value is RpcWorkflowGate {
190
- if (!isRecord(value)) return false;
191
- return (
192
- value.type === "workflow_gate" &&
193
- typeof value.gate_id === "string" &&
194
- typeof value.stage === "string" &&
195
- typeof value.kind === "string" &&
196
- isRecord(value.schema) &&
197
- typeof value.schema_hash === "string" &&
198
- isRecord(value.context) &&
199
- typeof value.created_at === "string" &&
200
- value.required === true
201
- );
202
- }
203
-
204
- /**
205
- * Unwrap a canonical agent-wire `event` frame `{ type:"event", payload:{ event_type, event } }`
206
- * to its inner `AgentSessionEvent`. Returns null for any frame that is not a
207
- * canonical event frame (session events are only delivered wrapped).
208
- */
209
- function unwrapAgentWireEventFrame(value: unknown): unknown {
210
- if (isRecord(value) && value.type === "event" && isRecord(value.payload)) {
211
- return value.payload.event;
212
- }
213
- return null;
214
- }
215
-
216
- function normalizeToolResult<TDetails>(result: RpcClientToolResult<TDetails>): AgentToolResult<TDetails> {
217
- if (typeof result === "string") {
218
- return {
219
- content: [{ type: "text", text: result }],
220
- };
221
- }
222
- return result;
223
- }
224
-
225
- // ============================================================================
226
- // RPC Client
227
- // ============================================================================
228
-
229
- export interface RpcTransport {
230
- readonly kind: "stdio" | "uds";
231
- start(onFrame: (frame: unknown) => void, onClose: (error?: Error) => void): Promise<void>;
232
- write(frame: unknown): void;
233
- stop(): void;
234
- getStderr(): string;
235
- }
236
-
237
- class StdioRpcTransport implements RpcTransport {
238
- readonly kind = "stdio" as const;
239
- #process: ptree.ChildProcess | null = null;
240
- #abortController = new AbortController();
241
- #startupStderrPromise: Promise<string> = Promise.resolve("");
242
-
243
- constructor(private readonly options: RpcClientOptions) {}
244
-
245
- async start(onFrame: (frame: unknown) => void, onClose: (error?: Error) => void): Promise<void> {
246
- if (this.#process) throw new Error("Transport already started");
247
- this.#abortController = new AbortController();
248
- const cliPath = this.options.cliPath ?? "dist/cli.js";
249
- const args = ["--mode", "rpc"];
250
- if (this.options.provider) args.push("--provider", this.options.provider);
251
- if (this.options.model) args.push("--model", this.options.model);
252
- if (this.options.sessionDir) args.push("--session-dir", this.options.sessionDir);
253
- if (this.options.args) args.push(...this.options.args);
254
- this.#process = ptree.spawn(["bun", cliPath, ...args], {
255
- cwd: this.options.cwd,
256
- env: { ...Bun.env, ...this.options.env },
257
- stdin: "pipe",
258
- stderr: "full",
259
- });
260
- this.#startupStderrPromise = this.#process.stderr
261
- ? new Response(this.#process.stderr).text().catch(() => "")
262
- : Promise.resolve("");
263
- const getStartupStderr = async () => this.#process?.peekStderr() || (await this.#startupStderrPromise);
264
- const { promise: readyPromise, resolve: readyResolve, reject: readyReject } = Promise.withResolvers<void>();
265
- let readySettled = false;
266
- const lines = readJsonl(this.#process.stdout, this.#abortController.signal);
267
- void (async () => {
268
- for await (const line of lines) {
269
- if (!readySettled && isRecord(line) && line.type === "ready") {
270
- readySettled = true;
271
- readyResolve();
272
- continue;
273
- }
274
- onFrame(line);
275
- }
276
- if (!readySettled) {
277
- const proc = this.#process;
278
- const exitCode = proc ? await proc.exited.catch(() => proc.exitCode ?? -1) : undefined;
279
- if (!readySettled) {
280
- const stderr = await getStartupStderr();
281
- readySettled = true;
282
- readyReject(
283
- new Error(
284
- `Agent process exited${exitCode === undefined ? "" : ` with code ${exitCode}`} before ready. Stderr: ${stderr}`,
285
- ),
286
- );
287
- }
288
- } else {
289
- onClose(new Error("RPC stdio transport closed"));
290
- }
291
- })().catch((err: Error) => {
292
- if (!readySettled) {
293
- readySettled = true;
294
- readyReject(err);
295
- } else onClose(err);
296
- });
297
- void this.#process.exited.then(async (exitCode: number) => {
298
- if (!readySettled) {
299
- const stderr = await getStartupStderr();
300
- if (readySettled) return;
301
- readySettled = true;
302
- readyReject(new Error(`Agent process exited with code ${exitCode}. Stderr: ${stderr}`));
303
- } else onClose(new Error(`RPC stdio transport exited with code ${exitCode}`));
304
- });
305
- const readyTimeout = setTimeout(() => {
306
- if (readySettled) return;
307
- readySettled = true;
308
- void getStartupStderr().then(stderr =>
309
- readyReject(new Error(`Timeout waiting for agent to become ready. Stderr: ${stderr}`)),
310
- );
311
- }, 30_000);
312
- readyTimeout.unref();
313
- try {
314
- await readyPromise;
315
- } finally {
316
- clearTimeout(readyTimeout);
317
- }
318
- }
319
-
320
- write(frame: unknown): void {
321
- if (!this.#process?.stdin) throw new Error("Client not started");
322
- const stdin = this.#process.stdin as import("bun").FileSink;
323
- stdin.write(`${JSON.stringify(frame)}\n`);
324
- const flushResult = stdin.flush();
325
- if (flushResult instanceof Promise) void flushResult;
326
- }
327
-
328
- stop(): void {
329
- if (!this.#process) return;
330
- this.#process.kill();
331
- this.#abortController.abort();
332
- this.#process = null;
333
- }
334
-
335
- getStderr(): string {
336
- return this.#process?.peekStderr() ?? "";
337
- }
338
- }
339
-
340
- class UdsRpcTransport implements RpcTransport {
341
- readonly kind = "uds" as const;
342
- #socket: import("bun").Socket | null = null;
343
- #buf = "";
344
- #decoder = new TextDecoder("utf-8", { fatal: false });
345
- constructor(private readonly socketPath: string) {}
346
-
347
- async start(onFrame: (frame: unknown) => void, onClose: (error?: Error) => void): Promise<void> {
348
- const { promise: readyPromise, resolve: readyResolve, reject: readyReject } = Promise.withResolvers<void>();
349
- let readySettled = false;
350
- this.#socket = await Bun.connect({
351
- unix: this.socketPath,
352
- socket: {
353
- data: (_socket, data) => {
354
- this.#buf += this.#decoder.decode(data);
355
- while (true) {
356
- const nl = this.#buf.indexOf("\n");
357
- if (nl < 0) break;
358
- const text = this.#buf.slice(0, nl).trim();
359
- this.#buf = this.#buf.slice(nl + 1);
360
- if (!text) continue;
361
- let frame: unknown;
362
- try {
363
- frame = JSON.parse(text);
364
- } catch (err) {
365
- onClose(err instanceof Error ? err : new Error(String(err)));
366
- continue;
367
- }
368
- if (!readySettled && isRecord(frame) && frame.type === "ready") {
369
- readySettled = true;
370
- readyResolve();
371
- continue;
372
- }
373
- onFrame(frame);
374
- }
375
- },
376
- close: () => {
377
- if (!readySettled) {
378
- readySettled = true;
379
- readyReject(new Error(`RPC UDS transport closed before ready: ${this.socketPath}`));
380
- } else onClose(new Error(`RPC UDS transport closed: ${this.socketPath}`));
381
- },
382
- error: (_socket, error) => {
383
- const err = error instanceof Error ? error : new Error(String(error));
384
- if (!readySettled) {
385
- readySettled = true;
386
- readyReject(err);
387
- } else onClose(err);
388
- },
389
- },
390
- });
391
- const readyTimeout = setTimeout(() => {
392
- if (readySettled) return;
393
- readySettled = true;
394
- readyReject(new Error(`Timeout waiting for RPC UDS ready frame: ${this.socketPath}`));
395
- }, 30_000);
396
- readyTimeout.unref();
397
- try {
398
- await readyPromise;
399
- } finally {
400
- clearTimeout(readyTimeout);
401
- }
402
- }
403
-
404
- write(frame: unknown): void {
405
- if (!this.#socket) throw new Error("Client not started");
406
- this.#socket.write(`${JSON.stringify(frame)}\n`);
407
- }
408
-
409
- stop(): void {
410
- this.#socket?.end();
411
- this.#socket = null;
412
- }
413
-
414
- getStderr(): string {
415
- return "";
416
- }
417
- }
418
-
419
- export class RpcClient {
420
- #transport: RpcTransport | null = null;
421
- #transportClosed = false;
422
- #eventListeners: RpcEventListener[] = [];
423
- #sessionEventListeners: RpcSessionEventListener[] = [];
424
- #pendingRequests: Map<string, { resolve: (response: RpcResponse) => void; reject: (error: Error) => void }> =
425
- new Map();
426
- #customTools: RpcClientCustomTool[] = [];
427
- #pendingHostToolCalls = new Map<string, { controller: AbortController }>();
428
- #requestId = 0;
429
- #extensionUiListeners: Set<(req: RpcExtensionUIRequest) => void> = new Set();
430
- #workflowGateListeners: Set<(gate: RpcWorkflowGate) => void> = new Set();
431
-
432
- constructor(private options: RpcClientOptions = {}) {
433
- this.#customTools = [...(options.customTools ?? [])];
434
- }
435
-
436
- /**
437
- * Start the RPC agent process.
438
- */
439
- async start(): Promise<void> {
440
- if (this.#transport) {
441
- throw new Error("Client already started");
442
- }
443
- this.#transportClosed = false;
444
- const transportKind = this.options.transport ?? (this.options.socketPath ? "uds" : "stdio");
445
- if (transportKind === "uds") {
446
- if (!this.options.socketPath) throw new Error("socketPath is required for uds transport");
447
- this.#transport = new UdsRpcTransport(this.options.socketPath);
448
- } else {
449
- this.#transport = new StdioRpcTransport(this.options);
450
- }
451
- try {
452
- await this.#transport.start(
453
- line => this.#handleLine(line),
454
- error => this.#handleTransportClose(error),
455
- );
456
- if (this.#customTools.length > 0) {
457
- await this.setCustomTools(this.#customTools);
458
- }
459
- } catch (error) {
460
- this.#transport = null;
461
- throw error;
462
- }
463
- }
464
-
465
- /**
466
- * Stop the RPC agent process.
467
- */
468
- stop() {
469
- if (!this.#transport) return;
470
-
471
- this.#transport.stop();
472
- this.#transport = null;
473
- this.#rejectAllPending(new Error("RPC client stopped"));
474
- }
475
-
476
- /**
477
- * Stop the RPC agent process and clean up resources.
478
- */
479
- [Symbol.dispose](): void {
480
- try {
481
- this.stop();
482
- } catch {
483
- // Ignore cleanup errors
484
- }
485
- }
486
-
487
- /**
488
- * Subscribe to the legacy core AgentEvent subset.
489
- */
490
- onEvent(listener: RpcEventListener): () => void {
491
- this.#eventListeners.push(listener);
492
- return () => {
493
- const index = this.#eventListeners.indexOf(listener);
494
- if (index !== -1) {
495
- this.#eventListeners.splice(index, 1);
496
- }
497
- };
498
- }
499
-
500
- /**
501
- * Subscribe to all renderer-facing agent session events.
502
- */
503
- onSessionEvent(listener: RpcSessionEventListener): () => void {
504
- this.#sessionEventListeners.push(listener);
505
- return () => {
506
- const index = this.#sessionEventListeners.indexOf(listener);
507
- if (index !== -1) {
508
- this.#sessionEventListeners.splice(index, 1);
509
- }
510
- };
511
- }
512
-
513
- /**
514
- * Subscribe to workflow lifecycle gates emitted by RPC mode.
515
- */
516
- onWorkflowGate(listener: (gate: RpcWorkflowGate) => void): () => void {
517
- this.#workflowGateListeners.add(listener);
518
- return () => {
519
- this.#workflowGateListeners.delete(listener);
520
- };
521
- }
522
-
523
- /**
524
- * Answer a workflow lifecycle gate and wait for the server resolution envelope.
525
- */
526
- async respondGate(gateId: string, answer: unknown, idempotencyKey?: string): Promise<RpcWorkflowGateResolution> {
527
- const response = await this.#send({
528
- type: "workflow_gate_response",
529
- gate_id: gateId,
530
- answer,
531
- idempotency_key: idempotencyKey,
532
- });
533
- return this.#getData(response);
534
- }
535
-
536
- /**
537
- * Subscribe to extension UI requests emitted by the server (e.g. select /
538
- * input / editor / confirm). Returns an unsubscribe function.
539
- */
540
- onExtensionUiRequest(listener: (req: RpcExtensionUIRequest) => void): () => void {
541
- this.#extensionUiListeners.add(listener);
542
- return () => {
543
- this.#extensionUiListeners.delete(listener);
544
- };
545
- }
546
-
547
- /** Respond to an extension UI request over the live transport. */
548
- respondExtensionUi(response: import("./rpc-types").RpcExtensionUIResponse): void {
549
- this.#writeFrame(response);
550
- }
551
-
552
- /** Observe transport close/error notifications. */
553
- onTransportError(listener: (error: Error) => void): () => void {
554
- const previousClose = this.options.onTransportClose;
555
- const previousError = this.options.onTransportError;
556
- this.options.onTransportClose = error => {
557
- previousClose?.(error);
558
- listener(error ?? new Error("RPC transport closed"));
559
- };
560
- this.options.onTransportError = error => {
561
- previousError?.(error);
562
- listener(error);
563
- };
564
- return () => {
565
- this.options.onTransportClose = previousClose;
566
- this.options.onTransportError = previousError;
567
- };
568
- }
569
-
570
- /**
571
- * Enter unattended mode by declaring budget + scopes + action allowlist.
572
- * Returns the accepted declaration, or rejects (fail-closed) on refusal.
573
- */
574
- async negotiateUnattended(declaration: RpcUnattendedDeclaration): Promise<RpcUnattendedAccepted> {
575
- const response = await this.#send({ type: "negotiate_unattended", declaration });
576
- return this.#getData(response);
577
- }
578
-
579
- /**
580
- * Get collected stderr output (useful for debugging).
581
- */
582
- getStderr(): string {
583
- return this.#transport?.getStderr() ?? "";
584
- }
585
-
586
- #startTimeout(timeoutMs: number, onTimeout: () => void): NodeJS.Timeout {
587
- const timer = setTimeout(onTimeout, timeoutMs);
588
- timer.unref();
589
- return timer;
590
- }
591
-
592
- // =========================================================================
593
- // Command Methods
594
- // =========================================================================
595
-
596
- /**
597
- * Send a prompt to the agent.
598
- * Returns immediately after sending; use onEvent() to receive streaming events.
599
- * Use waitForIdle() to wait for completion.
600
- */
601
- async prompt(message: string, images?: ImageContent[]): Promise<void> {
602
- await this.#send({ type: "prompt", message, images });
603
- }
604
-
605
- /**
606
- * Queue a steering message to interrupt the agent mid-run.
607
- */
608
- async steer(message: string, images?: ImageContent[]): Promise<void> {
609
- await this.#send({ type: "steer", message, images });
610
- }
611
-
612
- /**
613
- * Queue a follow-up message to be processed after the agent finishes.
614
- */
615
- async followUp(message: string, images?: ImageContent[]): Promise<void> {
616
- await this.#send({ type: "follow_up", message, images });
617
- }
618
-
619
- /**
620
- * Abort current operation.
621
- */
622
- async abort(): Promise<void> {
623
- await this.#send({ type: "abort" });
624
- }
625
-
626
- /**
627
- * Abort current operation and immediately start a new turn with the given message.
628
- */
629
- async abortAndPrompt(message: string, images?: ImageContent[]): Promise<void> {
630
- await this.#send({ type: "abort_and_prompt", message, images });
631
- }
632
-
633
- /**
634
- * Start a new session, optionally with parent tracking.
635
- * @param parentSession - Optional parent session path for lineage tracking
636
- * @returns Object with `cancelled: true` if an extension cancelled the new session
637
- */
638
- async newSession(parentSession?: string): Promise<{ cancelled: boolean }> {
639
- const response = await this.#send({ type: "new_session", parentSession });
640
- return this.#getData(response);
641
- }
642
-
643
- /**
644
- * Get current session state.
645
- */
646
- async getState(include?: RpcGetStateInclude[]): Promise<RpcSessionState> {
647
- const response = await this.#send(include ? { type: "get_state", include } : { type: "get_state" });
648
- return this.#getData(response);
649
- }
650
-
651
- /** Return unresolved workflow gates persisted by the RPC session. */
652
- async getPendingWorkflowGates(): Promise<RpcWorkflowGate[]> {
653
- const response = await this.#send({ type: "get_pending_workflow_gates" });
654
- return this.#getData<{ gates: RpcWorkflowGate[] }>(response).gates;
655
- }
656
-
657
- /**
658
- * Set model by provider and ID.
659
- */
660
- async setModel(provider: string, modelId: string): Promise<{ provider: string; id: string }> {
661
- const response = await this.#send({ type: "set_model", provider, modelId });
662
- return this.#getData(response);
663
- }
664
-
665
- async setDefaultModelSelection(
666
- provider: string,
667
- modelId: string,
668
- thinkingLevel: ResolvedThinkingLevel,
669
- ): Promise<RpcDefaultModelSelection> {
670
- const response = await this.#send(
671
- {
672
- type: "set_default_model_selection",
673
- provider,
674
- modelId,
675
- thinkingLevel,
676
- },
677
- DEFAULT_MODEL_SELECTION_TIMEOUT_MS,
678
- );
679
- if (!response.success) return this.#getData(response);
680
- if (response.command !== "set_default_model_selection" || !isDefaultModelSelection(response.data)) {
681
- throw new Error("Invalid set_default_model_selection response");
682
- }
683
- return this.#getData(response);
684
- }
685
-
686
- /**
687
- * Cycle to next model.
688
- */
689
- async cycleModel(): Promise<{
690
- model: { provider: string; id: string };
691
- thinkingLevel: ThinkingLevel | undefined;
692
- isScoped: boolean;
693
- } | null> {
694
- const response = await this.#send({ type: "cycle_model" });
695
- return this.#getData(response);
696
- }
697
-
698
- /**
699
- * Get list of available models.
700
- */
701
- async getAvailableModels(): Promise<ModelInfo[]> {
702
- const response = await this.#send({ type: "get_available_models" });
703
- return this.#getData<{ models: ModelInfo[] }>(response).models;
704
- }
705
-
706
- /**
707
- * Set thinking level.
708
- */
709
- async setThinkingLevel(level: ThinkingLevel): Promise<void> {
710
- await this.#send({ type: "set_thinking_level", level });
711
- }
712
-
713
- /**
714
- * Cycle thinking level.
715
- */
716
- async cycleThinkingLevel(): Promise<{ level: ThinkingLevel } | null> {
717
- const response = await this.#send({ type: "cycle_thinking_level" });
718
- return this.#getData(response);
719
- }
720
-
721
- /**
722
- * Set steering mode.
723
- */
724
- async setSteeringMode(mode: "all" | "one-at-a-time"): Promise<void> {
725
- await this.#send({ type: "set_steering_mode", mode });
726
- }
727
-
728
- /**
729
- * Set follow-up mode.
730
- */
731
- async setFollowUpMode(mode: "all" | "one-at-a-time"): Promise<void> {
732
- await this.#send({ type: "set_follow_up_mode", mode });
733
- }
734
-
735
- /**
736
- * Compact session context.
737
- */
738
- async compact(customInstructions?: string): Promise<CompactionResult> {
739
- const response = await this.#send({ type: "compact", customInstructions });
740
- return this.#getData(response);
741
- }
742
-
743
- /**
744
- * Set auto-compaction enabled/disabled.
745
- */
746
- async setAutoCompaction(enabled: boolean): Promise<void> {
747
- await this.#send({ type: "set_auto_compaction", enabled });
748
- }
749
-
750
- /**
751
- * Set auto-retry enabled/disabled.
752
- */
753
- async setAutoRetry(enabled: boolean): Promise<void> {
754
- await this.#send({ type: "set_auto_retry", enabled });
755
- }
756
-
757
- /**
758
- * Abort in-progress retry.
759
- */
760
- async abortRetry(): Promise<void> {
761
- await this.#send({ type: "abort_retry" });
762
- }
763
-
764
- /**
765
- * Execute a bash command.
766
- */
767
- async bash(command: string): Promise<BashResult> {
768
- const response = await this.#send({ type: "bash", command });
769
- return this.#getData(response);
770
- }
771
-
772
- /**
773
- * Abort running bash command.
774
- */
775
- async abortBash(): Promise<void> {
776
- await this.#send({ type: "abort_bash" });
777
- }
778
-
779
- /**
780
- * Get session statistics.
781
- */
782
- async getSessionStats(): Promise<SessionStats> {
783
- const response = await this.#send({ type: "get_session_stats" });
784
- return this.#getData(response);
785
- }
786
-
787
- /**
788
- * Hand off session context to a new session.
789
- */
790
- async handoff(customInstructions?: string): Promise<RpcHandoffResult | null> {
791
- const response = await this.#send({ type: "handoff", customInstructions });
792
- return this.#getData(response);
793
- }
794
-
795
- /**
796
- * Export session to HTML.
797
- */
798
- async exportHtml(outputPath?: string): Promise<{ path: string }> {
799
- const response = await this.#send({ type: "export_html", outputPath });
800
- return this.#getData(response);
801
- }
802
-
803
- /**
804
- * Switch to a different session file.
805
- * @returns Object with `cancelled: true` if an extension cancelled the switch
806
- */
807
- async switchSession(sessionPath: string): Promise<{ cancelled: boolean }> {
808
- const response = await this.#send({ type: "switch_session", sessionPath });
809
- return this.#getData(response);
810
- }
811
-
812
- /**
813
- * Branch from a specific message.
814
- * @returns Object with `text` (the message text) and `cancelled` (if extension cancelled)
815
- */
816
- async branch(entryId: string): Promise<{ text: string; cancelled: boolean }> {
817
- const response = await this.#send({ type: "branch", entryId });
818
- return this.#getData(response);
819
- }
820
-
821
- /**
822
- * Get messages available for branching.
823
- */
824
- async getBranchMessages(): Promise<Array<{ entryId: string; text: string }>> {
825
- const response = await this.#send({ type: "get_branch_messages" });
826
- return this.#getData<{ messages: Array<{ entryId: string; text: string }> }>(response).messages;
827
- }
828
-
829
- /**
830
- * Get text of last assistant message.
831
- */
832
- async getLastAssistantText(): Promise<string | null> {
833
- const response = await this.#send({ type: "get_last_assistant_text" });
834
- return this.#getData<{ text: string | null }>(response).text;
835
- }
836
-
837
- /**
838
- * Get all messages in the session.
839
- */
840
- async getMessages(): Promise<AgentMessage[]> {
841
- const response = await this.#send({ type: "get_messages" });
842
- return this.#getData<{ messages: AgentMessage[] }>(response).messages;
843
- }
844
-
845
- /**
846
- * Get list of OAuth providers available for login, with their current authentication status.
847
- */
848
- async getLoginProviders(): Promise<Array<{ id: string; name: string; available: boolean; authenticated: boolean }>> {
849
- const response = await this.#send({ type: "get_login_providers" });
850
- return this.#getData<{
851
- providers: Array<{ id: string; name: string; available: boolean; authenticated: boolean }>;
852
- }>(response).providers;
853
- }
854
-
855
- /**
856
- * Trigger OAuth login for the given provider.
857
- * The server will emit an `open_url` extension_ui_request for the auth URL.
858
- * Resolves when login completes or rejects on failure.
859
- *
860
- * @param onOpenUrl Called when the server emits the auth URL. The host must open
861
- * it in a browser for the callback-server OAuth flow to complete.
862
- */
863
- async login(
864
- providerId: string,
865
- options?: { onOpenUrl?: (url: string, instructions?: string) => void },
866
- ): Promise<{ providerId: string }> {
867
- const { onOpenUrl } = options ?? {};
868
- const listener = onOpenUrl
869
- ? (req: RpcExtensionUIRequest) => {
870
- if (req.method === "open_url") onOpenUrl(req.url, req.instructions);
871
- }
872
- : undefined;
873
- if (listener) this.#extensionUiListeners.add(listener);
874
- try {
875
- const response = await this.#send({ type: "login", providerId }, 600_000);
876
- return this.#getData<{ providerId: string }>(response);
877
- } finally {
878
- if (listener) this.#extensionUiListeners.delete(listener);
879
- }
880
- }
881
-
882
- /**
883
- * Replace the host-owned custom tools exposed to the RPC session.
884
- * Changes take effect before the next model call.
885
- */
886
- async setCustomTools(tools: RpcClientCustomTool[]): Promise<string[]> {
887
- this.#customTools = [...tools];
888
- if (!this.#transport) {
889
- return this.#customTools.map(tool => tool.name);
890
- }
891
- const definitions: RpcHostToolDefinition[] = this.#customTools.map(tool => ({
892
- name: tool.name,
893
- label: tool.label,
894
- description: tool.description,
895
- parameters: tool.parameters,
896
- hidden: tool.hidden,
897
- }));
898
- const response = await this.#send({ type: "set_host_tools", tools: definitions });
899
- return this.#getData<{ toolNames: string[] }>(response).toolNames;
900
- }
901
-
902
- // =========================================================================
903
- // Helpers
904
- // =========================================================================
905
-
906
- /**
907
- * Wait for agent to become idle (no streaming).
908
- * Resolves when agent_end event is received.
909
- */
910
- waitForIdle(timeout = 60000): Promise<void> {
911
- const { promise, resolve, reject } = Promise.withResolvers<void>();
912
- let settled = false;
913
- const unsubscribe = this.onEvent(event => {
914
- if (event.type === "agent_end") {
915
- settled = true;
916
- unsubscribe();
917
- clearTimeout(timeoutId);
918
- resolve();
919
- }
920
- });
921
-
922
- const timeoutId = this.#startTimeout(timeout, () => {
923
- if (settled) return;
924
- settled = true;
925
- unsubscribe();
926
- reject(new Error(`Timeout waiting for agent to become idle. Stderr: ${this.#transport?.getStderr() ?? ""}`));
927
- });
928
- return promise;
929
- }
930
-
931
- /**
932
- * Collect events until agent becomes idle.
933
- */
934
- collectEvents(timeout = 60000): Promise<AgentEvent[]> {
935
- const { promise, resolve, reject } = Promise.withResolvers<AgentEvent[]>();
936
- const events: AgentEvent[] = [];
937
- let settled = false;
938
- const unsubscribe = this.onEvent(event => {
939
- events.push(event);
940
- if (event.type === "agent_end") {
941
- settled = true;
942
- unsubscribe();
943
- clearTimeout(timeoutId);
944
- resolve(events);
945
- }
946
- });
947
-
948
- const timeoutId = this.#startTimeout(timeout, () => {
949
- if (settled) return;
950
- settled = true;
951
- unsubscribe();
952
- reject(new Error(`Timeout collecting events. Stderr: ${this.#transport?.getStderr() ?? ""}`));
953
- });
954
- return promise;
955
- }
956
-
957
- /**
958
- * Send prompt and wait for completion, returning all events.
959
- */
960
- async promptAndWait(message: string, images?: ImageContent[], timeout = 60000): Promise<AgentEvent[]> {
961
- const eventsPromise = this.collectEvents(timeout);
962
- await this.prompt(message, images);
963
- return eventsPromise;
964
- }
965
-
966
- // =========================================================================
967
- // Internal
968
- // =========================================================================
969
-
970
- #handleLine(data: unknown): void {
971
- // Check if it's a response to a pending request
972
- if (isRpcResponse(data)) {
973
- const id = data.id;
974
- if (id && this.#pendingRequests.has(id)) {
975
- const pending = this.#pendingRequests.get(id)!;
976
- this.#pendingRequests.delete(id);
977
- pending.resolve(data);
978
- return;
979
- }
980
- }
981
-
982
- if (isRpcHostToolCallRequest(data)) {
983
- void this.#handleHostToolCall(data);
984
- return;
985
- }
986
-
987
- if (isRpcExtensionUiRequest(data)) {
988
- for (const listener of this.#extensionUiListeners) {
989
- listener(data);
990
- }
991
- return;
992
- }
993
-
994
- if (isRpcWorkflowGate(data)) {
995
- for (const listener of this.#workflowGateListeners) {
996
- listener(data);
997
- }
998
- return;
999
- }
1000
-
1001
- if (isRpcHostToolCancelRequest(data)) {
1002
- this.#pendingHostToolCalls.get(data.targetId)?.controller.abort();
1003
- return;
1004
- }
1005
-
1006
- // Canonical agent-wire event frame: { type:"event", payload:{ event_type, event } }.
1007
- const event = unwrapAgentWireEventFrame(data);
1008
- if (!isAgentSessionEvent(event)) return;
1009
-
1010
- for (const listener of this.#sessionEventListeners) {
1011
- listener(event);
1012
- }
1013
- if (!isCoreAgentEvent(event)) return;
1014
- for (const listener of this.#eventListeners) {
1015
- listener(event);
1016
- }
1017
- }
1018
-
1019
- #send(command: RpcCommandBody, timeoutMs = 30_000): Promise<RpcResponse> {
1020
- if (!this.#transport || this.#transportClosed) {
1021
- throw new Error("Client not started");
1022
- }
1023
-
1024
- const id = `req_${++this.#requestId}`;
1025
- const fullCommand = { ...command, id } as RpcCommand;
1026
- const { promise, resolve, reject } = Promise.withResolvers<RpcResponse>();
1027
- let settled = false;
1028
- const timeoutId = this.#startTimeout(timeoutMs, () => {
1029
- if (settled) return;
1030
- this.#pendingRequests.delete(id);
1031
- settled = true;
1032
- reject(
1033
- new Error(`Timeout waiting for response to ${command.type}. Stderr: ${this.#transport?.getStderr() ?? ""}`),
1034
- );
1035
- });
1036
-
1037
- this.#pendingRequests.set(id, {
1038
- resolve: response => {
1039
- if (settled) return;
1040
- settled = true;
1041
- clearTimeout(timeoutId);
1042
- resolve(response);
1043
- },
1044
- reject: error => {
1045
- if (settled) return;
1046
- settled = true;
1047
- clearTimeout(timeoutId);
1048
- reject(error);
1049
- },
1050
- });
1051
-
1052
- this.#writeFrame(fullCommand, err => {
1053
- this.#pendingRequests.delete(id);
1054
- if (settled) return;
1055
- settled = true;
1056
- clearTimeout(timeoutId);
1057
- reject(err);
1058
- });
1059
- return promise;
1060
- }
1061
-
1062
- async #handleHostToolCall(request: RpcHostToolCallRequest): Promise<void> {
1063
- const tool = this.#customTools.find(candidate => candidate.name === request.toolName);
1064
- if (!tool) {
1065
- this.#writeFrame({
1066
- type: "host_tool_result",
1067
- id: request.id,
1068
- result: {
1069
- content: [{ type: "text", text: `Host tool "${request.toolName}" is not registered` }],
1070
- details: {},
1071
- },
1072
- isError: true,
1073
- } satisfies RpcHostToolResult);
1074
- return;
1075
- }
1076
-
1077
- const controller = new AbortController();
1078
- this.#pendingHostToolCalls.set(request.id, { controller });
1079
-
1080
- const sendUpdate = (partialResult: RpcClientToolResult<unknown>): void => {
1081
- if (controller.signal.aborted) return;
1082
- this.#writeFrame({
1083
- type: "host_tool_update",
1084
- id: request.id,
1085
- partialResult: normalizeToolResult(partialResult),
1086
- } satisfies RpcHostToolUpdate);
1087
- };
1088
-
1089
- try {
1090
- const result = await tool.execute(request.arguments, {
1091
- toolCallId: request.toolCallId,
1092
- signal: controller.signal,
1093
- sendUpdate,
1094
- });
1095
- if (controller.signal.aborted) return;
1096
- this.#writeFrame({
1097
- type: "host_tool_result",
1098
- id: request.id,
1099
- result: normalizeToolResult(result),
1100
- } satisfies RpcHostToolResult);
1101
- } catch (error) {
1102
- if (controller.signal.aborted) return;
1103
- this.#writeFrame({
1104
- type: "host_tool_result",
1105
- id: request.id,
1106
- result: {
1107
- content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
1108
- details: {},
1109
- },
1110
- isError: true,
1111
- } satisfies RpcHostToolResult);
1112
- } finally {
1113
- this.#pendingHostToolCalls.delete(request.id);
1114
- }
1115
- }
1116
-
1117
- #writeFrame(
1118
- frame:
1119
- | RpcCommand
1120
- | RpcWorkflowGateResponse
1121
- | RpcHostToolResult
1122
- | RpcHostToolUpdate
1123
- | import("./rpc-types").RpcExtensionUIResponse,
1124
- onError?: (error: Error) => void,
1125
- ): void {
1126
- if (!this.#transport || this.#transportClosed) {
1127
- throw new Error("Client not started");
1128
- }
1129
- try {
1130
- this.#transport.write(frame);
1131
- } catch (err) {
1132
- const error = err instanceof Error ? err : new Error(String(err));
1133
- onError?.(error);
1134
- this.#handleTransportClose(error);
1135
- }
1136
- }
1137
-
1138
- #handleTransportClose(error?: Error): void {
1139
- if (this.#transportClosed) return;
1140
- this.#transportClosed = true;
1141
- const closeError = error ?? new Error("RPC transport closed");
1142
- this.options.onTransportClose?.(closeError);
1143
- this.options.onTransportError?.(closeError);
1144
- this.#rejectAllPending(closeError);
1145
- }
1146
-
1147
- #rejectAllPending(error: Error): void {
1148
- for (const pending of this.#pendingRequests.values()) pending.reject(error);
1149
- this.#pendingRequests.clear();
1150
- for (const pendingCall of this.#pendingHostToolCalls.values()) pendingCall.controller.abort();
1151
- this.#pendingHostToolCalls.clear();
1152
- }
1153
-
1154
- #getData<T>(response: RpcResponse): T {
1155
- if (!response.success) {
1156
- const errorResponse = response as Extract<RpcResponse, { success: false }>;
1157
- throw new Error(
1158
- typeof errorResponse.error === "string" ? errorResponse.error : JSON.stringify(errorResponse.error),
1159
- );
1160
- }
1161
- // Type assertion: we trust response.data matches T based on the command sent.
1162
- // This is safe because each public method specifies the correct T for its command.
1163
- const successResponse = response as Extract<RpcResponse, { success: true; data: unknown }>;
1164
- return successResponse.data as T;
1165
- }
1166
- }