Skip to content

fix(server): agy Stop hooks run every turn in T3 and can continue the reply - #26

Closed
orbitingflea wants to merge 1 commit into
mainfrom
agy-stop-hook
Closed

orbitingflea wants to merge 1 commit into
mainfrom
agy-stop-hook

Conversation

@orbitingflea

@orbitingflea orbitingflea commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Problem

A Stop hook that works in the agy CLI does nothing in T3's Antigravity provider. Google's ACP server (agy_acp_server.par) registers hooks.json Stop hooks as a session-end hook, so they run once at teardown and ignore {"decision":"continue","reason":...}. The server also gives hooks different input from the CLI: terminationReason is UNSPECIFIED instead of NO_TOOL_CALL, and transcriptPath points to a SQLite database instead of JSONL. Google's latest release, 1.4.3, still has the same code.

Fix

  • Pin 1.4.3 (antigravityRelease.ts), with new archive hashes and sizes for all six platforms.
  • Patched copy for chat only (antigravityChatPatch.ts, AntigravityInstallation.chatExecutable). T3 copies the installed PAR to <managed>/patched/<releaseId>-r<rev>/ and patches four STORED entries at their original length: hooks.py, server.py, and their .pyc files, whose magic is broken so the sources load. It updates both CRC fields and publishes the copy with a rename. Each original entry is checked by sha256 first. On any mismatch, unknown version, non-Linux platform, or I/O error, T3 logs a warning and launches the original. Google's files are never changed.
  • Only orchestration chat uses the copy (AntigravityDriver.ts). Sign-in, model refresh, install validation, and text generation keep the original server.
  • What the patch does:
    • Stop hooks run at the end of every turn, and continue keeps the agent working inside the same session/prompt.
    • The hook input matches the CLI: NO_TOOL_CALL and the CLI's other reason names, plus a temporary JSONL transcript with the reply. After a continue, the transcript also holds the CLI's Stop hook blocked termination system message. Hooks written for the CLI, such as our agy.py adapter, work unchanged.
    • The continue reason reaches the client as an agent_thought_chunk tagged _meta["t3.stop_hook"], between the judged reply and the continued one.
    • A cancel kills a running hook command, so the cancel no longer waits up to the hook timeout.
  • T3 display (adapter.ts, AntigravityProtocol.ts, AntigravityAdapterV2.ts). An Antigravity-only flavor hook turns the tagged chunk into a system_notice item. The user sees the original reply, then a "Stop hook feedback: …" line, then the rewrite as a new assistant message, all in the same turn.

Verification

  • Unit tests (141 pass across 5 files):
    • The patcher on a synthetic ZIP: patched result, CRCs, size, untouched original, three fallback cases, cache reuse, unknown version.
    • The driver's chat and original selection.
    • The adapter's notice and segment split.
    • The marker extractor.
  • vp run check:changed passes.
  • Real run on 1.4.3 linux-x64, with the patched PAR built through chatExecutable and driven by a raw ACP client:
    • initialize reports 1.4.3 and 6 models;
    • a hook that asks to continue once gives HELLO-1, the tagged notice, PINEAPPLE-OK, then end_turn in a single prompt, and the second hook call allows;
    • agy.py's claude_payload() reads the reply and stop_hook_active correctly on both calls;
    • a cancel while a hook sleeps returns cancelled in about 0.1 s, and the next prompt works.

Not yet done:

  • Not run in the T3 UI, and no sandbox check yet (sandboxes mount the host runtime read-only).
  • linux-arm64 goes through the same byte checks but was not run.

Known limits

  • The patch uses private SDK attributes (_processor.step_queue, _client_cancelled). A new agy version fails the hash check and runs unpatched until the table is updated.
  • executionNum is the zero-based continue count. The CLI's numbering is undocumented (its docs example shows 1), and agy.py does not read it.
  • Old patched copies, about 0.93 GB each, stay until a full remove. Old versions/ directories already behave the same way.

Done with Claude Opus 5.5 in Claude Code (via T3 Code).

🤖 Generated with Claude Code

… reply

Google's agy ACP server runs hooks.json Stop hooks only at session end and
ignores `decision: "continue"`, so a Stop hook that works in the agy CLI does
nothing in T3. Its hook input also differs from the CLI (UNSPECIFIED instead of
NO_TOOL_CALL, a SQLite transcript instead of JSONL).

Pin the server to 1.4.3 and, for chat sessions only, launch a patched copy:
the Stop bridge becomes an SDK StopHook with CLI-shaped input, a continue
reason reaches the chat as a tagged notice before the continued reply, and a
cancel stops a running hook. Unknown versions or unexpected bytes fall back
to the original server with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 25, 2026 00:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL labels Oct 25, 2026
@orbitingflea

Copy link
Copy Markdown
Owner Author

Closed: the fix moves to a local patch script; T3 stays unchanged.

@orbitingflea
orbitingflea deleted the agy-stop-hook branch October 30, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants