@platless/axom-cli 0.5.9 → 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 (163) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +100 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  7. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  8. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  9. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  10. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  11. package/dist/axom-patch/cli/nightly-task.md +55 -13
  12. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  13. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  14. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  15. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  16. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  17. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  18. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  19. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  20. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  21. package/dist/axom-patch/cli/runtime.json +16 -18
  22. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  23. package/dist/axom-patch/cli/zcode-models.json +1 -2
  24. package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
  25. package/dist/cli.js +4 -36
  26. package/dist/commands/knowledge.js +47 -0
  27. package/dist/commands/update.js +65 -3
  28. package/dist/commands/workstation.js +73 -7
  29. package/dist/install/claude/commands.js +13 -13
  30. package/dist/mcp.js +1 -1
  31. package/dist/native/darwin-arm64/axom-quic +0 -0
  32. package/dist/workstation/acp/acpAgents.js +102 -98
  33. package/dist/workstation/acp/acpClient.js +26 -0
  34. package/dist/workstation/acp/builds/install/npm.js +99 -10
  35. package/dist/workstation/acp/initializeResponse.js +6 -1
  36. package/dist/workstation/acp/managedPatches.js +189 -25
  37. package/dist/workstation/acp/managedRuntime.js +314 -0
  38. package/dist/workstation/acp/patchAssets.js +40 -0
  39. package/dist/workstation/acp/processor/checkpoint.js +25 -34
  40. package/dist/workstation/acp/processor/control.js +5 -78
  41. package/dist/workstation/acp/processor/runtime.js +5 -81
  42. package/dist/workstation/acp/processor/session.js +51 -80
  43. package/dist/workstation/acp/processor/state.js +6 -13
  44. package/dist/workstation/acp/processor/step.js +3 -20
  45. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  46. package/dist/workstation/acp/processor/updates.js +131 -105
  47. package/dist/workstation/acp/processorSession.js +8 -172
  48. package/dist/workstation/acp/registry/catalog.js +65 -39
  49. package/dist/workstation/acp/registry/launch.js +16 -1
  50. package/dist/workstation/acp/switching.js +0 -3
  51. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  52. package/dist/workstation/acp/test/keeperWorld.js +28 -0
  53. package/dist/workstation/acp/test/raw/sources.js +16 -130
  54. package/dist/workstation/acp/test/realAdapter.js +1 -47
  55. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  56. package/dist/workstation/agentFlow.js +1 -1
  57. package/dist/workstation/agentSessions.js +38 -143
  58. package/dist/workstation/channels/management.js +95 -11
  59. package/dist/workstation/channels/url.js +1 -1
  60. package/dist/workstation/config.js +16 -4
  61. package/dist/workstation/connection/encryptedChannel.js +4 -1
  62. package/dist/workstation/control/bindingStore.js +200 -0
  63. package/dist/workstation/control/commands.js +124 -0
  64. package/dist/workstation/control/daemonIdentity.js +53 -0
  65. package/dist/workstation/control/enrollment.js +106 -0
  66. package/dist/workstation/control/inspect.js +71 -0
  67. package/dist/workstation/control/locks.js +63 -0
  68. package/dist/workstation/control/protocol.js +40 -0
  69. package/dist/workstation/control/restartGuard.js +46 -0
  70. package/dist/workstation/control/runtimeClient.js +97 -0
  71. package/dist/workstation/control/runtimeHost.js +148 -0
  72. package/dist/workstation/control/serverIdentity.js +35 -0
  73. package/dist/workstation/daemon.js +106 -6
  74. package/dist/workstation/daemonctl.js +77 -14
  75. package/dist/workstation/desktopControl/audio.js +99 -0
  76. package/dist/workstation/desktopControl/capabilities.js +92 -0
  77. package/dist/workstation/desktopControl/captureModes.js +9 -0
  78. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  79. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  80. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  81. package/dist/workstation/desktopControl/lobby.js +129 -0
  82. package/dist/workstation/desktopControl/macProvider.js +155 -0
  83. package/dist/workstation/desktopControl/media.js +331 -0
  84. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  85. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  86. package/dist/workstation/desktopControl/overview.js +58 -0
  87. package/dist/workstation/desktopControl/protocol.js +302 -0
  88. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  89. package/dist/workstation/desktopControl/rateController.js +226 -0
  90. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  91. package/dist/workstation/desktopControl/transport.js +27 -0
  92. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  93. package/dist/workstation/files/credentials.js +142 -0
  94. package/dist/workstation/files/text.js +42 -8
  95. package/dist/workstation/keepers/kit/bundle.js +21 -17
  96. package/dist/workstation/keepers/kit/launcher.js +47 -7
  97. package/dist/workstation/keepers/kit/paths.js +2 -1
  98. package/dist/workstation/managedInstall.js +57 -0
  99. package/dist/workstation/protocol/agent.js +2 -15
  100. package/dist/workstation/protocol/management.js +93 -4
  101. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  102. package/dist/workstation/rd/artifact.js +119 -0
  103. package/dist/workstation/rd/control.js +119 -0
  104. package/dist/workstation/rd/display.js +29 -0
  105. package/dist/workstation/rd/provider.js +222 -0
  106. package/dist/workstation/rd/sessions.js +391 -0
  107. package/dist/workstation/rd/state.js +45 -0
  108. package/dist/workstation/rd/update.js +171 -0
  109. package/dist/workstation/rd/upkeep.js +38 -0
  110. package/dist/workstation/register.js +27 -37
  111. package/dist/workstation/service/installer.js +104 -13
  112. package/dist/workstation/service/launchd.js +1 -0
  113. package/dist/workstation/storageMetrics.js +118 -0
  114. package/dist/workstation/systemMetrics.js +44 -92
  115. package/dist/workstation/toolchain.js +94 -0
  116. package/dist/workstation/tools/browser.js +28 -4
  117. package/dist/workstation/update/artifact.js +2 -1
  118. package/dist/workstation/update/globalInstall.js +116 -0
  119. package/dist/workstation/update/npmInstaller.js +49 -23
  120. package/dist/workstation/update/rdRollback.js +145 -0
  121. package/dist/workstation/update/runUpdate.js +3 -2
  122. package/dist/workstation/update/watchdog.js +16790 -334
  123. package/package.json +5 -5
  124. package/skills/axom/SKILL.md +11 -11
  125. package/skills/axom-definition/SKILL.md +3 -3
  126. package/skills/axom-platform/SKILL.md +3 -3
  127. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  128. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  129. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  130. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  131. package/dist/commands/guide.js +0 -38
  132. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  133. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  134. package/dist/workstation/acp/delegationMcp.js +0 -24
  135. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  136. package/dist/workstation/acp/qoderStartup.js +0 -45
  137. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  138. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  139. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  140. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  141. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  142. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  143. package/dist/workstation/qoderAdapter/contract.js +0 -78
  144. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  145. package/dist/workstation/qoderAdapter/history.js +0 -85
  146. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  147. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  148. package/dist/workstation/qoderAdapter/main.js +0 -95
  149. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  150. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  151. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  152. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  153. package/dist/workstation/qoderAdapter/profile.js +0 -93
  154. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  155. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  156. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  157. package/dist/workstation/qoderAdapter/session.js +0 -319
  158. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  159. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  160. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  161. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  162. package/dist/workstation/qoderAdapter/usage.js +0 -37
  163. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
