Skip to content

feat: add AGY (Antigravity) harness adapter, interactive prompts, and slash commands - #99

Merged
AltanS merged 8 commits into
AltanS:mainfrom
Kryvonis:main
Aug 28, 2026
Merged

feat: add AGY (Antigravity) harness adapter, interactive prompts, and slash commands#99
AltanS merged 8 commits into
AltanS:mainfrom
Kryvonis:main

Conversation

@Kryvonis

@Kryvonis Kryvonis commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for AGY (Google Antigravity CLI / agy / antigravity) to Collie:

  1. Interactive Prompt Up-Leveling (web/src/lib/harness/agy/):

    • Lifts ask_question dialogs, numbered options (1. Option, 1) Option, [1] Option, ( ) 1.Option), and permission/confirmation prompts into native tappable buttons.
    • Handles options with or without explicit footer hint lines.
    • Guards against free-text sends while an interactive prompt is open (composerReady).
    • Re-derives signatures through the race guard before sending keystrokes ([digit, "Enter"] or [digit]).
  2. Slash Command Palette (web/src/lib/agent-commands.ts):

    • Adds the public AGY command catalog with prefix matching for agy, agy-cli, and antigravity:
      /plan, /grill-me, /learn, /compact, /clear, /model, /status, /subagents, /skills, /rules, /mcp, /hooks, /review, /diff, /doctor, /theme, /help, /exit.
  3. Brand Styling & Icon (web/src/components/agent-icon*):

    • Adds the official brand mark and tile background (#1A73E8) for agy and antigravity pane badges and switcher tiles.
  4. Registry & Case/Prefix Normalization (web/src/lib/harness/registry.ts):

    • Normalizes agent keys in adapterFor to support casing and common prefix variants (agy, agy-cli, antigravity).

Suggested CHANGELOG Entry

AGY (Antigravity CLI) gets a harness adapter — up-levels interactive question options (ask_question) and tool permission dialogs into tappable buttons, gates the composer against typing into active menus, adds the AGY slash command palette, and includes brand icons.

Verification

- **Harness Unit Tests:**
  `cd web && bunx vitest run src/lib/harness/agy/` (7 passed)
- **Commands & Icon Tests:**
  `cd web && bunx vitest run src/lib/agent-commands.test.ts src/components/agent-icon.test.tsx` (39 passed)
- **Full Frontend Suite:**
  `cd web && bun run test` (108 test files, 2,161 tests passing)
- **Bridge Tests:**
  `bun test bridge/` (559 tests passing)
- **Typecheck & Production Build:**
  `cd web && bun run typecheck && bun run build` (0 errors, clean build)
- **Live Local Test:**
  Verified prompt button tapping and submission against a live `agy` session on Herdr.

@AltanS

AltanS commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Thanks — the agy work is welcome. The command catalog and the icon are fine as they are.

The adapter emits prompt-select, whose keystroke recipe is already live, so this is Tier 2 in HARNESS_CONTRIBUTING.md and needs: a captured fixture corpus, a notes file with the verified recipe, a describeAdapterConformance call, and live verification. I can't do the last one — agy isn't installed here.

Two changes I'd want regardless:

  1. keys: [n, "Enter"] synthesises digits from numbered rows, and the detector matches any numbered list near the tail — it'll claim /model and /skills. ADR 0009 closes that off (in Claude's /model a digit also rewrites the user's default). Drive it from the footer, or record the live probe showing agy's digits are safe.

  2. Please drop the adapterFor prefix-matching — it widens claude/omp to any agent string with those prefixes. Separate PR, separate argument. Register agy by its exact agent strings.

Bring the corpus + notes + conformance and run the live check on your session, and I'll take it whole.

kennymcavoy added a commit to kennymcavoy/collie that referenced this pull request Aug 21, 2026
agy's first drop emitted prompt-select (Tier 2, live keystrokes) and
prefix-matched inside adapterFor. Stay Tier 1, register agent "grok"
exactly, and keep catalog folding in canonicalAgent. ADR 0009 still
blocks digit synthesis if a later dialog lift lands.
kennymcavoy added a commit to kennymcavoy/collie that referenced this pull request Aug 21, 2026
rstrip not peel (same helper as omp/claude). Bottom-border capture
uses [\s\S] so U+2028 cannot silently decline a frame. Cite AltanS#99 the
way the rest of the repo cites issues. Revert the 0.33.0 bump —
CLAUDE.md: a fork PR leaves version and CHANGELOG to the maintainer.
kennymcavoy added a commit to kennymcavoy/collie that referenced this pull request Aug 22, 2026
Grok Build panes get the Claude-level treatment: stripped composer
chrome with an opaque status strip, native buttons for the probed
dialogs, a journal adapter, and a slash-command palette.

Registered by exact agent string only (the AltanS#99 bar): adapterFor stays
Object.hasOwn over adapter.agent; grok-build catalog folding lives in
canonicalAgent. Every keystroke the adapter emits comes from a dated
live probe recorded in the notes files, against a byte-faithful fixture
corpus, with describeAdapterConformance running grok against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): the rounded composer box is located by its bottom
status border and stripped; the status run is opaque across models and
efforts. composerReady gates the reply path. The startup screen's bare
[stable] telemetry chip under the box is chrome (matched literally —
any other bracket tag is torn transcript and refuses), so a fresh pane
accepts its first message. Grok paints background across fully blank
rows, which striped the mirror; empty rows lose their paint — no
colour inference — while glyph rows keep every background they drew.

Permission cards: classified, not layout-pinned. The footer must name
the family and count its rows (1/N:select), the last row must be the
reject, the row above it the one-shot Yes, and every earlier row must
prove itself a persistent mode change — those never become buttons.
Unrecognized control rows, or text below the options, refuse the whole
card. Both live layouts (3-option rm, 4-option edit) lift to Yes/No.

ask_user_question: radio cards lift with digit keys; the z free-text
row is modelled as feedback (purpose "free-text" — Collie never types
into it) and locks the buttons whenever the keyboard sits on it, which
the inner hint row's Enter:edit verb reveals even when the row repaints
idle (the hint is matched as a whole anchored row, never as a substring
of prose). Esc-parked cards send Tab (the footer's own key) before the
digit — a bare digit is swallowed in scrollback. Checkbox cards stay
raw: a digit submits rather than toggles. Multi-question wizards step
through as consecutive lifted cards.

Plan approval: a menu of footer-named keys only (ADR 0009 — no digits),
with the composer gated while the review is up.

Dialogs replace the screen from their first option row down, so the
question stays in the raw mirror above the buttons — the same contract
as Claude's prompt-select. The dialog signature spans the whole card;
the bridge race guard refuses a stale press (409) after the screen
moves.

Journal: bridge/journal/grok.ts reads chat_history.jsonl from
$GROK_HOME (override: COLLIE_GROK_ROOT) via containedRealpath, refusing
non-UUID refs. journal-probe verifies every populated root separately —
one healthy root never vouches for a broken sibling.

Neutral-model changes riding along: PromptFeedback.purpose (free-text
rows render no feedback composer and submitPromptFeedback refuses
them), and PromptOption.keyLabel (the badge shows the digit when a
recovery key precedes it). /auto joins the dangerous two-tap set — it
changes approval behavior. Claude behavior unchanged, pinned by the
existing claude suites.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd
kennymcavoy added a commit to kennymcavoy/collie that referenced this pull request Aug 23, 2026
Codex panes get the Claude-level treatment: the boxless chrome stripped
and re-surfaced natively, plus native buttons for the three probed
dialogs.

Registered by exact agent string only (the AltanS#99 bar):
adapterFor stays Object.hasOwn over adapter.agent; variant spellings
stay raw. Every keystroke the adapter emits comes from a dated live
probe recorded in the notes files, against a byte-faithful fixture
corpus (identifying strings length-preservingly genericized — public
repo), with describeAdapterConformance running codex against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): Codex paints a bare `› ` prompt row that word-wraps
onto indented continuation rows, a dot-separated status row beneath,
and exactly one blank row between every section. The composer is
located by the status row at the tail (the ` · Context N% left` token;
everything before it is opaque — models and paths change) with the
prompt row above it — a transcript echo of a submitted message paints
the same `› ` prefix and never has the status row beneath it. The
status row feeds the strip, the prompt row the draft (wrapped drafts
rejoin with single spaces, verified against the typed original) and
composerPrompt.

Trust prompt: the exact captured two-option layout lifts as family
trust. Digits confirm directly — 2 quit on the spot, 1 accepted through
the guarded send path (both probed).

Exec approval: classified, not layout-pinned — the first row must be
the one-shot Yes, the last the reject, and every row between must prove
itself persistent (`don't ask again …`); those never become buttons.
An unclassified row refuses the whole card. Digits confirm directly:
1 ran the approved command, 3 rejected it with the command verified
never run. The header, Reason and `$ command` rows stay in the raw
mirror above the buttons. Only the exec approval is captured; other
approval kinds fail closed until probed. Note the config dependency:
approvals_reviewer = "auto_review" (the host default here) approves
silently and the dialog never paints — probing used
`-c approvals_reviewer=user`.

request_user_input: the Question X/Y card lifts as family select, one
digit per row — a digit answers the current question, advances a
multi-question set, and submits on the last (all probed). The
notes-box state refuses to raw (footer `tab or esc to clear notes`, or
the `› Add notes` row): a digit there would type into the box. Esc
interrupts the whole conversation and is never emitted.

Live-verified end to end through the bridge on a real pane: trust
accepted, a reply typed-verified-submitted through the new draft
extraction, an approval Yes landing its command, and an ask answer
registering in the tool result — every press through the guarded /keys
endpoint with its region signature.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd
kennymcavoy added a commit to kennymcavoy/collie that referenced this pull request Aug 23, 2026
Grok Build panes get the Claude-level treatment: stripped composer
chrome with an opaque status strip, native buttons for the probed
dialogs, a journal adapter, and a slash-command palette.

Registered by exact agent string only (the AltanS#99 bar): adapterFor stays
Object.hasOwn over adapter.agent; grok-build catalog folding lives in
canonicalAgent. Every keystroke the adapter emits comes from a dated
live probe recorded in the notes files, against a byte-faithful fixture
corpus, with describeAdapterConformance running grok against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): the rounded composer box is located by its bottom
status border and stripped; the status run is opaque across models and
efforts. composerReady gates the reply path. The startup screen's bare
[stable] telemetry chip under the box is chrome (matched literally —
any other bracket tag is torn transcript and refuses), so a fresh pane
accepts its first message. Grok paints background across fully blank
rows, which striped the mirror; empty rows lose their paint — no
colour inference — while glyph rows keep every background they drew.

Permission cards: classified, not layout-pinned. The footer must name
the family and count its rows (1/N:select), the last row must be the
reject, the row above it the one-shot Yes, and every earlier row must
prove itself a persistent mode change — those never become buttons.
Unrecognized control rows, or text below the options, refuse the whole
card. Both live layouts (3-option rm, 4-option edit) lift to Yes/No.

ask_user_question: radio cards lift with digit keys; the z free-text
row is modelled as feedback (purpose "free-text" — Collie never types
into it) and locks the buttons whenever the keyboard sits on it, which
the inner hint row's Enter:edit verb reveals even when the row repaints
idle (the hint is matched as a whole anchored row, never as a substring
of prose). Esc-parked cards send Tab (the footer's own key) before the
digit — a bare digit is swallowed in scrollback. Checkbox cards stay
raw: a digit submits rather than toggles. Multi-question wizards step
through as consecutive lifted cards.

Plan approval: a menu of footer-named keys only (ADR 0009 — no digits),
with the composer gated while the review is up.

Dialogs replace the screen from their first option row down, so the
question stays in the raw mirror above the buttons — the same contract
as Claude's prompt-select. The dialog signature spans the whole card;
the bridge race guard refuses a stale press (409) after the screen
moves.

Journal: bridge/journal/grok.ts reads chat_history.jsonl from
$GROK_HOME (override: COLLIE_GROK_ROOT) via containedRealpath, refusing
non-UUID refs.

Neutral-model changes riding along: PromptFeedback.purpose (free-text
rows render no feedback composer and submitPromptFeedback refuses
them), and PromptOption.keyLabel (the badge shows the digit when a
recovery key precedes it). /auto joins the dangerous two-tap set — it
changes approval behavior. Claude behavior unchanged, pinned by the
existing claude suites.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.
@AltanS

AltanS commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Both asks are addressed — adapterFor is exact-keyed again, and the detector is footer-gated so /model and /skills stay raw. Thanks. Three things before I take it.

1. The fixtures aren't captures. Every file in the corpus is a byte-faithful format:ansi capture with real ESC bytes; the 8 agy files have none, and their rule widths disagree between files (60 vs 113 columns).

claude--select-menu.txt  27 ESC     grok--ask-color.txt   54 ESC
codex--ask-fruit.txt     78 ESC     agy--select-menu.txt   0 ESC

This is the one that blocks the rest: the keystroke table in AGY_NOTES.md is the only evidence the recipe is right, and hand-typed screens can't corroborate it. Capture them with scripts/capture-fixture.sh <paneId> <name> off a sandbox agy pane. If you need to scrub a username or path, keep the substitution length-preserving so the row padding stays byte-identical — see the codex corpus note in the fixtures README.

2. chrome.ts never finds the input box. ? for shortcuts matches its PROMPT_REGEX (the \?\s* branch), so the walk stops on the status line. On your own fixtures:

agy--fresh-idle.txt  DRAFT = "for shortcuts    accept-edits · Gemini 3.7 Flash · low"
agy--done.txt        DRAFT = "for shortcuts    accept-edits · Gemini 3.7 Flash · low"
agy--fresh-idle.txt  blocks = [{raw, 9 lines}]      // the box is still in the mirror

Collie would paint that as the operator's draft. extractStatusLines returns [] unconditionally too, so the status row is never lifted out. Please cover both against a fixture — the unit tests use hand-built strings, which is why this got through.

3. Please restore the completeness gate in prompt-binding-contract.test.ts. Narrowing it to claude-- also switches it off for codex, grok and omp. With the original filter, five agy fixtures come back unpinned:

agy--permission-bash, agy--permission-edit, agy--plan-approval,
agy--select-menu, agy--trust-prompt

Claude's detectPromptSelect claims all five. Add their regions to prompt-binding-regions.json rather than shrinking the filter.

Two notes, not conditions. isAlienBuffer is carrying more weight than it looks: with that one function stubbed out, the agy grammar claims 12 foreign fixtures with digit keys. Nothing routes that way in production, so it isn't a live hazard — but it tells you the grammar is loose, and a real agy screen that prints Enter to select will be claimed the same way. A brand-name blocklist won't catch that one. Separately, the geminisomeunknown edit in agent-icon.test.tsx looks unrelated; gemini is still an unknown agent everywhere else.

Bring the real captures and the chrome fix and I'll take it.

bermudi pushed a commit to bermudi/collie that referenced this pull request Aug 24, 2026
Codex panes get the Claude-level treatment: the boxless chrome stripped
and re-surfaced natively, plus native buttons for the three probed
dialogs.

Registered by exact agent string only (the AltanS#99 bar):
adapterFor stays Object.hasOwn over adapter.agent; variant spellings
stay raw. Every keystroke the adapter emits comes from a dated live
probe recorded in the notes files, against a byte-faithful fixture
corpus (identifying strings length-preservingly genericized — public
repo), with describeAdapterConformance running codex against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): Codex paints a bare `› ` prompt row that word-wraps
onto indented continuation rows, a dot-separated status row beneath,
and exactly one blank row between every section. The composer is
located by the status row at the tail (the ` · Context N% left` token;
everything before it is opaque — models and paths change) with the
prompt row above it — a transcript echo of a submitted message paints
the same `› ` prefix and never has the status row beneath it. The
status row feeds the strip, the prompt row the draft (wrapped drafts
rejoin with single spaces, verified against the typed original) and
composerPrompt.

Trust prompt: the exact captured two-option layout lifts as family
trust. Digits confirm directly — 2 quit on the spot, 1 accepted through
the guarded send path (both probed).

Exec approval: classified, not layout-pinned — the first row must be
the one-shot Yes, the last the reject, and every row between must prove
itself persistent (`don't ask again …`); those never become buttons.
An unclassified row refuses the whole card. Digits confirm directly:
1 ran the approved command, 3 rejected it with the command verified
never run. The header, Reason and `$ command` rows stay in the raw
mirror above the buttons. Only the exec approval is captured; other
approval kinds fail closed until probed. Note the config dependency:
approvals_reviewer = "auto_review" (the host default here) approves
silently and the dialog never paints — probing used
`-c approvals_reviewer=user`.

request_user_input: the Question X/Y card lifts as family select, one
digit per row — a digit answers the current question, advances a
multi-question set, and submits on the last (all probed). The
notes-box state refuses to raw (footer `tab or esc to clear notes`, or
the `› Add notes` row): a digit there would type into the box. Esc
interrupts the whole conversation and is never emitted.

Live-verified end to end through the bridge on a real pane: trust
accepted, a reply typed-verified-submitted through the new draft
extraction, an approval Yes landing its command, and an ask answer
registering in the tool result — every press through the guarded /keys
endpoint with its region signature.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd
(cherry picked from commit e5fab3a)
bermudi pushed a commit to bermudi/collie that referenced this pull request Aug 24, 2026
Grok Build panes get the Claude-level treatment: stripped composer
chrome with an opaque status strip, native buttons for the probed
dialogs, a journal adapter, and a slash-command palette.

Registered by exact agent string only (the AltanS#99 bar): adapterFor stays
Object.hasOwn over adapter.agent; grok-build catalog folding lives in
canonicalAgent. Every keystroke the adapter emits comes from a dated
live probe recorded in the notes files, against a byte-faithful fixture
corpus, with describeAdapterConformance running grok against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): the rounded composer box is located by its bottom
status border and stripped; the status run is opaque across models and
efforts. composerReady gates the reply path. The startup screen's bare
[stable] telemetry chip under the box is chrome (matched literally —
any other bracket tag is torn transcript and refuses), so a fresh pane
accepts its first message. Grok paints background across fully blank
rows, which striped the mirror; empty rows lose their paint — no
colour inference — while glyph rows keep every background they drew.

Permission cards: classified, not layout-pinned. The footer must name
the family and count its rows (1/N:select), the last row must be the
reject, the row above it the one-shot Yes, and every earlier row must
prove itself a persistent mode change — those never become buttons.
Unrecognized control rows, or text below the options, refuse the whole
card. Both live layouts (3-option rm, 4-option edit) lift to Yes/No.

ask_user_question: radio cards lift with digit keys; the z free-text
row is modelled as feedback (purpose "free-text" — Collie never types
into it) and locks the buttons whenever the keyboard sits on it, which
the inner hint row's Enter:edit verb reveals even when the row repaints
idle (the hint is matched as a whole anchored row, never as a substring
of prose). Esc-parked cards send Tab (the footer's own key) before the
digit — a bare digit is swallowed in scrollback. Checkbox cards stay
raw: a digit submits rather than toggles. Multi-question wizards step
through as consecutive lifted cards.

Plan approval: a menu of footer-named keys only (ADR 0009 — no digits),
with the composer gated while the review is up.

Dialogs replace the screen from their first option row down, so the
question stays in the raw mirror above the buttons — the same contract
as Claude's prompt-select. The dialog signature spans the whole card;
the bridge race guard refuses a stale press (409) after the screen
moves.

Journal: bridge/journal/grok.ts reads chat_history.jsonl from
$GROK_HOME (override: COLLIE_GROK_ROOT) via containedRealpath, refusing
non-UUID refs.

Neutral-model changes riding along: PromptFeedback.purpose (free-text
rows render no feedback composer and submitPromptFeedback refuses
them), and PromptOption.keyLabel (the badge shows the digit when a
recovery key precedes it). /auto joins the dangerous two-tap set — it
changes approval behavior. Claude behavior unchanged, pinned by the
existing claude suites.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.
@Kryvonis

Kryvonis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @AltanS
First of all for this powerful plugin and for maintaining it all.
I'm glad to commit and help a bit
alt


Addressed your comments so please review whenever you have free minute, and feel free to reach out to me

`locateInputBox` had a second, unanchored path: if the last non-blank row
matched `/^[❯›>]\s*/` it was accepted as a live composer outright. AGY's
prompt glyph is plain ASCII `>`, so that path claimed ordinary transcript
rows.

It is not a hypothetical shape. AGY echoes every submitted message as
`> …`, and paints an answered `ask_user_question` selection the same way —
both are visible in `agy--done.txt`. A markdown quote or a `git log` in the
transcript reads identically. On such a screen `composerReady` answered
true over a running turn and `extractInputDraft` handed the echoed line
back as the operator's own draft, so a reply was authorised into a pane
that could not receive it.

Every capture in the corpus shows the same composer: a top rule, the `>`
row, a bottom rule, then the `? for shortcuts …` status row. Path 1
already requires that box, and all three neutral fixtures resolve through
it — the fallback earned nothing and is removed. A screen with no located
box now fails closed, which costs a refused send instead of keystrokes
into a working agent.

The one unit test that depended on the fallback built its screen by hand
around `"❯ "`, a glyph AGY never paints; it is rewritten on the captured
boxed shape and joined by two regression tests for the unanchored rows.
Fixture behaviour is unchanged: all eight agy captures keep their previous
draft, status and block results.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AltanS
AltanS merged commit 4285457 into AltanS:main Aug 28, 2026
AltanS added a commit that referenced this pull request Aug 28, 2026
Brings #99 (AGY / Antigravity as a first-class harness adapter: ask_question menus,
permission, plan and trust dialogs lifted into native buttons, the boxed composer
stripped with its status row re-surfaced, a slash-command palette and the brand
icon), #132 (a large Codex send verified through `[Pasted Content N chars]`, and
destructive writes bound to the whole wrapped draft), #134 + the 0.150.1 status-row
work (Codex CLI 0.150.1 recognised on both of its rows, keyed on the renderer's own
paint) and #130 (a forward-auth 3xx read as a 401, and Authentik's outpost paths kept
out of the PWA cache) onto the v1 line.

Conflicts resolved:
- version trio: v1's 1.0.0-beta.32 kept; nothing else in those three files differed.
- CHANGELOG: both sides kept — v1's beta entries on top, 0.36.0 in chronological
  place directly above 0.35.0. Nothing rewritten.
- web/src/components/agent-icon-data.ts: v1's Map shape kept; main's agy and
  antigravity brand entries filed in it, and the source note extended to name them.
- web/src/lib/agent-commands.ts: v1's Map-shaped CATALOG kept; main's AGY catalog
  filed under both "agy" and "antigravity", the two spellings AGENT_FAMILIES lists
  and canonicalAgent folds neither onto the other.
- web/src/lib/api.ts: main's REDIRECT_STATUSES, normaliseProxyRedirect, apiFetch and
  the manual-redirect paragraph taken; v1's withScope naming kept over main's
  withSession. v1's fourth bridge call site, the /api/stt POST, routed through
  apiFetch too — it bypasses `req` like the other two and returns its refusal as a
  value, so a proxy 3xx has to reach it as a plain 401 as well. Every bridge call in
  the file now goes through apiFetch.

Imported code brought up to v1's gates: codex/markers.ts scans for a C0/DEL byte by
code unit instead of matching a control-character class, the way links.ts cuts the
same bytes out of an href; codex.test.ts's `row` helper keeps its inferred return
type rather than restating it as an anonymous object; server.test.ts's new
expected_prompt case passes its fake through v1's `asMux` seam instead of main's
`as unknown as HerdrClient` chain. api.test.ts gains one case for the /api/stt
redirect path, which v1 owns alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AltanS pushed a commit that referenced this pull request Aug 31, 2026
Grok Build panes get the Claude-level treatment: stripped composer
chrome with an opaque status strip, native buttons for the probed
dialogs, a journal adapter, and a slash-command palette.

Registered by exact agent string only (the #99 bar): adapterFor stays
Object.hasOwn over adapter.agent; grok-build catalog folding lives in
canonicalAgent. Every keystroke the adapter emits comes from a dated
live probe recorded in the notes files, against a byte-faithful fixture
corpus, with describeAdapterConformance running grok against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): the rounded composer box is located by its bottom
status border and stripped; the status run is opaque across models and
efforts. composerReady gates the reply path. The startup screen's bare
[stable] telemetry chip under the box is chrome (matched literally —
any other bracket tag is torn transcript and refuses), so a fresh pane
accepts its first message. Grok paints background across fully blank
rows, which striped the mirror; empty rows lose their paint — no
colour inference — while glyph rows keep every background they drew.

Permission cards: classified, not layout-pinned. The footer must name
the family and count its rows (1/N:select), the last row must be the
reject, the row above it the one-shot Yes, and every earlier row must
prove itself a persistent mode change — those never become buttons.
Unrecognized control rows, or text below the options, refuse the whole
card. Both live layouts (3-option rm, 4-option edit) lift to Yes/No.

ask_user_question: radio cards lift with digit keys; the z free-text
row is modelled as feedback (purpose "free-text" — Collie never types
into it) and locks the buttons whenever the keyboard sits on it, which
the inner hint row's Enter:edit verb reveals even when the row repaints
idle (the hint is matched as a whole anchored row, never as a substring
of prose). Esc-parked cards send Tab (the footer's own key) before the
digit — a bare digit is swallowed in scrollback. Checkbox cards stay
raw: a digit submits rather than toggles. Multi-question wizards step
through as consecutive lifted cards.

Plan approval: a menu of footer-named keys only (ADR 0009 — no digits),
with the composer gated while the review is up.

Dialogs replace the screen from their first option row down, so the
question stays in the raw mirror above the buttons — the same contract
as Claude's prompt-select. The dialog signature spans the whole card;
the bridge race guard refuses a stale press (409) after the screen
moves.

Journal: bridge/journal/grok.ts reads chat_history.jsonl from
$GROK_HOME (override: COLLIE_GROK_ROOT) via containedRealpath, refusing
non-UUID refs.

Neutral-model changes riding along: PromptFeedback.purpose (free-text
rows render no feedback composer and submitPromptFeedback refuses
them), and PromptOption.keyLabel (the badge shows the digit when a
recovery key precedes it). /auto joins the dangerous two-tap set — it
changes approval behavior. Claude behavior unchanged, pinned by the
existing claude suites.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.
AltanS pushed a commit that referenced this pull request Aug 31, 2026
Codex panes get the Claude-level treatment: the boxless chrome stripped
and re-surfaced natively, plus native buttons for the three probed
dialogs.

Registered by exact agent string only (the #99 bar):
adapterFor stays Object.hasOwn over adapter.agent; variant spellings
stay raw. Every keystroke the adapter emits comes from a dated live
probe recorded in the notes files, against a byte-faithful fixture
corpus (identifying strings length-preservingly genericized — public
repo), with describeAdapterConformance running codex against the claude
and omp cohorts and vice versa.

Chrome (Tier 1): Codex paints a bare `› ` prompt row that word-wraps
onto indented continuation rows, a dot-separated status row beneath,
and exactly one blank row between every section. The composer is
located by the status row at the tail (the ` · Context N% left` token;
everything before it is opaque — models and paths change) with the
prompt row above it — a transcript echo of a submitted message paints
the same `› ` prefix and never has the status row beneath it. The
status row feeds the strip, the prompt row the draft (wrapped drafts
rejoin with single spaces, verified against the typed original) and
composerPrompt.

Trust prompt: the exact captured two-option layout lifts as family
trust. Digits confirm directly — 2 quit on the spot, 1 accepted through
the guarded send path (both probed).

Exec approval: classified, not layout-pinned — the first row must be
the one-shot Yes, the last the reject, and every row between must prove
itself persistent (`don't ask again …`); those never become buttons.
An unclassified row refuses the whole card. Digits confirm directly:
1 ran the approved command, 3 rejected it with the command verified
never run. The header, Reason and `$ command` rows stay in the raw
mirror above the buttons. Only the exec approval is captured; other
approval kinds fail closed until probed. Note the config dependency:
approvals_reviewer = "auto_review" (the host default here) approves
silently and the dialog never paints — probing used
`-c approvals_reviewer=user`.

request_user_input: the Question X/Y card lifts as family select, one
digit per row — a digit answers the current question, advances a
multi-question set, and submits on the last (all probed). The
notes-box state refuses to raw (footer `tab or esc to clear notes`, or
the `› Add notes` row): a digit there would type into the box. Esc
interrupts the whole conversation and is never emitted.

Live-verified end to end through the bridge on a real pane: trust
accepted, a reply typed-verified-submitted through the new draft
extraction, an approval Yes landing its command, and an ask answer
registering in the tool result — every press through the guarded /keys
endpoint with its region signature.

Versions untouched per the fork-PR rule; CHANGELOG line in the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd
AltanS added a commit that referenced this pull request Aug 31, 2026
 feat: add AGY (Antigravity) harness adapter, interactive prompts, and slash commands
AltanS added a commit that referenced this pull request Aug 31, 2026
Brings #99 (AGY / Antigravity as a first-class harness adapter: ask_question menus,
permission, plan and trust dialogs lifted into native buttons, the boxed composer
stripped with its status row re-surfaced, a slash-command palette and the brand
icon), #132 (a large Codex send verified through `[Pasted Content N chars]`, and
destructive writes bound to the whole wrapped draft), #134 + the 0.150.1 status-row
work (Codex CLI 0.150.1 recognised on both of its rows, keyed on the renderer's own
paint) and #130 (a forward-auth 3xx read as a 401, and Authentik's outpost paths kept
out of the PWA cache) onto the v1 line.

Conflicts resolved:
- version trio: v1's 1.0.0-beta.32 kept; nothing else in those three files differed.
- CHANGELOG: both sides kept — v1's beta entries on top, 0.36.0 in chronological
  place directly above 0.35.0. Nothing rewritten.
- web/src/components/agent-icon-data.ts: v1's Map shape kept; main's agy and
  antigravity brand entries filed in it, and the source note extended to name them.
- web/src/lib/agent-commands.ts: v1's Map-shaped CATALOG kept; main's AGY catalog
  filed under both "agy" and "antigravity", the two spellings AGENT_FAMILIES lists
  and canonicalAgent folds neither onto the other.
- web/src/lib/api.ts: main's REDIRECT_STATUSES, normaliseProxyRedirect, apiFetch and
  the manual-redirect paragraph taken; v1's withScope naming kept over main's
  withSession. v1's fourth bridge call site, the /api/stt POST, routed through
  apiFetch too — it bypasses `req` like the other two and returns its refusal as a
  value, so a proxy 3xx has to reach it as a plain 401 as well. Every bridge call in
  the file now goes through apiFetch.

Imported code brought up to v1's gates: codex/markers.ts scans for a C0/DEL byte by
code unit instead of matching a control-character class, the way links.ts cuts the
same bytes out of an href; codex.test.ts's `row` helper keeps its inferred return
type rather than restating it as an anonymous object; server.test.ts's new
expected_prompt case passes its fake through v1's `asMux` seam instead of main's
`as unknown as HerdrClient` chain. api.test.ts gains one case for the /api/stt
redirect path, which v1 owns alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nmindz added a commit to nmindz/collie that referenced this pull request Sep 5, 2026
`DEFAULT_UPDATE_REPO` is the ONE place `cli/install-kind.ts` resolves where
releases come from, and it feeds both halves of the update subject: the SOURCE
on a binary install — it selects the tags endpoint and every constructed
download URL — and the ASSERTION on the git paths. Left at `AltanS/collie` in a
fork, the phone's update banner offers upstream's release, which does not carry
the four environment patches this checkout exists to hold, and
`scripts/install.sh` fetches upstream's tarball.

So the default names this fork. The override is untouched: upstream built
`COLLIE_UPDATE_REPO` as the fork seam and it still wins, so pointing a build
back at upstream stays one variable rather than a revert.

Changed alongside it, because each names this repo's own identity rather than
citing upstream's history:

  - `bridge/index.ts` — the release check behind the phone's banner.
  - `scripts/install.sh` — the binary installer's default repo.
  - the three `herdr plugin install <repo> --yes` recovery lines in
    `cli/update.ts` and the same string in `contrib/windows/collie-ctl.ps1`:
    they tell the operator how to refresh THIS install.
  - two comments that name the checkout's own origin.

Deliberately NOT changed: every reference that cites upstream's history rather
than naming a code source — `CHANGELOG.md`'s hundred commit links, the `.adr/`
records, and the `AltanS#34`/`AltanS#63`/`AltanS#89`/`AltanS#99`/`AltanS#103` issue citations in `docs/`,
`PLAN_FEEDBACK_NOTES.md` and `harness/registry.ts`. Those numbers belong to
AltanS/collie and resolve to different issues here, and rewriting the changelog
would conflict on every upstream release while fixing nothing.

Tests move with the strings they pin. The origin stubs in `update.test.ts`,
`update-check.test.ts` and `doctor.test.ts` stage a checkout whose `origin`
agrees with the configured source, so they had to follow: without that, 86 cases
across five files fail on `assertOrigin` refusing before the case under test
begins.

One consequence, recorded rather than worked around: the git remote named
`origin` in this checkout is still upstream, so `collie update` now REFUSES on
the origin/source mismatch instead of silently detaching onto an upstream tag
and discarding these patches. That is the safer of the two failures and it is
the guard doing its job. Making the verb usable means naming this fork `origin`
and upstream something else.
nmindz added a commit to nmindz/collie that referenced this pull request Sep 5, 2026
`DEFAULT_UPDATE_REPO` is the ONE place `cli/install-kind.ts` resolves where
releases come from, and it feeds both halves of the update subject: the SOURCE
on a binary install — it selects the tags endpoint and every constructed
download URL — and the ASSERTION on the git paths. Left at `AltanS/collie` in a
fork, the phone's update banner offers upstream's release, which does not carry
the four environment patches this checkout exists to hold, and
`scripts/install.sh` fetches upstream's tarball.

So the default names this fork. The override is untouched: upstream built
`COLLIE_UPDATE_REPO` as the fork seam and it still wins, so pointing a build
back at upstream stays one variable rather than a revert.

Changed alongside it, because each names this repo's own identity rather than
citing upstream's history:

  - `bridge/index.ts` — the release check behind the phone's banner.
  - `scripts/install.sh` — the binary installer's default repo.
  - the three `herdr plugin install <repo> --yes` recovery lines in
    `cli/update.ts` and the same string in `contrib/windows/collie-ctl.ps1`:
    they tell the operator how to refresh THIS install.
  - two comments that name the checkout's own origin.

Deliberately NOT changed: every reference that cites upstream's history rather
than naming a code source — `CHANGELOG.md`'s hundred commit links, the `.adr/`
records, and the `AltanS#34`/`AltanS#63`/`AltanS#89`/`AltanS#99`/`AltanS#103` issue citations in `docs/`,
`PLAN_FEEDBACK_NOTES.md` and `harness/registry.ts`. Those numbers belong to
AltanS/collie and resolve to different issues here, and rewriting the changelog
would conflict on every upstream release while fixing nothing.

Tests move with the strings they pin. The origin stubs in `update.test.ts`,
`update-check.test.ts` and `doctor.test.ts` stage a checkout whose `origin`
agrees with the configured source, so they had to follow: without that, 86 cases
across five files fail on `assertOrigin` refusing before the case under test
begins.

One consequence, recorded rather than worked around: the git remote named
`origin` in this checkout is still upstream, so `collie update` now REFUSES on
the origin/source mismatch instead of silently detaching onto an upstream tag
and discarding these patches. That is the safer of the two failures and it is
the guard doing its job. Making the verb usable means naming this fork `origin`
and upstream something else.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants