v0.68.8
feder-cr/invisible_dotsv0.68.8Sep 18, 2026by github-actions[bot]
Upgrading?
AI Summary
This release fixes a critical bug where deleting the default conversation would crash the entire interface. The root cause was incorrect task ownership of anyio cancel scopes, where closing the connection from a request task incorrectly cancelled the server's serving loop.
Key Highlights
- Fixed crash when deleting the default conversation by implementing an 'OWNER' task pattern.
- Replaced manual anyio context manager calls with explicit task management to ensure cancel scopes are exited in the correct task.
- Removed internal context variables `_ctx` and `_sess_ctx` from the Link class.
- Added 6 new integration tests against the real server to verify correct task ownership behavior.
Full Release Notes
Deleting the default conversation from the sessions panel killed the whole
interface. Measured over plain HTTP, no browser, no page:
/sessions/forget on any other conversation -> 200, interface alive
/sessions/forget on the DEFAULT -> 200, process DEAD, exit 1
`Link.open` called `__aenter__` on `stdio_client` and on `ClientSession` by
hand, and `Link.close` called `__aexit__` on them - from whatever task happened
to be closing. Both are anyio context managers, so each owns a cancel scope,
and a cancel scope has to be exited in the task that ENTERED it. Exiting it
elsewhere delivers the cancellation to the scope enclosing the entering task.
`cli.serve` opens the default conversation and then runs uvicorn in the same
task, so closing that link from a request task cancelled `server.serve()`.
And the others were not fine, they were quiet. Their scopes belong to request
tasks that had already finished, so the same wrong exit raised
`RuntimeError: Attempted to exit cancel scope in a different task than it was
entered in`, which `close` was swallowing under a sentence written about
teardown failures. EVERY close was wrong; exactly one of them had something
alive to damage. Making only the default lazy would have removed the visible
half and kept the defect.
So the connection has an OWNER. One task opens it, publishes it, waits to be
told to stop, and closes it itself; `open` starts that task and waits until the
connection is usable, `close` asks it to let go and waits until it has. Closing
from another task is not guarded against - it is made impossible, because no
other task ever holds the contexts. `_ctx` and `_sess_ctx` are gone with it.
A failure BEFORE the connection is usable is now `open`'s to report rather than
to swallow: it is the difference between being told the server did not start
and a page waiting for one that never will.
Six tests against the real server over stdio, because the defect is about task
ownership of a real transport and a double cannot have it. Five known-bad
inputs, all killed: the whole previous implementation put back (three of the
six go red with the cancel-scope sentence verbatim), an owner that does not
wait, an open that cannot report a failure, a close that does not wait for the
owner, and a close that does not even ask it to stop.
Two of those five SURVIVED the first draft of the tests, and both survivals
were real holes rather than bad mutations. Nothing asserted that an open
connection ANSWERS, so a link that closed itself the instant after it was
published read exactly like a live one; and the failed-open arm accepted any
exception, including the `TimeoutError` that means it hung, which is the very
thing it exists to forbid. Both are fixed in the tests, not in the code.
`mcp/session.py` has the same SHAPE on `InvisiblePlaywright` - `__aenter__` in
start, `__aexit__` in close - and was checked rather than assumed: no file in
`invisible_playwright` imports anyio, so there is no cancel scope there and no
such failure to have. Written down because an audit that names only the broken
places does not say how much it looked at.
Verified on the real product: deleting the default conversation now leaves the
interface alive and `/sessions` answering 200.
Suite: 738 passed, 9 skipped. Lint, `invisible_core.english` and
`check_content.py` clean.
Related Repositories
The AI that really does things. Any OS. Any Platform. The lobster way. 🦞
Interactive roadmaps, guides and other educational content to help developers grow in their careers.
Reverse engineer anything with agents, from app behavior down to native binaries.
The open source coding agent.
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.