Skip to content

refactor(computer-use): enforce semantic-only actions and enable guarded keyboard input - #4497

Merged
hqhq1025 merged 8 commits into
mainfrom
codex/semantic-only-action-space
Sep 3, 2026
Merged

refactor(computer-use): enforce semantic-only actions and enable guarded keyboard input#4497
hqhq1025 merged 8 commits into
mainfrom
codex/semantic-only-action-space

Conversation

@hqhq1025

@hqhq1025 hqhq1025 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Why

Maka exposed coordinate mutation names to the model even though shipping configurations refused them. Models could therefore plan pointer actions that no production executor would run, while platform integrations could drift toward separate schemas or pixel fallbacks.

The production Computer Use surface should express intent through observed elements, windows, or verified focus. Presentation geometry may animate the cursor, but it must never become executor input. Every model-facing action must have a reachable production path.

This PR also intentionally enables the supported keyboard path. That is a product capability change, not incidental cleanup.

What changes

  • remove model-facing coordinate actions and fields from the Core catalog, Runtime schemas and codec, approval classes, bindings, fingerprints, screenshot policy, real-model policy, and evaluation/report vocabularies
  • remove the Runtime and maka-cu backend path that converted model coordinates into dispatch.point
  • remove hold_key, which had no reachable shipping implementation; retain only historical transcript rendering and negative schema/codec tests
  • keep click_element, element_sequence.steps[].click, secondary_action, standalone screenshots, and semantic window geometry unchanged
  • intentionally enable type, key, and press_key when the executor advertises the required key capability
  • require every keyboard mutation to remain bound to the quoted observation and a named element or verified focus owner, pass the recent physical-input fence, and receive keyboard_mutation approval
  • preserve executor tier/path validation for dispatch.key
  • keep presentation-only element centers for cursor animation while removing obsolete move/drag overlay vocabulary
  • carry the immediately preceding bundled Computer Use skill hash into the trusted legacy chain so upgrades do not misclassify an untouched skill as modified
  • remove dead protocol/model vocabulary and unused Core types

This establishes one model-facing action space for macOS, Windows, and future executors. A platform backend may use a PID/HWND-bound native mechanism to implement an already observation-bound action, but it must not accept model-provided coordinates or silently fall back from semantic intent to pixels.

Protocol boundary

coordinate-background remains an executor result tier in the closed maka.cu/2 protocol. Renaming that result vocabulary is outside this action-space change.

The handshake still validates pointActions for complete protocol fidelity, even though Maka no longer consumes that capability or calls dispatch.point. CuPoint also remains because presentation geometry still uses it.

Companion executor contract PR maka-agent/maka-cu#6 is merged and makes dispatch.point fail closed. The currently pinned bundled executor predates that revision and may still advertise point actions, but this is not an execution gap because this PR removes every Maka producer of dispatch.point; merge order is therefore safe.

Coverage added in the final review round

  • an existing install locked to the previous bundled Computer Use skill hash remains trusted and unmodified after upgrade
  • type, key, and press_key flow through tool.permissionArgs as observation-bound keyboard_mutation requests
  • default backend configuration reaches dispatch.key only through a bound target or verified focus owner
  • a window with no verified focused element fails closed without sending dispatch.key
  • an invalid executor-reported keyboard path is rejected
  • a keyboard refusal reaches the next model prompt and the turn still completes normally
  • clearSession cannot mask a delivered keyboard mutation outcome

Validation

Validated on head 93152755f after merging origin/main cd4aa3d8f:

  • npm run build:test
  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm run check:asf-headers
  • Core tests: 781/781
  • Runtime tests: 3173 passed, 13 skipped
  • Computer Use tests: 114/114
  • Computer Use script tests: 4/4
  • focused keyboard approval, refusal propagation, no-focus, executor-path, and delivered-outcome tests
  • git diff --check