@@ -44,10 +44,10 @@
44
44
  *
45
45
  * 这里写下的来源**整个替换** registry 的 `version` 与 `distribution`,id 与展示名仍是 registry 的,
46
46
  * 于是已有的配置条目、roster、安装、启动、更新都不用知道发生过替换(见 {@link withCatalogOverrides})。
47
- * 本文件的 distribution 用于长期策略;Qoder 等临时的钉版本放在
47
+ * 本文件的 distribution 用于长期策略;临时的版本覆盖才放在
48
48
  * axom-patch/cli/runtime.json,按 registry 版本挑选(`when_registry_version` / `until_registry_version`,
49
49
  * 见 `patchAssets.ts`),registry 一到就自己让位。两者与上面的 `install` 是同一条安全边界:只能是
50
- * 随 CLI 发布、钉死版本、由真机验证过的可信配置。
50
+ * 随 CLI 发布、钉死版本、由补丁哈希和回归验证过的可信配置。
51
51
  *
52
52
  * ## `supplement` 与 `supersedes`:registry 缺的条目、registry 不肯撤的条目
53
53
  *
@@ -86,13 +86,13 @@ export const AXOM_AGENT_CATALOG = [
86
86
  defaultName: 'claude-agent',
87
87
  defaultLabel: 'Claude Agent',
88
88
  detect: { commands: ['claude'] },
89
- // 钉版本的唯一理由是补丁:claude-account-limits 的配方只核验过 0.90.0(runtime.json 的
89
+ // 钉版本的唯一理由是补丁:claude-account-limits 的配方只核验过 0.97.0(runtime.json 的
90
90
  // package_patches)。不钉的话 registry 一发新版,Install/Update 就装上一个配方不覆盖的包,
91
91
  // 账号配额扩展静默消失——2026-10-14 的 antigravity(registry 已到 1.4.0、native extension
92
92
  // 只认 1.3.0)就是这个缺口的实例。钉住后 scan 的 Update 比对(installedVersion vs 本清单的
93
93
  // version)只在落后于钉住版本时才成立,上游新版要等 nightly 核验移植配方后再 bump。
94
- // 周期核验步骤见 axom-patch/cli/nightly-task.md。
95
- distribution: { version: '0.90.0', npx: { package: '@agentclientprotocol/claude-agent-acp@0.90.0' } },
94
+ // 周期核验步骤见 axom-patch/DAILY_UPGRADE_CHECK.md。
95
+ distribution: { version: '0.97.0', npx: { package: '@agentclientprotocol/claude-agent-acp@0.97.0' } },
96
96
  launchEnv: {
97
97
  // Axom 拉起的 Claude Code——会话进程、配额读的 /usage、auth status 探针、terminalLogin 代跑的
98
98
  // `--cli auth login`——一律使用独立的 OAuth 凭据槽,与用户终端 `claude login` 写进默认槽
@@ -116,16 +116,18 @@ export const AXOM_AGENT_CATALOG = [
116
116
  defaultName: 'codex',
117
117
  defaultLabel: 'Codex',
118
118
  detect: { commands: ['codex'] },
119
- // 与 claude-acp 同一条规则:codex-account-limits 只核验过 2.2.2,钉住直到配方随新版本移植。
120
- distribution: { version: '2.2.2', npx: { package: '@agentclientprotocol/codex-acp@2.2.2' } },
119
+ // 与 claude-acp 同一条规则:codex-account-limits 只核验过 2.3.0(2026-10-27 nightly;2.3.0 的
120
+ // authStatus 锚点在两处 capabilities 里重复,已加长到 initialize 独有后缀消歧),钉住直到配方随新版本移植。
121
+ distribution: { version: '2.3.0', npx: { package: '@agentclientprotocol/codex-acp@2.3.0' } },
121
122
  },
122
123
  {
123
124
  registryAgent: 'qoder',
124
125
  defaultName: 'qoder',
125
126
  defaultLabel: 'Qoder',
126
127
  detect: { commands: ['qodercli'] },
127
- // The temporary registry catch-up pin lives in axom-patch/cli/runtime.json so a newer
128
- // upstream release can take over without another Axom release.
128
+ // Native ACP is the sole Qoder entry. The exact --acp argv is pinned here; the package
129
+ // patch layer verifies the 1.1.75 package and its qoder-native-acp recipe hashes.
130
+ distribution: { version: '1.1.75', npx: { package: '@qoder-ai/qodercli@1.1.75', args: ['--acp'] } },
129
131
  },
130
132
  // `gemini` was here and is deliberately gone. `@google/gemini-cli --acp` now refuses `session/new`
131
133
  // outright ("This client is no longer supported for Gemini Code Assist for individuals … migrate
@@ -144,7 +146,7 @@ export const AXOM_AGENT_CATALOG = [
144
146
  // 其余字段照 registry#458 的条目写。
145
147
  supplement: {
146
148
  name: 'Kimi Code',
147
- version: '2.1.1',
149
+ version: '2.3.3',
148
150
  description: "Moonshot AI's next-gen coding agent",
149
151
  repository: 'https://github.com/MoonshotAI/kimi-code',
150
152
  website: 'https://moonshotai.github.io/kimi-code/',
@@ -152,13 +154,13 @@ export const AXOM_AGENT_CATALOG = [
152
154
  license: 'MIT',
153
155
  icon: 'https://cdn.agentclientprotocol.com/registry/v1/latest/kimi.svg',
154
156
  distribution: {
155
- binary: kimiCodeBinary('2.1.1', {
156
- 'darwin-aarch64': ['darwin-arm64', '8c3bad99571b16abd113ab5d511a98a36b852c0d5493991842a67b8c2bc4ecd0'],
157
- 'darwin-x86_64': ['darwin-x64', 'f6dc0289112c24119d070f9203d44bcd53580e380819db25b58be65643b75bd4'],
158
- 'linux-aarch64': ['linux-arm64', '5c68d74b139b47fb3457f3805a159cdfa942a6a98a159ab158a1baf93b024bbd'],
159
- 'linux-x86_64': ['linux-x64', 'b66cc3787bc9dfa6a06761305dc8878f98e7a24f79fa4494c00714229a1486e8'],
160
- 'windows-aarch64': ['win32-arm64', '72bb17fef0d165a5f61905ede7e57c552791905d605a778967d054fe702873d4'],
161
- 'windows-x86_64': ['win32-x64', '87756a53a9bb0915842d884eaf43f69a06b2e404bcbb05d41c519c12d4b410cb'],
157
+ binary: kimiCodeBinary('2.3.3', {
158
+ 'darwin-aarch64': ['darwin-arm64', 'd097b397db785e9bde07e9d169870e315dd9a0d9d6542727ea4a50e4a630a280'],
159
+ 'darwin-x86_64': ['darwin-x64', '9c42127a60488add5cfb8ca60d35f4df9d881552eb3b05268c3a691f7b3c5406'],
160
+ 'linux-aarch64': ['linux-arm64', '4b12a13d64dbf9cde631e4c74965ccd1b72bfd93892b30d8272b4bc6448fe45f'],
161
+ 'linux-x86_64': ['linux-x64', '0e4a766d38e5202a2413b8517bd4b3a2c00486327a6e231c98f99a0bbf7422e1'],
162
+ 'windows-aarch64': ['win32-arm64', '655a4b0e57090fa414aec6c69e4801c12a28e7ca75dd627bfc64340b600079fd'],
163
+ 'windows-x86_64': ['win32-x64', '08bd5fcb7b0d6b3bcd70ef42e58b73c884149905b0044544a941ad795f587c4a'],
162
164
  }),
163
165
  },
164
166
  },
@@ -173,21 +175,20 @@ export const AXOM_AGENT_CATALOG = [
173
175
  registryAgent: 'command-code',
174
176
  defaultName: 'command-code',
175
177
  defaultLabel: 'Command Code',
176
- // registry 没有它。1.66.0 还没有 `acp` 子命令,1.74.3 起有、真机试跑通过;2026-10-14 nightly
177
- // 升到 1.82.1:`acp` 子命令仍在(四个 bin 不变),commandcode-account-limits 配方已随
178
- // generate.py 移植(handler map 换了名字 u/dn→m/hn,锚点已跟)。包里有 `cmd`、`cmdc`、
178
+ // registry 没有它。1.66.0 还没有 `acp` 子命令,1.74.3 起有、真机试跑通过;2026-10-27 已到 1.88.0(10-25):`acp` 子命令仍在(四个 bin 不变),commandcode-account-limits 配方已随
179
+ // generate.py 移植(handler map 枚举每版改名 u/dn→m/hn→m/vn→m/bn,锚点已跟)。包里有 `cmd`、`cmdc`、
179
180
  // `commandcode`、`command-code` 四个 bin,npx 挑与包名同名的那个。没有 ACP 登录方法,只有一句
180
181
  // 说明(终端里跑 `cmd login`);凭据在 `~/.commandcode`,与用户自己装的那份共用。npm 上的许可证
181
182
  // 写的是 UNLICENSED(专有)。
182
183
  supplement: {
183
184
  name: 'Command Code',
184
- version: '1.82.1',
185
+ version: '1.88.0',
185
186
  description: 'Command Code, coding agent that continuously learns your coding taste.',
186
187
  website: 'https://commandcode.ai',
187
188
  authors: ['Command Code'],
188
189
  license: 'UNLICENSED',
189
190
  icon: 'https://commandcode.ai/favicon.ico',
190
- distribution: { npx: { package: 'command-code@1.82.1', args: ['acp'] } },
191
+ distribution: { npx: { package: 'command-code@1.88.0', args: ['acp'] } },
191
192
  },
192
193
  detect: { commands: ['command-code', 'commandcode'] },
193
194
  },
@@ -203,19 +204,19 @@ export const AXOM_AGENT_CATALOG = [
203
204
  // 2026-10-13 真机验证(ZCode 桌面 3.14.5 + 0.69.3):initialize → session/new → 首轮 prompt 通过,
204
205
  // 日志 "serving the coding-plan API key (config.json or store)"。0.63/0.64 那轮不兼容(模型只剩
205
206
  // 无授权的 builtin:bigmodel-coding-plan\GLM-5.3,首轮 CONFIGURATION_ERROR "Model creation failed",
206
- // 2026-10-06 两台机器复现)自 0.68.1 起修复。2026-10-14 nightly 升到 0.69.4:三份配方的目标文件
207
- // 与 0.69.3 字节一致(配方 versions 同时覆盖两版),subagent 探针在 0.69.4 补丁副本上通过、
208
- // pristine 证实上游仍未原生发出 subagent/async 变体。
207
+ // 2026-10-06 两台机器复现)自 0.68.1 起修复。2026-10-27 nightly 升到 0.76.3:subagent 探针在
208
+ // 0.76.3 补丁副本上通过、pristine 复现缺失(上游仍无 subagent/async 变体、无配额能力、无模型
209
+ // 兜底);全部锚点存活,配方纯重哈希。
209
210
  supplement: {
210
211
  name: 'ZCode',
211
- version: '0.69.4',
212
+ version: '0.76.3',
212
213
  description: 'Community bridge that drives the real ZCode app-server (GLM) over ACP.',
213
214
  repository: 'https://github.com/william0wang/zcode-acp',
214
215
  website: 'https://github.com/william0wang/zcode-acp',
215
216
  authors: ['William Wang'],
216
217
  license: 'Apache-2.0',
217
218
  icon: 'https://raw.githubusercontent.com/william0wang/zcode-acp/main/registry/zcode-acp/icon.svg',
218
- distribution: { npx: { package: 'zcode-acp-server@0.69.4' } },
219
+ distribution: { npx: { package: 'zcode-acp-server@0.76.3' } },
219
220
  },
220
221
  prerequisite: {
221
222
  label: 'ZCode',
@@ -238,7 +239,7 @@ export const AXOM_AGENT_CATALOG = [
238
239
  recommended: true,
239
240
  // pi-steering 与 pi-subagent-subwork 两份配方只核验过 0.0.34。registry 今天也在 0.0.34,
240
241
  // 但那是巧合而不是保护:上游一发 0.0.35,未钉版本的 Install 就会装上一个 steering/子代理
241
- // 归一都不生效的包。钉住后版本只随配方移植走(nightly-task.md)。
242
+ // 归一都不生效的包。钉住后版本只随配方移植走(DAILY_UPGRADE_CHECK.md)。
242
243
  distribution: { version: '0.0.34', npx: { package: 'pi-acp@0.0.34' } },
243
244
  prerequisite: {
244
245
  label: 'Pi',
@@ -287,27 +288,52 @@ export const AXOM_AGENT_CATALOG = [
287
288
  // registry 自己的条目(binary .par),我们只补名字与本机检测;`agy` 是 Antigravity 装好后放进 PATH 的命令。
288
289
  // distribution 钉住:antigravity 的账号配额靠 native extension(runtime.json
289
290
  // native_extensions,account-limits/antigravity-proxy.mjs),proxy 是 stdio sidecar,只在核验过的
290
- // 版本上声明能力。2026-10-14 nightly 升到 1.4.0:真实 1.4.0 .par 经 proxy 拉起,initialize 透传无损、
291
- // `axom/accountRateLimits` 能力注入成功(本地实测;配额 READ 属于用户凭据路径,留给真机冒烟)。
292
- // 归档 URL 抄自 registry 的 1.4.0 条目;registry 本身不给这个条目发 sha256,这里同样不发,校验语义与
293
- // registry 直装一致(binaryInstall:无 sha256 即不校验)。上游发新版时按 nightly-task.md 核验后同升。
291
+ // 版本上声明能力。2026-10-25 nightly 升到 1.4.3:真实 1.4.3 .par 经 proxy 拉起,initialize 透传无损、
292
+ // `axom/accountRateLimits`(v5)能力注入成功(本地实测;配额 READ 属于用户凭据路径,留给真机冒烟)。
293
+ // 归档 URL 抄自 registry 的 1.4.3 条目(六平台 HEAD 200);registry 本身不给这个条目发 sha256,这里同样不发,校验语义与
294
+ // registry 直装一致(binaryInstall:无 sha256 即不校验)。上游发新版时按 axom-patch/DAILY_UPGRADE_CHECK.md 核验后同升。
294
295
  {
295
296
  registryAgent: 'antigravity-acp',
296
297
  defaultName: 'google-antigravity',
297
298
  defaultLabel: 'Google Antigravity',
298
299
  detect: { commands: ['agy'] },
299
300
  distribution: {
300
- version: '1.4.0',
301
+ version: '1.4.3',
301
302
  binary: {
302
- 'darwin-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.4.0-darwin-arm64.zip', cmd: './agy_acp_server.par' },
303
- 'darwin-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.4.0-darwin-x86_64.zip', cmd: './agy_acp_server.par' },
304
- 'linux-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.0-linux-x86_64.zip', cmd: './agy_acp_server.par', args: ['--uid='] },
305
- 'linux-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.0-linux-arm64.zip', cmd: './agy_acp_server.par', args: ['--uid='] },
306
- 'windows-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.0-windows-x86_64.zip', cmd: './agy_acp_server.exe' },
307
- 'windows-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.0-windows-arm64.zip', cmd: './agy_acp_server.exe' },
303
+ 'darwin-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.4.3-darwin-arm64.zip', cmd: './agy_acp_server.par' },
304
+ 'darwin-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.4.3-darwin-x86_64.zip', cmd: './agy_acp_server.par' },
305
+ 'linux-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.3-linux-x86_64.zip', cmd: './agy_acp_server.par', args: ['--uid='] },
306
+ 'linux-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.3-linux-arm64.zip', cmd: './agy_acp_server.par', args: ['--uid='] },
307
+ 'windows-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.3-windows-x86_64.zip', cmd: './agy_acp_server.exe' },
308
+ 'windows-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.3-windows-arm64.zip', cmd: './agy_acp_server.exe' },
308
309
  },
309
310
  },
310
311
  },
312
+ // CodeBuddy(腾讯的编码 agent,npm 是它唯一的官方 CLI 分发)。registry 已收录 `codebuddy-code`
313
+ // (npx @tencent-ai/codebuddy-code --acp,包自带 @agentclientprotocol/sdk 依赖),于是这是最普通
314
+ // 的「registry 条目 + 本清单认证」形态:不 supplement(registry 有这个 id)、不 pathLaunch(有可装
315
+ // 产物,Axom 装自己托管的那份)、不钉版本(没有依赖版本的配方)、不配 launchEnv——本条就是
316
+ // CodeBuddy 本身,登录落在它的 copilot.tencent.com。
317
+ //
318
+ // 2026-10-19/21 真机实测(npm 2.169.0/2.171.0,ACP capture):`--acp` 起 initialize 通过,能力
319
+ // image/embeddedContext/mcp http+sse/loadSession/delegateTools/multitask;authMethods 声明
320
+ // iOA、external(Google/Github)、internal(WeChat)、selfhosted;登录链接经 `_codebuddy.ai/authUrl`
321
+ // 扩展通知推送(acpClient 的处理器)。凭据在 CODEBUDDY_CONFIG_DIR(默认 ~/.codebuddy)的
322
+ // credentials.json,登录走 ACP 认证流(一次性,与终端 CLI 共用)。
323
+ //
324
+ // 同一引擎另有 App 产品形态(App 内 product.json 声明 productName/endpoint/计费,经其自设的
325
+ // ACC_PRODUCT_CONFIG_PATH 环境变量切换)——那是**另一个产品、另一套账号**,npm 包名、registry
326
+ // id、CLI bin 都不带它的名字;Axom 只策展 npm 的 CodeBuddy,不策展那个 App 形态,也不替用户
327
+ // 预设产品开关(真需要的用户可经 agentEnv 自行覆盖)。
328
+ //
329
+ // detect 认 npm 包的两个主 bin(codebuddy 与 cbc;codebuddy-code/codebuddy-lowmem 是同名
330
+ // 变体,不另列)。
331
+ {
332
+ registryAgent: 'codebuddy-code',
333
+ defaultName: 'codebuddy',
334
+ defaultLabel: 'CodeBuddy',
335
+ detect: { commands: ['codebuddy', 'cbc'] },
336
+ },
311
337
  ];
