# agent-signals: all pages This file holds the Markdown source of each page of https://xsavikx.github.io/agent-signals/, in the order of llms.txt. A line "Source:" starts each page. --- Source: https://github.com/xSAVIKx/agent-signals/blob/main/README.md # agent-signals Measured signals of AI coding agents, as a language-neutral test corpus. Tools that watch or drive an agent session read the same signals: the terminal screen of the agent, the payloads of its hooks, its session files, and its log lines. Each tool learns the shape of these signals again, and each tool breaks again when an agent changes its screen. This repository holds the measured signals once, with the result that each signal must give. Every tool tests its own reader against the same data. The first signals are terminal screens of Claude Code, Antigravity CLI (agy) and Codex CLI in tmux. The corpus also holds the first screen with no login of each other agent in [the roadmap](docs/roadmap.md). Hook payloads, session files and log lines follow. ## Who it is for - Authors of tools that watch or drive an agent session in a terminal. aw and tg-bridge are two such tools. Each tool tests its own reader against the same screens and facts. - Agents that do a job in this repository. Each job has a [task file](tasks/). - Operators who must know how an agent behaves on an OS before they automate it. ## How to use the corpus 1. Pin a corpus release tag, `corpus-vX.Y.Z`, from the [releases on GitHub](https://github.com/xSAVIKx/agent-signals/releases). 2. Copy `corpus/`, `schema/` and `facts/` from that tag into your tree. 3. Run one table test: for each screen, give the `.screen` bytes to your reader, and compare the result with `expect` in the `.yaml` file. The corpus holds no code that your tool runs. Each tool keeps its own reader. ## Run the probe kit on your machine Run the kit with one command. It downloads the kit of the latest release (about 30 KB) to a temporary folder, runs the probes, writes one file in the current folder, and deletes the temporary folder. | OS | Command | |---|---| | macOS, Linux | `curl -fsSL https://xsavikx.github.io/agent-signals/kit.sh \| sh` | | Windows (PowerShell) | `irm https://xsavikx.github.io/agent-signals/kit.ps1 \| iex` | | A guided step (macOS, Linux) | `curl -fsSL https://xsavikx.github.io/agent-signals/kit.sh \| sh -s -- --guided permission agent` | Read the scripts first if you want: [`probes/kit.sh`](probes/kit.sh) and [`probes/kit.ps1`](probes/kit.ps1). The kit only reads. Read the file that it writes before you send it. ## What is in the repository | Path | Content | |---|---| | `corpus//screens/` | One measured screen per pair of files: `.screen` and `.yaml`. | | `corpus//coverage.yaml` | The agent versions that were checked against the corpus. | | `schema/` | JSON Schema for each `.yaml` file. CI validates every file. | | `facts/` | Measured facts and the incidents behind them, one file per fact. | | `protocol/` | Rules for tools that answer an agent, for example the safe way to type into a pane. | | `probes/` | Read-only scripts for the first discovery of an agent on an OS, and the kit that runs them on a real machine. They read no conversation text. | | `scenarios//.sh` | Scripts that start an agent in a throwaway home folder and a private tmux server, and record its signals. | | `tools/` | The validator, the sync scripts for the Go and the Python libraries, the scrub for probe output, and the generator of the website. | | `go/` | The Go library. It holds a committed copy of `corpus/` and `facts/` in `go/data/`. | | `python/` | The Python library. It holds a committed copy of `corpus/` and `facts/` in `python/src/agent_signals/data/`. | | `docs/` | The format and the versioning rules, the roadmap, the checks for each OS, and the CI secrets. | | `AGENTS.md`, `tasks/` | Instructions for agents, and one task file for each job. | The Go library in `go/` and the Python library in `python/` load the corpus and give typed fixtures. No library is needed to use the data. Read [the guide to the Go library](go/README.md) and [the guide to the Python library](python/README.md). ## A screen A `.screen` file holds the exact output of `tmux capture-pane -p` (or `capture-pane -p -e` when the style matters), in UTF-8, with the trailing newline kept. Do not let an editor change these files. `.editorconfig` and `.gitattributes` keep the bytes as they are. The `.yaml` file beside it records the agent, the agent version, the pane size, how the screen was made, and the expected result. Read [`docs/format.md`](docs/format.md) for each field. ## Releases release-please makes each release from the Conventional Commits on `main`. The repository has three packages, and each package has its own tag and changelog: | Package | Tag | Changelog | |---|---|---| | The corpus | `corpus-vX.Y.Z` | [`CHANGELOG.md`](CHANGELOG.md) | | The Go library | `go/vX.Y.Z` | [`go/CHANGELOG.md`](go/CHANGELOG.md) | | The Python library | `python-vX.Y.Z` | [`python/CHANGELOG.md`](python/CHANGELOG.md) | The agent versions are data in the corpus, not the version of a package. Read [`docs/versioning.md`](docs/versioning.md) and the [releases on GitHub](https://github.com/xSAVIKx/agent-signals/releases). ## Develop ```sh cd tools && go test ./... && go run ./cmd/validate .. cd go && go vet ./... && go test ./... pip install -e "python[test]" && pytest python ``` After a change in `corpus/` or `facts/`, run `tools/sync-go-data.sh`. It also runs `tools/sync-python-data.sh`. Commit `go/data/` and `python/src/agent_signals/data/`. The Go tests and the Python tests fail when a copy is stale. ## The website The website at shows the Markdown files, the facts and the corpus. The Markdown files stay the source. The pages workflow builds the site and deploys it from `main`. To build it on your machine: ```sh cd tools && go run ./cmd/site -root .. -out ../_site python3 -m http.server 8000 --bind 127.0.0.1 --directory ../_site ``` ### For agents and programs The website also gives the data in forms that a program can read: | URL | Content | |---|---| | [`llms.txt`](https://xsavikx.github.io/agent-signals/llms.txt) | An index of the site for language models, in the [llms.txt](https://llmstxt.org/) format. | | [`llms-full.txt`](https://xsavikx.github.io/agent-signals/llms-full.txt) | All Markdown pages in one file. | | [`corpus.json`](https://xsavikx.github.io/agent-signals/corpus.json) | Each agent, its verified versions, and each screen with its fields and the URLs of its files. | | [`facts.json`](https://xsavikx.github.io/agent-signals/facts.json) | Each fact with its number, date, agents and URLs. | | `.md` | The Markdown copy of each page that comes from a Markdown file. | Each screen also has its `.screen` and `.yaml` files beside its page, for example `corpus/claude/.screen`. ## Writing style All prose uses ASD-STE100 Simplified Technical English. Code, commands, paths and quoted output stay exact. ## Maintainer Yurii Serhiichuk maintains this repository. Read more at [serhiichuk.dev](https://serhiichuk.dev/). --- Source: https://github.com/xSAVIKx/agent-signals/blob/main/AGENTS.md # Instructions for agents This repository holds measured signals of AI coding agents: terminal screens, hook payloads, session files and open files. Tools such as aw and tg-bridge test their readers against it. Read `CLAUDE.md` first. Its rules apply to every agent, not only to Claude: no secrets, no host details, no session details, Conventional Commits, and prose in ASD-STE100 Simplified Technical English. ## Read the data from outside the repository An agent that only reads the data does not need a clone. Start with . It links `corpus.json`, `facts.json`, and a Markdown copy of each page. ## Jobs Each job has a task file. Follow the task file step by step. | Job | Task file | |---|---| | Run the probe kit on a real machine and send the file | `tasks/run-the-kit.md` | | Write a fact from probe or scenario output | `tasks/write-a-fact.md` | | Add a probe for a new agent or a new OS | `tasks/add-a-probe.md` | | Add an agent to the roadmap, the probes and CI | `tasks/add-an-agent.md` | ## Where things are | Path | Content | |---|---| | `docs/roadmap.md` | The agents to cover, in order, and the state of each cell of the matrix. | | `docs/detection.md` | How a tool finds an agent process, links it to its session and reads its state. | | `docs/os-checks.md` | For each OS: how to see a process, its terminal, its open files and its folder. | | `docs/ci-secrets.md` | The repository secrets that the model scenarios need. | | `probes/README.md` | The rules for a probe, and the kit. | | `scenarios/README.md` | The rules for a scenario. | | `facts/README.md` | The parts of a fact. | | `docs/format.md` | The format of a screen. | ## Checks before a commit 1. `cd tools && go test ./... && go run ./cmd/validate ..` 2. `sh tools/sync-go-data.sh`, then `cd go && go test ./...` and `pytest python` in a virtual environment with `pip install -e "python[test]"`. The sync script also makes the copy for the Python library. 3. `shellcheck --severity=warning` on each changed `.sh` file. 4. `sh tools/test-scrub.sh` when the scrub changed. --- Source: https://github.com/xSAVIKx/agent-signals/blob/main/docs/format.md # The format of a screen A screen is two files in `corpus//screens/`, with the same slug: - `.screen`: the exact bytes. - `.yaml`: the metadata and the expected result. `schema/screen.schema.json` is the full definition. This page explains it. ## The `.screen` file 1. It holds the exact output of `tmux capture-pane -p`. When the style matters, it holds the output of `capture-pane -p -e`, with SGR escape codes. 2. It is UTF-8, with LF line endings, and the trailing newline is kept. 3. Do not reformat it. An editor that strips trailing white space changes the screen without a sign. `.editorconfig` and `.gitattributes` prevent this. ## The `.yaml` file ```yaml schema: 1 agent: claude # equals the folder name; see the list below agent_version: 2.1.309 valid_from: 2.1.251 # optional superseded_by: null # optional: the slug that replaces this screen measured: 2026-10-24 capture: plain # plain | styled pane: {cols: 120, rows: 40} # required for measured; optional when the size is not known provenance: measured # measured | measured-edited | derived edits: [] # measured-edited: one line for each edit derived_from: hand-built # derived: the source slug and what changed, or hand-built description: > What the screen shows, and why it is in the corpus. expect: state: PERMISSION # IDLE | BUSY | PERMISSION | TRUST | DIALOG_OVER_BOX | NO_BOX | UNKNOWN dialog: question # optional: permission | question | review | trust | none question: "Which colour?" # only where the screen pins it options: - {n: 1, label: Red, selected: true} # n is optional: some menus have no numbers - {n: 2, label: Blue} box_text: null negative_control_for: null # optional: the slug this screen must not be confused with known_false_positive: false # optional known_false_positive_reason: null facts: [7, 15] # optional: the facts this screen pins ``` ## The agents The `agent` field holds the folder name of the agent. The schemas allow these names: | Folder | Agent | |---|---| | `claude` | Claude Code | | `agy` | Antigravity CLI | | `codex` | Codex CLI | | `copilot` | GitHub Copilot CLI | | `gemini` | Gemini CLI | | `cursor` | Cursor CLI | | `opencode` | opencode | | `qwen` | Qwen Code | | `aider` | Aider | | `amp` | Amp | | `goose` | Goose | | `crush` | Crush | | `kimi` | Kimi Code CLI | | `continue` | Continue CLI | | `droid` | Factory Droid | | `cline` | Cline CLI | | `kilo` | Kilo Code CLI | | `auggie` | Auggie | | `openhands` | OpenHands CLI | | `opencode2` | opencode 2 | | `kiro` | Kiro CLI | | `junie` | Junie CLI | | `codebuff` | Codebuff | | `qoder` | Qoder CLI | | `codebuddy` | CodeBuddy Code | | `letta` | Letta Code | | `neovate` | Neovate Code | | `everycode` | Every Code | | `iflow` | iFlow CLI | | `vibe` | Mistral Vibe | | `gptme` | gptme | | `interpreter` | Open Interpreter | | `grok` | Grok CLI | A new agent needs its name in `schema/screen.schema.json` and `schema/coverage.schema.json`. A name that is not in the list is a schema problem. ## The states | State | Meaning | |---|---| | `IDLE` | A framed input box that waits for a message. A reader must prove this state. It must not give `IDLE` because no other rule matched. | | `BUSY` | The agent works on a turn. | | `PERMISSION` | A dialog waits for an answer: a permission, a question, or the review of a question. `dialog` tells them apart. | | `TRUST` | The folder-trust question. | | `DIALOG_OVER_BOX` | A dialog draws above a live input box, and the box keeps its frame. | | `NO_BOX` | The agent has not drawn its input box yet. | | `UNKNOWN` | A screen that the reader cannot read. A tool must send no key to it. | ## Provenance | Value | Meaning | |---|---| | `measured` | The bytes as tmux captured them. | | `measured-edited` | Captured, then edited. Each edit has one line in `edits`, for example "path scrubbed to /work/example". | | `derived` | Hand-built, or made from another screen. `derived_from` names the source and the change. | ## Known false positives Some expected states are wrong on purpose. A dialog quoted in a reply looks like a dialog, and a reader that reads it as a dialog sends no key, which is safe. A rule that reads it as an idle box would also read a real dialog as an idle box. Such a screen has `known_false_positive: true` and a reason. Do not "fix" it. --- Source: https://github.com/xSAVIKx/agent-signals/blob/main/docs/versioning.md # Versions ## The repository version The repository uses semantic versions. release-please makes each release from the Conventional Commits on `main`. The corpus, the Go library and the Python library are three packages with their own tags. On GitHub, the newest corpus release has the mark "Latest". The release workflow sets this mark, because GitHub otherwise gives it to the package that it released last. | Change | Version part | Commit type | |---|---|---| | A breaking change of a schema or of the layout | major | `feat!:` or a `BREAKING CHANGE:` footer | | A new screen, a new agent, a new verified agent version, a new fact | minor | `feat:` | | A correction of a screen, a fact or a document | patch | `fix:` | Before 1.0.0, a breaking change raises the minor part. ## Agent versions are data The agent versions are not the version of the repository, for four reasons: 1. One repository covers several agents, and each agent has its own version line. 2. The corpus changes when no agent changes, for example for a correction or a new field. 3. One screen is often valid across many agent releases. 4. A Go module needs a semantic version tag. The agent versions are recorded in two places: 1. Each `.yaml` file records the `agent_version` that drew the screen. It can also record `valid_from` and `superseded_by`. 2. `corpus//coverage.yaml` lists each agent version that was checked against the corpus, with the date and the method. Each release note names the agent versions that the release covers. ## Tags Each package in the repository has its own version and its own tag. | Package | Path | Tag | |---|---|---| | The corpus (data, schemas, facts, protocol) | `.` | `corpus-v1.2.3` | | The Go library | `go/` | `go/v1.2.3` | | The Python library | `python/` | `python-v1.2.3` | The Go tag has the form `go/vX.Y.Z` because Go finds the version of a module in a subfolder from a tag with that prefix. release-please makes this tag from the package settings `component: go`, `tag-separator: /` and `include-v-in-tag: true`. The default separator is `-`, so the setting is needed. ## The Go library and the corpus The Go library embeds a copy of `corpus/` and `facts/` in `go/data/`. Go cannot embed files from outside the module folder, so the copy is committed. 1. Run `tools/sync-go-data.sh` after each change in `corpus/` or `facts/`. 2. Commit `go/data/` in the same pull request. The test `TestDataIsInSync` in `go/` fails when the copy is stale, so CI catches a missing sync. The root package excludes `go/` and `python/` from its release notes, with `exclude-paths`. A commit that changes only `go/` or only `python/` does not make a corpus release. A change of the corpus also changes `go/data/`. The commit of such a change counts for both packages, so it makes a release of both. The library version is the version of the Go code and of the embedded corpus. It is not the version of an agent. ## The Python library and the corpus The Python package holds a copy of `corpus/` and `facts/` in `python/src/agent_signals/data/`, so that a wheel needs no other file. The copy is committed, as for Go. 1. Run `tools/sync-go-data.sh` after each change in `corpus/` or `facts/`. It also runs `tools/sync-python-data.sh`. 2. Commit `python/src/agent_signals/data/` in the same pull request. The test `test_data_is_in_sync` in `python/tests/` fails when the copy is stale. The tag has the form `python-vX.Y.Z`, from the package settings `component: python` and `release-type: python`. release-please writes the version into `python/pyproject.toml`. The data in a release of the Python library follows the corpus: a change of the corpus makes a release of the corpus and of the Python library together. ## Schema versions Each `.yaml` file has a `schema` field. A breaking change of a schema raises that number and the major part of the corpus version together. --- Source: https://github.com/xSAVIKx/agent-signals/blob/main/docs/os-checks.md # Checks for each OS How a tool or a probe sees an agent process, its terminal, its open files and its folder, on each OS. The column "Measured" says where the row was measured. "Probe" means that a probe uses it and CI runs that probe on a runner with no agent session. ## Linux | Question | Command | Measured | |---|---|---| | Processes and parents | `ps -axo pid=,ppid=,tty=,args=`; `/proc//stat` field 4 | real machine (Debian 13), probe | | Children | `pgrep -P ` | real machine | | Working folder | `readlink /proc//cwd` | real machine | | Open files | `readlink /proc//fd/*` (no `lsof` needed) | real machine (facts 25, 26, 28) | | Terminal | `ps -o tty=`; `readlink /proc//fd/0` | real machine | | Environment (own processes) | `/proc//environ`, NUL-separated. Print only names from a fixed list: the environment can hold secrets. | real machine | | ubuntu-slim runner | No `lsof` and no `tmux`. The probes read `/proc`. | CI runner (fact 49) | | tmux pane of a process | `tmux list-panes -a -F '#{pane_pid} #{pane_id}'`; `TMUX_PANE` in the environment; the `tmux` field of the Claude pid file (fact 23) | real machine | ## macOS | Question | Command | Measured | |---|---|---| | Processes and parents | `ps -axo pid=,ppid=,tty=,args=` | real machine (fact 22), probe | | Children | `pgrep -P ` | probe | | Working folder | `lsof -n -P -a -p -d cwd -Fn` | real machine (aw doctor) | | Open files | `lsof -n -P -p -Fn`. 24 agent processes took 0.7 s. | real machine (facts 21, 22; aw doctor) | | Terminal | `ps -o tty=` | real machine | | Environment (own processes) | `ps eww -o command= -p ` | probe (no session) | | Terminal tab of a tty | Terminal: `tell application "Terminal" to get tty of every tab of every window`; iTerm2: `tty of every session of every tab of every window`. The first call can open an Automation dialog. | real machine, Terminal only (fact 22) | | Bring a tab to the front | Terminal: `set selected of to true`; iTerm2: `select `. The kit step `--guided front-tab` tests it. | not measured | | JetBrains IDE terminal tabs | No known scripting access. `probes/terminals/macos.sh` checks for an AppleScript dictionary. | not measured | | Ghostty, WezTerm, kitty, Warp | `wezterm cli list`; kitty remote control (`kitten @`). | not measured | | tmux | Not on the macOS runner images. The scenarios install it with `brew install tmux`. | CI runners (fact 49) | | `lsof` and `/proc` compared | `lsof -p` lists mapped files (`.node`, `.dylib`) and more; Linux `/proc//fd` lists only open descriptors. | CI runners (fact 50) | ## Windows | Question | Command | Measured | |---|---|---| | Processes and parents | `Get-CimInstance Win32_Process` (`ProcessId`, `ParentProcessId`, `CommandLine`) | probe (no session) | | Working folder | No route without reading the memory of the process. A Claude session shows its folder in its session file. | aw (no route) | | Open files | psutil `Process.open_files()` lists the log files that an agent holds open (Cursor CLI). Sysinternals `handle.exe -p ` is not part of Windows. | CI runners (fact 54) | | Terminal driver | A pseudo console (ConPTY) through pywinpty 3.1.2, read with pyte 0.8.2 (`scenarios/conpty.py`). The console host is the `OpenConsole.exe` of the wheel, a child of the driver, not of the agent. | CI runners (fact 51) | | Home folder of an agent | Most agents read USERPROFILE. Codex CLI ignores USERPROFILE and HOME and writes to the profile of the account, but it uses `CODEX_HOME` when it is set. Open Interpreter, Every Code, Junie CLI and Kiro CLI also write to the profile of the account. Cursor CLI takes the account name from Windows, not from USERNAME. | CI runners (facts 53, 54, 62, 64) | | Terminal | A console process has no tty. The parent chain shows the terminal program: `WindowsTerminal`, `OpenConsole`, `conhost`, `Code`, a JetBrains IDE. | probe (no session) | | Environment of another process | No route without extra tools. | not measured | | psmux (tmux for Windows) | `psmux ls`, `psmux list-panes -a`. Under PowerShell, `send-keys ... C-m` sends a literal `^M`; `Enter` works. | aw (psmux 3.4.0) | | Codex CLI keys | Tab sends a message. Enter and C-j add a new line. | aw (Codex 0.164.0) | | Install folder and version | `Get-Command -All` lists an npm agent as `.ps1`, `.cmd` and ``. The version is in `node_modules\\package.json` below the npm prefix (`C:\npm\prefix` on the runners). The file version of an `.exe` is not always the agent version. | CI runners (facts 47, 48) | ## State folders of each agent The same on Linux and macOS, below the home folder. Windows uses `%USERPROFILE%` for the same names (not measured). | Agent | Session link | Live signal | Transcript | |---|---|---|---| | Claude Code | `~/.claude/sessions/.json` (`sessionId`, `tmux`) | `status` in the same file: `idle`, `busy`, `waiting` (with `waitingFor`), `shell` for a background shell (facts 23, 31) | `~/.claude/projects//.jsonl` | | Codex CLI | `~/.codex/thread-writer-locks/.lock`, open in the daemon (fact 26) | `task_complete`, `turn_aborted` records (fact 27); the `Interrupt` and `Stop` hooks (fact 38) | `~/.codex/sessions/YYYY/MM/DD/rollout-