@ai-workbench/remote-agent 0.8.5 → 0.8.10
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 +33 -8
- package/dist/acp.d.ts +22 -2
- package/dist/acp.js +143 -25
- package/dist/approval-label.d.ts +1 -1
- package/dist/approval-label.js +15 -3
- package/dist/broker-runtime.js +40 -8
- package/dist/broker.js +5 -1
- package/dist/claude-local.d.ts +37 -0
- package/dist/claude-local.js +207 -0
- package/dist/client.js +71 -2
- package/dist/conversation-details.d.ts +0 -1
- package/dist/conversation-details.js +37 -7
- package/dist/employee-endpoint.d.ts +91 -0
- package/dist/employee-endpoint.js +141 -0
- package/dist/employee-exec.d.ts +53 -0
- package/dist/employee-exec.js +423 -0
- package/dist/employee-location.d.ts +66 -0
- package/dist/employee-location.js +51 -0
- package/dist/employee-mcp.d.ts +42 -0
- package/dist/employee-mcp.js +181 -0
- package/dist/employee-native.d.ts +18 -0
- package/dist/employee-native.js +38 -0
- package/dist/employee-provider.d.ts +42 -0
- package/dist/employee-provider.js +326 -0
- package/dist/employee-relay.d.ts +18 -0
- package/dist/employee-relay.js +49 -0
- package/dist/employee-shell.d.ts +26 -0
- package/dist/employee-shell.js +89 -0
- package/dist/employee-ssh.d.ts +22 -0
- package/dist/employee-ssh.js +123 -0
- package/dist/employee-tools.d.ts +117 -0
- package/dist/employee-tools.js +292 -0
- package/dist/employee-workspace.d.ts +26 -0
- package/dist/employee-workspace.js +61 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +148 -28
- package/dist/input.d.ts +11 -0
- package/dist/input.js +61 -23
- package/dist/local-user-turn.d.ts +24 -0
- package/dist/local-user-turn.js +103 -0
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +46 -2
- package/dist/provider.d.ts +36 -2
- package/dist/provider.js +33 -0
- package/dist/readiness.js +1 -1
- package/dist/recovery.d.ts +14 -1
- package/dist/recovery.js +11 -2
- package/dist/remote-details.d.ts +7 -0
- package/dist/remote-details.js +11 -1
- package/helpers/ripgrep/LICENSE-vscode +13 -0
- package/helpers/ripgrep/PROVENANCE.md +9 -0
- package/helpers/ripgrep/darwin-arm64/rg +0 -0
- package/helpers/ripgrep/darwin-x64/rg +0 -0
- package/helpers/ripgrep/manifest.json +56 -0
- package/helpers/ripgrep/notices/COPYING-ripgrep +3 -0
- package/helpers/ripgrep/notices/DEPENDENCIES.json +946 -0
- package/helpers/ripgrep/notices/LICENCE-PCRE2.md +103 -0
- package/helpers/ripgrep/notices/LICENSE-MIT-ripgrep +21 -0
- package/helpers/ripgrep/notices/UNLICENSE-ripgrep +24 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/COPYING +3 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/COPYING +8 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-THIRD-PARTY +593 -0
- package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/COPYRIGHT +17 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-APACHE +202 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-WHATWG +26 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/COPYING +8 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-MIT +26 -0
- package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/lexopt-0.3.1/LICENSE +19 -0
- package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/log-0.4.28/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/log-0.4.28/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/COPYING +3 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-MIT +26 -0
- package/helpers/ripgrep/notices/msvc_spectre_libs-0.1.3/LICENSE +21 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/COPYING +3 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/COPYING +3 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/r-efi-5.3.0/AUTHORS +74 -0
- package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/src/unicode_tables/LICENSE-UNICODE +57 -0
- package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-BOOST +23 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/COPYING +3 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-APACHE +13 -0
- package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/COPYING +3 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/textwrap-0.16.2/LICENSE +21 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/jemalloc/COPYING +27 -0
- package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-UNICODE +39 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/COPYING +3 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/COPYING +3 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/windows-link-0.2.1/license-apache-2.0 +201 -0
- package/helpers/ripgrep/notices/windows-link-0.2.1/license-mit +21 -0
- package/helpers/ripgrep/notices/windows-sys-0.61.2/license-apache-2.0 +201 -0
- package/helpers/ripgrep/notices/windows-sys-0.61.2/license-mit +21 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-MIT +23 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +2 -2
- package/package.json +29 -11
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
**`@ai-workbench/remote-agent@0.8.10` 发布形态包;编排放行用于 leo 真机自验 Claude 线、设置界面和 D24 重开。Codex 线因主机适配器 aw.2 实装失败(UNSAFE_MODE_MISMATCH),待 S 修复后另验,当前未验证可用。由编排统一发布,X 未执行 publish。**
|
|
2
2
|
|
|
3
3
|
# 远程ACP插件
|
|
4
4
|
|
|
5
5
|
一个插件提供 **Claude(远程主机)** 和 **Codex(远程主机)**,通过员工主机上的官方CLI运行。Codex保留单次模式;Claude固定会话模式。只用官方模型列表、插件管理和配置入口,不提供自制面板、问答或登录按钮。登录和原生工作区绑定唯一归连接插件L。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
当前为0.8.10,包名采用`@ai-workbench/remote-agent`以涵盖两个品牌;本轮按编排放行准备发布形态包,供 leo 真机自验;X 不执行实际发布,由编排统一发布。开发fixture逐字节保留L0.7.17固定包,公开类型与SSH导出从该包消费。运行时仍要求L的`getContractInfo()`同时返回API0.7.0、implemented、native-local-v1;旧L包、draft.3或只有版本号的不完整实现会明确报缺项。依赖范围为`~0.7.17`,L由官方插件管理单独安装,不打进X。组件验证不代表真实开通、模型或leo E通过;HanaMesh另验。
|
|
8
8
|
|
|
9
9
|
## 员工路径
|
|
10
10
|
|
|
11
|
-
从官方插件管理安装,照常使用默认工作区/会话/模型入口。L0.7.
|
|
11
|
+
从官方插件管理安装,照常使用默认工作区/会话/模型入口。L0.7.17沿唯一“远程主机连接”设置节记录文件来源;主机来源沿用L既有解析;本地来源接入N0.4.4/S0.4.11员工端点。旧映射需在L逐工作区确认,新主机位置仍按L现有合同停止为WORKSPACE_LOCATION_PROVIDER_REQUIRED。本地六类工具经Claude公开toolAliases路由到员工电脑:cwd是员工选定目录,Git使用本机仓库,不回退主机同名目录。本地真实员工输入先取得官方当前轮次CAS预期,再调用L0.7.17 acceptLocalWorkspaceUserTurn;核回包后才打开D19和本轮主机目录/执行。每轮localAcceptanceId冻结进原详情sink,迟到结果不借新轮ID;LOCAL_USER_TURN_CHANGED立即停止本轮,不重接纳或退legacy。已用固定真包完成本地D19组件联测;真实N/S员工端仍NOT_RUN,产品验收以 leo 实际执行结果为准。本地文件不自动上传,X不猜目录、不选首项。首次说明使用官方普通回复;先经官方会话提交、持久化并读回核对,才向L确认。持久记录不等于员工已看见,GUI另验;日志丢失后的未知claim由L保留,不宣称端到端恰好一次。
|
|
12
12
|
|
|
13
13
|
未登录时两个模型均回复:**需要登录,请到设置 → 远程主机连接 点登录**。无L或缺正式能力时直接说明缺项,不让员工填写主机、用户、路径或密钥。
|
|
14
14
|
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
|
|
25
25
|
`usage_update`的used/size是上下文占用,不能当作本轮输入/输出计费量;远端真实prompt响应usage及Claude quota令牌计数是会话累计,按保存在当前实例/恢复标识中的可信基线减差后进入DSH本轮用量;重复值不重复结算。恢复缺少基线、计数缺失、回退或来源改变时明确不计入该轮,重建可信基线,不能把历史用量算成本轮。
|
|
26
26
|
|
|
27
|
-
会话模式跨轮保留同一ACP客户端和主机进程;关闭实际agent/session或插件时精确清理。重开恢复优先协商resume,无能力时明确使用load,不静默新建。恢复标识绑定品牌、员工目标、DSH会话和L
|
|
27
|
+
会话模式跨轮保留同一ACP客户端和主机进程;关闭实际agent/session或插件时精确清理。重开恢复优先协商resume,无能力时明确使用load,不静默新建。恢复标识绑定品牌、员工目标、DSH会话和L工作区;复制/分叉不能沿用父远端会话。主机来源沿用D24同实例自动恢复/安全重建。本地来源重开后原MCP socket失效,先由既有S同实例条件流程结束旧broker再重建,不代答旧审批;未获真实清理确认时保留未知,不自动重放。无TTL或隐式淘汰上限。
|
|
28
28
|
|
|
29
29
|
Codex单次模式首版仅文本,不转接中途审批;图片或系统/开发者上下文需使用会话模式。文件传输、远端VFS与自动同步不在本轮。其他模型不锁定、不拦截、不记录。
|
|
30
30
|
|
|
31
31
|
## 管理员配置
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
在官方 **设置 → 内置插件 → 远程助手参数** 查看和修改本插件全部七项参数。页面走官方ConfigForms与settings.plugins.tab,不新增连接设置节、不改DSH核心。每项列明默认值、范围、生效和调整后果;固定never及正确性约束明确列为不可关闭。当前profile不可写、服务未就绪或远程Web不支持持久写入时禁写并说明原因。保存携带编辑起点revision,冲突保留草稿、不自动重试;恢复继承值发unset,不写死默认。原生模型Edit卡未增加表单;“打开配置文件”仍可用,但不作为参数可见性的替代。
|
|
34
34
|
|
|
35
35
|
| 设置 | 默认 | 范围与用途 |
|
|
36
36
|
|---|---|---|
|
|
@@ -40,8 +40,9 @@ Codex单次模式首版仅文本,不转接中途审批;图片或系统/开
|
|
|
40
40
|
| brokerControlMaxBytes | 1048576 | 正整数,主机会话管理回复容量,默认1MiB |
|
|
41
41
|
| oneShotSandbox | workspace-write | Codex单次可选read-only/workspace-write/danger-full-access |
|
|
42
42
|
| oneShotApproval | never | 当前单次通道无中途交互审批;不代表自动批准 |
|
|
43
|
+
| employeeShellKillGraceMs | 1000 | 0至2147483647的整数;取消后外层SSH清理宽限毫秒。取消结果仍未知,不将宽限作为员工端退出证明 |
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
原六项默认保留;D20新增employeeShellKillGraceMs供管理员调节自己的SSH清理。非法值拒绝,删除覆盖遵循官方配置继承。SSH可执行程序从当前PATH发现,不写死某一系统路径。原始错误码保留在官方失败代码/技术详情,原始stderr、凭据和URL不展开。
|
|
45
46
|
|
|
46
47
|
仅新增中文文案和验证;保留已有英文名称及既有locale机制。提供方名称不附状态后缀,由原注册replace更新,不自造刷新或客户端品牌分支。
|
|
47
48
|
|
|
@@ -59,7 +60,7 @@ D19(0.8.1):相邻正文增量合成连续文本块,工作区首次说明
|
|
|
59
60
|
技术详情由 L 0.7.11 的唯一“远程主机连接”设置节展示,正文只放模型文本。命令目录保留完整描述;未知事件和工具输入输出保留字段、JSON类型及处理结果,未获准原文明确脱敏,数字用量可核对。没有 `remoteConversationDetails:1` 时明确提示“技术详情暂不可用”,本轮不发送模型请求。X 在官方当前 profile 的 `ai-workbench-remote-agent/diagnostics/` 保存私有诊断日志(目录700、文件600、落盘同步);写入/归属/顺序/容量失败中止本轮,不自动重发、换代重投、过期或淘汰。结果未知时只读核对原事件完整回执,未找到仍保持未知。X本轮无新增设置/阈值;L的单事件容量和分页设置在L设置节可见。
|
|
60
61
|
|
|
61
62
|
|
|
62
|
-
0.8.2 登录检查原交付需要 L `~0.7.11`(本统一版peer为`~0.7.
|
|
63
|
+
0.8.2 登录检查原交付需要 L `~0.7.11`(本统一版peer为`~0.7.17`),失败来源及固定分支保留到 L“登录前技术详情”(`loginDiagnostics:1`),员工正文保持原文。按 L 明确的本品牌 queued/checking/authorizing 状态订阅等待;不以全局checking猜测在途,不新增刷新、轮询、超时或重试。每轮ensureReady仍是合同规定的显式检查,L的新可信结果可以替代旧检查,较早返回不得覆盖它。
|
|
63
64
|
|
|
64
65
|
L安全规范的officialReason原串完整保留;旧/不支持格式明确记OFFICIAL_REASON_UNSUPPORTED与字节数/SHA,秘密原文不保存。L0.7.11的LOGIN_INSTANCE_MISMATCH记录通过observedLoginInstanceId与原始login.instanceId保存实际观测ID,事件及connection仍绑定当前可信ID/代次;L技术详情和X私有日志均保留真实双ID,不归一、不清空快照。复合未知官方原因以固定summary完整保存OFFICIAL_REASON_UNSUPPORTED及bytes/SHA,不交原文。X私有原快照先同步落盘,再追加L;ACK未知只读核实原事件,跨代不重写/重发。此版本的工程验证不替代leo员工路径复验,真实故障根因尚未证实。
|
|
65
66
|
|
|
@@ -71,4 +72,28 @@ D21(0.8.5):两个品牌的官方模型菜单从 L 的 `modelCatalogTarget:
|
|
|
71
72
|
|
|
72
73
|
每轮先核对目录代次和所选模型,再经 ACP 逐项配置模型及推理等级,收到完整配置确认后才发送输入。Claude 别名必须有当前会话的具体型号证据;没有就明确拒绝。真实回复追加所选值和远端确认值,并在恢复标识与技术详情保留,确认配置不等于验证推理效果。连接/身份/目录变化清除旧确认;远端清理待定或结果未知时阻止派发,不自动清理重试或另开替代会话。Codex 单次模式当前不能确认此选择,会在发送前明确拒绝。新增设置 0;本轮工程证据、真实只读链路与产品未验项见 `docs/evidence/2026-10-22-X2-D21/`。
|
|
73
74
|
|
|
74
|
-
0.8.5
|
|
75
|
+
0.8.5统一包原依赖L `~0.7.13`;当前0.8.10对齐L `~0.7.17`。D8模型目录双事件长期监听保留;同epoch的recovery提示/失败消息变化不触发执行状态失效,真实实例/epoch/phase/可信目标/登录变化仍失效。完整D9两品牌正文/状态/就绪门呈现按编排00:50允许的范围记下一版,本版不宣称已消费完成;L的connectionMessages.administratorAfterMs仍只由L管理,X新增设置0。
|
|
76
|
+
|
|
77
|
+
D18(0.8.6):Codex 需要主机自有 adapter `2.2.2-aw.1` 的明确新建会话上下文政策,官方 CLI 仍为未改的0.160.1。系统/开发者要求仅在新建时生效;恢复时与初建摘要相同则正常恢复且省略新建专用字段,不声称恢复接口会更新指令。要求不同或清空时,在同一真实回复说明“会话要求已变化,已新建 Codex 会话继续处理,原会话仍保留。”,并新建远端承接公开对话,保留旧标识和查询记录;旧/非法初建摘要明确拒绝,不能猜测。
|
|
78
|
+
|
|
79
|
+
新会话承接公开用户/助手文本与核验图片,内部reasoning留在旧记录并记录排除数量,官方offload图片保持占位语义、不重读字节。工具角色、工具块、file和未知类型没有可靠公开投影时明确在新建/发送前拒绝;不序列化内部恢复或工具数据代替正文。新建后重新核对模型/等级并重置用量基线;技术详情只记原因、旧/新标识、摘要及承接范围,不保存系统上下文原文。该政策当前仅Codex,Claude保持上述原策略。新增设置0;证据及主机离线闭包见 `docs/evidence/2026-10-22-X2-D18/DELIVERY.md`。S安装和真实员工路径仍由编排安排。
|
|
80
|
+
|
|
81
|
+
0.8.6仅完成D18与既有恢复的集成,D9完整呈现仍待X后续交付;系统/开发者上下文只支持公开text,非text在任何远端生命周期前明确拒绝。
|
|
82
|
+
|
|
83
|
+
## D20 Claude范围与依赖
|
|
84
|
+
|
|
85
|
+
Read/Write/Edit/Glob/Grep由员工DSH同UID后端执行;Bash经L严格SSH到S同代桥再经N员工SSH端点执行,命令正文只走stdin。每次调用核官方会话唯一工作区、L实例/epoch/绑定/目录身份、N真实prepare及S实时target。取消、SSH255或退出证明缺失记录UNKNOWN,不重放。只关闭自己的绑定执行器,不撤其他工作区或设备服务。cwd不是权限根,绝对路径和上级访问遵循真实OS权限;不增加目录白名单。
|
|
86
|
+
|
|
87
|
+
每轮同一官方回复明确:子代理和harness内部直接工具调用尚未证明可在员工端执行,可能仍在主机;已识别的原生未改道工具/Agent审批拒绝。PDF等二进制读取及后台命令明确不支持,不静默主机代跑。六类组件/MCP/配置转交证明不等于真实Claude原生alias执行或真实员工链路通过。实际Git依赖员工环境中已有Git,缺失会返回实际命令失败,不在主机替代,也不自动安装。
|
|
88
|
+
|
|
89
|
+
本地目前仅支持macOS(arm64/x64);Grep固定携带微软@vscode/ripgrep-universal1.18.0的两架构原字节并核SHA,无员工PATH搜索或安装下载。主机需S0.4.11,N可选peer~0.4.4(缺失明确报能力缺项),L当前~0.7.17,本地接纳与D19工程路径已接,真实员工端门仍开放。无DSH/CLI修改、额外设置节或npm发布。
|
|
90
|
+
|
|
91
|
+
## 合并保留的 Codex D20 路线
|
|
92
|
+
|
|
93
|
+
D20(0.8.8开发记录):本地来源的Codex原生文件与命令通过固定官方CLI0.160.1的实验执行环境接口改到员工电脑,cwd来自员工已选择的原生工作区,Git使用本机现有仓库。模型、登录及全局CODEX_HOME助手配置仍由远程主机提供;工作区AGENTS与所测原生子代理跟随员工执行环境。员工执行组件随标准npm依赖安装后自动发现、核版本和SHA;当前只支持已验证的Apple芯片Mac,其他平台明确拒绝,没有PATH替代或让员工手填路径的步骤。
|
|
94
|
+
|
|
95
|
+
本地会话需要N0.4.4正式公共服务、S0.4.11员工SSH桥和自有主机适配器2.2.2-aw.2。S现有aw.1保护安装器不能直接消费aw.2,aw.2保护消费仍须提供方交付。已消费正式L0.7.17本地user-turn品牌接纳:仅实际本轮human输入可建立CAS预期,接纳先于ensureReady的主机状态命令及本轮模型目录;每轮详情sink固定自己的localAcceptanceId,append通过第二参数传递。换轮冲突立即停本轮,不重试或借历史/legacy接纳。本轮主机 aw.2 实装失败,原因是 UNSAFE_MODE_MISMATCH,S 正在修复;Codex 线待主机修复后另验。真实员工端链路仍为NOT_RUN,工程真包消费不代表真实路径或GUI通过。
|
|
96
|
+
|
|
97
|
+
当前明确不支持本地单次模式、断开后的冷恢复/load/fork、快捷命令、主机文件报告及附加MCP/hooks/plugins。主机同路径的启用项目配置、会话层项目发现/信任覆盖也会明确拒绝;配置检查不静默清空或改写原配置。普通原生shell/patch/fs使用所选员工环境,未覆盖的辅助入口保留具体原因到员工回复与技术详情。位置、身份或代次改变只撤回对应执行器,在途结果保持UNKNOWN,不自动重放。新增产品设置0,执行通道和桥队列沿已有`acpMaxMessageBytes`控制容量。当前工程进度、首红和开放门见`docs/evidence/2026-10-23-X2-D20-088/STATUS.md`。
|
|
98
|
+
|
|
99
|
+
0.8.10仅增加公开插件参数页,不改变上述七项执行语义。mode/ACP/frame/control/grace的修改不改变运行中当轮;保留会话下一轮配置签名不一致会停止,须新建会话。ACP容量还在下一次独立目录查询读取新值。sandbox与approval仅单次通道适用,当前单次仍明确拒绝执行。GUI与真实链路门仍NOT_RUN;本次由编排在 leo 验证 Claude 线、设置界面和 D24 重开,Codex 线另验。完整逐项盘点见 docs/evidence/2026-10-23-X-settings-0810/INVENTORY.md。
|
package/dist/acp.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { StageTiming } from './stage-timing.ts';
|
|
|
6
6
|
import { type AcpRecovery } from './recovery.ts';
|
|
7
7
|
import { type SanitizedObservation } from './conversation-details.ts';
|
|
8
8
|
import { type ModelConfigurationSnapshot } from './model-configuration.ts';
|
|
9
|
+
import type { EmployeeRouting } from './employee-relay.ts';
|
|
9
10
|
export type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
|
|
10
11
|
export interface AcpHooks {
|
|
11
12
|
timing?: StageTiming;
|
|
@@ -15,9 +16,20 @@ export interface AcpHooks {
|
|
|
15
16
|
onNeed?: (need: Need) => void;
|
|
16
17
|
onDetail?: (detail: Readonly<SanitizedObservation>) => void;
|
|
17
18
|
broker?: BrokerTransport;
|
|
19
|
+
employeeExecution?: (params: unknown, signal?: AbortSignal) => Promise<Record<string, unknown>>;
|
|
20
|
+
}
|
|
21
|
+
export interface EmployeeExecutionMeta {
|
|
22
|
+
version: 1;
|
|
23
|
+
bindingId: string;
|
|
24
|
+
workspaceId: string;
|
|
25
|
+
cwd: string;
|
|
26
|
+
acpMaxMessageBytes: number;
|
|
18
27
|
}
|
|
19
28
|
export interface AcpStartContext {
|
|
20
29
|
systemPrompt?: string;
|
|
30
|
+
codexNewSessionOnly?: true;
|
|
31
|
+
employeeExecution?: EmployeeExecutionMeta;
|
|
32
|
+
employeeRouting?: EmployeeRouting;
|
|
21
33
|
}
|
|
22
34
|
export type AcpStartMethod = 'session/new' | 'session/resume' | 'session/load';
|
|
23
35
|
/** ACP v1 with explicitly negotiated SDK 2.0 lifecycle/context extensions. */
|
|
@@ -35,6 +47,10 @@ export declare class AcpSession {
|
|
|
35
47
|
private updating;
|
|
36
48
|
private cwd?;
|
|
37
49
|
private systemPrompt;
|
|
50
|
+
private codexNewSessionOnly;
|
|
51
|
+
private creatingReplacement;
|
|
52
|
+
private employeeMeta?;
|
|
53
|
+
private employeeNegotiated;
|
|
38
54
|
private configuration?;
|
|
39
55
|
private configRevision;
|
|
40
56
|
private selected?;
|
|
@@ -66,12 +82,16 @@ export declare class AcpSession {
|
|
|
66
82
|
get capabilities(): Readonly<acp.AgentCapabilities>;
|
|
67
83
|
get startMethod(): AcpStartMethod | undefined;
|
|
68
84
|
get checkpoint(): AcpRecovery | undefined;
|
|
69
|
-
restore(cwd: string, saved: AcpRecovery, context: AcpStartContext): void;
|
|
85
|
+
restore(cwd: string, saved: AcpRecovery, context: AcpStartContext, verifiedSameContext?: boolean): void;
|
|
70
86
|
validatePrompt(prompt: acp.ContentBlock[]): void;
|
|
71
87
|
validateContext(context: AcpStartContext): void;
|
|
88
|
+
validateNewSessionContext(context: AcpStartContext): void;
|
|
72
89
|
setHooks(hooks: AcpHooks): void;
|
|
73
90
|
get closed(): Promise<void>;
|
|
74
|
-
|
|
91
|
+
private validateNewContext;
|
|
92
|
+
start(cwd: string, resumeId?: string, signal?: AbortSignal, context?: AcpStartContext, prompt?: acp.ContentBlock[]): Promise<string>;
|
|
93
|
+
/** Codex context is immutable after creation; keep the old remote session intact. */
|
|
94
|
+
newContextSession(context: AcpStartContext, signal?: AbortSignal): Promise<string>;
|
|
75
95
|
updateContext(context: AcpStartContext, signal?: AbortSignal): Promise<AcpStartMethod | undefined>;
|
|
76
96
|
prompt(input: string | acp.ContentBlock[], signal?: AbortSignal): Promise<acp.StopReason>;
|
|
77
97
|
close(): void;
|
package/dist/acp.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as acp from '@agentclientprotocol/sdk/unstable';
|
|
2
2
|
import { NeedError } from "./remote.js";
|
|
3
|
+
import { employeeFailure } from "./presentation.js";
|
|
3
4
|
import { StageTiming } from "./stage-timing.js";
|
|
4
5
|
import { isolateRpcIds } from "./rpc-namespace.js";
|
|
5
|
-
import { recoveryCapabilities } from "./recovery.js";
|
|
6
|
+
import { recoveryCapabilities, supportsCodexContextPolicy } from "./recovery.js";
|
|
6
7
|
import { sanitizeObservation } from "./conversation-details.js";
|
|
7
8
|
import { ModelConfiguration, ModelConfigurationError } from "./model-configuration.js";
|
|
8
9
|
/** ACP v1 with explicitly negotiated SDK 2.0 lifecycle/context extensions. */
|
|
@@ -20,6 +21,10 @@ export class AcpSession {
|
|
|
20
21
|
updating = false;
|
|
21
22
|
cwd;
|
|
22
23
|
systemPrompt = '';
|
|
24
|
+
codexNewSessionOnly = false;
|
|
25
|
+
creatingReplacement = false;
|
|
26
|
+
employeeMeta;
|
|
27
|
+
employeeNegotiated = false;
|
|
23
28
|
configuration;
|
|
24
29
|
configRevision = 0;
|
|
25
30
|
selected;
|
|
@@ -101,20 +106,20 @@ export class AcpSession {
|
|
|
101
106
|
timing = new StageTiming();
|
|
102
107
|
stage(phase, operation) { return (this.hooks.timing ?? this.timing).run(phase, operation); }
|
|
103
108
|
get diagnostics() { return this.reports.map(entry => sanitizeObservation(entry)); }
|
|
104
|
-
detail(observation) {
|
|
109
|
+
detail(observation, hooks = this.hooks) {
|
|
105
110
|
const safe = sanitizeObservation(observation);
|
|
106
111
|
this.reports.push(safe);
|
|
107
112
|
try {
|
|
108
|
-
|
|
113
|
+
hooks.onDetail?.(safe);
|
|
109
114
|
}
|
|
110
115
|
catch (cause) {
|
|
111
116
|
this.reportingFailure ??= { cause };
|
|
112
117
|
this.connection?.close();
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
|
-
need(need) {
|
|
116
|
-
this.detail({ kind: 'need', severity: 'action-required', code: need.code, summary: need.message, payload: { requires: need.requires } });
|
|
117
|
-
|
|
120
|
+
need(need, hooks = this.hooks) {
|
|
121
|
+
this.detail({ kind: 'need', severity: 'action-required', code: need.code, summary: need.message, payload: { requires: need.requires } }, hooks);
|
|
122
|
+
hooks.onNeed?.(need);
|
|
118
123
|
}
|
|
119
124
|
checkReporting() { if (this.reportingFailure)
|
|
120
125
|
throw new NeedError('CONVERSATION_DETAILS_FAILED', '技术详情未能记录;本轮未继续发送', '可用的技术详情记录渠道'); }
|
|
@@ -123,6 +128,12 @@ export class AcpSession {
|
|
|
123
128
|
this.timing.bind(detail => this.detail(detail));
|
|
124
129
|
const app = acp.client({ name: '@ai-workbench/remote-agent' })
|
|
125
130
|
.onNotification(acp.methods.client.session.update, ({ params: notification }) => {
|
|
131
|
+
if (this.creatingReplacement) {
|
|
132
|
+
if (notification.sessionId !== this.sessionId && notification.update.sessionUpdate === 'config_option_update')
|
|
133
|
+
this.pendingConfigurations.set(notification.sessionId, notification.update.configOptions);
|
|
134
|
+
this.detail({ kind: 'session', severity: 'info', code: 'ACP_IDLE_UPDATE', summary: '新会话建立期间的轨迹,未作为正文处理', method: 'session/update', payload: notification });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
126
137
|
if (this.sessionId && notification.sessionId !== this.sessionId) {
|
|
127
138
|
this.detail({ kind: 'session', severity: 'warning', code: 'ACP_FOREIGN_SESSION_UPDATE', summary: '收到不属于当前远端会话的通知,未作为正文处理', method: 'session/update', payload: notification });
|
|
128
139
|
return;
|
|
@@ -152,13 +163,14 @@ export class AcpSession {
|
|
|
152
163
|
this.detail({ kind: notification.update.sessionUpdate === 'available_commands_update' ? 'commands' : 'session', severity: 'info', code: 'ACP_IDLE_UPDATE', summary: '远端会话建立、恢复或空闲时的轨迹', method: 'session/update', payload: notification });
|
|
153
164
|
})
|
|
154
165
|
.onRequest(acp.methods.client.session.requestPermission, async ({ params: request, signal: rpcSignal, requestId }) => {
|
|
166
|
+
const hooks = this.hooks, detail = (value) => this.detail(value, hooks), need = (value) => this.need(value, hooks);
|
|
155
167
|
if (request.sessionId !== this.sessionId || !this.busy || this.turnSignal?.aborted)
|
|
156
168
|
return { outcome: { outcome: 'cancelled' } };
|
|
157
|
-
|
|
169
|
+
hooks.broker?.acknowledge(acp.methods.agent.session.prompt);
|
|
158
170
|
const toolName = request.toolCall.name ?? record(request.toolCall._meta?.claudeCode)?.toolName;
|
|
159
|
-
|
|
171
|
+
detail({ kind: 'tool', severity: 'info', code: 'ACP_PERMISSION_REQUEST', summary: '远端工具审批标识', method: 'session/request_permission', payload: { sessionId: request.sessionId, toolCall: { toolCallId: request.toolCall.toolCallId, ...(typeof toolName === 'string' ? { name: toolName } : {}) } } });
|
|
160
172
|
if (toolName === 'AskUserQuestion') {
|
|
161
|
-
|
|
173
|
+
need({ code: 'INTERACTION_UNSUPPORTED', message: '首版不支持:远端提问;已取消,未提交答案', requires: '可承载此提问的官方交互接口' });
|
|
162
174
|
return { outcome: { outcome: 'cancelled' } };
|
|
163
175
|
}
|
|
164
176
|
const eligible = request.options.filter(option => {
|
|
@@ -166,11 +178,11 @@ export class AcpSession {
|
|
|
166
178
|
const sameKind = request.options.filter(candidate => candidate.kind === option.kind);
|
|
167
179
|
const unique = single && sameKind.length === 1 && request.options.filter(candidate => candidate.optionId === option.optionId).length === 1;
|
|
168
180
|
if (!unique)
|
|
169
|
-
|
|
181
|
+
detail({ kind: 'tool', severity: 'info', code: 'APPROVAL_OPTION_NOT_OFFERED', summary: '此远端审批选项未映射到单次审批', method: 'session/request_permission', payload: option });
|
|
170
182
|
return unique;
|
|
171
183
|
});
|
|
172
184
|
if (!eligible.length) {
|
|
173
|
-
|
|
185
|
+
need({ code: 'APPROVAL_OPTION_UNSUPPORTED', message: '远端没有可唯一映射的单次审批选项,此动作未获授权', requires: '唯一对应的允许一次或拒绝一次选项' });
|
|
174
186
|
return { outcome: { outcome: 'cancelled' } };
|
|
175
187
|
}
|
|
176
188
|
const presented = { ...request, options: eligible };
|
|
@@ -178,7 +190,7 @@ export class AcpSession {
|
|
|
178
190
|
const id = this.sessionId;
|
|
179
191
|
let outcome = 'unavailable';
|
|
180
192
|
const approvalSignal = this.turnSignal ? AbortSignal.any([this.turnSignal, rpcSignal]) : rpcSignal;
|
|
181
|
-
const broker =
|
|
193
|
+
const broker = hooks.broker;
|
|
182
194
|
let generation;
|
|
183
195
|
// A transport disconnect withdraws only the UI question, not the ACP RPC.
|
|
184
196
|
// Late UI answers lose their lease; a fresh online attachment asks again.
|
|
@@ -192,7 +204,7 @@ export class AcpSession {
|
|
|
192
204
|
const abort = () => interrupted.resolve('cancelled');
|
|
193
205
|
signal.addEventListener('abort', abort, { once: true });
|
|
194
206
|
try {
|
|
195
|
-
outcome = await Promise.race([
|
|
207
|
+
outcome = await Promise.race([hooks.approve?.(presented, signal) ?? Promise.resolve('unavailable'), interrupted.promise]);
|
|
196
208
|
}
|
|
197
209
|
finally {
|
|
198
210
|
signal.removeEventListener('abort', abort);
|
|
@@ -203,7 +215,7 @@ export class AcpSession {
|
|
|
203
215
|
}
|
|
204
216
|
}
|
|
205
217
|
catch {
|
|
206
|
-
|
|
218
|
+
need({ code: 'APPROVAL_FAILED', message: '审批渠道失败或连接已结束;此动作未获授权', requires: '可用的 DSH 审批渠道和原连接' });
|
|
207
219
|
}
|
|
208
220
|
const kind = outcome === 'allowed-once' ? 'allow_once' : outcome === 'rejected' ? 'reject_once' : undefined;
|
|
209
221
|
const option = kind && eligible.find(item => item.kind === kind);
|
|
@@ -212,7 +224,7 @@ export class AcpSession {
|
|
|
212
224
|
broker.authorizeResponse(requestId, generation);
|
|
213
225
|
return { outcome: { outcome: 'selected', optionId: option.optionId } };
|
|
214
226
|
}
|
|
215
|
-
|
|
227
|
+
need({ code: outcome === 'allowed-once' ? 'APPROVAL_OPTION_UNSUPPORTED' : 'APPROVAL_UNAVAILABLE',
|
|
216
228
|
message: '此动作未获授权;审批已取消、不可用或没有可映射的单次选项', requires: '本次动作的有效审批' });
|
|
217
229
|
return { outcome: { outcome: 'cancelled' } };
|
|
218
230
|
})
|
|
@@ -222,6 +234,20 @@ export class AcpSession {
|
|
|
222
234
|
})
|
|
223
235
|
.onNotification(acp.methods.client.elicitation.complete, () => {
|
|
224
236
|
this.need({ code: 'INTERACTION_UNSUPPORTED', message: '首版不支持:远端提问完成通知;未提交答案', requires: '可承载此提问的官方交互接口' });
|
|
237
|
+
})
|
|
238
|
+
.onRequest('_aw/employee-exec', (params) => params, async ({ params, signal }) => {
|
|
239
|
+
if (!this.employeeNegotiated || !this.employeeMeta || !this.hooks.employeeExecution)
|
|
240
|
+
throw new acp.RequestError(-32601, 'EMPLOYEE_EXECUTION_UNSUPPORTED');
|
|
241
|
+
try {
|
|
242
|
+
return await this.hooks.employeeExecution(params, signal);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (error instanceof NeedError) {
|
|
246
|
+
this.need(error);
|
|
247
|
+
throw new acp.RequestError(-32603, error.code);
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
225
251
|
})
|
|
226
252
|
.onRequest('*', ({ method, params }) => {
|
|
227
253
|
this.detail({ kind: 'extension', severity: 'action-required', code: 'ACP_UNKNOWN_REQUEST', summary: '未知远端交互请求未获授权', method, payload: params });
|
|
@@ -253,7 +279,7 @@ export class AcpSession {
|
|
|
253
279
|
const update = record(params?.update);
|
|
254
280
|
if (raw)
|
|
255
281
|
this.hooks.broker?.processedMessage(raw);
|
|
256
|
-
if (typeof raw?.method === 'string' && Object.hasOwn(raw, 'id') && this.hooks.broker && !this.hooks.broker.promptInFlight) {
|
|
282
|
+
if (typeof raw?.method === 'string' && Object.hasOwn(raw, 'id') && this.hooks.broker && !this.hooks.broker.promptInFlight && !(raw.method === '_aw/employee-exec' && this.hooks.employeeExecution && this.employeeMeta)) {
|
|
257
283
|
const error = new NeedError('RECOVERY_UNSAFE_OUTPUT', '主机会话有不属于当前输入的旧审批,未自动回答', '安全结束旧会话并新建');
|
|
258
284
|
this.hooks.broker.fail(error);
|
|
259
285
|
throw error;
|
|
@@ -270,7 +296,9 @@ export class AcpSession {
|
|
|
270
296
|
get capabilities() { return this.negotiated; }
|
|
271
297
|
get startMethod() { return this.method; }
|
|
272
298
|
get checkpoint() { return this.ready && !this.busy && !this.updating && !this.selecting && this.sessionId ? { sessionId: this.sessionId, capabilities: recoveryCapabilities(this.negotiated), ...(this.configuration ? { modelConfiguration: { configOptions: JSON.parse(JSON.stringify(this.configuration.snapshot.configOptions)), ...(this.resolvedEvidence ? { resolvedEvidence: { ...this.resolvedEvidence } } : {}) } } : {}) } : undefined; }
|
|
273
|
-
restore(cwd, saved, context) {
|
|
299
|
+
restore(cwd, saved, context, verifiedSameContext = false) {
|
|
300
|
+
if (context.employeeRouting)
|
|
301
|
+
throw new NeedError('EMPLOYEE_CHECKPOINT_STALE', '本机工具通道需要重新建立,旧审批没有代答。', '按原主机实例安全结束后新建本机会话');
|
|
274
302
|
if (this.startAttempted)
|
|
275
303
|
throw new NeedError('SESSION_ALREADY_STARTED', '此客户端已建立会话', '新的客户端连接');
|
|
276
304
|
this.startAttempted = true;
|
|
@@ -278,7 +306,11 @@ export class AcpSession {
|
|
|
278
306
|
this.negotiated = recoveryCapabilities(saved.capabilities);
|
|
279
307
|
this.cwd = cwd;
|
|
280
308
|
this.systemPrompt = context.systemPrompt ?? '';
|
|
281
|
-
this.
|
|
309
|
+
this.codexNewSessionOnly = !!context.codexNewSessionOnly;
|
|
310
|
+
// A verified same-context reattach does not create or update native context.
|
|
311
|
+
// Old journals may lack new-only metadata; never manufacture it for them.
|
|
312
|
+
if (this.codexNewSessionOnly && !verifiedSameContext)
|
|
313
|
+
this.validateNewContext(this.systemPrompt, context.systemPrompt !== undefined);
|
|
282
314
|
if (saved.modelConfiguration) {
|
|
283
315
|
this.receiveConfiguration(saved.modelConfiguration.configOptions);
|
|
284
316
|
this.resolvedEvidence = saved.modelConfiguration.resolvedEvidence;
|
|
@@ -289,6 +321,8 @@ export class AcpSession {
|
|
|
289
321
|
}
|
|
290
322
|
this.hooks.broker?.finishRecovery();
|
|
291
323
|
this.detail({ kind: 'need', severity: 'info', code: 'ACP_CLIENT_REATTACHED', summary: '已接回原模型会话', payload: { sessionId: saved.sessionId } });
|
|
324
|
+
this.checkReporting();
|
|
325
|
+
this.ready = true;
|
|
292
326
|
}
|
|
293
327
|
validatePrompt(prompt) {
|
|
294
328
|
const capability = this.negotiated.promptCapabilities;
|
|
@@ -299,39 +333,66 @@ export class AcpSession {
|
|
|
299
333
|
validateContext(context) {
|
|
300
334
|
if ((context.systemPrompt ?? '') === this.systemPrompt)
|
|
301
335
|
return;
|
|
336
|
+
if (this.codexNewSessionOnly)
|
|
337
|
+
throw new NeedError('SYSTEM_PROMPT_NEW_SESSION_REQUIRED', 'Codex会话要求已变化,需要保留原会话并新建会话承接', '新会话及完整公开历史承接');
|
|
302
338
|
if (!this.negotiated.sessionCapabilities?.systemPrompt)
|
|
303
339
|
throw new NeedError('SYSTEM_PROMPT_UNSUPPORTED', '远端未声明系统与开发者上下文更新能力,不能丢弃这些要求继续', '支持 systemPrompt 的公开 ACP 能力');
|
|
304
340
|
if (!this.negotiated.sessionCapabilities?.resume && !this.negotiated.loadSession)
|
|
305
341
|
throw new NeedError('SYSTEM_PROMPT_UPDATE_UNSUPPORTED', '远端没有可更新原会话上下文的恢复或加载能力', '支持带 systemPrompt 的 session/resume 或 session/load');
|
|
306
342
|
}
|
|
343
|
+
validateNewSessionContext(context) {
|
|
344
|
+
if (!this.codexNewSessionOnly)
|
|
345
|
+
throw new NeedError('SYSTEM_PROMPT_POLICY_UNSUPPORTED', '此会话没有使用Codex新建政策', 'Codex新建政策');
|
|
346
|
+
this.validateNewContext(context.systemPrompt ?? '', true);
|
|
347
|
+
}
|
|
307
348
|
setHooks(hooks) {
|
|
308
349
|
if (this.busy || this.updating || this.selecting)
|
|
309
350
|
throw new NeedError('TURN_BUSY', '上一轮或配置更新结束前不能更换会话钩子', '上一轮结束');
|
|
310
351
|
this.hooks = hooks;
|
|
311
352
|
}
|
|
312
353
|
get closed() { return this.connection.closed; }
|
|
313
|
-
|
|
354
|
+
validateNewContext(text, requirePolicy = false) {
|
|
355
|
+
if ((text || requirePolicy) && !this.negotiated.sessionCapabilities?.systemPrompt)
|
|
356
|
+
throw new NeedError('SYSTEM_PROMPT_UNSUPPORTED', '远端未声明系统与开发者上下文能力,不能丢弃这些要求继续', '支持 systemPrompt 的公开 ACP 能力');
|
|
357
|
+
if (this.codexNewSessionOnly && (text || requirePolicy)) {
|
|
358
|
+
if (!supportsCodexContextPolicy(this.negotiated))
|
|
359
|
+
throw new NeedError('SYSTEM_PROMPT_POLICY_UNSUPPORTED', '远端没有明确声明Codex上下文仅新建生效的兼容政策,本轮未发送', 'systemPrompt与精确的aiWorkbenchSystemPrompt新建政策');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
async start(cwd, resumeId, signal, context, prompt) {
|
|
314
363
|
if (this.startAttempted)
|
|
315
364
|
throw new NeedError('SESSION_ALREADY_STARTED', '此连接已用于建立会话,不能转用工作区或身份', '新的独立远端连接');
|
|
316
365
|
this.startAttempted = true;
|
|
366
|
+
this.employeeMeta = context?.employeeExecution;
|
|
317
367
|
const abort = () => this.connection.close(new NeedError('ABORTED', '会话建立已取消', '重新连接主机'));
|
|
318
368
|
signal?.throwIfAborted();
|
|
319
369
|
signal?.addEventListener('abort', abort, { once: true });
|
|
320
370
|
try {
|
|
321
371
|
const hello = await this.stage('acp-initialize', () => this.connection.agent.request(acp.methods.agent.initialize, {
|
|
322
|
-
protocolVersion: acp.PROTOCOL_VERSION, clientInfo: { name: '@ai-workbench/remote-agent', version: '0.8.
|
|
323
|
-
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false, session: { notices: {} } },
|
|
372
|
+
protocolVersion: acp.PROTOCOL_VERSION, clientInfo: { name: '@ai-workbench/remote-agent', version: '0.8.10' },
|
|
373
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false, session: { notices: {} }, ...(this.employeeMeta ? { _meta: { awEmployeeExecution: { version: 1 } } } : {}) },
|
|
324
374
|
}, { cancellationSignal: signal }));
|
|
325
375
|
this.hooks.broker?.acknowledge(acp.methods.agent.initialize);
|
|
326
376
|
if (hello.protocolVersion !== acp.PROTOCOL_VERSION)
|
|
327
377
|
throw new NeedError('ACP_VERSION_MISMATCH', 'ACP 协议版本不兼容', '支持 ACP v1 的远端 agent');
|
|
328
378
|
this.checkReporting();
|
|
329
379
|
this.negotiated = hello.agentCapabilities ?? {};
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
380
|
+
if (this.employeeMeta) {
|
|
381
|
+
const policy = record(hello._meta?.awEmployeeExecution);
|
|
382
|
+
if (!this.hooks.employeeExecution || !policy || Object.keys(policy).length !== 5 || policy.version !== 1 || policy.nativeCliVersion !== '0.160.1' || policy.native !== true || policy.perRpcGate !== true || policy.noHostFallback !== true)
|
|
383
|
+
throw new NeedError('EMPLOYEE_EXECUTION_UNSUPPORTED', '当前主机尚不支持在你的电脑执行Codex原生工具,本轮没有执行。', 'Codex员工端原生执行适配器与明确能力声明');
|
|
384
|
+
this.employeeNegotiated = true;
|
|
385
|
+
this.detail({ kind: 'session', severity: 'info', code: 'EMPLOYEE_EXECUTION_SELECTED', summary: '文件和命令在员工电脑的选定目录执行,Git使用本机仓库;模型、登录和全局助手要求仍在主机。本机MCP、hooks、plugins、冷恢复、快捷命令和主机文件报告尚不支持。外层ACP冷恢复已拒绝;原生子代理回收/缺少存活父会话时的恢复仅做源码检查,未实测。', payload: { bindingId: this.employeeMeta.bindingId, workspaceId: this.employeeMeta.workspaceId, cwd: this.employeeMeta.cwd, nativeCliVersion: '0.160.1', noHostFallback: true, agentConfigSource: 'host CODEX_HOME', unsupportedLocalFeatures: ['mcp', 'hooks', 'plugins', 'cold-resume', 'slash-commands', 'host-file-reports'] } });
|
|
333
386
|
}
|
|
334
|
-
const
|
|
387
|
+
const systemPrompt = context?.systemPrompt;
|
|
388
|
+
this.codexNewSessionOnly = !!context?.codexNewSessionOnly;
|
|
389
|
+
this.validateNewContext(systemPrompt ?? '', this.codexNewSessionOnly && systemPrompt !== undefined);
|
|
390
|
+
if (prompt)
|
|
391
|
+
this.validatePrompt(prompt);
|
|
392
|
+
if (context?.employeeRouting && resumeId)
|
|
393
|
+
throw new NeedError('EMPLOYEE_CHECKPOINT_STALE', '本机工具连接已变化,不能沿用旧会话。', '安全结束原主机实例并重新建立本机工具通道');
|
|
394
|
+
const routing = context?.employeeRouting;
|
|
395
|
+
const params = { cwd, mcpServers: routing ? structuredClone(routing.mcpServers) : [], ...((routing || this.employeeMeta) ? { _meta: { ...(this.employeeMeta ? { awEmployeeExecution: this.employeeMeta } : {}), ...(routing ? { claudeCode: { options: { toolAliases: structuredClone(routing.toolAliases) } } } : {}) } } : {}), ...(systemPrompt && !(resumeId && this.codexNewSessionOnly) ? { systemPrompt: { mode: 'append', content: [{ type: 'text', text: systemPrompt }] } } : {}) };
|
|
335
396
|
const configRevision = this.resetConfiguration();
|
|
336
397
|
if (resumeId) {
|
|
337
398
|
this.sessionId = resumeId;
|
|
@@ -375,6 +436,51 @@ export class AcpSession {
|
|
|
375
436
|
signal?.removeEventListener('abort', abort);
|
|
376
437
|
}
|
|
377
438
|
}
|
|
439
|
+
/** Codex context is immutable after creation; keep the old remote session intact. */
|
|
440
|
+
async newContextSession(context, signal) {
|
|
441
|
+
this.checkReporting();
|
|
442
|
+
if (!this.ready || !this.sessionId || !this.cwd)
|
|
443
|
+
throw new NeedError('SESSION_REQUIRED', '我需要先建立远端会话', 'ACP 会话');
|
|
444
|
+
if (!this.codexNewSessionOnly)
|
|
445
|
+
throw new NeedError('SYSTEM_PROMPT_POLICY_UNSUPPORTED', '此会话没有使用Codex新建政策', 'Codex新建政策');
|
|
446
|
+
if (this.busy || this.updating || this.selecting)
|
|
447
|
+
throw new NeedError('TURN_BUSY', '上一项会话操作尚未完成', '上一项操作结束');
|
|
448
|
+
const text = context.systemPrompt ?? '';
|
|
449
|
+
this.validateNewSessionContext(context);
|
|
450
|
+
signal?.throwIfAborted();
|
|
451
|
+
const oldId = this.sessionId;
|
|
452
|
+
this.updating = true;
|
|
453
|
+
this.ready = false;
|
|
454
|
+
this.creatingReplacement = true;
|
|
455
|
+
this.pendingConfigurations.clear();
|
|
456
|
+
const abort = () => this.connection.close(new NeedError('ABORTED', '新会话建立已取消,远端状态需确认', '管理员核对远端状态'));
|
|
457
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
458
|
+
try {
|
|
459
|
+
const revision = this.resetConfiguration();
|
|
460
|
+
const result = await this.stage('acp-session-new', () => this.connection.agent.request(acp.methods.agent.session.new, { cwd: this.cwd, mcpServers: [], ...(this.employeeMeta ? { _meta: { awEmployeeExecution: this.employeeMeta } } : {}), ...(text ? { systemPrompt: { mode: 'append', content: [{ type: 'text', text }] } } : {}) }, { cancellationSignal: signal }));
|
|
461
|
+
if (!result.sessionId || result.sessionId === oldId)
|
|
462
|
+
throw new NeedError('SESSION_STATE_INVALID', '新会话没有返回独立的远端标识,原会话状态需确认', '独立的新远端会话标识');
|
|
463
|
+
this.sessionId = result.sessionId;
|
|
464
|
+
this.lifecycleConfiguration(result, revision);
|
|
465
|
+
this.hooks.broker?.acknowledge(acp.methods.agent.session.new);
|
|
466
|
+
this.checkReporting();
|
|
467
|
+
this.systemPrompt = text;
|
|
468
|
+
this.method = acp.methods.agent.session.new;
|
|
469
|
+
this.ready = true;
|
|
470
|
+
return this.sessionId;
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
this.checkReporting();
|
|
474
|
+
if (error instanceof ModelConfigurationError)
|
|
475
|
+
throw error;
|
|
476
|
+
throw acpNeed(error);
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
this.creatingReplacement = false;
|
|
480
|
+
this.updating = false;
|
|
481
|
+
signal?.removeEventListener('abort', abort);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
378
484
|
async updateContext(context, signal) {
|
|
379
485
|
this.checkReporting();
|
|
380
486
|
if (!this.ready || !this.sessionId || !this.cwd)
|
|
@@ -472,6 +578,17 @@ export class AcpSession {
|
|
|
472
578
|
export function acpNeed(error) {
|
|
473
579
|
if (error instanceof NeedError)
|
|
474
580
|
return error;
|
|
581
|
+
if (error instanceof acp.RequestError) {
|
|
582
|
+
// Host SDK1.9 uses Internal error + data.details for thrown adapter errors.
|
|
583
|
+
// Only fixed codes cross this boundary; arbitrary detail strings are never copied.
|
|
584
|
+
for (const value of [error.message, record(error.data)?.details]) {
|
|
585
|
+
if (typeof value !== 'string')
|
|
586
|
+
continue;
|
|
587
|
+
const code = value.split(':', 1)[0];
|
|
588
|
+
if (employeeAdapterCodes.has(code))
|
|
589
|
+
return new NeedError(code, employeeFailure(code), `员工电脑执行适配器明确拒绝:${code};RPC 错误码 ${error.code}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
475
592
|
if (error instanceof acp.RequestError && error.code === -32000)
|
|
476
593
|
return new NeedError('LOGIN_REQUIRED', '需要登录,请到设置 → 远程主机连接 点登录', '该员工自己的远端模型登录');
|
|
477
594
|
if (error instanceof acp.RequestError)
|
|
@@ -481,3 +598,4 @@ export function acpNeed(error) {
|
|
|
481
598
|
function record(value) { return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined; }
|
|
482
599
|
const knownUpdates = new Set(['user_message_chunk', 'agent_message_chunk', 'agent_thought_chunk', 'tool_call', 'tool_call_update', 'plan', 'plan_update', 'plan_removed', 'available_commands_update', 'current_mode_update', 'config_option_update', 'session_info_update', 'usage_update', 'notice', 'compaction_update', 'compaction_summary_chunk', 'subagent_update', 'session_message', 'session_message_chunk']);
|
|
483
600
|
function safeLabel(value) { return value.replaceAll(/[\r\n\x00-\x1f\x7f]/g, ' '); }
|
|
601
|
+
const employeeAdapterCodes = new Set(["AW_EMPLOYEE_ADDITIONAL_DIRECTORIES_UNSUPPORTED", "AW_EMPLOYEE_SLASH_COMMAND_UNSUPPORTED", "AW_EMPLOYEE_HOST_FILE_REPORT_UNSUPPORTED", "AW_EMPLOYEE_CONFIG_DISCOVERY_OVERRIDE_UNSUPPORTED", "AW_EMPLOYEE_CONFIG_ADMISSION_INVALID", "AW_EMPLOYEE_CONFIG_ADMISSION_FAILED", "AW_EMPLOYEE_HOST_PROJECT_CONFIG_UNSUPPORTED", "AW_EMPLOYEE_EXECUTOR_MCP_UNSUPPORTED", "AW_EMPLOYEE_EXECUTOR_CONFIG_PROTOCOL_INVALID", "AW_EMPLOYEE_EXECUTOR_CONFIG_READ_FAILED", "AW_EMPLOYEE_AGENT_CONFIG_UNAVAILABLE", "AW_EMPLOYEE_BINDING_INVALID", "AW_EMPLOYEE_BINDING_MISMATCH", "AW_EMPLOYEE_CAPABILITY_REQUIRED", "AW_EMPLOYEE_CLOSE_FAILED", "AW_EMPLOYEE_CLOSE_RESPONSE_INVALID", "AW_EMPLOYEE_COLD_RESUME_UNSUPPORTED", "AW_EMPLOYEE_CWD_MISMATCH", "AW_EMPLOYEE_ENVIRONMENT_REGISTRATION_FAILED", "AW_EMPLOYEE_ENVIRONMENT_REQUIRED", "AW_EMPLOYEE_EXECUTION_CLOSED", "AW_EMPLOYEE_EXECUTION_ID_REQUIRED", "AW_EMPLOYEE_HOST_AUXILIARY_CONFIG_UNSUPPORTED", "AW_EMPLOYEE_HOST_AUXILIARY_UNSUPPORTED", "AW_EMPLOYEE_HOST_FALLBACK_FORBIDDEN", "AW_EMPLOYEE_MCP_UNSUPPORTED", "AW_EMPLOYEE_MESSAGE_INVALID", "AW_EMPLOYEE_MESSAGE_TOO_LARGE", "AW_EMPLOYEE_OPEN_RESPONSE_INVALID", "AW_EMPLOYEE_READ_MESSAGE_INVALID", "AW_EMPLOYEE_READ_RESPONSE_INVALID", "AW_EMPLOYEE_RELAY_ADDRESS_INVALID", "AW_EMPLOYEE_RELAY_FAILED", "AW_EMPLOYEE_REQUEST_INVALID", "AW_EMPLOYEE_SESSION_UNKNOWN", "AW_EMPLOYEE_WIRE_FAILED", "AW_EMPLOYEE_WRITE_RESPONSE_INVALID"]);
|
package/dist/approval-label.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RequestPermissionRequest } from '@agentclientprotocol/sdk/unstable';
|
|
2
2
|
/** ACP diff.oldText=null means a new file. Write alone may overwrite a file. */
|
|
3
|
-
export declare function approvalLabel(request: RequestPermissionRequest): {
|
|
3
|
+
export declare function approvalLabel(request: RequestPermissionRequest, location?: 'host' | 'local'): {
|
|
4
4
|
toolName: string;
|
|
5
5
|
reason: string;
|
|
6
6
|
} | undefined;
|
package/dist/approval-label.js
CHANGED
|
@@ -5,13 +5,25 @@ function display(value) {
|
|
|
5
5
|
return safe && !safe.includes('[已脱敏:') ? safe : undefined;
|
|
6
6
|
}
|
|
7
7
|
/** ACP diff.oldText=null means a new file. Write alone may overwrite a file. */
|
|
8
|
-
export function approvalLabel(request) {
|
|
8
|
+
export function approvalLabel(request, location = 'host') {
|
|
9
9
|
const tool = request.toolCall;
|
|
10
10
|
const diffs = tool.content?.filter(item => item.type === 'diff') ?? [];
|
|
11
|
-
const
|
|
11
|
+
const originalName = tool.name ?? tool._meta?.claudeCode?.toolName ?? /^(Write|Edit|MultiEdit) /.exec(tool.title ?? '')?.[1];
|
|
12
|
+
const employee = location === 'local' && originalName?.startsWith('mcp__employee__');
|
|
13
|
+
const name = employee ? originalName.slice('mcp__employee__'.length) : originalName;
|
|
12
14
|
const fileAction = diffs.length > 0 || name === 'Write' || name === 'Edit' || name === 'MultiEdit';
|
|
13
15
|
const paths = diffs.length ? diffs.map(diff => diff.path) : tool.locations?.map(location => location.path) ?? [];
|
|
14
16
|
let title = tool.title ?? '';
|
|
17
|
+
const raw = employee && tool.rawInput && typeof tool.rawInput === 'object' && !Array.isArray(tool.rawInput) ? tool.rawInput : undefined;
|
|
18
|
+
if (employee && raw) {
|
|
19
|
+
if ((fileAction || name === 'Read') && !paths.length && typeof raw.file_path === 'string')
|
|
20
|
+
paths.push(raw.file_path);
|
|
21
|
+
if ((name === 'Glob' || name === 'Grep') && !paths.length)
|
|
22
|
+
paths.push(typeof raw.path === 'string' ? raw.path : '当前文件夹');
|
|
23
|
+
title = typeof raw.description === 'string' ? raw.description : typeof raw.command === 'string' ? raw.command : '';
|
|
24
|
+
if ((name === 'Glob' || name === 'Grep') && typeof raw.pattern === 'string')
|
|
25
|
+
title = raw.pattern;
|
|
26
|
+
}
|
|
15
27
|
if (name && title.startsWith(name + ' '))
|
|
16
28
|
title = title.slice(name.length + 1);
|
|
17
29
|
else if (title === name || title === tool.toolCallId)
|
|
@@ -32,7 +44,7 @@ export function approvalLabel(request) {
|
|
|
32
44
|
const files = safe.map((path, index) => names.filter(name => name === names[index]).length > 1 ? path : names[index]);
|
|
33
45
|
return { toolName: action, reason: action + ' ' + files.join('、') };
|
|
34
46
|
}
|
|
35
|
-
const action = { read: '读取文件', edit: '修改文件', delete: '删除文件', move: '移动文件', search: '搜索', execute: '执行主机命令', think: '分析', fetch: '获取内容', other: '远程操作' }[tool.kind ?? ''] ?? '远程操作';
|
|
47
|
+
const action = (employee ? { Read: '读取文件', Glob: '查找文件', Grep: '搜索内容', Bash: '执行本机命令' }[name] : undefined) ?? { read: '读取文件', edit: '修改文件', delete: '删除文件', move: '移动文件', search: '搜索', execute: employee ? '执行本机命令' : '执行主机命令', think: '分析', fetch: '获取内容', other: '远程操作' }[tool.kind ?? ''] ?? '远程操作';
|
|
36
48
|
const safePaths = paths.map(display);
|
|
37
49
|
if (safePaths.some(path => path === undefined))
|
|
38
50
|
return;
|