312
338
  /**
313
339
  * Kimi Code 的六个平台归档:GitHub release `@moonshot-ai/kimi-code@<version>` 下的
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import path from 'node:path';
4
+ import semver from 'semver';
4
5
  import { fileURLToPath } from 'node:url';
6
+ import { managedRuntimeFor, verifyManagedRuntime, verifyRecordedManagedRuntime } from '../managedRuntime.js';
5
7
  import { nativeExtensionFor } from '../patchAssets.js';
6
8
  import { verifyPatchedFiles } from '../managedPatches.js';
7
9
  import { AdapterNotInstalledError, inside, labelFor } from '../builds/model.js';
@@ -45,10 +47,23 @@ function expandHomeInEnv(env) {
45
47
  return Object.fromEntries(Object.entries(env).map(([key, value]) => [key, value === '~' ? home : value.startsWith('~/') ? path.join(home, value.slice(2)) : value]));
46
48
  }
47
49
  export async function launchForBuild(build, root, options) {
50
+ const policy = options.policy ?? managedRuntimeFor(build.agentId);
51
+ // Only a caller resolving an explicit recorded old binding/rollback may use legacy launch rules.
52
+ // Removing runtimeReleaseId from the strict target never changes its policy classification.
53
+ const legacy = options.allowLegacy && !build.runtimeReleaseId && policy && (build.package !== policy.package || (semver.valid(build.version) !== null && semver.lt(build.version, policy.version)));
54
+ if (policy && !legacy) {
55
+ const verify = options.allowLegacy && build.runtimeReleaseId ? verifyRecordedManagedRuntime : verifyManagedRuntime;
56
+ await verify({ ...options, policy, build, storeRoot: root, initialize: false });
57
+ }
58
+ // A build recorded under a strict policy this CLI no longer ships launches like any other build:
59
+ // its runtimeReleaseId is history, and the patched-file check below still applies.
48
60
  const dir = inside(root, build.dir);
49
61
  const entry = inside(dir, build.launch.entry);
50
62
  if (build.kind === 'npm-patched' && build.package && build.patchedFiles) {
51
- await verifyPatchedFiles(inside(path.join(dir, 'node_modules'), build.package), build.patchedFiles);
63
+ if (build.patchedFilesBase !== undefined && build.patchedFilesBase !== 'node_modules')
64
+ throw new Error('Invalid Axom patched file base');
65
+ const modules = path.join(dir, 'node_modules');
66
+ await verifyPatchedFiles(build.patchedFilesBase === 'node_modules' ? modules : inside(modules, build.package), build.patchedFiles);
52
67
  }
53
68
  if (!existsSync(entry))
54
69
  throw new AdapterNotInstalledError(build.agentId, `build ${build.buildId} is missing its executable`);
@@ -62,7 +62,6 @@ const PLAIN_BLOCKERS = {
62
62
  'a permission or elicitation is unanswered': 'it is waiting for an answer',
63
63
  'the agent reports it is active': 'the agent says it is still working',
64
64
  'an async task or background terminal is still running': 'it has a background task or terminal still running',
65
- 'a delegation refresh is pending': 'its delegated access is being renewed',
66
65
  };
67
66
  /** Why a session may not be switched now (spec §6.3 step 2); empty = strictly idle. */
