Conversation
There was a problem hiding this comment.
Review from Claude Code
Verified hands-on: PR built into the backend image, agy 1.2.3 installed, adapter flags checked, unauthenticated call exercised, deny list inspected, full suite on the merged tree 2705 passed. The code is clean and the tests cover the contract. Two things to fix before merge:
-
The
:robind of the deny list rides an error path. agy rewritessettings.jsonon every start; over the read-only bind that fails (failed to write validated settings: atomic rename … device or resource busy) and it logsfailed to load cli settings, using defaults. Today the defaults still carry the deny list you shipped, but that is a closed binary's fallback behaviour — a version bump that really falls back to defaults would silently hand the agentrun_commandandwrite_fileback. Suggest copying the file into the volume from an entrypoint (or the adapter) before each call instead of binding it read-only, and/or asserting after startup that the loaded permissions contain the deny list (the log lineCLI settings initialized: permissions=&{… Deny:[command(*) …]}is greppable). -
A timeout keeps the transcript.
_run_cliraises on the 300 s wall clock before_agy_discardruns, so thebrain/<id>transcript holding the résumé survives exactly in the case where the call hung. Atry/finallyaround the discard (with the conversation id read from whatever stdout arrived) closes it.
Smaller, not blocking: the cli log under log/ grows ~16 KB per call and conversation_summaries.db is not in the discard list, so the volume grows without bound; and spike/antigravity/README.md is Ukrainian in an English repo — happy to keep the spike, but an English README or dropping the directory would be better.
Verified for the record: installer resolves to /root/.local/bin/agy 1.2.3 (+270 MB image); --input-format/--output-format stream-json, --print-timeout, --model, -p= all accepted; unauthenticated result event is status: ERROR, error: "authentication failed or timed out" in 0.6 s and matches your auth regex; the PR's "1037 passed" is a partial run, the merged tree gives 2705.
…oogle subscription works from Docker The provider question was one blocker and six unknowns, and none of them could be settled by reading the docs. This is the rig that settled them. It touches no application file. Two services share one Dockerfile: `plain` has no keyring, `keyring` carries D-Bus and gnome-keyring. probe.sh runs eight probes and prints one line each; models.sh emits the live catalog as a ready block for llm_models_list. What it found: - The CLI skips the OS keyring when no D-Bus session bus is present and writes an OAuth token to a file instead, so one volume is enough. The keyring variant of the image is unnecessary. - `-p` reads argv and swallows the next token. A prompt of 63 KB survives argv, but the stdin route is one NDJSON line and has no such ceiling. - `agy models` needs a login, and every slug carries its reasoning effort. - `--sandbox` restricts the terminal only. The agent still reads files unasked; a deny list in settings.json is the lever that stops it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ure, and the agent stays inside plain text Scoring, tailoring, letters, autofill and email can now run on a Google Antigravity subscription through `agy`, beside the Claude Code and Codex CLI providers. Sign in once with `docker compose exec -it backend agy`; the OAuth token lands in a file under the antigravity_auth volume, because the CLI skips the OS keyring when no D-Bus session bus is present. Cost counts as $0 and token usage is still logged. Three decisions worth knowing: The prompt travels as one NDJSON line on stdin. `-p` reads argv and argv caps near 128 KB, which a resume plus a job description can reach. `-p` also swallows the next token, so it goes last as `-p=`. A deny list holds the agent to text. `agy` carries 57 tools, among them run_command and write_file, and it reads files without asking. Its `--sandbox` flag restricts the terminal only, never the file tools. The image ships backend/antigravity-settings.json, and compose binds the same file read-only over the volume: the volume would otherwise hide the image's copy, and a stale deny list is a silent hole. The per-call transcript is deleted. agy writes the whole prompt to brain/<id>/transcript_full.jsonl, which means a copy of the user's resume on disk that nothing ever reads back. Errors map onto the existing contract: a missing login or a plan limit raises NonRetryableLLMError, so call_llm goes straight to the fallback instead of retrying four times. No login pre-check is needed - unlike codex, agy answers an unauthenticated call in about a second. Verified against a live subscription from inside the backend container: a plain call answers, a file read is refused by the deny rule, and the transcript count does not grow. 1037 backend tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…h, and a timed-out call still drops the transcript Both blocking points from the review, plus the two smaller ones. The read-only bind is gone. agy rewrites settings.json as it starts, so a read-only copy there made that write fail and put the CLI on the error path that logs "failed to load cli settings, using defaults". The shipped copy now lives at /opt/antigravity-settings.json, outside the volume, and the adapter installs it into the state directory before each call, where agy is free to rewrite it. Verified: the two error lines are gone from the log. That fallback is still a closed binary deciding what its defaults are, so the adapter no longer takes its word for it. After each call it reads "CLI settings initialized: permissions=..." from agy's own log and refuses the call unless every shipped deny rule is in that line. A missing log line, a missing rule or a missing settings file all raise NonRetryableLLMError: an unconstrained agent is not something to fall back to quietly. A timeout no longer keeps the transcript. _run_cli shields the read task, so after the kill it still returns what the process printed; the adapter takes the conversation id from the init event and discards in a finally. The hung call was the one case where the resume stayed on disk, and it is the case that matters. The per-call log goes to the temp working directory via --log-file. That is the ~16 KB per call the review measured, and the same file is what the permissions check reads, so it earns its place twice. The spike README is English now. Verified live from inside the backend container: a plain call answers, a file read is refused by the deny rule, and neither the log directory nor the conversation directory in the volume grows. Full suite: 2775 passed. (test_r4_live_contract.py::test_protected_routes_require_a_key fails on this machine on main as well; it wants an API key on the running instance.) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6af8a6c to
6da58a3
Compare
|
Rebased onto main and pushed. Both blocking points are fixed, and the two smaller ones with them. 1. The read-only bind is gone. You were right about the mechanism: agy rewrites I took the assertion as well, since a closed binary deciding its own defaults is not something to rely on. After each call the adapter reads that line out of agy's log and refuses the call unless every shipped deny rule appears in it. A missing line, a missing rule, or a missing settings file all raise 2. The timeout keeps nothing now. 3. The log. 4. The spike README is English now. Verified live from inside the backend container: a plain call answers, a file read is refused by the deny rule, and neither On the test count — you are right, mine was a One number worth putting on the record, since it is a cost you inherit: every call carries about 13 200 input tokens of agy's own preamble, its 57 tool definitions. Free in money, not free in subscription quota, and I found no lever to shrink the tool set. 🤖 Generated with Claude Code |
|
Generated by Claude Code Thanks, all four points check out. I built the PR's backend image and ran the adapter against agy 1.2.3: the deny list installs to the state dir, One last ask before merge: please drop The 13k-token preamble is good to know; I'll note it in the changelog line so people on a quota are not surprised. |
Adds Antigravity CLI as a fifth LLM provider, beside Claude API, Claude Code, Codex CLI, OpenAI, OpenRouter and Ollama. A Google Antigravity subscription now drives scoring, tailoring, cover letters, autofill and email classification. Cost counts as $0; token usage is still logged.
One extra: the catalog this subscription grants is not only Google.
agy modelsserves Gemini,claude-sonnet-4-6,claude-opus-4-6-thinkingandgpt-oss-120b-mediumthrough the same login.Setup
docker compose exec -it backend agyThe container has no browser, so the CLI prints a URL and asks for the code the browser shows. Then pick Antigravity CLI (Google Subscription) in Settings › AI. No API key.
Three decisions worth reviewing
The prompt travels on stdin, not argv.
-preads argv, and argv caps near 128 KB — a résumé plus a job description can reach that. The adapter sends one NDJSON line to--input-format stream-json.-palso swallows the next token, so it goes last as-p=.A deny list holds the agent to text.
agycarries 57 tools, among themrun_commandandwrite_file, and it reads files without asking. Its--sandboxflag restricts the terminal only, never the file tools. The image shipsbackend/antigravity-settings.json, and compose binds the same file read-only over the volume — the volume would otherwise hide the image's copy, and a stale deny list is a silent hole.The per-call transcript is deleted.
agywrites the whole prompt tobrain/<id>/transcript_full.jsonl. That is a copy of the user's résumé on disk that nothing ever reads back.Auth
The CLI skips the OS keyring when no D-Bus session bus is present and writes an OAuth token to a file instead. So the container needs one volume, exactly like
codex_auth, and no keyring daemon. No login pre-check is needed either: unlikecodex,agyanswers an unauthenticated call in about a second, with a readableerrorfield. A missing login or a plan limit raisesNonRetryableLLMError, socall_llmgoes to the fallback instead of retrying four times.Known cost
Every call carries about 13 200 input tokens of the agent's own preamble — its 57 tool definitions. That is free in money and not free in subscription quota. A CLI agent is a heavyweight way to run one completion, and I found no lever to shrink the tool set.
The spike
The first commit adds
spike/antigravity/— the isolated rig that answered these questions. It touches no application file and can be dropped if you would rather not carry it.probe.shruns eight probes;models.shprints the live catalog as a ready block forllm_models_list.Verification
Against a live subscription, from inside the backend container:
Backend suite: 1037 passed. (
test_r4_live_contract.py::test_protected_routes_require_a_keyfails on my machine before and after this branch — it needs an API key configured on the running instance.)🤖 Generated with Claude Code