fix: allow remote Execute without bubblewrap

Remote Hosts without the Ask-only bubblewrap tool could report matching Agent and Runtime versions but fail project creation because the Agent hid the Runtime ACP capability entirely. The Agent now advertises a verified Runtime independently of bubblewrap, so Execute can start directly while Ask keeps its existing read-only bubblewrap boundary.

Release note: 修复远程 Host 缺少 bubblewrap 时 Execute 项目无法创建的问题;更新到 Agent 0.11.16 后,Execute 不再受 Ask 专用系统工具影响。
This commit is contained in:
mesalogo
2026-09-03 15:59:05 +08:00
parent 74c3ec19b4
commit 987a08ad78
4 changed files with 13 additions and 25 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"formatVersion": 1, "formatVersion": 1,
"agentVersion": "0.11.15", "agentVersion": "0.11.16",
"protocol": { "protocol": {
"major": 2, "major": 2,
"minor": 0 "minor": 0
@@ -12,7 +12,7 @@ Linux x64/arm64、取消和离线 GoodBuddy 传输的真实 Host 验收。
Windows 到 Linux x64 的安装、Agent-owned Prompt、Agent 本地模型 gateway、断线恢复、 Windows 到 Linux x64 的安装、Agent-owned Prompt、Agent 本地模型 gateway、断线恢复、
同一 OpenCode Session 续接、取消和终态清理已经使用真实模型与工具验证。Agent 同一 OpenCode Session 续接、取消和终态清理已经使用真实模型与工具验证。Agent
`0.11.14` 已通过独立 workflow 发布 Linux x64/arm64 复合包和签名累计目录;当前未发布 `0.11.14` 已通过独立 workflow 发布 Linux x64/arm64 复合包和签名累计目录;当前未发布
Agent 源码 lock 为 `0.11.15`,Desktop 恢复候选为 `0.12.2`,等待各自独立发布审批。 Agent 源码 lock 为 `0.11.16`,Desktop 恢复候选为 `0.12.2`,等待各自独立发布审批。
现有源码显示本地与远端 OpenCode 原生 Task,并取消 GoodBuddy 对生产 Prompt 的 现有源码显示本地与远端 OpenCode 原生 Task,并取消 GoodBuddy 对生产 Prompt 的
固定墙钟总时限。失败的 `agent-v0.11.3` 保持不可变且未发布。 固定墙钟总时限。失败的 `agent-v0.11.3` 保持不可变且未发布。
@@ -192,7 +192,8 @@ Ask 使用系统 `bwrap`:
search、edit、execute、未知工具以及只提供持久授权的请求全部拒绝。 search、edit、execute、未知工具以及只提供持久授权的请求全部拒绝。
`allow_once` 只解决 OpenCode 发起原生读取前的 ACP 协商,真正的文件系统边界仍是只读 `allow_once` 只解决 OpenCode 发起原生读取前的 ACP 协商,真正的文件系统边界仍是只读
`bwrap` bind。Host 缺少 `bwrap` 时 Ask 不可用。 `bwrap` bind。Host 缺少 `bwrap` 时 Ask 启动失败,但不影响 Runtime 能力声明或 Execute
直接启动。
### Execute ### Execute
@@ -349,7 +350,7 @@ model bridge,以及生命周期和恢复逻辑。单元测试、mock、fixture
- 2026-08 的本地 fixture 完整验证 Linux x64 Agent `0.11.2-e2e.12`、Node `24.19.0` - 2026-08 的本地 fixture 完整验证 Linux x64 Agent `0.11.2-e2e.12`、Node `24.19.0`
和 Agent protocol `2.0`;当时没有 arm64 fixture,因此该记录不能作为当前独立发布 和 Agent protocol `2.0`;当时没有 arm64 fixture,因此该记录不能作为当前独立发布
的双架构验收。Agent `0.11.10` 后续已由原生 workflow 发布并公开验证双架构工件; 的双架构验收。Agent `0.11.10` 后续已由原生 workflow 发布并公开验证双架构工件;
当前源码 lock 固定为 `0.11.15`;正式发布状态以独立 Agent Release 与签名 catalog 为准。 当前源码 lock 固定为 `0.11.16`;正式发布状态以独立 Agent Release 与签名 catalog 为准。
- 2026-08-30 在共享 Linux x64 Host 的隔离测试 HOME 中验证当前 `0.11.13` 源码候选: - 2026-08-30 在共享 Linux x64 Host 的隔离测试 HOME 中验证当前 `0.11.13` 源码候选:
签名 Runtime 清单的测试墙钟上限为 1 秒,模型桥首轮故意延迟 2.515 秒后 Prompt 仍在 签名 Runtime 清单的测试墙钟上限为 1 秒,模型桥首轮故意延迟 2.515 秒后 Prompt 仍在
5.770 秒正常完成;随后 OpenCode 原生 Task 依次产生 `running`、`completed` 子 Agent 5.770 秒正常完成;随后 OpenCode 原生 Task 依次产生 `running`、`completed` 子 Agent
@@ -397,6 +398,11 @@ model bridge,以及生命周期和恢复逻辑。单元测试、mock、fixture
workflow 与 Release Notes 聚焦测试 `106/106` 通过、1 项平台用例跳过;全量 workflow 与 Release Notes 聚焦测试 `106/106` 通过、1 项平台用例跳过;全量
`npm test` 为 `3478` 通过、`51` 跳过,typecheck 与 lint 通过。本轮没有新增模型调用, `npm test` 为 `3478` 通过、`51` 跳过,typecheck 与 lint 通过。本轮没有新增模型调用,
也未在本地运行 production build、package 或安装探针。 也未在本地运行 production build、package 或安装探针。
- 2026-09-03 使用包含当前 Runtime capability 修复的源码 harness,在共享 Linux x64 Host
的唯一 `/tmp/goodbuddy-e2e-runtime-capability-*` 隔离 HOME 中读取签名 Runtime 副本。
Agent 成功报告已登记 OpenCode Runtime,Execute 随后直接启动 OpenCode 而未经过
`bwrap`;Runtime owner 和隔离目录均已清理,未修改 Host current Agent/Runtime。
本次只验证能力声明和真实进程启动,没有发送 Prompt,真实文本模型调用 0 次。
- 正常 Host 更新路径把 Linux x64 Host 的 Agent 更新为 `0.11.2-e2e.12`,并确认 - 正常 Host 更新路径把 Linux x64 Host 的 Agent 更新为 `0.11.2-e2e.12`,并确认
OpenCode Runtime 已安装版本与所需版本均为 `1.18.9`。 OpenCode Runtime 已安装版本与所需版本均为 `1.18.9`。
- 一条新的 Ask 用户操作只提交一次。OpenCode 先在 build 模型轮次请求一个原生 - 一条新的 Ask 用户操作只提交一次。OpenCode 先在 build 模型轮次请求一个原生
+2 -4
View File
@@ -37,7 +37,7 @@ afterEach(() => {
}) })
describe('production Runtime composition', () => { describe('production Runtime composition', () => {
it('loads metadata installed after startup and caches the registered Runtime identity', async () => { it('loads Runtime metadata independently of Ask-only Host prerequisites', async () => {
const fixture = await createRuntimeBundleTestFixture() const fixture = await createRuntimeBundleTestFixture()
const root = fixture.root const root = fixture.root
temporaryPaths.push(root) temporaryPaths.push(root)
@@ -62,7 +62,6 @@ describe('production Runtime composition', () => {
blobSink: async () => {}, blobSink: async () => {},
agentExecutablePath, agentExecutablePath,
reportError, reportError,
prerequisitesAvailable: async () => true,
verificationEnvironment: 'test', verificationEnvironment: 'test',
filesystemPlatform: 'win32' filesystemPlatform: 'win32'
}) })
@@ -128,7 +127,7 @@ describe('production Runtime composition', () => {
events.close() events.close()
}) })
it('composes all autonomous ACP methods but advertises no Runtime when prerequisites fail', async () => { it('composes all autonomous ACP methods when no Runtime is installed', async () => {
const root = temporaryDirectory() const root = temporaryDirectory()
const runtimeRoot = resolve(root, 'runtimes') const runtimeRoot = resolve(root, 'runtimes')
const stateDirectory = resolve(root, 'state') const stateDirectory = resolve(root, 'state')
@@ -155,7 +154,6 @@ describe('production Runtime composition', () => {
releaseKeyRegistry, releaseKeyRegistry,
runtimeLock: {} as RemoteRuntimeLock, runtimeLock: {} as RemoteRuntimeLock,
registry, registry,
prerequisitesAvailable: async () => false,
verificationEnvironment: 'test', verificationEnvironment: 'test',
filesystemPlatform: 'win32' filesystemPlatform: 'win32'
}) })
+1 -17
View File
@@ -46,8 +46,6 @@ import {
} from './agent-model-gateway' } from './agent-model-gateway'
import type { AgentDiagnosticLog } from './diagnostic-log' import type { AgentDiagnosticLog } from './diagnostic-log'
const BWRAP_EXECUTABLE = '/usr/bin/bwrap'
export type ProductionRuntimeProtocol = { export type ProductionRuntimeProtocol = {
runtimes: ReturnType<typeof createVerifiedRuntimeCapabilitySource> runtimes: ReturnType<typeof createVerifiedRuntimeCapabilitySource>
methods: RuntimeAcpBackend['methods'] methods: RuntimeAcpBackend['methods']
@@ -85,7 +83,6 @@ export type ProductionRuntimeCompositionOptions = {
releaseKeyRegistry?: AgentReleaseKeyRegistry releaseKeyRegistry?: AgentReleaseKeyRegistry
runtimeLock?: RemoteRuntimeLock runtimeLock?: RemoteRuntimeLock
registry?: RuntimeBundleRegistry registry?: RuntimeBundleRegistry
prerequisitesAvailable?: () => boolean | Promise<boolean>
launchProcess?: typeof launchDirectLinuxStdioProcessOwner launchProcess?: typeof launchDirectLinuxStdioProcessOwner
reconcileOrphanedProcesses?: ( reconcileOrphanedProcesses?: (
installationId: string installationId: string
@@ -222,9 +219,6 @@ export async function createProductionRuntimeProtocol(
}) })
return await source() return await source()
} }
const prerequisites =
options.prerequisitesAvailable ??
productionRuntimePrerequisitesAvailable
const launchProcess = const launchProcess =
options.launchProcess ?? launchDirectLinuxStdioProcessOwner options.launchProcess ?? launchDirectLinuxStdioProcessOwner
let backend: RuntimeAcpBackend let backend: RuntimeAcpBackend
@@ -328,8 +322,7 @@ export async function createProductionRuntimeProtocol(
options.agentExecutablePath === undefined || options.agentExecutablePath === undefined ||
!(await executableAvailable( !(await executableAvailable(
resolve(options.agentExecutablePath) resolve(options.agentExecutablePath)
)) || ))
!(await prerequisites())
) { ) {
return [] return []
} }
@@ -540,12 +533,3 @@ function bindingDirectoryDigest(
])) ]))
.digest('hex') .digest('hex')
} }
async function productionRuntimePrerequisitesAvailable(): Promise<boolean> {
try {
await access(BWRAP_EXECUTABLE, constants.X_OK)
return true
} catch {
return false
}
}