A full parallel npm run test:dist completed the Computer Use suite successfully but exposed six unrelated process/timing flakes in existing Runtime shell, Runtime Host candidate, Eval relay, and Desktop Rive tests under concurrent load. Every failed test passed when rerun serially. Hosted CI is the final clean-environment check for this head.

Refs #4318

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coordinate half of this is right and clean. The mismatch is real: main lets the model plan left_click, scroll, zoom and the rest while the only shipping guard (allowCompatibilityInputDispatch, set to false by the sole caller) refuses them, and maka-cu advertises pointActions: []. maka-agent/maka-cu#6 is merged and fails closed on dispatch.point, so merge order does not matter. The derived presentation point never reaches an executor (its two consumers are the overlay context and the hook), pointer_mutation has zero residue anywhere, the skill catalog is regenerated, and the approval scope keys are observation-scoped so nothing persisted is orphaned. Builds and the three CU suites pass locally.

One thing I need you to decide before this merges. On main the same option gated dispatchKey too (maka-cu-backend.ts:2116), and its comment says every type, key and press_key ends there in every shipping configuration. This PR removes both guards. Keyboard dispatch is now on by default, gated only by the executor's keyActions, which maka-cu advertises as ["type", "key"]. The test diff shows it: every keyboard case on main had to pass allowCompatibilityInputDispatch: true to reach dispatch.key, and here they reach it with no option at all. The body says "preserve keyboard actions" and "the option that could enable synthetic pointer input"; neither is what the code does. The remaining guards (focusToken, physicalInputFailure, keyboard_mutation approval) are all still there, so this is not a hole, but it is a feature switched on inside a deletion, and the skill text quietly moved from "do not plan around press_key, type, key" to "keyboard actions remain capability-dependent". If turning keyboard on is intended, say so in the title or body, add one test asserting that the default configuration reaches dispatch.key, and drop the now-dead allowCompatibilityInputDispatch: false in scripts/computer-use/real-ax-harness.mjs, whose stated purpose was "no synthetic input". If it is not intended, keep the keyboard guard (hard-coded off is fine, no option needed).

Deletion residue worth taking in the same round, since it is exactly the drift the comment you removed from core/computer-use.ts warned about: scripts/computer-use/e2e-scenarios.mjs and report-sanitize.mjs still list left_click, mouse_move, scroll and friends in their action vocabularies; MODEL_CALL_PLAIN_VALUES keeps a scroll entry that can never match, and MODEL_CALL_GEOMETRY_ARGS keeps coordinate, start_coordinate, region with a comment about drag origins and zoom rectangles (both callers are on the live path, so if this is for replay say so); pip-feed.ts in desktop still reads resolvedScreenPoint, a field that never had a producer and is now gone from CuRunResult.

Smaller: the deleted treats a global-pointer path as a compromised session case was the only backend-level check of the executor's self-reported path, and keyboard now really does go through cg_event_*, so an equivalent case on dispatch.key would be welcome; computer-action-label.ts keeping the coordinate cases for history rendering is reasonable but reads like a miss without a sentence; coordinate-background as a tier name now labels screenshot, wait and keyboard results, and the body should say why the tier vocabulary is not touched here.

Evidence boundary: static read of 2640f986 against main cdb29399; core, runtime and computer-use built and CU suites green (266, 35, 112); maka-cu read from its HOST_PROTOCOL.md and PR #6, not its Swift source; the pinned maka-cu build in bundled-tools.json not checked against #6.

AI-assisted review: drafted with Maka; I verified the keyboard guard removal, the presentation-point consumers and the vocabulary residue myself.

简体中文

坐标这一半删得对也删得干净,maka-cu#6 已合并且 fail-closed,合并顺序无约束。合并前需要你定一件事:main 上同一个选项也门控着 dispatchKey,本 PR 把两处守卫都删了,键盘合成输入在默认配置下被打开,正文却写的是 preserve。是有意的就在标题或正文说明,加一条默认配置下到达 dispatch.key 的测试,删掉 harness 里已失效的参数;不是有意的就把键盘守卫留下。另外 scripts/computer-use 的动作词表、core 里的坐标投影残留、desktop 的 resolvedScreenPoint 建议同轮清掉。

