Repository navigation
Conversation
`openadapt-agent serve --authoring --url <app> --headed` exited with "serve: Already running asyncio in this thread" before the MCP server started. Playwright's sync API was launched on the main thread before anyio.run, and it leaves its own event loop marked running on the thread that starts it. Even past startup, every MCP tool call ran on an anyio worker thread, and sync Playwright objects fail when called from a thread other than the one that created them. open_authoring_session now launches the --url browser, builds the Flow authoring session, serves every call, and closes the browser on one dedicated daemon thread. Callers get a ThreadOwnedSession that forwards attribute reads, method calls and attribute writes to that thread, so the mailbox Continue guard that swaps out type_text still reaches the real session. A session that fails to start now closes its browser instead of leaving it running. Tests cover the stdio serve path and the MCP server under anyio with a fake browser bound to its launch thread, the Continue guard through the owner thread, and (when Playwright Chromium is installed) a real headless browser through the MCP tools. Ledger: B076 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`openadapt-agent authoring connect '<runner-link>' --url <app>` without --headed, which is the command docs/MAILBOX_CLI.md showed, could not open its browser. The connect path swallowed that refusal, claimed the one-use runner link anyway, asked the person to Allow the chat, and then answered every command with a bare "error". The person needed a new runner link and had no idea why. The same happened with --headed when the browser extra was missing. connect_mailbox now refuses --url without --headed before it opens a browser or contacts the mailbox, and says to add --headed. Any other failure to open the --url browser is reported with its reason before the claim. Both messages say the runner link was not used. A mailbox client that has a --url but no open session now answers as coach-only instead of "error". The docs example now passes --headed, and a test checks every documented `authoring connect --url` command does. Ledger: B085 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When `serve --authoring --url` cannot pin a browser on Windows (no
--headed, or no browser extra), the session falls back to the coach-only
stand-in. The owner-thread change wraps that stand-in in a
ThreadOwnedSession, so AuthoringBridge's isinstance check no longer saw
it as coach-only. A `type` call then answered {"status": "ok",
"recorded": true} although nothing was typed or recorded.
AuthoringBridge now looks through the owner-thread wrapper when it
decides whether a session is coach-only, so `type` and `click` refuse
with COACH_ONLY again. The new test fails without this change.
Ledger: B076
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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 free
to 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.
openadapt-agent serve --authoring --url <app> --headedexited at startup with "serve: Already running asyncio in this thread", so the stdio web authoring path couldn't be used. The--urlbrowser now runs on its own thread, so the MCP server starts and every tool call reaches the browser.authoring connect --urlnow stops before it claims the one-use runner link when it can't open a browser you can sign in to. Before, it claimed the link and answered every command with "error".Fixes
serve --authoring --url <app> --headedexited with code 2 before the MCP server started. Playwright's sync API was started on the main thread, where it leaves an event loop marked as running, soanyio.runrefused to start. Tool calls also run on MCP worker threads, and sync Playwright objects fail when another thread calls them.--url,open_authoring_sessionlaunches the browser, builds the Flow authoring session, runs every call, and closes the browser on one daemon owner thread. It returns aThreadOwnedSessionthat forwards attribute reads, calls, and writes to that thread. The mailbox Continue guard that blockstype_textstill reaches the real session. If the session fails to start, its browser is closed.AuthoringBridgelooks inside the wrapper when it checks for the coach-only stand-in. A Windows--urlsession that falls back to coach-only still refusestypeandclickwithCOACH_ONLY.tests/test_authoring.py:test_url_session_keeps_playwright_on_one_owner_thread_under_anyio,test_authoring_url_headed_serve_starts_and_dispatches_tools,test_real_url_session_serves_tools_under_anyio(real Chromium; skipped when Playwright or Chromium is missing, never downloads),test_url_session_construction_failure_closes_the_browser,test_url_coach_only_fallback_still_refuses_type_behind_owner_thread,test_url_session_refusal_stops_the_owner_thread;tests/test_mailbox.py:test_continue_guard_reaches_a_browser_owner_thread_sessionauthoring connect '<runner-link>' --url <app>without--headedcouldn't open its browser, and that's the commanddocs/MAILBOX_CLI.mdshowed. The command claimed the one-use runner link anyway and asked the person to allow the chat. It then answered every command with a bare "error", so the person needed a new runner link and didn't know why. The same thing happened with--headedwhen the browser extra was missing.connect_mailboxrefuses--urlwithout--headedbefore it opens a browser or contacts the mailbox, and tells the person to add--headed. If the--urlbrowser fails to open for any other reason, the command reports why before it claims the link. Both messages say the runner link wasn't used. A mailbox client that has a--urlbut no session answers as coach-only instead of "error". The Playwright example indocs/MAILBOX_CLI.mdnow passes--headed.tests/test_mailbox.py:test_connect_url_without_headed_refuses_before_claim,test_connect_url_refuses_before_claim_when_the_browser_cannot_open,test_url_client_without_a_session_is_coach_only_not_error,test_documented_url_connect_commands_pass_headedNot fixed here
authoring connect --url --headedwithout the browser extra still claims the runner link. The session falls back to coach-only before the mailbox sees an error, so commands getCOACH_ONLYinstead of a bare "error".authoring connectdoesn't close its browser session when it exits. Without an output folder, it writes recordings toruns/authoringin the current directory. Neither behavior changed here.uv.locklocks openadapt-flow 1.34.0, which has noopenadapt_flow.authoringmodule, so--authoringdoesn't work in an environment built from the lock file. Installs with pip or uvx get a newer Flow that has the module.How it was tested
mainwith only the test files copied in. The B076 tests fail there with "Already running asyncio in this thread", or because a browser that failed to start was left open. The B085 tests fail becausemainclaims the runner link and doesn't raise an error, and because the docs example has no--headed. All of them pass on this branch.test_url_coach_only_fallback_still_refuses_type_behind_owner_threadfails without theAuthoringBridgechange. Without it,typereturns"recorded": trueeven though nothing was typed.test_url_session_refusal_stops_the_owner_threadandtest_continue_guard_reaches_a_browser_owner_thread_sessiondon't reproduce a bug. They check that the new owner thread stops after a refusal and that the Continue guard still applies.openadapt-agent serve --authoring --url http://127.0.0.1:<port>/ --headedcommand, with Chromium forced headless.observereturned the page's input and button, andstart_record,click, andstop_recordsucceeded. Onmain, the same command exits 2 with "serve: Already running asyncio in this thread". The real-browser test also checks thatobservereturns a non-empty tree.authoring connect --url --headedwith a real browser and a mock mailbox completed Allow,observe,start_record, andhalt.authoring connect '<runner-link>' --url <app>without--headedexits 2 with the new message and makes no network request.ruff check src tests scriptspasses.python -m build,scripts/check_release_artifacts.py,scripts/check_dist.py, andscripts/check_source_boundary.py --require-distpass.Before merging
tests/conftest.py,src/openadapt_agent/mcp.py,README.md, andllms.txt. The combined suite passes (336 passed, 5 skipped).🤖 Generated with Claude Code