@akira-tl/forgerelay 0.8.8 → 0.8.10

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 (264) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +10 -18
  3. package/capabilities/code-intelligence/GUIDE.md +7 -3
  4. package/capabilities/host-integration/GUIDE.md +2 -2
  5. package/capabilities/subagents/GUIDE.md +9 -0
  6. package/capabilities/{workspace-tasks → workspace/workspace-tasks}/GUIDE.md +6 -0
  7. package/dist/activity/{audit-store.js → history/audit-store.js} +129 -21
  8. package/dist/activity/history/bash-output-store.js +250 -0
  9. package/dist/activity/{host-turn-store.js → history/host-turn-store.js} +4 -1
  10. package/dist/activity/{query-service.js → history/query-service.js} +13 -7
  11. package/dist/activity/{lifecycle.js → runtime/lifecycle.js} +1 -1
  12. package/dist/activity/{mcp-query-tools.js → runtime/mcp-query-tools.js} +3 -3
  13. package/dist/activity/storage/history.js +12 -0
  14. package/dist/activity/storage/paths.js +56 -0
  15. package/dist/activity/storage/segmented-log.js +149 -0
  16. package/dist/cli/init.js +180 -0
  17. package/dist/cli/setup-support.js +181 -0
  18. package/dist/cli.js +23 -216
  19. package/dist/lsp/code-intelligence.js +18 -4
  20. package/dist/lsp/runtime/diagnostic-snapshots.js +52 -1
  21. package/dist/lsp/runtime/managed-language-servers.js +172 -0
  22. package/dist/lsp/runtime/manager.js +37 -2
  23. package/dist/lsp/runtime/process-launch.js +3 -0
  24. package/dist/lsp/test-support/server-fixture.js +14 -11
  25. package/dist/mcp/filesystem/filesystem-tools.js +245 -0
  26. package/dist/mcp/hooks/command-runner.js +79 -0
  27. package/dist/{hook-cli.js → mcp/hooks/hook-cli.js} +1 -1
  28. package/dist/{hooks.js → mcp/hooks/hooks.js} +3 -80
  29. package/dist/{oauth-provider.js → mcp/oauth/oauth-provider.js} +1 -1
  30. package/dist/{oauth-store.js → mcp/oauth/oauth-store.js} +5 -4
  31. package/dist/{operations → mcp/operations}/native-bulk-mutations.js +2 -2
  32. package/dist/mcp/panel/app.js +143 -0
  33. package/dist/{mcp-app-template.js → mcp/panel/mcp-app-template.js} +2 -38
  34. package/dist/{process-platform.js → mcp/process/process-platform.js} +21 -9
  35. package/dist/{process-sessions.js → mcp/process/process-sessions.js} +3 -3
  36. package/dist/mcp/process/runtime.js +257 -0
  37. package/dist/mcp/process/tools.js +346 -0
  38. package/dist/mcp/server/core/activity-support.js +70 -0
  39. package/dist/{capabilities.js → mcp/server/core/capabilities.js} +5 -3
  40. package/dist/{capability-registry.js → mcp/server/core/capability-registry.js} +9 -4
  41. package/dist/mcp/server/core/capability-support.js +90 -0
  42. package/dist/mcp/server/core/schemas.js +216 -0
  43. package/dist/mcp/server/core/tool-support.js +210 -0
  44. package/dist/mcp/server/core/version.js +9 -0
  45. package/dist/mcp/server/operations/runtime/filesystem-tools.js +455 -0
  46. package/dist/mcp/server/operations/runtime/mutation-diagnostics.js +54 -0
  47. package/dist/mcp/server/operations/runtime/operation-runtime.js +524 -0
  48. package/dist/mcp/server/transport/http-server.js +301 -0
  49. package/dist/mcp/server/transport/http-support.js +43 -0
  50. package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +242 -0
  51. package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +187 -0
  52. package/dist/mcp/server/workspace/runtime/workspace-open.js +555 -0
  53. package/dist/mcp/server/workspace/runtime/workspace-tools.js +601 -0
  54. package/dist/mcp/server-instructions.js +1 -1
  55. package/dist/{config.js → runtime/config/config.js} +4 -3
  56. package/dist/{user-config.js → runtime/config/user-config.js} +14 -63
  57. package/dist/{db → runtime/state/db}/client.js +1 -1
  58. package/dist/{db → runtime/state/db}/migrations.js +34 -3
  59. package/dist/runtime/state/lock/file-lock.js +130 -0
  60. package/dist/runtime/testing/server-fixture.js +190 -0
  61. package/dist/server.js +133 -4189
  62. package/dist/subagents/providers/adapters/claude.js +86 -52
  63. package/dist/subagents/providers/adapters/codex.js +103 -39
  64. package/dist/subagents/providers/adapters/opencode.js +114 -84
  65. package/dist/subagents/providers/adapters/pi.js +59 -8
  66. package/dist/subagents/providers/availability.js +15 -30
  67. package/dist/subagents/providers/runtime/external-command.js +56 -0
  68. package/dist/subagents/sessions/capability.js +1 -1
  69. package/dist/subagents/sessions/execution.js +1 -1
  70. package/dist/subagents/sessions/mcp/activity-audit-test-support.js +30 -0
  71. package/dist/subagents/sessions/store.js +1 -1
  72. package/dist/ui/.vite/manifest.json +342 -410
  73. package/dist/ui/activity-panel-app.html +2 -4
  74. package/dist/ui/assets/activity-panel-app-7Xe0alhm.css +1 -0
  75. package/dist/ui/assets/activity-panel-app-Sl1Jro8v.js +115 -0
  76. package/dist/ui/assets/angular-html-B_yn91wW.js +1 -0
  77. package/dist/ui/assets/{angular-ts-Bxh4-O1-.js → angular-ts-DSgtloKz.js} +1 -1
  78. package/dist/ui/assets/{apl-BmYHKQ6m.js → apl-NatWtF8X.js} +1 -1
  79. package/dist/ui/assets/{astro-C51vqDKq.js → astro-BYFEVBuN.js} +1 -1
  80. package/dist/ui/assets/{blade-CdhVsmwq.js → blade-DoO7yvMR.js} +1 -1
  81. package/dist/ui/assets/c-Rl2hMCcg.js +1 -0
  82. package/dist/ui/assets/{cobol-Cf6azoN_.js → cobol-ByC5mWiB.js} +1 -1
  83. package/dist/ui/assets/{coffee-vVdGzlmt.js → coffee-Dez77Qov.js} +1 -1
  84. package/dist/ui/assets/cpp-DtaAQT9h.js +1 -0
  85. package/dist/ui/assets/{crystal-5UZy6KIX.js → crystal-Bgmbf8ec.js} +1 -1
  86. package/dist/ui/assets/css-C1tjBtVN.js +1 -0
  87. package/dist/ui/assets/{edge-CESgo4ia.js → edge-DeH6u8Wg.js} +1 -1
  88. package/dist/ui/assets/{elixir-9FKfw4cJ.js → elixir-_H3KnmWT.js} +1 -1
  89. package/dist/ui/assets/{elm-Dv7A0aLa.js → elm-CsCe3U28.js} +1 -1
  90. package/dist/ui/assets/{erb-DHFmDvM0.js → erb-CbM-L2g8.js} +1 -1
  91. package/dist/ui/assets/{git-rebase-utbU6CzV.js → git-rebase-D0s5pDh9.js} +1 -1
  92. package/dist/ui/assets/{glimmer-js-t2kiCkyB.js → glimmer-js-BfOToq_A.js} +1 -1
  93. package/dist/ui/assets/{glimmer-ts-F0Jv5okV.js → glimmer-ts-CCUOkL2K.js} +1 -1
  94. package/dist/ui/assets/glsl-DH4ec9dE.js +1 -0
  95. package/dist/ui/assets/graphql-DilAMBoI.js +1 -0
  96. package/dist/ui/assets/{hack-8yjdbaSA.js → hack-BDkNDZlF.js} +1 -1
  97. package/dist/ui/assets/haml-D_QBp6K7.js +1 -0
  98. package/dist/ui/assets/{handlebars-BBFEg4Lo.js → handlebars-DePUrTpC.js} +1 -1
  99. package/dist/ui/assets/heavy-payload-DOwZpO6n.js +290 -0
  100. package/dist/ui/assets/html-By4dNnM4.js +1 -0
  101. package/dist/ui/assets/{html-derivative-DJAQcZyc.js → html-derivative-DDw9Ef4Q.js} +1 -1
  102. package/dist/ui/assets/{http-Dg5LSYRK.js → http-B0QxWc9M.js} +1 -1
  103. package/dist/ui/assets/{hurl-Y5uYzMZR.js → hurl-vMQ4lda1.js} +1 -1
  104. package/dist/ui/assets/java-DYRe2lWG.js +1 -0
  105. package/dist/ui/assets/javascript-CxyGqwcL.js +1 -0
  106. package/dist/ui/assets/{jinja-Bt30h8Ik.js → jinja-JUwV-fZt.js} +1 -1
  107. package/dist/ui/assets/{jison-D5uZ1Nyo.js → jison-7ZqCEvS8.js} +1 -1
  108. package/dist/ui/assets/json-dbltgiiA.js +1 -0
  109. package/dist/ui/assets/jsx-oDopT7YQ.js +1 -0
  110. package/dist/ui/assets/{julia-76hRaaGz.js → julia-CkKiNSav.js} +1 -1
  111. package/dist/ui/assets/{just-CDE7W0vW.js → just-DuqCAKJ3.js} +1 -1
  112. package/dist/ui/assets/{latex-CVO2Byab.js → latex-sudcRDSn.js} +1 -1
  113. package/dist/ui/assets/{liquid-CA-AS3EX.js → liquid-Di-08Js7.js} +1 -1
  114. package/dist/ui/assets/lua-YlqdMoV0.js +1 -0
  115. package/dist/ui/assets/{marko-_BWrPfny.js → marko-CJRNyJDb.js} +1 -1
  116. package/dist/ui/assets/{mdc-PyJv85eS.js → mdc-t12CzZHs.js} +1 -1
  117. package/dist/ui/assets/{nginx-DAWVw8yu.js → nginx-57-b4XwT.js} +1 -1
  118. package/dist/ui/assets/{nim-CKE1dzpU.js → nim-BzrswYQa.js} +1 -1
  119. package/dist/ui/assets/{perl-BBhWva19.js → perl-D9JR_iIh.js} +1 -1
  120. package/dist/ui/assets/{php-ChIeaAoF.js → php-Py2gFHil.js} +1 -1
  121. package/dist/ui/assets/{pug-CMgI5afi.js → pug-PzLLy0h1.js} +1 -1
  122. package/dist/ui/assets/{qml-CjxTkcFH.js → qml-By8fwgV4.js} +1 -1
  123. package/dist/ui/assets/r-CcezTbLq.js +1 -0
  124. package/dist/ui/assets/{razor-i4DbHr8p.js → razor-dG-vdhL_.js} +1 -1
  125. package/dist/ui/assets/regexp-BlsQ-N_8.js +1 -0
  126. package/dist/ui/assets/{rst-oy_97Kl4.js → rst-C2awDCat.js} +1 -1
  127. package/dist/ui/assets/{ruby-C5zYZIyl.js → ruby-zA2W_49d.js} +1 -1
  128. package/dist/ui/assets/{sas-BnMEh2S3.js → sas-D49bhyaN.js} +1 -1
  129. package/dist/ui/assets/scss-BQb-cp01.js +1 -0
  130. package/dist/ui/assets/shellscript-Dga4H_Ni.js +1 -0
  131. package/dist/ui/assets/{shellsession-BpE5o7_3.js → shellsession-n3-5CqcF.js} +1 -1
  132. package/dist/ui/assets/{soy-COesmhTY.js → soy-XYBOf_BA.js} +1 -1
  133. package/dist/ui/assets/sql-ET4u5Cpl.js +1 -0
  134. package/dist/ui/assets/{stata-CGQEuSZq.js → stata-CLHcJHO_.js} +1 -1
  135. package/dist/ui/assets/{surrealql-Bz4JbU-9.js → surrealql-BdjQ60T4.js} +1 -1
  136. package/dist/ui/assets/{svelte-1jM2ejXj.js → svelte-B2RPtKJe.js} +1 -1
  137. package/dist/ui/assets/{templ-C-VwELcp.js → templ-CrByPW_W.js} +1 -1
  138. package/dist/ui/assets/{tex-t1rUiQCm.js → tex-DZBkg363.js} +1 -1
  139. package/dist/ui/assets/{ts-tags-Bewoa6pS.js → ts-tags-DJR3Gphb.js} +1 -1
  140. package/dist/ui/assets/tsx-CajzROyy.js +1 -0
  141. package/dist/ui/assets/{twig-DVg-vELl.js → twig-BZjk5Zda.js} +1 -1
  142. package/dist/ui/assets/typescript-B-EWToLj.js +1 -0
  143. package/dist/ui/assets/{vue-wPQ7UlMy.js → vue-D_rHCCr8.js} +1 -1
  144. package/dist/ui/assets/{vue-html-BHtJ8geX.js → vue-html-CC4WlvVt.js} +1 -1
  145. package/dist/ui/assets/{vue-vine-CNSGAWwc.js → vue-vine-BgNMVgpk.js} +1 -1
  146. package/dist/ui/assets/xml-BI86-WbH.js +1 -0
  147. package/dist/ui/assets/{xsl-3_vJ27aE.js → xsl-DXeLZV4J.js} +1 -1
  148. package/dist/ui/assets/yaml-60_PFlWa.js +1 -0
  149. package/dist/workspaces/bootstrap.js +72 -0
  150. package/dist/workspaces/context.js +337 -0
  151. package/dist/workspaces/conversation-test-support.js +86 -0
  152. package/dist/{git-worktrees.js → workspaces/git/git-worktrees.js} +1 -1
  153. package/dist/workspaces/inventory.js +131 -0
  154. package/dist/workspaces/paths.js +55 -0
  155. package/dist/{remote-auth.js → workspaces/relay/auth/remote-auth.js} +2 -2
  156. package/dist/workspaces/relay/result-support.js +111 -0
  157. package/dist/workspaces/relay/tests/test-support.js +203 -0
  158. package/dist/{remote-mcp-connection-pool.js → workspaces/relay/transport/remote-mcp-connection-pool.js} +1 -1
  159. package/dist/{remote-transport.js → workspaces/relay/transport/remote-transport.js} +30 -3
  160. package/dist/workspaces/relay/types.js +1 -0
  161. package/dist/{remote-workspace-relay.js → workspaces/relay/workspace-relay.js} +23 -172
  162. package/dist/workspaces/resources/resource-monitor.js +377 -0
  163. package/dist/workspaces/resources/skills.js +283 -0
  164. package/dist/{review-checkpoints.js → workspaces/review/review-checkpoints.js} +2 -2
  165. package/dist/workspaces/sessions.js +460 -0
  166. package/dist/{workspace-store.js → workspaces/state/workspace-store.js} +2 -2
  167. package/dist/workspaces.js +77 -993
  168. package/docs/chatgpt-coding-workflow.md +7 -14
  169. package/docs/configuration.md +26 -34
  170. package/docs/gotchas.md +0 -30
  171. package/docs/roadmap.md +6 -10
  172. package/docs/security.md +1 -5
  173. package/docs/setup.md +0 -11
  174. package/package.json +6 -9
  175. package/scripts/ci/architecture.mjs +109 -0
  176. package/scripts/ci/verify.mjs +5 -0
  177. package/scripts/debug/accept/bootstrap.mjs +326 -0
  178. package/scripts/debug/accept/harness.mjs +76 -0
  179. package/scripts/debug/accept/support.mjs +432 -0
  180. package/scripts/debug/accept.mjs +43 -806
  181. package/scripts/debug/relay-accept/support.mjs +135 -0
  182. package/scripts/debug/relay-accept.mjs +26 -115
  183. package/scripts/debug/traffic/support.mjs +167 -0
  184. package/scripts/debug/traffic/traffic-audit.mjs +4 -161
  185. package/scripts/{lsp-interop-support.test.mjs → lsp/interop-support.test.mjs} +1 -1
  186. package/scripts/{lsp-interop.mjs → lsp/interop.mjs} +3 -3
  187. package/scripts/release/publish.mjs +12 -9
  188. package/scripts/release/release-gate.test.mjs +3 -1
  189. package/scripts/release/release-version.test.mjs +13 -1
  190. package/scripts/release-parity.mjs +1 -1
  191. package/scripts/release-proof.mjs +15 -0
  192. package/scripts/release-proof.test.mjs +27 -1
  193. package/scripts/release-version.mjs +16 -1
  194. package/scripts/wiki/sync.mjs +21 -10
  195. package/dist/activity/bash-output-store.js +0 -127
  196. package/dist/pi-tools.js +0 -110
  197. package/dist/skills.js +0 -127
  198. package/dist/ui/assets/activity-panel-app-7AckaxIR.js +0 -2
  199. package/dist/ui/assets/angular-html-BkgDQl-G.js +0 -1
  200. package/dist/ui/assets/c-IBzBpxAO.js +0 -1
  201. package/dist/ui/assets/chunk-EyZ2wyi3.js +0 -1
  202. package/dist/ui/assets/cpp-CVHeO-Jk.js +0 -1
  203. package/dist/ui/assets/css-DEfus9J5.js +0 -1
  204. package/dist/ui/assets/glsl-_ooCjaeB.js +0 -1
  205. package/dist/ui/assets/graphql-B9LqP-cY.js +0 -1
  206. package/dist/ui/assets/haml-BU5EoZBn.js +0 -1
  207. package/dist/ui/assets/heavy-payload-Bol1_mcs.js +0 -4
  208. package/dist/ui/assets/html-D-C6YZk4.js +0 -1
  209. package/dist/ui/assets/java-ppHvycj5.js +0 -1
  210. package/dist/ui/assets/javascript-DDTjKLlQ.js +0 -1
  211. package/dist/ui/assets/json-Cw91r8h1.js +0 -1
  212. package/dist/ui/assets/jsx-04X2FT_1.js +0 -1
  213. package/dist/ui/assets/lua-CiyBp52Y.js +0 -1
  214. package/dist/ui/assets/r-DfFga4si.js +0 -1
  215. package/dist/ui/assets/regexp-iC9_nbSI.js +0 -1
  216. package/dist/ui/assets/review-payload-BqJp0c5e.js +0 -1
  217. package/dist/ui/assets/scrollbar-CvE-I-jG.js +0 -287
  218. package/dist/ui/assets/scss-BfZXsAWQ.js +0 -1
  219. package/dist/ui/assets/shellscript-BC2srNaB.js +0 -1
  220. package/dist/ui/assets/sql-Ccv_yeUm.js +0 -1
  221. package/dist/ui/assets/tsx-CTXzrBkN.js +0 -1
  222. package/dist/ui/assets/typescript--ElHoFkw.js +0 -1
  223. package/dist/ui/assets/workspace-app-BLW7p6IZ.js +0 -114
  224. package/dist/ui/assets/workspace-app-BjNqR0en.js +0 -1
  225. package/dist/ui/assets/workspace-app-CmaYU4DW.js +0 -3
  226. package/dist/ui/assets/workspace-app-D2bJ5fjt.css +0 -1
  227. package/dist/ui/assets/workspace-lifecycle-app-BVbI2Ilf.js +0 -1
  228. package/dist/ui/assets/xml-JnmX6vyS.js +0 -1
  229. package/dist/ui/assets/yaml-OAcZkP-w.js +0 -1
  230. package/dist/ui/workspace-app.html +0 -18
  231. package/dist/ui/workspace-lifecycle-app.html +0 -18
  232. package/docs/agent-profile-schema.md +0 -114
  233. package/docs/artifact-exchange.md +0 -65
  234. package/docs/versioning.md +0 -205
  235. package/skills/subagent-delegation/SKILL.md +0 -132
  236. /package/capabilities/{managed-worktrees → workspace/managed-worktrees}/GUIDE.md +0 -0
  237. /package/dist/activity/{process-output-audit.js → runtime/process-output-audit.js} +0 -0
  238. /package/dist/{artifact-error.js → mcp/artifacts/artifact-error.js} +0 -0
  239. /package/dist/{artifact-tools.js → mcp/artifacts/artifact-tools.js} +0 -0
  240. /package/dist/{incoming-artifacts.js → mcp/artifacts/incoming-artifacts.js} +0 -0
  241. /package/dist/{advertised-files.js → mcp/filesystem/advertised-files.js} +0 -0
  242. /package/dist/{apply-patch.js → mcp/filesystem/apply-patch.js} +0 -0
  243. /package/dist/{file-mutations.js → mcp/filesystem/file-mutations.js} +0 -0
  244. /package/dist/{roots.js → mcp/filesystem/roots.js} +0 -0
  245. /package/dist/{oauth → mcp/oauth}/public-url.js +0 -0
  246. /package/dist/{oauth → mcp/oauth}/router.js +0 -0
  247. /package/dist/{operations → mcp/operations}/batch/executor.js +0 -0
  248. /package/dist/{operations → mcp/operations}/batch/scheduler.js +0 -0
  249. /package/dist/{operations → mcp/operations}/batch/types.js +0 -0
  250. /package/dist/{operations → mcp/operations}/bulk-mutation.js +0 -0
  251. /package/dist/{operations → mcp/operations}/bulk-read.js +0 -0
  252. /package/dist/{operations → mcp/operations}/core-operation-executor.js +0 -0
  253. /package/dist/{request-meta.js → mcp/request-meta.js} +0 -0
  254. /package/dist/{mcp-sessions.js → mcp/server/transport/mcp-sessions.js} +0 -0
  255. /package/dist/{server-shutdown.js → mcp/server/transport/server-shutdown.js} +0 -0
  256. /package/dist/{logger.js → runtime/logging/logger.js} +0 -0
  257. /package/dist/{db → runtime/state/db}/schema.js +0 -0
  258. /package/dist/{composite-activity.js → workspaces/composite/composite-activity.js} +0 -0
  259. /package/dist/{composite-workspaces.js → workspaces/composite/composite-workspaces.js} +0 -0
  260. /package/dist/{git.js → workspaces/git/git.js} +0 -0
  261. /package/dist/{workspace-presentation.js → workspaces/presentation/workspace-presentation.js} +0 -0
  262. /package/dist/{workspace-task-reminders.js → workspaces/tasks/workspace-task-reminders.js} +0 -0
  263. /package/dist/{workspace-tasks.js → workspaces/tasks/workspace-tasks.js} +0 -0
  264. /package/scripts/{lsp-interop-support.mjs → lsp/interop-support.mjs} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ All notable ForgeRelay changes are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.8.10] - 2026-09-03
