@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.
- package/README.md +18 -5
- package/dist/axom-patch/cli/README.md +100 -12
- package/dist/axom-patch/cli/account-limits/generate.py +15 -12
- package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
- package/dist/axom-patch/cli/claude-account-limits.json +9 -9
- package/dist/axom-patch/cli/codex-account-limits.json +11 -11
- package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
- package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
- package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
- package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
- package/dist/axom-patch/cli/nightly-task.md +55 -13
- package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
- package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
- package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
- package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
- package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
- package/dist/axom-patch/cli/qoder-steering.md +20 -25
- package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
- package/dist/axom-patch/cli/runtime.json +16 -18
- package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
- package/dist/axom-patch/cli/zcode-models.json +1 -2
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
- package/dist/cli.js +4 -36
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +65 -3
- package/dist/commands/workstation.js +73 -7
- package/dist/install/claude/commands.js +13 -13
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-arm64/axom-quic +0 -0
- package/dist/workstation/acp/acpAgents.js +102 -98
- package/dist/workstation/acp/acpClient.js +26 -0
- package/dist/workstation/acp/builds/install/npm.js +99 -10
- package/dist/workstation/acp/initializeResponse.js +6 -1
- package/dist/workstation/acp/managedPatches.js +189 -25
- package/dist/workstation/acp/managedRuntime.js +314 -0
- package/dist/workstation/acp/patchAssets.js +40 -0
- package/dist/workstation/acp/processor/checkpoint.js +25 -34
- package/dist/workstation/acp/processor/control.js +5 -78
- package/dist/workstation/acp/processor/runtime.js +5 -81
- package/dist/workstation/acp/processor/session.js +51 -80
- package/dist/workstation/acp/processor/state.js +6 -13
- package/dist/workstation/acp/processor/step.js +3 -20
- package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
- package/dist/workstation/acp/processor/updates.js +131 -105
- package/dist/workstation/acp/processorSession.js +8 -172
- package/dist/workstation/acp/registry/catalog.js +65 -39
- package/dist/workstation/acp/registry/launch.js +16 -1
- package/dist/workstation/acp/switching.js +0 -3
- package/dist/workstation/acp/test/fakeAgent.js +13 -0
- package/dist/workstation/acp/test/keeperWorld.js +28 -0
- package/dist/workstation/acp/test/raw/sources.js +16 -130
- package/dist/workstation/acp/test/realAdapter.js +1 -47
- package/dist/workstation/acp/test/scriptAgent.js +11 -3
- package/dist/workstation/agentFlow.js +1 -1
- package/dist/workstation/agentSessions.js +38 -143
- package/dist/workstation/channels/management.js +95 -11
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +16 -4
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +200 -0
- package/dist/workstation/control/commands.js +124 -0
- package/dist/workstation/control/daemonIdentity.js +53 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +63 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/runtimeClient.js +97 -0
- package/dist/workstation/control/runtimeHost.js +148 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/daemon.js +106 -6
- package/dist/workstation/daemonctl.js +77 -14
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/capabilities.js +92 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +353 -0
- package/dist/workstation/desktopControl/lobby.js +129 -0
- package/dist/workstation/desktopControl/macProvider.js +155 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/protocol.js +302 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1681 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +78 -0
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/text.js +42 -8
- package/dist/workstation/keepers/kit/bundle.js +21 -17
- package/dist/workstation/keepers/kit/launcher.js +47 -7
- package/dist/workstation/keepers/kit/paths.js +2 -1
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/protocol/agent.js +2 -15
- package/dist/workstation/protocol/management.js +93 -4
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +119 -0
- package/dist/workstation/rd/control.js +119 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/provider.js +222 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +45 -0
- package/dist/workstation/rd/update.js +171 -0
- package/dist/workstation/rd/upkeep.js +38 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/service/installer.js +104 -13
- package/dist/workstation/service/launchd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +44 -92
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/globalInstall.js +116 -0
- package/dist/workstation/update/npmInstaller.js +49 -23
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/runUpdate.js +3 -2
- package/dist/workstation/update/watchdog.js +16790 -334
- package/package.json +5 -5
- package/skills/axom/SKILL.md +11 -11
- package/skills/axom-definition/SKILL.md +3 -3
- package/skills/axom-platform/SKILL.md +3 -3
- package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
- package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
- package/dist/axom-patch/cli/qoder-steering.json +0 -52
- package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
- package/dist/commands/guide.js +0 -38
- package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
- package/dist/runtime-deps/qoder-sdk/package.json +0 -8
- package/dist/workstation/acp/delegationMcp.js +0 -24
- package/dist/workstation/acp/processor/sideChannel.js +0 -80
- package/dist/workstation/acp/qoderStartup.js +0 -45
- package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
- package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
- package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
- package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
- package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
- package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
- package/dist/workstation/qoderAdapter/contract.js +0 -78
- package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
- package/dist/workstation/qoderAdapter/history.js +0 -85
- package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
- package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
- package/dist/workstation/qoderAdapter/main.js +0 -95
- package/dist/workstation/qoderAdapter/mapper.js +0 -117
- package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
- package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
- package/dist/workstation/qoderAdapter/permissions.js +0 -61
- package/dist/workstation/qoderAdapter/profile.js +0 -93
- package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
- package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
- package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
- package/dist/workstation/qoderAdapter/session.js +0 -319
- package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
- package/dist/workstation/qoderAdapter/subwork.js +0 -85
- package/dist/workstation/qoderAdapter/taskControl.js +0 -38
- package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
- package/dist/workstation/qoderAdapter/usage.js +0 -37
- /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
|
|
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.
|
|
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/
|
|
95
|
-
distribution: { version: '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.
|
|
120
|
-
|
|
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
|
-
//
|
|
128
|
-
//
|
|
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.
|
|
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.
|
|
156
|
-
'darwin-aarch64': ['darwin-arm64', '
|
|
157
|
-
'darwin-x86_64': ['darwin-x64', '
|
|
158
|
-
'linux-aarch64': ['linux-arm64', '
|
|
159
|
-
'linux-x86_64': ['linux-x64', '
|
|
160
|
-
'windows-aarch64': ['win32-arm64', '
|
|
161
|
-
'windows-x86_64': ['win32-x64', '
|
|
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-
|
|
177
|
-
//
|
|
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.
|
|
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.
|
|
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-
|
|
207
|
-
//
|
|
208
|
-
//
|
|
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.
|
|
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.
|
|
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
|
-
// 归一都不生效的包。钉住后版本只随配方移植走(
|
|
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-
|
|
291
|
-
// `axom/accountRateLimits
|
|
292
|
-
// 归档 URL 抄自 registry 的 1.4.
|
|
293
|
-
// registry 直装一致(binaryInstall:无 sha256 即不校验)。上游发新版时按
|
|
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.
|
|
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.
|
|
303
|
-
'darwin-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.4.
|
|
304
|
-
'linux-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.
|
|
305
|
-
'linux-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.4.
|
|
306
|
-
'windows-x86_64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.
|
|
307
|
-
'windows-aarch64': { archive: 'https://dl.google.com/agy-extensions/releases/windows/agy-acp-server-1.4.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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) => (
|
|
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
|
-
|
|
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
|
|
202
|
-
|
|
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
|
|
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 = () =>
|
|
122
|
-
|
|
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
|
}
|