@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
@@ -0,0 +1,346 @@
1
+ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
2
+ import * as z from "zod/v4";
3
+ import { runToolWithHooks } from "../hooks/hooks.js";
4
+ import { commandPreview, logEvent, workspaceLogLabel } from "../../runtime/logging/logger.js";
5
+ import { resolveProcessId } from "./process-sessions.js";
6
+ import { buildShellMutationPolicy, toolNames } from "../server-instructions.js";
7
+ import { durableOutputResponse, processToolResponse, readWorkspaceBashOutput, recordBashCompletion, toolResultIsError, } from "./runtime.js";
8
+ const SHELL_TOOL_ANNOTATIONS = {
9
+ readOnlyHint: false,
10
+ destructiveHint: true,
11
+ idempotentHint: false,
12
+ openWorldHint: true,
13
+ };
14
+ /**
15
+ * Register the complete process surface behind one seam. Bash and Codex-style
16
+ * process adapters intentionally share the same durable-output and completion
17
+ * runtime so tool-mode changes cannot fork process semantics.
18
+ */
19
+ export function registerProcessTools(options) {
20
+ if (options.config.toolMode === "codex")
21
+ registerCodexProcessTools(options);
22
+ else
23
+ registerBashTool(options);
24
+ }
25
+ function registerBashTool(options) {
26
+ const { server, config, workspaces, processSessions, hooks, activityLifecycle, bashOutputStore, shellRun, routing, descriptions, } = options;
27
+ registerAppTool(server, toolNames.shell, {
28
+ title: "Bash",
29
+ description: descriptions.shell,
30
+ inputSchema: {
31
+ workspaceId: z.string().describe("Workspace identifier returned by open_workspace."),
32
+ member: z.string().optional().describe("Required for a Composite Workspace; explicit member name that owns this process operation."),
33
+ action: z.enum(["run", "process", "output"]).optional().describe("Defaults to run. Use process with a returned processId to poll/interact, or output with outputId to retrieve complete durable output."),
34
+ command: z.string().optional().describe(`${descriptions.shellCommand} Required for action=run.`),
35
+ processId: z.number().int().positive().optional().describe("Process identifier returned by a previous bash action=run call. Required for action=process."),
36
+ outputId: z.string().optional().describe("Stable output identifier returned by a Bash run. Required for action=output."),
37
+ input: z.string().optional().describe("Characters to write for action=process. Omit to poll/wait without input."),
38
+ interrupt: z.boolean().optional().describe("For action=process, send SIGINT to the process. Cannot be combined with input."),
39
+ tty: z.boolean().optional().describe("For action=run, allocate a pseudo-terminal for interactive commands. Defaults to false."),
40
+ columns: z.number().int().min(1).max(1_000).optional().describe("Initial PTY width for action=run, or resize width for action=process."),
41
+ rows: z.number().int().min(1).max(1_000).optional().describe("Initial PTY height for action=run, or resize height for action=process."),
42
+ workingDirectory: z.string().optional().describe("For action=run, working directory relative to the workspace root. Defaults to the workspace root."),
43
+ yieldTimeMs: z.number().int().min(0).max(300_000).optional().describe("Maximum feedback wait, not a minimum delay: if the process finishes sooner, the call returns immediately. For long-running commands or wait-only action=process calls, set a long window near the Host request deadline (60000ms when supported) instead of repeated short polling. For action=run, use 0 for immediate background handoff; otherwise defaults to 10000ms. For action=process, wait-only calls default to 60000ms and interaction to 250ms."),
44
+ timeoutMs: z.number().int().min(1).max(86_400_000).optional().describe("For action=run, total execution timeout from process start. On expiry ForgeRelay terminates the process. Omit for no ForgeRelay execution deadline."),
45
+ maxOutputTokens: z.number().int().positive().max(100_000).optional().describe("Approximate output token budget. Defaults to 10000."),
46
+ },
47
+ outputSchema: processOutputSchema(),
48
+ // The unified Activity Panel is mounted by activity_panel. Process tools
49
+ // remain headless and feed it through durable Activity state instead.
50
+ _meta: {},
51
+ annotations: SHELL_TOOL_ANNOTATIONS,
52
+ }, async ({ workspaceId, member, action = "run", command, processId, outputId, input, interrupt, tty, columns, rows, workingDirectory, yieldTimeMs, timeoutMs, maxOutputTokens, }, extra) => {
53
+ const target = routing.resolve(workspaceId, member);
54
+ const executionWorkspaceId = target.executionWorkspaceId;
55
+ const executionContext = await routing.prepare(target, extra._meta, extra.signal, extra.sessionId);
56
+ if (routing.isRemote(executionWorkspaceId)) {
57
+ const response = await routing.bashRemote(executionWorkspaceId, {
58
+ action,
59
+ ...(command !== undefined ? { command } : {}),
60
+ ...(processId !== undefined ? { processId } : {}),
61
+ ...(outputId !== undefined ? { outputId } : {}),
62
+ ...(input !== undefined ? { input } : {}),
63
+ ...(interrupt !== undefined ? { interrupt } : {}),
64
+ ...(tty !== undefined ? { tty } : {}),
65
+ ...(columns !== undefined ? { columns } : {}),
66
+ ...(rows !== undefined ? { rows } : {}),
67
+ ...(workingDirectory !== undefined ? { workingDirectory } : {}),
68
+ ...(yieldTimeMs !== undefined ? { yieldTimeMs } : {}),
69
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
70
+ ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
71
+ }, routing.hostScopeIdFor(extra._meta, extra.sessionId));
72
+ return action === "run"
73
+ ? routing.presentSemantic(response, target, routing.hostScopeIdFor(extra._meta, extra.sessionId))
74
+ : routing.present(response, target);
75
+ }
76
+ const workspace = workspaces.getWorkspace(executionWorkspaceId);
77
+ if (action === "run") {
78
+ if (!command)
79
+ throw new Error("bash action=run requires command.");
80
+ if (processId !== undefined || outputId !== undefined || input !== undefined || interrupt !== undefined) {
81
+ throw new Error("bash action=run does not accept processId, outputId, input, or interrupt.");
82
+ }
83
+ return routing.presentSemantic(await shellRun({
84
+ workspaceId: executionWorkspaceId,
85
+ command,
86
+ surface: "bash",
87
+ tty,
88
+ columns,
89
+ rows,
90
+ workingDirectory,
91
+ yieldTimeMs,
92
+ timeoutMs,
93
+ maxOutputTokens,
94
+ }, executionContext), target, routing.hostScopeIdFor(extra._meta, extra.sessionId));
95
+ }
96
+ if (action === "output") {
97
+ if (!outputId)
98
+ throw new Error("bash action=output requires outputId.");
99
+ if (command !== undefined || processId !== undefined || input !== undefined || interrupt !== undefined ||
100
+ tty !== undefined || columns !== undefined || rows !== undefined || workingDirectory !== undefined ||
101
+ yieldTimeMs !== undefined || timeoutMs !== undefined || maxOutputTokens !== undefined) {
102
+ throw new Error("bash action=output accepts only workspaceId and outputId.");
103
+ }
104
+ return runToolWithHooks(hooks, {
105
+ signal: extra.signal,
106
+ tool: toolNames.shell,
107
+ invocation: workspaceHookInvocation(workspace),
108
+ payload: { action, outputId },
109
+ operation: async () => durableOutputResponse(toolNames.shell, executionWorkspaceId, readWorkspaceBashOutput(bashOutputStore, executionWorkspaceId, outputId)),
110
+ }).then((result) => routing.present(result, target));
111
+ }
112
+ if (outputId !== undefined)
113
+ throw new Error("bash action=process does not accept outputId.");
114
+ if (command !== undefined || workingDirectory !== undefined || tty !== undefined || timeoutMs !== undefined) {
115
+ throw new Error("bash action=process does not accept command, workingDirectory, tty, or timeoutMs.");
116
+ }
117
+ if (processId === undefined)
118
+ throw new Error("bash action=process requires processId.");
119
+ if (interrupt && input !== undefined) {
120
+ throw new Error("bash action=process cannot combine interrupt with input.");
121
+ }
122
+ return runToolWithHooks(hooks, {
123
+ signal: extra.signal,
124
+ tool: toolNames.shell,
125
+ invocation: workspaceHookInvocation(workspace),
126
+ payload: {
127
+ action,
128
+ processId,
129
+ inputLength: input?.length ?? 0,
130
+ interrupt: interrupt ?? false,
131
+ columns,
132
+ rows,
133
+ },
134
+ isFailure: toolResultIsError,
135
+ operation: async () => {
136
+ const startedAt = performance.now();
137
+ const snapshot = await processSessions.write({
138
+ workspaceId: executionWorkspaceId,
139
+ processId,
140
+ chars: interrupt ? "\u0003" : input,
141
+ columns,
142
+ rows,
143
+ yieldTimeMs,
144
+ maxOutputTokens,
145
+ signal: extra.signal,
146
+ });
147
+ logProcessToolCall(config, workspace, {
148
+ tool: toolNames.shell,
149
+ exitCode: snapshot.exitCode,
150
+ running: snapshot.running,
151
+ processId: snapshot.processId,
152
+ success: snapshot.running || snapshot.exitCode === 0,
153
+ durationMs: Math.round(performance.now() - startedAt),
154
+ });
155
+ const response = processToolResponse(toolNames.shell, executionWorkspaceId, snapshot, {
156
+ action,
157
+ processId,
158
+ inputLength: input?.length ?? 0,
159
+ interrupt: interrupt ?? false,
160
+ running: snapshot.running,
161
+ exitCode: snapshot.exitCode,
162
+ wallTimeMs: snapshot.wallTimeMs,
163
+ });
164
+ if (!snapshot.running) {
165
+ recordBashCompletion(activityLifecycle, bashOutputStore, snapshot.outputId);
166
+ }
167
+ return routing.present(response, target);
168
+ },
169
+ });
170
+ });
171
+ }
172
+ function registerCodexProcessTools(options) {
173
+ const { server, config, workspaces, processSessions, hooks, activityLifecycle, bashOutputStore, shellRun, routing, } = options;
174
+ registerAppTool(server, "exec_command", {
175
+ title: "Execute command",
176
+ description: `Run a command inside an open workspace. Returns its result when it exits during the yield window, otherwise returns a processId for write_stdin. Use this for file inspection, tests, builds, package scripts, generators, formatters, and long-running processes. ${buildShellMutationPolicy()} Call open_workspace first and pass workspaceId.`,
177
+ inputSchema: {
178
+ workspaceId: z.string().describe("Workspace identifier returned by open_workspace."),
179
+ member: z.string().optional().describe("Required for a Composite Workspace; explicit member name that owns this process."),
180
+ cmd: z.string().min(1).describe("Shell command to execute."),
181
+ tty: z.boolean().optional().describe("Allocate a pseudo-terminal for interactive commands. Defaults to false."),
182
+ columns: z.number().int().min(1).max(1_000).optional().describe("Initial PTY width. Defaults to 80."),
183
+ rows: z.number().int().min(1).max(1_000).optional().describe("Initial PTY height. Defaults to 24."),
184
+ workingDirectory: z.string().optional().describe("Working directory relative to the workspace root. Defaults to the workspace root."),
185
+ yieldTimeMs: z.number().int().min(0).max(300_000).optional().describe("Feedback window before returning a processId. Use 0 for immediate background handoff. Defaults to 10000ms."),
186
+ timeoutMs: z.number().int().min(1).max(86_400_000).optional().describe("Total execution timeout from process start. On expiry ForgeRelay terminates the process. Omit for no ForgeRelay execution deadline."),
187
+ maxOutputTokens: z.number().int().positive().max(100_000).optional().describe("Approximate output token budget. Defaults to 10000."),
188
+ },
189
+ outputSchema: processOutputSchema(),
190
+ _meta: {},
191
+ annotations: SHELL_TOOL_ANNOTATIONS,
192
+ }, async ({ workspaceId, member, cmd, tty, columns, rows, workingDirectory, yieldTimeMs, timeoutMs, maxOutputTokens }, extra) => {
193
+ const target = routing.resolve(workspaceId, member);
194
+ const context = await routing.prepare(target, extra._meta, extra.signal, extra.sessionId);
195
+ if (routing.isRemote(target.executionWorkspaceId)) {
196
+ return routing.presentSemantic(await routing.execCommandRemote(target.executionWorkspaceId, {
197
+ cmd,
198
+ ...(tty !== undefined ? { tty } : {}),
199
+ ...(columns !== undefined ? { columns } : {}),
200
+ ...(rows !== undefined ? { rows } : {}),
201
+ ...(workingDirectory !== undefined ? { workingDirectory } : {}),
202
+ ...(yieldTimeMs !== undefined ? { yieldTimeMs } : {}),
203
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
204
+ ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
205
+ }, routing.hostScopeIdFor(extra._meta, extra.sessionId)), target, routing.hostScopeIdFor(extra._meta, extra.sessionId));
206
+ }
207
+ return routing.presentSemantic(await shellRun({
208
+ workspaceId: target.executionWorkspaceId,
209
+ command: cmd,
210
+ surface: "exec_command",
211
+ tty,
212
+ columns,
213
+ rows,
214
+ workingDirectory,
215
+ yieldTimeMs,
216
+ timeoutMs,
217
+ maxOutputTokens,
218
+ }, context), target, routing.hostScopeIdFor(extra._meta, extra.sessionId));
219
+ });
220
+ registerAppTool(server, "write_stdin", {
221
+ title: "Write to process",
222
+ description: "Poll or write characters to a running process returned by exec_command, or retrieve complete durable process output by outputId. Omit chars or pass an empty string to poll. Waiting never kills the process; pass \\u0003 to explicitly send Ctrl-C.",
223
+ inputSchema: {
224
+ workspaceId: z.string().describe("Workspace identifier used to start the process."),
225
+ member: z.string().optional().describe("Required for a Composite Workspace; explicit member name that owns the process."),
226
+ processId: z.number().int().positive().optional().describe("Canonical process identifier returned by bash or exec_command."),
227
+ sessionId: z.number().int().positive().optional().describe("Deprecated alias for processId. Retained for compatibility."),
228
+ outputId: z.string().optional().describe("Stable output identifier returned by exec_command. When supplied, retrieve the complete durable output instead of controlling a process."),
229
+ chars: z.string().optional().describe("Characters to write. Omit or pass an empty string to poll."),
230
+ columns: z.number().int().min(1).max(1_000).optional().describe("Resize a PTY to this width."),
231
+ rows: z.number().int().min(1).max(1_000).optional().describe("Resize a PTY to this height."),
232
+ yieldTimeMs: z.number().int().min(0).max(300_000).optional().describe("Milliseconds to keep waiting before returning again, max 300000. Polling defaults to 60000; interaction defaults to 250."),
233
+ maxOutputTokens: z.number().int().positive().max(100_000).optional().describe("Approximate output token budget. Defaults to 10000."),
234
+ },
235
+ outputSchema: processOutputSchema(),
236
+ _meta: {},
237
+ annotations: SHELL_TOOL_ANNOTATIONS,
238
+ }, async ({ workspaceId, member, processId, sessionId, outputId, chars, columns, rows, yieldTimeMs, maxOutputTokens }, extra) => {
239
+ const target = routing.resolve(workspaceId, member);
240
+ await routing.prepare(target, extra._meta, extra.signal, extra.sessionId);
241
+ if (routing.isRemote(target.executionWorkspaceId)) {
242
+ return routing.present(await routing.writeStdinRemote(target.executionWorkspaceId, {
243
+ ...(processId !== undefined ? { processId } : {}),
244
+ ...(sessionId !== undefined ? { sessionId } : {}),
245
+ ...(outputId !== undefined ? { outputId } : {}),
246
+ ...(chars !== undefined ? { chars } : {}),
247
+ ...(columns !== undefined ? { columns } : {}),
248
+ ...(rows !== undefined ? { rows } : {}),
249
+ ...(yieldTimeMs !== undefined ? { yieldTimeMs } : {}),
250
+ ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
251
+ }, routing.hostScopeIdFor(extra._meta, extra.sessionId)), target);
252
+ }
253
+ const executionWorkspaceId = target.executionWorkspaceId;
254
+ const workspace = workspaces.getWorkspace(executionWorkspaceId);
255
+ if (outputId !== undefined) {
256
+ if (processId !== undefined || sessionId !== undefined || chars !== undefined || columns !== undefined ||
257
+ rows !== undefined || yieldTimeMs !== undefined || maxOutputTokens !== undefined) {
258
+ throw new Error("write_stdin outputId lookup cannot be combined with process control fields.");
259
+ }
260
+ return runToolWithHooks(hooks, {
261
+ signal: extra.signal,
262
+ tool: "write_stdin",
263
+ invocation: workspaceHookInvocation(workspace),
264
+ payload: { outputId },
265
+ operation: async () => durableOutputResponse("write_stdin", executionWorkspaceId, readWorkspaceBashOutput(bashOutputStore, executionWorkspaceId, outputId)),
266
+ }).then((result) => routing.present(result, target));
267
+ }
268
+ const resolvedProcessId = resolveProcessId(processId, sessionId);
269
+ return runToolWithHooks(hooks, {
270
+ signal: extra.signal,
271
+ tool: "write_stdin",
272
+ invocation: workspaceHookInvocation(workspace),
273
+ payload: {
274
+ processId: resolvedProcessId,
275
+ charactersWritten: chars?.length ?? 0,
276
+ columns,
277
+ rows,
278
+ },
279
+ operation: async () => {
280
+ const startedAt = performance.now();
281
+ const snapshot = await processSessions.write({
282
+ workspaceId: executionWorkspaceId,
283
+ processId: resolvedProcessId,
284
+ chars,
285
+ columns,
286
+ rows,
287
+ yieldTimeMs,
288
+ maxOutputTokens,
289
+ signal: extra.signal,
290
+ });
291
+ logProcessToolCall(config, workspace, {
292
+ tool: "write_stdin",
293
+ exitCode: snapshot.exitCode,
294
+ running: snapshot.running,
295
+ processId: snapshot.processId,
296
+ success: snapshot.running || snapshot.exitCode === 0,
297
+ durationMs: Math.round(performance.now() - startedAt),
298
+ });
299
+ const response = processToolResponse("write_stdin", executionWorkspaceId, snapshot, {
300
+ processId: resolvedProcessId,
301
+ charactersWritten: chars?.length ?? 0,
302
+ running: snapshot.running,
303
+ exitCode: snapshot.exitCode,
304
+ wallTimeMs: snapshot.wallTimeMs,
305
+ });
306
+ if (!snapshot.running) {
307
+ recordBashCompletion(activityLifecycle, bashOutputStore, snapshot.outputId);
308
+ }
309
+ return response;
310
+ },
311
+ }).then((result) => routing.present(result, target));
312
+ });
313
+ }
314
+ function processOutputSchema() {
315
+ return {
316
+ result: z.string().describe("Model-readable result text for follow-up reasoning and plain MCP hosts."),
317
+ processId: z.number().int().positive().optional().describe("Canonical process handle for bash(action=\"process\") or the active command adapter."),
318
+ sessionId: z.number().int().positive().optional().describe("Deprecated alias of processId for compatibility."),
319
+ outputId: z.string().optional().describe("Stable local audit identifier for retrieving the complete original process output."),
320
+ running: z.boolean(),
321
+ exitCode: z.number().int().optional(),
322
+ signal: z.string().optional(),
323
+ timedOut: z.boolean(),
324
+ wallTimeMs: z.number().nonnegative(),
325
+ outputTruncated: z.boolean(),
326
+ };
327
+ }
328
+ function workspaceHookInvocation(workspace) {
329
+ return {
330
+ workspaceId: workspace.id,
331
+ workspaceRoot: workspace.root,
332
+ workspaceMode: workspace.mode,
333
+ sourceRoot: workspace.sourceRoot,
334
+ };
335
+ }
336
+ function logProcessToolCall(config, workspace, fields) {
337
+ if (!config.logging.toolCalls)
338
+ return;
339
+ const { command, ...safeFields } = fields;
340
+ logEvent(config.logging, fields.success ? "info" : "warn", "tool_call", {
341
+ ...safeFields,
342
+ workspaceId: workspace.id,
343
+ workspace: workspaceLogLabel(workspace.root, workspace.id),
344
+ commandPreview: config.logging.shellCommands && command ? commandPreview(command) : undefined,
345
+ });
346
+ }
@@ -0,0 +1,70 @@
1
+ import { runToolWithHooks } from "../../hooks/hooks.js";
2
+ import { toolResultIsError } from "./tool-support.js";
3
+ export function workspaceActivitySnapshot(workspace) {
4
+ return {
5
+ id: workspace.id,
6
+ root: workspace.root,
7
+ mode: workspace.mode,
8
+ ...(workspace.sourceRoot ? { sourceRoot: workspace.sourceRoot } : {}),
9
+ ...(workspace.worktree?.branch ? { branch: workspace.worktree.branch } : {}),
10
+ ...(workspace.worktree?.targetBranch ? { targetBranch: workspace.worktree.targetBranch } : {}),
11
+ };
12
+ }
13
+ export function activityFailureMessage(result) {
14
+ if (typeof result !== "object" || result === null)
15
+ return "Tool returned a failed result.";
16
+ const record = result;
17
+ if (Array.isArray(record.content)) {
18
+ const text = record.content
19
+ .map((entry) => {
20
+ if (typeof entry !== "object" || entry === null)
21
+ return "";
22
+ const value = entry.text;
23
+ return typeof value === "string" ? value : "";
24
+ })
25
+ .filter(Boolean)
26
+ .join("\n");
27
+ if (text)
28
+ return text;
29
+ }
30
+ if (typeof record.structuredContent === "object" && record.structuredContent !== null) {
31
+ const value = record.structuredContent.result;
32
+ if (typeof value === "string" && value)
33
+ return value;
34
+ }
35
+ return "Tool returned a failed result.";
36
+ }
37
+ export function standardActivityOutcome(result) {
38
+ return toolResultIsError(result)
39
+ ? { type: "failed", error: activityFailureMessage(result) }
40
+ : { type: "succeeded" };
41
+ }
42
+ export function activityRelationFor(context) {
43
+ return {
44
+ ...(context.parentActivityId ? { parentActivityId: context.parentActivityId } : {}),
45
+ ...(context.turnId ? { turnId: context.turnId } : {}),
46
+ };
47
+ }
48
+ export function activityRequestFor(input, context) {
49
+ if (!context.activityMember || !input || typeof input !== "object" || Array.isArray(input))
50
+ return input;
51
+ return {
52
+ ...input,
53
+ member: context.activityMember,
54
+ };
55
+ }
56
+ export function runActivityTool(lifecycle, workspace, conversationScopeId, tool, request, operation, outcome = standardActivityOutcome, relation = {}, auditResult) {
57
+ return lifecycle.run({
58
+ tool,
59
+ workspace: workspaceActivitySnapshot(workspace),
60
+ conversationScopeId,
61
+ request,
62
+ operation,
63
+ outcome,
64
+ ...(auditResult ? { auditResult } : {}),
65
+ ...relation,
66
+ });
67
+ }
68
+ export function runActivityToolWithHooks(lifecycle, hooks, workspace, conversationScopeId, request, hookOptions, relation = {}) {
69
+ return runActivityTool(lifecycle, workspace, conversationScopeId, hookOptions.tool, request, () => runToolWithHooks(hooks, hookOptions), standardActivityOutcome, relation);
70
+ }
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import { markAdvertisedFileSourceActivated, resolveAdvertisedFileReadPath, } from "./advertised-files.js";
3
+ import { markAdvertisedFileSourceActivated, resolveAdvertisedFileReadPath, } from "../../filesystem/advertised-files.js";
4
4
  const CAPABILITY_GUIDE_DEFINITIONS = [
5
5
  {
6
6
  name: "lifecycle-hooks",
@@ -9,6 +9,7 @@ const CAPABILITY_GUIDE_DEFINITIONS = [
9
9
  },
10
10
  {
11
11
  name: "managed-worktrees",
12
+ directory: "workspace/managed-worktrees",
12
13
  description: "Managed-worktree lifecycle, close safety, and recovery.",
13
14
  whenToRead: "Read for advanced mode=\"worktree\" flows.",
14
15
  },
@@ -41,6 +42,7 @@ const CAPABILITY_GUIDE_DEFINITIONS = [
41
42
  },
42
43
  {
43
44
  name: "workspace-tasks",
45
+ directory: "workspace/workspace-tasks",
44
46
  description: "Persistent Task Lists owned by the current Workspace.",
45
47
  whenToRead: "Read before creating or maintaining Workspace Tasks.",
46
48
  },
@@ -52,14 +54,14 @@ const CAPABILITY_GUIDE_DEFINITIONS = [
52
54
  },
53
55
  ];
54
56
  function capabilityGuidesDir() {
55
- return fileURLToPath(new URL("../capabilities", import.meta.url));
57
+ return fileURLToPath(new URL("../../../../capabilities", import.meta.url));
56
58
  }
57
59
  export function loadCapabilityGuides(config) {
58
60
  const root = capabilityGuidesDir();
59
61
  return CAPABILITY_GUIDE_DEFINITIONS
60
62
  .filter((definition) => definition.enabled?.(config) ?? true)
61
63
  .map((definition) => {
62
- const baseDir = join(root, definition.name);
64
+ const baseDir = join(root, definition.directory ?? definition.name);
63
65
  return {
64
66
  name: definition.name,
65
67
  description: definition.description,
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
- import { MAX_CODE_INTELLIGENCE_RESULT_LIMIT, } from "./lsp/code-intelligence-types.js";
3
- import { batchExecuteInputSchema, } from "./operations/batch/types.js";
2
+ import { MAX_CODE_INTELLIGENCE_RESULT_LIMIT, } from "../../../lsp/code-intelligence-types.js";
3
+ import { batchExecuteInputSchema, } from "../../operations/batch/types.js";
4
4
  export class CapabilityError extends Error {
5
5
  code;
6
6
  constructor(code, message) {
@@ -241,6 +241,11 @@ export function createCapabilityRegistry(dependencies) {
241
241
  path: z.string().min(1),
242
242
  limit: z.number().int().min(1).max(MAX_CODE_INTELLIGENCE_RESULT_LIMIT).optional(),
243
243
  }).strict(),
244
+ z.object({ operation: z.literal("managed.status") }).strict(),
245
+ z.object({
246
+ operation: z.literal("managed.install"),
247
+ servers: z.array(z.enum(["typescript", "pyright"])).min(1),
248
+ }).strict(),
244
249
  ]);
245
250
  return new CapabilityRegistry([
246
251
  {
@@ -273,7 +278,7 @@ export function createCapabilityRegistry(dependencies) {
273
278
  ...(dependencies.codeIntelligence
274
279
  ? [{
275
280
  name: "code.intelligence",
276
- description: "Read semantic code information through an available Language server without changing the Workspace.",
281
+ description: "Read semantic code information and, when explicitly enabled by the user, manage ForgeRelay-owned Language Servers for this instance.",
277
282
  guideName: "code-intelligence",
278
283
  readGuideBeforeFirstUse: true,
279
284
  batchPolicy: "parallel",
@@ -300,7 +305,7 @@ export function createCapabilityRegistry(dependencies) {
300
305
  ...(dependencies.subagentSession
301
306
  ? [{
302
307
  name: "subagent.session",
303
- description: "Coordinate provider-backed Subagent Sessions in the current Execution Workspace.",
308
+ description: "Delegate explicit work to provider-backed Subagent Sessions in the current Execution Workspace; disclose delegation and verify returned results before presenting them as final.",
304
309
  guideName: "subagents",
305
310
  readGuideBeforeFirstUse: true,
306
311
  batchPolicy: "unsupported",
@@ -0,0 +1,90 @@
1
+ import { CapabilityError } from "./capability-registry.js";
2
+ import { formatPathForPrompt } from "../../../workspaces/resources/skills.js";
3
+ export function workspaceHookInvocation(workspace) {
4
+ return {
5
+ workspaceId: workspace.id,
6
+ workspaceRoot: workspace.root,
7
+ workspaceMode: workspace.mode,
8
+ sourceRoot: workspace.sourceRoot,
9
+ };
10
+ }
11
+ export function capabilityContextFor(workspace) {
12
+ return {
13
+ workspaceId: workspace.id,
14
+ workspaceKind: "workspace",
15
+ workspaceRoot: workspace.root,
16
+ guides: workspace.capabilityGuides.map((guide) => ({
17
+ name: guide.name,
18
+ description: guide.description,
19
+ whenToRead: guide.whenToRead,
20
+ path: formatPathForPrompt(guide.filePath),
21
+ })),
22
+ };
23
+ }
24
+ export function compositeCapabilityContext(workspaceId, guides) {
25
+ return {
26
+ workspaceId,
27
+ workspaceKind: "composite",
28
+ guides: guides.map((guide) => ({
29
+ name: guide.name,
30
+ description: guide.description,
31
+ whenToRead: guide.whenToRead,
32
+ path: formatPathForPrompt(guide.filePath),
33
+ })),
34
+ };
35
+ }
36
+ export function requireCapabilityWorkspaceRoot(context) {
37
+ if (!context.workspaceRoot) {
38
+ throw new CapabilityError("capability_unavailable", `Capability execution requires a filesystem-backed Workspace; ${context.workspaceId} is ${context.workspaceKind}.`);
39
+ }
40
+ return context.workspaceRoot;
41
+ }
42
+ export function runWorkspaceTasksCapability(store, workspaceId, input) {
43
+ switch (input.operation) {
44
+ case "get":
45
+ if (input.level === "headers")
46
+ return store.readHeaders(workspaceId, input.listId);
47
+ if (input.level === "detail")
48
+ return store.readTaskDetail(workspaceId, input.listId, input.taskId);
49
+ return store.readSummary(workspaceId);
50
+ case "list.create":
51
+ store.createList(workspaceId, { name: input.name, position: input.position });
52
+ return store.readSummary(workspaceId);
53
+ case "list.update":
54
+ store.updateList(workspaceId, input.listId, {
55
+ name: input.name,
56
+ state: input.state,
57
+ position: input.position,
58
+ });
59
+ return store.readSummary(workspaceId);
60
+ case "list.delete":
61
+ store.deleteList(workspaceId, input.listId);
62
+ return store.readSummary(workspaceId);
63
+ case "task.create":
64
+ store.createTask(workspaceId, input.listId, {
65
+ subject: input.subject,
66
+ content: input.content,
67
+ status: input.status,
68
+ position: input.position,
69
+ });
70
+ return store.readHeaders(workspaceId, input.listId);
71
+ case "task.update":
72
+ store.updateTask(workspaceId, input.listId, input.taskId, {
73
+ subject: input.subject,
74
+ content: input.content,
75
+ status: input.status,
76
+ position: input.position,
77
+ });
78
+ return store.readHeaders(workspaceId, input.listId);
79
+ case "task.delete":
80
+ store.deleteTask(workspaceId, input.listId, input.taskId);
81
+ return store.readHeaders(workspaceId, input.listId);
82
+ }
83
+ }
84
+ export async function reviewWorkspaceChanges(reviewCheckpoints, workspace) {
85
+ return reviewCheckpoints.reviewChanges({
86
+ workspaceId: workspace.id,
87
+ root: workspace.root,
88
+ markReviewed: true,
89
+ });
90
+ }