@platless/axom-cli 0.1.0 → 0.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/README.md +1 -1
  2. package/dist/authScopes.js +19 -0
  3. package/dist/axom-patch/cli/README.md +132 -0
  4. package/dist/axom-patch/cli/account-limits/README.md +27 -0
  5. package/dist/axom-patch/cli/account-limits/antigravity-proxy.mjs +47 -0
  6. package/dist/axom-patch/cli/account-limits/antigravity-read.mjs +90 -0
  7. package/dist/axom-patch/cli/account-limits/antigravity.mjs +46 -0
  8. package/dist/axom-patch/cli/account-limits/generate.py +120 -0
  9. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +64 -0
  10. package/dist/axom-patch/cli/account-limits/normalize.mjs +151 -0
  11. package/dist/axom-patch/cli/claude-account-limits.json +37 -0
  12. package/dist/axom-patch/cli/codex-account-limits.json +37 -0
  13. package/dist/axom-patch/cli/commandcode-account-limits.json +30 -0
  14. package/dist/axom-patch/cli/nightly-task.md +152 -0
  15. package/dist/axom-patch/cli/pi-steering.json +37 -0
  16. package/dist/axom-patch/cli/pi-subagent-subwork.json +23 -0
  17. package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +38 -0
  18. package/dist/axom-patch/cli/probes/pi-steering.mjs +80 -0
  19. package/dist/axom-patch/cli/probes/pi-subagent-subwork.mjs +211 -0
  20. package/dist/axom-patch/cli/probes/qoder-steering.mjs +89 -0
  21. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +61 -0
  22. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +257 -0
  23. package/dist/axom-patch/cli/qoder-background-terminal.md +82 -0
  24. package/dist/axom-patch/cli/qoder-foreground-output.md +42 -0
  25. package/dist/axom-patch/cli/qoder-steering.json +52 -0
  26. package/dist/axom-patch/cli/qoder-steering.md +30 -0
  27. package/dist/axom-patch/cli/qoder-subagent-origin.json +73 -0
  28. package/dist/axom-patch/cli/qoder-subagent-subwork.md +73 -0
  29. package/dist/axom-patch/cli/runtime.json +63 -0
  30. package/dist/axom-patch/cli/steering/claude-steering-js.patch +89 -0
  31. package/dist/axom-patch/cli/steering/claude-steering-types.patch +28 -0
  32. package/dist/axom-patch/cli/steering/codex-steering.patch +57 -0
  33. package/dist/axom-patch/cli/steering/pi-steering.js +79 -0
  34. package/dist/axom-patch/cli/zcode-account-limits.json +31 -0
  35. package/dist/axom-patch/cli/zcode-models.json +38 -0
  36. package/dist/axom-patch/cli/zcode-subagent-subwork.json +73 -0
  37. package/dist/cli.js +36 -1
  38. package/dist/commands/auth.js +4 -13
  39. package/dist/commands/execute.js +4 -4
  40. package/dist/commands/record.js +6 -6
  41. package/dist/commands/struct.js +4 -4
  42. package/dist/commands/workstation.js +2 -2
  43. package/dist/credentials.js +2 -2
  44. package/dist/install/claude/commands.js +13 -13
  45. package/dist/keepers/acp/keeper.mjs +3083 -0
  46. package/dist/keepers/acp/manifest.json +9 -0
  47. package/dist/keepers/terminal/keeper.mjs +8469 -0
  48. package/dist/keepers/terminal/manifest.json +9 -0
  49. package/dist/keepers/terminal/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  50. package/dist/keepers/terminal/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  51. package/dist/keepers/terminal/node_modules/node-pty/lib/index.js +52 -0
  52. package/dist/keepers/terminal/node_modules/node-pty/lib/interfaces.js +7 -0
  53. package/dist/keepers/terminal/node_modules/node-pty/lib/shared/conout.js +10 -0
  54. package/dist/keepers/terminal/node_modules/node-pty/lib/terminal.js +190 -0
  55. package/dist/keepers/terminal/node_modules/node-pty/lib/types.js +7 -0
  56. package/dist/keepers/terminal/node_modules/node-pty/lib/unixTerminal.js +351 -0
  57. package/dist/keepers/terminal/node_modules/node-pty/lib/utils.js +38 -0
  58. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
  59. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
  60. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsTerminal.js +209 -0
  61. package/dist/keepers/terminal/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  62. package/dist/keepers/terminal/node_modules/node-pty/package.json +66 -0
  63. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  64. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  65. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  66. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  67. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  68. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  69. package/dist/mcp.js +32 -14
  70. package/dist/runtime-deps/qoder-sdk/package-lock.json +1189 -0
  71. package/dist/runtime-deps/qoder-sdk/package.json +8 -0
  72. package/dist/test/keeperKit.js +67 -0
  73. package/dist/workstation/acp/acpAgents.js +2514 -681
  74. package/dist/workstation/acp/acpClient.js +95 -318
  75. package/dist/workstation/acp/activityBusy.js +43 -0
  76. package/dist/workstation/acp/activityReporting.js +54 -0
  77. package/dist/workstation/acp/adoption.js +28 -356
  78. package/dist/workstation/acp/authUrlSniffer.js +36 -0
  79. package/dist/workstation/acp/binaryInstall.js +100 -27
  80. package/dist/workstation/acp/builds/gc.js +245 -0
  81. package/dist/workstation/acp/builds/install/binary.js +28 -0
  82. package/dist/workstation/acp/builds/install/npm.js +235 -0
  83. package/dist/workstation/acp/builds/install/uv.js +254 -0
  84. package/dist/workstation/acp/builds/migrate.js +195 -0
  85. package/dist/workstation/acp/builds/model.js +128 -0
  86. package/dist/workstation/acp/builds/nodeGate.js +100 -0
  87. package/dist/workstation/acp/builds/store.js +352 -0
  88. package/dist/workstation/acp/commandTableProbe.js +68 -0
  89. package/dist/workstation/acp/configOptions.js +103 -1
  90. package/dist/workstation/acp/delegationMcp.js +24 -0
  91. package/dist/workstation/acp/envSecrets.js +27 -0
  92. package/dist/workstation/acp/initializeResponse.js +285 -0
  93. package/dist/workstation/acp/installErrors.js +12 -2
  94. package/dist/workstation/acp/installProbe.js +1 -1
  95. package/dist/workstation/acp/managedPatches.js +144 -0
  96. package/dist/workstation/acp/outbox.js +330 -0
  97. package/dist/workstation/acp/patchAssets.js +80 -0
  98. package/dist/workstation/acp/permission.js +335 -37
  99. package/dist/workstation/acp/processor/checkpoint.js +87 -0
  100. package/dist/workstation/acp/processor/commands.js +35 -0
  101. package/dist/workstation/acp/processor/control.js +355 -0
  102. package/dist/workstation/acp/processor/emit.js +26 -0
  103. package/dist/workstation/acp/processor/jsonrpc.js +172 -0
  104. package/dist/workstation/acp/processor/memoryLog.js +49 -0
  105. package/dist/workstation/acp/processor/origins.js +48 -0
  106. package/dist/workstation/acp/processor/output.js +67 -0
  107. package/dist/workstation/acp/processor/projection.js +42 -0
  108. package/dist/workstation/acp/processor/record.js +128 -0
  109. package/dist/workstation/acp/processor/runtime.js +181 -0
  110. package/dist/workstation/acp/processor/sdkSchemas.js +61 -0
  111. package/dist/workstation/acp/processor/session.js +525 -0
  112. package/dist/workstation/acp/processor/sideChannel.js +80 -0
  113. package/dist/workstation/acp/processor/state.js +74 -0
  114. package/dist/workstation/acp/processor/step.js +459 -0
  115. package/dist/workstation/acp/processor/test/fixtureLog.js +65 -0
  116. package/dist/workstation/acp/processor/test/harness.js +120 -0
  117. package/dist/workstation/acp/processor/test/largeIdEcho.js +50 -0
  118. package/dist/workstation/acp/processor/test/rawAgent.js +70 -0
  119. package/dist/workstation/acp/processor/transport/generations.js +76 -0
  120. package/dist/workstation/acp/processor/transport/keeper.js +130 -0
  121. package/dist/workstation/acp/processor/transport/local.js +269 -0
  122. package/dist/workstation/acp/processor/transport/types.js +78 -0
  123. package/dist/workstation/acp/processor/updates.js +364 -0
  124. package/dist/workstation/acp/processorSession.js +1512 -0
  125. package/dist/workstation/acp/qoderStartup.js +45 -0
  126. package/dist/workstation/acp/registry/catalog.js +385 -4
  127. package/dist/workstation/acp/registry/detect.js +55 -0
  128. package/dist/workstation/acp/registry/launch.js +61 -119
  129. package/dist/workstation/acp/sessionUpdateMapper.js +56 -27
  130. package/dist/workstation/acp/steering.js +58 -0
  131. package/dist/workstation/acp/switching.js +114 -0
  132. package/dist/workstation/acp/terminalLogin.js +171 -0
  133. package/dist/workstation/acp/terminalLoginSpec.js +41 -0
  134. package/dist/workstation/acp/test/adapterFixtures.js +256 -0
  135. package/dist/workstation/acp/test/daemonLife.js +106 -0
  136. package/dist/workstation/acp/test/fakeAgent.js +185 -9
  137. package/dist/workstation/acp/test/keeperWorld.js +267 -0
  138. package/dist/workstation/acp/test/memoryOutbox.js +55 -0
  139. package/dist/workstation/acp/test/raw/fixtures.js +196 -0
  140. package/dist/workstation/acp/test/raw/qoderCaptures.js +125 -0
  141. package/dist/workstation/acp/test/raw/sanitize.js +344 -0
  142. package/dist/workstation/acp/test/raw/sources.js +249 -0
  143. package/dist/workstation/acp/test/realAdapter.js +377 -0
  144. package/dist/workstation/acp/test/scriptAgent.js +206 -0
  145. package/dist/workstation/acp/test/sessionFrames.js +42 -0
  146. package/dist/workstation/acp/timedCommand.js +4 -2
  147. package/dist/workstation/acp/uvxInstall.js +72 -122
  148. package/dist/workstation/acp/validate.js +33 -5
  149. package/dist/workstation/agentFlow.js +103 -0
  150. package/dist/workstation/agentSessions.js +1686 -348
  151. package/dist/workstation/channels/agent.js +14 -6
  152. package/dist/workstation/channels/computer.js +203 -7
  153. package/dist/workstation/channels/computerExecs.js +200 -0
  154. package/dist/workstation/channels/management.js +86 -6
  155. package/dist/workstation/config.js +3 -1
  156. package/dist/workstation/connection/encryptedChannel.js +88 -21
  157. package/dist/workstation/daemon.js +309 -6
  158. package/dist/workstation/daemonctl.js +28 -2
  159. package/dist/workstation/desktop/binding.js +36 -0
  160. package/dist/workstation/desktop/bridge.js +212 -0
  161. package/dist/workstation/desktop/flow-control.js +52 -0
  162. package/dist/workstation/desktop/focused-text.js +130 -0
  163. package/dist/workstation/desktop/protocol.js +35 -0
  164. package/dist/workstation/exec/attach.js +126 -0
  165. package/dist/workstation/exec/command.js +40 -0
  166. package/dist/workstation/exec/durable.js +517 -0
  167. package/dist/workstation/exec/fallback.js +106 -0
  168. package/dist/workstation/exec/policy.js +837 -0
  169. package/dist/workstation/exec/shaping.js +211 -0
  170. package/dist/workstation/exec.js +3 -112
  171. package/dist/workstation/files/adapters/computerFileFrames.js +51 -0
  172. package/dist/workstation/files/adapters/toolRequests.js +121 -0
  173. package/dist/workstation/files/client.js +455 -0
  174. package/dist/workstation/files/errors.js +28 -0
  175. package/dist/workstation/files/fence.js +350 -0
  176. package/dist/workstation/files/find/fileIndex.js +153 -0
  177. package/dist/workstation/files/find/fuzzy.js +155 -0
  178. package/dist/workstation/files/host.js +122 -0
  179. package/dist/workstation/files/hostProtocol.js +2 -0
  180. package/dist/workstation/files/media/cache.js +620 -0
  181. package/dist/workstation/files/media/encoding.js +308 -0
  182. package/dist/workstation/files/media/estimator.js +100 -0
  183. package/dist/workstation/files/media/media.js +325 -0
  184. package/dist/workstation/files/media/packedAudio.js +23 -0
  185. package/dist/workstation/files/media/scheduler.js +551 -0
  186. package/dist/workstation/files/media/types.js +5 -0
  187. package/dist/workstation/files/scope.js +45 -0
  188. package/dist/workstation/files/structured.js +284 -0
  189. package/dist/workstation/files/text.js +356 -0
  190. package/dist/workstation/files/uploads.js +351 -0
  191. package/dist/workstation/journal.js +65 -236
  192. package/dist/workstation/keepers/acp/client.js +101 -0
  193. package/dist/workstation/keepers/acp/envelope.js +62 -0
  194. package/dist/workstation/keepers/acp/host.js +419 -0
  195. package/dist/workstation/keepers/acp/main.js +18 -0
  196. package/dist/workstation/keepers/acp/memoryStream.js +121 -0
  197. package/dist/workstation/keepers/acp/params.js +137 -0
  198. package/dist/workstation/keepers/acp/protocol.js +66 -0
  199. package/dist/workstation/keepers/acp/recover.js +120 -0
  200. package/dist/workstation/keepers/acp/session.js +584 -0
  201. package/dist/workstation/keepers/acp/stderr.js +154 -0
  202. package/dist/workstation/keepers/kit/activity.js +103 -0
  203. package/dist/workstation/keepers/kit/bootstrap.js +164 -0
  204. package/dist/workstation/keepers/kit/buildVersion.js +51 -0
  205. package/dist/workstation/keepers/kit/bundle.js +120 -0
  206. package/dist/workstation/keepers/kit/client.js +432 -0
  207. package/dist/workstation/keepers/kit/identity.js +21 -0
  208. package/dist/workstation/keepers/kit/ipc.js +112 -0
  209. package/dist/workstation/keepers/kit/launchConfig.js +8 -0
  210. package/dist/workstation/keepers/kit/launcher.js +249 -0
  211. package/dist/workstation/keepers/kit/lock.js +123 -0
  212. package/dist/workstation/keepers/kit/orphanSweep.js +86 -0
  213. package/dist/workstation/keepers/kit/paths.js +75 -0
  214. package/dist/workstation/keepers/kit/protocol.js +43 -0
  215. package/dist/workstation/keepers/kit/server.js +518 -0
  216. package/dist/workstation/keepers/kit/streamLog.js +697 -0
  217. package/dist/workstation/keepers/legacy.js +296 -0
  218. package/dist/workstation/keepers/policy.js +576 -0
  219. package/dist/workstation/keepers/stop.js +34 -0
  220. package/dist/workstation/keepers/terminal/client.js +213 -0
  221. package/dist/workstation/keepers/terminal/exec.js +981 -0
  222. package/dist/workstation/keepers/terminal/generations.js +129 -0
  223. package/dist/workstation/keepers/terminal/keeper.js +62 -0
  224. package/dist/workstation/keepers/terminal/main.js +43 -0
  225. package/dist/workstation/keepers/terminal/orphanSweep.js +3 -0
  226. package/dist/workstation/keepers/terminal/params.js +27 -0
  227. package/dist/workstation/keepers/terminal/protocol.js +56 -0
  228. package/dist/workstation/keepers/terminal/pty.js +330 -0
  229. package/dist/workstation/oauth/deviceAuthorization.js +21 -12
  230. package/dist/workstation/protocol/accountLimits.js +47 -0
  231. package/dist/workstation/protocol/agent.js +201 -8
  232. package/dist/workstation/protocol/common.js +36 -0
  233. package/dist/workstation/protocol/computer.js +107 -7
  234. package/dist/workstation/protocol/management.js +50 -2
  235. package/dist/workstation/qoderAdapter/backgroundProcess.js +118 -0
  236. package/dist/workstation/qoderAdapter/backgroundTerminal.js +193 -0
  237. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +372 -0
  238. package/dist/workstation/qoderAdapter/callbackContext.js +76 -0
  239. package/dist/workstation/qoderAdapter/completedContinuation.js +139 -0
  240. package/dist/workstation/qoderAdapter/contract.js +78 -0
  241. package/dist/workstation/qoderAdapter/foregroundOutput.js +36 -0
  242. package/dist/workstation/qoderAdapter/history.js +85 -0
  243. package/dist/workstation/qoderAdapter/identityIndex.js +191 -0
  244. package/dist/workstation/qoderAdapter/inputQueue.js +42 -0
  245. package/dist/workstation/qoderAdapter/main.js +95 -0
  246. package/dist/workstation/qoderAdapter/mapper.js +117 -0
  247. package/dist/workstation/qoderAdapter/observerPlugin.js +245 -0
  248. package/dist/workstation/qoderAdapter/observerReceiver.js +187 -0
  249. package/dist/workstation/qoderAdapter/permissions.js +61 -0
  250. package/dist/workstation/qoderAdapter/profile.js +93 -0
  251. package/dist/workstation/qoderAdapter/sdkDriver.js +48 -0
  252. package/dist/workstation/qoderAdapter/sdkRuntime.js +93 -0
  253. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +145 -0
  254. package/dist/workstation/qoderAdapter/session.js +319 -0
  255. package/dist/workstation/qoderAdapter/subagentMeta.js +28 -0
  256. package/dist/workstation/qoderAdapter/subwork.js +85 -0
  257. package/dist/workstation/qoderAdapter/taskControl.js +38 -0
  258. package/dist/workstation/qoderAdapter/taskLedger.js +163 -0
  259. package/dist/workstation/qoderAdapter/types.js +1 -0
  260. package/dist/workstation/qoderAdapter/usage.js +37 -0
  261. package/dist/workstation/register.js +4 -0
  262. package/dist/workstation/systemMetrics.js +110 -1
  263. package/dist/workstation/tools/browser.js +394 -0
  264. package/dist/workstation/tools/client.js +277 -0
  265. package/dist/workstation/tools/info.js +50 -0
  266. package/dist/workstation/tools/keeperProcess.js +124 -0
  267. package/dist/workstation/tools/router.js +55 -0
  268. package/dist/workstation/tools/terminalRouter.js +222 -0
  269. package/dist/workstation/tools/validation.js +86 -0
  270. package/dist/workstation/update/coordinator.js +32 -0
  271. package/dist/workstation/update/npmInstaller.js +2 -0
  272. package/dist/workstation/update/ptyPreflight.js +45 -0
  273. package/dist/workstation/update/taskRegistry.js +141 -23
  274. package/dist/workstation/update/watchdog.js +66 -0
  275. package/package.json +20 -7
  276. package/skills/axom/SKILL.md +55 -17
  277. package/skills/axom-definition/SKILL.md +32 -32
  278. package/skills/axom-platform/SKILL.md +39 -39
  279. package/dist/workstation/acp/acpSession.js +0 -1047
  280. package/dist/workstation/acp/contract.js +0 -494
  281. package/dist/workstation/acp/draftInterceptor.js +0 -154