@@ -2113,9 +2067,6 @@ export function createMakaCuBackend(opts: MakaCuBackendOptions): MakaCuBackend {
*/
target?: { token: string; digest: string },
): Promise<CuRunResult> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On main this call sat behind allowCompatibilityInputDispatch, off in every shipping configuration. With that guard gone, keyboard synthesis is on by default, gated only by keyActions, which maka-cu advertises as ["type", "key"]. If that is intended, the body should say enable, not preserve, and one test should assert the default configuration reaches here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: enabling bound keyboard dispatch by default is intentional. This revision makes that contract explicit in the PR body and adds a default-configuration test that reaches dispatch.key. Keyboard mutations still require an observation-bound window/control or verified focus, pass the recent-physical-input guard, use an executor-advertised key capability, and remain subject to keyboard_mutation approval. I also added a backend test that rejects an invalid executor-reported path for dispatch.key.

@hqhq1025

hqhq1025 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review on head 8d7f733d5:

  • made default-enabled, observation/focus-bound keyboard dispatch an explicit design decision
  • added default dispatch.key coverage and invalid executor-path rejection
  • removed the dead compatibility option from the real AX harness
  • aligned real-model scenarios, policy, reporting, and sanitizer vocabularies with the production semantic action schema
  • removed stale coordinate projection and PiP result fields
  • kept legacy coordinate labels only for historical transcript rendering
  • documented why the coordinate-background protocol tier remains unchanged
  • fixed the Desktop Client Capability schema assertion

Local validation is listed in the updated PR body. CI is running on the new revision. @Astro-Han, please re-review when it is ready.

@hqhq1025

hqhq1025 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Merged the current main into this branch and completed a fresh self-review of the resulting 33-file diff. The previous CI failures were in streaming-remount.spec.ts and transcript-scroll.spec.ts; the corresponding focused Desktop E2E coverage now passes 15/15 on this revision.

Current validation: Core 770/770, Runtime 3152/3152, Computer Use 113/113, Desktop full dependency/application build, Biome on all affected files, and git diff --check. The PR body now records the complete commands and results.

@Astro-Han, could you please re-review head 19f764c90? Your earlier keyboard-dispatch and deletion-residue findings are addressed in this revision.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coordinate-action removal is internally consistent: the coordinate variants, compatibility gate, provider vocabulary, and presentation-only paths were traced across Core, Runtime, Desktop, Computer Use, scripts, and the native protocol boundary. I found one remaining model/executor contract mismatch.

Validation on exact head 19f764c905bc71f253a3a1717d264ad964de17f5: clean install, build:test, full typecheck, Core 770/770, Runtime 3139 passed / 13 skipped, Computer Use 113/113, changed-file Biome, ASF headers, and git diff --check. A conflict-free synthetic merge onto current origin/main 726fb809edd32ed2cc90688b5f6fbccbd78786a1 passed the same build/typecheck and all three suites. The hosted test check is still running.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

Comment thread packages/core/src/computer-use.ts Outdated
'wait',
'zoom',
] as const;
export const CU_ACTION_TYPES = ['screenshot', 'type', 'key', 'hold_key', 'wait'] as const;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Remove or implement hold_key before advertising this action space

This catalog feeds the model-facing schema, and the Runtime path accepts hold_key, adapts it to a CuAction, then calls deps.backend.run. The only production backend handles only wait, screenshot, type, and key (packages/computer-use/src/maka-cu-backend.ts:2306-2320); every other action reaches the unconditional unsupported_action return at lines 2322-2325. Therefore every valid model-generated hold_key call is guaranteed to fail. This revision also removes the previous tool-description warning that hold_key has no maka.cu/2 execution path while retaining it in real-run/report vocabularies, so the advertised semantic-only contract still contains an unusable verb. Please either remove hold_key from the model-facing catalogs/schemas/reporting, or add protocol/backend support plus an end-to-end backend test.

