Uh oh!
There was an error while loading. Please reload this page.
fix(runtime-host): restore desktop Computer Use capability - #2233
Merged
Conversation
M4n5ter
marked this pull request as ready for review
August 5, 2026 11:58
Uh oh!
There was an error while loading. Please reload this page.
UncertaintyDeterminesYou4ndMe added a commit
to UncertaintyDeterminesYou4ndMe/maka-agent
that referenced
this pull request
Aug 6, 2026
A pre-dispatch tool refusal used to kill the turn. The call event's lane was guessed when the event was built: an `operationId` claims the T1 dispatch protocol, so AgentRun skips the generic projection of that `function_call` and waits for `commitToolPrepared` — which sits after every refusal return. The call fact was never persisted, the synthetic result landed on the generic lane with nothing to attach to, and the ledger refused the `orphan_response` (apache#2234). apache#2233 closed that by predicting the refusals instead: every guard hoisted into a `preflightRejected` boolean read before the operationId. It is correct only while the prediction and the guards agree, and nothing holds them together — a new refusal path, or a guard that grows a condition its hoisted twin does not, silently restores the orphan. This decides the lane where it is known. `pushCallEvent('preflight'|'dispatch')` pushes at most once, every refusal routes through one `refuseBeforeDispatch` helper that keeps call and result together on the generic lane, and only `prepareDurableToolAttempt` asks for T1. The decision is the code path taken, so it cannot drift. apache#2233's predicate, its hoisted boundary read and its early slot reservation go with it; the boundary read and the reservation return to the guards they belong to, and everything else apache#2233 restored is untouched. Two things apache#2233 did not cover, both from the same failure: - A rejected append latched the RuntimeEvent store unavailable, and `commitTerminalRun` returns early on an unavailable store, so one refused event cost the run its own terminal write — a run stuck at `running` with no terminal event. `ToolLedgerRejectionError` now marks a refusal of one bad candidate against a healthy store, and only that skips the latch. An already corrupt ledger refuses well-formed writes for a different reason, so it throws `ToolLedgerCorruptionError` and keeps failing closed. - `agent_swarm`'s selector refusal said only "Provide exactly one of subagent_id or legacy profile", naming neither which of the two mistakes it was nor one value that would work — and the field list an args violation appends is the top-level one, not `items[n]` where the violation was. Tests: `pre-dispatch-refusal-ledger.test.ts` drives all seven refusal paths through the real `scanToolLedger`; on the tagged lane every one of them reproduces the production `orphan_response`. The terminal-write and ledger-corruption cases run against a `canonical` store, the only durability production ships. Refs apache#2234
UncertaintyDeterminesYou4ndMe added a commit
to UncertaintyDeterminesYou4ndMe/maka-agent
that referenced
this pull request
Aug 6, 2026
A pre-dispatch tool refusal used to kill the turn. The call event's lane was guessed when the event was built: an `operationId` claims the T1 dispatch protocol, so AgentRun skips the generic projection of that `function_call` and waits for `commitToolPrepared` — which sits after every refusal return. The call fact was never persisted, the synthetic result landed on the generic lane with nothing to attach to, and the ledger refused the `orphan_response` (apache#2234). apache#2233 closed that by predicting the refusals instead: every guard hoisted into a `preflightRejected` boolean read before the operationId. It is correct only while the prediction and the guards agree, and nothing holds them together — a new refusal path, or a guard that grows a condition its hoisted twin does not, silently restores the orphan. This decides the lane where it is known. `pushCallEvent('preflight'|'dispatch')` pushes at most once, every refusal routes through one `refuseBeforeDispatch` helper that keeps call and result together on the generic lane, and only `prepareDurableToolAttempt` asks for T1. The decision is the code path taken, so it cannot drift. apache#2233's predicate, its hoisted boundary read and its early slot reservation go with it; the boundary read and the reservation return to the guards they belong to, and everything else apache#2233 restored is untouched. Two things apache#2233 did not cover, both from the same failure: - A rejected append latched the RuntimeEvent store unavailable, and `commitTerminalRun` returns early on an unavailable store, so one refused event cost the run its own terminal write — a run stuck at `running` with no terminal event. `ToolLedgerRejectionError` now marks a refusal of one bad candidate against a healthy store, and only that skips the latch. Damage found by the workspace health scan throws `ToolLedgerCorruptionError` and keeps failing closed. Be precise about what that second class buys, because the obvious rationale is wrong: the health scan runs only for tool-bearing events, so a damaged ledger refuses tool facts and would have taken the terminal event. The latch is what keeps it out, which reproduces apache#2234's shape for an already-damaged workspace. Left standing deliberately — it is a behaviour change on a path this commit does not otherwise touch — and tracked in apache#2313, with a test that pins the current price rather than hiding it. - `agent_swarm`'s selector refusal said only "Provide exactly one of subagent_id or legacy profile", naming neither which of the two mistakes it was nor one value that would work — and the field list an args violation appends is the top-level one, not `items[n]` where the violation was. Tests: `pre-dispatch-refusal-ledger.test.ts` drives all seven refusal paths through the real `scanToolLedger`; on the tagged lane every one of them reproduces the production `orphan_response`. The terminal-write and ledger-corruption cases run against a `canonical` store, the only durability production ships. Both error classes are pinned where they are produced, in `sqlite-runtime-store.test.ts`: their messages are byte-identical to the plain `Error` strings they replaced, so nothing else in that suite would notice a regression to `throw new Error(...)` — and the latch exemption would silently stop working. Refs apache#2234
Astro-Han pushed a commit
that referenced
this pull request
Aug 6, 2026
…2240) A pre-dispatch tool refusal used to kill the turn. The call event's lane was guessed when the event was built: an `operationId` claims the T1 dispatch protocol, so AgentRun skips the generic projection of that `function_call` and waits for `commitToolPrepared` — which sits after every refusal return. The call fact was never persisted, the synthetic result landed on the generic lane with nothing to attach to, and the ledger refused the `orphan_response` (#2234). #2233 closed that by predicting the refusals instead: every guard hoisted into a `preflightRejected` boolean read before the operationId. It is correct only while the prediction and the guards agree, and nothing holds them together — a new refusal path, or a guard that grows a condition its hoisted twin does not, silently restores the orphan. This decides the lane where it is known. `pushCallEvent('preflight'|'dispatch')` pushes at most once, every refusal routes through one `refuseBeforeDispatch` helper that keeps call and result together on the generic lane, and only `prepareDurableToolAttempt` asks for T1. The decision is the code path taken, so it cannot drift. #2233's predicate, its hoisted boundary read and its early slot reservation go with it; the boundary read and the reservation return to the guards they belong to, and everything else #2233 restored is untouched. Two things #2233 did not cover, both from the same failure: - A rejected append latched the RuntimeEvent store unavailable, and `commitTerminalRun` returns early on an unavailable store, so one refused event cost the run its own terminal write — a run stuck at `running` with no terminal event. `ToolLedgerRejectionError` now marks a refusal of one bad candidate against a healthy store, and only that skips the latch. Damage found by the workspace health scan throws `ToolLedgerCorruptionError` and keeps failing closed. Be precise about what that second class buys, because the obvious rationale is wrong: the health scan runs only for tool-bearing events, so a damaged ledger refuses tool facts and would have taken the terminal event. The latch is what keeps it out, which reproduces #2234's shape for an already-damaged workspace. Left standing deliberately — it is a behaviour change on a path this commit does not otherwise touch — and tracked in #2313, with a test that pins the current price rather than hiding it. - `agent_swarm`'s selector refusal said only "Provide exactly one of subagent_id or legacy profile", naming neither which of the two mistakes it was nor one value that would work — and the field list an args violation appends is the top-level one, not `items[n]` where the violation was. Tests: `pre-dispatch-refusal-ledger.test.ts` drives all seven refusal paths through the real `scanToolLedger`; on the tagged lane every one of them reproduces the production `orphan_response`. The terminal-write and ledger-corruption cases run against a `canonical` store, the only durability production ships. Both error classes are pinned where they are produced, in `sqlite-runtime-store.test.ts`: their messages are byte-identical to the plain `Error` strings they replaced, so nothing else in that suite would notice a regression to `throw new Error(...)` — and the latch exemption would silently stop working. Refs #2234
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
English
Summary
Root cause
Desktop only publishes its Computer Use offer when the native backend is available. Once a real
maka-cubackend was present, its draft-07 tuple schemas and tool description exceeded assumptions in the Client Capability decoder, so registration failed and the Host exposed only the Agent and Browser groups.A separate failure existed when a call was rejected before dispatch, including Client Capability boundary and saturated subagent admission failures: the call received a durable operation id before durable preparation. Its synthetic response then had no matching durable call, producing an orphan response and draining the Runtime Host.
Finally, the native protocol already supported an explicit Accessibility prompt, but Maka used only silent permission probes. The first explicit Computer Use attempt now requests that grant once while routine per-action preflight remains silent.
Validation
@maka/runtime: 3179 passed, 9 skipped@maka/computer-use: 134 passed@maka/runtime-host: 693 passed@maka/desktop: 1738 passedgit diff --checkmaka_computer, grant Accessibility, invokelist_apps, and receive the native app listFollow-up
中文
概述
根因
Desktop 仅在 native backend 可用时发布 Computer Use offer。接入真实
maka-cu后,其 draft-07 tuple schema 和工具描述超出了 Client Capability decoder 原有假设,导致注册失败,Host 最终只能暴露 Agent 和 Browser 两个工具组。另一个问题出现在调用于 dispatch 前被拒绝时,包括 Client Capability 边界拒绝和 subagent admission 饱和:调用在 durable prepare 前已经获得 operation id,随后生成的 synthetic response 找不到对应 durable call,形成 orphan response 并使 Runtime Host 进入 draining。
此外,native 协议已经支持显式触发 Accessibility 授权,但 Maka 之前只执行静默检查。现在首次明确使用 Computer Use 时会请求一次授权,日常逐操作 preflight 仍保持静默。
验证
@maka/runtime:3179 passed,9 skipped@maka/computer-use:134 passed@maka/runtime-host:693 passed@maka/desktop:1738 passedgit diff --checkmaka_computer、完成 Accessibility 授权、调用list_apps并取得 native 应用列表后续