@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,1591 @@
1
+ // src/entry.ts
2
+ import { homedir } from "node:os";
3
+
4
+ // src/background/backgroundTerminalFold.ts
5
+ var MAX_TASKS = 512;
6
+ var MAX_RESULT = 128 * 1024;
7
+ var MAX_RESULTS = 4 * 1024 * 1024;
8
+ var CHILD_ROW_LIMIT = 4096;
9
+ var TERMINALS = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
10
+ function object(value) {
11
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
12
+ }
13
+ function address(value) {
14
+ return typeof value === "string" && /^[a-zA-Z0-9_-]{1,160}$/.test(value) ? value : void 0;
15
+ }
16
+ function own(record2, key) {
17
+ return Object.hasOwn(record2, key) ? record2[key] : void 0;
18
+ }
19
+ function put(record2, key, value) {
20
+ Object.defineProperty(record2, key, { value, enumerable: true, writable: true, configurable: true });
21
+ }
22
+ function utf8Length(text) {
23
+ let bytes = 0;
24
+ for (let index = 0; index < text.length; index += 1) {
25
+ const code = text.charCodeAt(index);
26
+ if (code < 128) bytes += 1;
27
+ else if (code < 2048) bytes += 2;
28
+ else if (code >= 55296 && code <= 56319 && index + 1 < text.length && text.charCodeAt(index + 1) >= 56320 && text.charCodeAt(index + 1) <= 57343) {
29
+ bytes += 4;
30
+ index += 1;
31
+ } else bytes += 3;
32
+ }
33
+ return bytes;
34
+ }
35
+ function rootRowFacts(row, context) {
36
+ if (row.sessionId !== context.nativeSessionId || row.cwd !== context.cwd || row.isSidechain !== false) return [];
37
+ const content = object(row.message).content;
38
+ if (!Array.isArray(content)) return [];
39
+ const facts = [];
40
+ if (context.restoreCalls && row.type === "assistant") {
41
+ for (const block of content.map(object)) {
42
+ const input = object(block.input), id3 = address(block.id);
43
+ if (block.type === "tool_use" && block.name === "Agent" && id3 && input.run_in_background === true && typeof input.prompt === "string" && (input.subagent_type == null || typeof input.subagent_type === "string")) {
44
+ facts.push({ kind: "call", toolUseId: id3, input });
45
+ }
46
+ }
47
+ return facts;
48
+ }
49
+ if (row.type !== "user") return facts;
50
+ const output = object(row.toolUseResult);
51
+ if (output.isAsync === true && output.status === "async_launched") {
52
+ const results = content.map(object).filter((c) => c.type === "tool_result");
53
+ const id3 = results.length === 1 ? address(results[0].tool_use_id) : void 0;
54
+ const agent = address(output.agentId);
55
+ if (id3) facts.push({ kind: "launch", toolUseId: id3, ...agent ? { agentId: agent } : {} });
56
+ return facts;
57
+ }
58
+ if (row.isMeta !== true || object(row.origin).kind !== "task-notification") return facts;
59
+ for (const block of content.map(object)) {
60
+ if (block.type !== "text" || typeof block.text !== "string" || !block.text.startsWith("[SYSTEM NOTIFICATION - NOT USER INPUT]\n")) continue;
61
+ const match = block.text.match(/<task-notification>\n<task-id>([a-zA-Z0-9_-]{1,160})<\/task-id>\n<tool-use-id>([a-zA-Z0-9_-]{1,160})<\/tool-use-id>\n(?:<output-file>[^\n]*<\/output-file>\n)?<status>(completed|failed|cancelled)<\/status>\n<summary>[^\n]*<\/summary>\n<result>([\s\S]*)<\/result>\n(?:<usage>[^\n]*<\/usage>\n)?<\/task-notification>\s*$/);
62
+ if (match && utf8Length(match[4]) <= MAX_RESULT) {
63
+ facts.push({ kind: "terminal", toolUseId: match[2], taskId: match[1], state: match[3], result: match[4] });
64
+ }
65
+ }
66
+ return facts;
67
+ }
68
+ function newTerminalRepairData(options) {
69
+ return {
70
+ nativeSessionId: options.nativeSessionId,
71
+ restoreCalls: options.restoreCalls === true,
72
+ includeProcess: options.includeProcess === true,
73
+ calls: {},
74
+ callCount: 0,
75
+ launches: [],
76
+ terminals: [],
77
+ delivered: {},
78
+ pendingProcesses: {},
79
+ resultBytes: 0,
80
+ childRows: {},
81
+ polledProcesses: {}
82
+ };
83
+ }
84
+ var TerminalRepairFold = class {
85
+ constructor(data) {
86
+ this.data = data;
87
+ }
88
+ data;
89
+ call(id3) {
90
+ return own(this.data.calls, id3);
91
+ }
92
+ agentOf(id3) {
93
+ return this.data.launches.find(([toolUseId]) => toolUseId === id3)?.[1];
94
+ }
95
+ /** Every ACP event the session routes or emits (live or load history): learns the certified Agent calls. */
96
+ observe(event) {
97
+ if (event.acpSessionId && event.acpSessionId !== this.data.nativeSessionId || !["tool_call", "tool_call_update"].includes(event.eventName)) return;
98
+ const payload = event.payload;
99
+ const id3 = address(payload.toolCallId);
100
+ if (!id3) return;
101
+ let call = this.call(id3);
102
+ const input = object(payload.rawInput);
103
+ const meta = object(object(payload._meta).qoder);
104
+ const backgroundOpening = input.run_in_background === true && typeof input.prompt === "string" && (input.subagent_type == null || typeof input.subagent_type === "string") && payload.kind === "think" && meta.toolName === "Agent";
105
+ if (!call && backgroundOpening && this.data.callCount < MAX_TASKS) {
106
+ call = {
107
+ input,
108
+ invocationComplete: false,
109
+ nativeTerminal: false,
110
+ process: this.data.includeProcess && meta.version === 1 && meta.source === "native-acp-patch"
111
+ };
112
+ put(this.data.calls, id3, call);
113
+ this.data.callCount += 1;
114
+ }
115
+ if (!call) return;
116
+ const update = event.eventName === "tool_call_update";
117
+ const processInput = update && !Object.hasOwn(payload, "rawInput") ? call.input : input;
118
+ const processKind = update && !Object.hasOwn(payload, "kind") ? "think" : payload.kind;
119
+ const processOpening = processInput.run_in_background === true && typeof processInput.prompt === "string" && (processInput.subagent_type == null || typeof processInput.subagent_type === "string") && processKind === "think";
120
+ if (processOpening && meta.toolName === "Agent" && this.data.includeProcess && meta.version === 1 && meta.source === "native-acp-patch" && !call.nativeTerminal && !Object.hasOwn(this.data.delivered, id3)) call.process = true;
121
+ if (payload.status === "completed") call.invocationComplete = true;
122
+ const output = object(payload.rawOutput);
123
+ if (output.kind === "agent-result" && (TERMINALS.has(String(output.state)) || output.state === "error")) {
124
+ call.nativeTerminal = true;
125
+ this.forget(id3, call);
126
+ }
127
+ }
128
+ /** A known call has no result yet: the reader keeps polling the root transcript. */
129
+ active() {
130
+ return Object.entries(this.data.calls).some(([id3, call]) => !Object.hasOwn(this.data.delivered, id3) && !call.nativeTerminal);
131
+ }
132
+ /** This call's child transcript is still followed. */
133
+ wantsProcess(toolUseId) {
134
+ const call = this.call(toolUseId);
135
+ return call !== void 0 && call.process && !Object.hasOwn(this.data.delivered, toolUseId) && !call.nativeTerminal;
136
+ }
137
+ /** Fold one fact; returns the ACP events to emit now (a child row's, or the results a poll's end publishes). */
138
+ apply(fact) {
139
+ const data = this.data;
140
+ switch (fact.kind) {
141
+ case "call":
142
+ if (data.restoreCalls && !this.call(fact.toolUseId) && data.callCount < MAX_TASKS) {
143
+ put(data.calls, fact.toolUseId, { input: fact.input, invocationComplete: false, nativeTerminal: false, process: false });
144
+ data.callCount += 1;
145
+ }
146
+ return [];
147
+ case "launch": {
148
+ const call = this.call(fact.toolUseId);
149
+ if (data.restoreCalls && call) call.invocationComplete = true;
150
+ if (fact.agentId && data.launches.length < MAX_TASKS && this.agentOf(fact.toolUseId) === void 0) {
151
+ data.launches.push([fact.toolUseId, fact.agentId]);
152
+ }
153
+ return [];
154
+ }
155
+ case "terminal": {
156
+ const bytes = utf8Length(fact.result);
157
+ if (bytes <= MAX_RESULT && data.resultBytes + bytes <= MAX_RESULTS && data.terminals.length < MAX_TASKS && !data.terminals.some((terminal2) => terminal2.toolUseId === fact.toolUseId)) {
158
+ data.resultBytes += bytes;
159
+ data.terminals.push({ toolUseId: fact.toolUseId, taskId: fact.taskId, state: fact.state, result: fact.result });
160
+ }
161
+ return [];
162
+ }
163
+ case "child": {
164
+ if (!this.wantsProcess(fact.toolUseId) || this.agentOf(fact.toolUseId) === void 0) return [];
165
+ let seen = own(data.childRows, fact.toolUseId);
166
+ if (!seen) put(data.childRows, fact.toolUseId, seen = []);
167
+ if (seen.includes(fact.uuid) || seen.length >= CHILD_ROW_LIMIT) return [];
168
+ seen.push(fact.uuid);
169
+ return [...fact.events];
170
+ }
171
+ case "child_status":
172
+ if (!this.wantsProcess(fact.toolUseId) || this.agentOf(fact.toolUseId) === void 0) return [];
173
+ if (fact.caughtUp) delete data.pendingProcesses[fact.toolUseId];
174
+ else put(data.pendingProcesses, fact.toolUseId, true);
175
+ put(data.polledProcesses, fact.toolUseId, true);
176
+ return [];
177
+ case "poll_end":
178
+ return this.publish();
179
+ }
180
+ }
181
+ publish() {
182
+ const data = this.data;
183
+ const events = [];
184
+ for (const terminal2 of data.terminals) {
185
+ const id3 = terminal2.toolUseId;
186
+ const call = this.call(id3), agentId = this.agentOf(id3);
187
+ if (call?.nativeTerminal) terminal2.result = "";
188
+ if (Object.hasOwn(data.delivered, id3) || Object.hasOwn(data.pendingProcesses, id3) || !call?.invocationComplete || call.nativeTerminal || !agentId || call.process && !Object.hasOwn(data.polledProcesses, id3)) continue;
189
+ put(data.delivered, id3, true);
190
+ events.push({ eventName: "tool_call_update", payload: {
191
+ name: "Agent",
192
+ kind: "think",
193
+ toolCallId: id3,
194
+ rawInput: call.input,
195
+ status: "completed",
196
+ rawOutput: { kind: "agent-result", state: terminal2.state, agentId, result: terminal2.result },
197
+ content: [{ type: "content", content: { type: "text", text: terminal2.result } }],
198
+ _meta: { qoder: { toolName: "Agent" }, axomNativeTerminalRepair: {
199
+ patch: "qoder-background-terminal",
200
+ nativeSessionId: data.nativeSessionId,
201
+ taskId: terminal2.taskId,
202
+ source: "native-task-notification"
203
+ } }
204
+ } });
205
+ terminal2.result = "";
206
+ this.forget(id3, call);
207
+ }
208
+ return events;
209
+ }
210
+ /**
211
+ * A delivered or natively finished call is never published and its child never followed again
212
+ * (`wantsProcess` is false for good), so what only those two read goes: the input (read by
213
+ * `publish` alone), the child rows' uuids and status. The call entry itself stays — `observe` must
214
+ * not register the same id afresh — and so do the launch and result entries, whose presence is what
215
+ * keeps a restarted reader's repeats from being taken as new.
216
+ */
217
+ forget(id3, call) {
218
+ call.input = {};
219
+ delete this.data.childRows[id3];
220
+ delete this.data.pendingProcesses[id3];
221
+ delete this.data.polledProcesses[id3];
222
+ }
223
+ };
224
+
225
+ // src/background/poller.ts
226
+ import { isAbsolute, join as join4, resolve as resolve2 } from "node:path";
227
+
228
+ // src/history.ts
229
+ import { dirname as dirname2, join as join2 } from "node:path";
230
+ import { TextDecoder as TextDecoder2 } from "node:util";
231
+
232
+ // src/subwork.ts
233
+ var object2 = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
234
+ var isSubworkId = (v) => typeof v === "string" && v.length > 0 && v.length <= 256 && !/[\x00-\x1f\x7f]/.test(v);
235
+ function newSubworkData() {
236
+ return { calls: {}, count: 0 };
237
+ }
238
+ function resultText(value) {
239
+ if (typeof value === "string") return value;
240
+ if (Array.isArray(value)) {
241
+ const texts = value.filter((v2) => object2(v2).type === "text").map((v2) => object2(v2).text).filter((v2) => typeof v2 === "string");
242
+ return texts.length ? texts.join("\n") : void 0;
243
+ }
244
+ const v = object2(value);
245
+ if (v.kind === "agent-result") return resultText(v.content ?? v.result);
246
+ return void 0;
247
+ }
248
+ var SubworkBoundary = class {
249
+ constructor(root, data = newSubworkData()) {
250
+ this.root = root;
251
+ this.data = data;
252
+ }
253
+ root;
254
+ data;
255
+ get(toolId) {
256
+ return Object.hasOwn(this.data.calls, toolId) ? this.data.calls[toolId] : void 0;
257
+ }
258
+ child(toolId) {
259
+ return `qoder-agent:${this.root}:${toolId}`;
260
+ }
261
+ map(event) {
262
+ if (event.acpSessionId && event.acpSessionId !== this.root) return [event];
263
+ const p = event.payload, q = object2(object2(p._meta).qoder), input = object2(p.rawInput);
264
+ const marked = q.version === 1 && ["native-acp-patch", "sdk", "correction"].includes(String(q.source));
265
+ const toolId = p.toolCallId;
266
+ let call = isSubworkId(toolId) ? this.get(toolId) : void 0;
267
+ const parent = marked && q.route !== "unknown" && isSubworkId(q.parentToolUseId) ? q.parentToolUseId : void 0;
268
+ const events = [];
269
+ if (!call && marked && p.kind === "think" && q.toolName === "Agent" && isSubworkId(toolId) && typeof input.prompt === "string" && (input.subagent_type == null || typeof input.subagent_type === "string") && (input.run_in_background === void 0 || typeof input.run_in_background === "boolean") && ["tool_call", "tool_call_update"].includes(event.eventName) && this.data.count < 4096) {
270
+ call = {
271
+ child: this.child(toolId),
272
+ ...parent ? { parent: this.child(parent) } : {},
273
+ background: input.run_in_background === true,
274
+ ended: false,
275
+ streamedText: ""
276
+ };
277
+ Object.defineProperty(this.data.calls, toolId, { value: call, enumerable: true, writable: true, configurable: true });
278
+ this.data.count += 1;
279
+ events.push({ eventName: "subagent_spawned", payload: {
280
+ subagentSessionId: call.child,
281
+ name: typeof input.description === "string" && input.description.trim() ? input.description : input.subagent_type ?? "Agent",
282
+ task: input.prompt,
283
+ capabilities: {}
284
+ }, ...call.parent ? { acpSessionId: call.parent } : {} });
285
+ events.push({ eventName: "user_message_chunk", payload: { content: { type: "text", text: input.prompt } }, acpSessionId: call.child });
286
+ }
287
+ if (call && ["tool_call", "tool_call_update"].includes(event.eventName)) {
288
+ if (call.ended) return events;
289
+ const output = object2(p.rawOutput);
290
+ const state = output.kind === "agent-result" ? output.state : void 0;
291
+ const terminal2 = state === "completed" ? "completed" : ["error", "failed"].includes(String(state)) ? "failed" : state === "cancelled" ? "cancelled" : ["failed", "cancelled"].includes(String(p.status)) ? p.status : !call.background && p.status === "completed" ? "completed" : void 0;
292
+ if (terminal2) {
293
+ const text = resultText(p.rawOutput);
294
+ if (text !== void 0 && !call.streamedText.endsWith(text)) events.push({
295
+ eventName: "agent_message_chunk",
296
+ payload: { content: { type: "text", text } },
297
+ acpSessionId: call.child
298
+ });
299
+ events.push({
300
+ eventName: "subagent_state_update",
301
+ payload: { subagentSessionId: call.child, state: terminal2 },
302
+ ...call.parent ? { acpSessionId: call.parent } : {}
303
+ });
304
+ call.ended = true;
305
+ call.streamedText = "";
306
+ }
307
+ return events;
308
+ }
309
+ if (parent) {
310
+ const owner = this.get(parent), content = object2(p.content);
311
+ if (owner && !owner.ended && event.eventName === "agent_message_chunk" && content.type === "text" && typeof content.text === "string") {
312
+ owner.streamedText += content.text;
313
+ }
314
+ return [{ ...event, acpSessionId: this.child(parent) }];
315
+ }
316
+ return [event];
317
+ }
318
+ };
319
+
320
+ // src/foregroundOutput.ts
321
+ function record(value) {
322
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
323
+ }
324
+ function newForegroundOutputData() {
325
+ return { pending: {} };
326
+ }
327
+ var ForegroundOutputBoundary = class {
328
+ constructor(nativeSessionId, data = newForegroundOutputData()) {
329
+ this.nativeSessionId = nativeSessionId;
330
+ this.data = data;
331
+ }
332
+ nativeSessionId;
333
+ data;
334
+ accept(event) {
335
+ if (event.acpSessionId && event.acpSessionId !== this.nativeSessionId) return true;
336
+ const p = event.payload, input = record(p.rawInput), pending = this.data.pending;
337
+ if (event.eventName === "tool_call" && p.kind === "think" && record(record(p._meta).qoder).toolName === "Agent" && typeof p.toolCallId === "string" && typeof input.prompt === "string" && (input.run_in_background === void 0 || input.run_in_background === false)) {
338
+ Object.defineProperty(pending, p.toolCallId, { value: true, enumerable: true, writable: true, configurable: true });
339
+ }
340
+ if (event.eventName === "tool_call_update" && typeof p.toolCallId === "string" && ["completed", "failed", "cancelled"].includes(String(p.status)) && Object.hasOwn(pending, p.toolCallId)) delete pending[p.toolCallId];
341
+ return Object.keys(pending).length === 0 || !["agent_message_chunk", "agent_thought_chunk"].includes(event.eventName) || typeof p.messageId === "string" || p._meta != null;
342
+ }
343
+ endTurn() {
344
+ for (const id3 of Object.keys(this.data.pending)) delete this.data.pending[id3];
345
+ }
346
+ };
347
+
348
+ // src/checkpoint.ts
349
+ import { constants } from "node:fs";
350
+ import { link, lstat, mkdir, open, readFile, readdir, realpath, rename, unlink } from "node:fs/promises";
351
+ import { createHash, randomUUID } from "node:crypto";
352
+ import { dirname, join, parse, resolve, sep } from "node:path";
353
+ var MAX_CHECKPOINT = 16 * 1024 * 1024;
354
+ var id = (s) => typeof s === "string" && s.length > 0 && s.length <= 256 && !/[\x00-\x1f\x7f]/.test(s);
355
+ var childId = (s) => typeof s === "string" && s.length > 0 && s.length <= 512 && !/[\x00-\x1f\x7f]/.test(s);
356
+ var obj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
357
+ function insist(ok, message) {
358
+ if (!ok)
359
+ throw new Error(`Qoder recovery ${message}`);
360
+ }
361
+ function newRecoveryData(root, cwd, releaseId) {
362
+ return { schema: 1, root, cwd, releaseId, foreground: newForegroundOutputData(), subwork: newSubworkData(), background: newTerminalRepairData({ nativeSessionId: root, restoreCalls: true, includeProcess: true }) };
363
+ }
364
+ function validateRecoveryData(value, identity) {
365
+ insist(obj(value) && value.schema === 1, "schema invalid");
366
+ insist(value.root === identity.root && value.cwd === identity.cwd && value.releaseId === identity.releaseId, "identity/release mismatch");
367
+ const dict = (v, n, label, validate) => {
368
+ insist(obj(v), `${label} shape`);
369
+ const entries = Object.entries(v);
370
+ insist(entries.length <= n, `${label} bound`);
371
+ for (const [k, x] of entries)
372
+ insist(id(k) && validate(x, k), `${label} entry`);
373
+ return entries.length;
374
+ };
375
+ const flags = (v, n, label) => dict(v, n, label, (x) => x === true);
376
+ insist(obj(value.foreground), "foreground shape");
377
+ flags(value.foreground.pending, 4096, "foreground");
378
+ const s = value.subwork;
379
+ insist(obj(s), "subwork shape");
380
+ const count = dict(s.calls, 4096, "subwork", (x, k) => obj(x) && x.child === `qoder-agent:${identity.root}:${k}` && childId(x.child) && (x.parent === void 0 || childId(x.parent) && x.parent.startsWith(`qoder-agent:${identity.root}:`)) && typeof x.background === "boolean" && typeof x.ended === "boolean" && typeof x.streamedText === "string" && Buffer.byteLength(x.streamedText) <= MAX_CHECKPOINT);
381
+ insist(s.count === count, "subwork count bound");
382
+ const b = value.background;
383
+ insist(obj(b) && b.nativeSessionId === identity.root && typeof b.restoreCalls === "boolean" && typeof b.includeProcess === "boolean", "background identity/shape");
384
+ const calls = dict(b.calls, MAX_TASKS, "background calls", (x, k) => !!address(k) && obj(x) && obj(x.input) && typeof x.invocationComplete === "boolean" && typeof x.nativeTerminal === "boolean" && typeof x.process === "boolean");
385
+ insist(b.callCount === calls, "background callCount bound");
386
+ insist(Array.isArray(b.launches) && b.launches.length <= MAX_TASKS && b.launches.every((x) => Array.isArray(x) && x.length === 2 && address(x[0]) && address(x[1])) && new Set(b.launches.map((x) => x[0])).size === b.launches.length, "background launches");
387
+ insist(Array.isArray(b.terminals) && b.terminals.length <= MAX_TASKS, "background terminals");
388
+ let bytes = 0;
389
+ const seen = /* @__PURE__ */ new Set();
390
+ for (const t of b.terminals) {
391
+ insist(obj(t) && address(t.toolUseId) && address(t.taskId) && ["completed", "failed", "cancelled"].includes(t.state) && typeof t.result === "string" && Buffer.byteLength(t.result) <= MAX_RESULT && !seen.has(t.toolUseId), "background terminal");
392
+ seen.add(t.toolUseId);
393
+ bytes += Buffer.byteLength(t.result);
394
+ }
395
+ insist(Number.isSafeInteger(b.resultBytes) && b.resultBytes >= bytes && b.resultBytes <= MAX_RESULTS, "background resultBytes bound");
396
+ for (const key of ["delivered", "pendingProcesses", "polledProcesses"])
397
+ flags(b[key], MAX_TASKS, `background ${key}`);
398
+ dict(b.childRows, MAX_TASKS, "background childRows", (x, k) => !!address(k) && Array.isArray(x) && x.length <= CHILD_ROW_LIMIT && x.every(address) && new Set(x).size === x.length);
399
+ }
400
+ function missing(error) {
401
+ return error.code === "ENOENT";
402
+ }
403
+ async function verifiedDirectory(path, create = false) {
404
+ const absolute = resolve(path), root = parse(absolute).root;
405
+ let current = root;
406
+ for (const part of absolute.slice(root.length).split(sep).filter(Boolean)) {
407
+ current = join(current, part);
408
+ let stat;
409
+ try {
410
+ stat = await lstat(current);
411
+ } catch (error) {
412
+ if (!create || !missing(error))
413
+ throw error;
414
+ try {
415
+ await mkdir(current, { mode: 448 });
416
+ } catch (e) {
417
+ if (e.code !== "EEXIST")
418
+ throw e;
419
+ }
420
+ stat = await lstat(current);
421
+ }
422
+ insist(!stat.isSymbolicLink() && stat.isDirectory(), "symlink or non-directory component");
423
+ }
424
+ insist(await realpath(absolute) === absolute, "directory realpath changed");
425
+ }
426
+ async function assertRegularOrMissing(path) {
427
+ try {
428
+ const s = await lstat(path);
429
+ insist(!s.isSymbolicLink() && s.isFile(), "symlink/nonregular file");
430
+ } catch (e) {
431
+ if (!missing(e))
432
+ throw e;
433
+ }
434
+ }
435
+ async function scanFileSnapshot(path, max, consume) {
436
+ await verifiedDirectory(dirname(path));
437
+ await assertRegularOrMissing(path);
438
+ const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
439
+ try {
440
+ const before = await file.stat();
441
+ insist(before.isFile() && before.size <= max, `file size exceeds ${max === MAX_CHECKPOINT ? "16 MiB" : max}`);
442
+ let offset = 0;
443
+ while (offset < before.size) {
444
+ const chunk = Buffer.alloc(Math.min(64 * 1024, before.size - offset));
445
+ const { bytesRead } = await file.read(chunk, 0, chunk.length, offset);
446
+ insist(bytesRead > 0, "file changed while reading");
447
+ offset += bytesRead;
448
+ consume(chunk.subarray(0, bytesRead));
449
+ }
450
+ const after = await file.stat(), named = await lstat(path);
451
+ insist(before.dev === after.dev && before.ino === after.ino && after.size >= before.size && named.dev === before.dev && named.ino === before.ino && !named.isSymbolicLink() && (after.size > before.size || after.mtimeMs === before.mtimeMs), "file replaced while reading");
452
+ await verifiedDirectory(dirname(path));
453
+ return { capturedBytes: before.size, fileIdentity: `${before.dev}:${before.ino}` };
454
+ } finally {
455
+ await file.close();
456
+ }
457
+ }
458
+ async function readFileSnapshot(path, max) {
459
+ const chunks = [];
460
+ const snapshot = await scanFileSnapshot(path, max, (chunk) => chunks.push(chunk));
461
+ return { bytes: Buffer.concat(chunks, snapshot.capturedBytes), fileIdentity: snapshot.fileIdentity };
462
+ }
463
+ async function readBoundedFile(path, max) {
464
+ return (await readFileSnapshot(path, max)).bytes;
465
+ }
466
+ async function openRecoveryStore(options) {
467
+ insist(/^[a-zA-Z0-9_-]{1,160}$/.test(options.root) && id(options.releaseId) && typeof options.cwd === "string" && options.cwd.length > 0, "identity invalid");
468
+ const directory = join(options.home, ".qoder", "axom-acp-state", createHash("sha256").update(options.cwd).digest("hex"));
469
+ await verifiedDirectory(directory, true);
470
+ const path = join(directory, options.root + ".json"), lock = path + ".lock";
471
+ const token = randomUUID();
472
+ let closed = false;
473
+ const processStat = process.platform === "linux" ? await readFile(`/proc/${process.pid}/stat`, "utf8") : void 0;
474
+ const processStart = processStat ? processStat.slice(processStat.lastIndexOf(")") + 2).split(" ")[19] : String(Math.round(Date.now() - process.uptime() * 1e3));
475
+ const withGuard = async (action) => {
476
+ const guard = lock + ".reclaim", claim = `${process.pid}-${randomUUID()}`;
477
+ await verifiedDirectory(guard, true);
478
+ const mine = join(guard, claim);
479
+ const f = await open(mine, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
480
+ try {
481
+ await f.writeFile(JSON.stringify({ pid: process.pid, processStart, token: claim }));
482
+ await f.sync();
483
+ await f.close();
484
+ for (const name of await readdir(guard)) {
485
+ if (name === claim) continue;
486
+ const match = name.match(/^([1-9][0-9]*)-[a-f0-9-]{36}$/);
487
+ insist(match, "owner guard malformed");
488
+ const pid = Number(match[1]);
489
+ let dead = false;
490
+ try {
491
+ process.kill(pid, 0);
492
+ } catch (e) {
493
+ dead = e.code === "ESRCH";
494
+ }
495
+ insist(dead, "owner acquisition locked");
496
+ await unlink(join(guard, name)).catch((e) => {
497
+ if (!missing(e)) throw e;
498
+ });
499
+ }
500
+ return await action();
501
+ } finally {
502
+ await f.close().catch(() => {
503
+ });
504
+ await unlink(mine).catch((error) => {
505
+ if (!missing(error)) throw error;
506
+ });
507
+ }
508
+ };
509
+ await withGuard(async () => {
510
+ const candidate = lock + "." + token + ".candidate";
511
+ const f = await open(candidate, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
512
+ try {
513
+ await f.writeFile(JSON.stringify({ pid: process.pid, token, processStart, started: Date.now() }));
514
+ await f.sync();
515
+ await f.close();
516
+ for (let attempt = 0; ; attempt++) {
517
+ try {
518
+ await link(candidate, lock);
519
+ break;
520
+ } catch (error) {
521
+ if (error.code !== "EEXIST")
522
+ throw error;
523
+ insist(attempt === 0, "owner locked");
524
+ const before = await lstat(lock);
525
+ const raw = await readBoundedFile(lock, 4096);
526
+ let owner;
527
+ try {
528
+ owner = JSON.parse(raw.toString());
529
+ } catch {
530
+ throw new Error("Qoder recovery owner lock malformed");
531
+ }
532
+ insist(Number.isSafeInteger(owner.pid) && owner.pid > 0 && typeof owner.token === "string", "owner lock invalid");
533
+ const dead = () => {
534
+ try {
535
+ process.kill(owner.pid, 0);
536
+ return false;
537
+ } catch (e) {
538
+ return e.code === "ESRCH";
539
+ }
540
+ };
541
+ insist(dead(), "owner locked");
542
+ const now = await lstat(lock);
543
+ const content = await readBoundedFile(lock, 4096);
544
+ insist(now.ino === before.ino && now.dev === before.dev && content.equals(raw) && dead(), "owner changed or live");
545
+ await unlink(lock);
546
+ }
547
+ }
548
+ } finally {
549
+ await f.close().catch(() => {
550
+ });
551
+ await unlink(candidate).catch((error) => {
552
+ if (!missing(error)) throw error;
553
+ });
554
+ }
555
+ });
556
+ const owned = async () => {
557
+ insist(!closed, "store closed");
558
+ const owner = JSON.parse((await readBoundedFile(lock, 4096)).toString());
559
+ insist(owner.token === token && owner.pid === process.pid, "owner changed");
560
+ };
561
+ return {
562
+ async load() {
563
+ await owned();
564
+ let bytes;
565
+ try {
566
+ bytes = await readBoundedFile(path, MAX_CHECKPOINT);
567
+ } catch (error) {
568
+ if (missing(error)) return void 0;
569
+ throw error;
570
+ }
571
+ let value;
572
+ try {
573
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
574
+ } catch {
575
+ throw new Error("Qoder recovery malformed checkpoint");
576
+ }
577
+ validateRecoveryData(value, options);
578
+ return value;
579
+ },
580
+ async save(data) {
581
+ await owned();
582
+ validateRecoveryData(data, options);
583
+ const bytes = Buffer.from(JSON.stringify(data));
584
+ insist(bytes.length <= MAX_CHECKPOINT, "checkpoint exceeds 16 MiB");
585
+ await verifiedDirectory(directory);
586
+ await assertRegularOrMissing(path);
587
+ const temporary = path + "." + randomUUID() + ".pending";
588
+ const f = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
589
+ try {
590
+ await f.writeFile(bytes);
591
+ await f.sync();
592
+ await f.close();
593
+ await owned();
594
+ await verifiedDirectory(directory);
595
+ await assertRegularOrMissing(path);
596
+ await rename(temporary, path);
597
+ } finally {
598
+ await f.close().catch(() => {
599
+ });
600
+ await unlink(temporary).catch((error) => {
601
+ if (!missing(error)) throw error;
602
+ });
603
+ }
604
+ },
605
+ async close() {
606
+ if (closed) return;
607
+ await withGuard(async () => {
608
+ await owned();
609
+ await unlink(lock);
610
+ closed = true;
611
+ });
612
+ }
613
+ };
614
+ }
615
+
616
+ // src/history.ts
617
+ var MAX_HISTORY = 64 * 1024 * 1024;
618
+ var MAX_LINE = 1024 * 1024;
619
+ var MAX_UUIDS = 1e5;
620
+ function refuse(ok, why) {
621
+ if (!ok) throw new Error(`Qoder history ${why}`);
622
+ }
623
+ async function readHistorySnapshot(path) {
624
+ const rows = [];
625
+ let pending = Buffer.alloc(0), completeBytes = 0;
626
+ const decoder = new TextDecoder2("utf-8", { fatal: true });
627
+ const snapshot = await scanFileSnapshot(path, MAX_HISTORY, (chunk) => {
628
+ const bytes = Buffer.concat([pending, chunk]);
629
+ let start = 0;
630
+ for (let end = bytes.indexOf(10); end >= 0; end = bytes.indexOf(10, start)) {
631
+ refuse(end - start <= MAX_LINE, "complete line exceeds 1 MiB");
632
+ let row;
633
+ try {
634
+ row = JSON.parse(decoder.decode(bytes.subarray(start, end)));
635
+ } catch {
636
+ throw new Error("Qoder history malformed complete line");
637
+ }
638
+ refuse(row && typeof row === "object" && !Array.isArray(row), "malformed row");
639
+ rows.push(row);
640
+ completeBytes += end - start + 1;
641
+ start = end + 1;
642
+ }
643
+ pending = Buffer.from(bytes.subarray(start));
644
+ refuse(pending.length <= MAX_LINE, "partial line exceeds 1 MiB");
645
+ });
646
+ return { ...snapshot, rows, completeBytes };
647
+ }
648
+ function metadata(row, root) {
649
+ const type = row.type;
650
+ if (!["active-leaf", "workspace-directories", "worktree-state", "last-prompt"].includes(String(type))) return false;
651
+ refuse(row.sessionId === root, "metadata identity mismatch");
652
+ if (type === "active-leaf") refuse((row.leafUuid === null || address(row.leafUuid)) && typeof row.explicit === "boolean" && Number.isSafeInteger(row.timestamp), "metadata active-leaf malformed");
653
+ if (type === "workspace-directories") refuse(Array.isArray(row.directories) && row.directories.length <= 4096 && row.directories.every((x) => typeof x === "string"), "metadata directories malformed");
654
+ if (type === "worktree-state") refuse(row.worktreeSession === null || !!row.worktreeSession && typeof row.worktreeSession === "object" && !Array.isArray(row.worktreeSession), "metadata worktree malformed");
655
+ if (type === "last-prompt") refuse(typeof row.lastPrompt === "string", "metadata last-prompt malformed");
656
+ return true;
657
+ }
658
+ function validateHistoryRow(row, context) {
659
+ const { root, cwd, historyVersions, child } = context;
660
+ if (metadata(row, root)) return false;
661
+ refuse(row.sessionId === root && row.cwd === cwd && row.isSidechain === (child !== void 0) && (!child || row.agentId === child.agentId && row.parent_tool_use_id === child.toolUseId), "identity mismatch");
662
+ refuse(typeof row.version === "string" && historyVersions.includes(row.version), "version incompatible");
663
+ refuse(address(row.uuid), "UUID identity invalid");
664
+ if (row.isMeta === true && object(row.origin).kind === "task-notification") {
665
+ const content = object(row.message).content;
666
+ if (Array.isArray(content)) for (const block of content.map(object)) {
667
+ if (typeof block.text !== "string") continue;
668
+ const result = block.text.match(/<result>([\s\S]*)<\/result>/);
669
+ if (result) refuse(Buffer.byteLength(result[1]) <= MAX_RESULT, "background result exceeds 128 KiB");
670
+ }
671
+ }
672
+ return true;
673
+ }
674
+ var own2 = (record2, key) => Object.hasOwn(record2, key);
675
+ function admitRecoveryEvent(data, event) {
676
+ if (event.acpSessionId && event.acpSessionId !== data.root || !["tool_call", "tool_call_update"].includes(event.eventName)) return;
677
+ const payload = event.payload, input = object(payload.rawInput), toolId = payload.toolCallId;
678
+ const qoder = object(object(payload._meta).qoder);
679
+ if (payload.kind !== "think" || qoder.toolName !== "Agent" || typeof input.prompt !== "string") return;
680
+ const optionalTypeValid = input.subagent_type == null || typeof input.subagent_type === "string";
681
+ const marked = qoder.version === 1 && ["native-acp-patch", "sdk", "correction"].includes(String(qoder.source));
682
+ if (isSubworkId(toolId) && marked && optionalTypeValid && (input.run_in_background === void 0 || typeof input.run_in_background === "boolean") && !own2(data.subwork.calls, toolId)) {
683
+ refuse(data.subwork.count < 4096, "subwork call bound");
684
+ }
685
+ const backgroundId = address(toolId);
686
+ if (backgroundId && optionalTypeValid && input.run_in_background === true && !own2(data.background.calls, backgroundId)) {
687
+ refuse(data.background.callCount < MAX_TASKS, "background call bound");
688
+ }
689
+ if (event.eventName === "tool_call" && typeof toolId === "string" && (input.run_in_background === void 0 || input.run_in_background === false) && !own2(data.foreground.pending, toolId)) {
690
+ refuse(Object.keys(data.foreground.pending).length < 4096, "foreground call bound");
691
+ }
692
+ }
693
+ function admitRecoveryFact(data, fold, fact) {
694
+ const background = data.background;
695
+ if (fact.kind === "call" && !own2(background.calls, fact.toolUseId)) refuse(background.callCount < MAX_TASKS, "background call bound");
696
+ if (fact.kind === "launch" && fact.agentId && !background.launches.some(([id3]) => id3 === fact.toolUseId)) refuse(background.launches.length < MAX_TASKS, "background launch bound");
697
+ if (fact.kind === "terminal" && !background.terminals.some((t) => t.toolUseId === fact.toolUseId)) {
698
+ refuse(background.terminals.length < MAX_TASKS && Buffer.byteLength(fact.result) <= MAX_RESULT && background.resultBytes + Buffer.byteLength(fact.result) <= MAX_RESULTS, "background result bound");
699
+ }
700
+ if (fact.kind === "child" && fold.wantsProcess(fact.toolUseId)) {
701
+ const seen = own2(background.childRows, fact.toolUseId) ? background.childRows[fact.toolUseId] : void 0;
702
+ if (!seen?.includes(fact.uuid)) refuse((seen?.length ?? 0) < CHILD_ROW_LIMIT, "background child UUID bound");
703
+ }
704
+ }
705
+ async function bootstrapHistory(options) {
706
+ const { home, cwd, root, historyVersions, data } = options;
707
+ validateRecoveryData(data, { root, cwd, releaseId: data.releaseId });
708
+ refuse(historyVersions.length > 0, "version allowlist missing");
709
+ const path = historyPath(home, cwd, root);
710
+ refuse(path, "identity path invalid");
711
+ const rootSnapshot = await readHistorySnapshot(path);
712
+ const rootRows = rootSnapshot.rows.filter((row) => !metadata(row, root)), rootAssistantUuids = /* @__PURE__ */ new Set();
713
+ const rootSeen = /* @__PURE__ */ new Set();
714
+ const check = (row, child) => validateHistoryRow(row, { root, cwd, historyVersions, child });
715
+ const launches = /* @__PURE__ */ new Map(), agentCalls = /* @__PURE__ */ new Set(), backgroundCalls = /* @__PURE__ */ new Set(), terminalIds = /* @__PURE__ */ new Set();
716
+ let resultBytes = 0;
717
+ for (const row of rootRows) {
718
+ check(row);
719
+ if (!rootSeen.has(row.uuid)) {
720
+ refuse(rootSeen.size < MAX_UUIDS, "UUID count exceeds 100000");
721
+ rootSeen.add(row.uuid);
722
+ }
723
+ if (row.type === "assistant")
724
+ rootAssistantUuids.add(row.uuid);
725
+ const content = object(row.message).content;
726
+ if (Array.isArray(content))
727
+ for (const block of content.map(object)) {
728
+ if (row.type === "assistant" && block.type === "tool_use" && block.name === "Agent" && address(block.id)) {
729
+ agentCalls.add(block.id);
730
+ refuse(agentCalls.size <= 4096, "subwork call bound");
731
+ if (object(block.input).run_in_background === true) {
732
+ backgroundCalls.add(block.id);
733
+ refuse(backgroundCalls.size <= MAX_TASKS, "background call bound");
734
+ }
735
+ }
736
+ if (row.isMeta === true && object(row.origin).kind === "task-notification" && typeof block.text === "string") {
737
+ const match = block.text.match(/<result>([\s\S]*)<\/result>/);
738
+ if (match)
739
+ refuse(Buffer.byteLength(match[1]) <= MAX_RESULT, "background result exceeds 128 KiB");
740
+ }
741
+ }
742
+ const facts = rootRowFacts(row, { nativeSessionId: root, cwd, restoreCalls: true });
743
+ for (const fact of facts) {
744
+ if (fact.kind === "terminal" && !terminalIds.has(fact.toolUseId)) {
745
+ terminalIds.add(fact.toolUseId);
746
+ resultBytes += Buffer.byteLength(fact.result);
747
+ refuse(terminalIds.size <= MAX_TASKS && resultBytes <= MAX_RESULTS, "background result bound");
748
+ }
749
+ }
750
+ for (const fact of facts)
751
+ if (fact.kind === "launch" && fact.agentId) {
752
+ refuse(launches.has(fact.toolUseId) || launches.size < MAX_TASKS, "background task bound");
753
+ launches.set(fact.toolUseId, fact.agentId);
754
+ }
755
+ }
756
+ const childSnapshots = [];
757
+ const children = [];
758
+ let total = rootSnapshot.capturedBytes;
759
+ for (const [toolUseId, agentId] of launches) {
760
+ const childPath = join2(dirname2(path), root, "subagents", `agent-${agentId}.jsonl`);
761
+ let snapshot;
762
+ try {
763
+ snapshot = await readHistorySnapshot(childPath);
764
+ } catch (error) {
765
+ if (error.code === "ENOENT")
766
+ continue;
767
+ throw error;
768
+ }
769
+ childSnapshots.push({ toolUseId, agentId, path: childPath, snapshotBytes: snapshot.completeBytes, fileIdentity: snapshot.fileIdentity });
770
+ total += snapshot.capturedBytes;
771
+ refuse(total <= MAX_HISTORY, "indexed history exceeds 64 MiB");
772
+ const childRows = snapshot.rows.filter((row) => !metadata(row, root)), seen = /* @__PURE__ */ new Set();
773
+ for (const row of childRows) {
774
+ check(row, { toolUseId, agentId });
775
+ seen.add(row.uuid);
776
+ refuse(seen.size <= CHILD_ROW_LIMIT, "child UUID count exceeds 4096");
777
+ }
778
+ children.push({ toolUseId, agentId, rows: childRows });
779
+ }
780
+ const restored = structuredClone(data);
781
+ restored.background = { ...restored.background, restoreCalls: true, includeProcess: true };
782
+ const fold = new TerminalRepairFold(restored.background), subwork = new SubworkBoundary(root, restored.subwork), foreground = new ForegroundOutputBoundary(root, restored.foreground);
783
+ const consume = (event) => {
784
+ admitRecoveryEvent(restored, event);
785
+ foreground.accept(event);
786
+ fold.observe(event);
787
+ subwork.map(event);
788
+ };
789
+ const apply = (fact) => {
790
+ admitRecoveryFact(restored, fold, fact);
791
+ return fold.apply(fact);
792
+ };
793
+ const toolNames = /* @__PURE__ */ new Map();
794
+ const consumeRow = (row, parentToolUseId) => {
795
+ const content = object(row.message).content;
796
+ if (!Array.isArray(content))
797
+ return;
798
+ for (const block of content.map(object)) {
799
+ const name = typeof block.name === "string" ? block.name : void 0;
800
+ const toolId = address(block.id);
801
+ const meta = (toolName) => ({ qoder: { version: 1, source: parentToolUseId ? "correction" : "native-acp-patch", ...parentToolUseId ? { parentToolUseId, route: "child" } : {}, ...toolName ? { toolName } : {} } });
802
+ if (row.type === "assistant" && block.type === "tool_use" && toolId && name) {
803
+ toolNames.set(toolId, name);
804
+ consume({ eventName: "tool_call", payload: { toolCallId: toolId, kind: name === "Agent" ? "think" : "other", rawInput: object(block.input), status: "pending", _meta: meta(name) } });
805
+ } else if (row.type === "user" && block.type === "tool_result" && address(block.tool_use_id)) {
806
+ const toolUseId = block.tool_use_id;
807
+ consume({ eventName: "tool_call_update", payload: { toolCallId: toolUseId, status: block.is_error === true ? "failed" : "completed", rawOutput: row.toolUseResult ?? block.content, _meta: meta(toolNames.get(toolUseId)) } });
808
+ } else if (parentToolUseId && row.type === "assistant" && block.type === "text" && typeof block.text === "string") {
809
+ consume({ eventName: "agent_message_chunk", payload: { content: { type: "text", text: block.text }, _meta: meta() } });
810
+ }
811
+ }
812
+ };
813
+ const foldedRoot = /* @__PURE__ */ new Set();
814
+ for (const row of rootRows) {
815
+ if (foldedRoot.has(row.uuid))
816
+ continue;
817
+ foldedRoot.add(row.uuid);
818
+ consumeRow(row);
819
+ for (const fact of rootRowFacts(row, { nativeSessionId: root, cwd, restoreCalls: true }))
820
+ apply(fact);
821
+ }
822
+ for (const child of children) {
823
+ const seen = /* @__PURE__ */ new Set();
824
+ for (const row of child.rows) {
825
+ const uuid = row.uuid;
826
+ if (seen.has(uuid))
827
+ continue;
828
+ seen.add(uuid);
829
+ if (fold.wantsProcess(child.toolUseId)) {
830
+ if (!Object.hasOwn(restored.background.childRows, child.toolUseId) || !restored.background.childRows[child.toolUseId].includes(uuid)) consumeRow(row, child.toolUseId);
831
+ apply({ kind: "child", toolUseId: child.toolUseId, uuid, events: [] });
832
+ }
833
+ }
834
+ apply({ kind: "child_status", toolUseId: child.toolUseId, caughtUp: true });
835
+ }
836
+ for (const [toolUseId] of launches)
837
+ apply({ kind: "child_status", toolUseId, caughtUp: true });
838
+ for (const event of apply({ kind: "poll_end" }))
839
+ consume(event);
840
+ validateRecoveryData(restored, { root, cwd, releaseId: data.releaseId });
841
+ Object.assign(data, restored);
842
+ return { rootAssistantUuids, snapshotBytes: rootSnapshot.completeBytes, rootIdentity: rootSnapshot.fileIdentity, childSnapshots };
843
+ }
844
+
845
+ // src/background/backgroundTerminal.ts
846
+ import { dirname as dirname4, join as join3 } from "node:path";
847
+
848
+ // src/background/transcript.ts
849
+ import { constants as constants2 } from "node:fs";
850
+ import { lstat as lstat2, open as open2 } from "node:fs/promises";
851
+ import { dirname as dirname3 } from "node:path";
852
+ import { TextDecoder as TextDecoder3 } from "node:util";
853
+ var Transcript = class {
854
+ constructor(path, snapshot, onSize) {
855
+ this.path = path;
856
+ this.snapshot = snapshot;
857
+ this.onSize = onSize;
858
+ if (snapshot) this.identity = snapshot.fileIdentity;
859
+ }
860
+ path;
861
+ snapshot;
862
+ onSize;
863
+ offset = 0;
864
+ identity = "";
865
+ tail = Buffer.alloc(0);
866
+ decoder = new TextDecoder3("utf-8", { fatal: true });
867
+ /** Preflight all followed files' sizes before any poll derives new evidence. */
868
+ async measure() {
869
+ await verifiedDirectory(dirname3(this.path));
870
+ await assertRegularOrMissing(this.path);
871
+ const stat = await lstat2(this.path), identity = `${stat.dev}:${stat.ino}`;
872
+ if (!stat.isFile() || this.identity && this.identity !== identity || stat.size < this.offset || this.snapshot && stat.size < this.snapshot.snapshotBytes) throw new Error("Qoder history file replaced or truncated");
873
+ if (stat.size > MAX_HISTORY) throw new Error("Qoder history exceeds 64 MiB");
874
+ this.onSize?.(stat.size);
875
+ this.identity = identity;
876
+ }
877
+ /** Replay only the fixed captured prefix, silently, before following later appends. */
878
+ async bootstrap(consume) {
879
+ if (this.snapshot && this.offset < this.snapshot.snapshotBytes) {
880
+ await this.read(consume, this.snapshot.snapshotBytes - this.offset, this.snapshot.snapshotBytes);
881
+ if (this.offset !== this.snapshot.snapshotBytes || this.tail.length) throw new Error("Qoder history snapshot incomplete");
882
+ }
883
+ }
884
+ async read(consume, budget = 4 * 1024 * 1024, boundary) {
885
+ await verifiedDirectory(dirname3(this.path));
886
+ await assertRegularOrMissing(this.path);
887
+ const file = await open2(this.path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
888
+ try {
889
+ const before = await file.stat(), identity = `${before.dev}:${before.ino}`;
890
+ if (!before.isFile() || this.identity && identity !== this.identity || before.size < this.offset || this.snapshot && before.size < this.snapshot.snapshotBytes) throw new Error("Qoder history file replaced or truncated");
891
+ if (before.size > MAX_HISTORY) throw new Error("Qoder history exceeds 64 MiB");
892
+ this.onSize?.(before.size);
893
+ this.identity = identity;
894
+ const end = Math.min(before.size, this.offset + budget, boundary ?? Infinity);
895
+ while (this.offset < end) {
896
+ const chunk = Buffer.alloc(Math.min(64 * 1024, end - this.offset));
897
+ const { bytesRead } = await file.read(chunk, 0, chunk.length, this.offset);
898
+ if (!bytesRead) throw new Error("Qoder history changed while reading");
899
+ this.offset += bytesRead;
900
+ this.lines(chunk.subarray(0, bytesRead), consume);
901
+ }
902
+ const after = await file.stat(), named = await lstat2(this.path);
903
+ if (after.size < before.size || named.dev !== before.dev || named.ino !== before.ino || named.isSymbolicLink() || after.size === before.size && after.mtimeMs !== before.mtimeMs) throw new Error("Qoder history file replaced while reading");
904
+ await verifiedDirectory(dirname3(this.path));
905
+ return this.offset >= before.size && this.tail.length === 0;
906
+ } finally {
907
+ await file.close();
908
+ }
909
+ }
910
+ get established() {
911
+ return this.identity !== "";
912
+ }
913
+ lines(chunk, consume) {
914
+ const bytes = Buffer.concat([this.tail, chunk]);
915
+ let start = 0;
916
+ for (let end = bytes.indexOf(10); end >= 0; end = bytes.indexOf(10, start)) {
917
+ if (end - start > MAX_LINE) throw new Error("Qoder history complete line exceeds 1 MiB");
918
+ let row;
919
+ try {
920
+ row = JSON.parse(this.decoder.decode(bytes.subarray(start, end)));
921
+ } catch {
922
+ throw new Error("Qoder history malformed complete line");
923
+ }
924
+ if (!row || typeof row !== "object" || Array.isArray(row)) throw new Error("Qoder history malformed row");
925
+ consume(row);
926
+ start = end + 1;
927
+ }
928
+ this.tail = Buffer.from(bytes.subarray(start));
929
+ if (this.tail.length > MAX_LINE) throw new Error("Qoder history partial line exceeds 1 MiB");
930
+ }
931
+ };
932
+
933
+ // src/background/backgroundProcess.ts
934
+ var object3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
935
+ var id2 = (value) => typeof value === "string" && /^[a-zA-Z0-9_-]{1,160}$/.test(value);
936
+ var BackgroundProcess = class {
937
+ constructor(options) {
938
+ this.options = options;
939
+ this.transcript = new Transcript(options.path, options.snapshot, options.onSize);
940
+ }
941
+ options;
942
+ seen = /* @__PURE__ */ new Set();
943
+ tools = /* @__PURE__ */ new Map();
944
+ transcript;
945
+ bootstrapped = false;
946
+ measure() {
947
+ return this.transcript.measure();
948
+ }
949
+ get established() {
950
+ return this.transcript.established;
951
+ }
952
+ async poll(onRow = this.options.onRow) {
953
+ if (!this.bootstrapped) {
954
+ await this.transcript.bootstrap((row) => this.consume(row, () => {
955
+ }));
956
+ this.bootstrapped = true;
957
+ }
958
+ return this.transcript.read((row) => this.consume(row, onRow));
959
+ }
960
+ consume(row, onRow = this.options.onRow) {
961
+ const o = this.options;
962
+ if (!validateHistoryRow(row, {
963
+ root: o.sessionId,
964
+ cwd: o.cwd,
965
+ historyVersions: o.historyVersions,
966
+ child: { agentId: o.agentId, toolUseId: o.parentToolUseId }
967
+ })) return;
968
+ if (this.seen.has(row.uuid)) return;
969
+ if (this.seen.size >= 4096) throw new Error("Qoder history child UUID count exceeds 4096");
970
+ this.seen.add(row.uuid);
971
+ const content = object3(row.message).content;
972
+ if (!Array.isArray(content)) return;
973
+ const events = [];
974
+ for (const block of content.map(object3)) {
975
+ if (row.type === "assistant" && block.type === "tool_use" && id2(block.id) && typeof block.name === "string") {
976
+ if (this.tools.has(block.id)) continue;
977
+ if (this.tools.size >= 4096) throw new Error("Qoder history child tool count exceeds 4096");
978
+ const input = object3(block.input);
979
+ this.tools.set(block.id, { name: block.name, input });
980
+ this.derive(events, "tool_call", {
981
+ toolCallId: block.id,
982
+ name: block.name,
983
+ title: block.name,
984
+ kind: block.name === "Read" ? "read" : block.name === "Bash" ? "execute" : block.name === "Agent" ? "think" : "other",
985
+ rawInput: input,
986
+ status: "pending"
987
+ }, block.name);
988
+ } else if (row.type === "user" && block.type === "tool_result" && id2(block.tool_use_id)) {
989
+ const tool = this.tools.get(block.tool_use_id);
990
+ if (!tool) continue;
991
+ const raw = row.toolUseResult ?? block.content;
992
+ const text = typeof block.content === "string" ? block.content : JSON.stringify(block.content ?? "");
993
+ this.derive(events, "tool_call_update", {
994
+ toolCallId: block.tool_use_id,
995
+ name: tool.name,
996
+ status: block.is_error === true ? "failed" : "completed",
997
+ rawOutput: raw,
998
+ content: [{ type: "content", content: { type: "text", text } }]
999
+ }, tool.name);
1000
+ } else if (row.type === "assistant" && (block.type === "text" && typeof block.text === "string" || block.type === "thinking" && typeof block.thinking === "string")) {
1001
+ this.derive(
1002
+ events,
1003
+ block.type === "text" ? "agent_message_chunk" : "agent_thought_chunk",
1004
+ { content: { type: "text", text: block.type === "text" ? block.text : block.thinking } }
1005
+ );
1006
+ }
1007
+ }
1008
+ if (onRow) {
1009
+ if (events.length > 0) onRow(row.uuid, events);
1010
+ } else for (const event of events) this.options.emit(event);
1011
+ }
1012
+ derive(events, eventName, payload, toolName) {
1013
+ events.push({ eventName, payload: { ...payload, _meta: { qoder: {
1014
+ version: 1,
1015
+ source: "correction",
1016
+ parentToolUseId: this.options.parentToolUseId,
1017
+ route: "child",
1018
+ ...toolName ? { toolName } : {}
1019
+ } } } });
1020
+ }
1021
+ };
1022
+
1023
+ // src/background/backgroundTerminal.ts
1024
+ var BackgroundSideReader = class {
1025
+ constructor(options) {
1026
+ this.options = options;
1027
+ this.restoring = options.restoreCalls === true;
1028
+ this.transcript = new Transcript(options.path, options.snapshot ? { snapshotBytes: options.snapshot.snapshotBytes, fileIdentity: options.snapshot.rootIdentity } : void 0, (size) => this.recordSize(options.path, size));
1029
+ for (const child of options.snapshot?.childSnapshots ?? []) {
1030
+ this.launches.set(child.toolUseId, child.agentId);
1031
+ this.sizes.set(child.path, child.snapshotBytes);
1032
+ }
1033
+ }
1034
+ options;
1035
+ launches = /* @__PURE__ */ new Map();
1036
+ sizes = /* @__PURE__ */ new Map();
1037
+ seen = /* @__PURE__ */ new Set();
1038
+ processes = /* @__PURE__ */ new Map();
1039
+ transcript;
1040
+ closed = false;
1041
+ restoring;
1042
+ bootstrapped = false;
1043
+ async read(demand, sink) {
1044
+ if (this.closed || !this.restoring && !demand.active()) return;
1045
+ try {
1046
+ await this.transcript.measure();
1047
+ for (const [id3, agentId] of this.launches) {
1048
+ if (!demand.wantsProcess(id3)) continue;
1049
+ const process2 = this.processFor(id3, agentId);
1050
+ try {
1051
+ await process2.measure();
1052
+ } catch (error) {
1053
+ if (error.code !== "ENOENT" || process2.established) throw error;
1054
+ }
1055
+ }
1056
+ if (!this.bootstrapped) {
1057
+ await this.transcript.bootstrap((row) => this.consume(row, () => {
1058
+ }));
1059
+ this.bootstrapped = true;
1060
+ }
1061
+ const caughtUp = await this.transcript.read((row) => this.consume(row, sink));
1062
+ if (caughtUp) this.restoring = false;
1063
+ if (!this.closed) {
1064
+ await this.pollProcesses(demand, sink);
1065
+ if (!this.closed && caughtUp) sink({ kind: "poll_end" });
1066
+ }
1067
+ } catch (error) {
1068
+ if (error.code === "ENOENT" && !this.transcript.established && this.options.allowMissing) return;
1069
+ this.close();
1070
+ throw error;
1071
+ }
1072
+ }
1073
+ close() {
1074
+ this.closed = true;
1075
+ this.launches.clear();
1076
+ this.processes.clear();
1077
+ }
1078
+ consume(row, sink) {
1079
+ if (this.closed) return;
1080
+ const { nativeSessionId, cwd, historyVersions } = this.options;
1081
+ if (!validateHistoryRow(row, { root: nativeSessionId, cwd, historyVersions })) return;
1082
+ if (this.seen.has(row.uuid)) return;
1083
+ if (this.seen.size >= MAX_UUIDS) throw new Error("Qoder history UUID count exceeds 100000");
1084
+ this.seen.add(row.uuid);
1085
+ for (const fact of rootRowFacts(row, { nativeSessionId, cwd, restoreCalls: this.options.restoreCalls === true })) {
1086
+ if (fact.kind === "launch" && fact.agentId && !this.launches.has(fact.toolUseId)) {
1087
+ if (this.launches.size >= MAX_TASKS) throw new Error("Qoder history background task bound");
1088
+ this.launches.set(fact.toolUseId, fact.agentId);
1089
+ }
1090
+ sink(fact);
1091
+ }
1092
+ }
1093
+ recordSize(path, bytes) {
1094
+ this.sizes.set(path, bytes);
1095
+ if ([...this.sizes.values()].reduce((a, b) => a + b, 0) > MAX_HISTORY) throw new Error("Qoder indexed history exceeds 64 MiB");
1096
+ }
1097
+ processFor(id3, agentId) {
1098
+ let process2 = this.processes.get(id3);
1099
+ if (!process2) {
1100
+ const path = join3(dirname4(this.options.path), this.options.nativeSessionId, "subagents", `agent-${agentId}.jsonl`);
1101
+ process2 = new BackgroundProcess({
1102
+ path,
1103
+ historyVersions: this.options.historyVersions,
1104
+ sessionId: this.options.nativeSessionId,
1105
+ cwd: this.options.cwd,
1106
+ agentId,
1107
+ parentToolUseId: id3,
1108
+ snapshot: this.options.snapshot?.childSnapshots.find((child) => child.toolUseId === id3),
1109
+ onSize: (bytes) => this.recordSize(path, bytes),
1110
+ emit: () => {
1111
+ }
1112
+ });
1113
+ this.processes.set(id3, process2);
1114
+ }
1115
+ return process2;
1116
+ }
1117
+ async pollProcesses(demand, sink) {
1118
+ for (const [id3, agentId] of this.launches) {
1119
+ if (this.closed || !demand.wantsProcess(id3)) continue;
1120
+ const process2 = this.processFor(id3, agentId);
1121
+ let caughtUp;
1122
+ try {
1123
+ caughtUp = await process2.poll((uuid, events) => {
1124
+ if (!this.closed) sink({ kind: "child", toolUseId: id3, uuid, events });
1125
+ });
1126
+ } catch (error) {
1127
+ if (error.code !== "ENOENT" || process2.established) throw error;
1128
+ caughtUp = true;
1129
+ }
1130
+ if (!this.closed) sink({ kind: "child_status", toolUseId: id3, caughtUp });
1131
+ }
1132
+ }
1133
+ };
1134
+
1135
+ // src/background/poller.ts
1136
+ var SIDE_POLL_MS = 1e3;
1137
+ var address2 = (v) => v.length > 0 && v.length <= 256 && !/[\x00-\x1f\x7f/\\]/.test(v);
1138
+ function historyPath(home, cwd, sessionId) {
1139
+ if (!address2(sessionId) || !isAbsolute(cwd) || !isAbsolute(home)) return void 0;
1140
+ return join4(home, ".qoder", "projects", resolve2(cwd).replace(/[^a-zA-Z0-9]/g, "-"), sessionId + ".jsonl");
1141
+ }
1142
+ var BackgroundPoller = class {
1143
+ constructor(options) {
1144
+ this.options = options;
1145
+ this.fold = new TerminalRepairFold(options.data?.background ?? newTerminalRepairData({ nativeSessionId: options.root, restoreCalls: options.restoreCalls, includeProcess: true }));
1146
+ const path = historyPath(options.home, options.cwd, options.root);
1147
+ if (!path) throw new Error("Qoder history identity path invalid");
1148
+ this.reader = new BackgroundSideReader({ ...options, path, nativeSessionId: options.root });
1149
+ }
1150
+ options;
1151
+ fold;
1152
+ reader;
1153
+ timer;
1154
+ inFlight;
1155
+ closed = false;
1156
+ observe(event) {
1157
+ if (!this.closed) this.fold.observe(event);
1158
+ }
1159
+ pollOnce() {
1160
+ if (this.closed) return Promise.resolve();
1161
+ if (this.inFlight) return this.inFlight;
1162
+ const read = async () => {
1163
+ if (this.closed) return;
1164
+ const events = [];
1165
+ await this.reader.read(this.fold, (fact) => {
1166
+ if (this.closed) return;
1167
+ if (this.options.data) admitRecoveryFact(this.options.data, this.fold, fact);
1168
+ events.push(...this.fold.apply(fact));
1169
+ });
1170
+ if (!this.closed) await this.options.emit(events);
1171
+ };
1172
+ this.inFlight = (this.options.serialize ? this.options.serialize(read) : read()).catch((error) => {
1173
+ this.halt();
1174
+ this.options.fail(error instanceof Error ? error : new Error(String(error)));
1175
+ throw error;
1176
+ }).finally(() => {
1177
+ this.inFlight = void 0;
1178
+ });
1179
+ return this.inFlight;
1180
+ }
1181
+ start() {
1182
+ if (this.closed || this.timer) return;
1183
+ this.timer = setInterval(() => {
1184
+ const polling = this.pollOnce();
1185
+ void polling.catch(() => {
1186
+ });
1187
+ return polling;
1188
+ }, SIDE_POLL_MS);
1189
+ this.timer.unref();
1190
+ }
1191
+ halt() {
1192
+ this.closed = true;
1193
+ if (this.timer) clearInterval(this.timer);
1194
+ this.timer = void 0;
1195
+ this.reader.close();
1196
+ }
1197
+ async stop() {
1198
+ this.halt();
1199
+ await this.inFlight?.catch(() => {
1200
+ });
1201
+ }
1202
+ };
1203
+
1204
+ // src/events.ts
1205
+ function toSessionUpdate(event, rootSessionId) {
1206
+ return {
1207
+ jsonrpc: "2.0",
1208
+ method: "session/update",
1209
+ params: { sessionId: event.acpSessionId ?? rootSessionId, update: { sessionUpdate: event.eventName, ...event.payload } }
1210
+ };
1211
+ }
1212
+
1213
+ // src/boundary.ts
1214
+ async function createBoundary(options) {
1215
+ const store = await openRecoveryStore(options);
1216
+ try {
1217
+ const saved = await store.load();
1218
+ if (options.mode === "new" && saved) throw new Error("Qoder new session already has recovery state; use load or resume");
1219
+ const data = saved ?? newRecoveryData(options.root, options.cwd, options.releaseId);
1220
+ const index = options.mode === "new" ? void 0 : await bootstrapHistory({ ...options, data });
1221
+ await store.save(data);
1222
+ const foreground = new ForegroundOutputBoundary(options.root, data.foreground);
1223
+ const subwork = new SubworkBoundary(options.root, data.subwork);
1224
+ const terminal2 = new TerminalRepairFold(data.background);
1225
+ const display = options.mode === "load" ? newRecoveryData(options.root, options.cwd, options.releaseId) : void 0;
1226
+ const displayForeground = display ? new ForegroundOutputBoundary(options.root, display.foreground) : void 0;
1227
+ const displaySubwork = display ? new SubworkBoundary(options.root, display.subwork) : void 0;
1228
+ let active = false, closing = false, fatalError;
1229
+ let serial = Promise.resolve(), closingPromise;
1230
+ let poller;
1231
+ let cleanup;
1232
+ const release = () => cleanup ??= store.close();
1233
+ const fatal = (error) => {
1234
+ if (fatalError) return;
1235
+ fatalError = error;
1236
+ poller?.halt();
1237
+ void serial.then(release).catch((releaseError) => {
1238
+ options.fail(releaseError instanceof Error ? releaseError : new Error(String(releaseError)));
1239
+ });
1240
+ options.fail(error);
1241
+ };
1242
+ const serialize = (operation) => {
1243
+ if (closing) return Promise.reject(fatalError ?? new Error("Qoder boundary closed"));
1244
+ const result = serial.then(async () => {
1245
+ if (fatalError) throw fatalError;
1246
+ try {
1247
+ return await operation();
1248
+ } catch (error) {
1249
+ const failure = error instanceof Error ? error : new Error(String(error));
1250
+ fatal(failure);
1251
+ throw failure;
1252
+ }
1253
+ });
1254
+ serial = result.then(() => {
1255
+ }, () => {
1256
+ });
1257
+ return result;
1258
+ };
1259
+ const params = (events) => events.map((event) => toSessionUpdate(event, options.root).params);
1260
+ poller = new BackgroundPoller({
1261
+ ...options,
1262
+ data,
1263
+ snapshot: index,
1264
+ restoreCalls: true,
1265
+ allowMissing: options.mode === "new",
1266
+ serialize,
1267
+ fail: fatal,
1268
+ emit: async (events) => {
1269
+ const output = [];
1270
+ for (const event of events) {
1271
+ admitRecoveryEvent(data, event);
1272
+ output.push(...subwork.map(event));
1273
+ }
1274
+ await store.save(data);
1275
+ for (const p of params(output)) {
1276
+ if (closing || fatalError) break;
1277
+ await options.send(p);
1278
+ }
1279
+ }
1280
+ });
1281
+ return {
1282
+ map(update) {
1283
+ return serialize(async () => {
1284
+ if (typeof update.sessionUpdate !== "string") throw new Error("Qoder boundary session update missing kind");
1285
+ const { sessionUpdate, ...payload } = update;
1286
+ const event = { eventName: sessionUpdate, payload };
1287
+ let events;
1288
+ if (display && !active) {
1289
+ admitRecoveryEvent(display, event);
1290
+ events = displayForeground.accept(event) ? displaySubwork.map(event) : [];
1291
+ } else {
1292
+ admitRecoveryEvent(data, event);
1293
+ terminal2.observe(event);
1294
+ events = foreground.accept(event) ? subwork.map(event) : [];
1295
+ }
1296
+ await store.save(data);
1297
+ return params(events);
1298
+ });
1299
+ },
1300
+ historyMessage(message) {
1301
+ if (fatalError) throw fatalError;
1302
+ if (closing) throw new Error("Qoder boundary closed");
1303
+ return message.type === "assistant" && message.sessionId === options.root && message.cwd === options.cwd && typeof message.uuid === "string" && index?.rootAssistantUuids.has(message.uuid) && message.parent_tool_use_id === void 0 ? { ...message, parent_tool_use_id: null } : message;
1304
+ },
1305
+ activate() {
1306
+ return serialize(async () => {
1307
+ if (active) return;
1308
+ await store.save(data);
1309
+ active = true;
1310
+ poller.start();
1311
+ });
1312
+ },
1313
+ endTurn() {
1314
+ return serialize(async () => {
1315
+ foreground.endTurn();
1316
+ displayForeground?.endTurn();
1317
+ await store.save(data);
1318
+ });
1319
+ },
1320
+ close() {
1321
+ if (closingPromise) return closingPromise;
1322
+ closing = true;
1323
+ poller.halt();
1324
+ closingPromise = (async () => {
1325
+ await serial;
1326
+ await poller.stop();
1327
+ await release();
1328
+ })();
1329
+ return closingPromise;
1330
+ }
1331
+ };
1332
+ } catch (error) {
1333
+ await store.close();
1334
+ throw error;
1335
+ }
1336
+ }
1337
+
1338
+ // src/steering.ts
1339
+ var SteeringValidationError = class extends Error {
1340
+ constructor(message) {
1341
+ super(message);
1342
+ this.name = "SteeringValidationError";
1343
+ }
1344
+ };
1345
+ function validatedPrompt(params) {
1346
+ const priority = params._meta?.steering?.priority;
1347
+ if (priority !== void 0 && priority !== "next")
1348
+ throw new SteeringValidationError("Only next steering priority is supported");
1349
+ if (!Array.isArray(params.prompt) || params.prompt.length === 0)
1350
+ throw new SteeringValidationError("Steering requires nonempty text content");
1351
+ const texts = [];
1352
+ for (const block of params.prompt) {
1353
+ if (block === null || typeof block !== "object" || block.type !== "text" || typeof block.text !== "string")
1354
+ throw new SteeringValidationError("Steering supports text content only");
1355
+ texts.push(block.text);
1356
+ }
1357
+ const prompt = texts.join("\n");
1358
+ if (prompt.trim().length === 0)
1359
+ throw new SteeringValidationError("Steering requires nonempty text content");
1360
+ if (Buffer.byteLength(prompt, "utf8") > 128 * 1024)
1361
+ throw new SteeringValidationError("Steering text exceeds 128 KiB UTF-8");
1362
+ return prompt;
1363
+ }
1364
+ var NextSteering = class {
1365
+ constructor(root, hooks) {
1366
+ this.root = root;
1367
+ this.hooks = hooks;
1368
+ }
1369
+ root;
1370
+ hooks;
1371
+ running = false;
1372
+ pending = /* @__PURE__ */ new Set();
1373
+ begin() {
1374
+ if (this.running) throw new Error("A native prompt is already running");
1375
+ if (this.pending.size) throw new Error("Prior native prompt still has owned pending inputs");
1376
+ this.running = true;
1377
+ }
1378
+ accept(params) {
1379
+ const prompt = validatedPrompt(params);
1380
+ if (!this.running) return { outcome: "promptRequired" };
1381
+ if (this.pending.size >= 32)
1382
+ throw new SteeringValidationError("Steering has 32 pending inputs");
1383
+ const uuid = this.hooks.uuid();
1384
+ if (this.pending.has(uuid)) throw new Error("Native input UUID collision");
1385
+ this.pending.add(uuid);
1386
+ try {
1387
+ this.hooks.enqueue({
1388
+ uuid,
1389
+ sessionId: this.root,
1390
+ prompt,
1391
+ priority: "next",
1392
+ commandMode: "prompt",
1393
+ origin: { kind: "human" }
1394
+ });
1395
+ } catch (enqueueError) {
1396
+ try {
1397
+ this.hooks.remove(/* @__PURE__ */ new Set([uuid]));
1398
+ } catch (removalError) {
1399
+ throw new AggregateError([enqueueError, removalError], "Native enqueue and owned rollback failed");
1400
+ }
1401
+ this.pending.delete(uuid);
1402
+ throw enqueueError;
1403
+ }
1404
+ return { outcome: "injected" };
1405
+ }
1406
+ consumed(uuids) {
1407
+ for (const uuid of uuids) this.pending.delete(uuid);
1408
+ }
1409
+ /** Native attachment cancellation is not successful consumption. */
1410
+ cancelled(uuids) {
1411
+ if (!uuids.some((uuid) => this.pending.has(uuid))) return false;
1412
+ this.cancel();
1413
+ return true;
1414
+ }
1415
+ tryFinish() {
1416
+ if (this.pending.size) return false;
1417
+ this.running = false;
1418
+ return true;
1419
+ }
1420
+ cancel() {
1421
+ this.running = false;
1422
+ if (!this.pending.size) return;
1423
+ const owned = new Set(this.pending);
1424
+ this.hooks.remove(owned);
1425
+ for (const uuid of owned) this.pending.delete(uuid);
1426
+ }
1427
+ };
1428
+
1429
+ // src/entry.ts
1430
+ var owners = /* @__PURE__ */ new Map();
1431
+ function initialize(result, releaseId, version, bindings) {
1432
+ for (const name of ["RequestError", "uuid", "enqueue", "remove", "select", "consume"])
1433
+ if (typeof bindings[name] !== "function")
1434
+ throw new Error(`Native ACP runtime binding unavailable: ${name}`);
1435
+ if (new bindings.RequestError(-32602, "probe").name !== "RequestError")
1436
+ throw new Error("Native ACP RequestError binding mismatch");
1437
+ if (result.agentInfo?.name !== "qoder-cli" || result.agentInfo?.version !== version || !result.agentCapabilities?.sessionCapabilities?.resume)
1438
+ throw new Error("Native ACP initialize identity/capability mismatch");
1439
+ if (result._meta?.axom !== void 0 || result._meta?.steering !== void 0)
1440
+ throw new Error("Conflicting ACP metadata");
1441
+ return { ...result, _meta: { ...result._meta, axom: { cachedReconnect: "resume", releaseId }, steering: { supported: true, timings: ["next"] } } };
1442
+ }
1443
+ function finish(root, child) {
1444
+ return child || (owners.get(root)?.tryFinish() ?? true);
1445
+ }
1446
+ function terminal(root, child, verdict) {
1447
+ if (child)
1448
+ return;
1449
+ const owner = owners.get(root);
1450
+ if (!owner)
1451
+ return;
1452
+ const pending = !owner.tryFinish();
1453
+ owner.cancel();
1454
+ if (pending && verdict.reason === "end_turn")
1455
+ throw new Error("Native ACP stopped before accepted next input was consumed");
1456
+ }
1457
+ function cancelled(root, child, uuids) {
1458
+ if (!child && owners.get(root)?.cancelled(uuids))
1459
+ throw new Error("Native ACP accepted next input was cancelled before consumption");
1460
+ }
1461
+ function consumed(root, child, uuids) {
1462
+ if (!child)
1463
+ owners.get(root)?.consumed(uuids);
1464
+ }
1465
+ function createBridge(options) {
1466
+ const { root, bindings } = options;
1467
+ if (owners.has(root))
1468
+ throw new Error("Native ACP root already owned");
1469
+ const steering = new NextSteering(root, { uuid: bindings.uuid, enqueue: bindings.enqueue, remove: (ids) => {
1470
+ bindings.remove((input) => input.sessionId === root && ids.has(input.uuid));
1471
+ } });
1472
+ owners.set(root, steering);
1473
+ let closed = false, fatal, closing;
1474
+ const origins = /* @__PURE__ */ new Map();
1475
+ const fail = (error) => {
1476
+ fatal ??= error;
1477
+ steering.cancel();
1478
+ options.fail(error);
1479
+ };
1480
+ const ready = createBoundary({ ...options, home: homedir(), fail }).catch((error) => {
1481
+ fatal = error;
1482
+ if (owners.get(root) === steering)
1483
+ owners.delete(root);
1484
+ throw error;
1485
+ });
1486
+ ready.catch(() => {
1487
+ });
1488
+ const check = () => {
1489
+ if (fatal)
1490
+ throw fatal;
1491
+ if (closed)
1492
+ throw new Error("Native ACP session closed");
1493
+ };
1494
+ const id3 = (value) => typeof value === "string" && value.length > 0 && value.length <= 256 && !/[\x00-\x1f\x7f]/.test(value);
1495
+ const observe = (message) => {
1496
+ if (message.type !== "assistant" || !Array.isArray(message.message?.content))
1497
+ return;
1498
+ const parent = message.parent_tool_use_id;
1499
+ if (parent !== null && !id3(parent))
1500
+ return;
1501
+ for (const block of message.message.content) {
1502
+ if (block?.type !== "tool_use" || !id3(block.id) || !id3(block.name))
1503
+ continue;
1504
+ const prior = origins.get(block.id);
1505
+ if (origins.has(block.id)) {
1506
+ if (prior && (prior.parent !== parent || prior.name !== block.name))
1507
+ origins.set(block.id, null);
1508
+ continue;
1509
+ }
1510
+ if (origins.size >= 4096)
1511
+ throw new Error("Native ACP tool origin bound exceeded");
1512
+ origins.set(block.id, { parent, name: block.name });
1513
+ }
1514
+ };
1515
+ return {
1516
+ async ready() {
1517
+ await ready;
1518
+ check();
1519
+ },
1520
+ begin() {
1521
+ check();
1522
+ steering.begin();
1523
+ },
1524
+ accept(params) {
1525
+ check();
1526
+ try {
1527
+ return steering.accept(params);
1528
+ } catch (error) {
1529
+ if (error instanceof SteeringValidationError)
1530
+ throw new bindings.RequestError(-32602, error.message);
1531
+ throw error;
1532
+ }
1533
+ },
1534
+ cancel() {
1535
+ steering.cancel();
1536
+ },
1537
+ observe,
1538
+ async history(message) {
1539
+ check();
1540
+ observe((await ready).historyMessage(message));
1541
+ },
1542
+ async send(update, message) {
1543
+ check();
1544
+ if (message && id3(message.parent_tool_use_id))
1545
+ update = { ...update, _meta: { ...update._meta, qoder: { ...update._meta?.qoder, version: 1, source: "native-acp-patch", parentToolUseId: message.parent_tool_use_id, route: "child" } } };
1546
+ if (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") {
1547
+ const origin = origins.get(update.toolCallId);
1548
+ if (origin)
1549
+ update = { ...update, _meta: { ...update._meta, qoder: { ...update._meta?.qoder, version: 1, source: "native-acp-patch", toolName: origin.name, ...origin.parent ? { parentToolUseId: origin.parent, route: "child" } : {} } } };
1550
+ }
1551
+ for (const value of await (await ready).map(update)) {
1552
+ check();
1553
+ await options.send(value);
1554
+ }
1555
+ },
1556
+ async activate() {
1557
+ check();
1558
+ await (await ready).activate();
1559
+ },
1560
+ async endTurn() {
1561
+ steering.cancel();
1562
+ await (await ready).endTurn();
1563
+ },
1564
+ close() {
1565
+ if (closing)
1566
+ return closing;
1567
+ closed = true;
1568
+ steering.cancel();
1569
+ closing = (async () => {
1570
+ try {
1571
+ await (await ready).close();
1572
+ } finally {
1573
+ origins.clear();
1574
+ if (owners.get(root) === steering)
1575
+ owners.delete(root);
1576
+ }
1577
+ })();
1578
+ closing.catch(() => {
1579
+ });
1580
+ return closing;
1581
+ }
1582
+ };
1583
+ }
1584
+ export {
1585
+ cancelled,
1586
+ consumed,
1587
+ createBridge,
1588
+ finish,
1589
+ initialize,
1590
+ terminal
1591
+ };