8
+
9
+ ### Added
10
+
11
+ - Added user-controlled managed Language Server installation for TypeScript/JavaScript code intelligence, including private TypeScript SDK wiring, live runtime refresh, and post-mutation diagnostics without restarting ForgeRelay.
12
+ - Added explicit setup flows for local/SSH-only use versus network-exposed ForgeRelay deployments.
13
+
14
+ ### Changed
15
+
16
+ - Workspace instructions and Skills now stream bounded live deltas after a Workspace is opened, while Agents are prompted to maintain `workspace.tasks` proactively for long-running work instead of querying Tasks mechanically on every open.
17
+ - Agent Bash execution now uses an isolated non-interactive shell environment, and SSH-assisted authentication no longer sources the user's interactive shell configuration.
18
+ - Retired the bundled `subagent-delegation` Skill; required delegation behavior remains in the Subagent capability contract and guide.
19
+
20
+ ### Fixed
21
+
22
+ - Increased the default foreground process wait and stabilized cancellation/concurrency behavior around Language Server diagnostics.
23
+ - Fixed Windows `.cmd`/`.bat` Language Server launchers and kept ordinary external LSP interoperability independent of ForgeRelay's managed-LSP config directory.
24
+ - Preserved Windows `cmd.exe` command quoting for isolated Agent Bash execution, including commands whose executable path is already quoted.
25
+ - Hardened release verification with pinned publishing tooling and dedicated per-version release notes.
26
+
27
+ ## [0.8.9] - 2026-09-02
28
+
29
+ ### Changed
30
+
31
+ - Removed the remaining DevSpace-era runtime compatibility identifiers and migration aliases while retaining upstream MIT attribution in `LICENSE`, `NOTICE.md`, and release provenance checks.
32
+ - Removed bundled Codex, Claude, OpenCode, and Pi executor packages; subagent providers now invoke user-installed external CLIs, while ForgeRelay keeps only protocol/runtime infrastructure it owns.
33
+ - Upgraded `better-sqlite3` to 13.0.3 and replaced Pi-owned file/Skill helpers with ForgeRelay implementations.
34
+
35
+ ### Fixed
36
+
37
+ - Eliminated the install-time `prebuild-install` and `node-domexception` deprecation chains without dependency overrides.
38
+
7
39
  ## [0.8.8] - 2026-09-02