@@ -0,0 +1,330 @@
1
+ /**
2
+ * ## Subagent relations bypass the window (fix round 1, spec §5.6)
3
+ *
4
+ * The server holds back its applied watermark — so its acks — at the first child frame whose subagent relation (`subagent_spawned`,
5
+ * the server's `TimelineEvent.SubagentSpawned`) it has not received yet (`noteApplied` capped at `nativePendingBefore - 1`,
6
+ * `ExternalAgentEventApplier.kt`). Every later frame then counts against the window; if the relation comes 256 or more frames after
7
+ * that first orphan child frame, a window that held it back would stall the session for good (the server waits 30 s, poisons, the
8
+ * reopen repeats it). So:
9
+ *
10
+ * - **producing**: while a child frame whose relation is not recorded yet is unacknowledged ({@link orphanOutstanding}), the record
11
+ * loop may run past the window, so the relation can be folded at all. Whatever the branch, this life never holds more than
12
+ * {@link RELATION_WAIT_EVENTS} / {@link RELATION_WAIT_BYTES} (1024 frames / 32 MiB) unacknowledged — pinned entries included — plus
13
+ * the frames of the one record being folded: that is the daemon's memory bound per session;
14
+ * - **sending**: once a relation that resolves such a child (a *resolving* relation) is recorded, it and every entry before it stop
15
+ * counting against either window ({@link pinnedThrough}): the sender takes them past the window, in seq order (sending the
16
+ * relation first would make the server drop the lower seqs as duplicates); the socket gate still applies. They must keep not
17
+ * counting after they are sent: the server applied them but wasted its acks while capped (each ack attempt resets its 32-event
18
+ * count), so its next ack needs up to 31 more applied frames — a window still full of them would deadlock there. They leave the
19
+ * outbox with that ack. A relation that resolves nothing recorded is not what the server waits for and keeps to the window
20
+ * (sending past it could overflow a parked server's 512-frame hold-off).
21
+ *
22
+ * ## Residual (documented, not fixed)
23
+ *
24
+ * While the producing window is full — a server parked on an approval with ≥ 256 root frames held, a connection that is down, an
25
+ * applier that never acknowledges — the record loop folds nothing: a steer or config RPC waits for the processor's reply and can
26
+ * time out on the server; an `agent_interrupt` note is folded (and the `session/cancel` written) only once the server unparks.
27
+ */
28
+ /** Unacknowledged events per session (spec §5.6, `W_EVT`). `ACK_EVERY_EVENTS(32) < W_EVT ≤ HOLD_OFF_CAPACITY(512)`. */
29
+ export const SESSION_WINDOW_EVENTS = 256;
30
+ /** Unacknowledged bytes per session (spec §5.6): a few huge frames cannot hog the window. */
31
+ export const SESSION_WINDOW_BYTES = 8 * 1024 * 1024;
32
+ /** How far production may run past the window while a recorded child frame still waits for its relation (bounds memory). */
33
+ export const RELATION_WAIT_EVENTS = 4 * SESSION_WINDOW_EVENTS;
34
+ export const RELATION_WAIT_BYTES = 4 * SESSION_WINDOW_BYTES;
35
+ /** A frame carrying a subagent relation (the server's `SubagentSpawned`): it bypasses the session window. */
36
+ export function isRelationFrame(frame) {
37
+ return frame.t === 'agent_event' && frame.eventName === 'subagent_spawned';
38
+ }
39
+ /**
40
+ * `src.i` of an outbox entry no record produced: a frame this daemon numbered itself (a degraded session's failed `turn_done`,
41
+ * `src.o` = the last record folded before it) or one imported from a legacy journal (`src.o` = 0). A replay never reproduces it
42
+ * (T21 skips it when comparing). Far above any per-record frame index.
43
+ */
44
+ export const DAEMON_FRAME_INDEX = 1 << 24;
45
+ /**
46
+ * Connection epoch of an entry that may have reached the server on an earlier connection (or in an earlier daemon life): it is not
47
+ * sent again until the server reopens the ref and says how far it got ({@link SessionOutbox.resendAll} after an `ack` of the open's
48
+ * `resumeFromSeq`) — resending a frame the server already applied could, for a `turn_done`, end a newer turn (analysis B §3.1).
49
+ */
50
+ export const SENT_EARLIER = -1;
51
+ const frameBytes = (frame) => Buffer.byteLength(JSON.stringify(frame));
52
+ export function isOutboxTail(value) {
53
+ if (typeof value !== 'object' || value === null)
54
+ return false;
55
+ const { seq, src } = value;
56
+ return Number.isSafeInteger(seq) && typeof src === 'object' && src !== null &&
57
+ Number.isSafeInteger(src.o) && Number.isSafeInteger(src.i);
58
+ }
59
+ export class SessionOutbox {
60
+ store;
61
+ items = [];
62
+ committed;
63
+ highestSeq = 0;
64
+ ownEvents = 0;
65
+ ownBytes = 0;
66
+ chain = Promise.resolve();
67
+ roomWaiters = new Set();
68
+ listeners = new Set();
69
+ stopped = false;
70
+ /** Children whose relation frame this outbox recorded. */
71
+ related = new Set();
72
+ /** Seq of the latest resolving relation: entries at or below it count against neither window (file comment). */
73
+ pinned = 0;
74
+ isRelated;
75
+ /**
76
+ * `isRelated`: whether the session already knows a child's relation from elsewhere (the processor's relation graph — a relation
77
+ * recorded by an earlier life, or replayed by a load). Without it only relations this outbox recorded count.
78
+ */
79
+ constructor(store, init = {}) {
80
+ this.store = store;
81
+ this.isRelated = init.isRelated ?? (() => false);
82
+ this.committed = isOutboxTail(init.committedTail) ? { seq: init.committedTail.seq, src: { ...init.committedTail.src } } : null;
83
+ this.highestSeq = this.committed?.seq ?? 0;
84
+ for (const record of init.leftovers ?? []) {
85
+ // Read back past the watermark only; a record at or below it is acknowledged (truncation is by whole segments).
86
+ if (this.committed && record.seq <= this.committed.seq)
87
+ continue;
88
+ this.items.push({ o: record.o, seq: record.seq, src: { ...record.src }, frame: record.frame, bytes: frameBytes(record.frame), leftover: true,
89
+ ...this.classify(record.seq, record.frame), sentOn: SENT_EARLIER });
90
+ this.highestSeq = Math.max(this.highestSeq, record.seq);
91
+ }
92
+ }
93
+ /**
94
+ * Resolving relation / orphan frame, in record order (a relation relates its child for every later frame). An orphan is a frame
95
+ * the server retains until a relation it has not received: a child frame (`acpSessionId`) whose child is unrelated, and — Task
96
+ * 22 — a `subagent_state_update` (any origin) naming an unrelated child (`payload.subagentSessionId`): the server's
97
+ * `receiveNativeEventLocked` holds it, and with it its acks, until that child's `subagent_spawned` exactly as it holds a child
98
+ * frame. The state update's own child is the key: a root-origin one would otherwise not be an orphan at all, its relation would
99
+ * keep to the window, and the session would stall where Task 20 fixed it for child frames.
100
+ */
101
+ classify(seq, frame) {
102
+ const unrelated = (child) => child !== undefined && !this.related.has(child) && !this.isRelated(child) ? child : undefined;
103
+ const origin = frame.t === 'agent_event' && typeof frame.acpSessionId === 'string' ? frame.acpSessionId : undefined;
104
+ const stateChild = frame.t === 'agent_event' && frame.eventName === 'subagent_state_update'
105
+ ? frame.payload?.subagentSessionId
106
+ : undefined;
107
+ const orphanOf = unrelated(typeof stateChild === 'string' ? stateChild : undefined) ?? unrelated(origin);
108
+ let resolving = false;
109
+ if (isRelationFrame(frame)) {
110
+ const child = frame.payload?.subagentSessionId;
111
+ if (typeof child === 'string') {
112
+ resolving = this.items.some((item) => item.orphanOf === child);
113
+ this.related.add(child);
114
+ if (resolving)
115
+ this.pinned = Math.max(this.pinned, seq);
116
+ }
117
+ }
118
+ return { resolving, ...(orphanOf !== undefined ? { orphanOf } : {}) };
119
+ }
120
+ /** Entries at or below this seq count against neither window (the latest resolving relation; 0: none). */
121
+ get pinnedThrough() { return this.pinned; }
122
+ /** An unacknowledged child frame whose relation is still not recorded: the server is (likely) holding its acks for it. */
123
+ orphanOutstanding() {
124
+ return this.items.some((item) => item.orphanOf !== undefined && !this.related.has(item.orphanOf) && !this.isRelated(item.orphanOf));
125
+ }
126
+ /** Highest seq this outbox ever held (entries, or the acknowledged watermark): a new life numbers after it (§5.5). */
127
+ get lastSeq() { return this.highestSeq; }
128
+ /** The acknowledged watermark (`committedTail`), `null` before the first truncating ack. */
129
+ get committedTail() { return this.committed; }
130
+ /** Unacknowledged entries, in seq order. */
131
+ get pending() { return this.items; }
132
+ /** Unacknowledged entries appended by this life (the producing window), past {@link pinnedThrough}. */
133
+ get backlog() {
134
+ if (this.pinned === 0)
135
+ return { events: this.ownEvents, bytes: this.ownBytes };
136
+ let events = 0;
137
+ let bytes = 0;
138
+ for (const item of this.items)
139
+ if (!item.leftover && item.seq > this.pinned) {
140
+ events += 1;
141
+ bytes += item.bytes;
142
+ }
143
+ return { events, bytes };
144
+ }
145
+ /**
146
+ * Entries sent on connection `epoch` and not acknowledged (the sending window). Entries at or below {@link pinnedThrough} are
147
+ * sent past the window either way (the sender bypasses it for them); once sent they count only when `pinnedCountsWhenSent` —
148
+ * a server that acknowledges as soon as the relation releases its cap (`AGENT_WIRE_RELATION_ACK_VERSION`, Task 22). An older
149
+ * server wastes its acks while capped, so its first ack after the relation can need up to 31 more applied frames: counting the
150
+ * pinned ones there would deadlock right after the relation (fix round 1 of Task 20).
151
+ */
152
+ inflight(epoch, pinnedCountsWhenSent = false) {
153
+ let events = 0;
154
+ let bytes = 0;
155
+ for (const item of this.items) {
156
+ if (item.sentOn === epoch && (pinnedCountsWhenSent || item.seq > this.pinned)) {
157
+ events += 1;
158
+ bytes += item.bytes;
159
+ }
160
+ }
161
+ return { events, bytes };
162
+ }
163
+ /** Called whenever there may be something new to send (an append, an ack opening the window, a resend). */
164
+ onChange(listener) {
165
+ this.listeners.add(listener);
166
+ return () => { this.listeners.delete(listener); };
167
+ }
168
+ changed() { for (const listener of [...this.listeners])
169
+ listener(); }
170
+ run(op) {
171
+ const result = this.chain.then(op);
172
+ this.chain = result.then(() => undefined, () => undefined);
173
+ return result;
174
+ }
175
+ /** Waits for every stream operation queued so far. */
176
+ flush() { return this.chain; }
177
+ /**
178
+ * Record frames before they are sent. Resolves once they are durable; rejects when the stream refused them (the caller degrades
179
+ * the session — a frame that could not be recorded is not sent either).
180
+ */
181
+ append(frames) {
182
+ if (frames.length === 0)
183
+ return this.chain;
184
+ return this.run(async () => {
185
+ const entries = frames.map((f) => ({ seq: f.seq, src: { o: f.src.o, i: f.src.i }, frame: { ...f.frame } }));
186
+ const { first } = await this.store.append(entries);
187
+ entries.forEach((entry, index) => {
188
+ const bytes = frameBytes(entry.frame);
189
+ this.items.push({ o: first + index, seq: entry.seq, src: entry.src, frame: entry.frame, bytes, leftover: false, ...this.classify(entry.seq, entry.frame), sentOn: null });
190
+ this.highestSeq = Math.max(this.highestSeq, entry.seq);
191
+ this.ownEvents += 1;
192
+ this.ownBytes += bytes;
193
+ });
194
+ this.wakeRoom(); // a resolving relation unpins the producing window
195
+ this.changed();
196
+ });
197
+ }
198
+ /**
199
+ * The server applied everything through `throughSeq` (`agent_event_ack`, or an open's `resumeFromSeq`): forget those entries and
200
+ * truncate the stream through the last of them, persisting `{seq, src}` of that entry as the watermark. Nothing held at or below
201
+ * it: nothing changes. A failed commit is reported (`onError`) — the entries are acknowledged either way; the stream keeps them
202
+ * until a later commit passes them.
203
+ */
204
+ ack(throughSeq, onError) {
205
+ return this.run(async () => {
206
+ let k = -1;
207
+ for (let i = 0; i < this.items.length && this.items[i].seq <= throughSeq; i++)
208
+ k = i;
209
+ if (k < 0)
210
+ return;
211
+ const last = this.items[k];
212
+ const removed = this.items.splice(0, k + 1);
213
+ for (const item of removed)
214
+ if (!item.leftover) {
215
+ this.ownEvents -= 1;
216
+ this.ownBytes -= item.bytes;
217
+ }
218
+ this.committed = { seq: last.seq, src: { ...last.src } };
219
+ this.wakeRoom();
220
+ this.changed();
221
+ try {
222
+ await this.store.commit({ outbox: { through: last.o, tail: { seq: last.seq, src: { ...last.src } } } });
223
+ }
224
+ catch (error) {
225
+ onError?.(error);
226
+ }
227
+ });
228
+ }
229
+ /**
230
+ * A processor checkpoint at raw `offset`, and raw truncation through it (the keeper refuses anything past it) unless `truncate` is
231
+ * false (a marker checkpoint, Task 21).
232
+ */
233
+ commitCheckpoint(offset, blob, truncate = true) {
234
+ return this.run(async () => {
235
+ await this.store.commit({ checkpoint: { offset, blob }, ...(truncate ? { rawThrough: offset } : {}) });
236
+ });
237
+ }
238
+ /**
239
+ * The session re-attached to keeper-acp after its connection was lost (Task 21): the stream is the truth again. An append whose
240
+ * reply was lost may have landed (it is in `leftovers` now) or not (it is gone, and the record loop folds its record again). The
241
+ * entries are replaced by what the stream holds past its watermark; an entry this object already had keeps whether (and on which
242
+ * connection) it was sent and whether an earlier life left it; one it never saw was never sent. Call it with no stream operation
243
+ * in flight ({@link flush} first). Listeners and room waiters are kept.
244
+ */
245
+ rebase(init) {
246
+ const previous = new Map(this.items.map((item) => [item.seq, item]));
247
+ this.items.length = 0;
248
+ this.related.clear();
249
+ this.pinned = 0;
250
+ this.ownEvents = 0;
251
+ this.ownBytes = 0;
252
+ if (isOutboxTail(init.committedTail) && (this.committed === null || init.committedTail.seq >= this.committed.seq)) {
253
+ this.committed = { seq: init.committedTail.seq, src: { ...init.committedTail.src } };
254
+ }
255
+ this.highestSeq = Math.max(this.highestSeq, this.committed?.seq ?? 0);
256
+ for (const record of init.leftovers) {
257
+ if (this.committed && record.seq <= this.committed.seq)
258
+ continue;
259
+ const before = previous.get(record.seq);
260
+ const leftover = before?.leftover ?? false;
261
+ const bytes = frameBytes(record.frame);
262
+ this.items.push({ o: record.o, seq: record.seq, src: { ...record.src }, frame: record.frame, bytes, leftover,
263
+ ...this.classify(record.seq, record.frame), sentOn: before ? before.sentOn : null });
264
+ if (!leftover) {
265
+ this.ownEvents += 1;
266
+ this.ownBytes += bytes;
267
+ }
268
+ this.highestSeq = Math.max(this.highestSeq, record.seq);
269
+ }
270
+ this.wakeRoom();
271
+ this.changed();
272
+ }
273
+ /** Mark every unacknowledged entry unsent: the sender resends them in order (a reopen's replay, after the ack of its watermark). */
274
+ resendAll() {
275
+ for (const item of this.items)
276
+ item.sentOn = null;
277
+ this.changed();
278
+ }
279
+ /**
280
+ * A new connection: everything recorded so far — sent on an earlier connection or produced while none was bound — is held until
281
+ * the server reopens the ref and says how far it got ({@link SENT_EARLIER}); what is recorded from now on is sent at once. (The
282
+ * journal's semantics: frames produced while unbound were replayed by the reopen, never sent on their own.)
283
+ */
284
+ connectionChanged() {
285
+ for (const item of this.items)
286
+ item.sentOn = SENT_EARLIER;
287
+ }
288
+ /** The first entry never sent (the sender's next candidate), in seq order. */
289
+ nextUnsent() {
290
+ return this.items.find((item) => item.sentOn === null);
291
+ }
292
+ /** Resolves when this life's unacknowledged backlog is below the window (immediately once {@link stopWaiting} ran). */
293
+ room() {
294
+ if (this.hasRoom())
295
+ return undefined;
296
+ return new Promise((resolve) => { this.roomWaiters.add(resolve); });
297
+ }
298
+ hasRoom() {
299
+ if (this.stopped)
300
+ return true;
301
+ // A hard bound on everything this life holds unacknowledged, pinned or not: chained resolving relations each reset `backlog`,
302
+ // but pinned entries leave memory only with an ack.
303
+ if (this.ownEvents >= RELATION_WAIT_EVENTS || this.ownBytes >= RELATION_WAIT_BYTES)
304
+ return false;
305
+ const { events, bytes } = this.backlog;
306
+ if (events < SESSION_WINDOW_EVENTS && bytes < SESSION_WINDOW_BYTES)
307
+ return true;
308
+ // Past the window only while a relation is awaited: the server cannot acknowledge before it, so it must be folded.
309
+ return this.orphanOutstanding();
310
+ }
311
+ wakeRoom() {
312
+ if (!this.hasRoom())
313
+ return;
314
+ for (const resolve of [...this.roomWaiters])
315
+ resolve();
316
+ this.roomWaiters.clear();
317
+ }
318
+ /** The session is closing or degraded: nobody waits for room any more. */
319
+ stopWaiting() {
320
+ this.stopped = true;
321
+ this.wakeRoom();
322
+ }
323
+ }
324
+ /** An outbox store over a transport session (`acp.outbox.append` / `acp.commit`). */
325
+ export function transportOutboxStore(transport, sessionKey) {
326
+ return {
327
+ append: (entries) => transport.appendOutbox(sessionKey, entries),
328
+ commit: (params) => transport.commit(sessionKey, params),
329
+ };
330
+ }
@@ -0,0 +1,80 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import semver from 'semver';
4
+ // Production reads only assets shipped in this CLI. The .ts path is for source-based tests.
5
+ export const patchAssetRoot = new URL(import.meta.url.endsWith('.ts') ? '../../../../axom-patch/cli/' : '../../axom-patch/cli/', import.meta.url);
6
+ const runtime = JSON.parse(readFileSync(new URL('runtime.json', patchAssetRoot), 'utf8'));
7
+ if (runtime.schema_version !== 1)
8
+ throw new Error('Unsupported bundled Axom patch schema');
9
+ for (const entry of runtime.overrides)
10
+ validateOverride(entry);
11
+ function validateCondition(id, when, until, whenKey, untilKey) {
12
+ if (when !== undefined && until !== undefined)
13
+ throw new Error(`Axom patch ${id}: ${whenKey} and ${untilKey} are exclusive`);
14
+ if (when !== undefined && (!Array.isArray(when) || when.length === 0 || when.some((version) => !semver.valid(version)))) {
15
+ throw new Error(`Axom patch ${id}: ${whenKey} must list exact versions`);
16
+ }
17
+ if (until !== undefined && !semver.valid(until))
18
+ throw new Error(`Axom patch ${id}: ${untilKey} is not a version`);
19
+ }
20
+ export function validateOverride(entry) {
21
+ validateCondition(entry.id, entry.when_registry_version, entry.until_registry_version, 'when_registry_version', 'until_registry_version');
22
+ const until = entry.until_registry_version;
23
+ // A catch-up pin may equal its bound: use it until the registry publishes that release.
24
+ // A pin above the bound would instead let the registry replace it with an older release.
25
+ if (until !== undefined && entry.distribution && !(semver.valid(entry.distribution.version) && semver.lte(entry.distribution.version, until))) {
26
+ throw new Error(`Axom patch ${entry.id}: pinned ${entry.distribution.version} must not sort above ${until}`);
27
+ }
28
+ }
29
+ export function validatePackagePatch(recipe) {
30
+ validateCondition(recipe.id, recipe.versions, recipe.until_version, 'versions', 'until_version');
31
+ if (recipe.versions === undefined && recipe.until_version === undefined) {
32
+ throw new Error(`Axom patch ${recipe.id}: needs versions or until_version`);
33
+ }
34
+ }
35
+ function inRange(version, when, until) {
36
+ if (when)
37
+ return semver.valid(version) !== null && when.some((listed) => semver.eq(listed, version));
38
+ if (until)
39
+ return !(semver.valid(version) && semver.gte(version, until));
40
+ return true;
41
+ }
42
+ export function overrideApplies(entry, registryVersion) {
43
+ return inRange(registryVersion, entry.when_registry_version, entry.until_registry_version);
44
+ }
45
+ /** Exact versions are a promise that the bytes match; a range only says the recipe may fit. */
46
+ export function packagePatchMatch(recipe, version) {
47
+ if (recipe.versions)
48
+ return recipe.versions.includes(version) ? 'exact' : undefined;
49
+ return inRange(version, undefined, recipe.until_version) ? 'range' : undefined;
50
+ }
51
+ /** The overrides that apply to `agent` at `registryVersion`, merged: the first distribution wins, env merges. */
52
+ export function patchOverridesFor(agent, registryVersion, overrides = runtime.overrides) {
53
+ const matching = overrides.filter((entry) => entry.agent === agent && overrideApplies(entry, registryVersion));
54
+ const distribution = matching.find((entry) => entry.distribution)?.distribution;
55
+ const env = Object.assign({}, ...matching.map((entry) => entry.env ?? {}));
56
+ return { ...(distribution && { distribution }), ...(Object.keys(env).length && { env }) };
57
+ }
58
+ export const packagePatches = runtime.package_patches.map((file) => {
59
+ if (!/^[a-z0-9-]+\.json$/.test(file))
60
+ throw new Error('Invalid bundled Axom patch filename');
61
+ const recipe = JSON.parse(readFileSync(fileURLToPath(new URL(file, patchAssetRoot)), 'utf8'));
62
+ validatePackagePatch(recipe);
63
+ return recipe;
64
+ });
65
+ /** The declared native wrappers, for the catalog↔patch consistency guard in `catalog.test.ts`. */
66
+ export const nativeExtensions = runtime.native_extensions ?? [];
67
+ /** The bundled overrides, for the same catalog↔patch consistency guard. */
68
+ export const runtimeOverrides = runtime.overrides;
69
+ /** Native wrappers are declared in the patch inventory, never selected by agent-specific launch branches. */
70
+ export function nativeExtensionFor(agent, version) {
71
+ const extension = runtime.native_extensions?.find(entry => entry.agent === agent && entry.versions.includes(version));
72
+ if (!extension)
73
+ return undefined;
74
+ if (extension.entry.startsWith('/') || extension.entry.split('/').includes('..'))
75
+ throw new Error('Invalid native extension path');
76
+ return new URL(extension.entry, patchAssetRoot);
77
+ }
78
+ export function nativeAccountLimitsVersionFor(agent, version) {
79
+ return runtime.native_extensions?.find(entry => entry.agent === agent && entry.versions.includes(version))?.accountLimitsVersion ?? 2;
80
+ }