Skip to content

feat(protocol): add the computer driver SDK - #336

Merged
earakely-scale merged 19 commits into
mainfrom
edgararakelyan/fd-4380-m00-protocol-sdk-prerequisites-for-the-driver-base-class
Oct 27, 2026
Merged

earakely-scale merged 19 commits into
mainfrom
edgararakelyan/fd-4380-m00-protocol-sdk-prerequisites-for-the-driver-base-class

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Adds the computer driver SDK to agentenv-protocol: a driver is a ComputerDriverEnvironment subclass that implements screen operations (capture, click, type, …), and the base class serves the computer contract from them. Linear: FD-4380 (M0.0, the SDK prerequisites) and FD-4348 (M0.2, the base class) of the CUA Env epic. Nothing under src/agent_env/ changes; core gains one test.

SDK changes

  • Structured errors. AgentEnvError(code, message, retryable, applied, hint, status). Raised in an extension it answers with its status and those fields in the error body; raised in a tool (mcp's FastMCP and fastmcp) it answers isError with "<code>: <message>" as text and the fields as structured content. Other extension failures stay extension_failed / 500, now applied: "unknown". invoke_extension raises ExtensionCallError, an httpx.HTTPStatusError carrying the parsed fields, so callers that catch HTTPStatusError (DeployedEnv.invoke) keep working.
  • Several methods per extension. @extension(uri, op=...): handlers sharing a URI merge into one card entry, each method with its own verb and endpoint (/agentenv/ext/<extension>/<op> by default). extension_params(uri) adds extension-level params; declared_extensions() advertises declaration-only ones. Without op, routes and cards are unchanged.
  • @tool metadata. annotations, structured_output and meta on both FastMCPs; a tool returning Annotated[CallToolResult, Model] is sent as is and listed with Model's schema.
  • Choosing what is served. served_tools(tools), served_extensions(extensions) and served_operations(operations) choose at mount which tools, extensions (and methods of one) and data-plane methods an env serves; returning something with no handler is refused. update_tool(name, description=...) changes a served tool's description after the mount, on the card and in tools/list, on both FastMCPs; mounted_card() is the card the mounted application serves, and the card route serves it as it stands.
  • Tool access without a gateway. tool_access = True filters tools/list and refuses tools/call per caller role, and serves disable-tool/v1 / enable-tool/v1 at the gateway's paths and bodies, so modify_env_tool_access works on an env with no gateway. The gateway keeps its own code (its image installs no protocol); tst/unit/env/gateway/tool_access_parity_test.py runs the same rule cases, 5,000 random step sequences included, against both.

agentenv_protocol.computer

  • Operations (@display, @capture, @click, @drag, @scroll, @type_text, @press; the pointer and keyboard groups; @set_display; @run_in_session and @write_files with Linux defaults), checked when the class is defined, chosen per platform by predicates (os, distribution, version, arch, display).
  • The seven computer_* tools and the group tools, with structured results, output schemas, a one-line summary as text, and the errors table's codes and applied.
  • Frames (f<n>, native crops that map back exactly, last 64 kept, stale on a display change, each caller's latest), coordinate spaces (frame and norm, refused rather than clamped, the default written into tool descriptions), the input queue (one call at a time, input_busy, others_since_frame).
  • Framework extensions: computer-ready (real checks; tools refuse device_unavailable until they pass once), computer-display, computer-spaces, computer-keyboard, computer-exec (idempotent by request_id, output capped, timed out; a background app's open pipes are drained rather than waited on).
  • Data plane: data/add into the session user's home from inline bytes, https URLs and read grants, atomic with path checks; data/get of the settings' data_paths, inline or under a write_namespace grant.
  • Layout. The driver class (driver.py) holds the features and hooks. Its state lives in one ComputerRuntime (self._computer: operations, frames, queue, readiness, held keys, command runs). card.py decides what is served, through the hooks above. results.py has each tool's result model, which gives both its output schema and its result. checks.py has the definition-time checks. The mixins (tools.py, extensions.py, dataplane.py) declare what they read from the driver.
  • x and y are optional on click and scroll in every schema, with a description saying when they are needed: a computer without computer-pointer/v1 refuses a call that leaves them out (invalid_argument).
  • An example driver, packages/agentenv-protocol/examples/computer_x11/: the X11 driver the end-to-end runs used (mss capture, XTEST input through python-xlib, both groups, an XTEST ready check, reconnect on a dropped X connection), an image that runs it on Xvfb beside a Tk practice app, and a README. test_computer_x11_example.py runs it against fake mss and Xlib modules. M0.3's reference driver and computer_driver env type come later.
  • Settings from AGENTENV_COMPUTER_SETTINGS. A new computer extra installs mcp, uvicorn and Pillow (uv.lock adds Pillow, from PyPI). agentenv_protocol.computer is not imported by import agentenv_protocol.

Design choices, as built

The 13 choices in the design doc stand. Where the build adds to or departs from them:

  1. Shared tool-access rules: the gateway keeps its own code, one rule-case table tests both.
  2. Routes for several methods: /agentenv/ext/<extension>/<op> with op, today's otherwise.
  3. Error transport: the body carries the meaning; ExtensionCallError subclasses httpx.HTTPStatusError.
  4. Role names: rules keyed by default and *; no role means default.
  5. Frames: f<n>, last 64, latest per caller identity, which is default for every caller until FD-4386; caller_identity(context) is the seam.
  6. Operation signatures as designed, called positionally.
  7. Pointer group: four operations; the base class supplies computer_pointer_list (the default pointer only, no leasing).
  8. Card features as class attributes, plus max_key_repeat, max_scroll_amount and layouts, which the tools needed to bound repeat, amount and the keyboard layout.
  9. Default limits as designed, all in ComputerSettings.
  10. Settings from one JSON env var, AGENTENV_COMPUTER_SETTINGS.
  11. The computer extra is mcp, uvicorn, Pillow; mss and python-xlib go to the reference driver (M0.3).
  12. Readiness gating: tools refuse until the ready checks pass once; a tool call runs the checks itself if nothing has yet, so a driver nobody polls doesn't refuse forever.
  13. data/add: inline bytes, https URLs and read grants (ReadObject under the data-objects/v1 marker's read); no data/reset.

Left out, since nothing uses them yet: @list_windows (no default in this PR), the pointer and window tool parameters (leasing and windows come later), and a shipped fake screen (it lives in packages/agentenv-protocol/tests/computer_fake.py).

agentenv_protocol.computer is a normal public API with no experimental marker; a break after merge gets a !. plugin-api reads only agent_env, so it doesn't cover the protocol.

Testing

  • make unit-test (the protocol's tests and core's unit tier) and make lint pass; the protocol's tests also pass under Python 3.10.
  • Coverage with pytest-cov, run for this PR (not added to the dev extra): every module under computer/ 100%, errors.py and tool_access.py 100%. Every changed line in agent_env_environment.py and client.py is covered except the base _describe_registered_tool stub, which mirrors _register_tool's: an app without MCP tools refuses update_tool before it gets there.
  • tst/unit/env/gateway/process_scope_socket_test.py::test_an_armed_time_trigger_keeps_firing_after_a_client_leaves failed once under full parallel load and passes alone; it waits 0.3 s for a virtual-time mark and predates this branch.
  • End to end on a real X server (EC2), through agent-env and the gateway, with a real agent, and chaos testing: contract 58/58, gateway 9/9, Claude Code agent 4/4 runs, chaos 29/29. Details in feat(protocol): add the computer driver SDK #336 (comment).
  • Rerun at ba1a5f5, after Greptile's findings on the example driver and the runtime (a cancelled timed hold, a click at the pointer queued behind a drag, input during a screenshot) were fixed: its own image passes the contract 58/58. With its driver code in the bundle image: chaos 29/29, gateway 9/9 and Claude Code 2/2 (Sonnet 5.5 and Opus 5.5). A real-X probe of the keyboard fixes: computer_key with A and ! types A!; a Shift held by computer_keyboard_down stays down through typing and a Shift-scroll (ABc); 59 distinct CJK characters, more than Xvfb's 19 spare keycodes, plus 10 repeats come out exact.
  • Rerun at 91a7ccf, after the runtime and card refactor, on local Docker (arm64): contract 58/58, chaos 29/29, gateway 9/9, and Claude Code 2/2 (Sonnet 5.5 and Opus 5.5, all six checks each, 9 and 8 computer_* calls).

Docs

The protocol README gains the SDK changes and a "Computer drivers" section. The website should get a "Computer drivers" page covering the same: what a driver is, the operations table, what the base class serves, errors and settings.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The latest changes appear safe to merge.

Summary

Adds the computer driver SDK, structured errors, serving hooks, and tool-access support.

  • Computer drivers provide screen operations that agents can call through shared tools.
  • Computer drivers expose display, keyboard, and session-command settings through framework endpoints.
  • Computer drivers can transfer files to and from the session user's home.
  • Extension callers get named methods and structured details when an operation fails.
  • Environments can choose their served tools and update tool details after mounting.
  • Environments can filter and restrict tools by caller role without a gateway.
  • An X11 example driver connects the computer SDK to a virtual desktop.

Reviews (12) · Last reviewed commit: "fix(protocol): find every keycode a comp..." · Reviewed by Greptile

earakely-scale and others added 6 commits October 26, 2026 11:51
AgentEnvError(code, message, retryable, applied, hint, status). Raised in an
extension it answers with its status and the fields in the error body; raised
in a tool, on mcp's FastMCP and on fastmcp alike, it answers isError with
"<code>: <message>" as text and the fields as structured content. Any other
extension failure stays extension_failed / 500, now with applied "unknown".
invoke_extension raises ExtensionCallError, an httpx.HTTPStatusError carrying
the parsed fields, so callers that catch HTTPStatusError keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@extension(uri, op=...) makes a handler one method of an extension: handlers
sharing the URI merge into one card entry, each method under methods.<op> with
its own verb and endpoint, by default /agentenv/ext/<extension>/<op>. The
environment's extension_params(uri) adds extension-level params beside the
methods, and declared_extensions() advertises declaration-only extensions.
Two handlers for one method, a handler without op on a URI another uses, and
methods disagreeing on description or required are refused at mount. Without
op, routes and cards are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d_tools

@tool takes annotations (MCP ToolAnnotations fields), structured_output and
meta, passed through to mcp's FastMCP and to fastmcp, with the hidden-by-default
marker merged into meta. A tool returning Annotated[CallToolResult, Model] is
sent as is on either FastMCP and listed with Model's schema. served_tools(tools)
lets an environment choose at mount which @tool methods it serves, and with
which descriptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An environment with tool_access = True filters tools/list and refuses
tools/call per caller role (AgentEnv-Role, or a gateway-stamped _meta role)
on both FastMCPs, and serves disable-tool/v1 and enable-tool/v1 at the
gateway's paths and bodies, so modify_env_tool_access works on an env with no
gateway in front. agentenv_protocol.tool_access keeps the gateway's rule
semantics, hidden-by-default tools included; a refused call answers
permission_denied. The gateway keeps its own code, since its image installs no
protocol, and tst/unit/env/gateway/tool_access_parity_test.py runs the same
rule cases against both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lass

agentenv_protocol.computer: a driver subclasses ComputerDriverEnvironment and
marks the methods that capture and send input (@display, @capture, @click,
@drAg, @scroll, @type_text, @press; the pointer and keyboard groups;
@set_display; @run_in_session and @write_files, with Linux defaults). The class
is checked when defined: every required operation, whole groups, one method per
operation per platform, and features that match the decorators. Predicates
(os, distribution, version, arch, display) choose a method per platform.

The base class serves the contract from those operations:
- the seven computer_* tools and the pointer and keyboard group tools, with
  structured results, a one-line summary as text, output schemas and the
  errors table's codes and applied;
- frames: f<n> ids, native region crops that map back exactly, the last 64
  kept, stale when the display geometry changes, each caller's latest;
- frame and norm coordinate spaces, refused rather than clamped outside the
  frame or range, with the default written into the tool descriptions;
- the input queue (one call at a time, input_busy past the wait) and
  others_since_frame;
- computer-ready (real checks; tools refuse device_unavailable until they pass
  once), computer-display, computer-spaces, computer-keyboard, computer-exec
  (idempotent by request_id, output capped, timed out, a background app's open
  pipes drained rather than waited on);
- data/add into the session user's home from inline bytes, https URLs and read
  grants, written atomically with path checks; data/get of the data paths,
  inline or under a write_namespace grant;
- the generated card, and per-role tool access.

Settings come from AGENTENV_COMPUTER_SETTINGS. The new computer extra installs
mcp, uvicorn and Pillow. Tests drive the whole base class through an in-memory
screen in tests/computer_fake.py, which doesn't ship.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… computer drivers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale force-pushed the edgararakelyan/fd-4380-m00-protocol-sdk-prerequisites-for-the-driver-base-class branch from eb167be to 4318955 Compare October 26, 2026 18:51
@earakely-scale
earakely-scale marked this pull request as ready for review October 26, 2026 18:52
@earakely-scale
earakely-scale requested a review from a team as a code owner October 26, 2026 18:52
@socket-security

socket-security Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/queue.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/driver.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/tools.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/driver.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/driver.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/extensions.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/agent_env_environment.py Outdated
earakely-scale and others added 2 commits October 26, 2026 12:08
…'s way

Found end to end: an X11 driver's own `_button` helper replaced the base
class's `_button`, so every click failed with a TypeError. The base class's
private members are now all `_computer_*`, and a driver that redefines one of
them, another private member of the base class, or a computer_* tool is
refused when it is defined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eing

From review:
- a caller cancelled while waiting for the input queue no longer leaves its
  lock acquisition behind to take the queue and never give it back;
- a screenshot that fails after an input was delivered reports the input as
  done (applied yes, no frame) rather than an error that invites a retry, and
  held keys are recorded as soon as they are pressed;
- the automatic key release keeps waiting while other input keeps the queue
  busy;
- a point is checked against its frame again once the call has its turn, and a
  display change takes a turn too, so a queued click can't land on the old
  geometry;
- a running command is joined by its request_id however many others finish
  meanwhile; only finished results are evicted;
- structured_output=False lists no schema for a CallToolResult tool on fastmcp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/linux.py Outdated
earakely-scale and others added 3 commits October 26, 2026 12:26
Found end to end: 64 raw 1280×800 frames held about 230 MB in the driver, and
the bench's 1920×1080 would hold over 400 MB. A frame now keeps the PNG it is
encoded to once at capture, the one every screenshot answers with anyway, and
decodes it only to crop or to answer in JPEG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale

earakely-scale commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator Author

End-to-end and chaos results at 1c71b35

The only later commit, 659293f, realigns lines in tools.py.

The SDK was exercised as a driver author would use it. An X11 driver built on ComputerDriverEnvironment (mss capture, XTEST input through python-xlib, about 250 lines) runs in an Ubuntu 24.04 image with Xvfb and a Tk fixture app, the Fixture Desk. The fixture records everything that reaches it, so every effect is read back from the app's own state, not inferred from the driver's answers. The protocol came from a wheel built from this branch. The driver and harness are e2e scaffolding and aren't in this PR; M0.3's reference driver replaces them.

Contract on a real X server (EC2 c6i.4xlarge, agent-environments-admin): 58/58

  • Every tool, against the fixture:
    • Unicode typing through the keymap (Hello, Wörld! 東京), chords and repeats;
    • clicks in frame and norm space, right and double clicks;
    • native crops whose points map back exactly;
    • scroll then select, a drag through a waypoint, the pointer group, Shift held across typed text;
    • screenshot after acting, and wait.
  • Refusals leave the fixture unchanged: out of bounds, an unknown frame, a bad key with its hint, control characters, a count over the limit.
  • 16 concurrent clicks all land.
  • The extensions: spaces (descriptions rewritten), keyboard, display, exec (session environment, timeout, 1 MiB cap, a background GUI app, idempotent request_id, missing binary), data/add picked up by the app, data/get of the data paths, and per-role tool access with no gateway.

Through agent-env and the existing gateway (agent-env run of a bundle; deploy_env, modify_env_tool_access, env_outcome_verifier): 9/9

  • The gateway proxies image blocks, structured content and error fields.
  • The driver's extensions and data/add go through its /svc/desktop/... REST proxy.
  • The modify_env_tool_access step hides a tool.

A real agent (deploy_agent claude-code, prompt_agent, env_outcome_verifier): 4/4 runs passed

  • The task: type a name, tick Subscribe, scroll to and select Item 37 in a 100-item list, click a grid cell, and Save. Six checks are read from the fixture's state, including that the name arrived as real key events.
  • Claude Code used only computer_* tools, 8–9 calls a run, with screenshot: true on its inputs to check each step without a separate screenshot call:
    • three runs on anthropic/claude-sonnet-5-5, 43–51 s each;
    • one run on anthropic/claude-opus-5-5, 47 s.

Chaos (EC2): 29/29

  • X server killed under load: calls answer device_unavailable, none hangs (slowest 0.09 s). The first full success comes 0.57 s after the kill, computer-ready flips to not ready and back, and the driver stays up.
  • Driver container restarted: old frame ids are refused stale_frame, and it serves again from f1.
  • Flood: 154 mixed calls over three sessions in 3.1 s with no errors and all 64 clicks landed. 20 concurrent typed words never interleave. With a 0.3 s queue wait, 7 of 8 one-second drags are refused input_busy (retryable, applied no), and none is half delivered.
  • Fuzz: 1,000 random malformed tool calls and garbage REST bodies. No transport failure, every error carries a known code, and the driver is still ready.
  • Network with netem 150 ms ± 50 ms delay and 3% loss: all calls succeed, and a 2 MB response is capped correctly.
  • Data plane: 30 concurrent data/add writes to one path leave exactly one payload whole, with no temp files. A 25 MiB inline file takes 0.8 s. ../, absolute and symlink escapes are refused.
  • Exec:
    • a timed-out process tree is killed;
    • 50 concurrent commands run;
    • 10 concurrent duplicates of one request_id run once;
    • 100 MB of output is capped in 0.1 s;
    • stdin is closed.
  • Memory: driver RSS goes from 67 MB to 80 MB after 500 frames, and stays there after 700.
  • Fixture killed mid-session: input is still delivered.

Fixed on this branch because of these runs

  • 441ac12: a driver's own _button helper silently replaced the base class's, so every click failed. Base-class internals are now _computer_*, and a driver that redefines one is refused when it is defined.
  • 9aee10c: frames held raw pixels, 300 MB after 500 frames at 1280×800 and over 400 MB at the bench's 1920×1080. They now keep their PNG (80 MB).

For M0.3, not this PR

  • The bundle loader finds an env's name only from @environment_card(name=), not @computer_driver(name=), so the e2e env needed environment_name in env.toml. The computer_driver env type should read it.
  • A driver running as PID 1 doesn't reap the orphans a timed-out command leaves (they're killed, but stay zombies). The reference image should run the driver under an init or as a session service.

earakely-scale and others added 2 commits October 26, 2026 13:28
…ne methods, and update a served tool

served_extensions(extensions) and served_operations(operations) choose at mount which @extension
handlers, extension methods and data-plane methods are served, beside served_tools; a hook naming
something with no handler is refused. update_tool(name, description=...) changes a served tool's
description after the mount, on the card and in tools/list, on mcp's FastMCP and on fastmcp. The
card route now serves the application's current card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…models and the card hooks

ComputerDriverEnvironment keeps its state in one ComputerRuntime (self._computer: operations,
frames, the input queue, readiness, held keys and command runs) instead of _computer_* attributes,
and the mixins declare what they read from the driver. The card's choices move to card.py and go
through served_tools, served_extensions and served_operations, so a missing operation's handlers
are no longer withdrawn by setting them to None. The definition-time checks move to checks.py.

Every tool result is built from its pydantic model, the same one that gives its output schema, so
unset fields are left out (parent_frame_id on a full screenshot, effect when there is none).
Setting the default space rewrites the point tools' descriptions through update_tool rather than
mcp's tool manager, and x and y stay optional on click and scroll everywhere: a computer without
computer-pointer/v1 refuses a call that leaves them out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/extensions.py Outdated
earakely-scale and others added 2 commits October 26, 2026 14:07
…default space changes

computer-spaces/v1 set chose the tools to update from the driver's operations, so a driver whose
served_tools left out a point tool answered 500, after the new space was already set. It now reads
the tools from mounted_card(), the card the mounted application serves, which the SDK adds beside
update_tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…un it

examples/computer_x11 is the driver the end-to-end runs used: mss for capture and XTEST through
python-xlib for input, the pointer and keyboard groups, an XTEST ready check, and a reconnect when
the X connection drops. Its X calls run off the event loop. The image runs it on Xvfb beside a Tk
practice app that records what reaches it, built from this checkout's SDK.
test_computer_x11_example runs the driver against fake mss and Xlib modules, so it keeps up with
the SDK without an X server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
earakely-scale and others added 2 commits October 26, 2026 15:01
…the keys it is given

- Input is no longer resent when the X connection drops during it, which could click or type twice; the
  connection is checked with a round trip before input is sent, so a dead X server still answers
  device_unavailable, and a drop mid-input answers delivery_failed (applied unknown).
- A drag that fails or is cancelled lets go of its button and modifiers.
- A character on a key's second level, such as A or !, is pressed with Shift, in computer_key and
  computer_keyboard_down as in typing.
- Only keys an action pressed are released, so a key held by computer_keyboard_down stays down through
  typing, clicks, scrolls and chords; computer_keyboard_up releases what each key's press pressed.
- A character the keymap lacks borrows a spare keycode, kept for that character and reused, or the least
  recently used one once all are taken, instead of using up a keycode per character; text that can't be
  typed at all is refused before any of it is sent.
- The click gap and drag spacing are named constants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
uv.lock is main's, locked again with the protocol's computer extra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/runtime.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/tools.py Outdated
Comment thread packages/agentenv-protocol/examples/computer_x11/driver.py Outdated
Comment thread packages/agentenv-protocol/src/agentenv_protocol/computer/runtime.py Outdated
earakely-scale and others added 2 commits October 26, 2026 15:29
…s the turn, and release timed holds

- A click or scroll at the pointer reads the pointer once its input turn comes, not before it waits, so
  one queued behind a drag acts where the drag left the pointer.
- A screenshot takes the input count before it captures, so input that lands while it runs counts as
  after the frame in others_since_frame.
- A timed computer_keyboard_down that is cancelled during its hold still releases its keys.
- The X11 example never maps a keycode a key holds down to another character, and finds every keycode a
  chord needs before pressing any, so a chord it can't hold whole is refused with nothing sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re pressing any in the X11 example

keys_down held its keys one at a time, so keys it couldn't hold all at once were refused after the first
were already down. It now plans them together through hold_each, which still returns the keycodes each key
pressed for computer_keyboard_up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale merged commit afc0693 into main Oct 27, 2026
14 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/fd-4380-m00-protocol-sdk-prerequisites-for-the-driver-base-class branch October 27, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant