@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
@@ -0,0 +1,121 @@
1
+ export class MemoryStream {
2
+ options;
3
+ records = [];
4
+ sizes = [];
5
+ bytes = 0;
6
+ firstOffset;
7
+ nextOffset;
8
+ committedOffset;
9
+ tail;
10
+ checkpoints = [];
11
+ checkpointSeq = 0;
12
+ closed = false;
13
+ notifyPending = false;
14
+ watchers = new Set();
15
+ constructor(options) {
16
+ this.options = options;
17
+ const base = options.baseOffset ?? 0;
18
+ if (!Number.isSafeInteger(base) || base < 0)
19
+ throw new RangeError(`Invalid baseOffset ${base}`);
20
+ this.firstOffset = base;
21
+ this.nextOffset = base;
22
+ this.committedOffset = base - 1;
23
+ }
24
+ append(input) {
25
+ this.assertOpen();
26
+ if (typeof input.k !== 'string' || input.k === '')
27
+ throw new TypeError('A stream record needs a non-empty kind `k`');
28
+ if ('o' in input || 't' in input)
29
+ throw new TypeError('`o` and `t` are stamped by the stream log');
30
+ const { k, ...rest } = input;
31
+ const record = { o: this.nextOffset, t: this.options.now(), k, ...rest };
32
+ const size = Buffer.byteLength(JSON.stringify(record)) + 1;
33
+ this.records.push(record);
34
+ this.sizes.push(size);
35
+ this.bytes += size;
36
+ this.nextOffset += 1;
37
+ this.scheduleNotify();
38
+ return record;
39
+ }
40
+ readFrom(from, limit, options = {}) {
41
+ this.assertOpen();
42
+ const first = this.firstOffset;
43
+ const truncated = from < first;
44
+ const out = [];
45
+ const maxBytes = options.maxBytes ?? Number.POSITIVE_INFINITY;
46
+ let used = 0;
47
+ for (let i = Math.max(from, first) - first; i < this.records.length && out.length < limit; i++) {
48
+ if (out.length > 0 && used + this.sizes[i] > maxBytes)
49
+ break; // as the kit log: never cross the budget, always the first record
50
+ out.push(this.records[i]);
51
+ used += this.sizes[i];
52
+ }
53
+ return { records: out, first, truncated };
54
+ }
55
+ read(from, limit, options = {}) { return this.readFrom(from, limit, options).records; }
56
+ /** As the kit log: marks `o <= through` consumed and forgets them; `tail` is kept as the watermark. */
57
+ commit(through, tail) {
58
+ this.assertOpen();
59
+ if (!Number.isSafeInteger(through) || through < -1)
60
+ throw new RangeError(`Invalid commit offset ${through}`);
61
+ if (through > this.nextOffset - 1)
62
+ throw new RangeError(`Commit offset ${through} is beyond the head ${this.nextOffset - 1}`);
63
+ if (tail !== undefined)
64
+ this.tail = tail;
65
+ if (through <= this.committedOffset)
66
+ return;
67
+ this.committedOffset = through;
68
+ const drop = Math.min(this.records.length, through + 1 - this.firstOffset);
69
+ if (drop > 0) {
70
+ for (let i = 0; i < drop; i++)
71
+ this.bytes -= this.sizes[i];
72
+ this.records = this.records.slice(drop);
73
+ this.sizes = this.sizes.slice(drop);
74
+ this.firstOffset += drop;
75
+ }
76
+ }
77
+ writeCheckpoint(offset, blob) {
78
+ this.assertOpen();
79
+ if (!Number.isSafeInteger(offset) || offset < -1)
80
+ throw new RangeError(`Invalid checkpoint offset ${offset}`);
81
+ const checkpoint = { seq: ++this.checkpointSeq, offset, t: this.options.now(), blob: typeof blob === 'string' ? Buffer.from(blob, 'utf8') : Buffer.from(blob) };
82
+ this.checkpoints = [...this.checkpoints, checkpoint].slice(-2);
83
+ return checkpoint;
84
+ }
85
+ latestCheckpoint() { return this.checkpoints[this.checkpoints.length - 1]; }
86
+ state() {
87
+ return {
88
+ head: this.nextOffset - 1, committed: this.committedOffset, committedTail: this.tail, overQuota: false,
89
+ checkpointOffset: this.latestCheckpoint()?.offset ?? -1, first: this.firstOffset, next: this.nextOffset, bytes: this.bytes, segments: 0,
90
+ };
91
+ }
92
+ watch(notify) { this.watchers.add(notify); return () => { this.watchers.delete(notify); }; }
93
+ asEventSource(options = {}) {
94
+ return {
95
+ read: (from, limit, perRead) => {
96
+ const caps = [options.maxBytes, perRead?.maxBytes].filter((cap) => cap !== undefined);
97
+ return this.readFrom(from, limit, { maxBytes: caps.length > 0 ? Math.min(...caps) : undefined }).records;
98
+ },
99
+ watch: (notify) => this.watch(notify),
100
+ };
101
+ }
102
+ close() { this.closed = true; this.watchers.clear(); }
103
+ scheduleNotify() {
104
+ if (this.notifyPending || this.watchers.size === 0)
105
+ return;
106
+ this.notifyPending = true;
107
+ queueMicrotask(() => {
108
+ this.notifyPending = false;
109
+ if (this.closed)
110
+ return;
111
+ for (const watcher of [...this.watchers]) {
112
+ try {
113
+ watcher();
114
+ }
115
+ catch { /* a watcher's failure is its own */ }
116
+ }
117
+ });
118
+ }
119
+ assertOpen() { if (this.closed)
120
+ throw new Error('Stream is closed'); }
121
+ }
@@ -0,0 +1,137 @@
1
+ // Parameter checks for `acp.*` (the daemon decides everything; the keeper only refuses malformed input).
2
+ // Keeper-side module: node builtins and kit siblings only (the import guard enforces it).
3
+ import { statSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { KeeperMethodError } from '../kit/protocol.js';
6
+ import { ACP_ANNOTATION_BYTES, ACP_ENV_BYTES, ACP_LABEL_BYTES, ACP_SESSION_KEY_CHARS, AcpErrorCode, } from './protocol.js';
7
+ const MAX_PATH = 4096;
8
+ const MAX_ARGS = 4096;
9
+ const MAX_ENV_ENTRIES = 4096;
10
+ const MAX_LIST = 256;
11
+ export const bad = (message) => { throw new KeeperMethodError(AcpErrorCode.badRequest, message); };
12
+ export const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ const jsonBytes = (value) => { try {
14
+ return Buffer.byteLength(JSON.stringify(value) ?? '');
15
+ }
16
+ catch {
17
+ return Number.POSITIVE_INFINITY;
18
+ } };
19
+ /** Only the listed keys: a typo in the daemon's parameters must fail loudly, not be ignored. */
20
+ export function onlyKeys(params, allowed) {
21
+ for (const key of Object.keys(params))
22
+ if (!allowed.includes(key))
23
+ bad(`Unknown parameter ${key}`);
24
+ }
25
+ export function sessionKeyOf(value) {
26
+ if (typeof value !== 'string' || !value || value.length > ACP_SESSION_KEY_CHARS || value.includes('\0'))
27
+ bad('Invalid sessionKey');
28
+ return value;
29
+ }
30
+ export function offsetOf(value, name, min = 0) {
31
+ if (!Number.isSafeInteger(value) || value < min)
32
+ bad(`Invalid ${name}`);
33
+ return value;
34
+ }
35
+ export function causeOf(value, name = 'cause') {
36
+ if (!isRecord(value) || !Number.isSafeInteger(value.o) || value.o < 0 || !Number.isSafeInteger(value.i) || value.i < 0)
37
+ bad(`Invalid ${name}`);
38
+ return { o: value.o, i: value.i };
39
+ }
40
+ function absolutePath(value, name) {
41
+ if (typeof value !== 'string' || !value || value.length > MAX_PATH || value.includes('\0') || !path.isAbsolute(value))
42
+ bad(`Invalid ${name}`);
43
+ return path.normalize(value);
44
+ }
45
+ function stringList(value, name, max) {
46
+ if (value === undefined)
47
+ return [];
48
+ if (!Array.isArray(value) || value.length > max || value.some((v) => typeof v !== 'string' || v.includes('\0')))
49
+ bad(`Invalid ${name}`);
50
+ return [...value];
51
+ }
52
+ /** The environment exactly as the daemon sent it: string values only, no `=` or NUL in names, no NUL in values, bounded. */
53
+ function environment(value) {
54
+ if (!isRecord(value))
55
+ return bad('Invalid env');
56
+ const entries = Object.entries(value);
57
+ if (entries.length > MAX_ENV_ENTRIES || jsonBytes(value) > ACP_ENV_BYTES)
58
+ bad('Invalid env');
59
+ const env = {};
60
+ for (const [name, entry] of entries) {
61
+ if (!name || name.includes('=') || name.includes('\0') || typeof entry !== 'string' || entry.includes('\0'))
62
+ bad('Invalid env');
63
+ env[name] = entry;
64
+ }
65
+ return env;
66
+ }
67
+ /**
68
+ * A path the keeper may delete when the process exits: absolute, normalised, at least two levels below the root, never a parent of
69
+ * the keeper's own data. Only paths handed over at spawn are ever deleted.
70
+ */
71
+ export function removablePath(value, protectedDirs) {
72
+ const target = absolutePath(value, 'removeOnExit');
73
+ if (target.split(path.sep).filter(Boolean).length < 2)
74
+ bad('removeOnExit path is too shallow');
75
+ for (const dir of protectedDirs) {
76
+ const rel = path.relative(target, dir);
77
+ if (rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel)))
78
+ bad('removeOnExit may not contain keeper data');
79
+ }
80
+ return target;
81
+ }
82
+ export function spawnSpecOf(value, protectedDirs) {
83
+ if (!isRecord(value))
84
+ return bad('Invalid spec');
85
+ onlyKeys(value, ['command', 'args', 'env', 'cwd', 'stderrSecrets', 'removeOnExit']);
86
+ const { command } = value;
87
+ if (typeof command !== 'string' || !command || command.length > MAX_PATH || command.includes('\0'))
88
+ bad('Invalid command');
89
+ const args = stringList(value.args, 'args', MAX_ARGS);
90
+ const cwd = absolutePath(value.cwd, 'cwd');
91
+ let isDirectory = false;
92
+ try {
93
+ isDirectory = statSync(cwd).isDirectory();
94
+ }
95
+ catch { /* missing */ }
96
+ if (!isDirectory)
97
+ bad('Adapter working directory is not a directory');
98
+ const stderrSecrets = stringList(value.stderrSecrets, 'stderrSecrets', MAX_LIST);
99
+ const removeOnExit = stringList(value.removeOnExit, 'removeOnExit', MAX_LIST).map((p) => removablePath(p, protectedDirs));
100
+ return { command: command, args, env: environment(value.env), cwd, stderrSecrets, removeOnExit };
101
+ }
102
+ export function labelOf(value) {
103
+ if (!isRecord(value) || jsonBytes(value) > ACP_LABEL_BYTES)
104
+ return bad('Invalid label');
105
+ return JSON.parse(JSON.stringify(value));
106
+ }
107
+ export function annotationOf(value) {
108
+ if (!isRecord(value) || jsonBytes(value) > ACP_ANNOTATION_BYTES)
109
+ return bad('Invalid annotation');
110
+ causeOf(value.cause, 'annotation.cause');
111
+ return value;
112
+ }
113
+ export function redactionsOf(value) {
114
+ if (value === undefined)
115
+ return [];
116
+ if (!Array.isArray(value) || value.length > MAX_LIST)
117
+ return bad('Invalid redact');
118
+ return value.map((entry) => {
119
+ if (!isRecord(entry) || typeof entry.ref !== 'string' || !entry.ref || typeof entry.value !== 'string')
120
+ return bad('Invalid redact');
121
+ return { ref: entry.ref, value: entry.value };
122
+ });
123
+ }
124
+ export function noteOf(value) {
125
+ if (!isRecord(value) || typeof value.type !== 'string' || !value.type)
126
+ return bad('Invalid note');
127
+ return value;
128
+ }
129
+ export function outboxEntriesOf(value) {
130
+ if (!Array.isArray(value) || value.length === 0)
131
+ return bad('Invalid entries');
132
+ return value.map((entry) => {
133
+ if (!isRecord(entry) || !Number.isSafeInteger(entry.seq) || !isRecord(entry.frame))
134
+ return bad('Invalid outbox entry');
135
+ return { seq: entry.seq, src: causeOf(entry.src, 'outbox src'), frame: entry.frame };
136
+ });
137
+ }
@@ -0,0 +1,66 @@
1
+ // keeper-acp's wire vocabulary, shared by the keeper (`main.ts`, `host.ts`) and the daemon (`client.ts`, the processor transports).
2
+ // Keeper-side module: node builtins and kit siblings only (the import guard enforces it). Spec §5.3, §3.3, §13, §14.
3
+ //
4
+ // keeper-acp is ACP-agnostic: it hosts adapter processes, keeps one ordered raw stream and one outbox stream per session and
5
+ // never interprets JSON-RPC beyond sniffing `id`/`method` for diagnostics. Everything that has meaning lives in the daemon.
6
+ /** The keeper protocol this code speaks. `main.ts` declares the same number for the bundler (a test keeps them equal). */
7
+ export const ACP_PROTOCOL_VERSION = 1;
8
+ /** `keeper.hello` capability: `acp.*` is served. */
9
+ export const ACP_CAPABILITY = 'acp';
10
+ export const ACP_METHODS = [
11
+ 'acp.spawn', 'acp.list', 'acp.attach', 'acp.write', 'acp.note', 'acp.read', 'acp.downs',
12
+ 'acp.outbox.append', 'acp.commit', 'acp.terminate', 'acp.release',
13
+ ];
14
+ export const acpStream = (stream, sessionKey) => `${stream}/${sessionKey}`;
15
+ export function parseAcpStream(name) {
16
+ for (const stream of ['raw', 'outbox'])
17
+ if (name.startsWith(`${stream}/`) && name.length > stream.length + 1)
18
+ return { stream, sessionKey: name.slice(stream.length + 1) };
19
+ return undefined;
20
+ }
21
+ // ---- constants (spec §14 and this task) --------------------------------------------------------------------------------
22
+ /** SIGTERM to the adapter's process group, then SIGKILL after this long, then pipes are closed after this long again. */
23
+ export const ACP_TERMINATE_GRACE_MS = 2000;
24
+ export const ACP_KILL_GRACE_MS = 2000;
25
+ /** After the adapter exits by itself, its stdio is drained for at most this long before the pipes are closed and `exit` is written. */
26
+ export const ACP_EXIT_DRAIN_MS = 2000;
27
+ /** The redacted stderr tail carried by `proc exit` (today's `AgentSpawnError` text is built from it). */
28
+ export const ACP_STDERR_TAIL_BYTES = 8 * 1024;
29
+ /** A stderr line longer than this is never written anywhere (only a placeholder): a cut line could split a secret. */
30
+ export const ACP_STDERR_LINE_BYTES = 16 * 1024;
31
+ /** Per-session stderr file: rotated to `stderr.log.1` at this size, so at most twice this is kept. */
32
+ export const ACP_STDERR_FILE_BYTES = 1024 * 1024;
33
+ /**
34
+ * Largest serialised record (one stream line). Keeps every record readable through one 32 MiB frame (spec §3.3: an image prompt
35
+ * line is about 10.7 MB). A larger `down` is refused; a larger stdout line is dropped and a `gap` record says so.
36
+ */
37
+ export const ACP_MAX_RECORD_BYTES = 30 * 1024 * 1024;
38
+ /** Byte budget of one `acp.read` answer or one pushed batch (at least one record is always returned). */
39
+ export const ACP_READ_MAX_BYTES = 16 * 1024 * 1024;
40
+ export const ACP_READ_MAX_RECORDS = 4096;
41
+ /** The hard limit of spec §3.4: below this much free disk the keeper stops reading adapter stdout until space returns. */
42
+ export const ACP_MIN_FREE_DISK_BYTES = 1024 * 1024 * 1024;
43
+ export const ACP_DISK_CHECK_MS = 5000;
44
+ /** Sessions one keeper holds (live and exited, until released). */
45
+ export const ACP_SESSION_LIMIT = 256;
46
+ export const ACP_SESSION_KEY_CHARS = 256;
47
+ export const ACP_LABEL_BYTES = 4096;
48
+ export const ACP_ANNOTATION_BYTES = 64 * 1024;
49
+ export const ACP_ENV_BYTES = 1024 * 1024;
50
+ /** Unpaired requests the envelope sniffer remembers per direction (diagnostics only; the oldest are forgotten). */
51
+ export const ACP_ENVELOPE_PENDING = 256;
52
+ /** Lines longer than this are not parsed by the envelope sniffer (counted instead): sniffing must stay cheap. */
53
+ export const ACP_ENVELOPE_SNIFF_BYTES = 256 * 1024;
54
+ export const AcpErrorCode = {
55
+ badRequest: 'bad_request',
56
+ notFound: 'acp_session_not_found',
57
+ /** A process is starting or running in this session (spawn, release). */
58
+ busy: 'acp_session_busy',
59
+ limit: 'acp_limit',
60
+ /** The command could not be started (ENOENT, EACCES, …): nothing was appended to the session. */
61
+ spawnFailed: 'acp_spawn_failed',
62
+ /** The session belongs to another live keeper-acp generation. */
63
+ foreign: 'acp_session_foreign',
64
+ /** The session's directory has a session.json this keeper cannot read (corrupt, another schema): left alone, never reused. */
65
+ unreadable: 'acp_session_unreadable',
66
+ };
@@ -0,0 +1,120 @@
1
+ // keeper-acp start-up recovery (spec §5.3 "keeper-acp 自身重启"): every session directory whose owning keeper process is gone is
2
+ // adopted. A session whose adapter was running when its keeper died is marked exited with `proc exit{reason:'keeper_lost'}`, and the
3
+ // adapter's process group is killed — only after verifying the leader by pid + start time (`kit/orphanSweep.ts`, the same
4
+ // PID-reuse-safe rule keeper-terminal applies to execs); a group that cannot be verified is never signalled. Its `removeOnExit`
5
+ // paths are removed after the kill grace. The daemon then folds the tail (it may hold a real `turn_done`) and reopens the session
6
+ // through `session/load`. Keeper-side module: node builtins and kit siblings only (the import guard enforces it).
7
+ import { closeSync, openSync, readFileSync, readSync, readdirSync, fstatSync } from 'node:fs';
8
+ import path from 'node:path';
9
+ import { settleOrphan } from '../kit/orphanSweep.js';
10
+ import { ACP_KILL_GRACE_MS, ACP_STDERR_TAIL_BYTES } from './protocol.js';
11
+ export const SESSION_FILE = 'session.json';
12
+ export const STDERR_FILE = 'stderr.log';
13
+ export function validMeta(value) {
14
+ if (typeof value !== 'object' || value === null)
15
+ return false;
16
+ const m = value;
17
+ const spawn = m.spawn;
18
+ return m.v === 1 && typeof m.sessionKey === 'string' && typeof m.label === 'object' && m.label !== null && Number.isFinite(m.createdAtMs)
19
+ && typeof m.exited === 'boolean' && Array.isArray(m.removeOnExit) && m.removeOnExit.every((p) => typeof p === 'string')
20
+ && (spawn === null || (typeof spawn === 'object' && typeof spawn.spawnId === 'string' && Number.isSafeInteger(spawn.pid) && Number.isSafeInteger(spawn.pgid)
21
+ && (spawn.startTime === null || typeof spawn.startTime === 'string')));
22
+ }
23
+ /** Session directories under `sessionsDir` with a readable `session.json` (others are being created, or not sessions: left alone). */
24
+ /**
25
+ * A session directory's metadata: `absent` only when `session.json` does not exist (a start killed between mkdir and the metadata
26
+ * rename); `unreadable` for anything else (corrupt, another schema version, unreadable) — such a directory is never deleted.
27
+ */
28
+ export function readSessionMeta(dir) {
29
+ let text;
30
+ try {
31
+ text = readFileSync(path.join(dir, SESSION_FILE), 'utf8');
32
+ }
33
+ catch (error) {
34
+ return error.code === 'ENOENT' ? 'absent' : 'unreadable';
35
+ }
36
+ try {
37
+ const value = JSON.parse(text);
38
+ return validMeta(value) ? value : 'unreadable';
39
+ }
40
+ catch {
41
+ return 'unreadable';
42
+ }
43
+ }
44
+ export function listSessionDirs(sessionsDir, report) {
45
+ let names;
46
+ try {
47
+ names = readdirSync(sessionsDir);
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ const found = [];
53
+ for (const name of names.sort()) {
54
+ const dir = path.join(sessionsDir, name);
55
+ let value;
56
+ try {
57
+ value = JSON.parse(readFileSync(path.join(dir, SESSION_FILE), 'utf8'));
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ if (!validMeta(value)) {
63
+ report(`${name}: unreadable session metadata, left alone`);
64
+ continue;
65
+ }
66
+ found.push({ dir, meta: value });
67
+ }
68
+ return found;
69
+ }
70
+ /** The redacted stderr tail the session's file kept (the file only ever holds redacted lines). */
71
+ export function stderrTailOf(dir) {
72
+ let fd;
73
+ try {
74
+ fd = openSync(path.join(dir, STDERR_FILE), 'r');
75
+ const size = fstatSync(fd).size;
76
+ const length = Math.min(size, ACP_STDERR_TAIL_BYTES);
77
+ const buffer = Buffer.alloc(length);
78
+ readSync(fd, buffer, 0, length, size - length);
79
+ let text = buffer.toString('utf8');
80
+ if (length < size)
81
+ text = text.replace(/^[^\n]*\n?/, ''); // never start mid-line (or mid-character)
82
+ return text;
83
+ }
84
+ catch {
85
+ return '';
86
+ }
87
+ finally {
88
+ if (fd !== undefined)
89
+ try {
90
+ closeSync(fd);
91
+ }
92
+ catch { /* ignore */ }
93
+ }
94
+ }
95
+ /**
96
+ * The adapter a lost keeper ran: verified by pid + start time, then SIGTERM to its group and SIGKILL after the grace
97
+ * (`settleOrphan`). `afterGrace` runs once the SIGKILL is due (or at once when nothing was signalled) — removeOnExit goes there,
98
+ * so a bearer file outlives no process that could still read it.
99
+ */
100
+ export async function settleLostAdapter(meta, afterGrace, deps = {}) {
101
+ const spawn = meta.spawn;
102
+ if (!spawn) {
103
+ afterGrace();
104
+ return 'gone';
105
+ }
106
+ const later = deps.later ?? ((ms, run) => { setTimeout(run, ms).unref(); });
107
+ let killDue;
108
+ const outcome = await settleOrphan({ pid: spawn.pid, pgid: spawn.pgid, startTime: spawn.startTime }, 'kill', ACP_KILL_GRACE_MS, {
109
+ ...deps,
110
+ later: (ms, run) => { killDue = run; later(ms, () => { try {
111
+ run();
112
+ }
113
+ finally {
114
+ afterGrace();
115
+ } }); },
116
+ });
117
+ if (!killDue)
118
+ afterGrace();
119
+ return outcome;
120
+ }