@akira-tl/forgerelay 0.8.7 → 0.8.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 (246) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +10 -18
  3. package/capabilities/host-integration/GUIDE.md +2 -2
  4. package/dist/activity/{audit-store.js → history/audit-store.js} +129 -21
  5. package/dist/activity/history/bash-output-store.js +250 -0
  6. package/dist/activity/{host-turn-store.js → history/host-turn-store.js} +4 -1
  7. package/dist/activity/{query-service.js → history/query-service.js} +13 -7
  8. package/dist/activity/{lifecycle.js → runtime/lifecycle.js} +1 -1
  9. package/dist/activity/{mcp-query-tools.js → runtime/mcp-query-tools.js} +3 -3
  10. package/dist/activity/storage/history.js +12 -0
  11. package/dist/activity/storage/paths.js +56 -0
  12. package/dist/activity/storage/segmented-log.js +149 -0
  13. package/dist/cli/setup-support.js +116 -0
  14. package/dist/cli.js +18 -126
  15. package/dist/lsp/code-intelligence.js +1 -1
  16. package/dist/lsp/test-support/server-fixture.js +9 -9
  17. package/dist/mcp/filesystem/filesystem-tools.js +245 -0
  18. package/dist/mcp/hooks/command-runner.js +79 -0
  19. package/dist/{hook-cli.js → mcp/hooks/hook-cli.js} +1 -1
  20. package/dist/{hooks.js → mcp/hooks/hooks.js} +3 -80
  21. package/dist/{oauth-provider.js → mcp/oauth/oauth-provider.js} +1 -1
  22. package/dist/{oauth-store.js → mcp/oauth/oauth-store.js} +5 -4
  23. package/dist/{operations → mcp/operations}/native-bulk-mutations.js +2 -2
  24. package/dist/mcp/panel/app.js +143 -0
  25. package/dist/{mcp-app-template.js → mcp/panel/mcp-app-template.js} +2 -38
  26. package/dist/mcp/process/runtime.js +257 -0
  27. package/dist/mcp/process/tools.js +346 -0
  28. package/dist/mcp/server/core/activity-support.js +70 -0
  29. package/dist/{capabilities.js → mcp/server/core/capabilities.js} +5 -3
  30. package/dist/{capability-registry.js → mcp/server/core/capability-registry.js} +2 -2
  31. package/dist/mcp/server/core/capability-support.js +90 -0
  32. package/dist/mcp/server/core/schemas.js +216 -0
  33. package/dist/mcp/server/core/tool-support.js +207 -0
  34. package/dist/mcp/server/core/version.js +9 -0
  35. package/dist/mcp/server/operations/runtime/filesystem-tools.js +400 -0
  36. package/dist/mcp/server/operations/runtime/operation-runtime.js +523 -0
  37. package/dist/mcp/server/transport/http-server.js +301 -0
  38. package/dist/mcp/server/transport/http-support.js +43 -0
  39. package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +222 -0
  40. package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +187 -0
  41. package/dist/mcp/server/workspace/runtime/workspace-open.js +552 -0
  42. package/dist/mcp/server/workspace/runtime/workspace-tools.js +601 -0
  43. package/dist/{config.js → runtime/config/config.js} +3 -3
  44. package/dist/{user-config.js → runtime/config/user-config.js} +14 -54
  45. package/dist/{db → runtime/state/db}/client.js +1 -1
  46. package/dist/{db → runtime/state/db}/migrations.js +34 -3
  47. package/dist/runtime/state/lock/file-lock.js +130 -0
  48. package/dist/runtime/testing/server-fixture.js +190 -0
  49. package/dist/server.js +139 -4192
  50. package/dist/subagents/providers/adapters/claude.js +86 -52
  51. package/dist/subagents/providers/adapters/codex.js +103 -39
  52. package/dist/subagents/providers/adapters/opencode.js +114 -84
  53. package/dist/subagents/providers/adapters/pi.js +59 -8
  54. package/dist/subagents/providers/availability.js +15 -30
  55. package/dist/subagents/providers/runtime/external-command.js +56 -0
  56. package/dist/subagents/sessions/capability.js +1 -1
  57. package/dist/subagents/sessions/execution.js +1 -1
  58. package/dist/subagents/sessions/mcp/activity-audit-test-support.js +30 -0
  59. package/dist/subagents/sessions/store.js +1 -1
  60. package/dist/ui/.vite/manifest.json +342 -409
  61. package/dist/ui/activity-panel-app.html +2 -4
  62. package/dist/ui/assets/activity-panel-app-7Xe0alhm.css +1 -0
  63. package/dist/ui/assets/activity-panel-app-Sl1Jro8v.js +115 -0
  64. package/dist/ui/assets/angular-html-B_yn91wW.js +1 -0
  65. package/dist/ui/assets/{angular-ts-Bxh4-O1-.js → angular-ts-DSgtloKz.js} +1 -1
  66. package/dist/ui/assets/{apl-BmYHKQ6m.js → apl-NatWtF8X.js} +1 -1
  67. package/dist/ui/assets/{astro-C51vqDKq.js → astro-BYFEVBuN.js} +1 -1
  68. package/dist/ui/assets/{blade-CdhVsmwq.js → blade-DoO7yvMR.js} +1 -1
  69. package/dist/ui/assets/c-Rl2hMCcg.js +1 -0
  70. package/dist/ui/assets/{cobol-Cf6azoN_.js → cobol-ByC5mWiB.js} +1 -1
  71. package/dist/ui/assets/{coffee-vVdGzlmt.js → coffee-Dez77Qov.js} +1 -1
  72. package/dist/ui/assets/cpp-DtaAQT9h.js +1 -0
  73. package/dist/ui/assets/{crystal-5UZy6KIX.js → crystal-Bgmbf8ec.js} +1 -1
  74. package/dist/ui/assets/css-C1tjBtVN.js +1 -0
  75. package/dist/ui/assets/{edge-CESgo4ia.js → edge-DeH6u8Wg.js} +1 -1
  76. package/dist/ui/assets/{elixir-9FKfw4cJ.js → elixir-_H3KnmWT.js} +1 -1
  77. package/dist/ui/assets/{elm-Dv7A0aLa.js → elm-CsCe3U28.js} +1 -1
  78. package/dist/ui/assets/{erb-DHFmDvM0.js → erb-CbM-L2g8.js} +1 -1
  79. package/dist/ui/assets/{git-rebase-utbU6CzV.js → git-rebase-D0s5pDh9.js} +1 -1
  80. package/dist/ui/assets/{glimmer-js-t2kiCkyB.js → glimmer-js-BfOToq_A.js} +1 -1
  81. package/dist/ui/assets/{glimmer-ts-F0Jv5okV.js → glimmer-ts-CCUOkL2K.js} +1 -1
  82. package/dist/ui/assets/glsl-DH4ec9dE.js +1 -0
  83. package/dist/ui/assets/graphql-DilAMBoI.js +1 -0
  84. package/dist/ui/assets/{hack-8yjdbaSA.js → hack-BDkNDZlF.js} +1 -1
  85. package/dist/ui/assets/haml-D_QBp6K7.js +1 -0
  86. package/dist/ui/assets/{handlebars-BBFEg4Lo.js → handlebars-DePUrTpC.js} +1 -1
  87. package/dist/ui/assets/heavy-payload-DOwZpO6n.js +290 -0
  88. package/dist/ui/assets/html-By4dNnM4.js +1 -0
  89. package/dist/ui/assets/{html-derivative-DJAQcZyc.js → html-derivative-DDw9Ef4Q.js} +1 -1
  90. package/dist/ui/assets/{http-Dg5LSYRK.js → http-B0QxWc9M.js} +1 -1
  91. package/dist/ui/assets/{hurl-Y5uYzMZR.js → hurl-vMQ4lda1.js} +1 -1
  92. package/dist/ui/assets/java-DYRe2lWG.js +1 -0
  93. package/dist/ui/assets/javascript-CxyGqwcL.js +1 -0
  94. package/dist/ui/assets/{jinja-Bt30h8Ik.js → jinja-JUwV-fZt.js} +1 -1
  95. package/dist/ui/assets/{jison-D5uZ1Nyo.js → jison-7ZqCEvS8.js} +1 -1
  96. package/dist/ui/assets/json-dbltgiiA.js +1 -0
  97. package/dist/ui/assets/jsx-oDopT7YQ.js +1 -0
  98. package/dist/ui/assets/{julia-76hRaaGz.js → julia-CkKiNSav.js} +1 -1
  99. package/dist/ui/assets/{just-CDE7W0vW.js → just-DuqCAKJ3.js} +1 -1
  100. package/dist/ui/assets/{latex-CVO2Byab.js → latex-sudcRDSn.js} +1 -1
  101. package/dist/ui/assets/{liquid-CA-AS3EX.js → liquid-Di-08Js7.js} +1 -1
  102. package/dist/ui/assets/lua-YlqdMoV0.js +1 -0
  103. package/dist/ui/assets/{marko-_BWrPfny.js → marko-CJRNyJDb.js} +1 -1
  104. package/dist/ui/assets/{mdc-PyJv85eS.js → mdc-t12CzZHs.js} +1 -1
  105. package/dist/ui/assets/{nginx-DAWVw8yu.js → nginx-57-b4XwT.js} +1 -1
  106. package/dist/ui/assets/{nim-CKE1dzpU.js → nim-BzrswYQa.js} +1 -1
  107. package/dist/ui/assets/{perl-BBhWva19.js → perl-D9JR_iIh.js} +1 -1
  108. package/dist/ui/assets/{php-ChIeaAoF.js → php-Py2gFHil.js} +1 -1
  109. package/dist/ui/assets/{pug-CMgI5afi.js → pug-PzLLy0h1.js} +1 -1
  110. package/dist/ui/assets/{qml-CjxTkcFH.js → qml-By8fwgV4.js} +1 -1
  111. package/dist/ui/assets/r-CcezTbLq.js +1 -0
  112. package/dist/ui/assets/{razor-i4DbHr8p.js → razor-dG-vdhL_.js} +1 -1
  113. package/dist/ui/assets/regexp-BlsQ-N_8.js +1 -0
  114. package/dist/ui/assets/{rst-oy_97Kl4.js → rst-C2awDCat.js} +1 -1
  115. package/dist/ui/assets/{ruby-C5zYZIyl.js → ruby-zA2W_49d.js} +1 -1
  116. package/dist/ui/assets/{sas-BnMEh2S3.js → sas-D49bhyaN.js} +1 -1
  117. package/dist/ui/assets/scss-BQb-cp01.js +1 -0
  118. package/dist/ui/assets/shellscript-Dga4H_Ni.js +1 -0
  119. package/dist/ui/assets/{shellsession-BpE5o7_3.js → shellsession-n3-5CqcF.js} +1 -1
  120. package/dist/ui/assets/{soy-COesmhTY.js → soy-XYBOf_BA.js} +1 -1
  121. package/dist/ui/assets/sql-ET4u5Cpl.js +1 -0
  122. package/dist/ui/assets/{stata-CGQEuSZq.js → stata-CLHcJHO_.js} +1 -1
  123. package/dist/ui/assets/{surrealql-Bz4JbU-9.js → surrealql-BdjQ60T4.js} +1 -1
  124. package/dist/ui/assets/{svelte-1jM2ejXj.js → svelte-B2RPtKJe.js} +1 -1
  125. package/dist/ui/assets/{templ-C-VwELcp.js → templ-CrByPW_W.js} +1 -1
  126. package/dist/ui/assets/{tex-t1rUiQCm.js → tex-DZBkg363.js} +1 -1
  127. package/dist/ui/assets/{ts-tags-Bewoa6pS.js → ts-tags-DJR3Gphb.js} +1 -1
  128. package/dist/ui/assets/tsx-CajzROyy.js +1 -0
  129. package/dist/ui/assets/{twig-DVg-vELl.js → twig-BZjk5Zda.js} +1 -1
  130. package/dist/ui/assets/typescript-B-EWToLj.js +1 -0
  131. package/dist/ui/assets/{vue-wPQ7UlMy.js → vue-D_rHCCr8.js} +1 -1
  132. package/dist/ui/assets/{vue-html-BHtJ8geX.js → vue-html-CC4WlvVt.js} +1 -1
  133. package/dist/ui/assets/{vue-vine-CNSGAWwc.js → vue-vine-BgNMVgpk.js} +1 -1
  134. package/dist/ui/assets/xml-BI86-WbH.js +1 -0
  135. package/dist/ui/assets/{xsl-3_vJ27aE.js → xsl-DXeLZV4J.js} +1 -1
  136. package/dist/ui/assets/yaml-60_PFlWa.js +1 -0
  137. package/dist/workspaces/bootstrap.js +72 -0
  138. package/dist/workspaces/context.js +302 -0
  139. package/dist/workspaces/conversation-test-support.js +86 -0
  140. package/dist/{git-worktrees.js → workspaces/git/git-worktrees.js} +1 -1
  141. package/dist/workspaces/inventory.js +131 -0
  142. package/dist/workspaces/paths.js +55 -0
  143. package/dist/{remote-auth.js → workspaces/relay/auth/remote-auth.js} +2 -2
  144. package/dist/workspaces/relay/result-support.js +111 -0
  145. package/dist/workspaces/relay/tests/test-support.js +203 -0
  146. package/dist/{remote-mcp-connection-pool.js → workspaces/relay/transport/remote-mcp-connection-pool.js} +1 -1
  147. package/dist/workspaces/relay/types.js +1 -0
  148. package/dist/{remote-workspace-relay.js → workspaces/relay/workspace-relay.js} +23 -172
  149. package/dist/workspaces/resources/skills.js +285 -0
  150. package/dist/{review-checkpoints.js → workspaces/review/review-checkpoints.js} +2 -2
  151. package/dist/workspaces/sessions.js +457 -0
  152. package/dist/{workspace-store.js → workspaces/state/workspace-store.js} +2 -2
  153. package/dist/workspaces.js +69 -991
  154. package/docs/chatgpt-coding-workflow.md +3 -11
  155. package/docs/configuration.md +12 -25
  156. package/docs/gotchas.md +0 -30
  157. package/docs/roadmap.md +6 -10
  158. package/docs/security.md +1 -5
  159. package/docs/setup.md +0 -11
  160. package/package.json +6 -8
  161. package/scripts/ci/architecture.mjs +109 -0
  162. package/scripts/ci/verify.mjs +5 -0
  163. package/scripts/debug/accept/bootstrap.mjs +326 -0
  164. package/scripts/debug/accept/harness.mjs +76 -0
  165. package/scripts/debug/accept/support.mjs +432 -0
  166. package/scripts/debug/accept.mjs +43 -806
  167. package/scripts/debug/relay-accept/support.mjs +135 -0
  168. package/scripts/debug/relay-accept.mjs +26 -115
  169. package/scripts/debug/traffic/support.mjs +167 -0
  170. package/scripts/debug/traffic/traffic-audit.mjs +4 -161
  171. package/scripts/{lsp-interop-support.test.mjs → lsp/interop-support.test.mjs} +1 -1
  172. package/scripts/{lsp-interop.mjs → lsp/interop.mjs} +3 -3
  173. package/scripts/wiki/sync.mjs +21 -10
  174. package/dist/activity/bash-output-store.js +0 -127
  175. package/dist/pi-tools.js +0 -110
  176. package/dist/skills.js +0 -127
  177. package/dist/ui/assets/activity-panel-app-CUAN6zyW.js +0 -2
  178. package/dist/ui/assets/angular-html-BkgDQl-G.js +0 -1
  179. package/dist/ui/assets/c-IBzBpxAO.js +0 -1
  180. package/dist/ui/assets/chunk-EyZ2wyi3.js +0 -1
  181. package/dist/ui/assets/cpp-CVHeO-Jk.js +0 -1
  182. package/dist/ui/assets/css-DEfus9J5.js +0 -1
  183. package/dist/ui/assets/glsl-_ooCjaeB.js +0 -1
  184. package/dist/ui/assets/graphql-B9LqP-cY.js +0 -1
  185. package/dist/ui/assets/haml-BU5EoZBn.js +0 -1
  186. package/dist/ui/assets/heavy-payload-CgzrutLm.js +0 -4
  187. package/dist/ui/assets/html-D-C6YZk4.js +0 -1
  188. package/dist/ui/assets/java-ppHvycj5.js +0 -1
  189. package/dist/ui/assets/javascript-DDTjKLlQ.js +0 -1
  190. package/dist/ui/assets/json-Cw91r8h1.js +0 -1
  191. package/dist/ui/assets/jsx-04X2FT_1.js +0 -1
  192. package/dist/ui/assets/lua-CiyBp52Y.js +0 -1
  193. package/dist/ui/assets/r-DfFga4si.js +0 -1
  194. package/dist/ui/assets/regexp-iC9_nbSI.js +0 -1
  195. package/dist/ui/assets/review-payload-BrLbezbq.js +0 -1
  196. package/dist/ui/assets/scrollbar-CbhpdW05.js +0 -287
  197. package/dist/ui/assets/scss-BfZXsAWQ.js +0 -1
  198. package/dist/ui/assets/shellscript-BC2srNaB.js +0 -1
  199. package/dist/ui/assets/sql-Ccv_yeUm.js +0 -1
  200. package/dist/ui/assets/tsx-CTXzrBkN.js +0 -1
  201. package/dist/ui/assets/typescript--ElHoFkw.js +0 -1
  202. package/dist/ui/assets/workspace-app-Bhj96tsR.js +0 -1
  203. package/dist/ui/assets/workspace-app-CxwJuZyS.js +0 -111
  204. package/dist/ui/assets/workspace-app-D6UR0AFl.js +0 -5
  205. package/dist/ui/assets/workspace-app-ldjBmCJR.css +0 -1
  206. package/dist/ui/assets/workspace-lifecycle-app-Cqfhx9pV.js +0 -1
  207. package/dist/ui/assets/xml-JnmX6vyS.js +0 -1
  208. package/dist/ui/assets/yaml-OAcZkP-w.js +0 -1
  209. package/dist/ui/workspace-app.html +0 -18
  210. package/dist/ui/workspace-lifecycle-app.html +0 -18
  211. package/docs/agent-profile-schema.md +0 -114
  212. package/docs/artifact-exchange.md +0 -65
  213. package/docs/versioning.md +0 -205
  214. /package/capabilities/{managed-worktrees → workspace/managed-worktrees}/GUIDE.md +0 -0
  215. /package/capabilities/{workspace-tasks → workspace/workspace-tasks}/GUIDE.md +0 -0
  216. /package/dist/activity/{process-output-audit.js → runtime/process-output-audit.js} +0 -0
  217. /package/dist/{artifact-error.js → mcp/artifacts/artifact-error.js} +0 -0
  218. /package/dist/{artifact-tools.js → mcp/artifacts/artifact-tools.js} +0 -0
  219. /package/dist/{incoming-artifacts.js → mcp/artifacts/incoming-artifacts.js} +0 -0
  220. /package/dist/{advertised-files.js → mcp/filesystem/advertised-files.js} +0 -0
  221. /package/dist/{apply-patch.js → mcp/filesystem/apply-patch.js} +0 -0
  222. /package/dist/{file-mutations.js → mcp/filesystem/file-mutations.js} +0 -0
  223. /package/dist/{roots.js → mcp/filesystem/roots.js} +0 -0
  224. /package/dist/{oauth → mcp/oauth}/public-url.js +0 -0
  225. /package/dist/{oauth → mcp/oauth}/router.js +0 -0
  226. /package/dist/{operations → mcp/operations}/batch/executor.js +0 -0
  227. /package/dist/{operations → mcp/operations}/batch/scheduler.js +0 -0
  228. /package/dist/{operations → mcp/operations}/batch/types.js +0 -0
  229. /package/dist/{operations → mcp/operations}/bulk-mutation.js +0 -0
  230. /package/dist/{operations → mcp/operations}/bulk-read.js +0 -0
  231. /package/dist/{operations → mcp/operations}/core-operation-executor.js +0 -0
  232. /package/dist/{process-platform.js → mcp/process/process-platform.js} +0 -0
  233. /package/dist/{process-sessions.js → mcp/process/process-sessions.js} +0 -0
  234. /package/dist/{request-meta.js → mcp/request-meta.js} +0 -0
  235. /package/dist/{mcp-sessions.js → mcp/server/transport/mcp-sessions.js} +0 -0
  236. /package/dist/{server-shutdown.js → mcp/server/transport/server-shutdown.js} +0 -0
  237. /package/dist/{logger.js → runtime/logging/logger.js} +0 -0
  238. /package/dist/{db → runtime/state/db}/schema.js +0 -0
  239. /package/dist/{composite-activity.js → workspaces/composite/composite-activity.js} +0 -0
  240. /package/dist/{composite-workspaces.js → workspaces/composite/composite-workspaces.js} +0 -0
  241. /package/dist/{git.js → workspaces/git/git.js} +0 -0
  242. /package/dist/{workspace-presentation.js → workspaces/presentation/workspace-presentation.js} +0 -0
  243. /package/dist/{remote-transport.js → workspaces/relay/transport/remote-transport.js} +0 -0
  244. /package/dist/{workspace-task-reminders.js → workspaces/tasks/workspace-task-reminders.js} +0 -0
  245. /package/dist/{workspace-tasks.js → workspaces/tasks/workspace-tasks.js} +0 -0
  246. /package/scripts/{lsp-interop-support.mjs → lsp/interop-support.mjs} +0 -0
