Skip to content

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 05 Aug 15:17
· 386 commits to main since this release
  • Add outbound ACP vision delegation: a workflow harness performs the vision work over ACP and bobby stores no provider credentials. Bounded packet and result validation, isolated child-session lifecycle, image capability negotiation, and harness-advertised authentication. bobby vision connect --backend acp writes the config and bobby doctor covers it; direct providers are unchanged.
  • An isolated ACP vision harness that requests interactive permission fails closed β€” the request is cancelled and the child session closed, so it cannot produce an accepted result after asking for authority the parent session did not grant.
  • The optional session visionNode selector is exposed through the MCP session_create input/output schemas and the TypeScript SDK contract validator. MCP agents could configure ACP vision but session_create rejected the selector, leaving the configured route unreachable through that interface. The versioned Rust session contract is unchanged.
  • A rejected advertised ACP authentication is classified as AcpClientError::Authentication and fails before the isolated child session is created, instead of surfacing as transport loss.
  • Firefox executes vision-assisted intents: bounded accessibility snapshots become semantic intent candidates, and vision-selected coordinates execute through native BiDi pointer actions. Unsupported candidate scopes are rejected.
  • The Firefox companion popup pairs and re-pairs the profile through the native host's enrollProfile control path, so the credential never leaves the host. First-time enroll bootstraps the companion; a day-2 enroll reuses the live bobby serve descriptor. The install persists browser-selection.json and the enroll defaults, sharing one selection builder with the CLI. Adds the toolbar icons. The guide prefers popup Pair; bobby enroll-firefox-profile remains for CI.
  • bobby install --companion and make firefox upgrade a bobby-managed native host instead of failing with native-host installation destination already exists whenever the installed wrapper or manifest differed by bytes β€” JSON key order, or a different bobby path. Operator-owned files still refuse. Manifest keys serialize alphabetically so a repeat install is idempotent.
  • scripts/check-version-agreement.py covers packages/firefox-companion/manifest.json. It had drifted to 0.3.1, which is the version about:addons showed.
  • The Chromium worker never holds the pages mutex across browser I/O. The guard was held across every CDP round trip β€” navigate (up to 300s), inspect, click, humanized typing, screenshot, HAR dump β€” so one hung page call serialized every other page on the session, and close/terminate blocked on the same mutex, making a single stuck call unrecoverable. All 30 lookup sites clone the Arc-backed page handle under the lock and drop the guard before I/O; the remaining guard uses are map mutations.
  • The envelope deadline is enforced mid-flight, not only at admission. inspect, click, screenshot, and the cookie operations carry no timeout of their own, so a hung call stalled its command forever. Execution now races the deadline at the single dispatch site and fails retryable DeadlineExceeded; callers declare the budget via timeoutMs, clamped to the 300s ceiling. An aborted call may still execute browser-side, as with any timeout.
  • cookie_set and cookie_delete held the pages mutex across a recursive get_cookies acquisition of the same non-reentrant lock. Every cookie command hung its session permanently.
  • download_url is advertised, required, parsed, and threaded through the MCP gateway. The gateway always sent pageId: None while the executor requires one, so every call failed invalidRequest.
  • Firefox page_close returns Evidence::Page captured before teardown. Without it the executor recorded every successful close as a retryable failure, so agents retried a destructive operation.
  • Extraction and the Firefox JavaScript-result path truncate on a character boundary. Byte-index truncation panicked inside a multi-byte codepoint, so any non-ASCII page could kill the command.
  • Two check-then-act races are single-flighted: concurrent leases on one session launched two browsers against one profile directory (SingletonLock), and concurrent network_log calls spawned duplicate HAR collectors that split entries into a flaky verificationFailed.
  • Security: get_job and cancel_job checked the capability but never ownership, so any principal holding job:read could read another principal's job payload and result, and job:cancel could cancel their work. Jobs record their submitting owner (serde-compatible with pre-ownership journals; a None owner stays readable) and cross-principal access answers as absence.
  • Security: the cookie_get URL filter matched by substring, so example.com matched notexample.com and path=/ matched every URL β€” agents received other origins' cookies as correct results and re-injected them with cookie_set. Matching is now dot-boundary host suffix plus real path prefix.
  • Security: an idempotency permit dropped between reserve and finish β€” a cancel or panic mid-request β€” wedged its key until deadline for every retry, and enough wedges exhausted the principal's capacity. Permits abandon on Drop, disarmed by finish or explicit abandon.
  • Security: companion reconnect credentials are stored as a SHA-256 digest and compared in constant time. They were stored in plaintext and compared with an early-exit !=.
  • Security: a live MCP SSE stream re-evaluates its SubscribeEvents guard on every poll, so a token rotation pauses event delivery instead of the stream running on the capability set it opened with. Revoked or expired principals still close the channel.
  • Security: the SSRF deny path covers IPv4-compatible IPv6 (::127.0.0.1), the 6to4 and Teredo prefixes, IPv4 broadcast, and CGNAT 100.64.0.0/10.
  • Security: vision endpoint responses check Content-Length up front and enforce the size bound while reading chunked, instead of buffering the whole body before the check.
  • vision-proxy's validate_extract enforces a 64 KiB serialized bound. It was a no-op stub, so unbounded upstream extraction JSON reached the runtime unchecked.
  • Checkpoint files are written 0600 on Unix; they previously took the process umask.
  • authority.json syncs the file and its directory before the rename β€” flush() is userspace-only, so a crash could leave a torn file β€” and an unparsable file is quarantined to <path>.corrupt instead of failing boot with every token stranded.
  • A corrupt artifact-ownership record is quarantined instead of permanently rejecting all future registrations.
  • ArtifactStore construction sweeps crash-orphaned staging directories, which each leaked up to max_bytes for the life of the installation.
  • The session manager releases the worker before unregistering the session. The old order dropped the API handle first, so a failed release leaked the browser with nothing left to retry through; on failure the session now stays registered.
  • events_read no longer journals a receipt per poll into the 64-event ring, where its own traffic evicted real events and poisoned resume cursors.
  • Page.getFrameTree awaits the page lock instead of fabricating an about:blank frame tree from a try_lock fallback under contention.
  • The task scheduler re-checks terminal state when inserting an abort handle; a job that finished faster than the insert left the handle in place forever.
  • bobby doctor's 15s MCP handshake deadline applies to the read. The blocking read_line ignored it, so a mute gateway hung; the read runs on a thread with recv_timeout.
  • The checkpoint store prunes uncontended entries from its per-workflow lock map, which otherwise held one Arc<Mutex> per workflow id for the life of the process.
  • The page registry URL update after navigate (a stale page_list) and the expected-URL settle wait before click verification are logged instead of silently discarded.
  • The Firefox companion removes pending prompts when their context is destroyed, and serializes cookie names and values as JSON into the document.cookie statement β€” the hand-rolled escape let a ; or newline break out of or inject into it.
  • A bootstrap.env parse error reports the line number instead of the offending line, which is the bearer token.
  • The MCP stdio read loop shares the 64-deep in-flight bound the notification branch already had. A client pipelining thousands of tools/call frames without reading responses could exhaust memory and browser processes.
  • replace_session's timeout error states its contract: the timeout reports while the owned cleanup finishes in the background, and the swap lands exactly once afterwards.
  • The MCP gateway tests run against a real RuntimeService β€” journal, worker pool, recovery coordinator β€” over an evidence-producing fake worker, and assert terminal outcomes: intentKind, resolutionPath, candidates, verification evidence, and the resolved evidence inside a checkpoint read back through a real RecoveryCoordinator. The RuntimeService::default() fixture behind ~50 of them had no journal, workers, or recovery, so every dispatched command failed downstream and the assertions (assert_ne!(code, -32602), dispatch counts) passed under total downstream failure.
  • Twelve of the fourteen security release cases run without a browser. They prove auth, framing, quota, store, and lifecycle boundaries and never lease a worker, but ran only behind the installed-Chromium ignore gate, so CI proved none of them. The release matrix still runs all fourteen; canary leakage and principal isolation open real pages and stay Chrome-gated.
  • page-runtime pins that a typed value which never lands fails verificationFailed. The positive type_text test passed whether or not the post-type comparison ran, because the fake echoed the expected value after any write.
  • The default fingerprint profile and the behavioral benchmark are pinned to reviewed literals β€” UA, platform, locale, timezone, WebGL vendor and renderer, screen and client-hint fields, plus the seeded overall, category, and 19 per-dimension scores β€” in addition to matching generated output. Both previously compared against values the code under test produced, so regeneration blessed any drift.