agent-id-cli 0.4.0

Portable identity registry for coding-agent sessions
Documentation
# Agent ID repository guide

## Documentation boundaries

Keep `README.md` human-facing: what the project does, installation, normal use, and project origins. Keep implementation details and complete command references in this file. Instructions for a running agent belong in the output generated by `src/prime.rs` (`agent-id prime`).

## CLI reference

The binary is installed as `agent-id`; the Rust crate is `agent-id-cli`.

### `agent-id register [SESSION_ID]`

Allocates a permanent identity. Registration is idempotent: an existing session keeps its identity and receives a new `updated_at`.

```text
--family NAME       Prefer a family name, useful for child agents
--realm NAME        Select the computer realm
--session-id ID     Provide the session ID explicitly
--json              Print the complete assignment as JSON
```

### `agent-id lookup [IDENTIFIER]`

Reads an existing identity by session ID, canonical name, or slug. Without an explicit identifier, lookup uses `AGENT_ID_SESSION_ID`.

```text
--session-id ID     Provide the session ID explicitly
--json              Print the complete assignment as JSON
```

### `agent-id current`

Reads the identity for `AGENT_ID_SESSION_ID` without registering a missing session. Use `--json` to print the complete assignment.

```text
--json              Print the complete assignment as JSON
```

### `agent-id annotate [SESSION_ID]`

Updates summary, OMP lifecycle state, working-directory metadata, or namespaced extension metadata independently; omitted fields remain unchanged. The `--state` option writes the OMP signal under `extensions.omp`.

```text
--summary TEXT         Set a concise summary (maximum 240 characters)
--clear-summary        Remove the summary
--state VALUE          Set the OMP lifecycle state signal
--clear-state          Remove the OMP lifecycle state signal
--cwd PATH             Set the current working directory
--clear-cwd            Remove the current working directory
--extension OWNER=JSON Set namespaced unstructured JSON metadata; repeatable
--clear-extension NAME Remove one extension namespace; repeatable
--session-id ID        Provide the session ID explicitly
--json                 Print the complete assignment as JSON
```

### `agent-id discover`

Lists non-stopped assignments by `updated_at`, newest first. Use `--all` to include stopped assignments. The top-level state is materialized from Herdr runtime state first, then the OMP extension state, and otherwise `unknown`. Human-readable results include available summaries, materialized states, and working directories; JSON includes the complete assignments. Inside Herdr, discover queries `herdr api snapshot` and adds a non-persistent `runtime` projection with the Herdr state and locations for assignments whose OMP session-file metadata exactly matches a live Herdr agent.

```text
--limit N           Maximum records (default 20; zero means all)
--recent HOURS      Only records updated within this many hours
--realm NAME        Only records in this realm
--all              Include stopped assignments
--json              Print the complete assignments and available runtime projections as JSON
```

### `agent-id prune`

Removes assignments older than an RFC 3339 cutoff, including their name claims.

```text
--before TIMESTAMP  Delete records updated before this timestamp
--dry-run           Preview matches without deleting
--json              Print the prune report as JSON
```

### `agent-id prime`

Prints the agent-facing workflow and command contract. `--prelude` omits the command reference; `--json` wraps the documentation in a JSON object.

## OMP extension implementation

`extensions/agent-id.ts` is the companion adapter. It reads the authoritative session ID and working directory from OMP, invokes the `agent-id` binary from `PATH` with explicit arguments, parses the returned assignment, and manages session lifecycle and summaries. OMP lifecycle signals are stored under the `extensions.omp` namespace.

| OMP event | Extension behavior |
|---|---|
| Session start, switch, branch, or tree navigation | Looks up or registers the identity, records the OMP session file and working directory, and publishes `idle` under `extensions.omp`. |
| Agent turn starts | Refreshes the OMP session file and publishes `working` under `extensions.omp`. |
| Agent turn ends | Publishes `idle` under `extensions.omp` and may refresh its current-work summary. |
| Tool call | Injects `AGENT_ID_SESSION_ID` only into matching `agent-id current` invocations through OMP's Bash tool. |
| Session shuts down | Publishes `stopped` under `extensions.omp`. |

On session start, switch, branch, and tree navigation, the extension inserts one hidden persistent context message per branch pointing agents to `agent-id prime` and `agent-id current --json`. It checks the stable message type before insertion so resumed branches reuse the existing prompt prefix.

The extension does not register an identity tool. For matching `agent-id current` invocations through OMP's Bash tool, the extension injects the current session ID as `AGENT_ID_SESSION_ID`. It preserves a caller-provided value and does not modify the parent shell or unrelated Bash commands.

After completed turns, the extension can derive up to three successful summaries. Each completion receives only the previous summary, latest request, and latest reply; it uses the `@tiny` model role with `@smol` as fallback, caps output at 80 characters, and persists through `agent-id annotate`. Summary generation state is stored as a session entry and restored across resume, branch, and tree navigation. Missing model access does not prevent registration or lifecycle updates.

Lifecycle signals are `working`, `idle`, `waiting`, `blocked`, and `stopped`; `unknown` is the materialized fallback when no signal is available. Agent ID generates signal timestamps. The top-level `state` is not persisted as a source field.

## Registry implementation

The registry root is `$AGENT_ID_HOME` when set, otherwise `$XDG_DATA_HOME/agent-id`, or `$HOME/.local/share/agent-id` when `XDG_DATA_HOME` is unset.

Session records live at `by-session/<session-id>.json`; session IDs must be filename-safe. Name claims live under `by-name/`. The assignment stores the permanent name and slug, realm, optional timestamped summary, working-directory metadata, and optional timestamped extension-owned JSON metadata. OMP lifecycle state is stored in `extensions.omp.data.state`.

Realm resolution order is `--realm`, `AGENT_REALM` for tests or overrides, then `$XDG_CONFIG_HOME/agent-id/realm` with `$HOME/.config/agent-id/realm` as fallback. If no configuration exists, registration selects a realm and persists it for future sessions on the machine.

Summary, working-directory, and extension fields update independently of the permanent name. Extension owners are bounded lowercase namespaces; each update atomically replaces one owner's JSON value. The OMP extension writes its absolute session file and lifecycle state under `extensions.omp`; discover materializes the top-level state from the Herdr runtime projection when present, then the OMP signal, then `unknown`.

## Release workflow

When the user says “Let's do the release workflow,” drive the release through publication:

1. Confirm the worktree contains only the intended release changes, the branch is `main`, and the `v<version>` tag does not already exist.
2. Update the version in `Cargo.toml` and `package.json`, then run Cargo without `--locked` once to refresh the root `agent-id-cli` entry in `Cargo.lock`.
3. Run `cargo fmt --all -- --check`, `cargo test --all --locked`, `bun test extensions/agent-id.test.ts`, and `cargo package --locked --allow-dirty`.
4. Commit all release changes with a Conventional Commit message, the relevant `SQ-Task` trailer, and the required OMP/model co-author trailers.
5. Push `main`, create and push the annotated `v<version>` tag, then publish a GitHub release for that existing tag with generated notes. A tag push runs only the version check; publishing the GitHub release triggers macOS artifacts, crates.io publishing, and the Homebrew tap update.
6. Watch the release-triggered `Publish` workflow until every required job succeeds. Verify the GitHub release has both macOS archives and checksums; report whether crates.io and the Homebrew tap published successfully.

Required repository secrets are `CARGO_REGISTRY_TOKEN` and `HOMEBREW_TAP_TOKEN`.