Commit Graph

93 Commits

Author SHA1 Message Date
can1357 e7009452b4 feat(coding-agent/tools): simplified browser screenshot persistence and return paths
- Remove the per-call `save` option from `tab.screenshot()` to simplify usage.
- Update `tab.screenshot()` to return the saved file path as a promise string.
- Configure screenshot persistence to use daemon path or custom `browser.screenshotDir`.
- Add comprehensive tests verifying temp path return and custom directory saving.
2026-07-28 06:40:31 +02:00
can1357 ff49b986d5 feat(coding-agent/tools): introduced language-specific code formatters for display rendering
- Added language-specific code formatters for JavaScript, Julia, Python, and Ruby to improve display rendering.
- Integrated display formatting into browser run and eval render tools while preserving verbatim execution.
- Added comprehensive test suites verifying formatting stability, lexical safety, and streaming behavior.
2026-07-24 16:26:03 +02:00
can1357 1e1d2e91ea style: applied biome formatting 2026-07-24 02:27:35 +02:00
can1357 3d64910bb0 feat(coding-agent/live): added realtime voice interaction with Codex Live sessions (experimental)
- Added `src/live/` subsystem with WebRTC transport, protocol parser, session controller, and headless Chromium audio injection.
- Added `LiveVisualizer` component with phase states, transcript display, and animated waveform rendering.
- Added `/live` slash command and `LiveCommandController` to toggle live voice sessions.
- Suppressed local TTS via `vocalizer.suspend()` during live mode to prevent audio conflicts.
- Added live-instructions and agent-final-message prompts for agent messaging context.
- Added `protocol.test.ts` covering event parsing, chunking, and context message construction.
2026-07-24 02:20:43 +02:00
roboomp d4b1fd5107 fix(browser): bound open timeout and lease browser across tab acquisition
The browser tool's open action only passed the requested timeout to acquireTab; acquireBrowser ran under the caller signal alone, so CDP discovery/connect could run through its own fixed 5s/30s waits past the requested deadline. A freshly-created browser also sat in the registry at refCount 0 during worker/surface acquisition: the worker-abort branch released it only on tempHold (never on the fresh refCount-0 case), orphaning the handle, and two different-name opens sharing one refCount-0 browser let a single failure dispose it out from under the survivor.

Compose one open deadline from the caller signal and params.timeout and thread it through both acquireBrowser and acquireTab; caller cancellation stays ToolAbortError, the requested timeout becomes a timeout ToolError. Hold one explicit registry lease across tab acquisition, released exactly once on the mutually-exclusive success/rollback paths, and make the worker-abort browser release mirror the error paths' refCount-0 check.

Fixes #6365
2026-07-23 19:02:38 +00:00
can1357 4617d71ba7 fix(browser): keep raw AbortSignal identity through run facade
Native combinators (AbortSignal.any, fetch) brand-check internal slots
that a Proxy cannot forward; return signals unwrapped from
bindBrowserRunFacade so tab.signal composes with native cancellation.
2026-07-18 21:04:16 +02:00
can1357 707932e786 Merge PR #5588: fix(browser): observe raw promises before target close (@serverinspector)
# Conflicts:
#	packages/coding-agent/src/tools/browser/tab-worker.ts
#	packages/coding-agent/test/tools/browser-tab-evaluate.test.ts
2026-07-18 21:04:16 +02:00
can1357 31f7aa6d58 Merge PR #5778: fix(browser): reject non-string tab selectors with a named error (@roboomp)
# Conflicts:
#	packages/coding-agent/src/prompts/tools/browser.md
#	packages/coding-agent/src/tools/browser/aria/aria-snapshot.ts
2026-07-18 20:13:44 +02:00
can1357 22b2a1d8f0 Merge PR #5789: fix(browser): support authenticated cmux TCP relays (@roboomp) 2026-07-18 20:12:47 +02:00
can1357 6c8b0f4a2f Merge PR #5906: fix(browser): bound scroll renderer acknowledgement wait (@roboomp) 2026-07-18 19:57:44 +02:00
can1357 b95a25820f Merge PR #6005: fix(browser): isolate request interception per run (@roboomp) 2026-07-18 19:42:36 +02:00
roboomp e0358b77a0 fix(browser): unregister fired once request handlers
- Removed the once wrapper from Puppeteer's request emitter on first fire so it cannot leak into later runs.
- Added regression coverage asserting a fired once handler leaves zero residual request listeners.

