@platless/axom-cli 0.3.13 → 0.6.36

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 (320) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +119 -9
  3. package/dist/axom-patch/cli/account-limits/README.md +27 -0
  4. package/dist/axom-patch/cli/account-limits/antigravity-proxy.mjs +47 -0
  5. package/dist/axom-patch/cli/account-limits/antigravity-read.mjs +90 -0
  6. package/dist/axom-patch/cli/account-limits/antigravity.mjs +46 -0
  7. package/dist/axom-patch/cli/account-limits/generate.py +123 -0
  8. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +81 -0
  9. package/dist/axom-patch/cli/account-limits/normalize.mjs +151 -0
  10. package/dist/axom-patch/cli/claude-account-limits.json +37 -0
  11. package/dist/axom-patch/cli/codex-account-limits.json +37 -0
  12. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  13. package/dist/axom-patch/cli/commandcode-account-limits.json +30 -0
  14. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  15. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  16. package/dist/axom-patch/cli/nightly-task.md +194 -0
  17. package/dist/axom-patch/cli/pi-subagent-subwork.json +23 -0
  18. package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +5 -1
  19. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  20. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  21. package/dist/axom-patch/cli/probes/pi-subagent-subwork.mjs +211 -0
  22. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +257 -0
  23. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  24. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  25. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -5
  26. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -7
  27. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  28. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  29. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  30. package/dist/axom-patch/cli/runtime.json +39 -15
  31. package/dist/axom-patch/cli/zcode-account-limits.json +30 -0
  32. package/dist/axom-patch/cli/zcode-models.json +37 -0
  33. package/dist/axom-patch/cli/zcode-subagent-subwork.json +72 -0
  34. package/dist/cli.js +4 -9
  35. package/dist/commands/knowledge.js +47 -0
  36. package/dist/commands/update.js +65 -3
  37. package/dist/commands/workstation.js +73 -7
  38. package/dist/install/claude/commands.js +13 -13
  39. package/dist/keepers/acp/keeper.mjs +3083 -0
  40. package/dist/keepers/acp/manifest.json +9 -0
  41. package/dist/keepers/terminal/keeper.mjs +8469 -0
  42. package/dist/keepers/terminal/manifest.json +9 -0
  43. package/dist/keepers/terminal/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  44. package/dist/keepers/terminal/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  45. package/dist/keepers/terminal/node_modules/node-pty/lib/index.js +52 -0
  46. package/dist/keepers/terminal/node_modules/node-pty/lib/interfaces.js +7 -0
  47. package/dist/keepers/terminal/node_modules/node-pty/lib/shared/conout.js +10 -0
  48. package/dist/keepers/terminal/node_modules/node-pty/lib/terminal.js +190 -0
  49. package/dist/keepers/terminal/node_modules/node-pty/lib/types.js +7 -0
  50. package/dist/keepers/terminal/node_modules/node-pty/lib/unixTerminal.js +351 -0
  51. package/dist/keepers/terminal/node_modules/node-pty/lib/utils.js +38 -0
  52. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
  53. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
  54. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsTerminal.js +209 -0
  55. package/dist/keepers/terminal/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  56. package/dist/keepers/terminal/node_modules/node-pty/package.json +66 -0
  57. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  58. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  59. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  60. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  61. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  62. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  63. package/dist/mcp.js +1 -1
  64. package/dist/native/darwin-arm64/axom-quic +0 -0
  65. package/dist/test/keeperKit.js +67 -0
  66. package/dist/workstation/acp/acpAgents.js +2192 -636
  67. package/dist/workstation/acp/acpClient.js +84 -495
  68. package/dist/workstation/acp/adoption.js +10 -396
  69. package/dist/workstation/acp/binaryInstall.js +100 -27
  70. package/dist/workstation/acp/builds/gc.js +245 -0
  71. package/dist/workstation/acp/builds/install/binary.js +28 -0
  72. package/dist/workstation/acp/builds/install/npm.js +324 -0
  73. package/dist/workstation/acp/builds/install/uv.js +254 -0
  74. package/dist/workstation/acp/builds/migrate.js +195 -0
  75. package/dist/workstation/acp/builds/model.js +128 -0
  76. package/dist/workstation/acp/builds/nodeGate.js +100 -0
  77. package/dist/workstation/acp/builds/store.js +352 -0
  78. package/dist/workstation/acp/commandTableProbe.js +68 -0
  79. package/dist/workstation/acp/configOptions.js +103 -1
  80. package/dist/workstation/acp/envSecrets.js +27 -0
  81. package/dist/workstation/acp/initializeResponse.js +290 -0
  82. package/dist/workstation/acp/installErrors.js +12 -2
  83. package/dist/workstation/acp/installProbe.js +1 -1
  84. package/dist/workstation/acp/managedPatches.js +239 -159
  85. package/dist/workstation/acp/managedRuntime.js +314 -0
  86. package/dist/workstation/acp/outbox.js +330 -0
  87. package/dist/workstation/acp/patchAssets.js +60 -3
  88. package/dist/workstation/acp/permission.js +42 -10
  89. package/dist/workstation/acp/processor/checkpoint.js +78 -0
  90. package/dist/workstation/acp/processor/commands.js +35 -0
  91. package/dist/workstation/acp/processor/control.js +282 -0
  92. package/dist/workstation/acp/processor/emit.js +26 -0
  93. package/dist/workstation/acp/processor/jsonrpc.js +172 -0
  94. package/dist/workstation/acp/processor/memoryLog.js +49 -0
  95. package/dist/workstation/acp/processor/origins.js +48 -0
  96. package/dist/workstation/acp/processor/output.js +67 -0
  97. package/dist/workstation/acp/processor/projection.js +42 -0
  98. package/dist/workstation/acp/processor/record.js +128 -0
  99. package/dist/workstation/acp/processor/runtime.js +105 -0
  100. package/dist/workstation/acp/processor/sdkSchemas.js +61 -0
  101. package/dist/workstation/acp/processor/session.js +496 -0
  102. package/dist/workstation/acp/processor/state.js +67 -0
  103. package/dist/workstation/acp/processor/step.js +442 -0
  104. package/dist/workstation/acp/processor/test/fixtureLog.js +58 -0
  105. package/dist/workstation/acp/processor/test/harness.js +120 -0
  106. package/dist/workstation/acp/processor/test/largeIdEcho.js +50 -0
  107. package/dist/workstation/acp/processor/test/rawAgent.js +70 -0
  108. package/dist/workstation/acp/processor/transport/generations.js +76 -0
  109. package/dist/workstation/acp/processor/transport/keeper.js +130 -0
  110. package/dist/workstation/acp/processor/transport/local.js +269 -0
  111. package/dist/workstation/acp/processor/transport/types.js +78 -0
  112. package/dist/workstation/acp/processor/updates.js +390 -0
  113. package/dist/workstation/acp/processorSession.js +1348 -0
  114. package/dist/workstation/acp/registry/catalog.js +189 -41
  115. package/dist/workstation/acp/registry/launch.js +76 -119
  116. package/dist/workstation/acp/sessionUpdateMapper.js +18 -7
  117. package/dist/workstation/acp/steering.js +58 -0
  118. package/dist/workstation/acp/switching.js +111 -0
  119. package/dist/workstation/acp/terminalLogin.js +16 -42
  120. package/dist/workstation/acp/terminalLoginSpec.js +41 -0
  121. package/dist/workstation/acp/test/adapterFixtures.js +256 -0
  122. package/dist/workstation/acp/test/daemonLife.js +106 -0
  123. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  124. package/dist/workstation/acp/test/keeperWorld.js +295 -0
  125. package/dist/workstation/acp/test/memoryOutbox.js +55 -0
  126. package/dist/workstation/acp/test/raw/fixtures.js +196 -0
  127. package/dist/workstation/acp/test/raw/sanitize.js +344 -0
  128. package/dist/workstation/acp/test/raw/sources.js +135 -0
  129. package/dist/workstation/acp/test/realAdapter.js +331 -0
  130. package/dist/workstation/acp/test/scriptAgent.js +214 -0
  131. package/dist/workstation/acp/test/sessionFrames.js +42 -0
  132. package/dist/workstation/acp/timedCommand.js +4 -2
  133. package/dist/workstation/acp/uvxInstall.js +72 -122
  134. package/dist/workstation/agentFlow.js +103 -0
  135. package/dist/workstation/agentSessions.js +1289 -544
  136. package/dist/workstation/channels/agent.js +7 -5
  137. package/dist/workstation/channels/computer.js +69 -21
  138. package/dist/workstation/channels/computerExecs.js +200 -0
  139. package/dist/workstation/channels/management.js +172 -8
  140. package/dist/workstation/channels/url.js +1 -1
  141. package/dist/workstation/config.js +16 -4
  142. package/dist/workstation/connection/encryptedChannel.js +4 -1
  143. package/dist/workstation/control/bindingStore.js +200 -0
  144. package/dist/workstation/control/commands.js +124 -0
  145. package/dist/workstation/control/daemonIdentity.js +53 -0
  146. package/dist/workstation/control/enrollment.js +106 -0
  147. package/dist/workstation/control/inspect.js +71 -0
  148. package/dist/workstation/control/locks.js +63 -0
  149. package/dist/workstation/control/protocol.js +40 -0
  150. package/dist/workstation/control/restartGuard.js +46 -0
  151. package/dist/workstation/control/runtimeClient.js +97 -0
  152. package/dist/workstation/control/runtimeHost.js +148 -0
  153. package/dist/workstation/control/serverIdentity.js +35 -0
  154. package/dist/workstation/daemon.js +393 -12
  155. package/dist/workstation/daemonctl.js +103 -14
  156. package/dist/workstation/desktopControl/audio.js +99 -0
  157. package/dist/workstation/desktopControl/capabilities.js +92 -0
  158. package/dist/workstation/desktopControl/captureModes.js +9 -0
  159. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  160. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  161. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  162. package/dist/workstation/desktopControl/lobby.js +129 -0
  163. package/dist/workstation/desktopControl/macProvider.js +155 -0
  164. package/dist/workstation/desktopControl/media.js +331 -0
  165. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  166. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  167. package/dist/workstation/desktopControl/overview.js +58 -0
  168. package/dist/workstation/desktopControl/protocol.js +302 -0
  169. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  170. package/dist/workstation/desktopControl/rateController.js +226 -0
  171. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  172. package/dist/workstation/desktopControl/transport.js +27 -0
  173. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  174. package/dist/workstation/exec/attach.js +126 -0
  175. package/dist/workstation/exec/command.js +40 -0
  176. package/dist/workstation/exec/durable.js +517 -0
  177. package/dist/workstation/exec/fallback.js +106 -0
  178. package/dist/workstation/exec/policy.js +837 -0
  179. package/dist/workstation/exec/shaping.js +211 -0
  180. package/dist/workstation/exec.js +3 -112
  181. package/dist/workstation/files/adapters/computerFileFrames.js +51 -0
  182. package/dist/workstation/files/adapters/toolRequests.js +121 -0
  183. package/dist/workstation/files/client.js +455 -0
  184. package/dist/workstation/files/credentials.js +142 -0
  185. package/dist/workstation/files/errors.js +28 -0
  186. package/dist/workstation/files/fence.js +350 -0
  187. package/dist/workstation/files/find/fileIndex.js +153 -0
  188. package/dist/workstation/files/find/fuzzy.js +155 -0
  189. package/dist/workstation/files/host.js +122 -0
  190. package/dist/workstation/files/hostProtocol.js +2 -0
  191. package/dist/workstation/files/media/cache.js +620 -0
  192. package/dist/workstation/files/media/encoding.js +308 -0
  193. package/dist/workstation/files/media/estimator.js +100 -0
  194. package/dist/workstation/files/media/media.js +325 -0
  195. package/dist/workstation/files/media/packedAudio.js +23 -0
  196. package/dist/workstation/files/media/scheduler.js +551 -0
  197. package/dist/workstation/files/media/types.js +5 -0
  198. package/dist/workstation/files/scope.js +45 -0
  199. package/dist/workstation/files/structured.js +284 -0
  200. package/dist/workstation/{fileOps.js → files/text.js} +152 -39
  201. package/dist/workstation/files/uploads.js +351 -0
  202. package/dist/workstation/journal.js +65 -236
  203. package/dist/workstation/keepers/acp/client.js +101 -0
  204. package/dist/workstation/keepers/acp/envelope.js +62 -0
  205. package/dist/workstation/keepers/acp/host.js +419 -0
  206. package/dist/workstation/keepers/acp/main.js +18 -0
  207. package/dist/workstation/keepers/acp/memoryStream.js +121 -0
  208. package/dist/workstation/keepers/acp/params.js +137 -0
  209. package/dist/workstation/keepers/acp/protocol.js +66 -0
  210. package/dist/workstation/keepers/acp/recover.js +120 -0
  211. package/dist/workstation/keepers/acp/session.js +584 -0
  212. package/dist/workstation/keepers/acp/stderr.js +154 -0
  213. package/dist/workstation/keepers/kit/activity.js +103 -0
  214. package/dist/workstation/keepers/kit/bootstrap.js +164 -0
  215. package/dist/workstation/keepers/kit/buildVersion.js +51 -0
  216. package/dist/workstation/keepers/kit/bundle.js +124 -0
  217. package/dist/workstation/keepers/kit/client.js +432 -0
  218. package/dist/workstation/keepers/kit/identity.js +21 -0
  219. package/dist/workstation/keepers/kit/ipc.js +112 -0
  220. package/dist/workstation/keepers/kit/launchConfig.js +8 -0
  221. package/dist/workstation/keepers/kit/launcher.js +289 -0
  222. package/dist/workstation/keepers/kit/lock.js +123 -0
  223. package/dist/workstation/keepers/kit/orphanSweep.js +86 -0
  224. package/dist/workstation/keepers/kit/paths.js +76 -0
  225. package/dist/workstation/keepers/kit/protocol.js +43 -0
  226. package/dist/workstation/keepers/kit/server.js +518 -0
  227. package/dist/workstation/keepers/kit/streamLog.js +697 -0
  228. package/dist/workstation/keepers/legacy.js +296 -0
  229. package/dist/workstation/keepers/policy.js +576 -0
  230. package/dist/workstation/keepers/stop.js +34 -0
  231. package/dist/workstation/keepers/terminal/client.js +213 -0
  232. package/dist/workstation/keepers/terminal/exec.js +981 -0
  233. package/dist/workstation/keepers/terminal/generations.js +129 -0
  234. package/dist/workstation/keepers/terminal/keeper.js +62 -0
  235. package/dist/workstation/keepers/terminal/main.js +43 -0
  236. package/dist/workstation/keepers/terminal/orphanSweep.js +3 -0
  237. package/dist/workstation/keepers/terminal/params.js +27 -0
  238. package/dist/workstation/keepers/terminal/protocol.js +56 -0
  239. package/dist/workstation/keepers/terminal/pty.js +330 -0
  240. package/dist/workstation/managedInstall.js +57 -0
  241. package/dist/workstation/protocol/accountLimits.js +47 -0
  242. package/dist/workstation/protocol/agent.js +50 -19
  243. package/dist/workstation/protocol/common.js +30 -0
  244. package/dist/workstation/protocol/computer.js +15 -4
  245. package/dist/workstation/protocol/management.js +141 -4
  246. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  247. package/dist/workstation/rd/artifact.js +119 -0
  248. package/dist/workstation/rd/control.js +119 -0
  249. package/dist/workstation/rd/display.js +29 -0
  250. package/dist/workstation/rd/provider.js +222 -0
  251. package/dist/workstation/rd/sessions.js +391 -0
  252. package/dist/workstation/rd/state.js +45 -0
  253. package/dist/workstation/rd/update.js +171 -0
  254. package/dist/workstation/rd/upkeep.js +38 -0
  255. package/dist/workstation/register.js +27 -37
  256. package/dist/workstation/service/installer.js +104 -13
  257. package/dist/workstation/service/launchd.js +1 -0
  258. package/dist/workstation/storageMetrics.js +118 -0
  259. package/dist/workstation/systemMetrics.js +154 -93
  260. package/dist/workstation/toolchain.js +94 -0
  261. package/dist/workstation/tools/browser.js +28 -4
  262. package/dist/workstation/tools/client.js +9 -1
  263. package/dist/workstation/tools/info.js +50 -0
  264. package/dist/workstation/tools/keeperProcess.js +15 -5
  265. package/dist/workstation/tools/router.js +55 -0
  266. package/dist/workstation/tools/terminalRouter.js +222 -0
  267. package/dist/workstation/tools/validation.js +37 -1
  268. package/dist/workstation/update/artifact.js +2 -1
  269. package/dist/workstation/update/coordinator.js +3 -0
  270. package/dist/workstation/update/globalInstall.js +116 -0
  271. package/dist/workstation/update/npmInstaller.js +49 -23
  272. package/dist/workstation/update/ptyPreflight.js +21 -0
  273. package/dist/workstation/update/rdRollback.js +145 -0
  274. package/dist/workstation/update/runUpdate.js +3 -2
  275. package/dist/workstation/update/taskRegistry.js +105 -11
  276. package/dist/workstation/update/watchdog.js +16790 -334
  277. package/package.json +11 -8
  278. package/skills/axom/SKILL.md +11 -11
  279. package/skills/axom-definition/SKILL.md +3 -3
  280. package/skills/axom-platform/SKILL.md +3 -3
  281. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  282. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -49
  283. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  284. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  285. package/dist/commands/guide.js +0 -38
  286. package/dist/workstation/acp/acpSession.js +0 -1475
  287. package/dist/workstation/acp/contract.js +0 -499
  288. package/dist/workstation/acp/delegationMcp.js +0 -24
  289. package/dist/workstation/acp/draftInterceptor.js +0 -163
  290. package/dist/workstation/acp/qoderStartup.js +0 -45
  291. package/dist/workstation/acp/sessionScope.js +0 -19
  292. package/dist/workstation/filePaths.js +0 -174
  293. package/dist/workstation/fileWorker.js +0 -81
  294. package/dist/workstation/fileWorkerClient.js +0 -121
  295. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -106
  296. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -262
  297. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  298. package/dist/workstation/qoderAdapter/contract.js +0 -78
  299. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -30
  300. package/dist/workstation/qoderAdapter/history.js +0 -87
  301. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  302. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  303. package/dist/workstation/qoderAdapter/main.js +0 -93
  304. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  305. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  306. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  307. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  308. package/dist/workstation/qoderAdapter/profile.js +0 -93
  309. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -49
  310. package/dist/workstation/qoderAdapter/session.js +0 -319
  311. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  312. package/dist/workstation/qoderAdapter/subwork.js +0 -77
  313. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  314. package/dist/workstation/qoderAdapter/taskEvent.js +0 -26
  315. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  316. package/dist/workstation/qoderAdapter/usage.js +0 -37
  317. package/dist/workstation/tools/core.js +0 -50
  318. package/dist/workstation/tools/files.js +0 -525
  319. package/dist/workstation/tools/terminal.js +0 -214
  320. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