@hqhq1025

hqhq1025 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up on the model/executor contract review: head 64e2e6b10 removes hold_key from the core action catalog, both model-facing Runtime schemas, the codec adapter, screenshot policy, and evaluation/report vocabularies. The shipping backend had no implementation for it, so keeping it model-visible violated the same fail-closed action-space principle as the removed coordinate actions.

Negative coverage now verifies that both schemas and the codec reject hold_key. The only remaining production reference is the UI label used to render historical transcripts.

Fresh local validation after merging current main: Core 770/770, Runtime 3152 passed with 13 skipped, Computer Use 113/113, Computer Use scripts 4/4, changed-file Biome, and git diff --check.

@Astro-Han, could you please re-review this new head after hosted CI completes? An independent human approval is still required before merge.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous hold_key model/executor mismatch is fixed on this head. The action is no longer advertised by the Core catalog (packages/core/src/computer-use.ts:170), is absent from Runtime's strict accepted union (packages/runtime/src/computer-use-codec.ts:72-289), and is rejected by both model-facing schemas and the adapter (packages/runtime/src/__tests__/computer-use-wire-schema.test.ts:129-138, packages/runtime/src/__tests__/computer-use-codec-adapt.test.ts:85-95). The remaining UI label is explicitly retained only for rendering persisted historical transcripts (packages/ui/src/tool-activity/computer-action-label.ts:229-237). I found no remaining P0-P3 issue in the full 33-file change.

Validation on exact head 64e2e6b1020c3725733590365e4cb288576bf769: clean Node 22 install, build:test, full typecheck, Core 774/774, Runtime 3139 passed / 13 skipped, Computer Use 113/113, changed-file Biome, ASF headers, and git diff --check. A conflict-free synthetic merge 651ccc50e5ad4b87667cd0f43acec2f757f6feaf onto current origin/main 079f784a59b2717b46460458eca754f7fe9c5386 passed the same build, typecheck, and all three suites. The hosted test check also completed successfully. Native macOS execution was not available on this Linux review host.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers. One defect to fix before merge, and one part of the diff I would rather see land on its own.

The direction holds up. Coordinate actions were model-facing while allowCompatibilityInputDispatch was hard off in every shipping wiring, so models could plan pointer actions no executor would run. This is a net deletion that leaves the strict schemas as the single authority, and the negative coverage pins it through the exports the tool actually uses rather than the schema module.

I checked the two things that could have forced a migration, and both come back clean:

hold_key is genuinely unreachable. It falls to pointActionFor, which has no case for it, so run() answers unsupported_action before any dispatch gate. Deleting it is cleanup, not capability removal.

Removing 'pointer_mutation' from COMPUTER_USE_APPROVAL_CLASSES strands no stored records. That constant is a strict decode gate (interaction-permission-review.ts:1007, and oneOf at :1447 throws), so it was worth being sure. It is safe because no persisted request carries an approvalClass at all: the only constructor that produces one, projectInteractionPermissionRequest at interaction.ts:414, has no production caller, and the sole writer into core_interaction_requests is HostInteractionCoordinator, which admits exactly three request shapes (interaction-coordinator.ts:29-31). There is no permission branch, so the table holds no row the narrowed union could reject.

[P2] The old skill hash was not carried forward

Editing the Computer Use skill body moves contentSha256 from sha256:64aa2ef2… to sha256:ef548414…, but legacyContentSha256 still holds only the two older hashes.

isTrustedBundledLock (skills-governance.ts:246) builds the trusted set from both fields. A user who installed before this change is pinned at 64aa2ef2…, now in neither, so :139 returns metadataError('unsupported_schema', 'Skill lock source is not trusted in this Maka version.') and governance reports a skill they never touched as failing validation.