8
40
 
9
41
  ### Fixed
package/README.md CHANGED
@@ -231,7 +231,8 @@ forgerelay hooks list --project /path/to/project
231
231
 
232
232
  ForgeRelay can delegate work to local coding runtimes through user-defined
233
233
  profiles. The current adapter layer supports Codex, Claude, OpenCode, Pi,
234
- Cursor, and Copilot where the corresponding local integration is available.
234
+ Cursor, and Copilot when the corresponding CLI is installed on the server.
235
+ ForgeRelay does not install or bundle those coding runtimes.
235
236
 
236
237
  Profiles can live in:
237
238
 
@@ -248,12 +249,12 @@ forgerelay agents run <profile-or-provider-or-id> "<prompt>"
248
249
  forgerelay agents show <id>
249
250
  ```
250
251
 
251
- A first-class MCP subagent interface is planned so a parent agent can use the
252
- same runtime without going through the CLI.
252
+ MCP hosts delegate through the `subagent.session` Capability Gateway; the CLI
253
+ remains useful for local diagnostics and manual session inspection.
253
254
 
254
- See [Agent Profile Schema](docs/agent-profile-schema.md) for the profile format.
255
+ See [Agent Profile Schema](docs/agents/profile-schema.md) for the profile format.
255
256
 
256
- ## Configuration and upgrades from DevSpace
257
+ ## Configuration
257
258
 
258
259
  Configuration uses the `FORGERELAY_*` prefix. For example:
259
260
 
@@ -263,15 +264,6 @@ FORGERELAY_PUBLIC_BASE_URL="https://forge.example.com" \
263
264
  forgerelay serve
264
265
  ```
265
266
 
266
- The rename-era automatic compatibility window has ended. `DEVSPACE_*`
267
- environment variables are ignored, ForgeRelay no longer auto-selects
268
- `~/.devspace`, and project `.devspace/agents` profiles are not discovered.
269
-
270
- If an older installation still has data under DevSpace-named directories,
271
- migrate it explicitly or point the canonical ForgeRelay settings at those paths
272
- while moving the data. Persisted internal identifiers that would otherwise
273
- orphan existing state remain compatible and are not product-facing configuration.
274
-
275
267
  See [Configuration Reference](docs/configuration.md) for all supported options.
276
268
 
277
269
  ## Security model
@@ -341,7 +333,7 @@ after all platforms succeed. `npm run release:verify` remains available only whe
341
333
  cloud failure needs local reproduction against the pinned Node runtime; it is not a
342
334
  release prerequisite.
343
335
 
344
- See [Versioning and Release Management](docs/versioning.md) for the bootstrap and
336
+ See [Versioning and Release Management](docs/reference/versioning.md) for the bootstrap and
345
337
  Trusted Publishing setup.
346
338
 
347
339
  ## Documentation
@@ -351,12 +343,12 @@ Trusted Publishing setup.
351
343
  - [Local Debugging and 7677 Acceptance](docs/debugging.md)
352
344
  - [ChatGPT Coding Workflow](docs/chatgpt-coding-workflow.md)
353
345
  - [Configuration Reference](docs/configuration.md)
354
- - [Agent Profile Schema](docs/agent-profile-schema.md)
355
- - [Native File Download](docs/artifact-exchange.md)
346
+ - [Agent Profile Schema](docs/agents/profile-schema.md)
347
+ - [Native File Download](docs/reference/artifact-exchange.md)
356
348
  - [Security Model](docs/security.md)
357
349
  - [Troubleshooting](docs/gotchas.md)
358
350
  - [Roadmap](docs/roadmap.md)
359
- - [Versioning and Release Management](docs/versioning.md)
351
+ - [Versioning and Release Management](docs/reference/versioning.md)
360
352
  - [GitHub Wiki Maintenance](docs/maintenance/wiki.md)
361
353
  - [Changelog](CHANGELOG.md)
362
354
  - [Attribution Notice](NOTICE.md)
@@ -1,8 +1,10 @@
1
1
  # Code Intelligence
2
2
 
3
- Use the `code.intelligence` Capability for read-only semantic code navigation backed by Language servers that are already installed on the user's machine or explicitly configured for the project.
3
+ Use the `code.intelligence` Capability for semantic code navigation, diagnostics, and optional ForgeRelay-managed Language Server installation.
4
4
 
5
- ForgeRelay does not install Language servers. It discovers supported executables when available and accepts explicit definitions from the global ForgeRelay config or `<workspace>/.forgerelay/language-servers.json`. Project definitions override global definitions, and global definitions override built-in discovery. An explicit definition may disable discovery with `enabled: false`.
5
+ ForgeRelay discovers supported executables from project/global configuration, its private managed Language Server directory, and the inherited process `PATH`. Project definitions override global definitions, while built-in discovery can use ForgeRelay-managed executables before falling back to `PATH`. An explicit definition may disable discovery with `enabled: false`.
6
+
7
+ `managed.status` reports which ForgeRelay-managed servers are supported and currently installed. `managed.install` can install `typescript` or `pyright` into ForgeRelay's private config directory, but only when the user has explicitly enabled Agent-managed installation during `forgerelay init`. Installation is persistent and network-active, so it is disabled by default. A successful install requires no ForgeRelay restart: the next semantic request resolves executables again, and any changed Language-service command/fingerprint replaces the prior idle service automatically.
6
8
 
7
9
  ForgeRelay 0.4 LSP v1 exposes `definition`, `hover`, `references`, `documentSymbols`, `workspaceSymbols`, and `diagnostics`. Position-based operations accept a workspace-relative source `path` plus 1-based `line` and `column` values. `documentSymbols` needs only `path` and an optional bounded `limit`. `workspaceSymbols` uses `path` to select the Language project/service and accepts a `query` plus optional `limit`; it does not merge multiple nested Language services. `diagnostics` uses `path` plus an optional bounded `limit`. Columns are Unicode code-point positions; ForgeRelay converts them to the position encoding negotiated with the Language server.
8
10
 
@@ -12,6 +14,8 @@ Document symbols preserve hierarchical server responses as a tree and keep flat
12
14
 
13
15
  Diagnostics use the same normalized result contract for push and pull providers. When a Language server advertises LSP pull diagnostics, ForgeRelay prefers pull, supplies the previous `resultId` when available, and treats an `unchanged` report as a refresh of the same bounded snapshot for the newly synchronized filesystem version. Otherwise ForgeRelay uses traditional `publishDiagnostics` snapshots. Push diagnostics retain only the latest snapshot for each synchronized document: no diagnostic history is accumulated. A result reports `provider`, bounded normalized diagnostics, `returned`/`truncated`/`total`, and `freshness`. `freshness.state` distinguishes fresh, stale, missing, and unknown snapshots; filesystem changes advance ForgeRelay's synchronized document version, making an older push snapshot detectably stale until the Language server publishes a replacement. Pull and push state are bounded independently so a mixed-capability server cannot overwrite pull `resultId` state with an asynchronous push. All diagnostic state is released with the Language service.
