Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bdb58dc
feat(harness): add the agy (Antigravity CLI) crewmate adapter
RooseveltAdvisors Sep 3, 2026
c80c067
no-mistakes(review): clear inherited agy marker, fix secondmate arg a…
RooseveltAdvisors Sep 3, 2026
0cfb622
no-mistakes(review): wire agy into herdr scrub, teardown, quota, disp…
RooseveltAdvisors Sep 4, 2026
fbf1062
no-mistakes(review): retire agy workspace trust at teardown
RooseveltAdvisors Sep 4, 2026
0a420ce
no-mistakes(review): withdraw agy trust when a spawn aborts
RooseveltAdvisors Sep 4, 2026
31dd653
no-mistakes(review): retire agy trust by record, fix registry mkdir race
RooseveltAdvisors Sep 4, 2026
4130b93
no-mistakes(review): withdraw agy trust before release and after roll…
RooseveltAdvisors Sep 4, 2026
70da456
no-mistakes(review): classify agy panes, withdraw both trust spellings
RooseveltAdvisors Sep 4, 2026
529b78e
no-mistakes(review): withdraw only agy trust this task registered
RooseveltAdvisors Sep 4, 2026
317dd4e
no-mistakes(review): record only agy trust the registration added
RooseveltAdvisors Sep 4, 2026
d841aab
no-mistakes(review): withdraw exactly the agy trust spelling named
RooseveltAdvisors Sep 4, 2026
aa4a618
no-mistakes(review): drop tooling creep, fix live guard, record agy s…
RooseveltAdvisors Sep 4, 2026
237de77
no-mistakes(review): name unwithdrawn agy trust, unwind live guard pr…
RooseveltAdvisors Sep 4, 2026
6311265
no-mistakes(review): scrub agy marker in sibling suites, fix stale la…
RooseveltAdvisors Sep 4, 2026
0c328c4
no-mistakes(document): document agy adapter facts in their owner docs
RooseveltAdvisors Sep 4, 2026
8adcfd8
no-mistakes(document): record agy's liveness drift-guard coverage bou…
RooseveltAdvisors Sep 4, 2026
c89f258
no-mistakes(lint): fix shellcheck findings in agy harness test
RooseveltAdvisors Sep 4, 2026
7e29491
Merge house main into the agy adapter branch
RooseveltAdvisors Sep 4, 2026
21e57ce
no-mistakes(document): Fix agy doc style violations: dashes and sente…
RooseveltAdvisors Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .agents/skills/harness-adapters/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: harness-adapters
description: >-
Agent-only reference for firstmate harness operations.
Use before spawning or recovering a crewmate or secondmate, handling a trust dialog, sending a harness-specific skill invocation, interrupting or exiting an agent, resuming an exited agent, or verifying a new harness adapter.
Contains verified facts for claude, codex, opencode, pi, pi-signed, grok, kimi, cursor, gemini, and muse.
Contains verified facts for claude, codex, opencode, pi, pi-signed, grok, kimi, cursor, gemini, muse, and agy.
user-invocable: false
metadata:
internal: true
Expand Down Expand Up @@ -52,7 +52,7 @@ A new adapter's verified marker and command name must land in `../../../bin/fm-h
Every emitted plan appends the selected or recorded harness reference after the named common references.
The `harness-adapter-routing-v1` object is the machine-readable and human-visible selection contract: choose the operation, choose the scenario within it, then append the selected harness reference.
`default` is the normal scenario when no narrower scenario applies.
Kimi establishes its unsupported primary boundary in its selected harness reference; Muse and Gemini follow Non-negotiable safety above.
Kimi and agy establish their unsupported primary boundary in their selected harness reference; Muse and Gemini follow Non-negotiable safety above.
A new tool remains undispatchable until the `verify` plan, its harness entry, every named owner, and the live checks land.

```json harness-adapter-routing-v1
Expand Down Expand Up @@ -90,7 +90,8 @@ A new tool remains undispatchable until the `verify` plan, its harness entry, ev
"kimi": "references/harness/kimi.md",
"cursor": "references/harness/cursor.md",
"gemini": "references/harness/gemini.md",
"muse": "references/harness/muse.md"
"muse": "references/harness/muse.md",
"agy": "references/harness/agy.md"
}
}
```
73 changes: 73 additions & 0 deletions .agents/skills/harness-adapters/references/harness/agy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# agy (Antigravity CLI)

Verified on 2026-09-03 with Antigravity CLI 1.1.24, which self-updated to 1.1.25 mid-investigation.
Describe agy by behavior rather than by version: it updates itself without being asked, and the hooks facility this adapter depends on is absent from `--help` and has changed recently in its own changelog.

## Operating facts

| Fact | Value |
|---|---|
| Binary | `agy` on `PATH`. |
| Launch | `agy --dangerously-skip-permissions [--model M] [--effort E] -i "<prompt>"`. `-i` starts an interactive session seeded with the prompt; `-p` runs one turn and exits. |
| Flag order | Go-style flags: `-i` and `-p` consume the NEXT argument, so the prompt must be last and every other flag must precede it. `agy -p --dangerously-skip-permissions "..."` takes the flag as its prompt and reports the real prompt ignored. |
| Models | `agy models` lists current ids; the Gemini family encodes an effort tier in the id itself (`gemini-3.8-flash-high`), and `--model` and `--effort` may both be passed. |
| Effort | `--effort low\|medium\|high`; agy names that set in its own refusal. `xhigh` and `max` are omitted rather than rejected at launch. |
| Busy state | No semantic source and deliberately unarmed; classifies `unknown missing`. Supervision rides the turn-end wake below. |
| Exit command | `/exit` (aliased `quit`). Two `Ctrl+D` presses also exit, the first showing `press ctrl+d again to exit`. |
| Interrupt | Single Escape. Renders `⎿ Interrupted · What should Antigravity CLI do instead?` and leaves the composer EMPTY, so no clear key is needed. |
| Resume | `agy --conversation=<id>`, printed on exit. Restores real model context, NOT the workspace: the banner shows the launching cwd, so resume must run from the original worktree. |
| Environment marker | `ANTIGRAVITY_CONVERSATION_ID`, exported to every tool subprocess, whose value equals the `conversationId` in the Stop payload. |
| Composer | Bare `>` prompt inside horizontal rules; no bordered box. `>` is a shell-prompt glyph outside a bordered container, so the composer verdict is always `unknown` - typed-submit confirmation therefore works on tmux (whose submit core resolves it through the busy footer) and reports unconfirmed on EVERY other backend including herdr, whose footer rescue is gated on `pending` - a narrower scope than cursor, which reads `pending` and is rescued on herdr. The brief rides the launch command, not `fm-send`. |
| Status bar | `? for shortcuts` when it will accept a prompt; `esc to cancel` when it will not. |

## Detection ordering

agy does NOT clear an inherited `CLAUDECODE`.
`CLAUDECODE=1 agy -p` and having the agent print its own environment returned both `CLAUDECODE=1` and `ANTIGRAVITY_CONVERSATION_ID`, so an agy worker launched from a claude primary carries both markers and whichever is tested first wins.
`../../../bin/fm-harness.sh` therefore tests `ANTIGRAVITY_CONVERSATION_ID` BEFORE `CLAUDECODE`, exactly as it does for cursor, and `../../../bin/fm-spawn.sh` also clears the foreign markers at the launch boundary.

## Workspace trust is the spawn-blocking hazard