Fixes #6004
2026-07-18 16:31:10 +00:00
roboomp fe2fdf6564 fix(browser): isolated request interception per run
- Removed run-scoped Puppeteer request handlers and disabled interception on every browser.run exit path.
- Recycled workers when bounded interception cleanup cannot restore the tab, with raw CDP recovery for held requests.
- Added live Chromium coverage for held requests, normal traffic restoration, and thrown setup calls.

Fixes #6004
2026-07-18 16:22:40 +00:00
can1357 1a6fc09611 feat(tools): added bare ARIA ref support in browser actions, fixed select() double-count
- Added bare `eN`/`@eN` regex to `parseAriaRefSelector` so agents can copy refs straight from snapshot output.
- Applied ref resolution to `press`, `screenshot`, `drag`, `select`, and `uploadFile` action handlers.
- Fixed `#select` to assign the full option set first then read back, avoiding double-counting when unselecting mid-loop.
2026-07-18 17:51:54 +02:00
roboomp 4e216a3b98 fix(browser): canceled settled scroll timers
- Replaced the losing Bun.sleep with an unrefed timeout cleared in a finally block.
- Added regression coverage that verifies prompt wheel acknowledgements leave no timer behind.

Fixes #5905
2026-07-17 20:10:01 +00:00
roboomp b8ef46a0c8 fix(browser): bounded scroll acknowledgement wait
- Released tab.scroll after two seconds when a queued wheel event waits on a busy renderer acknowledgement.
- Preserved immediate dispatch failures and added regression coverage for both outcomes.

Fixes #5905
2026-07-17 20:02:53 +00:00
roboomp 940f19d8c4 fix(browser): supported authenticated cmux tcp relays
Dialed loopback CMUX_SOCKET_PATH endpoints over TCP and completed the cmux relay HMAC challenge before sending JSON-RPC requests.

Loaded relay credentials from the session environment or the per-port cmux auth file while preserving Unix socket behavior.

Fixes #5788
2026-07-17 03:42:01 +00:00
roboomp 7715132e71 fix(browser): guard cmux selector funnel against non-string selectors
CmuxTab.#selectorSpec bypassed the puppeteer-backend guards and called
normalized.startsWith(...) directly, so tab.click(await tab.ref("e5")) on a
cmux surface still threw the opaque TypeError instead of the named ToolError.
Apply assertSelectorString at the cmux funnel too.

Fixes #5776
2026-07-17 02:14:58 +00:00
roboomp 9f836712e8 fix(browser): rejected non-string tab selectors with a named error
tab.click/type/fill/waitFor*/scrollIntoView route their selector through
parseAriaRefSelector (.trim) and normalizeSelector (.startsWith) before any
validation, so passing the ElementHandle from tab.id(n)/tab.ref(...) (or an
un-awaited Promise of one) crashed with the opaque minified
"A.trim is not a function" instead of an actionable error.

- Added assertSelectorString guard at both selector funnels; throws a ToolError
  naming the recovery ((await tab.id(n)).click() or a string selector) and
  distinguishing ElementHandle / Promise / primitive.
- Corrected browser.md: handles are called directly, not fed to tab.click.
- Regression tests in both selector suites.