14
16
 
17
+ Successful `write`, `edit`, `rename`, and Codex `apply_patch` mutations automatically request diagnostics for the affected code files and append non-empty results to the same mutation response. This post-mutation validation does not create a second Activity. Files with no matching Language Server are skipped silently; a Language Server validation failure never rolls back an already-successful file mutation, but ForgeRelay appends a bounded validation warning so the Agent can react instead of forgetting to check.
18
+
15
19
  Semantic locations may identify External code locations outside the Workspace, but that does not expand ForgeRelay's allowed roots or grant the file tools permission to read those paths.
16
20
 
17
21
  Language-server definitions use structured process configuration rather than shell command strings. A project configuration entry may contain `command`, `args`, `env`, `languages`, `extensions`, `languageIdByExtension`, `projectMarkers`, and `enabled` fields. Use `languageIdByExtension` when one server definition covers multiple language IDs whose extensions do not map one-to-one by array position. The server command is launched directly without a shell.
@@ -22,4 +26,4 @@ An unexpected Language-server process exit fails its pending JSON-RPC work immed
22
26
 
23
27
  Language services are shared by physical Language project identity rather than logical Workspace id, so multiple conversations over the same checkout do not multiply server processes. Idle services are reclaimed after a bounded TTL, and the global service cap evicts the least-recently-used truly idle service; a request that has returned to the Host but whose server ignored cancellation still counts as active until the underlying LSP request settles. Managed-worktree finalization releases idle Language services rooted in that worktree before removing it and refuses finalization while semantic work is still active. Debug runtime telemetry reports only aggregate service/process/request/document/diagnostic/stderr counts and never source contents.
24
28
 
25
- The built-in discovery candidates are `typescript-language-server`, `pyright-langserver`, `rust-analyzer`, `gopls`, and `clangd`. Contributors can run `npm run lsp:interop` to exercise any of those executables that are already present on `PATH`; each detected server is tested through built-in discovery and real stdio LSP, while absent servers are reported as explicit skips. The interoperability check never downloads or installs a Language server.
29
+ The built-in discovery candidates are `typescript-language-server`, `pyright-langserver`, `rust-analyzer`, `gopls`, and `clangd`. `forgerelay init` can install TypeScript/JavaScript (`typescript-language-server` plus TypeScript) and Python (Pyright) into the ForgeRelay config directory without modifying global npm. Rust Analyzer, `gopls`, and `clangd` remain external toolchain/system installations. Contributors can run `npm run lsp:interop` to exercise detected executables through real stdio LSP; the interoperability check itself never downloads or installs a Language server.
@@ -25,7 +25,7 @@ ForgeRelay 本地监听一个 HTTP origin,MCP endpoint 是 `/mcp`。当 Host
25
25
 
26
26
  ## OAuth owner flow
27
27
 
28
- ForgeRelay 使用 single-user Owner-password OAuth approval flow。新安装的 owner secret 通常保存在 `~/.forgerelay/auth.json`;迁移安装可能继续使用旧 `.devspace` 配置目录。不要把 owner token、refresh token 或 `auth.json` 内容放进 Agent 输出、项目文件或日志。
28
+ ForgeRelay 使用 single-user Owner-password OAuth approval flowowner secret 通常保存在 `~/.forgerelay/auth.json`。不要把 owner token、refresh token 或 `auth.json` 内容放进 Agent 输出、项目文件或日志。
29
29
 
30
30
  Host 可通过这些 metadata endpoint 发现授权配置:
31
31
 
@@ -34,7 +34,7 @@ Host 可通过这些 metadata endpoint 发现授权配置:
34
34
  /.well-known/oauth-authorization-server