68
67
  export function switchBlockers(input) {
@@ -83,8 +82,6 @@ export function switchBlockers(input) {
83
82
  blockers.push('the agent reports it is active');
84
83
  if ((session.liveBackgroundWork ?? 0) > 0)
85
84
  blockers.push('an async task or background terminal is still running');
86
- if (session.delegation?.refreshPending)
87
- blockers.push('a delegation refresh is pending');
88
85
  const quiet = input.quietMs ?? SWITCH_QUIET_MS;
89
86
  const last = Math.max(input.lastActivityAt, session.lastFoldAt ?? 0);
90
87
  if (input.now - last < quiet)
@@ -113,6 +113,12 @@
113
113
  // // with this message instead of succeeding — lets a
114
114
  // // test prove a real agent-side rejection surfaces as
115
115
  // // a rejected promise
116
+ // authenticateNotifications?: Array<{method: string, params: unknown}>,
117
+ // // sent as client notifications at the top of the
118
+ // // `authenticate` handler, before anything else —
119
+ // // models an agent that pushes a login URL as an
120
+ // // extension notification (`_codebuddy.ai/authUrl`)
121
+ // // rather than eliciting or printing it
116
122
  // hangOnAuthenticate?: boolean, // if true, `authenticate` never resolves — the shape
117
123
  // // measured on a real agent that opens a browser and
118
124
  // // waits for its callback (spec §6.2: antigravity's
@@ -348,6 +354,13 @@ async function main() {
348
354
  writeFileSync(config.authenticateSink, JSON.stringify(ctx.params));
349
355
  if (config.authenticateRejectsWith)
350
356
  throw new Error(config.authenticateRejectsWith);
357
+ // Extension notifications an agent pushes while answering `authenticate` (measured on the real
358
+ // CodeBuddy adapter: `_codebuddy.ai/authUrl` with `{authUrl, provider}` — a URL channel that
359
+ // is neither `elicitation/create` nor stderr). Sent before the reply, so a client that only
360
+ // listens from the reply onward still misses them, exactly like the real one.
361
+ for (const { method, params } of config.authenticateNotifications ?? []) {
362
+ await ctx.client.notify(method, params);
363
+ }
351
364
  if (config.createElicitationDuringAuthenticate) {
352
365
  // `requestId` is filled in here rather than in the config: a login elicitation is
353
366
  // request-scoped (ACP's `ElicitationRequestScope` — "tied to a specific JSON-RPC request
@@ -16,8 +16,10 @@ import { expect, vi } from 'vitest';
16
16
  import { killKeepersUnder, tempDir } from '../../../test/keeperKit.js';
17
17
  import { AcpKeeperClient } from '../../keepers/acp/client.js';
18
18
  import { computeBuildVersion } from '../../keepers/kit/buildVersion.js';
19
+ import { readKeeperManifest } from '../../keepers/kit/bundle.js';
19
20
  import { keeperGen } from '../../keepers/kit/identity.js';
20
21
  import { KeeperLauncher } from '../../keepers/kit/launcher.js';
22
+ import { acquireStartupLock, releaseStartupLock } from '../../keepers/kit/lock.js';
21
23
  import { AgentSessionManager } from '../../agentSessions.js';
22
24
  import { AGENT_WIRE_VERSION } from '../../protocol/agent.js';
23
25
  import { TaskRegistry } from '../../update/taskRegistry.js';
@@ -97,6 +99,32 @@ export class KeeperWorld {
97
99
  this.agentDir = this.newAgentDir(`${prefix}agent-`);
98
100
  this.launcher = this.launcherFor(realDist);
99
101
  }
102
+ /** These integrations require keeper hosting, rather than fallback after an unrelated lock-port collision. */
103
+ static async create(prefix, includeOlderGeneration = false) {
104
+ const current = keeperGen((await readKeeperManifest(join(realDist, 'acp'))).buildVersion);
105
+ for (let attempt = 0; attempt < 100; attempt++) {
106
+ const candidate = new this(prefix);
107
+ const locks = [];
108
+ try {
109
+ const generations = [current];
110
+ if (includeOlderGeneration)
111
+ generations.push(candidate.olderGeneration().oldGen);
112
+ for (const gen of new Set(generations)) {
113
+ locks.push(await acquireStartupLock(candidate.launcher.pathsFor('acp', gen).lockPort));
114
+ }
115
+ return candidate;
116
+ }
117
+ catch (error) {
118
+ await candidate.dispose();
119
+ if (error.code !== 'EADDRINUSE')
120
+ throw error;
121
+ }
122
+ finally {
123
+ await Promise.all(locks.map(releaseStartupLock));
124
+ }
125
+ }
126
+ throw new Error('No unoccupied keeper-acp integration fixture identity');
127
+ }
100
128
  launcherFor(distDir) {
101
129
  return new KeeperLauncher({ configDir: this.configDir, workstationId: 'ws-t21', server: 'http://test.invalid', distDir, useSystemd: false,
102
130
  startTimeoutMs: 20_000, umask: 0o022 });
@@ -1,29 +1,15 @@
1
- /**
2
- * Every capture Task 17d's gate runs on, and how each becomes a processor raw log.
3
- *
4
- * - `real`: the committed, sanitised real captures (`*.jsonl` in this directory, by discovery).
5
- * - `qoder`: Qoder captures wrapped from the update-level Qoder fixtures (`qoderCaptures.ts`) — there
6
- * is no raw Qoder capture; a real one is T23's.
7
- * - `synthetic`: hand-written captures that pin the known ordering fixes — experiment 0.1's wire
8
- * (analysis B §0.1) and out-of-turn chunks followed by the next prompt (17b) — an interrupt
9
- * mid-turn (`session/cancel`), which no real capture has, and (T19 prerequisite) a `session/load`
10
- * with history and a Qoder `session/resume`.
11
- *
12
- * {@link buildLog} plays the daemon: the transducer (`fixtures.ts`), plus, for Qoder, the
13
- * `qoder_terminal_repair` note where `AcpAgents` enables the repair today (after the `session/new`
14
- * response) and the side-file notes the daemon's `BackgroundSideNotes` reader appends once the native
15
- * rows exist (after the turn, as the live repair sees them).
16
- */
17
- import { existsSync, readFileSync } from 'node:fs';
18
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
19
- import { tmpdir } from 'node:os';
20
- import { dirname, join } from 'node:path';
21
- import { BackgroundSideNotes, nativeTerminalHistoryPath } from '../../../qoderAdapter/backgroundTerminal.js';
1
+ import { join } from 'node:path';
22
2
  import { decodeStreamRecord } from '../../processor/record.js';
23
- import { sideDemand } from '../../processor/sideChannel.js';
24
- import { run } from '../../processor/test/harness.js';
25
3
  import { fixtureNames, RAW_DIR, readFixtureText, transduce } from './fixtures.js';
26
- import { captureText, CERTIFIED_QODER, down, qoderCaptures, up, update } from './qoderCaptures.js';
4
+ /** Lines → kit-stream ndjson (a synthetic `proc spawn` first, `t` = position). */
5
+ export function captureText(lines) {
6
+ const records = [JSON.stringify({ o: 0, t: 0, k: 'proc', kind: 'spawn', spawnId: 'capture', pid: 0, pgid: 0, startTime: 0 })];
7
+ lines.forEach((line, index) => records.push(JSON.stringify({ o: index + 1, t: index + 1, k: line.k, l: JSON.stringify(line.message) })));
8
+ return `${records.join('\n')}\n`;
9
+ }
10
+ export const up = (message) => ({ k: 'up', message });
11
+ export const down = (message) => ({ k: 'down', message });
12
+ export const update = (sessionId, value) => up({ jsonrpc: '2.0', method: 'session/update', params: { sessionId, update: value } });
27
13
  const SYNTHETIC_PROMPT = [{ type: 'text', text: 'go' }];
28
14
  const opening = (sessionId) => [
29
15
  down({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: 1, clientCapabilities: {} } }),
@@ -117,7 +103,7 @@ function loadThenPrompt() {
117
103
  ]) };
118
104
  }
119
105
  /**
120
- * T19 prerequisite: a Qoder cached `session/resume` (the certified runtime advertising
106
+ * T19 prerequisite: a cached `session/resume` (an adapter advertising
121
107
  * `sessionCapabilities.resume`) — no history, a config snapshot on the response, then one turn.
122
108
  */
123
109
  function resumeThenPrompt() {
@@ -125,7 +111,7 @@ function resumeThenPrompt() {
125
111
  return { name: 'synthetic-resume', kind: 'synthetic', text: captureText([
126
112
  down({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: 1, clientCapabilities: {} } }),
127
113
  up({ jsonrpc: '2.0', id: 1, result: { protocolVersion: 1, agentCapabilities: { loadSession: true, sessionCapabilities: { resume: {} } },
128
- authMethods: [], agentInfo: CERTIFIED_QODER } }),
114
+ authMethods: [], agentInfo: { name: '@agentclientprotocol/claude-agent-acp', version: '1.0.0' } } }),
129
115
  down({ jsonrpc: '2.0', id: 2, method: 'session/resume', params: { sessionId: S, cwd: '/work/synthetic', mcpServers: [] } }),
130
116
  up({ jsonrpc: '2.0', id: 2, result: { configOptions: [{ id: 'model', name: 'Model', type: 'select', currentValue: 'model-0001',
131
117
  options: [{ value: 'model-0001', name: 'xxxxx' }, { value: 'model-0002', name: 'xxxxx' }] }] } }),
@@ -134,116 +120,16 @@ function resumeThenPrompt() {
134
120
  up({ jsonrpc: '2.0', id: 3, result: { stopReason: 'end_turn' } }),
135
121
  ]) };
136
122
  }
