@platless/axom-cli 0.1.0 → 0.3.13
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 +1 -1
- package/dist/authScopes.js +19 -0
- package/dist/axom-patch/cli/README.md +110 -0
- package/dist/axom-patch/cli/pi-steering.json +37 -0
- package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +34 -0
- package/dist/axom-patch/cli/probes/pi-steering.mjs +80 -0
- package/dist/axom-patch/cli/probes/qoder-steering.mjs +89 -0
- package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +49 -0
- package/dist/axom-patch/cli/qoder-background-terminal.md +81 -0
- package/dist/axom-patch/cli/qoder-foreground-output.md +41 -0
- package/dist/axom-patch/cli/qoder-steering.json +52 -0
- package/dist/axom-patch/cli/qoder-steering.md +30 -0
- package/dist/axom-patch/cli/qoder-subagent-origin.json +73 -0
- package/dist/axom-patch/cli/qoder-subagent-subwork.md +73 -0
- package/dist/axom-patch/cli/runtime.json +37 -0
- package/dist/axom-patch/cli/steering/claude-steering-js.patch +89 -0
- package/dist/axom-patch/cli/steering/claude-steering-types.patch +28 -0
- package/dist/axom-patch/cli/steering/codex-steering.patch +57 -0
- package/dist/axom-patch/cli/steering/pi-steering.js +79 -0
- package/dist/cli.js +9 -1
- package/dist/commands/auth.js +4 -13
- package/dist/commands/execute.js +4 -4
- package/dist/commands/record.js +6 -6
- package/dist/commands/struct.js +4 -4
- package/dist/commands/workstation.js +2 -2
- package/dist/credentials.js +2 -2
- package/dist/install/claude/commands.js +13 -13
- package/dist/mcp.js +32 -14
- package/dist/workstation/acp/acpAgents.js +447 -166
- package/dist/workstation/acp/acpClient.js +232 -18
- package/dist/workstation/acp/acpSession.js +502 -74
- package/dist/workstation/acp/activityBusy.js +43 -0
- package/dist/workstation/acp/activityReporting.js +54 -0
- package/dist/workstation/acp/adoption.js +107 -49
- package/dist/workstation/acp/authUrlSniffer.js +36 -0
- package/dist/workstation/acp/contract.js +11 -6
- package/dist/workstation/acp/delegationMcp.js +24 -0
- package/dist/workstation/acp/draftInterceptor.js +9 -0
- package/dist/workstation/acp/managedPatches.js +228 -0
- package/dist/workstation/acp/patchAssets.js +63 -0
- package/dist/workstation/acp/permission.js +300 -34
- package/dist/workstation/acp/qoderStartup.js +45 -0
- package/dist/workstation/acp/registry/catalog.js +263 -4
- package/dist/workstation/acp/registry/detect.js +55 -0
- package/dist/workstation/acp/sessionScope.js +19 -0
- package/dist/workstation/acp/sessionUpdateMapper.js +39 -21
- package/dist/workstation/acp/terminalLogin.js +197 -0
- package/dist/workstation/acp/test/fakeAgent.js +185 -9
- package/dist/workstation/acp/validate.js +33 -5
- package/dist/workstation/agentSessions.js +532 -44
- package/dist/workstation/channels/agent.js +7 -1
- package/dist/workstation/channels/computer.js +152 -4
- package/dist/workstation/channels/management.js +3 -3
- package/dist/workstation/config.js +3 -1
- package/dist/workstation/connection/encryptedChannel.js +88 -21
- package/dist/workstation/daemon.js +25 -3
- package/dist/workstation/desktop/binding.js +36 -0
- package/dist/workstation/desktop/bridge.js +212 -0
- package/dist/workstation/desktop/flow-control.js +52 -0
- package/dist/workstation/desktop/focused-text.js +130 -0
- package/dist/workstation/desktop/protocol.js +35 -0
- package/dist/workstation/fileOps.js +277 -0
- package/dist/workstation/filePaths.js +174 -0
- package/dist/workstation/fileWorker.js +81 -0
- package/dist/workstation/fileWorkerClient.js +121 -0
- package/dist/workstation/oauth/deviceAuthorization.js +21 -12
- package/dist/workstation/protocol/agent.js +154 -5
- package/dist/workstation/protocol/common.js +6 -0
- package/dist/workstation/protocol/computer.js +92 -3
- package/dist/workstation/qoderAdapter/backgroundProcess.js +106 -0
- package/dist/workstation/qoderAdapter/backgroundTerminal.js +262 -0
- package/dist/workstation/qoderAdapter/completedContinuation.js +139 -0
- package/dist/workstation/qoderAdapter/contract.js +78 -0
- package/dist/workstation/qoderAdapter/foregroundOutput.js +30 -0
- package/dist/workstation/qoderAdapter/history.js +87 -0
- package/dist/workstation/qoderAdapter/identityIndex.js +191 -0
- package/dist/workstation/qoderAdapter/inputQueue.js +42 -0
- package/dist/workstation/qoderAdapter/main.js +93 -0
- package/dist/workstation/qoderAdapter/mapper.js +117 -0
- package/dist/workstation/qoderAdapter/observerPlugin.js +245 -0
- package/dist/workstation/qoderAdapter/observerReceiver.js +187 -0
- package/dist/workstation/qoderAdapter/permissions.js +61 -0
- package/dist/workstation/qoderAdapter/profile.js +93 -0
- package/dist/workstation/qoderAdapter/sdkDriver.js +49 -0
- package/dist/workstation/qoderAdapter/session.js +319 -0
- package/dist/workstation/qoderAdapter/subagentMeta.js +28 -0
- package/dist/workstation/qoderAdapter/subwork.js +77 -0
- package/dist/workstation/qoderAdapter/taskControl.js +38 -0
- package/dist/workstation/qoderAdapter/taskEvent.js +26 -0
- package/dist/workstation/qoderAdapter/taskLedger.js +163 -0
- package/dist/workstation/qoderAdapter/types.js +1 -0
- package/dist/workstation/qoderAdapter/usage.js +37 -0
- package/dist/workstation/register.js +4 -0
- package/dist/workstation/tools/browser.js +394 -0
- package/dist/workstation/tools/client.js +269 -0
- package/dist/workstation/tools/core.js +50 -0
- package/dist/workstation/tools/files.js +525 -0
- package/dist/workstation/tools/keeperProcess.js +114 -0
- package/dist/workstation/tools/terminal.js +214 -0
- package/dist/workstation/tools/validation.js +50 -0
- package/dist/workstation/update/coordinator.js +29 -0
- package/dist/workstation/update/npmInstaller.js +2 -0
- package/dist/workstation/update/ptyPreflight.js +24 -0
- package/dist/workstation/update/taskRegistry.js +44 -20
- package/dist/workstation/update/watchdog.js +66 -0
- package/package.json +14 -4
- package/skills/axom/SKILL.md +55 -17
- package/skills/axom-definition/SKILL.md +32 -32
- package/skills/axom-platform/SKILL.md +39 -39
|
@@ -1,18 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axom 认证过的 agent 清单 —— 取代 `officialCli.ts`。
|
|
3
|
+
*
|
|
4
|
+
* ## 它是什么:Axom 对官方 registry 的本地扩展层
|
|
5
|
+
*
|
|
6
|
+
* 一个 agent 在本清单里,就是我们真机验证过(roster 的 `curated`),设置页把它放进展开区;其余官方
|
|
7
|
+
* 条目进折叠的 "Unverified Agents"。本清单能做的事,各有一节在下面:
|
|
8
|
+
*
|
|
9
|
+
* - **推荐**:`recommended` 是静态的,只给愿意向任何一台新机器推荐的(claude、codex、pi)。
|
|
10
|
+
* - **本地检测**(`detect`,见 `detect.ts`):PATH 上的命令或固定文件位置,只看文件在不在、不起进程。
|
|
11
|
+
* 检测到 = 这台机器的用户自己在用它,于是它也进推荐位,原因写"本机在用"。
|
|
12
|
+
* - **前置依赖**(`prerequisite`):适配器之外、它起得来所必需的本体(Pi 的 CLI、ZCode 桌面应用),
|
|
13
|
+
* 同样靠 `detect` 判断在不在;有 `install` 就由 Install 先装,没有就只告诉用户怎么办。
|
|
14
|
+
* - **补充、取代与钉版本**:`supplement` / `supersedes` / `distribution`,以及 `launchEnv`。
|
|
15
|
+
*
|
|
16
|
+
* ## 托管与检测是两件事
|
|
17
|
+
*
|
|
18
|
+
* Axom 启动的**一律是 Axom 托管的那份**——npx 缓存、`uv tool dir`、`<config dir>/acp-adapters` 里我们
|
|
19
|
+
* 自己装的、钉了版本的副本,与用户自己的安装隔离、独立升级。`adapterInstalled` 只回答这一份在不在。
|
|
20
|
+
* 用户 PATH 上的 `kimi`、`command-code` **不算** Axom 已安装,也从不被拿来启动:它只是 `detect` 的
|
|
21
|
+
* 推荐信号。两份共用数据目录(`~/.kimi-code`、`~/.commandcode`),所以登录是共享的。
|
|
22
|
+
*
|
|
23
|
+
* ## 它与被它取代的那张表不是一回事
|
|
24
|
+
*
|
|
25
|
+
* `officialCli.ts` 断言的是"这个 agent 的官方 CLI 在不在 PATH",而那个信号既不决定能否启动
|
|
26
|
+
* (npx 适配器多数自带引擎),也不决定能否使用(凭据才是),于是它撑起来的 `Installed` 徽章
|
|
27
|
+
* 是一句没人能负责的话。本清单不做任何这类断言:它只提供**我们自己认证过**的三样东西 ——
|
|
28
|
+
* 用户友好的默认名字、这个 agent 除适配器之外还需要什么、以及它是否进推荐位;本地检测也只回答
|
|
29
|
+
* "用户自己装了没有",从不替 Axom 托管的那份作答。
|
|
30
|
+
*
|
|
31
|
+
* ## 为什么不为全部 39 个 agent 建表
|
|
32
|
+
*
|
|
33
|
+
* 见 spec §2.3:适配器需不需要本机 vendor CLI 是**版本级**属性,只能靠扒依赖树猜,而且完全
|
|
34
|
+
* 没有必要 —— 试跑失败时 agent 自己那句话(`Pi needs to be installed. Install it via …`)
|
|
35
|
+
* 带着确切的安装命令,且永远与它自身版本同步。本清单只覆盖我们真机验证过的少数几个。
|
|
36
|
+
*
|
|
37
|
+
* ## `prerequisite.install` 是安全边界
|
|
38
|
+
*
|
|
39
|
+
* 它会在工作站上**直接执行**,不弹二次确认。所以它只能是本文件里的字面量参数数组,永远不接受
|
|
40
|
+
* 来自 registry、agent 响应、网络内容或用户输入的字符串;以 argv 形式交给 execFile,不经 shell。
|
|
41
|
+
* `catalog.test.ts` 有一条机械守卫盯着这件事。
|
|
42
|
+
*
|
|
43
|
+
* ## `distribution` 覆盖 registry 的安装来源
|
|
44
|
+
*
|
|
45
|
+
* 这里写下的来源**整个替换** registry 的 `version` 与 `distribution`,id 与展示名仍是 registry 的,
|
|
46
|
+
* 于是已有的配置条目、roster、安装、启动、更新都不用知道发生过替换(见 {@link withCatalogOverrides})。
|
|
47
|
+
* 本文件里的是长期的(Qoder:registry 落后于验证过的原生版本);临时的钉版本放在
|
|
48
|
+
* axom-patch/cli/runtime.json,按 registry 版本挑选(`when_registry_version` / `until_registry_version`,
|
|
49
|
+
* 见 `patchAssets.ts`),registry 一到就自己让位。两者与上面的 `install` 是同一条安全边界:只能是
|
|
50
|
+
* 随 CLI 发布、钉死版本、由真机验证过的可信配置。
|
|
51
|
+
*
|
|
52
|
+
* ## `supplement` 与 `supersedes`:registry 缺的条目、registry 不肯撤的条目
|
|
53
|
+
*
|
|
54
|
+
* registry 的 `kimi` 跟的是 `MoonshotAI/kimi-cli`,那个仓库已归档,停在 1.52.0;继任者 Kimi Code
|
|
55
|
+
* (`MoonshotAI/kimi-code`)的条目 `kimi-code` 还在 registry#458 里等审核。Command Code 与社区的
|
|
56
|
+
* ZCode 适配器(registry#566、#650)也都还没进 registry。registry 没有「已停止维护」这样的字段
|
|
57
|
+
* (agent.schema.json 只有 `preview` 渠道),所以由本清单声明:
|
|
58
|
+
*
|
|
59
|
+
* - `supplement`:registry 里**没有**这个 id 时由我们补上的整条条目,钉死版本。registry 一收录这个 id
|
|
60
|
+
* 就用 registry 的,之后的版本由 registry 的机器人每小时跟进,补充条目不再生效。
|
|
61
|
+
* - `supersedes`:被这个条目取代的 registry id,从列表里隐藏。Kimi Code 装上后会把旧版的数据目录
|
|
62
|
+
* 迁走,旧适配器从此看不见用户的登录(2026-09-25 实测),两个并列只会让人选错。
|
|
63
|
+
*
|
|
64
|
+
* ## `launchEnv`:Axom 启动适配器时带的环境变量
|
|
65
|
+
*
|
|
66
|
+
* 经 ACP 适配器拉起的 agent 跑在无头/SDK 模式,它们的默认工具集与用户在终端里交互使用时**不同**,
|
|
67
|
+
* 而差出来的恰好是我们要的东西。2026-09-30 实测:claude-agent-acp 0.84.0 拉起的 Claude Code 没有
|
|
68
|
+
* TaskCreate/TaskUpdate/TodoWrite(ToolSearch 答 "No matching deferred tools found"),codex-acp
|
|
69
|
+
* 2.0.1 拉起的 Codex 0.159.2 没有 `update_plan`(问它自己答 "NO")——于是这两个 agent 从不发 ACP
|
|
70
|
+
* `plan`,生产上三天 218 个外部会话一张计划卡都没有。各自打开一个开关后同一段提示词分别发出 14 次与
|
|
71
|
+
* 4 次 `plan`。
|
|
72
|
+
*
|
|
73
|
+
* 这里写下的变量并进 registry 条目的 `distribution.*.env`,所以它是**最底下的一层**:registry
|
|
74
|
+
* 自己声明的 env、工作站配置、用户按 agent 配的 `agentEnv` 都能覆盖它(见 `connectAgent`)。一个
|
|
75
|
+
* 用户自己配了 `CODEX_CONFIG` 的 Codex 因此会整份替换掉这里的值——那是显式配置,按它来;要保留计划
|
|
76
|
+
* 工具,把 `tools.update_plan.enabled` 写进自己那份里。
|
|
77
|
+
*
|
|
78
|
+
* 这些开关不是上游文档化的稳定接口,适配器升级时要复核仍然生效(上面那段实测的做法:直接用 ACP
|
|
79
|
+
* 客户端拉起适配器,问它有没有计划工具)。
|
|
80
|
+
*/
|
|
81
|
+
import { patchOverridesFor } from '../patchAssets.js';
|
|
82
|
+
// Temporary distribution/environment overrides are bundled from axom-patch/cli/runtime.json.
|
|
1
83
|
export const AXOM_AGENT_CATALOG = [
|
|
2
|
-
{
|
|
3
|
-
|
|
84
|
+
{
|
|
85
|
+
registryAgent: 'claude-acp',
|
|
86
|
+
defaultName: 'claude-agent',
|
|
87
|
+
defaultLabel: 'Claude Agent',
|
|
88
|
+
recommended: true,
|
|
89
|
+
detect: { commands: ['claude'] },
|
|
90
|
+
},
|
|
91
|
+
{ registryAgent: 'codex-acp', defaultName: 'codex', defaultLabel: 'Codex', recommended: true, detect: { commands: ['codex'] } },
|
|
92
|
+
{
|
|
93
|
+
registryAgent: 'qoder',
|
|
94
|
+
defaultName: 'qoder',
|
|
95
|
+
defaultLabel: 'Qoder',
|
|
96
|
+
detect: { commands: ['qodercli'] },
|
|
97
|
+
// The registry still launches 0.2.14, which only reports legacy `models`. The tested native
|
|
98
|
+
// release reports ACP configOptions for model/mode and accepts session/set_config_option.
|
|
99
|
+
// Keep registry installs on the same verified runtime as the native production baseline.
|
|
100
|
+
distribution: { version: '1.1.61', npx: { package: '@qoder-ai/qodercli@1.1.61', args: ['--acp'] } },
|
|
101
|
+
},
|
|
4
102
|
// `gemini` was here and is deliberately gone. `@google/gemini-cli --acp` now refuses `session/new`
|
|
5
103
|
// outright ("This client is no longer supported for Gemini Code Assist for individuals … migrate
|
|
6
104
|
// to the Antigravity suite"), so recommending it puts an agent in the popover's top tier that
|
|
7
105
|
// cannot open a session for whoever takes the recommendation. It stays in the ACP registry and can
|
|
8
106
|
// still be added by hand from the collapsed section; what is withdrawn is Axom's own endorsement,
|
|
9
107
|
// which is the only thing this file publishes.
|
|
108
|
+
{
|
|
109
|
+
registryAgent: 'kimi-code',
|
|
110
|
+
defaultName: 'kimi-code',
|
|
111
|
+
defaultLabel: 'Kimi Code',
|
|
112
|
+
// Kimi Code 发布独立二进制(GitHub release,6 平台 tar.gz,各带 `.sha256`;归档根目录就是 `kimi`,
|
|
113
|
+
// Windows 是 `kimi.exe`),不依赖本机 Node —— npm 包 `@moonshot-ai/kimi-code` 要 Node >= 22.19,
|
|
114
|
+
// 会把 Node 版本变成又一个"装好了却起不来"的理由。`acp` 子命令是它的 ACP 服务端;凭据在
|
|
115
|
+
// `~/.kimi-code`,与用户自己装的那份共用——实测把二进制挪到别的目录照样免登录开会话。
|
|
116
|
+
// 其余字段照 registry#458 的条目写。
|
|
117
|
+
supplement: {
|
|
118
|
+
name: 'Kimi Code',
|
|
119
|
+
version: '2.1.1',
|
|
120
|
+
description: "Moonshot AI's next-gen coding agent",
|
|
121
|
+
repository: 'https://github.com/MoonshotAI/kimi-code',
|
|
122
|
+
website: 'https://moonshotai.github.io/kimi-code/',
|
|
123
|
+
authors: ['Moonshot AI'],
|
|
124
|
+
license: 'MIT',
|
|
125
|
+
icon: 'https://cdn.agentclientprotocol.com/registry/v1/latest/kimi.svg',
|
|
126
|
+
distribution: {
|
|
127
|
+
binary: kimiCodeBinary('2.1.1', {
|
|
128
|
+
'darwin-aarch64': ['darwin-arm64', '8c3bad99571b16abd113ab5d511a98a36b852c0d5493991842a67b8c2bc4ecd0'],
|
|
129
|
+
'darwin-x86_64': ['darwin-x64', 'f6dc0289112c24119d070f9203d44bcd53580e380819db25b58be65643b75bd4'],
|
|
130
|
+
'linux-aarch64': ['linux-arm64', '5c68d74b139b47fb3457f3805a159cdfa942a6a98a159ab158a1baf93b024bbd'],
|
|
131
|
+
'linux-x86_64': ['linux-x64', 'b66cc3787bc9dfa6a06761305dc8878f98e7a24f79fa4494c00714229a1486e8'],
|
|
132
|
+
'windows-aarch64': ['win32-arm64', '72bb17fef0d165a5f61905ede7e57c552791905d605a778967d054fe702873d4'],
|
|
133
|
+
'windows-x86_64': ['win32-x64', '87756a53a9bb0915842d884eaf43f69a06b2e404bcbb05d41c519c12d4b410cb'],
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
supersedes: ['kimi'],
|
|
138
|
+
// 官方安装器把二进制放在 `~/.kimi-code/bin`,不一定进了 daemon 看得见的 PATH。
|
|
139
|
+
detect: {
|
|
140
|
+
commands: ['kimi'],
|
|
141
|
+
locations: everyPlatform(['~/.kimi-code/bin/kimi'], ['~/.kimi-code/bin/kimi.exe']),
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
registryAgent: 'command-code',
|
|
146
|
+
defaultName: 'command-code',
|
|
147
|
+
defaultLabel: 'Command Code',
|
|
148
|
+
// registry 没有它。1.66.0 还没有 `acp` 子命令,1.74.3 起有、真机试跑通过。包里有 `cmd`、`cmdc`、
|
|
149
|
+
// `commandcode`、`command-code` 四个 bin,npx 挑与包名同名的那个。没有 ACP 登录方法,只有一句
|
|
150
|
+
// 说明(终端里跑 `cmd login`);凭据在 `~/.commandcode`,与用户自己装的那份共用。npm 上的许可证
|
|
151
|
+
// 写的是 UNLICENSED(专有)。
|
|
152
|
+
supplement: {
|
|
153
|
+
name: 'Command Code',
|
|
154
|
+
version: '1.74.3',
|
|
155
|
+
description: 'Command Code, coding agent that continuously learns your coding taste.',
|
|
156
|
+
website: 'https://commandcode.ai',
|
|
157
|
+
authors: ['Command Code'],
|
|
158
|
+
license: 'UNLICENSED',
|
|
159
|
+
icon: 'https://commandcode.ai/favicon.ico',
|
|
160
|
+
distribution: { npx: { package: 'command-code@1.74.3', args: ['acp'] } },
|
|
161
|
+
},
|
|
162
|
+
detect: { commands: ['command-code', 'commandcode'] },
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
registryAgent: 'zcode-acp',
|
|
166
|
+
defaultName: 'zcode',
|
|
167
|
+
defaultLabel: 'ZCode',
|
|
168
|
+
unverified: 'zcode-acp 0.63/0.64 与 ZCode 桌面 3.14.x 不兼容:3.14 不写 ~/.zcode/v2/config.json,模型只剩无授权的 ' +
|
|
169
|
+
'builtin:bigmodel-coding-plan\\GLM-5.3,首轮 CONFIGURATION_ERROR "Model creation failed"(2026-10-06 两台机器复现)。',
|
|
170
|
+
// 社区适配器(registry#566、#650 都还没合),驱动的是 ZCode 桌面应用自带的 `zcode app-server`,
|
|
171
|
+
// 所以 ZCode 本体是前置依赖,而且没有一条我们能替用户跑的安装命令 —— 只能告诉他去装。
|
|
172
|
+
// 凭据在 `~/.zcode/v2/config.json`,由 ZCode 自己登录写下。Linux 上本体没有固定路径,只认 PATH。
|
|
173
|
+
supplement: {
|
|
174
|
+
name: 'ZCode',
|
|
175
|
+
version: '0.63.0',
|
|
176
|
+
description: 'Community bridge that drives the real ZCode app-server (GLM) over ACP.',
|
|
177
|
+
repository: 'https://github.com/william0wang/zcode-acp',
|
|
178
|
+
website: 'https://github.com/william0wang/zcode-acp',
|
|
179
|
+
authors: ['William Wang'],
|
|
180
|
+
license: 'Apache-2.0',
|
|
181
|
+
icon: 'https://raw.githubusercontent.com/william0wang/zcode-acp/main/registry/zcode-acp/icon.svg',
|
|
182
|
+
distribution: { npx: { package: 'zcode-acp-server@0.63.0' } },
|
|
183
|
+
},
|
|
184
|
+
prerequisite: {
|
|
185
|
+
label: 'ZCode',
|
|
186
|
+
detect: {
|
|
187
|
+
commands: ['zcode'],
|
|
188
|
+
locations: {
|
|
189
|
+
'darwin-aarch64': ['/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs'],
|
|
190
|
+
'darwin-x86_64': ['/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs'],
|
|
191
|
+
'windows-aarch64': ['%LOCALAPPDATA%/Programs/ZCode/resources/glm/zcode.cjs'],
|
|
192
|
+
'windows-x86_64': ['%LOCALAPPDATA%/Programs/ZCode/resources/glm/zcode.cjs'],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
hint: 'Install the ZCode desktop app (https://zcode.z.ai) and sign in once.',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
10
198
|
{
|
|
11
199
|
registryAgent: 'pi-acp',
|
|
12
200
|
defaultName: 'pi',
|
|
13
201
|
defaultLabel: 'Pi',
|
|
14
|
-
|
|
15
|
-
|
|
202
|
+
recommended: true,
|
|
203
|
+
prerequisite: {
|
|
204
|
+
label: 'Pi',
|
|
205
|
+
detect: { commands: ['pi'] },
|
|
16
206
|
// 不用 `curl -fsSL https://pi.dev/install.sh | sh`:读过那个脚本,它最终跑的就是这一条
|
|
17
207
|
// (`npm install -g --ignore-scripts @earendil-works/pi-coding-agent`),装出来的是同一个
|
|
18
208
|
// npm 包 —— pi 没有原生分发。脚本唯一的增值是 Node 不够新时帮你装 Node,而那一步是交互式的
|
|
@@ -22,8 +212,77 @@ export const AXOM_AGENT_CATALOG = [
|
|
|
22
212
|
requiresNode: '>=22.19.0',
|
|
23
213
|
},
|
|
24
214
|
},
|
|
215
|
+
// registry 自己的条目(binary .par),我们只补名字与本机检测;`agy` 是 Antigravity 装好后放进 PATH 的命令。
|
|
216
|
+
{ registryAgent: 'antigravity-acp', defaultName: 'google-antigravity', defaultLabel: 'Google Antigravity', detect: { commands: ['agy'] } },
|
|
25
217
|
];
|
|
218
|
+
/**
|
|
219
|
+
* Kimi Code 的六个平台归档:GitHub release `@moonshot-ai/kimi-code@<version>` 下的
|
|
220
|
+
* `kimi-code-<target>.tar.gz`,`sha256` 抄自同名 `.sha256` 资产。版本升级时三样一起换。
|
|
221
|
+
*/
|
|
222
|
+
function kimiCodeBinary(version, targets) {
|
|
223
|
+
const release = `https://github.com/MoonshotAI/kimi-code/releases/download/@moonshot-ai/kimi-code@${version}`;
|
|
224
|
+
return Object.fromEntries(Object.entries(targets).map(([triple, [target, sha256]]) => [triple, {
|
|
225
|
+
archive: `${release}/kimi-code-${target}.tar.gz`,
|
|
226
|
+
sha256,
|
|
227
|
+
cmd: triple.startsWith('windows-') ? './kimi.exe' : './kimi',
|
|
228
|
+
args: ['acp'],
|
|
229
|
+
}]));
|
|
230
|
+
}
|
|
231
|
+
/** 同一组位置用于全部平台(Windows 三元组用 `windows` 那组)。 */
|
|
232
|
+
function everyPlatform(unix, windows) {
|
|
233
|
+
return {
|
|
234
|
+
'darwin-aarch64': unix, 'darwin-x86_64': unix, 'linux-aarch64': unix, 'linux-x86_64': unix,
|
|
235
|
+
'windows-aarch64': windows, 'windows-x86_64': windows,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
26
238
|
const BY_ID = new Map(AXOM_AGENT_CATALOG.map((entry) => [entry.registryAgent, entry]));
|
|
27
239
|
export function catalogEntryFor(registryAgent) {
|
|
28
240
|
return BY_ID.get(registryAgent);
|
|
29
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* The registry entry as Axom will actually install and launch it: unchanged unless something
|
|
244
|
+
* overrides it. A distribution replaces the registry's `version` and `distribution` — a runtime.json
|
|
245
|
+
* override that applies at this registry version first, else this catalog's own. Launch env is merged
|
|
246
|
+
* under every distribution's own `env` (the registry's declared variables win over ours). Identity
|
|
247
|
+
* and the registry's own words never move (see the file header).
|
|
248
|
+
*/
|
|
249
|
+
export function applyCatalogOverrides(agent) {
|
|
250
|
+
const entry = catalogEntryFor(agent.id);
|
|
251
|
+
const patch = patchOverridesFor(agent.id, agent.version);
|
|
252
|
+
const pinned = patch.distribution ?? entry?.distribution;
|
|
253
|
+
const launchEnv = { ...entry?.launchEnv, ...patch.env };
|
|
254
|
+
let effective = agent;
|
|
255
|
+
if (pinned) {
|
|
256
|
+
const { version, ...distribution } = pinned;
|
|
257
|
+
effective = { ...effective, version, distribution };
|
|
258
|
+
}
|
|
259
|
+
if (Object.keys(launchEnv).length) {
|
|
260
|
+
effective = { ...effective, distribution: withLaunchEnv(effective.distribution, launchEnv) };
|
|
261
|
+
}
|
|
262
|
+
return effective;
|
|
263
|
+
}
|
|
264
|
+
/** The registry's list with this catalog's supplements added where the registry lacks them and superseded ids removed. */
|
|
265
|
+
export function withCatalogEntries(agents) {
|
|
266
|
+
const published = new Set(agents.map((agent) => agent.id));
|
|
267
|
+
const superseded = new Set(AXOM_AGENT_CATALOG.flatMap((entry) => entry.supersedes ?? []));
|
|
268
|
+
const supplements = AXOM_AGENT_CATALOG.flatMap((entry) => entry.supplement && !published.has(entry.registryAgent) ? [{ ...entry.supplement, id: entry.registryAgent }] : []);
|
|
269
|
+
return [...agents.filter((agent) => !superseded.has(agent.id)), ...supplements];
|
|
270
|
+
}
|
|
271
|
+
function withLaunchEnv(distribution, launchEnv) {
|
|
272
|
+
const under = (target) => ({ ...target, env: { ...launchEnv, ...(target.env ?? {}) } });
|
|
273
|
+
return {
|
|
274
|
+
...distribution,
|
|
275
|
+
...(distribution.npx && { npx: under(distribution.npx) }),
|
|
276
|
+
...(distribution.uvx && { uvx: under(distribution.uvx) }),
|
|
277
|
+
...(distribution.binary && {
|
|
278
|
+
binary: Object.fromEntries(Object.entries(distribution.binary).map(([platform, target]) => [platform, target && under(target)])),
|
|
279
|
+
}),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* {@link withCatalogEntries} then {@link applyCatalogOverrides} over a whole registry. `AcpAgents` reads the registry through this
|
|
284
|
+
* and nothing else, so roster, install, launch and update all see the same effective entries.
|
|
285
|
+
*/
|
|
286
|
+
export function withCatalogOverrides(registry) {
|
|
287
|
+
return { load: async () => withCatalogEntries(await registry.load()).map(applyCatalogOverrides) };
|
|
288
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地检测:这台机器上**用户自己**有没有装某个 agent(或它的前置依赖)。
|
|
3
|
+
*
|
|
4
|
+
* ## 它回答的不是"Axom 装好了没有"
|
|
5
|
+
*
|
|
6
|
+
* Axom 启动的一律是自己托管的那份(npx 缓存、`uv tool dir`、`<config dir>/acp-adapters`),
|
|
7
|
+
* 那是 `acpAgents.ts` 的 `adapterInstalled` 的事。这里看的是用户在终端里用的那份:PATH 上的命令、
|
|
8
|
+
* 厂商安装器放下的固定文件。它只有两个用途(见 `catalog.ts` 文件头):
|
|
9
|
+
*
|
|
10
|
+
* - **推荐信号**:检测到 = 这台机器的用户在用它,于是设置页把它排进推荐位,原因写"本机在用"。
|
|
11
|
+
* - **前置依赖**:ZCode、Pi 这类适配器自己不带引擎,检测不到本体就起不来。
|
|
12
|
+
*
|
|
13
|
+
* ## 只看文件在不在,不起进程
|
|
14
|
+
*
|
|
15
|
+
* 与 `installProbe.ts` 的 `findOnPath` 同一条规矩:roster 一次要对 registry 里全部 agent 回答,
|
|
16
|
+
* 一个会执行东西的探针就是每次扫描几十个子进程。`--version` 回答的是另一个更贵的问题。
|
|
17
|
+
*/
|
|
18
|
+
import { statSync } from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { findOnPath } from '../installProbe.js';
|
|
21
|
+
/**
|
|
22
|
+
* 按 `spec` 在这台机器上找:PATH 上的任一命令,或本机三元组下的任一文件位置。
|
|
23
|
+
*
|
|
24
|
+
* `triple` 是 `binaryInstall.ts` 的 `platformTriple` 算出来的本机三元组;落在 registry 六个三元组
|
|
25
|
+
* 之外的机器传 `undefined`,此时只看 `commands`。
|
|
26
|
+
*/
|
|
27
|
+
export function detected(spec, probe, triple) {
|
|
28
|
+
if (!spec)
|
|
29
|
+
return false;
|
|
30
|
+
if (spec.commands?.some((command) => findOnPath(command, probe) !== undefined))
|
|
31
|
+
return true;
|
|
32
|
+
const locations = triple ? spec.locations?.[triple] : undefined;
|
|
33
|
+
return locations?.some((location) => {
|
|
34
|
+
const resolved = resolveLocation(location, probe);
|
|
35
|
+
return resolved !== undefined && isFile(resolved);
|
|
36
|
+
}) ?? false;
|
|
37
|
+
}
|
|
38
|
+
/** 展开 `~/` 与 `%LOCALAPPDATA%/`;对应的目录未知时返回 `undefined`(这条线索查不了,不是命中)。 */
|
|
39
|
+
export function resolveLocation(location, probe) {
|
|
40
|
+
if (location.startsWith('~/'))
|
|
41
|
+
return probe.home ? path.join(probe.home, location.slice(2)) : undefined;
|
|
42
|
+
if (location.startsWith('%LOCALAPPDATA%/')) {
|
|
43
|
+
return probe.localAppData ? path.join(probe.localAppData, location.slice('%LOCALAPPDATA%/'.length)) : undefined;
|
|
44
|
+
}
|
|
45
|
+
return location;
|
|
46
|
+
}
|
|
47
|
+
// `statSync` 而不是 `lstatSync`:与 `findOnPath` 同理,厂商安装器常放符号链接。
|
|
48
|
+
function isFile(candidate) {
|
|
49
|
+
try {
|
|
50
|
+
return statSync(candidate).isFile();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SessionMapperState } from './sessionUpdateMapper.js';
|
|
2
|
+
export function scopeKey(scope) {
|
|
3
|
+
return JSON.stringify([scope.rootRuntimeId, scope.nativeSessionId]);
|
|
4
|
+
}
|
|
5
|
+
export class SessionMapperScopes {
|
|
6
|
+
create;
|
|
7
|
+
states = new Map();
|
|
8
|
+
constructor(create = () => new SessionMapperState()) {
|
|
9
|
+
this.create = create;
|
|
10
|
+
}
|
|
11
|
+
get(scope) {
|
|
12
|
+
const key = scopeKey(scope);
|
|
13
|
+
let state = this.states.get(key);
|
|
14
|
+
if (!state)
|
|
15
|
+
this.states.set(key, state = this.create(scope));
|
|
16
|
+
return state;
|
|
17
|
+
}
|
|
18
|
+
clear() { this.states.clear(); }
|
|
19
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { codexActivityRaw } from './activityReporting.js';
|
|
1
2
|
/**
|
|
2
3
|
* One session's memory, and the reason this file is no longer stateless: **what this session has
|
|
3
4
|
* already been told**, for the two branches that cannot answer a frame from the frame alone.
|
|
@@ -30,7 +31,22 @@
|
|
|
30
31
|
* already (every call's `rawInput`/`rawOutput` is journaled), so there is no eviction here.
|
|
31
32
|
*/
|
|
32
33
|
export class SessionMapperState {
|
|
34
|
+
onActivityReported;
|
|
33
35
|
byId = new Map();
|
|
36
|
+
/**
|
|
37
|
+
* Where an agent-reported activity-state push (today, only `codexActivityRaw`'s extraction of
|
|
38
|
+
* `session_info_update._meta.codex.threadStatus`) actually goes once {@link reportActivity} is
|
|
39
|
+
* called — `agentSessions.ts`'s job, not this file's: this module stays "zero IO" (see the file
|
|
40
|
+
* doc comment) by taking the effect as a constructor dependency rather than reaching for a
|
|
41
|
+
* socket itself.
|
|
42
|
+
*
|
|
43
|
+
* Defaults to a no-op so every existing `new SessionMapperState()` — the tests, `adoption.ts`'s
|
|
44
|
+
* replay path, which has no live connection to report anything on — keeps compiling and keeps
|
|
45
|
+
* doing nothing, exactly as it always has.
|
|
46
|
+
*/
|
|
47
|
+
constructor(onActivityReported = () => { }) {
|
|
48
|
+
this.onActivityReported = onActivityReported;
|
|
49
|
+
}
|
|
34
50
|
/**
|
|
35
51
|
* The agent queue depth this session has already reported to the server.
|
|
36
52
|
*
|
|
@@ -97,6 +113,16 @@ export class SessionMapperState {
|
|
|
97
113
|
this.lastReportedQueueDepth = depth;
|
|
98
114
|
return true;
|
|
99
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* One agent-reported activity-state push, vendor-verbatim. Not a `WireEvent` and never routed
|
|
118
|
+
* through {@link mapSessionUpdate}'s return value — activity is a level, not an edge (see the
|
|
119
|
+
* `agent_session_state` frame's own doc comment in `protocol/agent.ts`), so it must not become a
|
|
120
|
+
* durable item, must not be journaled and must not advance any seq. Forwarded to whatever
|
|
121
|
+
* {@link onActivityReported} this instance was constructed with.
|
|
122
|
+
*/
|
|
123
|
+
reportActivity(raw) {
|
|
124
|
+
this.onActivityReported(raw);
|
|
125
|
+
}
|
|
100
126
|
/** Remember what this call is called. Later updates that carry no `name` fall back to it. */
|
|
101
127
|
record(toolCallId, name) {
|
|
102
128
|
if (name)
|
|
@@ -136,7 +162,7 @@ function compact(value) {
|
|
|
136
162
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
137
163
|
}
|
|
138
164
|
/**
|
|
139
|
-
* `ToolCall.locations` → the wire `tool_call.locations` shape;
|
|
165
|
+
* `ToolCall.locations` → the wire `tool_call.locations` shape; [] explicitly clears a snapshot.
|
|
140
166
|
*
|
|
141
167
|
* An identity copy of ACP's `ToolCallLocation` (`types.gen.d.ts:581`), which is `{path, line?}` —
|
|
142
168
|
* `line` is `number | null | undefined` (`:586-589`) and used to be dropped here, which cost the
|
|
@@ -145,7 +171,7 @@ function compact(value) {
|
|
|
145
171
|
* enclosing payload.
|
|
146
172
|
*/
|
|
147
173
|
function mapLocations(locations) {
|
|
148
|
-
if (
|
|
174
|
+
if (locations == null)
|
|
149
175
|
return undefined;
|
|
150
176
|
return locations.map((location) => compact({ path: location.path, line: location.line ?? undefined }));
|
|
151
177
|
}
|
|
@@ -166,7 +192,7 @@ function toolCallCreatedEvent(update, state) {
|
|
|
166
192
|
// only field guaranteed to give the card a name.
|
|
167
193
|
name: update.name ?? update.title,
|
|
168
194
|
toolCallId: update.toolCallId,
|
|
169
|
-
rawInput: update.rawInput,
|
|
195
|
+
rawInput: update.rawInput ?? undefined,
|
|
170
196
|
kind: mapToolKind(update.kind),
|
|
171
197
|
title: update.title,
|
|
172
198
|
locations: mapLocations(update.locations),
|
|
@@ -239,16 +265,10 @@ function toolCallUpdateEvent(update, state) {
|
|
|
239
265
|
title: update.title ?? undefined,
|
|
240
266
|
kind: update.kind ?? undefined,
|
|
241
267
|
status: update.status ?? undefined,
|
|
242
|
-
|
|
243
|
-
// card's fold *does* honour ACP's "replace the content collection": `applyChunks` treats an
|
|
244
|
-
// empty chunk list as "unchanged" at every call site, never as a clear. So an empty array
|
|
245
|
-
// carries nothing either way — and the server drops it on the way back out anyway
|
|
246
|
-
// (`TimelineEvent.ToolCallUpdated.payload` only writes `content` when it is non-empty), which
|
|
247
|
-
// is why sending one could not express a clear even if a consumer wanted to act on it.
|
|
248
|
-
content: update.content && update.content.length > 0 ? update.content : undefined,
|
|
268
|
+
content: update.content == null ? undefined : update.content,
|
|
249
269
|
locations: mapLocations(update.locations),
|
|
250
|
-
rawInput: update.rawInput,
|
|
251
|
-
rawOutput: update.rawOutput,
|
|
270
|
+
rawInput: update.rawInput ?? undefined,
|
|
271
|
+
rawOutput: update.rawOutput ?? undefined,
|
|
252
272
|
// See the doc comment above: ACP's extension slot, copied verbatim and read by nothing *here*
|
|
253
273
|
// (the server's `decodeToolUpdate` does read it, also verbatim).
|
|
254
274
|
_meta: update._meta ?? undefined,
|
|
@@ -257,16 +277,11 @@ function toolCallUpdateEvent(update, state) {
|
|
|
257
277
|
}
|
|
258
278
|
function mapToolCallEvents(update, state) {
|
|
259
279
|
const events = [toolCallCreatedEvent(update, state)];
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
// only ever leaves "Running" on an update reporting a non-pending status
|
|
263
|
-
// (`WorkTimelineTools.foldToolUpdate`'s outcome branch), and the creation event carries no status,
|
|
264
|
-
// so without this second event an already-terminal call would render as permanently "Running".
|
|
265
|
-
//
|
|
280
|
+
// Creation carries initial update fields too. Emit them immediately under the same id so pending
|
|
281
|
+
// content and status, as well as an already-terminal result, survive a missing later update.
|
|
266
282
|
// `ToolCall` is structurally a `ToolCallUpdate` (every field of the latter, with `title` and `kind`
|
|
267
|
-
// merely narrower), so the same function emits both
|
|
268
|
-
|
|
269
|
-
if (update.status === 'completed' || update.status === 'failed') {
|
|
283
|
+
// merely narrower), so the same function emits both.
|
|
284
|
+
if (update.status != null || update.content != null || update.rawOutput != null) {
|
|
270
285
|
events.push(toolCallUpdateEvent(update, state));
|
|
271
286
|
}
|
|
272
287
|
return events;
|
|
@@ -476,6 +491,9 @@ export function mapSessionUpdate(update, state) {
|
|
|
476
491
|
const queueNotice = agentQueueNotice(update._meta, state);
|
|
477
492
|
if (queueNotice)
|
|
478
493
|
events.push(queueNotice);
|
|
494
|
+
const activityRaw = codexActivityRaw(update._meta);
|
|
495
|
+
if (activityRaw)
|
|
496
|
+
state.reportActivity(activityRaw);
|
|
479
497
|
return events;
|
|
480
498
|
}
|
|
481
499
|
case 'usage_update':
|