I checked how far it goes: the metadata_error branches in skill-catalog-repository.ts all sit on managed-source paths, so a bundled skill is not disabled by it. The damage is the reported state, not the capability.

The list is hand-maintained at scripts/gen-bundled-skill-catalog.mjs:37, and computer-use already carries two entries there from earlier body edits, so appending sha256:64aa2ef2d608e15792cc04eff7204731671b6b18818964ba95c65f53c694db62 and regenerating should be the whole fix.

[P2, and I would rather it were its own PR] Keyboard synthesis turns on here

dispatchKey used to open with if (opts.allowCompatibilityInputDispatch !== true) return compatibilityInputBlocked(...), and that flag is false in every shipping wiring, so type, key and press_key were unreachable in production. This removes the guard, the option and the pass-through.

I checked the shipped executor rather than its current source: bundled-tools.json pins maka-cu at 4a9787d2, whose HostProtocolWire.swift:592 has keyActions: ["type", "key"]. So keystroke synthesis goes live on merge.

It is not a hole. All four constraints exist on that same path: bound to one observation, verified focus with focusPolicy defaulting to require, the physical-input fence at maka-cu-backend.ts:2081, and keyboard_mutation approval. The worst reachable outcome needs the user to approve it themselves.

What I am raising is scope. Maka loses its own switch, the only remaining decision being the executor's keyActions advertisement in another repository. The fence has never actually run in production, since keyboard and coordinate dispatch were both hard off and element dispatch deliberately does not use it, so this is its first execution. And the title still says semantic-only, which is the opposite direction; the body admits it in one bullet, but anyone reading the diff against the title will misread it.

Coordinate removal stands entirely on its own and its acceptance does not change either way. Your call.

The companion contract is half shipped

maka-cu#6 merged on 09-02 and sets pointActions: [], but bundled-tools.json still pins 4a9787d2 from 08-10 where it is non-empty, and this does not move the pin. Not a hole, Maka stops sending dispatch.point so the advertisement stops mattering and both orders are safe. But the description reads as though both halves are in place.

[P2] Coverage the newly live path is missing

Nothing asserts type, key or press_key reach an approval prompt through tool.permissionArgs; every such assertion uses click_element or observe. The classifier is unit-tested to return keyboard_mutation but is not pinned to the tool path, and the product answer to "can the model type" just flipped. The !target && !snapshot.focused branch at maka-cu-backend.ts:2072 has no test either, and it is now the first thing a model hits sending key at an unfocused window.

The flip side is a real improvement: the focus and fence tests had to pass allowCompatibilityInputDispatch: true to reach dispatch.key, so they were testing a configuration that never shipped. Now they test the shipping one.

[P2] Two obligations left with the deleted model-loop case

That a tool refusal's text reaches the model's next prompt, and that a turn still reaches complete after a failed tool call. Both still hold. unsupported_action is still produced at HEAD, and nothing now asserts a refusal reaches the prompt (the tool-level tests assert result.text, one layer below). The surviving model-loop case is the all-success path. Rewriting the deleted case with a semantic action, driven by a schema or backend refusal, keeps both.

Separately, collapsing for (const semantic of [false, true]) drops the only coverage of the backend.run() branch's rule that clearSession cannot mask a delivered mutation outcome, and that branch still ships for type and key.

[P3] Residue

CuRegion, CU_SCROLL_DIRECTIONS/CuScrollDirection, CuPoint and stringsIn have no consumers left. The comment at computer-use.ts:688 still refers to the POINTER_ACTIONS this PR deleted, which is the drift the deleted comment warned about. computer-use-overlay-hook.ts:154's 'drag' branch is unreachable and :27 still declares 'move' | 'drag'. SAFE_DISPATCH_TOOLS still lists drag and zoom whose only producer is gone, though scroll should stay. maka-cu-service.ts:969 parses capabilities.pointActions with no reader left, defensible as protocol fidelity but worth a comment saying so. Since the test job does not cover them, please run npm run format and npm run lint after.

Next step

The skill hash is the one to fix, a one-line append plus a regenerate. Then the keyboard scope decision, since it is the only remaining one that changes the diff's shape; the model-loop rewrite and residue can follow in the same round.

The skill-hash check is to install on a build without this change, upgrade to this branch, and confirm governance still reports it as unmodified. If the keyboard stays, four manual checks have no automated substitute: type and key raise a keyboard_mutation approval, refusing produces no keystroke, an approved keystroke lands only in the verified focused control, and acting while the user types returns user_intervened.

Evidence boundary: read 64e2e6b10 against merge base 15e4b6b9e. I verified the decode gate and the absence of any production writer for permission requests, hold_key's unreachability, the four keyboard constraints, the pinned maka-cu build's capabilities, and the trusted-hash set. I ran no test suites, did not exercise Computer Use on a real machine, and did not reproduce the governance state on an upgraded install.

AI-assisted review: drafted with Maka.

@hqhq1025 hqhq1025 changed the title refactor(computer-use): make the model action space semantic-only refactor(computer-use): enforce semantic-only actions and enable guarded keyboard input Sep 3, 2026
@hqhq1025

hqhq1025 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the complete review round on head 93152755f and kept the keyboard activation in this PR as an explicit maintainer decision.

Changes made:

  • appended the immediately preceding bundled Computer Use hash (sha256:64aa2ef2...) to legacyContentSha256, regenerated the catalog, and added a governance regression proving an untouched pre-upgrade lock remains trusted
  • added product-path coverage showing type, key, and press_key reach tool.permissionArgs as observation-bound keyboard_mutation requests
  • added backend coverage that a targetless key action with no verified focused element fails closed and sends no dispatch.key
  • retained and exercised rejection of an invalid executor-reported keyboard path
  • restored model-loop coverage proving a keyboard refusal is visible in the next prompt and the turn still completes
  • added coverage proving clearSession cannot replace a delivered keyboard mutation outcome with a local session-stop result
  • removed unused CuRegion, CU_SCROLL_DIRECTIONS, CuScrollDirection, stale POINTER_ACTIONS prose, obsolete overlay move/drag kinds and drag pulse, and dead report sanitizer drag/zoom entries
  • retained CuPoint because it is still the presentation-point type
  • retained and documented pointActions parsing because maka.cu/2 is a closed handshake and Maka should still reject malformed protocol capabilities even though it no longer consumes point actions

The title and body now state that keyboard synthesis is intentionally enabled and document all four constraints: observation/focus binding, executor capability, recent physical-input fencing, and keyboard_mutation approval. They also clarify that the currently pinned executor may predate maka-cu#6, but Maka has no remaining dispatch.point producer, so the merge order is safe.

Validation on this head: full build:test, full typecheck, lint, format check, ASF headers, Core 781/781, Runtime 3173 passed with 13 skipped, Computer Use 114/114, and Computer Use scripts 4/4. A concurrent all-workspace test run exposed six unrelated existing process/timing flakes; each failed case passed in a serial rerun. Hosted CI is now running on the pushed revision.

@Astro-Han, please re-review head 93152755f when CI completes.

@hqhq1025
hqhq1025 merged commit 91624f1 into main Sep 3, 2026
1 check passed
@hqhq1025
hqhq1025 deleted the codex/semantic-only-action-space branch September 3, 2026 12:40
ggbdpq pushed a commit to ggbdpq/maka that referenced this pull request Sep 4, 2026
…ded keyboard input (apache#4497)

* refactor(computer-use): make the model action space semantic-only

* test(computer-use): close semantic action-space residue

* fix(computer-use): remove unsupported hold key action

* fix(computer-use): close keyboard activation review gaps

Generated-by: GLM-5.3-Flash (ZCode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants