Repository navigation
feat(antigravity): reuse one stream-json driver across successful turns (#535) - #618
Draft
Tericerice wants to merge 11 commits into
Draft
Tericerice wants to merge 11 commits into
Tericerice wants to merge 11 commits into
Conversation
…ns (BytePioneer-AI#535) Each turn.start previously spawned agy and every #completeTurn closed its stdin and waited for exit, so continuing a conversation repaid the full CLI, Language Server, and customization startup cost. The official headless protocol keeps one stream-json process and reads the next user message from the same stdin. - A successful Turn hands its process, Question bridge, and log to the Session; the next Turn rebinds the bridge to its own turn id, ownership check, and duplicate guard - Reused processes ignore the previous Turn's replayed result and steps, keyed on num_turns and step index, so history receives each Turn exactly once - A different model/effort/permission selection, a dead or unwritable process, or a Turn that failed, was cancelled, or closed the Session disposes the driver with bounded SIGTERM/SIGKILL escalation - Test fixtures now answer each stdin line like the real CLI instead of exiting after one Turn, and a new test asserts one PID across three Turns and a new one after model.select Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… made ambiguous (BytePioneer-AI#535) Co-Authored-By: Claude Code <noreply@anthropic.com>
…ePioneer-AI#535) - Claim the Session synchronously before any await, so two concurrent turn.start calls cannot both pass the busy check and overwrite #active - Stamp each Question request with a bridge generation and reject one that straddles a rebind, instead of attributing it to the Turn that now owns the bridge - Release a retained driver that exits between Turns; nothing else could reach its bridge or log once the close listener cleared #transport - Cancel a failed Turn's Subagents before awaiting them, so an observable-but-stuck Subagent cannot strand the driver and leave the Session busy - Filter a replayed result regardless of its native status: an ERROR the Host accepted as success keeps the driver, so its replay would fail the next Turn early Co-Authored-By: Claude Code <noreply@anthropic.com>
…ve driver (BytePioneer-AI#535) Pi's round-1 review confirmed the concurrency and attribution Blocker were already fixed on head, and raised three remaining issues: - Step replay filtering assumed agy keeps numbering steps across stdin messages, with no evidence and no test. It now recognises a replay by comparing each step line with the previous Turn's at the same index, and only while the Turn is still reading that contiguous replay prefix. This is correct whether agy continues or restarts numbering, and a new test replays the previous Turn then emits steps that reuse indices 1 and 2. - A retained driver keeps writing the Conversation database between Turns, so fork and rollback were copying a live SQLite file. Both now release an idle driver first. - Two doc statements described the old per-Turn process lifecycle, and the idle-unload proposal listed Antigravity as having no resident process; all three are corrected. Also: escalate to SIGKILL after the bounded wait instead of cancelling it, report a disposed/busy bridge as a non-retryable invalidState and drop the driver, and describe the real fallback behaviour in the driver-reuse comment. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ver exited (BytePioneer-AI#535) Codex's confirmation round found two gaps in the previous fix: - Replay detection only ended when a step line differed, so a new Turn whose own first step repeated the previous Turn's line verbatim was still dropped. Each replayed line is now consumed as it matches, so an identical new step is delivered once the replay is spent. The regression test runs four Turns and fails against the previous code. - releaseIdleDriver resolved after scheduling a SIGKILL, not after the process exited, so fork and rollback could still copy a database a live agy was writing. Disposal now reports whether exit was confirmed, and both entry points fail with sessionBusy instead of copying when it was not. - A disposed or busy bridge is now evaluated inside the setup try, so it reports a non-retryable invalidState and releases the driver rather than escaping as a retryable nativeFailure. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ePioneer-AI#535) Codex's round-4 review found that releasing an unresponsive driver cleared #transport before exit was confirmed. A first attempt therefore returned false, but an immediate retry saw no driver and returned true, letting fork or rollback copy a database a live agy was still writing. - Clear #transport only once the process exit is confirmed, in both the close listener and disposal, so repeated release attempts keep reporting the truth until the escalation actually kills it - Test: a driver that ignores stdin ending and SIGTERM keeps reporting false on every attempt, and the fork entry point refuses while it does Co-Authored-By: Claude Code <noreply@anthropic.com>
… timing (BytePioneer-AI#535) Co-Authored-By: Claude Code <noreply@anthropic.com>
…ts cwd (BytePioneer-AI#535) Windows keeps a directory locked while a process still has it as its working directory, and this fixture deliberately starts a driver that ignores stdin ending and SIGTERM. The escalation's SIGKILL does end it, so the test now records its pid and waits for it to disappear before cleaning up, instead of failing on EBUSY. Co-Authored-By: Claude Code <noreply@anthropic.com>
…succeeds (BytePioneer-AI#535) Windows keeps a directory locked while any process still uses it as its working directory, and this fixture starts a driver that ignores stdin ending and SIGTERM. The driver now ends itself after both bounded release waits have run, and cleanup waits for it with a bounded poll, so the test asserts the release behaviour without depending on temp-directory removal timing. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ePioneer-AI#535) The stuck-driver fixture assumed POSIX signal semantics: it ignored SIGTERM and expected a retry to be refused. Windows has no SIGTERM — kill() terminates the process — so the first attempt could legitimately succeed there, and the test failed on Windows and macOS. The test now asserts the invariant that actually matters and holds everywhere: release reports success only when the process is really gone. When it cannot confirm exit, a retry must keep telling the truth rather than report release because the first attempt already asked. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ePioneer-AI#535) The fixture needs a driver that survives termination so the release path cannot confirm exit. Windows has no signal to ignore, and its .cmd wrapper runs agy as a grandchild, so killing the wrapper leaves the grandchild holding the pipe. The adapter gap that reveals is worth a separate look; a test that cannot model it on that platform should say so rather than fail or assert something weaker. Co-Authored-By: Claude Code <noreply@anthropic.com>
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.
Summary
The Antigravity adapter spawned
agyfor everyturn.startand, on every#completeTurnincluding successes, closed its stdin, waited for exit, and signalled a lingering process. Continuing a conversation therefore repaid the CLI, Language Server, and customization startup cost on each Turn. The official headless protocol keeps onestream-jsonprocess and reads the next user message from the same stdin, which this PR adopts.Fixes #535
Behaviour
--conversationis not needed on that path, and--add-dir/--log-filekeep pointing at the same directory and log.rebindmoves the Turn identity, the owning-Turn check, the emit callback, and the duplicate-question guard to the new Turn, so a question from an earlier Turn can neither be attributed to the current Turn nor answered. A disposed or busy bridge refuses to rebind.num_turnsadvanced (or whose payload differs) and steps at or beyond the new Turn's first index reach the Host, so history records each Turn once.num_turnsthat did not advance, still fails the Turn; nothing is reported as success merely because text arrived.Tests
model.selectfollowed by a fourth Turn starts a new PID, with all four Turns present in history.Limits (unchanged from the issue)
🤖 Generated with Claude Code