@@ -2,6 +2,10 @@ import { spawn } from "node:child_process";
2
2
  import { removeForgeRelayNodeModulesBinFromPath } from "../path.js";
3
3
  import { asRecord, assertPipedChild, errorMessage, readArray, readNestedString, requireFinalResponse, terminateChildOnAbort, unwrapProviderPayload, } from "../shared.js";
4
4
  const PI_AGENT_TIMEOUT_MS = 120_000;
5
+ const PI_RPC_MAX_LINE_BYTES = 8 * 1024 * 1024;
6
+ const PI_RPC_STDERR_TAIL_BYTES = 128 * 1024;
7
+ const PI_STREAMING_TEXT_MAX_BYTES = 8 * 1024 * 1024;
8
+ const PI_ERROR_PREVIEW_BYTES = 4 * 1024;
5
9
  export class PiRpcSubagentAdapter {
6
10
  provider = "pi";
7
11
  async run(input) {
@@ -22,14 +26,23 @@ export class PiRpcSubagentAdapter {
22
26
  const detachAbort = terminateChildOnAbort(child, input.signal);
23
27
  const rpc = new JsonLineRpc(child);
24
28
  let streamingText = "";
29
+ let streamingTextBytes = 0;
25
30
  let streamingProviderError = "";
26
31
  rpc.onEvent((event) => {
27
32
  const text = extractPiStreamingText([event]);
28
- if (text)
33
+ if (text) {
34
+ const nextBytes = streamingTextBytes + Buffer.byteLength(text, "utf8");
35
+ if (nextBytes > PI_STREAMING_TEXT_MAX_BYTES) {
36
+ rpc.fail(new Error(`Pi streaming response exceeded ${PI_STREAMING_TEXT_MAX_BYTES} bytes.`));
37
+ child.kill();
38
+ return;
39
+ }
29
40
  streamingText += text;
41
+ streamingTextBytes = nextBytes;
42
+ }
30
43
  const providerError = extractPiProviderError(event);
31
44
  if (providerError)
32
- streamingProviderError = providerError;
45
+ streamingProviderError = boundedUtf8Tail(providerError, PI_RPC_STDERR_TAIL_BYTES);
33
46
  });
34
47
  try {
35
48
  const state = await rpc.request({ type: "get_state" });
@@ -72,11 +85,13 @@ export function piCommandEnvironment(env) {
72
85
  PATH: removeForgeRelayNodeModulesBinFromPath(path),
73
86
  };
74
87
  }
75
- class JsonLineRpc {
88
+ export class JsonLineRpc {
76
89
  child;
77
90
  pending = new Map();
78
91
  eventSubscribers = new Set();
92
+ failureSubscribers = new Set();
79
93
  buffer = "";
94
+ bufferBytes = 0;
80
95
  nextId = 1;
81
96
  stderr = "";
82
97
  fatalError;
@@ -84,7 +99,7 @@ class JsonLineRpc {
84
99
  this.child = child;
85
100
  child.stdout.on("data", (chunk) => this.handleStdout(chunk.toString("utf8")));
86
101
  child.stderr.on("data", (chunk) => {
87
- this.stderr += chunk.toString("utf8");
102
+ this.stderr = boundedUtf8Tail(`${this.stderr}${chunk.toString("utf8")}`, PI_RPC_STDERR_TAIL_BYTES);
88
103
  });
89
104
  child.on("exit", (code, signal) => {
90
105
  this.failAll(new Error(`Pi RPC process exited with code ${code ?? "null"} and signal ${signal ?? "null"}\n${this.stderr}`.trim()));
@@ -105,11 +120,15 @@ class JsonLineRpc {
105
120
  this.eventSubscribers.add(callback);
106
121
  return () => this.eventSubscribers.delete(callback);
107
122
  }
123
+ fail(error) {
124
+ this.failAll(error);
125
+ }
108
126
  waitForEvent(predicate, timeoutMs, signal) {
109
127
  return new Promise((resolve, reject) => {
110
128
  const finish = (callback) => {
111
129
  clearTimeout(timer);
112
130
  unsubscribe();
131
+ unsubscribeFailure();
113
132
  signal?.removeEventListener("abort", abort);
114
133
  callback();
115
134
  };
@@ -126,6 +145,7 @@ class JsonLineRpc {
126
145
  return;
127
146
  finish(() => resolve(event));
128
147
  });
148
+ const unsubscribeFailure = this.onFailure((error) => finish(() => reject(error)));
129
149
  if (signal?.aborted)
130
150
  abort();
131
151
  else
@@ -134,21 +154,38 @@ class JsonLineRpc {
134
154
  }
135
155
  handleStdout(chunk) {
136
156
  this.buffer += chunk;
157
+ this.bufferBytes += Buffer.byteLength(chunk, "utf8");
137
158
  for (;;) {
138
159
  const newline = this.buffer.indexOf("\n");
139
- if (newline === -1)
160
+ if (newline === -1) {
161
+ if (this.bufferBytes > PI_RPC_MAX_LINE_BYTES) {
162
+ const error = new Error(`Pi RPC line exceeded ${PI_RPC_MAX_LINE_BYTES} bytes.`);
163
+ this.failAll(error);
164
+ this.child.kill();
165
+ }
140
166
  return;
141
- const line = this.buffer.slice(0, newline).trim();
167
+ }
168
+ const consumed = this.buffer.slice(0, newline + 1);
169
+ const line = consumed.slice(0, -1).trim();
142
170
  this.buffer = this.buffer.slice(newline + 1);
171
+ this.bufferBytes = Math.max(0, this.bufferBytes - Buffer.byteLength(consumed, "utf8"));
143
172
  if (!line)
144
173
  continue;
174
+ if (Buffer.byteLength(line, "utf8") > PI_RPC_MAX_LINE_BYTES) {
175
+ const error = new Error(`Pi RPC line exceeded ${PI_RPC_MAX_LINE_BYTES} bytes.`);
176
+ this.failAll(error);
177
+ this.child.kill();
178
+ return;
179
+ }
145
180
  let message;
146
181
  try {
147
182
  message = JSON.parse(line);
148
183
  }
149
184
  catch {
150
- this.stderr += `${line}\n`;
151
- this.failAll(new Error(`Pi RPC emitted malformed JSON on stdout: ${line}`));
185
+ const preview = boundedUtf8Tail(line, PI_ERROR_PREVIEW_BYTES);
186
+ this.stderr = boundedUtf8Tail(`${this.stderr}${preview}\n`, PI_RPC_STDERR_TAIL_BYTES);
187
+ this.failAll(new Error(`Pi RPC emitted malformed JSON on stdout: ${preview}`));
188
+ this.child.kill();
152
189
  return;
153
190
  }
154
191
  if (message.type !== "response") {
@@ -171,14 +208,28 @@ class JsonLineRpc {
171
208
  }
172
209
  }
173
210
  }
211
+ onFailure(callback) {
212
+ this.failureSubscribers.add(callback);
213
+ return () => this.failureSubscribers.delete(callback);
214
+ }
174
215
  failAll(error) {
216
+ if (this.fatalError)
217
+ return;
175
218
  this.fatalError = error;
176
219
  for (const pending of this.pending.values()) {
177
220
  pending.reject(error);
178
221
  }
179
222
  this.pending.clear();
223
+ for (const subscriber of [...this.failureSubscribers])
224
+ subscriber(error);
180
225
  }
181
226
  }
227
+ function boundedUtf8Tail(value, maxBytes) {
228
+ const bytes = Buffer.from(value, "utf8");
229
+ if (bytes.length <= maxBytes)
230
+ return value;
231
+ return bytes.subarray(bytes.length - maxBytes).toString("utf8");
232
+ }
182
233
  export function extractPiFinalResponse(value) {
183
234
  const root = unwrapProviderPayload(value);
184
235
  const messages = Array.isArray(root) ? root : readArray(root, "messages");
@@ -9,19 +9,25 @@ export function getSubagentProviderAvailabilitySnapshot(env = process.env) {
9
9
  export function checkSubagentProviderAvailability(provider, env = process.env) {
10
10
  switch (provider) {
11
11
  case "codex":
12
- return packageAvailability(provider, "@openai/codex-sdk");
12
+ return commandAvailability(provider, env.CODEX_COMMAND ?? "codex", {
13
+ env: externalProviderEnvironment(env, "CODEX_COMMAND"),
14
+ });
13
15
  case "claude":
14
- return packageAvailability(provider, "@anthropic-ai/claude-agent-sdk");
16
+ return commandAvailability(provider, env.CLAUDE_COMMAND ?? "claude", {
17
+ env: externalProviderEnvironment(env, "CLAUDE_COMMAND"),
18
+ });
15
19
  case "opencode":
16
- return packageAvailability(provider, "@opencode-ai/sdk/v2");
20
+ return commandAvailability(provider, env.OPENCODE_COMMAND ?? "opencode", {
21
+ env: externalProviderEnvironment(env, "OPENCODE_COMMAND"),
22
+ });
17
23
  case "pi":
18
24
  return commandAvailability(provider, env.PI_COMMAND ?? "pi", {
19
- env: piAvailabilityEnvironment(env),
25
+ env: externalProviderEnvironment(env, "PI_COMMAND"),
20
26
  });
21
27
  case "cursor":
22
- return commandAvailability(provider, "cursor-agent");
28
+ return commandAvailability(provider, "cursor-agent", { env });
23
29
  case "copilot":
24
- return commandAvailability(provider, "copilot");
30
+ return commandAvailability(provider, "copilot", { env });
25
31
  }
26
32
  }
27
33
  export function assertSubagentProviderAvailable(provider, env = process.env) {
@@ -45,24 +51,6 @@ export function formatSubagentProviderAvailabilitySummary(providers) {
45
51
  unavailable.length > 0 ? `unavailable: ${unavailable.join(", ")}` : undefined,
46
52
  ].filter(Boolean).join("; ");
47
53
  }
48
- function packageAvailability(provider, packageName) {
49
- try {
50
- import.meta.resolve(packageName);
51
- return {
52
- name: provider,
53
- available: true,
54
- continuationSupported: subagentProviderContinuationSupported(provider),
55
- };
56
- }
57
- catch {
58
- return {
59
- name: provider,
60
- available: false,
61
- continuationSupported: subagentProviderContinuationSupported(provider),
62
- reason: `${packageName} package not found`,
63
- };
64
- }
65
- }
66
54
  function commandAvailability(provider, command, options = {}) {
67
55
  const executable = resolveCommand(command, options.env);
68
56
  if (!executable) {
@@ -120,14 +108,11 @@ function executableExists(command, env) {
120
108
  : undefined;
121
109
  return code !== "ENOENT";
122
110
  }
123
- function piAvailabilityEnvironment(env) {
124
- if (env.PI_COMMAND)
125
- return env;
126
- const path = env.PATH;
127
- if (!path)
111
+ function externalProviderEnvironment(env, explicitCommandKey) {
112
+ if (env[explicitCommandKey] || !env.PATH)
128
113
  return env;
129
114
  return {
130
115
  ...env,
131
- PATH: removeForgeRelayNodeModulesBinFromPath(path),
116
+ PATH: removeForgeRelayNodeModulesBinFromPath(env.PATH),
132
117
  };
133
118
  }
@@ -0,0 +1,56 @@
1
+ import { spawn } from "node:child_process";
2
+ import { assertPipedChild, errorMessage, terminateChildOnAbort } from "../shared.js";
3
+ export const runExternalCommand = async (request) => {
4
+ request.signal?.throwIfAborted();
5
+ const child = spawn(request.command, request.args, {
6
+ cwd: request.cwd,
7
+ env: request.env,
8
+ stdio: ["pipe", "pipe", "pipe"],
9
+ windowsHide: true,
10
+ });
11
+ assertPipedChild(child);
12
+ let stdout = "";
13
+ let stderr = "";
14
+ child.stdout.on("data", (chunk) => {
15
+ stdout += chunk.toString("utf8");
16
+ });
17
+ child.stderr.on("data", (chunk) => {
18
+ stderr += chunk.toString("utf8");
19
+ });
20
+ const detachAbort = terminateChildOnAbort(child, request.signal);
21
+ try {
22
+ const exitCode = await new Promise((resolve, reject) => {
23
+ child.once("error", (error) => reject(error));
24
+ child.once("exit", (code, signal) => {
25
+ if (request.signal?.aborted) {
26
+ const aborted = new Error(`${request.label} command cancelled.`);
27
+ aborted.name = "AbortError";
28
+ reject(aborted);
29
+ return;
30
+ }
31
+ if (signal) {
32
+ reject(new Error(`${request.label} command terminated by ${signal}${stderr ? `\n${stderr.trim()}` : ""}`));
33
+ return;
34
+ }
35
+ resolve(code ?? 0);
36
+ });
37
+ });
38
+ if (exitCode !== 0) {
39
+ throw new Error(`${request.label} command exited with code ${exitCode}${stderr ? `\n${stderr.trim()}` : ""}`);
40
+ }
41
+ return { stdout, stderr, exitCode };
42
+ }
43
+ catch (error) {
44
+ if (request.signal?.aborted && !(error instanceof Error && error.name === "AbortError")) {
45
+ const aborted = new Error(`${request.label} command cancelled: ${errorMessage(error)}`);
46
+ aborted.name = "AbortError";
47
+ throw aborted;
48
+ }
49
+ throw error;
50
+ }
51
+ finally {
52
+ detachAbort();
53
+ if (!child.killed && child.exitCode === null)
54
+ child.kill();
55
+ }
56
+ };
@@ -1,4 +1,4 @@
1
- import { CapabilityError, } from "../../capability-registry.js";
1
+ import { CapabilityError, } from "../../mcp/server/core/capability-registry.js";
2
2
  import { isSubagentProvider } from "../profiles.js";
3
3
  import { subagentProviderContinuationSupported } from "../providers/continuation.js";
4
4
  import { SubagentDeliveryMailbox } from "./delivery-mailbox.js";
@@ -1,4 +1,4 @@
1
- import { HookRunner } from "../../hooks.js";
1
+ import { HookRunner } from "../../mcp/hooks/hooks.js";
2
2
  import { isSubagentProvider, loadSubagentProfiles, } from "../profiles.js";
3
3
  import { runSubagentProvider } from "../providers/registry.js";
4
4
  import { SubagentDeliveryMailbox } from "./delivery-mailbox.js";
@@ -0,0 +1,30 @@
1
+ import assert from "node:assert/strict";
2
+ import { openDatabase } from "../../../runtime/state/db/client.js";
3
+ export function readActivityAuditSnapshot(stateDir, auditStore) {
4
+ const database = openDatabase(stateDir);
5
+ try {
6
+ const rows = database.sqlite.prepare(`select activity_id, event_type, tool, request_json, result_json, error,
7
+ payload_file, payload_offset, payload_length
8
+ from activity_audit_events
9
+ order by rowid`).all();
10
+ for (const row of rows) {
11
+ assert.equal(row.request_json, null, "Activity request payload must not be stored in SQLite.");
12
+ assert.equal(row.result_json, null, "Activity result payload must not be stored in SQLite.");
13
+ assert.equal(row.error, null, "Activity error payload must not be stored in SQLite.");
14
+ }
15
+ const activityIds = [...new Set(rows.map((row) => row.activity_id))];
16
+ return {
17
+ rows,
18
+ events: activityIds.flatMap((activityId) => auditStore.listEvents(activityId)),
19
+ };
20
+ }
21
+ finally {
22
+ database.close();
23
+ }
24
+ }
25
+ export function activityEventsForTool(snapshot, tool) {
26
+ const activityIds = new Set(snapshot.rows
27
+ .filter((row) => row.tool === tool)
28
+ .map((row) => row.activity_id));
29
+ return snapshot.events.filter((event) => activityIds.has(event.activityId));
30
+ }
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { resolve } from "node:path";
3
- import { openDatabase } from "../../db/client.js";
3
+ import { openDatabase } from "../../runtime/state/db/client.js";
4
4
  export class SubagentSessionStore {
5
5
  database;
6
6
  constructor(stateDir) {