@@ -1,163 +0,0 @@
1
- /**
2
- * Peels off the `session/update` notifications the pinned ACP SDK would destroy, before it sees them.
3
- *
4
- * ## Why this exists at all
5
- *
6
- * Two classes of notification never reach a handler we are allowed to register, and both were found
7
- * by running the thing rather than reading the types:
8
- *
9
- * 1. **Draft variants throw.** `ClientApp`'s constructor installs `client-session-update-router` as
10
- * the *first* static handler (`sdk/dist/acp.js:869-872`), and that router parses every
11
- * notification through `zSessionNotification` (`:680`), whose `update` is a closed 15-member
12
- * union. A `subagent_spawned` fails that parse. The dispatch loop's catch treats a failed
13
- * notification as `console.error` + return (`sdk/dist/jsonrpc.js:777-790`) — connection alive,
14
- * event gone.
15
- * 2. **A child session's ordinary frames are dropped.** The same router routes by
16
- * `notification.sessionId` into a map of sessions *this client opened*. A subagent's session id
17
- * was never opened here, so nothing is enqueued, the router answers `Handled.no`, and no later
18
- * handler exists to catch it.
19
- *
20
- * `builder.withHandler` only ever appends, so there is no handler position ahead of the router. The
21
- * stream is the only place left — and it is the right place anyway, because "which consumer do
22
- * these bytes belong to" is a transport question.
23
- *
24
- * ## What it must not become
25
- *
26
- * Pure carriage. It answers two predicates and either diverts or forwards; it never reads a payload
27
- * for meaning, and it never rewrites one. Every message it does not divert must reach the SDK
28
- * byte-for-byte — a re-encode that changed key order or dropped an unknown field would make this
29
- * layer a silent second normaliser, which is precisely what ruling L1 (normalise in one place,
30
- * server ingest) exists to prevent.
31
- *
32
- * ## Lifetime
33
- *
34
- * Predicate 1 dies with the draft: when RFD #1992 ships in the SDK, delete it along with
35
- * `draftSubagents.ts`. Predicate 2 outlives it — it can only go once the SDK has a concept of a
36
- * session it did not open.
37
- */
38
- import { DRAFT_SESSION_UPDATE_NAMES } from './draftSubagents.js';
39
- /** ACP's client-bound notification for session updates. */
40
- const SESSION_UPDATE_METHOD = 'session/update';
41
- const DRAFT_NAMES = new Set(DRAFT_SESSION_UPDATE_NAMES);
42
- /**
43
- * Whether this raw JSON-RPC message is a `session/update` that the SDK cannot deliver.
44
- *
45
- * Returns the diversion reason, or `null` to forward. Exported for its own tests: the two
46
- * predicates are the whole contract, and testing them through a stream would only obscure them.
47
- *
48
- * Deliberately total — any shape it does not recognise is forwarded. Being wrong in that direction
49
- * costs nothing (the SDK handles what it always handled); being wrong the other way silently
50
- * removes a message from the protocol.
51
- */
52
- export function diversionReasonFor(message, isOwnSession) {
53
- if (typeof message !== 'object' || message === null)
54
- return null;
55
- const record = message;
56
- if (record.method !== SESSION_UPDATE_METHOD)
57
- return null;
58
- // A request carries an `id` and is owed a response; this notification does not. Anything with an
59
- // id is left alone rather than answered by a layer that has no business answering.
60
- if ('id' in record)
61
- return null;
62
- const params = record.params;
63
- if (typeof params !== 'object' || params === null)
64
- return null;
65
- const { sessionId, update } = params;
66
- if (typeof sessionId !== 'string' || sessionId.length === 0)
67
- return null;
68
- if (typeof update !== 'object' || update === null)
69
- return null;
70
- const updateRecord = update;
71
- const variant = updateRecord.sessionUpdate;
72
- if (typeof variant === 'string' && DRAFT_NAMES.has(variant)) {
73
- return { sessionId, update: updateRecord, reason: 'draft-variant' };
74
- }
75
- if (!isOwnSession(sessionId)) {
76
- return { sessionId, update: updateRecord, reason: 'foreign-session' };
77
- }
78
- return null;
79
- }
80
- /**
81
- * Wraps the child's stdout so diverted notifications never reach the SDK, and everything else
82
- * arrives unchanged.
83
- *
84
- * Works on whole lines because that is the framing ACP uses over stdio and because it lets the
85
- * forwarded half stay **the original bytes**: a line that is not diverted is enqueued exactly as it
86
- * arrived, so no re-encoding can perturb it. Only a diverted line is ever parsed.
87
- *
88
- * A line that is not valid JSON is forwarded untouched — reporting a protocol error is the SDK's
89
- * job, and a transport shim that swallowed malformed input would hide the failure from the layer
90
- * equipped to describe it.
91
- */
92
- export function interceptDraftUpdates(source, options) {
93
- const decoder = new TextDecoder();
94
- const encoder = new TextEncoder();
95
- let buffered = '';
96
- const handleLine = (line, enqueue) => {
97
- if (line.trim().length === 0)
98
- return;
99
- let parsed;
100
- try {
101
- parsed = JSON.parse(line);
102
- }
103
- catch {
104
- enqueue(encoder.encode(`${line}\n`));
105
- return;
106
- }
107
- if (options.captureUpdate && typeof parsed === 'object' && parsed !== null) {
108
- const message = parsed;
109
- const params = message.params;
110
- if (message.method === SESSION_UPDATE_METHOD && !('id' in message) &&
111
- params && typeof params.sessionId === 'string' && params.sessionId.length > 0 &&
112
- typeof params.update === 'object' && params.update !== null &&
113
- options.captureUpdate(params))
114
- return;
115
- }
116
- const diverted = diversionReasonFor(parsed, options.isOwnSession);
117
- if (!diverted) {
118
- // The original text, not a re-serialisation of the parse — see the doc comment.
119
- enqueue(encoder.encode(`${line}\n`));
120
- return;
121
- }
122
- try {
123
- options.onDiverted(diverted);
124
- }
125
- catch (err) {
126
- // The sink is ours and is documented as non-throwing; if it throws anyway, losing one update
127
- // is strictly better than wedging the stream that carries every other one.
128
- console.error(`[acp] draft update sink threw for session ${diverted.sessionId}: ${String(err)}`);
129
- }
130
- };
131
- return new ReadableStream({
132
- async start(controller) {
133
- const reader = source.getReader();
134
- try {
135
- for (;;) {
136
- const { done, value } = await reader.read();
137
- if (done)
138
- break;
139
- buffered += decoder.decode(value, { stream: true });
140
- let newline = buffered.indexOf('\n');
141
- while (newline >= 0) {
142
- const line = buffered.slice(0, newline);
143
- buffered = buffered.slice(newline + 1);
144
- handleLine(line, (chunk) => controller.enqueue(chunk));
145
- newline = buffered.indexOf('\n');
146
- }
147
- }
148
- // A final line with no trailing newline is still a message: an agent that exits right after
149
- // writing one would otherwise have its last word dropped.
150
- buffered += decoder.decode();
151
- if (buffered.length > 0)
152
- handleLine(buffered, (chunk) => controller.enqueue(chunk));
153
- controller.close();
154
- }
155
- catch (err) {
156
- controller.error(err);
157
- }
158
- finally {
159
- reader.releaseLock();
160
- }
161
- },
162
- });
163
- }
@@ -1,45 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { buildSessionMcpServers } from './delegationMcp.js';
6
- export const QODER_MANAGED_VERSION = '1.1.61';
7
- /** Owned by one process; the bearer is never an argument or environment variable. */
8
- export async function prepareQoderStartup(launch, lease, cwd, env, agentEnv) {
9
- buildSessionMcpServers([], lease);
10
- if (launch.args.some((arg) => arg === '--' || /^(--mcp-config|--strict-mcp-config|--settings|--setting-sources)(=|$)/.test(arg))) {
11
- throw new Error('Managed Qoder startup has conflicting configuration arguments');
12
- }
13
- const version = await new Promise((resolve, reject) => {
14
- execFile(launch.command, [...launch.args, '--version'], {
15
- cwd, env: { ...process.env, ...launch.env, ...env, ...agentEnv },
16
- encoding: 'utf8', timeout: 10_000, maxBuffer: 64 * 1024, windowsHide: true,
17
- }, (error, stdout) => error ? reject(new Error('Managed Qoder version could not be verified')) : resolve(stdout.trim()));
18
- });
19
- if (version !== QODER_MANAGED_VERSION)
20
- throw new Error('Managed Qoder version is not certified');
21
- const dir = await mkdtemp(join(tmpdir(), 'axom-qoder-mcp-'));
22
- let disposal;
23
- const dispose = () => disposal ??= rm(dir, { recursive: true, force: true });
24
- try {
25
- await chmod(dir, 0o700);
26
- const config = join(dir, 'mcp.json');
27
- const authorization = Object.values(lease.headers)[0];
28
- await writeFile(config, JSON.stringify({ mcpServers: {
29
- 'axom-delegation': { type: 'http', url: lease.url, headers: { Authorization: authorization } },
30
- } }), { mode: 0o600, flag: 'wx' });
31
- // Validate again after the asynchronous version probe; an expired lease cannot start a process.
32
- buildSessionMcpServers([], lease);
33
- return {
34
- launch: { ...launch, args: [...launch.args, '--strict-mcp-config', '--mcp-config', config,
35
- '--setting-sources', '', '--settings', '{"autoMemoryEnabled":false}',
36
- '--disallowed-tools', 'Agent', '--cwd', cwd,
37
- '--append-system-prompt', 'Use the axom-delegation MCP tools for delegation. Native Agent execution is disabled in this session.'] },
38
- secrets: [authorization, authorization.slice('Bearer '.length)], dispose,
39
- };
40
- }
41
- catch (error) {
42
- await dispose();
43
- throw error;
44
- }
45
- }
@@ -1,19 +0,0 @@
1
- import { SessionMapperState } from './sessionUpdateMapper.js';
2
- export function scopeKey(scope) {
3
- return JSON.stringify([scope.rootRuntimeId, scope.nativeSessionId]);
4
- }
5
- export class SessionMapperScopes {
6
- create;
7
- states = new Map();
8
- constructor(create = () => new SessionMapperState()) {
9
- this.create = create;
10
- }
11
- get(scope) {
12
- const key = scopeKey(scope);
13
- let state = this.states.get(key);
14
- if (!state)
15
- this.states.set(key, state = this.create(scope));
16
- return state;
17
- }
18
- clear() { this.states.clear(); }
19
- }
@@ -1,174 +0,0 @@
1
- import { lstat, realpath } from 'node:fs/promises';
2
- import { isAbsolute, join } from 'node:path';
3
- export class ComputerFileError extends Error {
4
- code;
5
- constructor(code, message) {
6
- super(message);
7
- this.code = code;
8
- this.name = 'ComputerFileError';
9
- }
10
- }
11
- export function computerFileError(error) {
12
- if (error instanceof ComputerFileError)
13
- return error;
14
- const code = error?.code;
15
- if (code === 'ENOENT')
16
- return new ComputerFileError('not_found', 'File or parent directory not found');
17
- if (code === 'EACCES' || code === 'EPERM')
18
- return new ComputerFileError('permission_denied', 'File access denied');
19
- if (code === 'EEXIST')
20
- return new ComputerFileError('conflict', 'Target already exists');
21
- if (code === 'ELOOP')
22
- return new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
23
- return new ComputerFileError('io_error', 'File operation failed');
24
- }
25
- /** Validate portable slash components before touching the filesystem. */
26
- export function validateComputerFilePath(path) {
27
- if (!path || path.includes('\\') || path.includes(':') || path.includes('\0') || isAbsolute(path)) {
28
- throw new ComputerFileError('invalid_path', 'Path must be relative to the selected workspace');
29
- }
30
- const components = path.split('/');
31
- if (components.some(component => !component || component === '.' || component === '..')) {
32
- throw new ComputerFileError('invalid_path', 'Path contains an unsafe component');
33
- }
34
- return components;
35
- }
36
- /** The configured root may itself be a symlink; none of its descendants may be. */
37
- export async function resolveComputerFilePath(root, path, allowMissingLeaf) {
38
- const components = validateComputerFilePath(path);
39
- let cursor;
40
- try {
41
- cursor = await realpath(root);
42
- }
43
- catch (error) {
44
- throw computerFileError(error);
45
- }
46
- for (let index = 0; index < components.length; index++) {
47
- cursor = join(cursor, components[index]);
48
- let stat;
49
- try {
50
- stat = await lstat(cursor);
51
- }
52
- catch (error) {
53
- if (error.code === 'ENOENT' && allowMissingLeaf && index === components.length - 1)
54
- return cursor;
55
- throw computerFileError(error);
56
- }
57
- if (stat.isSymbolicLink())
58
- throw new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
59
- if (index === components.length - 1) {
60
- if (!stat.isFile())
61
- throw new ComputerFileError('not_regular_file', 'Target is not a regular file');
62
- }
63
- else if (!stat.isDirectory()) {
64
- throw new ComputerFileError('not_regular_file', 'Parent is not a directory');
65
- }
66
- }
67
- return cursor;
68
- }
69
- /** Resolve only a leaf relative to an already pinned process cwd; never turn it back into an absolute pathname. */
70
- export async function resolvePinnedLeafPath(_root, path, allowMissingLeaf) {
71
- const components = validateComputerFilePath(path);
72
- if (components.length !== 1)
73
- throw new ComputerFileError('invalid_path', 'Pinned file operation requires one path component');
74
- try {
75
- const entry = await lstat(path);
76
- if (entry.isSymbolicLink())
77
- throw new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
78
- if (!entry.isFile())
79
- throw new ComputerFileError('not_regular_file', 'Target is not a regular file');
80
- }
81
- catch (error) {
82
- if (error.code !== 'ENOENT' || !allowMissingLeaf)
83
- throw computerFileError(error);
84
- }
85
- return path;
86
- }
87
- /** Every component of a directory listing target must exist, be a directory, and not be a symlink; '' is the root. */
88
- export async function resolveComputerDirPath(root, path) {
89
- const components = path === '' ? [] : validateComputerFilePath(path);
90
- let cursor;
91
- try {
92
- cursor = await realpath(root);
93
- }
94
- catch (error) {
95
- throw computerFileError(error);
96
- }
97
- for (const component of components) {
98
- cursor = join(cursor, component);
99
- let entry;
100
- try {
101
- entry = await lstat(cursor);
102
- }
103
- catch (error) {
104
- throw computerFileError(error);
105
- }
106
- if (entry.isSymbolicLink())
107
- throw new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
108
- if (!entry.isDirectory())
109
- throw new ComputerFileError('not_regular_file', 'Target is not a directory');
110
- }
111
- return cursor;
112
- }
113
- /** Pinned variant for the file worker: '' lists the pinned cwd itself, otherwise exactly one component. */
114
- export async function resolvePinnedDirPath(_root, path) {
115
- const components = path === '' ? [] : validateComputerFilePath(path);
116
- if (components.length > 1)
117
- throw new ComputerFileError('invalid_path', 'Pinned directory operation requires at most one path component');
118
- const target = path === '' ? '.' : join('.', path);
119
- let entry;
120
- try {
121
- entry = await lstat(target);
122
- }
123
- catch (error) {
124
- throw computerFileError(error);
125
- }
126
- if (entry.isSymbolicLink())
127
- throw new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
128
- if (!entry.isDirectory())
129
- throw new ComputerFileError('not_regular_file', 'Target is not a directory');
130
- return target;
131
- }
132
- /**
133
- * Locate the deepest existing ancestor for a directory creation. Without parents the anchor must be the
134
- * target's own parent (only the leaf may be missing); with parents any prefix may be missing. Existing
135
- * components must be directories and never symlinks; an existing leaf counts as "already created".
136
- */
137
- export async function resolveComputerDirCreatePath(root, path, parents) {
138
- const components = validateComputerFilePath(path);
139
- let cursor;
140
- try {
141
- cursor = await realpath(root);
142
- }
143
- catch (error) {
144
- throw computerFileError(error);
145
- }
146
- let anchorIndex = 0;
147
- for (; anchorIndex < components.length; anchorIndex++) {
148
- const candidate = join(cursor, components[anchorIndex]);
149
- let entry;
150
- try {
151
- entry = await lstat(candidate);
152
- }
153
- catch (error) {
154
- if (error.code !== 'ENOENT')
155
- throw computerFileError(error);
156
- break;
157
- }
158
- if (entry.isSymbolicLink())
159
- throw new ComputerFileError('invalid_path', 'Symbolic links are not allowed');
160
- if (!entry.isDirectory())
161
- throw new ComputerFileError('not_found', 'Path is not a directory');
162
- cursor = candidate;
163
- }
164
- const missing = components.slice(anchorIndex);
165
- if (!parents && missing.length > 1) {
166
- throw new ComputerFileError('not_found', 'Parent directory not found');
167
- }
168
- return { anchor: cursor, missing };
169
- }
170
- /** Pinned variant for the file worker: create the missing components below the pinned cwd, one leaf at a time. */
171
- export async function resolvePinnedDirCreatePath(_root, path, _parents) {
172
- const components = path === '' ? [] : validateComputerFilePath(path);
173
- return { anchor: '.', missing: components };
174
- }
@@ -1,81 +0,0 @@
1
- import { realpath } from 'node:fs/promises';
2
- import { isAbsolute, relative, sep } from 'node:path';
3
- import { computerFileError, ComputerFileError, resolvePinnedDirCreatePath, resolvePinnedDirPath, resolvePinnedLeafPath, } from './filePaths.js';
4
- import { createComputerDir, listComputerDir, readComputerFile, writeComputerFile, } from './fileOps.js';
5
- const controller = new AbortController();
6
- let started = false;
7
- let pending;
8
- /**
9
- * This worker is a separate process so its cwd is a kernel-pinned directory reference, not a
10
- * pathname re-resolved after validation. A swapped ancestor symlink may affect process launch,
11
- * but the post-launch realpath check then rejects it before touching the leaf. Directory
12
- * operations accept at most one pinned component: multi-component `parents` creation walks
13
- * one leaf at a time from the pinned anchor instead of re-resolving full pathnames.
14
- */
15
- process.on('message', (message) => {
16
- if (message.kind === 'cancel') {
17
- controller.abort();
18
- if (pending) {
19
- pending = undefined;
20
- process.disconnect();
21
- }
22
- return;
23
- }
24
- if (message.kind === 'continue') {
25
- const work = pending;
26
- if (!work)
27
- return;
28
- pending = undefined;
29
- void (async () => {
30
- try {
31
- if (work.kind === 'read') {
32
- const request = { ...work.request, root: '.' };
33
- const data = await readComputerFile(request, controller.signal, resolvePinnedLeafPath);
34
- process.send?.({ ok: true, data }, () => process.disconnect());
35
- }
36
- else if (work.kind === 'write') {
37
- const request = { ...work.request, root: '.' };
38
- const data = await writeComputerFile(request, controller.signal, resolvePinnedLeafPath);
39
- process.send?.({ ok: true, data }, () => process.disconnect());
40
- }
41
- else if (work.kind === 'ls') {
42
- const request = { ...work.request, root: '.' };
43
- const data = await listComputerDir(request, controller.signal, resolvePinnedDirPath);
44
- process.send?.({ ok: true, data }, () => process.disconnect());
45
- }
46
- else {
47
- const request = { ...work.request, root: '.' };
48
- const data = await createComputerDir(request, controller.signal, resolvePinnedDirCreatePath);
49
- process.send?.({ ok: true, data }, () => process.disconnect());
50
- }
51
- }
52
- catch (error) {
53
- const mapped = computerFileError(error);
54
- process.send?.({ ok: false, error: { code: mapped.code, message: mapped.message } }, () => process.disconnect());
55
- }
56
- })();
57
- return;
58
- }
59
- if (started)
60
- return;
61
- started = true;
62
- void (async () => {
63
- try {
64
- const parent = await realpath('.');
65
- const rel = relative(message.root, parent);
66
- if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
67
- throw new ComputerFileError('invalid_path', 'File parent escaped the configured workspace');
68
- }
69
- if (controller.signal.aborted) {
70
- process.disconnect();
71
- return;
72
- }
73
- pending = message;
74
- process.send?.({ kind: 'ready' });
75
- }
76
- catch (error) {
77
- const mapped = computerFileError(error);
78
- process.send?.({ ok: false, error: { code: mapped.code, message: mapped.message } }, () => process.disconnect());
79
- }
80
- })();
81
- });
@@ -1,121 +0,0 @@
1
- import { fork } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
- import { realpath } from 'node:fs/promises';
4
- import { basename, dirname } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import { ComputerFileError, computerFileError, resolveComputerDirCreatePath, resolveComputerDirPath, resolveComputerFilePath } from './filePaths.js';
7
- const workerUrl = existsSync(new URL('./fileWorker.js', import.meta.url))
8
- ? new URL('./fileWorker.js', import.meta.url)
9
- : new URL('../../dist/workstation/fileWorker.js', import.meta.url);
10
- const writeLocks = new Map();
11
- async function serialWrite(target, action) {
12
- const previous = writeLocks.get(target) ?? Promise.resolve();
13
- let release;
14
- const gate = new Promise(resolve => { release = resolve; });
15
- const queued = previous.then(() => gate);
16
- writeLocks.set(target, queued);
17
- await previous;
18
- try {
19
- return await action();
20
- }
21
- finally {
22
- release();
23
- if (writeLocks.get(target) === queued)
24
- writeLocks.delete(target);
25
- }
26
- }
27
- async function execute(kind, request, anchor, leaf, signal, hooks) {
28
- if (signal?.aborted)
29
- throw new ComputerFileError('workstation_offline', 'File operation was cancelled');
30
- const root = await realpath(request.root).catch(error => { throw computerFileError(error); });
31
- await hooks?.afterValidation?.();
32
- return new Promise((resolve, reject) => {
33
- const child = fork(fileURLToPath(workerUrl), [], {
34
- cwd: anchor, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], execArgv: [],
35
- });
36
- let settled = false;
37
- let killTimer;
38
- const finish = (error, data) => {
39
- if (settled)
40
- return;
41
- settled = true;
42
- signal?.removeEventListener('abort', abort);
43
- if (error)
44
- reject(error);
45
- else
46
- resolve(data);
47
- };
48
- const abort = () => {
49
- if (settled)
50
- return;
51
- child.send({ kind: 'cancel' }, () => { });
52
- killTimer = setTimeout(() => child.kill(), 2_000);
53
- killTimer.unref();
54
- finish(new ComputerFileError(kind === 'write' || kind === 'mkdir' ? 'outcome_unknown' : 'workstation_offline', 'File operation was cancelled'));
55
- };
56
- child.on('message', (message) => {
57
- if (typeof message !== 'object' || message === null)
58
- return;
59
- const reply = message;
60
- if (reply.kind === 'ready') {
61
- void (async () => {
62
- try {
63
- await hooks?.afterWorkerReady?.();
64
- if (!settled)
65
- child.send({ kind: 'continue' }, error => { if (error)
66
- finish(computerFileError(error)); });
67
- }
68
- catch (error) {
69
- child.kill();
70
- finish(computerFileError(error));
71
- }
72
- })();
73
- return;
74
- }
75
- if (reply.ok === true)
76
- finish(undefined, reply.data);
77
- else if (reply.ok === false && reply.error)
78
- finish(new ComputerFileError(reply.error.code, reply.error.message));
79
- });
80
- child.once('error', error => finish(computerFileError(error)));
81
- child.once('exit', () => {
82
- if (killTimer)
83
- clearTimeout(killTimer);
84
- finish(new ComputerFileError(kind === 'write' || kind === 'mkdir' ? 'outcome_unknown' : 'workstation_offline', 'File worker exited without a result'));
85
- });
86
- signal?.addEventListener('abort', abort, { once: true });
87
- if (signal?.aborted) {
88
- abort();
89
- return;
90
- }
91
- child.send({ kind, root, request: { ...request, root: '.', path: leaf } }, error => {
92
- if (error)
93
- finish(computerFileError(error));
94
- });
95
- });
96
- }
97
- export async function readComputerFileSafely(request, signal, hooks) {
98
- const root = await realpath(request.root).catch(error => { throw computerFileError(error); });
99
- const target = await resolveComputerFilePath(root, request.path, false);
100
- return execute('read', request, dirname(target), basename(target), signal, hooks);
101
- }
102
- export async function writeComputerFileSafely(request, signal, hooks) {
103
- const root = await realpath(request.root).catch(error => { throw computerFileError(error); });
104
- const target = await resolveComputerFilePath(root, request.path, request.mode === 'create');
105
- return serialWrite(target, () => execute('write', request, dirname(target), basename(target), signal, hooks));
106
- }
107
- export async function listComputerDirSafely(request, signal, hooks) {
108
- const root = await realpath(request.root).catch(error => { throw computerFileError(error); });
109
- const target = await resolveComputerDirPath(root, request.path);
110
- // Listing the workspace root pins the worker at the root itself; '' is the zero-component leaf.
111
- const anchor = target === root ? root : dirname(target);
112
- const leaf = target === root ? '' : basename(target);
113
- const data = await execute('ls', request, anchor, leaf, signal, hooks);
114
- return { ...data, path: request.path };
115
- }
116
- export async function createComputerDirSafely(request, signal, hooks) {
117
- const root = await realpath(request.root).catch(error => { throw computerFileError(error); });
118
- const plan = await resolveComputerDirCreatePath(root, request.path, request.parents);
119
- const data = await execute('mkdir', request, plan.anchor, plan.missing.join('/'), signal, hooks);
120
- return { ...data, path: request.path };
121
- }