Fixes #5776
2026-07-17 02:10:55 +00:00
can1357 7363684cb6 merged PR #5453: fix(browser): bound tab teardown waits
# Conflicts:
#	packages/coding-agent/src/tools/browser/registry.ts
#	packages/coding-agent/src/tools/browser/tab-supervisor.ts
2026-07-16 03:43:17 +02:00
serverinspector d52084f536 fix(browser): observe raw promises before target close 2026-07-15 14:35:44 +00:00
can1357 6f8a3ab94a Merge PR #5458: fix(browser): bound headless browser close to unblock tab cleanup (@roboomp) 2026-07-14 22:58:47 +02:00
roboomp 33e87d5930 fix(browser): bound headless browser close to unblock tab cleanup
disposeBrowserHandle awaited Puppeteer's browser.close() for the headless
kind with no timeout. browser.close() resolves only once Chromium fully
exits, so a wedged process (a Windows failure mode) left releaseTab stuck
in the "Closing tab" phase forever.

Cap the close at 5s and force-kill the Chromium process tree on timeout so
the tool call always releases.

Fixes #5260
2026-07-14 17:48:18 +00:00
roboomp 7b399b32a2 fix(browser): bounded tab teardown waits
- Applied close deadlines to cmux surfaces, orphan targets, and browser handles.
- Surfaced the backend, tab name, and pending cleanup resource on timeout.
- Forced stuck headless browser processes down after Browser.close timed out.

Fixes #5259
2026-07-14 17:37:01 +00:00
can1357 3047c27c33 fix(test): skip real-browser tab.evaluate test when chromium cannot exec
- Exported ensureChromiumExecutable so the test can probe launchability.
- CI runner holds the downloaded Chrome but lacks libnspr4 & co., so the
  binary fails at dynamic-link time; probe --version and skipIf instead
  of failing the release run.
2026-07-14 02:12:21 +02:00
can1357 8c8afaf477 fix(browser): made tab evaluation use the main world 2026-07-13 00:05:43 +02:00
can1357 0d07da529a feat(coding-agent-tools): implemented browser execution safety controls
- Implemented cell budget clamping for timeouts to prevent stalled browser operations from exceeding execution limits.
- Added `recover` capabilities for tab workers to clear blocking dialogs and safely terminate hung navigations.
- Introduced `//!world=main` support for `tab.evaluate` via Puppeteer patch to allow execution in main execution contexts.
- Improved failure attribution for tab terminations by tracking dialogs, stalled operations, and specific termination reasons.
2026-07-13 00:05:43 +02:00
can1357 bd7d395222 feat(coding-agent/tools): added predicate polling support to wait()
- Enhanced `wait()` in browser tools to accept a predicate function in addition to milliseconds.
- Implemented automatic polling with configurable timeout and interval, resolving with the first truthy value.
- Throws a descriptive `ToolError` on timeout instead of waiting for the full execution deadline.
- Added comprehensive unit tests to verify polling behavior, cancellation, and error handling.
2026-07-13 00:05:42 +02:00
can1357 9ebc239280 feat(coding-agent/tools): added fail-fast watchdog for browser selector ops
- Implement a zero-match watchdog for browser selector operations that aborts after ~2s if no elements are found, preventing actions from unnecessarily consuming the full deadline.
- Lower the default interactive action operation ceiling from 15s to 8s.
- Update `waitFor` and `waitForSelector` to conditionally opt out of the fast-fail mechanism when explicit timeouts or hidden-state expectations are provided.
2026-07-11 07:33:20 +02:00
can1357 a9cdaf427a feat(coding-agent/tools): standardized browser tool execution flow
- Introduced `RunOutput` class to standardize buffering and sequencing of stream text, displays, and screenshots.
- Standardized element interaction via `ActionableHandle` and `fillViaHandle` to improve focus, clear, and typing reliability.
- Updated browser tool method signatures to return consistent actionable handles and integrated diagnostic hints for selector timeouts.
- Consolidated utility functions for safer serialization and cross-boundary value passing into the new output module.
2026-07-11 07:33:19 +02:00
can1357 d993b13c80 fix(coding-agent): strengthened browser interaction reliability and error transparency
- Implemented a try/catch envelope for browser evaluations to surface detailed page-side JS exceptions and detect unsupported Promise returns.
- Added transparent error reporting for cmux browser surface limitations regarding screenshot clipping and full-page captures.
- Forced tab activation before screenshot capture to prevent stale or sibling-tab image data in shared-endpoint environments.
2026-07-11 05:47:54 +02:00
can1357 70754dfa01 fix(coding-agent): hardened same-realm runtime guards from PR review
- setCwd now updates the saved __omp_session__ stack entry so a deferred
  cross-runtime setCwd is visible to the runtime's next run (review should-fix)
- JsRuntime installation asserts realm ownership before mutating globals;
  a first init during another runtime's live run fails via init-failed
  instead of clobbering the active run's globals
- cmux runCmuxCode marks the armed cancel rejection as handled so a sync
  setup throw under an already-aborted signal cannot become an unhandled
  rejection (review P2)
- credited #4907 in the changelog entry
2026-07-10 12:33:53 +02:00
ben 71c9cc0324 fix(coding-agent): keep same-realm setCwd from killing TUI sessions
When the JS eval worker falls back to the in-process inline path, concurrent
JsRuntime instances share one realm. setCwd used to throw on exclusive-owner
conflicts, and the microtask delivery path turned that into a fatal
unhandledRejection that postmortem exited on. Stamp local cwd without
stealing the active realm, report init failures over the worker protocol,
and cover process survival with in-process and child-process regressions.
2026-07-09 16:02:23 +08:00
qfrtt e787a90a7d Fix Edge browser launch 2026-07-07 15:09:58 -04:00
can1357 dd5c329bc3 fix(coding-agent): resolved browser teardown crashes by tracking expected abort errors
- Added `markHandled` helper to prevent unhandled promise rejections in fire-and-forget browser tasks.
- Integrated `postmortem.markExpectedCleanupError` to distinguish between expected teardown aborts and actual runtime failures.
- Updated `runCmuxCode` and `WorkerCore` to propagate abort reasons via `ToolAbortError` cause chains.
- Modified `tab-protocol` and supervisor logic to signal expected cleanup states during tab release.
- Added test coverage for `ToolAbortError` wrapping and cause preservation.
2026-07-06 08:07:24 +02:00
roboomp 0b7f4865a7 fix(tui): handled invalid path render args
Validated path-like renderer inputs before calling path helpers so provider-supplied arrays or objects cannot crash TUI rendering before schema validation reports the bad tool call.

Added renderer regression coverage for read, write, and edit call/result components with array and object path arguments.

Fixes #4525
2026-07-04 15:47:52 +00:00
roboomp a6a8258945 fix(browser): propagate cmux tab-close into the run body, not only the caller
Codex review of #4502 flagged that a bare `.catch(() => undefined)`
neutralizes the unhandledRejection but leaves the affected `runInTab`
call blocked inside `runCmuxCode` until timeout when the in-flight
code does not make another cmux socket request (e.g. `await
wait(60_000)`). `releaseTab` was signaling the run only by rejecting
an orphaned promise.

Wire the tab-close event all the way into the cmux run body:

- `PendingRun` gains a `closeAc: AbortController` that `releaseTab`
  aborts BEFORE calling `pending.reject`. `wait(...)` (via
  `waitForBrowserRun` -> `untilAborted`), in-flight cmux socket calls
  (via CmuxTab's `#request` -> `untilAborted`), and facade proxies
  (via `bindBrowserRunFacade`) all consume the composed signal, so
  the run body unwinds within a microtask instead of blocking to its
  own timeout.
- `runInTabWithSnapshot`'s cmux branch composes `closeAc.signal` into
  the run's signal (`AbortSignal.any([opts.signal, closeAc.signal])`)
  and now publishes `runCmuxCode(...)`'s outcome to the shared
  `promise` via `.then(resolve, reject)` and returns `await promise`.
  Both branches thus await the same promise, so `pending.reject`
  always has an attached handler (removing the original crash) AND
  the caller sees `Tab "..." was closed` immediately instead of
  waiting on the run's timeout.
- Drop the defensive `promise.catch(() => undefined)` — the promise
  is now actively consumed on both backends.

The new regression test adds a second case that exercises the
reviewer's exact scenario (`await wait(60_000);`) and asserts:
1. `pending.closeAc.signal.aborted` flips from `false` to `true`
   across `releaseTab`, with the tab-close error as its reason.
2. The awaited `runInTab(...)` rejects with `Tab "..." was closed`.
3. No `unhandledRejection` fires.

Verified locally by temporarily removing `closeAc.abort(...)` in
`releaseTab` — the new assertions fail; restoring it makes them pass.

Fixes #4499
2026-07-04 06:24:45 +00:00
roboomp 3622094e91 fix(browser): swallowed cmux tab-close rejection to prevent session crash
The cmux branch of `runInTabWithSnapshot` awaits `runCmuxCode(...)`
directly and never awaits/`.catch`es the `Promise.withResolvers()`
promise it stashes on `tab.pending`. When `releaseTab` walks pending
runs and calls `pending.reject(new ToolError("Tab ... was closed"))`
(a sibling subagent's `browser close --all`, session-scoped reap, etc.),
that orphaned promise had zero handlers and Bun surfaced the rejection
as `unhandledRejection`, which the CLI's top-level handler treats as
fatal — killing every other tab and subagent sharing the process, not
just the affected run.

Attach a no-op `.catch(() => undefined)` to the promise immediately
after creation. Inert for the worker branch (which still awaits the
same promise via `raceWithTimeout`, and attaching a second handler is
safe) and neutralizes the orphan on the cmux branch.

Adds a regression test that drives real `acquireBrowser` /
`acquireTab` / `runInTab` / `releaseTab` against a mocked
`CmuxSocketClient`, races `releaseTab` against an in-flight cmux run,
and asserts no `unhandledRejection` fires.

Fixes #4499
2026-07-04 06:06:18 +00:00
roboomp fb2804247a fix(tui): gated live tool spinner ticks
Added renderer metadata for pending and partial result paths that visibly consume spinner frames.

Stopped headerless bash pending previews from scheduling repaint ticks while preserving eval and shell header animation.
2026-07-01 18:34:12 +00:00
can1357 d562f28c1b Merge remote-tracking branch 'origin/farm/795471ef/cancel-timed-out-browser-run' 2026-07-01 04:46:41 +02:00
roboomp f7398a1aa8 fix(browser): bound cmux facades to runs
Wrapped cmux page, browser, and tab globals with per-run abort checks so stale continuations cannot reuse the long-lived CmuxTab after timeout.

Fixes #3964
2026-07-01 02:30:07 +00:00
roboomp 09625b1ca8 style: bun run fix 2026-07-01 02:21:41 +00:00
roboomp db8560f934 fix(browser): stopped stale timed-out runs
Aborted browser run helpers and recycled timed-out workers so losing JavaScript continuations cannot mutate a live tab after timeout.

Fixes #3964
2026-07-01 02:21:19 +00:00
roboomp 19b85bca70 fix(browser): reap Chromium/Puppeteer on aborted open and session dispose
Two termination boundaries in the browser tool leaked browser-owned OS resources into the long-lived coding-agent process.

1. Aborted 'open' published an orphan. #open wrapped acquisition in untilAborted, which rejects its outer wrapper on abort but lets the inner launch resolve in the background; acquireBrowser then unconditionally stored the resolved handle in the module-global browsers map. releaseAllTabs walks tabs, not browsers, so the refCount:0 handle stayed alive to process exit.

2. Session dispose had no browser teardown. Browser/tab state lives in module-global maps, and AgentSession.dispose() had no hook to walk them, so headless/spawned Chromium the session opened survived it.

acquireBrowser now short-circuits before launch on a pre-aborted signal and disposes the handle when the launch completes after abort. TabSession records the creating session's id (opts.ownerSessionId, threaded through BrowserTool.#open), preserved across reuse so a subagent re-driving an existing tab does not yank teardown responsibility. AgentSession.dispose() invokes releaseTabsForOwner bounded by withTimeout(3s), mirroring the async-job/MCP disposal pattern.

Regression tests exercise both boundaries via spied CmuxSocketClient (no real puppeteer/socket) and cover: pre-aborted open short-circuit, aborted-mid-launch cleanup, releaseTabsForOwner reaping only owned tabs, and reuse preserving original ownership.

Fixes #3963
2026-07-01 02:09:04 +00:00
can1357 1dd78b207e feat(coding-agent): removed unused eval helper functions
- Removed deprecated eval prelude helpers `append`, `tree`, `diff`, `sort`, `uniq`, and `counter` from all supported runtimes.
- Cleaned up runtime implementations, protocol definitions, and UI rendering logic associated with the removed helpers.
- Updated project documentation, prompts, and test suites to reflect the reduced helper API surface.
- Recorded functional changes in the package changelog.
2026-06-23 01:39:24 +02:00
can1357 1cb433cbde feat(coding-agent): added browser navigation helpers and improved timeout management
- Implemented `waitForSelector` and `waitForNavigation` methods in the browser tool API.
- Introduced per-operation fail-fast budget management with dynamic timeout clamping.
- Added validation for selector engines to reject unsupported Playwright-only platform features.
- Standardized error handling to provide descriptive, named timeouts for stalled browser operations.
2026-06-21 18:10:10 +02:00
can1357 e649017322 feat(coding-agent): added ARIA snapshot support for browser tools
- Implemented `tab.ariaSnapshot()` to capture and represent page structures as ARIA-tree YAML.
- Introduced `tab.ref()` and ref-based selector parsing to enable precise element interaction via unique ARIA identifiers.
- Integrated automated script bundling for cross-environment evaluation of ARIA snapshot logic.
- Updated browser action methods to resolve and target elements using ARIA-ref handles.
2026-06-21 06:39:14 +02:00
can1357 57cb7447e0 feat(coding-agent): enhanced browser stealth and automation capabilities
- Overhauled stealth spoofing mechanisms for WebGL, screen dimensions, Web workers, and iframe contexts using prototype-aware injection.
- Centralized function string representation patching to improve mimicry of native browser behavior across global objects.
- Patched puppeteer-core to remove detectable evaluation markers and implement lazy, pull-style execution context management.
- Enabled support for capturing LLM request JSON dumps and adjusted launcher flags to improve organic request patterns.
2026-06-21 04:14:26 +02:00
can1357 8427e69e1e fix(coding-agent): prevented puppeteer startup crashes
- Avoided value-imports of `puppeteer-core` in main startup files to prevent eager loading of the browser-data dependency graph.
- Used `import type` and literal string checks to defer `puppeteer-core` initialization until browser tools are actually invoked.
- Added a test to verify that `puppeteer-core` and `@puppeteer/browsers` remain off the eager startup import graph.
- Externalized additional heavy dependencies to reduce bundle size and improved minification settings in the build script.
2026-06-18 17:22:37 +02:00
can1357 eb67863e75 feat(coding-agent/tools): secured browser stealth scripts against detection
- Bound native Reflect methods to local variables in the browser launch script to prevent detection.
- Updated stealth injection scripts to use the bound Reflect methods instead of global Reflect calls.
- Fixed a type assertion issue in the AgentSession tool proxy.
2026-06-17 20:59:20 +02:00