@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,364 @@
1
+ /**
2
+ * `session/update` → journaled `agent_event`s, one record at a time, in log order (Task 17b).
3
+ *
4
+ * ## One path, in log order (replaces `draftInterceptor` + the SDK's async chain)
5
+ *
6
+ * Today a draft variant or a child session's update is diverted by `interceptDraftUpdates` and
7
+ * applied **synchronously** while the stream is read, and every other update travels the SDK's
8
+ * **asynchronous** handler chain — so the two orders depend on pipe chunking (analysis B §0.1: the
9
+ * same bytes, written at once or line by line, gave different event orders). Here both kinds are
10
+ * handled on their own record, so the order of events is the order of the log, whatever the
11
+ * chunking. What differs between them is only what `AcpSession` does with each, kept as it is:
12
+ *
13
+ * - **The root session's own ordinary update** is parsed with the SDK's `zSessionNotification` (as the
14
+ * SDK's router does — so unknown keys are stripped and an unknown variant is dropped), mapped with
15
+ * the root scope, and routed like `AcpSession.routeMappedEvent`: background-writer evidence and
16
+ * message-boundary tracking for `agent_message` synthesis.
17
+ * - **A draft variant (any session) or any update on another session** is mapped raw, with that
18
+ * session's own scope, like `AcpSession.applyDivertedUpdate` → `emitEvent`: a child's events carry
19
+ * `acpSessionId`, and none of them touch the root's message boundaries.
20
+ *
21
+ * Both feed the permission-origin graph (`subagent_spawned`) and the unfinished async-task set.
22
+ *
23
+ * ## Binding, and what replaced the pre-bind buffer
24
+ *
25
+ * The root is bound by the open **response** (`control.ts`). An update that arrives before any root
26
+ * is bound — outside a load's history window — has no session to belong to and is dropped
27
+ * (`droppedUpdates`). Today's 32-entry, 5-second pre-bind buffer existed because the SDK processed
28
+ * the `session/new` response *after* an update the agent had written *after* it; in log order that
29
+ * update follows the response and is applied normally (analysis B A3/C1; the raw captures in `tmp/`
30
+ * show Claude's first `available_commands_update` after the response line).
31
+ *
32
+ * ## Load history
33
+ *
34
+ * Between a `session/load` request and the `historyEnd` note, every update (any session) is history — except a level update
35
+ * ({@link LEVEL_UPDATES}) the loaded session sends once its response is in, which is folded live (Task 21):
36
+ * folded with its own scopes and message boundaries exactly as `adoption.ts` `foldScopedReplayEvents`
37
+ * folds a finished list — incrementally here — then validated and emitted **unnumbered** as
38
+ * `processor.history_event` frames for the daemon's backfill, and observed for origins and
39
+ * background writers (`AcpSession.observeReplayEvent`).
40
+ *
41
+ * ## `agent_message` synthesis and out-of-turn chunks
42
+ *
43
+ * `AcpSession`'s rule: an `agent_message` closes the streaming assistant message when its `messageId`
44
+ * changes and when a turn ends (before the turn's `agent_turn_done`). One addition, decided for 17b:
45
+ * chunks that arrive with **no turn running** — after a prompt's response, or an agent working on its
46
+ * own — are emitted as they are (an `agent_event` never carries a turn id; that is what today's path
47
+ * does with them too), and the message they open is closed **before the next turn starts** instead
48
+ * of being merged into that turn's message when neither carries a `messageId`.
49
+ *
50
+ * ## The Qoder adapter boundaries (Task 17c)
51
+ *
52
+ * `AcpSession` passes events through three adapter boundaries, and so does the processor, over its
53
+ * checkpointed data (`state.boundaries`), at the same points:
54
+ * - the root's own update ({@link routeOwnEvent} = `routeMappedEvent`): the foreground-output boundary
55
+ * may hold an unaddressed chunk back (certified qoder-cli), the terminal repair observes it, the
56
+ * subwork boundary maps it (`supportsSubworkMeta`) — and only an unscoped mapped chunk tracks the
57
+ * root's message boundary;
58
+ * - everything `AcpSession.emitEvent` emits ({@link emitSessionEvent}): diverted updates, cards, and the
59
+ * terminal repair's own events (`sideChannel.ts`) — observed by the repair, then subwork-mapped;
60
+ * - load history (`observeReplayEvent`): observed by the repair.
61
+ */
62
+ import { DRAFT_SESSION_UPDATE_NAMES } from '../draftSubagents.js';
63
+ import { mapSessionUpdate, newMapperScopeData, SessionMapperState } from '../sessionUpdateMapper.js';
64
+ import { certifiedRuntime, ForegroundOutputBoundary } from '../../qoderAdapter/foregroundOutput.js';
65
+ import { SubworkBoundary, supportsSubworkMeta } from '../../qoderAdapter/subwork.js';
66
+ import { TerminalRepairFold } from '../../qoderAdapter/backgroundTerminalFold.js';
67
+ import { validateWireEvent } from '../validate.js';
68
+ import { emitEvent, isRecord } from './emit.js';
69
+ import { observeSubagentSpawned } from './origins.js';
70
+ import { reportActivity } from './runtime.js';
71
+ import { INBOUND_NOTIFICATION_SCHEMAS } from './sdkSchemas.js';
72
+ import { ASYNC_TASK_LIMIT, getOwn, putOwn } from './state.js';
73
+ const DRAFT_NAMES = new Set(DRAFT_SESSION_UPDATE_NAMES);
74
+ /**
75
+ * Updates that state what the session **is now** (its command table, mode, config, usage, info), not what happened in it (Task 21).
76
+ * One the loaded session sends after the load's response — the root is bound — but before the daemon's `historyEnd` is not history:
77
+ * it is folded live (numbered), or a reopened session would never show its command table (Claude sends `available_commands_update`
78
+ * from a timer right after answering `session/load`). Before the response such an update is part of the replay and stays history.
79
+ */
80
+ const LEVEL_UPDATES = new Set([
81
+ 'available_commands_update', 'current_mode_update', 'config_option_update', 'usage_update', 'session_info_update',
82
+ ]);
83
+ const SESSION_NOTIFICATION = INBOUND_NOTIFICATION_SCHEMAS['session/update'];
84
+ function stringField(payload, key) {
85
+ const value = payload[key];
86
+ return typeof value === 'string' ? value : null;
87
+ }
88
+ /** The mapper for `nativeSessionId`, over the checkpointed scope data (created on first use). */
89
+ function liveMapper(state, out, nativeSessionId, t) {
90
+ let data = getOwn(state.mapperScopes, nativeSessionId);
91
+ if (!data)
92
+ putOwn(state.mapperScopes, nativeSessionId, data = newMapperScopeData());
93
+ // Codex's activity rides the root's own `session_info_update` (`AcpSession.scopes`' callback).
94
+ return new SessionMapperState((raw) => {
95
+ if (nativeSessionId === state.rootSessionId)
96
+ reportActivity(state, out, raw, t);
97
+ }, data);
98
+ }
99
+ /** An inbound `session/update` notification (params not yet validated). */
100
+ export function onSessionUpdate(state, params, out, t) {
101
+ if (!isRecord(params) || typeof params.sessionId !== 'string' || params.sessionId.length === 0 || !isRecord(params.update)) {
102
+ state.diagnostics.droppedUpdates += 1;
103
+ return;
104
+ }
105
+ const { sessionId, update } = params;
106
+ if (state.history && !(LEVEL_UPDATES.has(String(update.sessionUpdate)) && state.rootSessionId !== null && sessionId === state.rootSessionId)) {
107
+ onHistoryUpdate(state, state.history, sessionId, update, out);
108
+ return;
109
+ }
110
+ const root = state.rootSessionId;
111
+ if (root === null) {
112
+ state.diagnostics.droppedUpdates += 1;
113
+ return;
114
+ }
115
+ const variant = update.sessionUpdate;
116
+ const draft = typeof variant === 'string' && DRAFT_NAMES.has(variant);
117
+ if (!draft && sessionId === root) {
118
+ const parsed = SESSION_NOTIFICATION.safeParse(params);
119
+ if (!parsed.success) {
120
+ state.diagnostics.droppedUpdates += 1;
121
+ return;
122
+ }
123
+ const stripped = parsed.data.update;
124
+ for (const event of mapSessionUpdate(stripped, liveMapper(state, out, root, t)))
125
+ routeOwnEvent(state, out, event);
126
+ return;
127
+ }
128
+ let events;
129
+ try {
130
+ events = mapSessionUpdate(update, liveMapper(state, out, sessionId, t));
131
+ }
132
+ catch {
133
+ // `applyDivertedUpdate`: an unmapped variant on a diverted update is dropped, the session lives on.
134
+ state.diagnostics.droppedUpdates += 1;
135
+ return;
136
+ }
137
+ for (const event of events)
138
+ emitSessionEvent(state, out, sessionId === root ? event : { ...event, acpSessionId: sessionId });
139
+ }
140
+ /** The agent `initialize` named (`AcpConnection.agentInfo`), if it is ready. */
141
+ function agentInfo(state) {
142
+ return state.initialize.status === 'ready' ? state.initialize.facts.agentInfo : undefined;
143
+ }
144
+ /** The terminal repair, once a `qoder_terminal_repair` note enabled it. */
145
+ export function repairFold(state) {
146
+ return state.boundaries.repair ? new TerminalRepairFold(state.boundaries.repair) : undefined;
147
+ }
148
+ /** `this.subworkBoundary?.map(event) ?? [event]`. */
149
+ function throughSubwork(state, event) {
150
+ const root = state.rootSessionId;
151
+ if (root === null || !supportsSubworkMeta(agentInfo(state)))
152
+ return [event];
153
+ return new SubworkBoundary(root, state.boundaries.subwork).map(event);
154
+ }
155
+ /** `this.foregroundOutput?.endTurn()` — no turn remains. */
156
+ export function endForegroundTurn(state) {
157
+ state.boundaries.foreground.pending = {};
158
+ }
159
+ /** `AcpSession.routeMappedEvent`, Qoder boundaries included. */
160
+ function routeOwnEvent(state, out, event) {
161
+ const root = state.rootSessionId;
162
+ if (certifiedRuntime(agentInfo(state)) && !new ForegroundOutputBoundary(root, state.boundaries.foreground).accept(event))
163
+ return;
164
+ repairFold(state)?.observe(event);
165
+ const subwork = supportsSubworkMeta(agentInfo(state));
166
+ for (const mapped of throughSubwork(state, event)) {
167
+ observe(state, mapped);
168
+ observeBackgroundWriter(state, mapped);
169
+ if ((!subwork || !mapped.acpSessionId || mapped.acpSessionId === root) &&
170
+ (mapped.eventName === 'agent_message_chunk' || mapped.eventName === 'agent_thought_chunk'))
171
+ trackMessageBoundary(state, out, mapped);
172
+ emitEvent(state, out, mapped);
173
+ }
174
+ }
175
+ /**
176
+ * `AcpSession.emitEvent` (minus a card's own checks, `session.ts`): a diverted update, a card, or a
177
+ * repair event. The repair observes it as given; a root id on `acpSessionId` is dropped; the subwork
178
+ * boundary maps it; each mapped event feeds the origin graph and is numbered.
179
+ */
180
+ export function emitSessionEvent(state, out, event) {
181
+ repairFold(state)?.observe(event);
182
+ // Inside a load's history window the session being loaded is the root (an attached `AcpSession` knows it).
183
+ const normalized = event.acpSessionId === (state.rootSessionId ?? state.history?.rootSessionId)
184
+ ? { eventName: event.eventName, payload: event.payload }
185
+ : event;
186
+ for (const mapped of throughSubwork(state, normalized)) {
187
+ observe(state, mapped);
188
+ emitEvent(state, out, mapped);
189
+ }
190
+ }
191
+ /** What every live event feeds besides the outbox: the origin graph and the async-task set. */
192
+ function observe(state, event) {
193
+ if (event.eventName === 'subagent_spawned') {
194
+ observeSubagentSpawned(state, event.acpSessionId ?? state.rootSessionId, event.payload.subagentSessionId);
195
+ }
196
+ trackAsyncTask(state, event);
197
+ }
198
+ /** `AcpSession.observeBackgroundWriter`: an explicitly detached writer blocks `executionEnded` for good. */
199
+ function observeBackgroundWriter(state, event) {
200
+ const input = event.payload.rawInput;
201
+ if (isRecord(input) && input.run_in_background === true)
202
+ state.backgroundWriterUnconfirmed = true;
203
+ const output = event.payload.rawOutput;
204
+ if (isRecord(output) && output.backgrounded === true && (typeof output.taskId === 'string' || typeof output.pid === 'number')) {
205
+ state.backgroundWriterUnconfirmed = true;
206
+ }
207
+ }
208
+ const TERMINAL_ASYNC_STATES = new Set(['completed', 'failed', 'stopped']);
209
+ /**
210
+ * The unfinished async tasks (AIR `async_task_*`), per native session: spawned → running; a state
211
+ * update sets the last non-terminal state or removes the task on a terminal one. Settles on the
212
+ * **last** state — a finished shell sends `stopped` then `completed` (`draftSubagents.ts`), and the
213
+ * second finds nothing left to remove.
214
+ */
215
+ function trackAsyncTask(state, event) {
216
+ if (event.eventName !== 'async_task_spawned' && event.eventName !== 'async_task_state_update')
217
+ return;
218
+ const taskId = event.payload.asyncTaskId;
219
+ if (typeof taskId !== 'string' || taskId.length === 0)
220
+ return;
221
+ const owner = event.acpSessionId ?? state.rootSessionId;
222
+ if (owner === null)
223
+ return;
224
+ const next = event.eventName === 'async_task_spawned' ? 'running' : event.payload.state;
225
+ let tasks = getOwn(state.asyncTasks, owner);
226
+ if (typeof next === 'string' && TERMINAL_ASYNC_STATES.has(next)) {
227
+ if (tasks && Object.hasOwn(tasks, taskId)) {
228
+ delete tasks[taskId];
229
+ if (Object.keys(tasks).length === 0)
230
+ delete state.asyncTasks[owner];
231
+ }
232
+ return;
233
+ }
234
+ if (next !== 'running' && next !== 'paused')
235
+ return;
236
+ if (!(tasks && Object.hasOwn(tasks, taskId)) && unfinishedAsyncTaskCount(state) >= ASYNC_TASK_LIMIT) {
237
+ state.diagnostics.untrackedAsyncTasks += 1;
238
+ return;
239
+ }
240
+ if (!tasks)
241
+ putOwn(state.asyncTasks, owner, tasks = {});
242
+ putOwn(tasks, taskId, next);
243
+ }
244
+ function unfinishedAsyncTaskCount(state) {
245
+ let count = 0;
246
+ for (const tasks of Object.values(state.asyncTasks))
247
+ count += Object.keys(tasks).length;
248
+ return count;
249
+ }
250
+ /** Every unfinished async task, as `{nativeSessionId, asyncTaskId, state}`, in insertion order. */
251
+ export function unfinishedAsyncTasks(state) {
252
+ return Object.entries(state.asyncTasks).flatMap(([nativeSessionId, tasks]) => Object.entries(tasks).map(([asyncTaskId, status]) => ({ nativeSessionId, asyncTaskId, state: status })));
253
+ }
254
+ // ---- agent_message synthesis --------------------------------------------------------------------
255
+ /** `AcpSession.trackMessageBoundary`, plus remembering whether the message began outside any turn. */
256
+ function trackMessageBoundary(state, out, event) {
257
+ const message = state.message;
258
+ const messageId = stringField(event.payload, 'messageId');
259
+ if (message.open && messageId !== null && message.messageId !== null && messageId !== message.messageId)
260
+ flushMessage(state, out);
261
+ if (!message.open)
262
+ message.outOfTurn = state.turns.length === 0;
263
+ message.open = true;
264
+ if (messageId !== null)
265
+ message.messageId = messageId;
266
+ }
267
+ /** `AcpSession.flushMessageCompleted`: close the streaming message with `agent_message` (no-op if none). */
268
+ export function flushMessage(state, out) {
269
+ const message = state.message;
270
+ if (!message.open)
271
+ return;
272
+ emitEvent(state, out, { eventName: 'agent_message', payload: { messageId: message.messageId ?? 'turn' } });
273
+ message.open = false;
274
+ message.messageId = null;
275
+ message.outOfTurn = false;
276
+ }
277
+ /** A turn is about to start: a message that began outside any turn is closed now, never merged into it. */
278
+ export function flushOutOfTurnMessage(state, out) {
279
+ if (state.message.open && state.message.outOfTurn)
280
+ flushMessage(state, out);
281
+ }
282
+ // ---- load history -----------------------------------------------------------------------------
283
+ /** `foldScopedReplayEvents`, one update at a time. */
284
+ function onHistoryUpdate(state, history, sessionId, update, out) {
285
+ let data = getOwn(history.scopes, sessionId);
286
+ if (!data)
287
+ putOwn(history.scopes, sessionId, data = newMapperScopeData());
288
+ let events;
289
+ try {
290
+ events = mapSessionUpdate(update, new SessionMapperState(() => { }, data));
291
+ }
292
+ catch {
293
+ // Today an unmappable update throws out of the whole load (`replayNativeSession`); here one
294
+ // update is lost and the history goes on — counted, and reported on `processor.history_end`.
295
+ state.diagnostics.droppedUpdates += 1;
296
+ history.droppedCount += 1;
297
+ return;
298
+ }
299
+ for (const event of events) {
300
+ if (event.eventName === 'user_message_chunk')
301
+ flushHistoryMessage(state, history, sessionId, out);
302
+ if (event.eventName === 'agent_message_chunk' || event.eventName === 'agent_thought_chunk') {
303
+ const messageId = stringField(event.payload, 'messageId');
304
+ const index = history.messages.findIndex((entry) => entry.nativeSessionId === sessionId);
305
+ const previous = index >= 0 ? history.messages[index] : undefined;
306
+ if (previous && messageId !== null && previous.messageId !== null && messageId !== previous.messageId) {
307
+ flushHistoryMessage(state, history, sessionId, out);
308
+ history.messages.push({ nativeSessionId: sessionId, messageId });
309
+ }
310
+ else if (previous) {
311
+ previous.messageId = messageId ?? previous.messageId;
312
+ }
313
+ else {
314
+ history.messages.push({ nativeSessionId: sessionId, messageId });
315
+ }
316
+ }
317
+ emitHistory(state, history, out, event, sessionId);
318
+ }
319
+ }
320
+ function flushHistoryMessage(state, history, sessionId, out) {
321
+ const index = history.messages.findIndex((entry) => entry.nativeSessionId === sessionId);
322
+ if (index < 0)
323
+ return;
324
+ const [message] = history.messages.splice(index, 1);
325
+ emitHistory(state, history, out, { eventName: 'agent_message', payload: { messageId: message.messageId ?? 'replay' } }, sessionId);
326
+ }
327
+ function emitHistory(state, history, out, event, sessionId) {
328
+ const scoped = sessionId === history.rootSessionId ? event : { ...event, acpSessionId: sessionId };
329
+ if (validateWireEvent(scoped)) {
330
+ state.diagnostics.invalidEvents += 1;
331
+ return;
332
+ }
333
+ // `AcpSession.observeReplayEvent`: history restores the repair's calls, addresses and background-writer evidence.
334
+ repairFold(state)?.observe(scoped);
335
+ if (scoped.eventName === 'subagent_spawned') {
336
+ observeSubagentSpawned(state, scoped.acpSessionId ?? history.rootSessionId, scoped.payload.subagentSessionId, history.rootSessionId);
337
+ }
338
+ observeBackgroundWriter(state, scoped);
339
+ out.unjournaled({
340
+ t: 'processor.history_event',
341
+ eventName: scoped.eventName,
342
+ payload: scoped.payload,
343
+ ...(scoped.acpSessionId ? { acpSessionId: scoped.acpSessionId } : {}),
344
+ });
345
+ }
346
+ /**
347
+ * Close the history window: flush the open history messages (in first-chunk order, as
348
+ * `foldScopedReplayEvents` does at the end of its list) and report it. `ok: false` when the load
349
+ * failed or the connection closed — the daemon discards what it collected, as today's load does.
350
+ * `droppedCount` (history updates that could not be mapped) lets the daemon report a partial
351
+ * backfill. The `processor.history_event`s are unjournaled: a daemon that crashes inside the window
352
+ * restarts the load rather than resuming from a checkpoint cut inside it.
353
+ */
354
+ export function endHistory(state, out, ok) {
355
+ const history = state.history;
356
+ if (!history)
357
+ return;
358
+ if (ok) {
359
+ while (history.messages.length > 0)
360
+ flushHistoryMessage(state, history, history.messages[0].nativeSessionId, out);
361
+ }
362
+ state.history = null;
363
+ out.unjournaled({ t: 'processor.history_end', ok, droppedCount: history.droppedCount });
364
+ }