@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,61 @@
1
+ /**
2
+ * The ACP SDK's own zod schemas, used synchronously as a schema library (spec 2026-10-11 §5.4: "SDK
3
+ * 只作 schema 库(同步 zod 校验/剥离)").
4
+ *
5
+ * ## Why these schemas, and why loaded this way
6
+ *
7
+ * The live path validates and strips every inbound message with the SDK's generated schemas
8
+ * (`dist/schema/zod.gen.js` — e.g. `ClientApp` parses `session/update` through `zSessionNotification`,
9
+ * `session/request_permission` through `zRequestPermissionRequest`). The processor has to apply the
10
+ * **same** schemas or moving a session onto it changes payload bytes (analysis B F2). Copying them
11
+ * would drift on the next SDK bump; importing them is the only honest option.
12
+ *
13
+ * The package's `exports` map does not expose `zod.gen.js`, so a bare subpath import is refused. It is
14
+ * reached instead by resolving the package entry and taking its sibling — a plain file path, which
15
+ * `exports` does not police — and loaded with `require` (synchronous `require(esm)`, Node ≥ 22.12;
16
+ * `engines.node` is `>=24`). This happens once, at module load: nothing here runs inside `step`.
17
+ * `guard.test.ts` allows exactly this file to import `node:module`.
18
+ *
19
+ * ## The pin
20
+ *
21
+ * `dist/schema/zod.gen.js` is the SDK's **private** layout, so `package.json` pins the SDK to one
22
+ * exact version (no range) and `sdkSchemas.test.ts` checks the installed version is that pin and the
23
+ * file resolves — an SDK bump has to be a deliberate change that re-runs that test.
24
+ */
25
+ import { createRequire } from 'node:module';
26
+ const require = createRequire(import.meta.url);
27
+ /** The absolute path of the SDK's generated zod module (a sibling of its `acp.js` entry). */
28
+ export function sdkZodGenPath(resolve = require.resolve) {
29
+ const entry = resolve('@agentclientprotocol/sdk');
30
+ const suffix = 'acp.js';
31
+ if (!entry.endsWith(suffix)) {
32
+ throw new Error(`Unexpected @agentclientprotocol/sdk entry point ${entry}: the processor reads the SDK's private dist/schema/zod.gen.js next to dist/acp.js; re-check the pinned SDK version`);
33
+ }
34
+ return `${entry.slice(0, -suffix.length)}schema/zod.gen.js`;
35
+ }
36
+ function loadZodGen() {
37
+ const path = sdkZodGenPath();
38
+ try {
39
+ return require(path);
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Cannot load the ACP SDK's zod schemas from ${path} (private SDK layout; re-check the pinned SDK version): ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+ const zodGen = loadZodGen();
46
+ function schema(name) {
47
+ const found = zodGen[name];
48
+ if (!found || typeof found.safeParse !== 'function')
49
+ throw new Error(`@agentclientprotocol/sdk has no schema ${name}`);
50
+ return found;
51
+ }
52
+ /** Inbound (agent → client) request params, by method — the schemas `clientRequestSpecs` uses. */
53
+ export const INBOUND_REQUEST_SCHEMAS = {
54
+ 'session/request_permission': schema('zRequestPermissionRequest'),
55
+ 'elicitation/create': schema('zCreateElicitationRequest'),
56
+ };
57
+ /** Inbound notification params, by method — the schemas `clientNotificationSpecs` uses. */
58
+ export const INBOUND_NOTIFICATION_SCHEMAS = {
59
+ 'session/update': schema('zSessionNotification'),
60
+ 'elicitation/complete': schema('zCompleteElicitationNotification'),
61
+ };
@@ -0,0 +1,496 @@
1
+ /** Session turns, permission and elicitation cards, and callback snapshots over checkpointed state. Root and child permission origins are verified before a card is emitted; callback generations derive from the process spawn ID. */
2
+ import { buildElicitationRequestedEvent, buildPermissionRequestedEvent, elicitationFieldKeys, isBlank, permissionRequestId, } from '../permission.js';
3
+ import { validateWireEvent } from '../validate.js';
4
+ import { isRecord, nextSeq } from './emit.js';
5
+ import { inboundKey, settlementErrorCode } from './jsonrpc.js';
6
+ import { sameCause } from './record.js';
7
+ import { publishCallbacks, resetRuntime } from './runtime.js';
8
+ import { getOwn, initialCallbacks, initialMessage, PENDING_PERMISSION_LIMIT, SETTLED_PERMISSION_LIMIT, } from './state.js';
9
+ import { emitSessionEvent, endHistory, flushMessage, flushOutOfTurnMessage } from './updates.js';
10
+ /** `agentSessions.ts`'s admission refusal text, verbatim. */
11
+ export const ADMISSION_REFUSED = 'Workstation is not accepting new work';
12
+ /** The agent answered `session/prompt` with `stopReason: 'refusal'` and no words of its own. */
13
+ export const PROMPT_REFUSED = 'The agent refused this prompt.';
14
+ /** `AcpSession.prompt`'s overlap refusal text, verbatim. */
15
+ export const OVERLAPPING_TURN = 'An exact platform turn is still running; overlapping prompts are unavailable';
16
+ /** What the SDK rejects pending requests with when the adapter's stdout ends (`jsonrpc.js` `close()`). */
17
+ export const CONNECTION_CLOSED = 'ACP connection closed';
18
+ /** A prompt for a connection with no bound session (Task 17b binds it from the open response). */
19
+ export const NO_SESSION = 'No ACP session is open on this connection';
20
+ /** `ClientApp` closes the connection on a batch; that reason is kept as the turn's error. */
21
+ export const BATCH_CLOSE_REASON = 'JSON-RPC batches are not supported on this connection';
22
+ /** Pending `'form'` elicitations kept at once (new bound, mirrors permissions' 64). */
23
+ export const PENDING_ELICITATION_LIMIT = 64;
24
+ const PERMISSION_CANCELLED = { outcome: { outcome: 'cancelled' } };
25
+ const ELICITATION_DECLINE = { action: 'decline' };
26
+ const ELICITATION_CANCEL = { action: 'cancel' };
27
+ /** Connected = a running process whose JSON-RPC connection has not closed. */
28
+ export function connected(state) {
29
+ return state.process.status === 'running' && state.closed === undefined;
30
+ }
31
+ /** The one setter of `rootSessionId` — `control.ts` calls it on the `session/new|load|resume` response. */
32
+ export function bindRootSession(state, sessionId) {
33
+ state.rootSessionId = sessionId;
34
+ }
35
+ /** Every unanswered card id — `agent_opened.pendingRequestIds` (spec §5.5.4). */
36
+ export function pendingRequestIds(state) {
37
+ return [...Object.keys(state.permissions), ...Object.keys(state.elicitations)];
38
+ }
39
+ /**
40
+ * A new adapter process is a new connection: today's per-connection, per-`AcpSession` and
41
+ * per-`LiveSession` fields start fresh (a respawn is a new session there too). `seq` is not reset.
42
+ */
43
+ export function resetForSpawn(state) {
44
+ state.rootSessionId = null;
45
+ state.open = { status: 'idle' };
46
+ state.configOptions = null;
47
+ state.history = null;
48
+ state.mapperScopes = {};
49
+ state.message = initialMessage();
50
+ state.steers = {};
51
+ state.steered = [];
52
+ state.asyncTasks = {};
53
+ state.draftSubagents = { children: {}, overflow: false };
54
+ resetRuntime(state);
55
+ state.turns = [];
56
+ state.exactExecutionUnconfirmed = false;
57
+ state.backgroundWriterUnconfirmed = false;
58
+ state.permissionsInterrupted = false;
59
+ state.permissions = {};
60
+ state.elicitations = {};
61
+ state.settledPermissions = [];
62
+ state.callbacks = initialCallbacks();
63
+ state.permissionOrigins = {};
64
+ state.permissionParents = {};
65
+ state.unresolvedPermissionOrigins = {};
66
+ }
67
+ /**
68
+ * The connection closed: unanswered cards can no longer be answered (nothing to write them to) — the
69
+ * read loop's `drainPendingApprovals`, so the callback snapshot goes incomplete and is published.
70
+ */
71
+ export function dropCards(state, out) {
72
+ state.permissions = {};
73
+ state.elicitations = {};
74
+ state.callbacks.complete = false;
75
+ publishCallbacks(state, out);
76
+ }
77
+ export function closeErrorText(state) {
78
+ return state.closed === BATCH_CLOSE_REASON ? BATCH_CLOSE_REASON : CONNECTION_CLOSED;
79
+ }
80
+ // ---- turns ------------------------------------------------------------------------------------
81
+ /**
82
+ * The short `agent_turn_done` `agentSessions.ts` builds itself for a prompt that never reached the
83
+ * session (admission refused, `prompt()` threw): no `nativeSessionId`, no `stopReason`.
84
+ */
85
+ function refuseTurn(state, out, error, platformTurnId) {
86
+ out.frame({
87
+ t: 'agent_turn_done',
88
+ seq: nextSeq(state),
89
+ status: 'failed',
90
+ error,
91
+ ...(platformTurnId !== undefined ? { platformTurnId } : {}),
92
+ });
93
+ }
94
+ const STOP_REASONS = {
95
+ end_turn: 'end_turn',
96
+ max_tokens: 'max_tokens',
97
+ max_turn_requests: 'max_turns',
98
+ // No `refusal`: a refusal result settles 'failed' (see `settlePrompt`), and a completed turn
99
+ // never carries it.
100
+ };
101
+ /**
102
+ * End turn `index` — `AcpSession.finishTurn` + `agentSessions.ts`'s `onAgentTurnDone` frame, in one.
103
+ * The frame keeps that method's key order (`agent_turn_done` minus `sessionRef`); `agentRefused` and
104
+ * `errorCode` are carried for the projection, which turns them into `refusalFields` (Task 17b).
105
+ */
106
+ function finishTurn(state, out, index, status, finish) {
107
+ const [turn] = state.turns.splice(index, 1);
108
+ if (!turn)
109
+ return;
110
+ // Before the turn's own frame, on every path that ends a turn (`flushMessageCompleted`).
111
+ flushMessage(state, out);
112
+ const resolved = turn.interrupted ? 'interrupted' : status;
113
+ if (resolved === 'interrupted') {
114
+ state.permissionsInterrupted = true;
115
+ drainCards(state, out);
116
+ }
117
+ const frame = { t: 'agent_turn_done', seq: nextSeq(state), status: resolved };
118
+ if (turn.platformTurnId !== undefined)
119
+ frame.platformTurnId = turn.platformTurnId;
120
+ if (finish.executionEnded && state.turns.length === 0 && !state.backgroundWriterUnconfirmed) {
121
+ frame.executionEnded = true;
122
+ state.exactExecutionUnconfirmed = false;
123
+ }
124
+ const stopReason = typeof finish.stopReason === 'string' ? STOP_REASONS[finish.stopReason] : undefined;
125
+ if (resolved === 'completed' && stopReason !== undefined)
126
+ frame.stopReason = stopReason;
127
+ frame.nativeSessionId = state.rootSessionId !== null && state.rootSessionId.length > 0 ? state.rootSessionId : null;
128
+ frame.error = resolved === 'failed' && finish.error ? finish.error : null;
129
+ if (resolved === 'failed' && finish.agentRefused) {
130
+ frame.agentRefused = true;
131
+ if (finish.errorCode !== undefined)
132
+ frame.errorCode = finish.errorCode;
133
+ }
134
+ out.frame(frame);
135
+ }
136
+ /** `agent_prompt{blocksRef, platformTurnId?, admitted?}`. */
137
+ export function onPromptNote(state, note, out) {
138
+ const { blocksRef, platformTurnId, admitted } = note;
139
+ if (typeof blocksRef !== 'string' || blocksRef === '' || (platformTurnId !== undefined && typeof platformTurnId !== 'string')
140
+ || (admitted !== undefined && typeof admitted !== 'boolean')) {
141
+ state.diagnostics.droppedNotes += 1;
142
+ return;
143
+ }
144
+ if (admitted === false) {
145
+ refuseTurn(state, out, ADMISSION_REFUSED, platformTurnId);
146
+ return;
147
+ }
148
+ if (!connected(state) || state.rootSessionId === null) {
149
+ // `AcpSession.prompt`'s `readLoopDead` branch: registered as a turn and finished on the spot, so
150
+ // "one prompt, one terminal result" holds here too.
151
+ state.turns.push({ requestId: -1, cause: { o: out.o, i: -1 }, interrupted: false, sent: false,
152
+ ...(platformTurnId !== undefined ? { platformTurnId } : {}) });
153
+ finishTurn(state, out, state.turns.length - 1, 'failed', { error: connected(state) ? NO_SESSION : closeErrorText(state) });
154
+ return;
155
+ }
156
+ if (state.exactExecutionUnconfirmed || (state.turns.length > 0 && (platformTurnId !== undefined
157
+ || state.turns.some((turn) => turn.platformTurnId !== undefined)))) {
158
+ refuseTurn(state, out, OVERLAPPING_TURN, platformTurnId);
159
+ return;
160
+ }
161
+ state.permissionsInterrupted = false;
162
+ // A turn ends any load history still open: its output is live, never history (T19 writes
163
+ // `historyEnd` before admitting a prompt; this is the backstop).
164
+ endHistory(state, out, true);
165
+ flushOutOfTurnMessage(state, out);
166
+ const { id, cause } = out.request('session/prompt', { sessionId: state.rootSessionId, prompt: [] }, {
167
+ fill: { blocksRef },
168
+ ...(platformTurnId !== undefined ? { annotation: { platformTurnId } } : {}),
169
+ });
170
+ const pending = { id, method: 'session/prompt', kind: 'prompt', cause };
171
+ state.outbound[String(id)] = pending;
172
+ state.turns.push({ requestId: id, cause, interrupted: false, sent: false, ...(platformTurnId !== undefined ? { platformTurnId } : {}) });
173
+ if (platformTurnId !== undefined)
174
+ state.exactExecutionUnconfirmed = true;
175
+ }
176
+ /**
177
+ * A `down` record: if it is a queued prompt's line, the turn is now on the agent, and its
178
+ * `platformTurnId` is the one the record's annotation carries.
179
+ */
180
+ export function onDown(state, record) {
181
+ const annotation = record.annotation;
182
+ if (!annotation)
183
+ return;
184
+ const turn = state.turns.find((candidate) => sameCause(annotation.cause, candidate.cause));
185
+ if (!turn || turn.sent)
186
+ return;
187
+ turn.sent = true;
188
+ if (typeof annotation.platformTurnId === 'string')
189
+ turn.platformTurnId = annotation.platformTurnId;
190
+ else
191
+ delete turn.platformTurnId;
192
+ }
193
+ /**
194
+ * The measured `_meta` key a refusal *result* carries its would-be error response under: a JSON
195
+ * string of the JSON-RPC error the agent chose not to send (`codebuddy-code` 2.171.0; shape-read,
196
+ * same rule as `acpClient`'s `_codebuddy.ai/authUrl`).
197
+ */
198
+ const REFUSAL_ERROR_META_KEY = 'codebuddy.ai/errorMessage';
199
+ function refusalErrorDetail(result) {
200
+ if (!isRecord(result) || !isRecord(result._meta))
201
+ return undefined;
202
+ const raw = result._meta[REFUSAL_ERROR_META_KEY];
203
+ if (typeof raw !== 'string')
204
+ return undefined;
205
+ try {
206
+ const parsed = JSON.parse(raw);
207
+ if (!isRecord(parsed) || typeof parsed.message !== 'string')
208
+ return undefined;
209
+ return { ...(typeof parsed.code === 'number' ? { code: parsed.code } : {}), message: parsed.message };
210
+ }
211
+ catch {
212
+ return undefined;
213
+ }
214
+ }
215
+ /** A `session/prompt` request settled: its turn ends on this record. */
216
+ export function settlePrompt(state, entry, settlement, out) {
217
+ const index = state.turns.findIndex((turn) => turn.requestId === entry.id);
218
+ if (index < 0)
219
+ return;
220
+ switch (settlement.outcome) {
221
+ case 'result': {
222
+ const stopReason = isRecord(settlement.result) ? settlement.result.stopReason : undefined;
223
+ // ACP's own `cancelled` forces `interrupted` even when no interrupt was sent (a backstop).
224
+ if (stopReason === 'cancelled') {
225
+ finishTurn(state, out, index, 'interrupted', { stopReason, executionEnded: true });
226
+ return;
227
+ }
228
+ // Measured (codebuddy-code 2.171.0): an agent that needs credentials answers `session/prompt`
229
+ // with a *result* `stopReason: 'refusal'` — zero content, no JSON-RPC error — which used to
230
+ // settle 'completed' and render as nothing at all. A refusal result settles exactly like the
231
+ // agent's own refusal below ('failed' + `agentRefused`), with the agent's detail read out of
232
+ // the measured `_meta` key; a code-less detail stays an ordinary failure card server-side,
233
+ // while `-32000` beside the observed auth methods is what turns it into the sign-in card.
234
+ if (stopReason === 'refusal') {
235
+ const detail = refusalErrorDetail(settlement.result);
236
+ finishTurn(state, out, index, 'failed', {
237
+ agentRefused: true, executionEnded: true,
238
+ error: detail?.message ?? PROMPT_REFUSED, ...(detail?.code !== undefined ? { errorCode: detail.code } : {}),
239
+ });
240
+ return;
241
+ }
242
+ finishTurn(state, out, index, 'completed', { stopReason, executionEnded: true });
243
+ return;
244
+ }
245
+ case 'error': {
246
+ // The one exit where the agent itself answered (`TurnResult.agentRefused`).
247
+ const errorCode = settlementErrorCode(settlement);
248
+ finishTurn(state, out, index, 'failed', {
249
+ error: settlement.error.message, agentRefused: true, errorCode, executionEnded: errorCode !== undefined,
250
+ });
251
+ return;
252
+ }
253
+ case 'closed':
254
+ finishTurn(state, out, index, 'failed', { error: closeErrorText(state) });
255
+ return;
256
+ }
257
+ }
258
+ /**
259
+ * `agent_prompt_failed{requestId, error}` — the executor could not write a prompt's line (its blocks
260
+ * were gone after a restart, or the write failed). Ignored once the line is in raw: then the agent
261
+ * has it and its response ends the turn. The prompt never reached the agent, which is proof its
262
+ * execution ended (`TurnResult.executionEnded`).
263
+ */
264
+ export function onPromptFailedNote(state, note, out) {
265
+ const { requestId, error } = note;
266
+ const entry = typeof requestId === 'number' ? state.outbound[String(requestId)] : undefined;
267
+ const index = entry ? state.turns.findIndex((turn) => turn.requestId === entry.id) : -1;
268
+ if (!entry || entry.kind !== 'prompt' || index < 0 || state.turns[index].sent || typeof error !== 'string') {
269
+ state.diagnostics.droppedNotes += 1;
270
+ return;
271
+ }
272
+ delete state.outbound[String(entry.id)];
273
+ finishTurn(state, out, index, 'failed', { error, executionEnded: true });
274
+ }
275
+ /**
276
+ * `agent_interrupt{}` — `AcpSession.interrupt`: every turn is marked interrupted (its result will say
277
+ * so whatever the agent answers), later permission requests are refused until the next prompt, every
278
+ * pending card is answered "cancelled", then `session/cancel` goes out.
279
+ */
280
+ export function onInterruptNote(state, out) {
281
+ for (const turn of state.turns)
282
+ turn.interrupted = true;
283
+ state.permissionsInterrupted = true;
284
+ drainCards(state, out);
285
+ if (connected(state) && state.rootSessionId !== null)
286
+ out.notify('session/cancel', { sessionId: state.rootSessionId });
287
+ }
288
+ /** The connection is gone: every turn left ends `failed` (`finishAllTurns`). */
289
+ export function finishAllTurns(state, out) {
290
+ while (state.turns.length > 0)
291
+ finishTurn(state, out, 0, 'failed', { error: closeErrorText(state) });
292
+ }
293
+ // ---- cards ------------------------------------------------------------------------------------
294
+ function respond(state, out, rpcKey, result) {
295
+ const inbound = state.inbound[rpcKey];
296
+ if (!inbound)
297
+ return;
298
+ delete state.inbound[rpcKey];
299
+ if (connected(state))
300
+ out.result(inbound.id, result);
301
+ }
302
+ /** `AcpSession.settleApproval`'s bookkeeping: remembered with its reason, or — past the bound — the snapshot goes incomplete. */
303
+ function rememberSettledPermission(state, requestId, reason) {
304
+ if (state.settledPermissions.length < SETTLED_PERMISSION_LIMIT)
305
+ state.settledPermissions.push({ requestId, reason });
306
+ else
307
+ state.callbacks.complete = false;
308
+ }
309
+ /** Settle one card and answer the agent; `publish: false` inside a drain, which publishes once for all. */
310
+ function settlePermission(state, out, requestId, result, reason, publish = true) {
311
+ const entry = state.permissions[requestId];
312
+ if (!entry)
313
+ return;
314
+ delete state.permissions[requestId];
315
+ rememberSettledPermission(state, requestId, reason);
316
+ if (publish)
317
+ publishCallbacks(state, out);
318
+ respond(state, out, entry.rpcKey, result);
319
+ }
320
+ function settleElicitation(state, out, requestId, result) {
321
+ const entry = state.elicitations[requestId];
322
+ if (!entry)
323
+ return;
324
+ delete state.elicitations[requestId];
325
+ respond(state, out, entry.rpcKey, result);
326
+ }
327
+ /**
328
+ * `drainPendingApprovals` + `drainPendingElicitations`: nobody will answer these any more. The
329
+ * callback snapshot goes incomplete and is published once (`drainPendingApprovals` publishes before
330
+ * it resolves the cards).
331
+ */
332
+ function drainCards(state, out) {
333
+ state.callbacks.complete = false;
334
+ publishCallbacks(state, out);
335
+ for (const requestId of Object.keys(state.permissions))
336
+ settlePermission(state, out, requestId, PERMISSION_CANCELLED, 'canceled', false);
337
+ for (const requestId of Object.keys(state.elicitations))
338
+ settleElicitation(state, out, requestId, ELICITATION_CANCEL);
339
+ }
340
+ /** A request answered without a card. */
341
+ function refuseRequest(state, out, id, result) {
342
+ state.diagnostics.refusedRequests += 1;
343
+ out.result(id, result);
344
+ return undefined;
345
+ }
346
+ /** `AcpSession.registerPendingApproval`'s field rule. */
347
+ function badCardField(value) {
348
+ // `Buffer.byteLength` without `Buffer`: UTF-8 length of a JS string.
349
+ let bytes = 0;
350
+ for (const char of value) {
351
+ const code = char.codePointAt(0);
352
+ bytes += code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
353
+ }
354
+ return !value.trim() || bytes > 256 || /[\x00-\x1f\x7f]/.test(value);
355
+ }
356
+ /**
357
+ * `session/request_permission` (params already through the SDK's zod schema) — `registerPermissionHandlers`
358
+ * → `handleRequestPermission` → `registerPendingApproval` → `emitEvent`, in their order. Returns the
359
+ * card id if a card now waits for the user (the request stays held), `undefined` if it was answered.
360
+ */
361
+ export function onPermissionRequest(state, id, params, out) {
362
+ const sessionId = params.sessionId;
363
+ if (typeof sessionId !== 'string' || !sessionId.trim())
364
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
365
+ const options = Array.isArray(params.options) ? params.options : [];
366
+ if (options.length === 0)
367
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
368
+ if (state.process.status !== 'running')
369
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
370
+ const requestId = permissionRequestId(state.process.spawn.spawnId, id);
371
+ const rpcRequestId = String(id);
372
+ const event = buildPermissionRequestedEvent({
373
+ requestId, rpcRequestId, sessionId, toolCall: params.toolCall, options: options,
374
+ });
375
+ if (validateWireEvent(event))
376
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
377
+ const origin = sessionId;
378
+ // Inside a load's history window the root is not bound yet, but the session being loaded is it
379
+ // (today's attached session already knows its id).
380
+ const root = state.rootSessionId ?? state.history?.rootSessionId ?? null;
381
+ if (origin !== root && !getOwn(state.permissionOrigins, origin))
382
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
383
+ if (state.permissionsInterrupted)
384
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
385
+ if ([requestId, origin, rpcRequestId].some(badCardField) || Object.keys(state.permissions).length >= PENDING_PERMISSION_LIMIT
386
+ || state.permissions[requestId] !== undefined || state.settledPermissions.some((settled) => settled.requestId === requestId)) {
387
+ // `registerPendingApproval`'s refusal: the snapshot can no longer vouch for every card.
388
+ state.callbacks.complete = false;
389
+ publishCallbacks(state, out);
390
+ return refuseRequest(state, out, id, PERMISSION_CANCELLED);
391
+ }
392
+ const rpcKey = inboundKey(id);
393
+ state.permissions[requestId] = { requestId, rpcKey, origin, rpcRequestId, optionIds: options.map((option) => option.optionId) };
394
+ publishCallbacks(state, out);
395
+ const payload = event.payload;
396
+ emitSessionEvent(state, out, { eventName: event.eventName, payload, acpSessionId: origin });
397
+ return requestId;
398
+ }
399
+ /**
400
+ * `elicitation/create` — `handleCreateElicitation` on a session connection (no `present`, so `'url'`
401
+ * declines) → `handleFormElicitation` → `registerPendingElicitation`. Returns the card id, as
402
+ * {@link onPermissionRequest} does.
403
+ */
404
+ export function onElicitationRequest(state, id, params, out) {
405
+ if (params.mode !== 'form')
406
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
407
+ const requestId = String(id);
408
+ if (!requestId)
409
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
410
+ const request = params;
411
+ if (elicitationFieldKeys(request.requestedSchema).length === 0)
412
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
413
+ const event = buildElicitationRequestedEvent(request, requestId);
414
+ if (validateWireEvent(event))
415
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
416
+ if (typeof request.message === 'string' && isBlank(request.message)) {
417
+ // Legible, just not collectible: raise it, then decline (`handleFormElicitation`).
418
+ emitSessionEvent(state, out, event);
419
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
420
+ }
421
+ if (state.elicitations[requestId] === undefined && Object.keys(state.elicitations).length >= PENDING_ELICITATION_LIMIT) {
422
+ return refuseRequest(state, out, id, ELICITATION_DECLINE);
423
+ }
424
+ // Same id twice replaces the earlier card, as `pendingElicitations.set` does.
425
+ state.elicitations[requestId] = { requestId, rpcKey: inboundKey(id) };
426
+ emitSessionEvent(state, out, event);
427
+ return requestId;
428
+ }
429
+ /**
430
+ * The agent sent `$/cancel_request` for one of its requests: a card waiting on it is settled the way
431
+ * the handler's abort listener settles it (permission → cancelled, elicitation → `cancel`). The card
432
+ * the server shows is not retracted, as today.
433
+ */
434
+ export function onInboundCancelled(state, rpcKey, out) {
435
+ const card = state.inbound[rpcKey]?.card;
436
+ if (card === undefined)
437
+ return;
438
+ if (state.permissions[card]?.rpcKey === rpcKey)
439
+ settlePermission(state, out, card, PERMISSION_CANCELLED, 'canceled');
440
+ else if (state.elicitations[card]?.rpcKey === rpcKey)
441
+ settleElicitation(state, out, card, ELICITATION_CANCEL);
442
+ }
443
+ /** A low-level `rpc.respond` answered a carded request: the card goes with it (settled as answered). */
444
+ export function forgetCard(state, rpcKey, card, out) {
445
+ if (card === undefined)
446
+ return;
447
+ if (state.permissions[card]?.rpcKey === rpcKey) {
448
+ delete state.permissions[card];
449
+ rememberSettledPermission(state, card, 'answered');
450
+ publishCallbacks(state, out);
451
+ }
452
+ else if (state.elicitations[card]?.rpcKey === rpcKey) {
453
+ delete state.elicitations[card];
454
+ }
455
+ }
456
+ /** `agent_answer{requestId, outcome}` — `AcpSession.answer`; an unknown id is ignored (counted). */
457
+ export function onAnswerNote(state, note, out) {
458
+ const { requestId, outcome } = note;
459
+ const entry = typeof requestId === 'string' ? state.permissions[requestId] : undefined;
460
+ if (!entry || typeof outcome !== 'string' || !connected(state)) {
461
+ state.diagnostics.droppedNotes += 1;
462
+ return;
463
+ }
464
+ settlePermission(state, out, entry.requestId, entry.optionIds.includes(outcome)
465
+ ? { outcome: { outcome: 'selected', optionId: outcome } }
466
+ : PERMISSION_CANCELLED, 'answered');
467
+ }
468
+ /**
469
+ * `agent_answer_undeliverable{requestId}` — a permission card the server cannot be shown: the send-time
470
+ * projection dropped it (a child session's card for a peer older than `AGENT_WIRE_SCOPED_ORIGIN_VERSION`),
471
+ * so the daemon logs this note and the agent is answered "cancelled", as `onAgentEvent` answers it
472
+ * today. Driven by a note, so a replay answers it again from the log, never from the connection.
473
+ */
474
+ export function onUndeliverableNote(state, note, out) {
475
+ const { requestId } = note;
476
+ const entry = typeof requestId === 'string' ? getOwn(state.permissions, requestId) : undefined;
477
+ if (!entry || !connected(state)) {
478
+ state.diagnostics.droppedNotes += 1;
479
+ return;
480
+ }
481
+ settlePermission(state, out, entry.requestId, PERMISSION_CANCELLED, 'answered');
482
+ }
483
+ /**
484
+ * `agent_elicitation_answer{requestId, action, content?}` — `agentSessions.ts`'s narrowing (an action
485
+ * other than accept/decline/cancel declines) + `AcpSession.answerElicitation`.
486
+ */
487
+ export function onElicitationAnswerNote(state, note, out) {
488
+ const { requestId, action, content } = note;
489
+ const entry = typeof requestId === 'string' ? state.elicitations[requestId] : undefined;
490
+ if (!entry || typeof action !== 'string' || !connected(state) || (content !== undefined && !isRecord(content))) {
491
+ state.diagnostics.droppedNotes += 1;
492
+ return;
493
+ }
494
+ const known = action === 'accept' || action === 'decline' || action === 'cancel';
495
+ settleElicitation(state, out, entry.requestId, known ? { action, ...(content ? { content } : {}) } : ELICITATION_DECLINE);
496
+ }
@@ -0,0 +1,67 @@
1
+ /** State history: 2 — open-session layer; 3 — callback snapshots and adapter fold data;
2
+ * 4 — polled process state; 5 — adapter fold data and retired optional state removed.
3
+ * Schemas before 4 remain unreadable; schema 4 is upgraded by checkpoint.ts. */
4
+ export const PROCESSOR_STATE_SCHEMA = 5;
5
+ export const SETTLED_HISTORY_LIMIT = 16;
6
+ export const SETTLED_PERMISSION_LIMIT = 64;
7
+ export const PENDING_PERMISSION_LIMIT = 64;
8
+ export const UNRESOLVED_ORIGIN_LIMIT = 1024;
9
+ export const STEERED_HISTORY_LIMIT = 64;
10
+ /** Unfinished async tasks tracked per connection, all sessions together. */
11
+ export const ASYNC_TASK_LIMIT = 1024;
12
+ export function initialRuntime() {
13
+ return { revision: 0, available: true };
14
+ }
15
+ export function initialCallbacks() {
16
+ return { complete: true, rootSessionId: null };
17
+ }
18
+ export function initialMessage() {
19
+ return { open: false, messageId: null, outOfTurn: false };
20
+ }
21
+ export function initialState() {
22
+ return {
23
+ schema: PROCESSOR_STATE_SCHEMA,
24
+ offset: -1,
25
+ t: 0,
26
+ process: { status: 'none' },
27
+ initialize: { status: 'idle' },
28
+ outbound: {},
29
+ inbound: {},
30
+ rootSessionId: null,
31
+ seq: 0,
32
+ turns: [],
33
+ exactExecutionUnconfirmed: false,
34
+ backgroundWriterUnconfirmed: false,
35
+ permissionsInterrupted: false,
36
+ permissions: {},
37
+ elicitations: {},
38
+ settledPermissions: [],
39
+ callbacks: initialCallbacks(),
40
+ permissionOrigins: {},
41
+ permissionParents: {},
42
+ unresolvedPermissionOrigins: {},
43
+ open: { status: 'idle' },
44
+ configOptions: null,
45
+ history: null,
46
+ mapperScopes: {},
47
+ message: initialMessage(),
48
+ steers: {},
49
+ steered: [],
50
+ draftSubagents: { children: {}, overflow: false },
51
+ asyncTasks: {},
52
+ activity: null,
53
+ runtime: initialRuntime(),
54
+ diagnostics: {
55
+ orphanResponses: 0, protocolErrors: 0, ignoredLines: 0, unknownNotes: 0, droppedNotes: 0, refusedRequests: 0,
56
+ rejectedRelations: 0, droppedUpdates: 0, invalidEvents: 0, untrackedAsyncTasks: 0, settled: [],
57
+ },
58
+ };
59
+ }
60
+ /** Set `record[key]` as an own property: agent-chosen keys (`__proto__`) stay plain keys. */
61
+ export function putOwn(record, key, value) {
62
+ Object.defineProperty(record, key, { value, enumerable: true, writable: true, configurable: true });
63
+ }
64
+ /** `record[key]` if it is an own property. */
65
+ export function getOwn(record, key) {
66
+ return Object.hasOwn(record, key) ? record[key] : undefined;
67
+ }