Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 41 additions & 0 deletions .agents/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"firstmate-sessionstart": {
"PreInvocation": [
{
"type": "command",
"command": "../bin/fm-antigravity-hook.sh sessionstart",
"timeout": 180
}
]
},
"firstmate-shell-seatbelts": {
"PreToolUse": [
{
"matcher": "run_command",
"hooks": [
{
"type": "command",
"command": "../bin/fm-arm-pretool-check.sh --antigravity"
},
{
"type": "command",
"command": "../bin/fm-cd-pretool-check.sh --antigravity"
}
]
}
]
},
"firstmate-delegation-seatbelt": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "../bin/fm-subagent-pretool-check.sh --antigravity"
}
]
}
]
}
}
5 changes: 3 additions & 2 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 antigravity.
user-invocable: false
metadata:
internal: true
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",
"antigravity": "references/harness/antigravity.md"
}
}
```
72 changes: 72 additions & 0 deletions .agents/skills/harness-adapters/references/harness/antigravity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Google Antigravity CLI

Google's `agy` TUI, verified end to end on 2026-09-04 with Antigravity CLI 1.1.26 on macOS.
This is a distinct adapter from Google's separate `gemini` CLI.
It is verified for crewmates, scouts, second mates, and interactive primary sessions.

## Operating facts

| Fact | Value |
|---|---|
| Launch | `agy --dangerously-skip-permissions --add-dir <exact-worktree> --add-dir <firstmate-hook-overlay> --model <id> --effort <low|medium|high> --prompt-interactive "<brief>"`. Second mates omit the task overlay because their own tracked `.agents/hooks.json` supplies primary hooks. |
| Workspace | The first `--add-dir` selects the exact isolated project and makes its `AGENTS.md` and `.agents/skills/` available. Without it, terminal tools start in `~/.gemini/antigravity-cli`, not the launching shell's directory. |
| Autonomy | `--dangerously-skip-permissions` is the documented always-proceed mode and runs terminal tools unattended. `--mode accept-edits` alone does not grant command execution in headless mode; the command is automatically denied. |
| Marker | Tool subprocesses carry `ANTIGRAVITY_AGENT=1`. Inherited `AI_AGENT` and Pi markers are not Antigravity identity and are cleared by the canonical launch. |
| Model | `--model <id>`; `agy models` is the authoritative current catalog. The Firstmate adapter is Gemini-only: it validates an explicit `gemini-*` id or chooses a live Gemini catalog entry, preferring the requested effort suffix. |
| Effort | `--effort low|medium|high`; xhigh and max are unsupported and deliberately omitted. |
| Busy state | Semantic `antigravity-hook`: task `PreInvocation` opens activity and `Stop` settles it. Herdr's native `agent get` also reports `agy` with working/idle status, and the rendered delivery token is `esc to cancel`. |
| Turn end | The Firstmate-owned task overlay's `Stop` hook settles activity and touches `state/<id>.turn-ended`. |
| Interrupt | One `Escape`; the active turn cancels and the TUI remains open. |
| Exit | `/quit`, then one Enter. |
| Skills | Antigravity automatically discovers skills under `.agents/skills/` from the added workspace. |
| Primary | One foreground `bin/fm-watch.sh` terminal call; see `../../../../../docs/supervision-protocols/antigravity.md`. |

## Version and sign-in

Require `agy --version` 1.1.26 or newer.
That release advertises the fix for repeated subagent approvals in always-proceed mode; use the vendor's `agy update` command when the installed version is older.
Complete Antigravity's own sign-in once before dispatch.
Firstmate never copies or embeds the account credential in a launch command.

## Detection and liveness

`../../../../../bin/fm-harness.sh` checks `ANTIGRAVITY_AGENT=1` before inherited Pi or other generic markers and recognizes exact `agy` process ancestry.
Never use `AI_AGENT` as identity: a verified Antigravity tool process retained its Pi launcher's value.
The exact executable name `agy` is also registered with the shared session-lock and tmux foreground-process classifiers.
Herdr 0.8.0 natively reports `agent=agy` and working/idle state for the same process.
A bare command or path merely containing the substring `agy` is not accepted as identity.

## Workspace, instructions, and hooks

Antigravity discovers `.agents/hooks.json`, `AGENTS.md`, and `.agents/skills/` from a directory passed with `--add-dir`.
Hook discovery is independent of the argument's position in the repeated `--add-dir` list.
Hook commands run with the directory containing `hooks.json` as their working directory.
The tracked root hook injects the normal startup reminder with `PreInvocation` and gates terminal/delegation tools with `PreToolUse`.
For workers, `fm-spawn.sh` writes `state/<id>.antigravity-hooks/.agents/hooks.json` and adds that isolated overlay after the project path, so it never writes or replaces the project's own `.agents/hooks.json`.
`fm-control.sh relaunch` retires the old hook file before arming a replacement generation, and cleanup removes the overlay directory.
A raw launch command receives none of this task wiring and has no trusted semantic activity state.

## Composer, steering, and lifecycle

Antigravity draws a `>` input row between two solid separator rows.
The shared composer classifier accepts that shell-like glyph only inside the verified separated shape and only when the live identity probe says Antigravity; a bare `>` remains a dead shell and can never prove an empty composer.
This preserves the ordinary durable-inbox steering path and Enter-only retry rule on tmux and Herdr.
Lifecycle operations go only through `../../../../../bin/fm-control.sh`.
The executable table in `../../../../../bin/fm-control-lib.sh` owns the one-Escape interrupt, `/quit` exit, task-kind support, and hook-overlay cleanup path.

## Primary safety and supervision

Antigravity's `PreToolUse` input is `.toolCall.name` plus `.toolCall.args.CommandLine`, and deny output is `{"decision":"deny","reason":"..."}`.
The tracked primary hooks adapt that native contract to Firstmate's watcher-arm, persistent-directory-change, and built-in delegation guards.
Those guards are primary-scoped and remain inert in isolated worker copies where delegation is legitimate.
A live Gemini turn on 1.1.26 identified the built-in delegation tools as `invoke_subagent` and `send_message`; both are denied by the guard's existing delegation-shape classification in a Firstmate primary.
Antigravity hooks are synchronous and expose no verified asynchronous background-task-to-model wake.
The primary therefore uses the named foreground supervision protocol rather than borrowing another harness's background mechanics.
Headless `--print` is not a primary host because it has no persistent conversation for later fleet notifications.

## Verification boundary

The live checks used a named disposable Herdr lab and explicit Gemini models only.
They proved model and effort display, exact workspace tool execution, `AGENTS.md` and skill discovery, autonomous terminal execution, marker precedence, native Herdr identity, hook discovery, one-Escape interrupt, and `/quit` exit.
No Claude-family model was selected.
See `../../../../../docs/verification/antigravity.md` for the dated commands, counterfactuals, and remaining limitations.
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ state/ runtime records and signals; gitignored
<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>.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>.antigravity-hooks/ firstmate-owned Antigravity `--add-dir` overlay carrying per-task busy-state and turn-end hooks without touching the project's own `.agents/hooks.json`; 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
<id>.reconcile-nudged epoch second of the last inventory-reconcile nudge sent to this secondmate; bin/fm-secondmate-reconcile.sh owns its per-home cooldown window
Expand Down Expand Up @@ -199,7 +200,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`, `cursor`, and `antigravity`, plus `muse` and `gemini` 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
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Full detail on every feature lives in [docs/architecture.md](docs/architecture.m

### Requirements

- A verified primary agent harness: Claude Code, Grok, Pi, `pi-signed`, Codex, OpenCode, or Cursor Agent CLI.
- A verified primary agent harness: Claude Code, Grok, Pi, `pi-signed`, Codex, OpenCode, Cursor Agent CLI, or Google Antigravity CLI (`agy` 1.1.26+).
- Git and the GitHub CLI, authenticated through `gh auth login`.
- The CLI and dependencies for your selected runtime backend; tmux is the reference default.

Expand All @@ -75,6 +75,8 @@ Pick whichever one matches your subscription and workflow.
Codex and OpenCode are also verified and supported as primary harnesses; Codex uses bounded foreground checkpoints, and OpenCode uses a TUI plugin, so both carry more harness-specific supervision tradeoffs than the three co-primaries.
Cursor Agent CLI is verified as a primary too, using a tracked project-scope `.cursor/hooks.json` whose `stop` hook parks on the watcher between turns, closest in shape to Claude Code's.
Launch it with `--trust`, or none of its project hooks load; it also has no turn-end hook in headless `cursor-agent -p`, so run the primary session interactively.
Google Antigravity CLI 1.1.26+ is verified as a primary and worker harness with native Gemini model and low/medium/high effort selection.
It uses a foreground terminal-tool supervision wait because Antigravity exposes no verified asynchronous process-to-model wake, and `fm-spawn.sh` supplies the exact isolated project, autonomous permission mode, and Firstmate-owned task hooks.

### Install and launch

Expand All @@ -84,7 +86,7 @@ git clone https://github.com/kunchenguid/firstmate
cd firstmate
```

Then launch one of the co-primary harnesses; AGENTS.md takes over from there:
Then launch a verified primary harness; AGENTS.md takes over from there:

**Claude Code**

Expand All @@ -106,6 +108,16 @@ pi
FM_PI_HARNESS=pi-signed pi-signed
```

**Google Antigravity CLI**

```sh
agy --dangerously-skip-permissions --add-dir "$PWD" --model <gemini-model> --effort low
```

Install Antigravity from [antigravity.google/download](https://antigravity.google/download), sign in once, and use `agy models` for the current model ids.
Run `agy update` when `agy --version` is older than 1.1.26.
The tracked `.agents/hooks.json` loads startup and primary safety hooks from this repository; use the interactive TUI rather than headless `--print` for a primary session.

For Grok, `--trust` is needed once per clone so project hooks and the turn-end guard load; `/hooks-trust` inside Grok works too.
For Pi, approve the project trust prompt once per clone on first launch so the tracked `.pi/extensions/*.ts` files auto-load.
Pi's `/calm` toggle hides supported transcript chrome, including canonically classified Firstmate operational user rows, and uses a Calm-only animated working boat during active runs while preserving all model context and session data.
Expand Down Expand Up @@ -216,7 +228,7 @@ Firstmate's skills live in two separate places with different audiences:
- [docs/gitlab-merge-watch.md](docs/gitlab-merge-watch.md) - maintainer verification for watching and merging GitLab merge requests on arbitrary instances.
- [docs/turnend-guard.md](docs/turnend-guard.md) - the primary session's current "no turn ends blind" backstop, scope, loop safety, and compatibility limits.
- [docs/verification/supervision.md](docs/verification/supervision.md) - active maintainer verification for session-start, guard, continuity, and wedge integrations.
- [docs/supervision-protocols/](docs/supervision-protocols/) - rendered primary-harness watcher protocols for Claude, Codex, OpenCode, Pi and `pi-signed`, Grok, Cursor, and unknown harness fallback.
- [docs/supervision-protocols/](docs/supervision-protocols/) - rendered primary-harness watcher protocols for Claude, Codex, OpenCode, Pi and `pi-signed`, Grok, Cursor, Antigravity, and unknown harness fallback.
- [docs/scripts.md](docs/scripts.md) - the `bin/` toolbelt reference.
- [docs/documentation-audiences.md](docs/documentation-audiences.md) - documentation audiences and the machine-checked placement boundary.
- [`AGENTS.md`](AGENTS.md) - the distro's always-loaded operating contract and routing index for conditional procedures.
Expand Down
6 changes: 3 additions & 3 deletions bin/backends/herdr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2620,7 +2620,7 @@ fm_backend_herdr_capture_ansi() { # <target> <lines>
# ANSI pane capture (with its small-N workaround), the native `agent get`
# identity probe, and the capability descriptor. Every shape - the bordered
# box, the bare agent-glyph row, opencode's left-bar, and pi's
# identity-gated separated pair (which this adapter pioneered) - now lives in
# identity-gated separated pair (Pi and Antigravity) - now lives in
# the shared owner (bin/fm-composer-lib.sh, fm_composer_classify_screen), so
# a new harness shape is taught there once and every backend learns it in the
# same commit. The muse `⟩` glyph this adapter's local bare-prompt pattern
Expand All @@ -2633,7 +2633,7 @@ fm_backend_herdr_agent_identity_raw() { # <session> <pane> -> <agent>\t<status>
}

# fm_backend_herdr_composer_identity: the native agent identity/state probe
# backing the shared classifier's separated (pi) shape - the genuine herdr
# backing the shared classifier's separated shape - the genuine herdr
# primitive no other backend has natively.
fm_backend_herdr_composer_identity() { # <target> -> "<agent>\t<status>"
fm_backend_herdr_parse_target "$1" || return 1
Expand All @@ -2645,7 +2645,7 @@ fm_backend_herdr_composer_identity() { # <target> -> "<agent>\t<status>"
# shared classifier strip ghost/placeholder text); when it fails on an older
# herdr, the plain capture degrades the descriptor to styled=0 rather than
# letting ghost text be misread as typed input. Identity is fetched lazily,
# only when the classifier reports the verdict depends on it (a pi separator
# only when the classifier reports the verdict depends on it (a separated
# pair below every other candidate), preserving this adapter's original
# consult-only-when-needed behavior.
fm_backend_herdr_composer_state() { # <target> -> empty|pending|pending-unproven|unknown
Expand Down
2 changes: 1 addition & 1 deletion bin/backends/tmux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fm_backend_tmux_classify_process_name() { # <path> [argv0] -> agent|shell|other
# would classify musescore or amuse as a live agent pane. The install path
# 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' ;;
muse|muse-bin-*|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
60 changes: 60 additions & 0 deletions bin/fm-antigravity-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Antigravity hook adapter for Firstmate.
#
# Usage:
# fm-antigravity-hook.sh sessionstart
# fm-antigravity-hook.sh task-busy <state-dir> <task-id> <generation>
# fm-antigravity-hook.sh task-stop <state-dir> <task-id> <generation> <turn-ended-file>
#
# Antigravity discovers .agents/hooks.json in every --add-dir root. The task
# launcher points one added directory at a Firstmate-owned overlay so projects'
# own customization remains untouched. Hook stdin is consumed but never trusted
# for task identity: spawn bakes the canonical state directory, safe task id,
# and fresh busy generation into the isolated hook file.
#
# PreInvocation opens semantic busy state before the model starts or resumes.
# Stop settles that state and publishes the ordinary turn-end edge. Sessionstart
# reuses Firstmate's read-only startup nudge and returns Antigravity's documented
# PreInvocation injectSteps.ephemeralMessage shape.
set -euo pipefail

SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

usage() {
echo "usage: $0 sessionstart | task-busy <state-dir> <task-id> <generation> | task-stop <state-dir> <task-id> <generation> <turn-ended-file>" >&2
exit 2
}

mode=${1:-}
case "$mode" in
sessionstart)
[ "$#" -eq 1 ] || usage
cat >/dev/null
message=$(
FM_SESSIONSTART_HOOK_MODE=1 \
"$SCRIPT_DIR/fm-sessionstart-nudge.sh" 2>/dev/null || true
)
if [ -n "$message" ]; then
jq -n --arg message "$message" \
'{injectSteps:[{ephemeralMessage:$message}]}'
else
printf '{}\n'
fi
;;
task-busy)
[ "$#" -eq 4 ] || usage
cat >/dev/null
"$SCRIPT_DIR/fm-busy-event.sh" apply "$2" "$3" busy \
--gen "$4" --source antigravity-hook --event pre-invocation >/dev/null 2>&1 || true
printf '{}\n'
;;
task-stop)
[ "$#" -eq 5 ] || usage
cat >/dev/null
"$SCRIPT_DIR/fm-busy-event.sh" apply "$2" "$3" idle \
--gen "$4" --source antigravity-hook --event stop >/dev/null 2>&1 || true
: > "$5"
printf '{"decision":"stop"}\n'
;;
*) usage ;;
esac
Loading