`--dangerously-skip-permissions` governs TOOL permissions only and does NOT suppress the workspace-trust dialog.
Launching with that flag into a folder agy has never seen still renders `Do you trust the contents of this project?`, so every fresh task worktree hits it.
The dialog draws NO status-bar text, so a pane parked on it is indistinguishable from idle by any rendered signal - no spinner, no `esc to cancel`.
`../../../bin/fm-agy-trust.sh` therefore registers the worktree in `trustedWorkspaces` in `$HOME/.gemini/antigravity-cli/settings.json` before launch, and refuses rather than degrades.
Teardown withdraws the same entry with `--remove`, which runs no scope test because removal can only withdraw trust, and writes nothing when the path is already absent.
Its scope test is structural: only a linked git worktree of the named project is accepted, and a primary checkout, a foreign project's worktree, a worktree subdirectory, a plain directory, the home directory, and the settings directory are each refused.
Trust is not inherited by a nested repository - `/tmp/claude-1000` trusted did not cover the git repo at `/tmp/claude-1000/agylab` - so each task worktree needs its own entry.

## Crew turn-end hook

agy is outside the primary turn-end guard scope; it is a crewmate/scout adapter only and `../../../bin/fm-spawn.sh` refuses a `--secondmate` launch on it, because there is no agy primary supervision protocol.

`../../../bin/fm-agy-turnend-hook.sh` owns one `firstmate-turn-end` key in `$HOME/.gemini/config/hooks.json`, one silent always-zero hook script, and one private token registry under `$HOME/.gemini/antigravity-cli/fm-turn-end.d/`.
Every operator hook in that file is preserved.
Each agy worker worktree receives a gitignored `.fm-agy-turnend` pointer, and the global hook touches `state/<id>.turn-ended` only when the Stop payload's `workspacePaths`, the pointer, and the registry entry all agree.
Workspace-local `<worktree>/.agents/hooks.json` also loads, but only in interactive mode: print mode logs `loaded 0 named hooks from 0 hooks.json file(s)` for the same file.

**A Stop event is not on its own a finished turn.**
agy moves a shell command that outruns its own wait into the background, yields the composer, and fires Stop with `fullyIdle` false while that command still runs; a second Stop with `fullyIdle` true follows once it finishes and the agent reports it.
The installed hook fires only on `fullyIdle` true, and any future busy-state writer must apply the same gate.

## Where the turn-end signal is silent

Two paths end a turn with NO Stop event, so a worker on either goes idle and quiet and the watcher's staleness check is the only backstop.
Do not read a silent pane as a healthy one.

- **A DECLINED tool call.** Choosing `4. No` at a permission prompt returns the pane to idle with `⎿ User declined the tool call` and fires nothing.
Reproduced twice, with a same-session plain turn firing Stop normally as a positive control.
Launching with `--dangerously-skip-permissions` is what keeps a crewmate off this path.
- **An Escape interrupt.** Cancelling a turn fires nothing.
Firstmate initiates its own interrupts, so it already knows, but a captain interrupting a pane by hand leaves no wake.

Stop correctly does NOT fire while parked at a permission prompt, which is the safe direction: there is no false "done".

`terminationReason` values beyond `NO_TOOL_CALL` are UNVERIFIED.
The payload documents `model_stop`, `max_steps_exceeded` and `error`, but every observation here returned `NO_TOOL_CALL`; forcing an error, a step-limit, and a context-limit stop would settle whether Stop fires on those paths at all.

## Rendered states