35
35
  ```
36
36
 
37
- 默认 redirect hosts 包含 `chatgpt.com`、`localhost`、`127.0.0.1`。其他 MCP client 需要显式加入 `FORGERELAY_OAUTH_ALLOWED_REDIRECT_HOSTS`。OAuth scope 默认仍使用 legacy-compatible internal identifier `devspace`,这是协议兼容值,不是产品名回退。
37
+ 默认 redirect hosts 包含 `chatgpt.com`、`localhost`、`127.0.0.1`。其他 MCP client 需要显式加入 `FORGERELAY_OAUTH_ALLOWED_REDIRECT_HOSTS`。OAuth scope 默认使用 `forgerelay`。
38
38
 
39
39
  Owner password 不被接受时先用 `forgerelay doctor` 确认实际 auth/config 路径;只有用户明确要重建配置时才使用 `forgerelay init --force`。
40
40
 
@@ -2,6 +2,15 @@
2
2
 
3
3
  当任务需要委派给另一个本地 coding agent、获取第二意见、并行调查,或用户明确点名 Subagent/provider 时读取本指南。
4
4
 
5
+ ## 委派策略与透明性
6
+
7
+ - 只在用户明确要求委派、点名 Subagent/provider、要求第二意见或并行调查,或任务本身明确需要另一个独立 coding agent 时使用 Subagent;不要因为 profile 存在就静默委派普通工作。
8
+ - 使用 Subagent 时应让用户知道正在委派给哪个 profile/provider;不要隐藏 Subagent 的参与。
9
+ - 不要通过 `bash` 直接启动 `codex`、`claude`、`opencode`、`pi`、`cursor-agent` 或 `copilot` 来绕过 ForgeRelay 的 Session ownership、审计与结果交付。正常 Host 委派使用 `subagent.session`。
10
+ - delegated prompt 必须自包含:说明目标、相关 Workspace/文件、关键约束与验收标准。Subagent 不会自动继承主 Agent 未显式传递的隐含上下文。
11
+ - `reviewer` 适合第二意见/风险/测试缺口,`explorer` 适合只读调查,`implementer` 只在用户允许委派写入工作时使用。
12
+ - Subagent 返回值不是已经验证的最终答案。主 Agent 必须核对关键事实;写入任务检查真实 diff 并运行相关测试,只读调查核对关键路径/符号证据。最终向用户说明使用的 profile/provider、结论、已做验证与剩余风险。
13
+
5
14
  ## 当前接口
6
15
 
7
16
  启用 Subagent 后,`open_workspace` 会在 `capabilityCatalog` 中公开 `subagent.session`,同时返回紧凑的 provider/profile 元数据。不要为 Subagent 寻找或假设额外的 Core MCP tool;长期可见 Core tool surface 保持不变。
@@ -10,6 +10,12 @@
10
10
  - Task 状态只有 `pending`、`in_progress`、`completed`。`content` 保存继续工作真正需要的要求、阻塞点、结论或下一步,而不是日志或对话转录。
11
11
  - Task/List ID 创建后保持稳定。完成 Task 不会删除它;删除必须显式执行。
12
12
 
13
+ ## 主动维护
14
+
15
+ 把 `workspace.tasks` 当作需要跨多个步骤、较长工作过程或跨会话续接时的轻量工作记忆:当存在值得保留的下一步、阻塞点或当前进展时,主动创建或更新 Task;完成后及时标记 `completed`。不要因为刚执行了 `open_workspace` 就机械查询 Task。
16
+
17
+ 需要续接相关未完成工作时,再从 summary 开始渐进读取;只有 summary 表明确有相关未完成 Task,才继续读取 headers/detail。
18
+
13
19
  ## 渐进式读取
14
20
 
15
21
  Task 读取默认使用渐进式披露,不会一次返回所有 `content`:
@@ -1,11 +1,18 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { openDatabase } from "../db/client.js";
2
+ import { openDatabase } from "../../runtime/state/db/client.js";
3
+ import { SegmentedLogStore } from "../storage/segmented-log.js";
4
+ import { activityEventLogPrefix } from "../storage/paths.js";
3
5
  export class ActivityAuditStore {
4
6
  database;
5
7
  now;
8
+ stateDir;
9
+ payloads;
6
10
  constructor(stateDir, options = {}) {
7
11
  this.database = openDatabase(stateDir);
8
12
  this.now = options.now ?? (() => new Date());
13
+ this.stateDir = stateDir;
14
+ this.payloads = new SegmentedLogStore(stateDir);
15
+ this.migrateLegacyPayloads();
9
16
  }
10
17
  append(input) {
11
18
  return this.database.sqlite.transaction(() => {
@@ -30,7 +37,14 @@ export class ActivityAuditStore {
30
37
  const sequence = existing.length + 1;
31
38
  const id = `evt_${randomUUID().replaceAll("-", "")}`;
32
39
  const createdAt = this.now().toISOString();
33
- const row = eventInputToRow(input, { id, sequence, createdAt });
40
+ const workspace = input.type === "started"
41
+ ? input.workspace
42
+ : workspaceFromStartedRow(existing[0]);
43
+ const payload = eventPayload(input);
44
+ const payloadRef = payload === undefined
45
+ ? undefined
46
+ : this.payloads.append(activityEventLogPrefix(this.stateDir, workspace), Buffer.from(JSON.stringify(payload), "utf8"));
47
+ const row = eventInputToRow(input, { id, sequence, createdAt }, payloadRef);
34
48
  this.database.sqlite.prepare(`insert into activity_audit_events (
35
49
  id,
36
50
  activity_id,
@@ -49,13 +63,16 @@ export class ActivityAuditStore {
49
63
  request_json,
50
64
  result_json,
51
65
  error,
66
+ payload_file,
67
+ payload_offset,
68
+ payload_length,
52
69
  created_at
53
- ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.activity_id, row.sequence, row.event_type, row.turn_id, row.parent_activity_id, row.conversation_scope_id, row.tool, row.workspace_id, row.workspace_root, row.workspace_mode, row.workspace_source_root, row.workspace_branch, row.workspace_target_branch, row.request_json, row.result_json, row.error, row.created_at);
54
- return rowToEvent(row);
70
+ ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.activity_id, row.sequence, row.event_type, row.turn_id, row.parent_activity_id, row.conversation_scope_id, row.tool, row.workspace_id, row.workspace_root, row.workspace_mode, row.workspace_source_root, row.workspace_branch, row.workspace_target_branch, row.request_json, row.result_json, row.error, row.payload_file, row.payload_offset, row.payload_length, row.created_at);
71
+ return this.rowToEvent(row);
55
72
  })();
56
73
  }
57
74
  listEvents(activityId) {
58
- return this.readRows(activityId).map(rowToEvent);
75
+ return this.readRows(activityId).map((row) => this.rowToEvent(row));
59
76
  }
60
77
  listActivitiesByTurn(turnId) {
61
78
  const rows = this.database.sqlite.prepare(`select activity_id from activity_audit_events
@@ -164,6 +181,13 @@ export class ActivityAuditStore {
164
181
  updatedAt,
165
182
  };
166
183
  }
184
+ deleteWorkspace(workspaceId) {
185
+ this.database.sqlite.prepare(`delete from activity_audit_events
186
+ where activity_id in (
187
+ select activity_id from activity_audit_events
188
+ where event_type = 'started' and workspace_id = ?
189
+ )`).run(workspaceId);
190
+ }
167
191
  close() {
168
192
  this.database.close();
169
193
  }
@@ -172,8 +196,51 @@ export class ActivityAuditStore {
172
196
  where activity_id = ?
173
197
  order by sequence asc`).all(activityId);
174
198
  }
199
+ rowToEvent(row) {
200
+ return rowToEvent(row, this.readPayload(row));
201
+ }
202
+ readPayload(row) {
203
+ if (row.payload_file !== null && row.payload_offset !== null && row.payload_length !== null) {
204
+ return JSON.parse(this.payloads.read({
205
+ prefix: row.payload_file,
206
+ offset: row.payload_offset,
207
+ length: row.payload_length,
208
+ }).toString("utf8"));
209
+ }
210
+ return legacyPayload(row);
211
+ }
212
+ migrateLegacyPayloads() {
213
+ const rows = this.database.sqlite.prepare(`select * from activity_audit_events
214
+ where payload_file is null
215
+ and (request_json is not null or result_json is not null or error is not null)
216
+ order by rowid asc`);
217
+ const workspaceByActivity = new Map();
218
+ const update = this.database.sqlite.prepare(`update activity_audit_events
219
+ set payload_file = ?, payload_offset = ?, payload_length = ?,
220
+ request_json = null, result_json = null, error = null
221
+ where id = ?`);
222
+ for (const row of rows.iterate()) {
223
+ const payload = legacyPayload(row);
224
+ if (payload === undefined)
225
+ continue;
226
+ let workspace = workspaceByActivity.get(row.activity_id);
227
+ if (!workspace) {
228
+ const started = row.event_type === "started"
229
+ ? row
230
+ : this.database.sqlite.prepare(`select * from activity_audit_events
231
+ where activity_id = ? and event_type = 'started'
232
+ order by sequence asc limit 1`).get(row.activity_id);
233
+ if (!started)
234
+ continue;
235
+ workspace = workspaceFromStartedRow(started);
236
+ workspaceByActivity.set(row.activity_id, workspace);
237
+ }
238
+ const reference = this.payloads.append(activityEventLogPrefix(this.stateDir, workspace), Buffer.from(JSON.stringify(payload), "utf8"));
239
+ update.run(reference.prefix, reference.offset, reference.length, row.id);
240
+ }
241
+ }
175
242
  }
176
- function eventInputToRow(input, identity) {
243
+ function eventInputToRow(input, identity, payloadRef) {
177
244
  if (input.type === "started") {
178
245
  return {
179
246
  id: identity.id,
@@ -190,9 +257,12 @@ function eventInputToRow(input, identity) {
190
257
  workspace_source_root: input.workspace.sourceRoot ?? null,
191
258
  workspace_branch: input.workspace.branch ?? null,
192
259
  workspace_target_branch: input.workspace.targetBranch ?? null,
193
- request_json: serializeJson(input.request),
260
+ request_json: null,
194
261
  result_json: null,
195
262
  error: null,
263
+ payload_file: payloadRef?.prefix ?? null,
264
+ payload_offset: payloadRef?.offset ?? null,
265
+ payload_length: payloadRef?.length ?? null,
196
266
  created_at: identity.createdAt,
197
267
  };
198
268
  }
@@ -212,12 +282,15 @@ function eventInputToRow(input, identity) {
212
282
  workspace_branch: null,
213
283
  workspace_target_branch: null,
214
284
  request_json: null,
215
- result_json: "result" in input ? serializeJson(input.result) : null,
216
- error: "error" in input ? input.error : null,
285
+ result_json: null,
286
+ error: null,
287
+ payload_file: payloadRef?.prefix ?? null,
288
+ payload_offset: payloadRef?.offset ?? null,
289
+ payload_length: payloadRef?.length ?? null,
217
290
  created_at: identity.createdAt,
218
291
  };
219
292
  }
220
- function rowToEvent(row) {
293
+ function rowToEvent(row, payload) {
221
294
  const base = {
222
295
  id: row.id,
223
296
  activityId: row.activity_id,
@@ -244,47 +317,82 @@ function rowToEvent(row) {
244
317
  ...(row.workspace_branch ? { branch: row.workspace_branch } : {}),
245
318
  ...(row.workspace_target_branch ? { targetBranch: row.workspace_target_branch } : {}),
246
319
  },
247
- ...(row.request_json !== null ? { request: parseJson(row.request_json) } : {}),
320
+ ...(payload?.request !== undefined ? { request: payload.request } : {}),
248
321
  };
249
322
  case "succeeded":
250
323
  return {
251
324
  ...base,
252
325
  type: "succeeded",
253
- result: parseJson(row.result_json),
326
+ ...(payload?.result !== undefined ? { result: payload.result } : {}),
254
327
  };
255
328
  case "returned":
256
329
  return {
257
330
  ...base,
258
331
  type: "returned",
259
- result: parseJson(row.result_json),
332
+ ...(payload?.result !== undefined ? { result: payload.result } : {}),
260
333
  };
261
334
  case "failed":
262
- if (!row.error)
335
+ if (!payload?.error)
263
336
  throw new Error(`Activity audit failed event ${row.id} is missing an error.`);
264
337
  return {
265
338
  ...base,
266
339
  type: "failed",
267
- result: parseJson(row.result_json),
268
- error: row.error,
340
+ ...(payload.result !== undefined ? { result: payload.result } : {}),
341
+ error: payload.error,
269
342
  };
270
343
  case "blocked":
271
- if (!row.error)
344
+ if (!payload?.error)
272
345
  throw new Error(`Activity audit blocked event ${row.id} is missing an error.`);
273
346
  return {
274
347
  ...base,
275
348
  type: "blocked",
276
- error: row.error,
349
+ error: payload.error,
277
350
  };
278
351
  default:
279
352
  throw new Error(`Unknown Activity audit event type: ${row.event_type}`);
280
353
  }
281
354
  }
355
+ function eventPayload(input) {
356
+ if (input.type === "started") {
357
+ return input.request === undefined ? undefined : { request: input.request };
358
+ }
359
+ if (input.type === "blocked")
360
+ return { error: input.error };
361
+ if (input.type === "failed") {
362
+ return {
363
+ ...(input.result !== undefined ? { result: input.result } : {}),
364
+ error: input.error,
365
+ };
366
+ }
367
+ return input.result === undefined ? undefined : { result: input.result };
368
+ }
369
+ function legacyPayload(row) {
370
+ const request = parseJson(row.request_json);
371
+ const result = parseJson(row.result_json);
372
+ if (request === undefined && result === undefined && row.error === null)
373
+ return undefined;
374
+ return {
375
+ ...(request !== undefined ? { request } : {}),
376
+ ...(result !== undefined ? { result } : {}),
377
+ ...(row.error !== null ? { error: row.error } : {}),
378
+ };
379
+ }
380
+ function workspaceFromStartedRow(row) {
381
+ if (!row.workspace_root || !isWorkspaceMode(row.workspace_mode)) {
382
+ throw new Error(`Activity audit start event ${row.id} is missing Workspace context.`);
383
+ }
384
+ return {
385
+ ...(row.workspace_id ? { id: row.workspace_id } : {}),
386
+ root: row.workspace_root,
387
+ mode: row.workspace_mode,
388
+ ...(row.workspace_source_root ? { sourceRoot: row.workspace_source_root } : {}),
389
+ ...(row.workspace_branch ? { branch: row.workspace_branch } : {}),
390
+ ...(row.workspace_target_branch ? { targetBranch: row.workspace_target_branch } : {}),
391
+ };
392
+ }
282
393
  function isWorkspaceMode(value) {
283
394
  return value === "checkout" || value === "worktree";
284
395
  }
285
- function serializeJson(value) {
286
- return value === undefined ? null : JSON.stringify(value);
287
- }
288
396
  function parseJson(value) {
289
397
  return value === null ? undefined : JSON.parse(value);
290
398
  }