137
- /**
138
- * Real Qoder captures (T30a/T30b): `qoder-real-<what>.jsonl`, written by `scripts/acp-capture.mjs` against `qodercli --acp` or the SDK
139
- * adapter and sanitised by `axom-cli/scripts/acp-sanitize.mjs` (with `--side` when the capture has one). Each is discovered like any
140
- * real capture, joins every gate that runs on all sources (`processor/rawFixtures.test.ts`, the goldens of
141
- * `processor/equivalence.test.ts`), and is played as the daemon plays a live Qoder session: the repair note right after the open's
142
- * response (`session/new`) or right after a load's open note, for the certified runtime only (`nativeTerminalHistoryPath`), and the
143
- * side file `<name>.side.ndjson` — the root native transcript rows — read through the daemon's `BackgroundSideNotes` after the turn.
144
- */
145
- export const REAL_QODER_PREFIX = 'qoder-real-';
146
- export const sideFileOf = (fixture) => fixture.replace(/\.jsonl$/, '.side.ndjson');
147
- /** The discovered real Qoder capture names (without `.jsonl`), for the static source lists of the gates. */
148
- export const realQoderNames = () => fixtureNames().filter((name) => name.startsWith(REAL_QODER_PREFIX)).map((name) => name.replace(/\.jsonl$/, ''));
149
- /** The capture's first open (`session/new` / `load` / `resume`), its response and the adapter's `initialize` identity. */
150
- export function openFacts(capture) {
151
- const lines = capture.filter((r) => r.kind === 'up' || r.kind === 'down')
152
- .map((r) => { try {
153
- return { r, m: JSON.parse(r.line) };
154
- }
155
- catch {
156
- return undefined;
157
- } }).filter((x) => x !== undefined);
158
- const responseTo = (id) => lines.find(({ r, m }) => r.kind === 'up' && m.method === undefined && JSON.stringify(m.id) === JSON.stringify(id));
159
- const init = lines.find(({ r, m }) => r.kind === 'down' && m.method === 'initialize');
160
- const agentInfo = init ? responseTo(init.m.id)?.m.result?.agentInfo : undefined;
161
- const open = lines.find(({ r, m }) => r.kind === 'down' && ['session/new', 'session/load', 'session/resume'].includes(m.method ?? ''));
162
- if (!open)
163
- return undefined;
164
- const response = responseTo(open.m.id);
165
- if (!response)
166
- return undefined;
167
- const mode = open.m.method.slice('session/'.length);
168
- const sessionId = String(mode === 'new' ? response.m.result?.sessionId : open.m.params?.sessionId);
169
- return { mode, responseO: response.r.o, sessionId, cwd: String(open.m.params?.cwd), ...(agentInfo ? { agentInfo } : {}) };
170
- }
171
- /** A real Qoder source from a capture's text and its side file's text (if any). */
172
- export function qoderRealSource(name, text, sideText, path) {
173
- const facts = openFacts(text.split('\n').filter((line) => line.trim()).map((line) => decodeStreamRecord(JSON.parse(line))));
174
- return { name, kind: 'qoder-real', text, ...(path ? { path } : {}),
175
- ...(facts ? { sessionId: facts.sessionId, cwd: facts.cwd } : {}),
176
- ...(sideText !== undefined ? { nativeRows: sideText.split('\n').filter((line) => line.trim()).map((line) => JSON.parse(line)) } : {}) };
177
- }
178
- function realQoderSource(fixture) {
179
- const side = join(RAW_DIR, sideFileOf(fixture));
180
- return qoderRealSource(fixture.replace(/\.jsonl$/, ''), readFixtureText(fixture), existsSync(side) ? readFileSync(side, 'utf8') : undefined, join(RAW_DIR, fixture));
181
- }
182
123
  export async function allSources() {
183
- const real = fixtureNames().map((name) => (name.startsWith(REAL_QODER_PREFIX) ? realQoderSource(name) : {
124
+ const real = fixtureNames().map((name) => ({
184
125
  name: name.replace(/\.jsonl$/, ''), kind: 'real', text: readFixtureText(name), path: join(RAW_DIR, name),
185
126
  }));
186
- const qoder = (await qoderCaptures()).map((capture) => ({ ...capture, kind: 'qoder' }));
187
- return [...real, ...qoder, experiment01(), outOfTurnThenPrompt(), interruptMidTurn(), loadThenPrompt(), resumeThenPrompt()];
127
+ return [...real, experiment01(), outOfTurnThenPrompt(), interruptMidTurn(), loadThenPrompt(), resumeThenPrompt()];
188
128
  }
189
129
  export function captureRecords(source) {
190
130
  return source.text.split('\n').filter((line) => line.trim()).map((line) => decodeStreamRecord(JSON.parse(line)));
191
131
  }
192
- /** Write the Qoder native rows where the certified repair reads them under `home`. */
193
- export async function writeNativeRows(source, home) {
194
- const path = nativeTerminalHistoryPath(CERTIFIED_QODER, source.sessionId, source.cwd, home);
195
- await mkdir(dirname(path), { recursive: true });
196
- await writeFile(path, `${source.nativeRows.map((row) => JSON.stringify(row)).join('\n')}\n`);
197
- return path;
198
- }
199
- /** The processor raw log for `source` (see the file comment). */
200
132
  export async function buildLog(source, options = {}) {
201
- const capture = captureRecords(source);
202
- const after = new Map();
203
- const repairNote = (nativeSessionId) => [{ kind: 'note', note: { type: 'qoder_terminal_repair', nativeSessionId } }];
204
- let afterLoadOpen;
205
- if (source.kind === 'qoder') {
206
- const openResponse = capture.find((record) => record.kind === 'up' && JSON.parse(record.line).id === 2);
207
- after.set(openResponse.o, () => repairNote(source.sessionId));
208
- }
209
- else if (source.kind === 'qoder-real') {
210
- // `AcpAgents.startSession`: the repair is enabled only where `nativeTerminalHistoryPath` certifies the runtime.
211
- const facts = openFacts(capture);
212
- if (facts && nativeTerminalHistoryPath(facts.agentInfo, facts.sessionId, facts.cwd, '/home/certification')) {
213
- if (facts.mode === 'load')
214
- afterLoadOpen = repairNote;
215
- else
216
- after.set(facts.responseO, () => repairNote(facts.sessionId));
217
- }
218
- }
219
- const transduced = transduce(capture, { after, ...(afterLoadOpen ? { afterLoadOpen } : {}),
220
- ...(options.launch !== undefined ? { launch: options.launch } : {}) });
221
- let sideNotes = 0;
222
- if (source.nativeRows && source.sessionId && source.cwd && after.size + (afterLoadOpen ? 1 : 0) > 0) {
223
- const home = await mkdtemp(join(tmpdir(), 'axom-raw-side-'));
224
- try {
225
- const path = await writeNativeRows(source, home);
226
- const reader = new BackgroundSideNotes({ path, nativeSessionId: source.sessionId, cwd: source.cwd });
227
- let next = transduced.session.log.head;
228
- // T19's loop: ask the processor's current demand, read, append the notes, fold; until a poll
229
- // brings nothing new (the live repair's 1 s timer, without the timer).
230
- for (let poll = 0; poll < 8; poll += 1) {
231
- const demand = sideDemand(transduced.processor.state);
232
- if (!demand)
233
- break;
234
- const notes = await reader.poll(demand);
235
- for (const note of notes)
236
- transduced.session.log.append({ kind: 'note', note });
237
- next = run(transduced.processor, transduced.session, next);
238
- sideNotes += notes.length;
239
- if (notes.length === 0)
240
- break;
241
- }
242
- reader.close();
243
- }
244
- finally {
245
- await rm(home, { recursive: true, force: true });
246
- }
247
- }
248
- return { ...transduced, records: transduced.session.log.read(0), source, sideNotes };
133
+ const transduced = transduce(captureRecords(source), options);
134
+ return { ...transduced, records: transduced.session.log.read(0), source, sideNotes: 0 };
249
135
  }
@@ -1,37 +1,3 @@
1
- /**
2
- * The opt-in real-adapter harness on the processor path (Task 30a). It replaces the real-adapter coverage Task 29 deleted with
3
- * `AcpSession` (`qoderContract.test.ts`'s `AXOM_ACP_CONTRACT_COMMAND` certification, `providerLifecycleBaseline`,
4
- * `qoderAdapter/acpHostIntegration`): every case opens through `AcpAgents.open` → `ProcessorSession`, over the local transport and
5
- * over a real keeper-acp (the built bundle in `dist/keepers`), and reads what the daemon would — the session's frames and its raw log.
6
- *
7
- * ## Isolation
8
- *
9
- * Every run gets its own temp config dir (keeper-acp's sockets, raw logs and outboxes live there, never `~/.config/axom`), its own
10
- * temp workspace and its own temp HOME. The adapter is started by a small launcher ({@link writeLauncher}) that:
11
- * - puts it in a process group of its own (the owned-group cleanup of the deleted certification), and
12
- * - replaces its environment with a minimal one read from a 0600 file in the run dir: PATH, the temp HOME, temp `CLAUDE_CONFIG_DIR`,
13
- * `CODEX_HOME`, `XDG_CONFIG_HOME` (all created), the build's own launch env, and only the variables named in
14
- * `AXOM_REAL_ADAPTER_ENV_PASS`. Nothing else of this process's environment (API keys, `CLAUDE_CODE_*`, tokens) reaches the
15
- * adapter, and the environment is never in the spawn's argv (keeper-acp records argv in its label).
16
- *
17
- * A case that needs a logged-in adapter uses `AXOM_REAL_ADAPTER_AUTH_HOME` as HOME instead of a temp one: a directory the operator
18
- * prepared for this purpose (e.g. `HOME=<dir> qodercli` once, then log in). The harness never defaults to the real HOME.
19
- *
20
- * ## Flags (all off: `npm test` runs only the hermetic cases and reports the rest as skipped, with the reason)
21
- *
22
- * | variable | effect |
23
- * |---|---|
24
- * | `AXOM_REAL_ADAPTER_HARNESS=1` | enables the real-adapter tiers at all |
25
- * | `AXOM_REAL_ADAPTER_AGENTS=claude-acp,codex-acp` | registry adapters for the no-prompt tier, installed into the temp config dir |
26
- * | `AXOM_ACP_CONTRACT_COMMAND` / `AXOM_ACP_CONTRACT_ARGS` | an adapter command (and a JSON argv, default `["--acp"]`): the no-prompt tier, and the Qoder certification's adapter |
27
- * | `AXOM_REAL_ADAPTER_PROMPTS=1` | also runs the cases that send prompts (model usage, see each case's cost) |
28
- * | `AXOM_REAL_ADAPTER_AUTH_HOME` | HOME for the prompt cases (required by them) |
29
- * | `AXOM_REAL_ADAPTER_ENV_PASS=A,B` | extra variables passed through to the adapter (token-based auth) |
30
- * | `AXOM_REAL_ADAPTER_TRANSPORTS=local,keeper` | which transports to run on (default both) |
31
- * | `AXOM_QODER_SDK_QODERCLI=<path>` | the real SDK adapter tier: `node dist/cli.js qoder-acp --qodercli <path>` (prompts) |
32
- */
33
- import { spawn } from 'node:child_process';
34
- import { existsSync } from 'node:fs';
35
1
  import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
36
2
  import { tmpdir } from 'node:os';
37
3
  import path, { join } from 'node:path';
@@ -68,7 +34,6 @@ export function realAdapterConfig(env = process.env) {
68
34
  ...(env.AXOM_REAL_ADAPTER_AUTH_HOME ? { authHome: env.AXOM_REAL_ADAPTER_AUTH_HOME } : {}),
69
35
  envPass: list(env.AXOM_REAL_ADAPTER_ENV_PASS),
70
36
  transports: transports.length > 0 ? transports : ['local', 'keeper'],
71
- ...(env.AXOM_QODER_SDK_QODERCLI ? { sdkQodercli: env.AXOM_QODER_SDK_QODERCLI } : {}),
72
37
  };
73
38
  }
74
39
  /** Why a tier does not run (undefined: it runs). Shown as the skipped test's name. */
@@ -84,7 +49,7 @@ export function skipReason(config, tier) {
84
49
  return 'needs a logged-in adapter: set AXOM_REAL_ADAPTER_AUTH_HOME';
85
50
  if (tier === 'qoder')
86
51
  return config.command ? undefined : 'set AXOM_ACP_CONTRACT_COMMAND to qodercli';
87
- return config.sdkQodercli ? undefined : 'set AXOM_QODER_SDK_QODERCLI to qodercli';
52
+ return undefined;
88
53
  }
89
54
  /**
90
55
  * A provider history failure fails certification but never prevents local teardown; each local step is guarded on its own, so one
@@ -364,14 +329,3 @@ export async function historyOf(opened) {
364
329
  history.push(event);
365
330
  return history;
366
331
  }
367
- /** `node dist/cli.js` (the built CLI), for the real SDK adapter (`qoder-acp --qodercli`). */
368
- export const BUILT_CLI = join(PACKAGE_ROOT, 'dist', 'cli.js');
369
- export const builtCliExists = () => existsSync(BUILT_CLI);
370
- /** Runs `node dist/cli.js qoder-acp --prepare-sdk` once (network: the SDK runtime) under `env`. */
371
- export async function prepareSdkRuntime(env) {
372
- await new Promise((resolve, reject) => {
373
- const child = spawn(process.execPath, [BUILT_CLI, 'qoder-acp', '--prepare-sdk'], { env, stdio: ['ignore', 'inherit', 'inherit'] });
374
- child.once('error', reject);
375
- child.once('exit', (code) => (code === 0 ? resolve() : reject(new Error(`qoder-acp --prepare-sdk exited ${code}`))));
376
- });
377
- }
@@ -19,7 +19,9 @@
19
19
  // stderrMcpHeaders?: boolean on session/new and session/load: every MCP header value received, on stderr (raw and JSON)
20
20
  // version?: string initialize's agentInfo.version (default '1.0.0') (Task 24)
21
21
  // noLoad?: boolean initialize does not advertise loadSession
22
+ // failInitializeOn?: string initialize answers an error once this marker exists
22
23
  // failLoad?: boolean session/load answers an error
24
+ // failLoadOn?: string session/load answers an error once this marker exists
23
25
  // loadWaitFor?: string session/load waits for this file before it answers
24
26
  // historyOnLoad?: string[] agent_message_chunk texts replayed (as history) before session/load answers
25
27
  // configOptions?: object[] session/new and session/load answer these configOptions (load: as configured — the agent keeps
@@ -118,8 +120,14 @@ function onLine(line) {
118
120
  }
119
121
  switch (message.method) {
120
122
  case 'initialize': {
121
- const answer = () => send({ id: message.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: !config.noLoad, ...(config.mcpHttp ? { mcpCapabilities: { http: true } } : {}) }, authMethods: [],
122
- agentInfo: { name: 'script-agent', version: config.version ?? '1.0.0' } } });
123
+ const answer = () => {
124
+ if (config.failInitializeOn && existsSync(at(config.failInitializeOn))) {
125
+ send({ id: message.id, error: { code: -32002, message: 'Initialize refused: old build unavailable' } });
126
+ return;
127
+ }
128
+ send({ id: message.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: !config.noLoad, ...(config.mcpHttp ? { mcpCapabilities: { http: true } } : {}) }, authMethods: [],
129
+ agentInfo: { name: 'script-agent', version: config.version ?? '1.0.0' } } });
130
+ };
123
131
  if (config.initializeWaitFor)
124
132
  void waitForFile(config.initializeWaitFor).then(answer);
125
133
  else
@@ -135,7 +143,7 @@ function onLine(line) {
135
143
  void (async () => {
136
144
  if (config.loadWaitFor)
137
145
  await waitForFile(config.loadWaitFor);
138
- if (config.failLoad) {
146
+ if (config.failLoad || (config.failLoadOn && existsSync(at(config.failLoadOn)))) {
139
147
  send({ id: message.id, error: { code: -32002, message: 'Resource not found: session' } });
140
148
  return;
141
149
  }