`? for shortcuts` means idle.
`esc to cancel` means busy, parked at a tool-permission prompt, or holding an open slash-command menu - the status bar alone does not separate them, so look for `Requesting permission for:` / `Do you want to proceed?` in the body.
A trailing `· N task(s) · /tasks` on an otherwise idle bar means background work is still running.
`../../../bin/fm-composer-lib.sh` matches `esc to cancel` as a DELIVERY guard only; `../../../bin/fm-busy-lib.sh` owns why it is never a recorded worker state and what a semantic `PreInvocation`/`Stop` pair would take.
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ state/ runtime records and signals; gitignored
<id>.turn-ended touched by turn-end hooks
<id>.grok-turnend-token firstmate-owned grok hook registry token for the task; removed by teardown
<id>.kimi-turnend-token firstmate-owned Kimi hook registry token for the task; removed by teardown
<id>.agy-turnend-token firstmate-owned agy hook registry token for the task; removed by teardown
<id>.agy-trust the agy workspace-trust paths this task's spawn actually added to the operator's store; teardown withdraws exactly these and nothing else
<id>.gemini-settings.json firstmate-owned per-task Gemini settings carrying the busy-state and turn-end hooks, reached through GEMINI_CLI_SYSTEM_SETTINGS_PATH so nothing is written into the project's own .gemini/; removed by teardown
<id>.muse-session muse busy-source binding (sessions root plus task worktree) written by fm-spawn; removed by teardown
<id>.cursor-session cursor busy-source binding (projects root, task worktree, prior conversations) written by fm-spawn; removed by teardown
Expand Down Expand Up @@ -199,7 +201,7 @@ A silent bootstrap section needs no action; for any printed actionable diagnosti
## 4. Harness and runtime dispatch

Load `harness-adapters` before every spawn or recovery and before trust handling, skill invocation, interrupt, exit, resume, or adapter verification.
The verified harnesses are `claude`, `codex`, `opencode`, `pi`, `pi-signed`, `grok`, `kimi`, and `cursor`, plus `muse` and `gemini` for crewmates and scouts only; never dispatch on an unverified adapter.
The verified harnesses are `claude`, `codex`, `opencode`, `pi`, `pi-signed`, `grok`, `kimi`, and `cursor`, plus `muse`, `gemini`, and `agy` for crewmates and scouts only; never dispatch on an unverified adapter.
If static `config/crew-harness` or `config/secondmate-harness` names an unverified adapter, report it and fall back only to a verified adapter rather than launching it.

`docs/configuration.md` owns dispatch-profile and runtime-backend schemas, `bin/fm-harness.sh` owns static resolution, and `bin/fm-spawn.sh` owns launch flags and fail-closed validation.
Expand Down
2 changes: 1 addition & 1 deletion bin/backends/herdr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1456,7 +1456,7 @@ fm_backend_herdr_server_ensure() { # <session>
[ "$running" = "true" ] && return 0
(
unset FM_HOME FM_ROOT_OVERRIDE FM_STATE_OVERRIDE FM_DATA_OVERRIDE FM_PROJECTS_OVERRIDE FM_CONFIG_OVERRIDE \
CURSOR_AGENT CURSOR_INVOKED_AS CLAUDECODE PI_CODING_AGENT FM_PI_HARNESS GROK_AGENT FM_SUPERVISION_MODEL
CURSOR_AGENT CURSOR_INVOKED_AS CLAUDECODE PI_CODING_AGENT FM_PI_HARNESS GROK_AGENT ANTIGRAVITY_CONVERSATION_ID FM_SUPERVISION_MODEL
fm_backend_herdr_cli "$session" server >/dev/null 2>&1 &
) || return 1
for i in $(seq 1 20); do
Expand Down
6 changes: 6 additions & 0 deletions bin/backends/tmux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ fm_backend_tmux_classify_process_name() { # <path> [argv0] -> agent|shell|other
# cannot carry it either: ~/.local/bin/muse-bin-<version> has no `muse` path
# COMPONENT, so the fm_harness_path_name fallback below never fires for it.
muse|muse-bin-*) printf 'agent' ;;
# agy is anchored for the same reason muse is: `agy` is a short fragment that
# a glob would find inside ordinary names like legacy or agyneja, and its
# launch execs the bare binary through `env`, so the live process name and
# argv[0] are both exactly `agy`. bin/fm-harness.sh applies the same exact
# anchoring to it.
agy) printf 'agent' ;;
*claude*|*codex*|*opencode*|*grok*|*kimi*|pi|pi-signed|pi-launcher|Pi) printf 'agent' ;;
zsh|bash|sh|dash|ash|ksh|mksh|tcsh|csh|fish) printf 'shell' ;;
*)
Expand Down
Loading
Loading