@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,173 @@
1
+ import {readdir, readFile} from 'node:fs/promises';
2
+ import {homedir} from 'node:os';
3
+ import {join} from 'node:path';
4
+
5
+ const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
6
+ const text = value => typeof value === 'string' ? value : '';
7
+ const terminalStates = new Map([['completed', 'completed'], ['failed', 'failed'], ['killed', 'cancelled'], ['aborted', 'cancelled'], ['lost', 'disconnected'], ['cancelled', 'cancelled']]);
8
+ const terminal = value => terminalStates.get(value);
9
+ const field = (body, name) => body.split('\n\n', 1)[0].match(new RegExp(`^${name}: ([^\\r\\n]+)$`, 'm'))?.[1];
10
+ const notification = (sessionId, update) => ({jsonrpc: '2.0', method: 'session/update', params: {sessionId, update}});
11
+ const outputText = update => typeof update.rawOutput === 'string' ? update.rawOutput : (update.content ?? [])
12
+ .filter(block => block.type === 'content' && block.content?.type === 'text').map(block => block.content.text).join('\n');
13
+
14
+ export class KimiSubagents {
15
+ constructor(emit, {home = process.env.KIMI_CODE_HOME ?? join(homedir(), '.kimi-code')} = {}) {
16
+ this.emit = emit;
17
+ this.home = home;
18
+ this.enabled = false;
19
+ this.requests = new Map();
20
+ this.sessions = new Map();
21
+ }
22
+
23
+ fromClient(message) {
24
+ if (!record(message)) return;
25
+ if (message.method === 'initialize') {
26
+ const capabilities = message.params?.clientCapabilities;
27
+ this.enabled = record(capabilities?.subagents) || capabilities?._meta?.jetbrains?.air?.capabilities?.includes('nativeSubagentSessions') === true;
28
+ }
29
+ if (message.id !== undefined && ['initialize', 'session/prompt', 'session/delete', 'session/close'].includes(message.method)) this.requests.set(message.id, {method: message.method, sessionId: message.params?.sessionId});
30
+ if (message.method === 'session/load') this.sessions.delete(message.params?.sessionId);
31
+ }
32
+
33
+ session(id) {
34
+ let session = this.sessions.get(id);
35
+ if (!session) {
36
+ session = {calls: new Map(), agents: new Map()};
37
+ this.sessions.set(id, session);
38
+ }
39
+ return session;
40
+ }
41
+
42
+ send(sessionId, update) {
43
+ this.emit(notification(sessionId, update));
44
+ }
45
+
46
+ spawn(sessionId, session, callId, spec, index) {
47
+ const known = spec.resume && session.agents.get(spec.resume);
48
+ const nativeCallId = callId.replace(/^\d+:/, '');
49
+ const child = {id: known?.id ?? `kimi-agent:${sessionId}:${nativeCallId}:${index}`, name: spec.name, task: spec.task, callId, done: false};
50
+ if (!known) this.send(sessionId, {sessionUpdate: 'subagent_spawned', subagentSessionId: child.id, name: child.name, task: child.task, capabilities: {}});
51
+ else this.send(sessionId, {sessionUpdate: 'subagent_state_update', subagentSessionId: child.id, _meta: {axom: {state: 'running'}}});
52
+ return child;
53
+ }
54
+
55
+ start(sessionId, session, call, update) {
56
+ const args = update.rawInput;
57
+ if (!record(args) || call.children.length) return;
58
+ const name = text(args.subagent_type) || 'coder';
59
+ const specs = call.kind === 'AgentSwarm'
60
+ ? [...Object.entries(record(args.resume_agent_ids) ? args.resume_agent_ids : {}).map(([resume, task]) => ({resume, task: text(task), name})),
61
+ ...(Array.isArray(args.items) ? args.items : []).filter(item => typeof item === 'string').map(item => ({name: `${name}: ${item}`, task: text(args.prompt_template).replaceAll('{{item}}', item), item}))]
62
+ : [{name, task: text(args.prompt), resume: text(args.resume)}];
63
+ call.args = args;
64
+ call.children = specs.slice(0, 128).map((spec, index) => ({...this.spawn(sessionId, session, call.id, spec, index), item: spec.item}));
65
+ }
66
+
67
+ finish(sessionId, child, state, body) {
68
+ if (child.done) return;
69
+ child.done = true;
70
+ if (body) this.send(child.id, {sessionUpdate: 'agent_message_chunk', content: {type: 'text', text: body}});
71
+ this.send(sessionId, {sessionUpdate: 'subagent_state_update', subagentSessionId: child.id, state});
72
+ }
73
+
74
+ complete(sessionId, session, call, update) {
75
+ if (!call.children.length) {
76
+ call.children = [this.spawn(sessionId, session, call.id, {name: call.kind, task: ''}, 0)];
77
+ }
78
+ const body = outputText(update);
79
+ if (call.kind === 'AgentSwarm') {
80
+ const results = [...body.matchAll(/<subagent\b([^>]*)>([\s\S]*?)<\/subagent>/g)];
81
+ const attribute = (attributes, name) => attributes.match(new RegExp(`(?:^| )${name}="([^"]*)"`))?.[1];
82
+ for (const [index, child] of call.children.entries()) {
83
+ const result = results[index];
84
+ const agentId = result && attribute(result[1], 'agent_id');
85
+ if (agentId) session.agents.set(agentId, child);
86
+ this.finish(sessionId, child, result ? terminal(attribute(result[1], 'outcome')) ?? 'failed' : 'failed', result?.[2] ?? body);
87
+ }
88
+ return;
89
+ }
90
+ const child = call.children[0];
91
+ const agentId = field(body, 'agent_id');
92
+ if (agentId) session.agents.set(agentId, child);
93
+ const taskId = field(body, 'task_id');
94
+ if (field(body, 'status') === 'running' && taskId && /^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-z]{8}$/.test(taskId)) {
95
+ child.background = {taskId, agentId};
96
+ return;
97
+ }
98
+ const state = field(body, 'stop_reason') === 'cancelled' ? 'cancelled' : terminal(field(body, 'status')) ?? (update.status === 'failed' ? 'failed' : 'completed');
99
+ const summary = body.match(/\n\[summary\]\n([\s\S]*?)(?:\n\nresume_hint:|$)/)?.[1];
100
+ this.finish(sessionId, child, state, summary ?? body);
101
+ }
102
+
103
+ fromAgent(message) {
104
+ if (!record(message)) return false;
105
+ if (!message.method && message.id !== undefined) {
106
+ const request = this.requests.get(message.id);
107
+ this.requests.delete(message.id);
108
+ if (['session/delete', 'session/close'].includes(request?.method) && !message.error) this.sessions.delete(request.sessionId);
109
+ if (this.enabled && request?.method === 'initialize' && message.result?.agentCapabilities) {
110
+ if (record(message.result.agentCapabilities.sessionCapabilities?.subagents)) {
111
+ this.enabled = false;
112
+ return false;
113
+ }
114
+ message.result.agentCapabilities.sessionCapabilities ??= {};
115
+ message.result.agentCapabilities.sessionCapabilities.subagents = {};
116
+ }
117
+ if (request?.method === 'session/prompt' && (message.error || message.result?.stopReason === 'cancelled')) {
118
+ const session = this.sessions.get(request.sessionId);
119
+ for (const call of session?.calls.values() ?? []) for (const child of call.children) {
120
+ if (!child.background) this.finish(request.sessionId, child, message.error ? 'failed' : 'cancelled', '');
121
+ }
122
+ }
123
+ }
124
+ if (!this.enabled || message.method !== 'session/update') return false;
125
+ const {sessionId, update} = message.params ?? {};
126
+ if (!sessionId || !record(update) || !['tool_call', 'tool_call_update'].includes(update.sessionUpdate)) return false;
127
+ if (typeof update.toolCallId !== 'string' || !update.toolCallId) return false;
128
+ const session = this.session(sessionId);
129
+ const id = update.toolCallId;
130
+ let call = session.calls.get(id);
131
+ if (!call) {
132
+ const title = text(update.name) || text(update.title);
133
+ const kind = title === 'AgentSwarm' || title.startsWith('Launching agent swarm:') ? 'AgentSwarm'
134
+ : title === 'Agent' || /^Launching (?:background )?.+ agent:/.test(title) ? 'Agent' : undefined;
135
+ if (!kind) return false;
136
+ call = {id, kind, children: []};
137
+ session.calls.set(id, call);
138
+ }
139
+ this.start(sessionId, session, call, update);
140
+ if (['completed', 'failed'].includes(update.status)) this.complete(sessionId, session, call, update);
141
+ return true;
142
+ }
143
+
144
+ async poll() {
145
+ if (this.polling) return;
146
+ this.polling = true;
147
+ try {
148
+ for (const [sessionId, session] of this.sessions) {
149
+ const children = [...session.calls.values()].flatMap(call => call.children).filter(child => child.background && !child.done);
150
+ if (!children.length || !/^session_[a-zA-Z0-9-]+$/.test(sessionId)) continue;
151
+ if (!session.directory) {
152
+ for (const workspace of await readdir(join(this.home, 'sessions'), {withFileTypes: true}).catch(() => [])) {
153
+ if (!workspace.isDirectory()) continue;
154
+ const directory = join(this.home, 'sessions', workspace.name, sessionId);
155
+ const state = await readFile(join(directory, 'state.json'), 'utf8').then(JSON.parse).catch(() => null);
156
+ if (state?.id === sessionId) {session.directory = directory; break;}
157
+ }
158
+ }
159
+ if (!session.directory) continue;
160
+ for (const child of children) {
161
+ const {taskId, agentId} = child.background;
162
+ const directory = join(session.directory, 'agents', 'main', 'tasks');
163
+ const task = await readFile(join(directory, `${taskId}.json`), 'utf8').then(JSON.parse).catch(() => null);
164
+ if (task?.taskId !== taskId || task.kind !== 'agent' || task.agentId !== agentId) continue;
165
+ const state = terminal(task.status);
166
+ if (!state) continue;
167
+ const body = await readFile(join(directory, taskId, 'output.log'), 'utf8').catch(() => text(task.stopReason));
168
+ if (this.sessions.get(sessionId) === session) this.finish(sessionId, child, state, body);
169
+ }
170
+ }
171
+ } finally {this.polling = false;}
172
+ }
173
+ }
@@ -0,0 +1,194 @@
1
+ # Nightly 任务:catalog agent 版本升级
2
+
3
+ > 流程总入口与全部经验教训见 **[`../DAILY_UPGRADE_CHECK.md`](../DAILY_UPGRADE_CHECK.md)**(唯一权威
4
+ > runbook)。本文件保留 Qoder 原生 ACP 的候选认证门槛细节;两文冲突时以 runbook 为准。
5
+
6
+ 供 AI 周期执行的任务说明。Qoder 原生 ACP 候选优先遵循下面的完整认证门槛;发现新版本身不授权改钉版本。**默认产出是把 catalog / runtime.json 的钉版本升到上游最新**:
7
+ 对每个有新版本的 agent,判定新版本是否已解决补丁问题(或官方提供了可替代方案),已解决走下线,
8
+ 没解决就把补丁移植到新版本并把钉版本升上去。「只分析不升级」是例外,只允许用在判定不了、
9
+ 且报告里写明了缺什么证据/设备的情况。
10
+
11
+ 与每周一 09:00 的 [`scripts/check-upstream-patches.py`](../../scripts/check-upstream-patches.py)
12
+ (launchd,覆盖全部组件、只产报告)互补:本任务只管 **CLI catalog / adapter 补丁层**,但执行升级。
13
+
14
+
15
+ ## Qoder 原生 ACP 候选认证门槛
16
+
17
+ 本节覆盖其他段落对 Qoder 的自动升钉建议。先发现官方精确版本,再使用
18
+ `qoder/scripts/build.mjs` 和 `generate.mjs` 生成独立候选;始终运行 `bin.qodercli`
19
+ 并记录 `bin.args:["--acp"]`、完整主包文件哈希、现有 ordered recipe `patchDigest`、
20
+ 所需平台原生工具身份与哈希。新版本如果无法通过结构化钩子生成,应报告并停止。
21
+
22
+ 在 authoring checkout 上执行(Node 必须满足候选策略;不安装定时任务):
23
+
24
+ ```bash
25
+ export EV=/home/admin/agent-artifacts/qoder-nightly-YYYYMMDD
26
+ export TMPDIR="$EV/tmp"
27
+ mkdir -p "$TMPDIR"
28
+ # beaver 外部 npm 下载前加载 ~/.config/vpn/proxy.env。
29
+ # CANDIDATE 为生成的主包目录,其同级 .release.json / .recipe.json 是输入。
30
+ # EVIDENCE 必须位于同一隔离 npm install 的祖先 node_modules 可解析的位置。
31
+ node axom-patch/cli/qoder/scripts/certify.mjs --candidate "$CANDIDATE" --evidence "$EVIDENCE" --platform linux --arch x64 --auth-home "$AUTH_SOURCE_HOME"
32
+ ```
33
+
34
+ `--auth-home` 只把允许的认证文件复制到新 HOME,不复用原始配置或历史。省略时仅跑
35
+ 真实 initialize。工具的依赖须用隔离 npm install --ignore-scripts 安装;需要的原生
36
+ helper 只能在新 staging 中设置执行位。认证工具从实际 Node、候选和依赖重算绑定,
37
+ 错误平台标签、缺项、重复、陈旧摘要、失败、坏工件或 synthetic 证据均不能通过。
38
+ 每次输出新 `run-*` 目录,`result.json` 与 `result.sha256` 禁止覆盖旧结果。
39
+
40
+ 后续真实 UI、legacy migration、安装及各平台检查的记录放入 `$EVIDENCE/records/`;
41
+ 按 README 的 schema 绑定同一候选、suite、Node、平台、工具和依赖。执行同一命令加
42
+ `--collect-only` 重新验证字节和工件再归并;它不会运行模型或信任旧 PASS 标签。
43
+ 不要同时导入一个检查的多次记录,重复即拒绝。直接 ACP next 不等于 insert-button。
44
+
45
+ 只有 15 个必需检查在目标平台全部真实 PASS,才可提出钉版本变更。实际 UI、Windows
46
+ 或迁移缺失必须报告 NOT_PASS,保留当前钉版本;合成 fixture 只验证 schema。
47
+ 不自动发布、升钉、安装 cron、推送或部署。候选/release/historyVersions/suite 变更后
48
+ 旧证据作废,必须对最终字节重新执行。
49
+
50
+ ## 钉版本规则(本任务的前提)
51
+
52
+ - **Axom 给适配器打了补丁(package_patches 配方 / native_extensions 包装)⇒ 必须钉版本**。
53
+ 不钉的话 registry/npm 一发新版,Install/Update 就装上一个补丁不生效的包。
54
+ - **没打补丁 ⇒ 不钉**,版本跟 registry 走(用户自己的 Update 决定)。
55
+ - **例外:registry 没有这个 agent**(supplement:kimi-code、command-code、zcode-acp、hermes)⇒
56
+ 版本天然由我们的 catalog 决定,永远钉着。
57
+ - scan/Update 的比对语义:roster 上报的 `version`/`registryVersion` 对钉住条目就是**生效(钉住)
58
+ 版本**,设置页 `updatable` 因此永远对着 catalog 钉版本比,不对着官方 registry 比——这是特性,
59
+ 不要"修"。
60
+ - `catalog.test.ts > patched agents stay pinned` 是机械守卫:新增补丁不补钉会红;本任务改版本后
61
+ 它也负责防止「钉了一个配方不覆盖的版本」。
62
+
63
+ ## 产物
64
+
65
+ 每次运行(无论动作多少)写报告到 `docs/superpowers/nightly-reports/YYYY-MM-DD-cli-catalog.md`:
66
+ 每个 agent 的结论(已解决/仍需要/无法判定)、证据、动作、遗留。报告不写本目录——整个
67
+ `axom-patch/cli/` 随 CLI 打包进 dist。
68
+
69
+ ## 第 0 步:边界(每次都要读)
70
+
71
+ - 永不自动 git push、发版、发 issue/PR;改动仅限第 4 步编辑集。
72
+ - 补丁**下线**必须满足 [`docs/handbook/upstream-patches.md`](../../docs/handbook/upstream-patches.md)
73
+ 的五条证据(修复链接、`fixed_release`、`adopted_version`、无补丁回归、`removal_commit`)。
74
+ 「上游发了新版本」不是「已修复」;探针通过才是。
75
+ - 钉版本只升不降;配方与钉版本一起动,不允许钉着一个配方不生效的版本。
76
+ - 改了任何随 CLI 发布的内容**必须 bump `axom-cli/package.json` npm version**——相同版本不触发
77
+ Server 自动升级。
78
+
79
+ ## 第 1 步:数据收集
80
+
81
+ ```bash
82
+ cd <repo>
83
+ curl -s --max-time 30 https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json -o $EV/acp-registry.json
84
+ for p in pi-acp @agentclientprotocol/claude-agent-acp @agentclientprotocol/codex-acp \
85
+ command-code zcode-acp-server @qoder-ai/qodercli; do npm view "$p" version; done
86
+ gh pr view 458 -R agentclientprotocol/registry --json state,title # kimi-code 收录
87
+ gh pr view 566 -R agentclientprotocol/registry --json state,title # zcode-acp 收录
88
+ gh pr view 650 -R agentclientprotocol/registry --json state,title
89
+ python3 -c "import json;d=json.load(open('$HOME/.axom/workstation/acp-adapters/builds.json'));print({k:v['current'] for k,v in d['agents'].items()})"
90
+ ```
91
+
92
+ 对照表(钉版本与配方以文件为准,本表是索引;升级后同步更新本表):
93
+
94
+ | agent id | 来源 | 钉在哪 | 配方 |
95
+ |---|---|---|---|
96
+ | claude-acp | npm @agentclientprotocol/claude-agent-acp | catalog.ts `distribution` | claude-account-limits + launchEnv |
97
+ | codex-acp | npm @agentclientprotocol/codex-acp | catalog.ts `distribution` | codex-account-limits + launchEnv |
98
+ | pi-acp | npm pi-acp | catalog.ts `distribution` | pi-steering、pi-subagent-subwork |
99
+ | qoder | npm @qoder-ai/qodercli@1.1.75 | catalog.ts 固定 `--acp` + `qoder-native-acp` package patch;runtime.json `install_policies` 禁用 npm scripts | qoder-native-acp |
100
+ | kimi-code | GitHub release 二进制 | catalog.ts `supplement` | native extension kimi-proxy |
101
+ | command-code | npm command-code | catalog.ts `supplement` | commandcode-account-limits |
102
+ | zcode-acp | npm zcode-acp-server | catalog.ts `supplement` | zcode-account-limits、zcode-models、zcode-subagent-subwork |
103
+ | antigravity-acp | registry binary .par(dl.google.com) | catalog.ts `distribution` | native extension antigravity-proxy |
104
+
105
+ ## 第 2 步:逐 agent 判定(三选一,必须给结论)
106
+
107
+ 对每个「npm latest / registry 版本 > 当前钉版本」的 agent:
108
+
109
+ 1. **上游已解决,或官方有替代方案** → 走下线证据门槛(第 0 步),按台账 remove_when 退休补丁,
110
+ 删钉版本,版本交还 registry。证据:探针在无补丁新版本上通过,或上游实现了等价能力
111
+ (如 adapter 原生暴露配额、原生发出 subagent 变体)。
112
+ 2. **仍需要补丁**(默认预期)→ 第 3 步移植配方,第 4 步升钉版本。**当天完成**。
113
+ 3. **判定不了** → 保持钉住,报告写明缺什么(哪个探针不存在、哪步需要真机/凭据)。这是唯一
114
+ 允许不升级的出口。
115
+
116
+ 另查两件让位事件:
117
+
118
+ - registry 收录了 supplement 的 id → supplement 让位前确认让位目标版本有配方覆盖;没有就先把
119
+ supplement 换成显式 `distribution` 钉 + 移植配方,再让位。
120
+ - 条件 override 到界(qoder until_registry_version)→ 让位目标高于配方版本时同样先移植再放行。
121
+
122
+ ## 第 3 步:移植补丁到新版本
123
+
124
+ 1. **取 pristine**:`mkdir -p "$EV/pristine" && cd "$EV/pristine" && npm pack <pkg>@<ver>` 解包
125
+ (antigravity 用 curl 拉 darwin-arm64 zip)。
126
+ 2. **先问「还需要吗」**:
127
+ - 配额类:grep 新版本有无原生 `accountRateLimits` / 等价能力;没有 → 仍需要。
128
+ - 行为类(steering / subagent):查 initialize 有无 `_meta.steering`、有无 `subagent_spawned`
129
+ 等原生实现;没有 → 仍需要。
130
+ 3. **account-limits 类配方**(claude/codex/commandcode/zcode):改 `account-limits/generate.py`
131
+ 里的版本号与锚点串(`count==1` 断言失败 = 上游改了形状,对照新 pristine 修锚点),指向新
132
+ pristine 目录重跑,生成新配方 JSON。
133
+ 4. **手工配方**(zcode-models、zcode-subagent-subwork):对每个 edit 检查 `before` 在新
134
+ pristine 里是否仍唯一存在;压缩标识符改名(如 `gnd`→`Ocd`、`as`→`cs`)就**同步改锚点和载荷**。
135
+ Qoder 使用官方 1.1.75 的 `qoder-native-acp.json`,不再维护旧 steering/subagent 配方;
136
+ `patchDigest` 必须由生成器按完整 recipe(含 asset)重算。Qoder 与其他包补丁一样,
137
+ 安装和启动只依赖 Catalog 钉版本、配方锚点和哈希;行为/平台认证仅用于升级或退休判断,
138
+ 不得成为普通安装的前置条件。
139
+ 5. **验证**(缺一不可):
140
+ - 顺序应用配方到 pristine 副本,逐 edit 核对 before/after sha256;
141
+ - 需要行为证据的候选可运行 `node axom-patch/cli/qoder/scripts/certify.mjs`,并保存每个
142
+ check 的原始记录、候选 digest、Node/tool identity 和平台信息;这些记录只用于升级/退休判断;
143
+ - `manifest.json` 的 `compatible_versions` 与配方 `versions` 同步(`acp/qoderPatches.test.ts` 会断言);
144
+ - 生成器/认证器结构变化时修复对应断言并重新生成 release,不通过手工修改 certification 绕过 gate。
145
+
146
+ ## 第 4 步:升钉版本(编辑集,同一提交改完)
147
+
148
+ 1. `axom-cli/src/workstation/acp/registry/catalog.ts`:bump `distribution.version` /
149
+ `supplement.version` 与来源(npx spec / 六平台归档 URL);binary 条目换 URL 时逐平台 HEAD
150
+ 确认,能拿 sha256 就补。**diff registry 现条目的 args/env**——钉住来源整个替换 registry 的
151
+ distribution,registry 新加的要并进来。
152
+ 2. `axom-patch/cli/runtime.json`:override 钉版本与 `until_registry_version` 同步;配方文件的
153
+ `versions`(字节一致可双版本并存,如 zcode 0.69.3+0.69.4;锚点变了就只留新版本);native
154
+ extension `versions` 加新版本(旧的保留,过渡期安装仍被包住)。
155
+ 3. `axom-patch/manifest.json`:`dependencies.<pkg>.reviewed_version` = 本次核验的版本;
156
+ `patches[].artifacts` 的 sha256 全部重算(copy-patches.mjs 会校验,漏了 build 直接红)。
157
+ 4. `axom-cli/package.json` bump npm version(相对上一个已发布版本)。
158
+ 5. 文档:`axom-patch/cli/README.md` 当前补丁表、本文件对照表。
159
+
160
+ ## 第 5 步:验证
161
+
162
+ ```bash
163
+ cd axom-cli
164
+ npx vitest run src/workstation/acp/patchAssets.test.ts src/workstation/acp/managedPatches.test.ts \
165
+ src/workstation/acp/registry/catalog.test.ts src/workstation/acp/acpAgents.test.ts
166
+ npm run build # 含 copy-patches 的台账工件校验
167
+ npm run test:qoder-patch # Qoder 补丁模块与生成器(npm test 也会跑)
168
+ cd ..
169
+ python3 scripts/check-upstream-patches.py --offline
170
+ ```
171
+
172
+ main 上有一批与本任务无关的既有失败(daemon 接线、浏览器 keeper、journal 时序、migrate 的
173
+ claude 旧 override 用例);与 `git stash` 前后对照失败集一致即可,不要顺手修。
174
+
175
+ 真机冒烟(可选;涉及启动行为变化时做):dev 实例 Install/Update 一次,scan 确认版本行收敛到
176
+ 新钉版本;`reconcileCertifiedBuilds` 会在 CLI 发出后自动把已装工作站前移。
177
+
178
+ ## 第 6 步:报告
179
+
180
+ 写入 `docs/superpowers/nightly-reports/`:对照表快照、每个 agent 的三选一结论与证据、动作清单、
181
+ 遗留(只属于出口 3)。发版走既有 Client release 流程,本任务不触碰。
182
+
183
+ ## 快照(2026-10-14 首跑,全部完成升级)
184
+
185
+ | agent | 旧钉 | 新钉 | registry/npm 当时 | 结论 |
186
+ |---|---|---|---|---|
187
+ | claude-acp | —(补钉) | 0.90.0 | 0.90.0 | 上游无原生配额 → 补钉;配方经 generate.py 重生成(normalize.mjs 对齐) |
188
+ | codex-acp | —(补钉) | 2.2.2 | 2.2.2 | 同上 |
189
+ | pi-acp | —(补钉) | 0.0.34 | 0.0.34 | 上游无 steering/subagent 变体 → 补钉 |
190
+ | antigravity-acp | —(补钉 1.3.0 后升) | **1.4.0** | registry 1.4.0 | 真实 1.4.0 .par 经 proxy 实测:透传无损、能力注入成功;配额 READ 留真机 |
191
+ | command-code | 1.74.3 | **1.82.1** | npm 1.82.1 | 上游仍无配额能力;handler map 改名(u/dn→m/hn)锚点已跟,generate.py 移植 |
192
+ | zcode-acp | 0.69.3 | **0.69.4** | npm 0.69.4 | 三份配方目标文件字节一致(versions 双版本);subagent 探针补丁副本过、pristine 复现缺失 |
193
+ | qoder | 1.1.74 adapter | **1.1.75 native ACP** | npm `@qoder-ai/qodercli@1.1.75` | `qoder-native-acp` recipe 已生成;真实行为/平台 evidence 只作为后续升级或退休依据,不阻塞当前安装 |
194
+ | kimi-code | 2.1.1 | 2.1.1 | registry 未收录 | 无新版(GitHub release 同版) |
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "pi-subagent-subwork",
3
+ "package": "pi-acp",
4
+ "versions": [
5
+ "0.0.34"
6
+ ],
7
+ "edits": [
8
+ {
9
+ "file": "dist/index.js",
10
+ "before_sha256": "1ee621825b7e02d74312336fc0f820046bd41fe26455143f6f6e5078a727fa79",
11
+ "after_sha256": "228205f547edccd553d873a5f5c5dea6795ab6d33f989f14067e239153bbb2ef",
12
+ "before": "var PiAcpSession = class {\n axomSteering = new AxomPiSteering(this);",
13
+ "after": "// Injected into pi-acp 0.0.34 after pi-steering. Normalizes pi-subagents children into\n// draft ACP subagent variants (RFD #1992), the same contract claude-agent-acp ships\n// natively. All ids are derived from the root session id plus toolCallId/runId, never\n// random, so a keeper raw-log replay re-derives identical frames. Frames ride the\n// session's existing lastEmit chain so they keep one total order with every other frame.\nconst VOLATILE_PROGRESS_KEYS = /* @__PURE__ */ new Set([\n \"durationMs\",\n \"lastActivityAt\",\n \"recentOutput\",\n \"inputTokens\",\n \"outputTokens\",\n \"cacheRead\",\n \"cacheWrite\",\n \"tokens\",\n \"cost\",\n \"window\",\n \"windowPeak\",\n \"usage\"\n]);\nclass AxomPiSubagents {\n constructor(session) {\n this.session = session;\n this.children = /* @__PURE__ */ new Map();\n this.runs = /* @__PURE__ */ new Map();\n }\n emitTo(sessionId, update) {\n const session = this.session;\n session.lastEmit = session.lastEmit.then(\n () => session.conn.sessionUpdate({ sessionId, update })\n ).catch(() => {\n });\n }\n isLaunch(args) {\n return args && typeof args === \"object\" && typeof args.agent === \"string\" && args.agent && !args.action && (typeof args.task === \"string\" || args.workflow !== undefined);\n }\n textOf(content, cap) {\n if (!Array.isArray(content)) return \"\";\n let text = \"\";\n for (const block of content) {\n if (block && typeof block === \"object\" && block.type === \"text\" && typeof block.text === \"string\") text += (text ? \"\\n\" : \"\") + block.text;\n if (text.length >= cap) break;\n }\n return text.slice(0, cap);\n }\n spawn(childId, name, task, meta) {\n this.emitTo(this.session.sessionId, {\n sessionUpdate: \"subagent_spawned\",\n subagentSessionId: childId,\n name,\n task,\n capabilities: {},\n _meta: { pi: meta }\n });\n }\n onToolStart(ev) {\n if (ev.toolName !== \"subagent\" || !this.isLaunch(ev.args)) return;\n const toolCallId = String(ev.toolCallId ?? \"\");\n if (!toolCallId || this.children.has(toolCallId)) return;\n const args = ev.args;\n const childId = `pi-agent:${this.session.sessionId}:${toolCallId}`;\n const task = typeof args.task === \"string\" && args.task ? args.task : `workflow: ${String(args.workflow)}`;\n const child = { childId, terminal: false, lastUpdate: undefined };\n this.children.set(toolCallId, child);\n this.spawn(childId, args.agent, task, { toolCallId });\n }\n // pi-subagents resends the full foreground progress snapshot on a timer; measured on real\n // genius runs, frame-to-frame deltas are timers (durationMs/lastActivityAt), a rolling output\n // tail (recentOutput[] — the child's content, which must not stream into the parent session\n // anyway) and token/cost/window telemetry that ticks on every model step. Strip all of those\n // recursively and drop an update whose fingerprint did not change. Discrete signals — status\n // text, turnCount, recentTools[] and toolCalls[] growth — still pass, and the terminal frames\n // (with the final usage) arrive on tool_execution_end, which is never deduplicated.\n stripVolatile(value) {\n if (Array.isArray(value)) return value.map((item) => this.stripVolatile(item));\n if (value && typeof value === \"object\") {\n const out = {};\n for (const key of Object.keys(value).sort()) {\n if (VOLATILE_PROGRESS_KEYS.has(key)) continue;\n out[key] = this.stripVolatile(value[key]);\n }\n return out;\n }\n return value;\n }\n pruneEmpty(value) {\n if (Array.isArray(value)) {\n const kept = value.map((item) => this.pruneEmpty(item)).filter((item) => item !== void 0);\n return kept.length ? kept : void 0;\n }\n if (value && typeof value === \"object\") {\n const out = {};\n for (const key of Object.keys(value)) {\n const pruned = this.pruneEmpty(value[key]);\n if (pruned !== void 0) out[key] = pruned;\n }\n return Object.keys(out).length ? out : void 0;\n }\n return value;\n }\n onToolUpdate(ev) {\n const child = this.children.get(String(ev.toolCallId ?? \"\"));\n if (!child) return false;\n const fingerprint = JSON.stringify(this.pruneEmpty(this.stripVolatile(ev.partialResult)));\n if (fingerprint === child.lastUpdate) return true;\n child.lastUpdate = fingerprint;\n return false;\n }\n onToolEnd(ev) {\n const toolCallId = String(ev.toolCallId ?? \"\");\n const child = this.children.get(toolCallId);\n if (!child || child.terminal) return;\n const details = ev.result && typeof ev.result === \"object\" && ev.result.details && typeof ev.result.details === \"object\" ? ev.result.details : {};\n const runId = typeof details.runId === \"string\" ? details.runId : typeof details.asyncId === \"string\" ? details.asyncId : undefined;\n if (runId && !this.runs.has(runId)) this.runs.set(runId, child);\n if (details.asyncId !== undefined || details.asyncDir !== undefined) return;\n child.terminal = true;\n const preview = this.textOf(ev.result && ev.result.content, 2000);\n if (preview) this.emitTo(child.childId, { sessionUpdate: \"agent_message_chunk\", content: { type: \"text\", text: preview } });\n this.emitTo(this.session.sessionId, {\n sessionUpdate: \"subagent_state_update\",\n subagentSessionId: child.childId,\n state: ev.isError === true ? \"failed\" : \"completed\",\n _meta: { pi: { toolCallId } }\n });\n }\n onNotify(entry) {\n const details = entry.details && typeof entry.details === \"object\" ? entry.details : {};\n const runs = Array.isArray(details.runs) ? details.runs : [];\n for (const run of runs) {\n if (!run || typeof run !== \"object\") continue;\n const runId = typeof run.runId === \"string\" ? run.runId : typeof run.id === \"string\" ? run.id : undefined;\n if (!runId) continue;\n let child = this.runs.get(runId);\n if (!child) {\n child = { childId: `pi-agent:${this.session.sessionId}:run:${runId}`, terminal: false };\n this.runs.set(runId, child);\n this.spawn(child.childId, typeof run.agent === \"string\" && run.agent ? run.agent : \"subagent\", typeof run.summary === \"string\" ? run.summary : \"\", { adopted: true, runId });\n }\n if (child.terminal) continue;\n const status = typeof run.status === \"string\" ? run.status : \"\";\n const state = status === \"completed\" ? \"completed\" : status === \"failed\" ? \"failed\" : status === \"stopped\" ? \"cancelled\" : undefined;\n if (!state) continue;\n child.terminal = true;\n const meta = { pi: { runId } };\n for (const key of [\"session\", \"resultPath\", \"asyncDir\"]) {\n if (typeof run[key] === \"string\" && run[key]) meta.pi[key] = run[key];\n }\n if (typeof run.summary === \"string\" && run.summary) {\n this.emitTo(child.childId, { sessionUpdate: \"agent_message_chunk\", content: { type: \"text\", text: run.summary.slice(0, 2000) } });\n }\n this.emitTo(this.session.sessionId, { sessionUpdate: \"subagent_state_update\", subagentSessionId: child.childId, state, _meta: meta });\n }\n }\n observe(ev) {\n const type = String(ev.type ?? \"\");\n if (type === \"tool_execution_start\") this.onToolStart(ev);\n else if (type === \"tool_execution_end\") this.onToolEnd(ev);\n else if (type === \"tool_execution_update\") return this.onToolUpdate(ev);\n else if (type === \"message_end\") {\n // pi delivers extension custom messages (pi.sendMessage) as message_start/message_end\n // pairs with role \"custom\"; pi-acp 0.0.34 has no case for them and drops them.\n const message = ev.message;\n if (message && typeof message === \"object\" && message.role === \"custom\" && message.customType === \"subagent-notify\") this.onNotify(message);\n }\n return false;\n }\n}\n\nvar PiAcpSession = class {\n axomSteering = new AxomPiSteering(this);\n axomSubagents = new AxomPiSubagents(this);"
14
+ },
15
+ {
16
+ "file": "dist/index.js",
17
+ "before_sha256": "228205f547edccd553d873a5f5c5dea6795ab6d33f989f14067e239153bbb2ef",
18
+ "after_sha256": "41e76175c97e4d0e042e6fa7cfabca0eb0fea757a4fcfec39d8d238fc2e9088e",
19
+ "before": " handlePiEvent(ev) {\n const type = String(ev.type ?? \"\");",
20
+ "after": " handlePiEvent(ev) {\n if (this.axomSubagents && this.axomSubagents.observe(ev)) return;\n const type = String(ev.type ?? \"\");"
21
+ }
22
+ ]
23
+ }
@@ -13,7 +13,11 @@ for (const state of ['completed', 'failed', 'stopped']) {
13
13
  for (const closing of ['terminalState', 'terminalPromise']) {
14
14
  const events = [];
15
15
  const publish = async (notification) => { events.push(notification); };
16
- const native = new NativeSubagentRuntime(true, 'root', {}, publish, { log() {} });
16
+ // "air", not `true`: 0.90.0 renamed the first argument from `enabled` to `form` ("air" | "rfd"),
17
+ // and in the rfd form a finished child keeps receiving its own late updates by design. Axom
18
+ // declares the AIR extension meta, so "air" is the form it gets; on pre-0.90 versions the
19
+ // string is still truthy, so `enabled` behaves as before.
20
+ const native = new NativeSubagentRuntime('air', 'root', {}, publish, { log() {} });
17
21
  const child = { sessionId: 'child', parentSessionId: 'root' };
18
22
  native.childByToolCall.set('call', child);
19
23
  const tasks = new AsyncTaskRuntime(true, 'root', publish, { routeOf: (call) => native.routeOfToolCall(call) });
@@ -0,0 +1,33 @@
1
+ import assert from 'node:assert/strict';
2
+ import { readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import vm from 'node:vm';
5
+
6
+ const packageRoot = process.argv[2];
7
+ assert.ok(packageRoot, 'Pass the codex-acp package directory');
8
+ const source = readFileSync(path.join(packageRoot, 'dist/index.js'), 'utf8');
9
+ const helpers = source.slice(source.indexOf('function usesTerminal(item)'), source.indexOf('function startFacts(item)')) +
10
+ source.slice(source.indexOf('function completionFacts(item,'), source.indexOf('function searchTitle(')) +
11
+ source.slice(source.indexOf('function toTerminalToolStatus(status)'), source.indexOf('// src/tool-calls/reporters/CommandReporter.ts'));
12
+ const context = vm.createContext({});
13
+ vm.runInContext(helpers, context);
14
+ for (const streamed of [false, true]) {
15
+ for (const status of ['completed', 'failed']) {
16
+ context.item = {
17
+ id: 'terminal-output-probe', source: 'unifiedExec', commandActions: [],
18
+ status, exitCode: status === 'completed' ? 0 : 7,
19
+ aggregatedOutput: 'BEGIN\nEND\n',
20
+ };
21
+ context.streamed = streamed;
22
+ const facts = vm.runInContext('completionFacts(item, streamed, false)', context);
23
+ if (process.argv.includes('--expect-bug')) {
24
+ assert.equal(facts.opaqueResult, undefined);
25
+ } else {
26
+ assert.equal(facts.opaqueResult.formatted_output, context.item.aggregatedOutput);
27
+ assert.equal(facts.opaqueResult.exit_code, context.item.exitCode);
28
+ }
29
+ assert.equal(facts.status, status);
30
+ assert.equal(facts.terminalExit.exitCode, context.item.exitCode);
31
+ }
32
+ }
33
+ console.log(process.argv.includes('--expect-bug') ? 'Confirmed missing complete terminal result' : 'Complete terminal results pass: streamed/unstreamed and completed/failed');
@@ -0,0 +1,82 @@
1
+ import assert from 'node:assert/strict';
2
+ import {spawn} from 'node:child_process';
3
+ import {createInterface} from 'node:readline';
4
+ import {fileURLToPath} from 'node:url';
5
+
6
+ const binary = process.argv[2];
7
+ assert.ok(binary, 'Usage: node kimi-subagent-subwork.mjs /path/to/managed/kimi');
8
+ const child = spawn(process.execPath, [fileURLToPath(new URL('../account-limits/kimi-proxy.mjs', import.meta.url)), binary, 'acp'], {stdio: ['pipe', 'pipe', 'inherit']});
9
+ const pending = new Map();
10
+ const updates = [];
11
+ const sessions = [];
12
+ let nextId = 1;
13
+ const write = message => child.stdin.write(JSON.stringify(message) + '\n');
14
+ const request = (method, params) => new Promise((resolve, reject) => {
15
+ const id = nextId++;
16
+ const timer = setTimeout(() => {pending.delete(id); reject(new Error(`Timeout: ${method}`));}, 90000);
17
+ pending.set(id, {resolve, reject, timer});
18
+ write({jsonrpc: '2.0', id, method, params});
19
+ });
20
+ createInterface({input: child.stdout}).on('line', line => {
21
+ let message;
22
+ try {message = JSON.parse(line);} catch {return;}
23
+ if (message.method === 'session/update') updates.push(message.params);
24
+ if (message.method === 'session/request_permission') {
25
+ const option = message.params.options.find(option => option.kind === 'allow_once');
26
+ write({jsonrpc: '2.0', id: message.id, result: {outcome: option ? {outcome: 'selected', optionId: option.optionId} : {outcome: 'cancelled'}}});
27
+ }
28
+ const waiter = !message.method && pending.get(message.id);
29
+ if (!waiter) return;
30
+ pending.delete(message.id);
31
+ clearTimeout(waiter.timer);
32
+ if (message.error) waiter.reject(new Error(JSON.stringify(message.error)));
33
+ else waiter.resolve(message.result);
34
+ });
35
+ child.on('exit', code => {
36
+ for (const waiter of pending.values()) {clearTimeout(waiter.timer); waiter.reject(new Error(`Kimi exited: ${code}`));}
37
+ pending.clear();
38
+ });
39
+ const prompt = (sessionId, text) => request('session/prompt', {sessionId, prompt: [{type: 'text', text}]});
40
+ const create = async () => {
41
+ const session = await request('session/new', {cwd: '/tmp', mcpServers: []});
42
+ sessions.push(session.sessionId);
43
+ return session.sessionId;
44
+ };
45
+ const states = () => updates.filter(({update}) => update.sessionUpdate === 'subagent_state_update' && update.state);
46
+ try {
47
+ const initialized = await request('initialize', {protocolVersion: 1, clientCapabilities: {subagents: {}, _meta: {jetbrains: {air: {version: 1, capabilities: ['nativeSubagentSessions', 'asyncTasks']}}}}, clientInfo: {name: 'axom-kimi-subagent-probe', version: '1.0.0'}});
48
+ assert.ok(initialized.agentCapabilities.sessionCapabilities.subagents);
49
+ assert.ok(initialized.agentCapabilities._meta['axom/accountRateLimits']);
50
+ console.log('agent', JSON.stringify(initialized.agentInfo));
51
+ const foreground = await create();
52
+ await prompt(foreground, 'ACP rendering verification. Use Agent exactly once, subagent_type explore, prompt "Return the exact literal KIMI_FOREGROUND_OK; do not use tools, read files or run commands." Wait for its result, then repeat the literal. Do not use any other tools or modify files.');
53
+ const spawn = updates.find(({sessionId, update}) => sessionId === foreground && update.sessionUpdate === 'subagent_spawned');
54
+ assert.ok(spawn, 'Foreground dispatch');
55
+ assert.ok(states().some(({sessionId, update}) => sessionId === foreground && update.state === 'completed'));
56
+ assert.ok(updates.some(({sessionId, update}) => sessionId === spawn.update.subagentSessionId && update.content?.text.includes('KIMI_FOREGROUND_OK')));
57
+ assert.ok(!updates.some(({update}) => update.title === 'Agent' || update.title?.startsWith('Launching explore agent:')), 'No duplicate Agent tool card');
58
+ const beforeResume = updates.length;
59
+ await prompt(foreground, 'Use Agent exactly once with resume="agent-0" and prompt="Return KIMI_RESUME_OK without tools." Do not set subagent_type. Wait for its result, then repeat it. No other tools or file changes.');
60
+ assert.ok(updates.slice(beforeResume).some(({update}) => update.sessionUpdate === 'subagent_state_update' && update.subagentSessionId === spawn.update.subagentSessionId && update.state === 'completed'), 'Resume reuses child identity');
61
+ assert.ok(!updates.slice(beforeResume).some(({update}) => update.sessionUpdate === 'subagent_spawned'));
62
+ console.log('PASS foreground + resume');
63
+
64
+ const background = await create();
65
+ await prompt(background, 'ACP background rendering verification. Use Agent exactly once with run_in_background=true, subagent_type explore, prompt="Return the exact literal KIMI_BACKGROUND_OK without tools, file access or commands." After the launch receipt say launched and finish your turn. Do not wait, poll or call any other tools.');
66
+ const deadline = Date.now() + 45000;
67
+ while (!states().some(({sessionId, update}) => sessionId === background && update.state === 'completed') && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100));
68
+ assert.ok(states().some(({sessionId, update}) => sessionId === background && update.state === 'completed'), 'Background terminal after launch receipt');
69
+ const backgroundChild = updates.find(({sessionId, update}) => sessionId === background && update.sessionUpdate === 'subagent_spawned')?.update.subagentSessionId;
70
+ assert.ok(updates.some(({sessionId, update}) => sessionId === backgroundChild && update.content?.text.includes('KIMI_BACKGROUND_OK')), 'Background output');
71
+ console.log('PASS background late output + terminal');
72
+
73
+ const beforeLoad = updates.length;
74
+ await request('session/load', {sessionId: foreground, cwd: '/tmp', mcpServers: []});
75
+ assert.ok(updates.slice(beforeLoad).some(({update}) => update.sessionUpdate === 'subagent_spawned' && update.subagentSessionId === spawn.update.subagentSessionId), 'Load reconstructs stable child identity');
76
+ console.log('PASS load replay');
77
+ console.log(JSON.stringify({sessions: sessions.length, children: updates.filter(({update}) => update.sessionUpdate === 'subagent_spawned').length, terminals: states().map(({update}) => update.state)}));
78
+ } finally {
79
+ for (const sessionId of sessions) await request('session/delete', {sessionId}).catch(error => console.error('cleanup', error.message));
80
+ child.stdin.end();
81
+ child.kill();
82
+ }