Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f4d1120
feat(ai-harness, ai): protocol v2 frames with error codes and request…
AlemTuzlak Oct 17, 2026
27ed0b3
docs(ai-harness): first draft of the harness protocol page
AlemTuzlak Oct 17, 2026
cfea3ab
Merge branch 'feat/harness-log-session' into feat/harness-reporting
AlemTuzlak Oct 17, 2026
2051764
feat(ai-harness): one wire encoding on every transport with hello, ve…
AlemTuzlak Oct 17, 2026
171b1d5
test(e2e): the access spec skips the hello frame of the session stream
AlemTuzlak Oct 17, 2026
04b667b
feat(ai-harness): a grouped stream option for the heartbeat, the slow…
AlemTuzlak Oct 18, 2026
da677f0
Merge branch 'feat/harness-handoffs' into feat/harness-reporting
AlemTuzlak Oct 18, 2026
d3d9d8d
feat(ai-harness): run tags on every event frame, ctx.model.chat for p…
AlemTuzlak Oct 18, 2026
22d1caf
Merge branch 'feat/harness-coding-workspace' into feat/harness-reporting
AlemTuzlak Oct 18, 2026
f07d16c
feat(ai-harness): lifecycle events, counted router and transfer model…
AlemTuzlak Oct 18, 2026
e59918d
Merge branch 'feat/harness-coding-workspace' into feat/harness-reporting
AlemTuzlak Oct 18, 2026
2ab325f
docs(ai-harness): the harness protocol page, keeping the stream open,…
AlemTuzlak Oct 18, 2026
4169f3d
docs(ai-harness): run tags and the gap reload in the custom UI page
AlemTuzlak Oct 18, 2026
8bf66f3
test(e2e): the wire, the gap, the run tree in one stream, lifecycle e…
AlemTuzlak Oct 18, 2026
b742c17
test(ai-harness): give polled checks room for slower CI coverage runs
AlemTuzlak Oct 18, 2026
0dd6227
feat(ai-mcp, ai-acp, ai-code-mode, ai-skills, ai-harness-cli, ai-harn…
AlemTuzlak Oct 19, 2026
d2e1a2a
fix(ai-harness): use the real OpenTelemetry types, guard a tracer tha…
AlemTuzlak Oct 19, 2026
f8f42f1
fix(ai-harness): trim the protocol to one version check, put the pare…
AlemTuzlak Oct 19, 2026
6385462
fix(ai-harness): no usage event after a run finished, and bySource on…
AlemTuzlak Oct 19, 2026
e095e8c
fix(ai-harness, ai-persistence): read takeover and open questions fro…
AlemTuzlak Oct 19, 2026
3c89fc4
test(ai-harness): an append is an input, so a non-owner forwards it a…
AlemTuzlak Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .changeset/ai-harness-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

Add `@tanstack/ai-harness-cli`: `runCli(harness)` runs a harness from the terminal.

- Line mode reads one message or `/command` per line, hides a secret answer while the user types, and sends `@path` files. When a turn waits, each next line answers one wait: `y` or `n` for an approval (`once`, `always`, or `reject` for an approval of `permissions()`), and an answer read with the schema for a question. Pass `ui` to show your own screen on a session view instead.
- Line mode reads one message or `/command` per line, hides a secret answer while the user types, and sends `@path` files. In a terminal, `/cancel` and a message typed while a turn runs act at once: the message joins the running turn. When a turn waits, each next line answers one wait: `y` or `n` for an approval (`once`, `always`, or `reject` for an approval of `permissions()`), and an answer read with the schema for a question. Pass `ui` to show your own screen on a session view instead.
- `-p "prompt"` runs one prompt for scripts and CI, and `--output ndjson` prints every AG-UI event. `-p` exits with code 3 (`EXIT.needsAction`) at the first wait of the turn: an approval, a question, or a sign-in. The exit code for this case changed from 2 to 3. A script that checks for exit code 2 must check for 3.
- `--serve` serves the session protocol over HTTP behind a bearer token.
- `--acp`, `--mcp`, and `--dashboard` load `@tanstack/ai-acp`, `@tanstack/ai-mcp`, and `@tanstack/ai-dashboard` only when you use the flag. A missing package, or one without harness support, fails that flag with a clear message.
- `--acp` and `--mcp` load `@tanstack/ai-acp` and `@tanstack/ai-mcp` only when you use the flag. A missing package, or one without harness support, fails that flag with a clear message. Any other error while the package loads is reported, not hidden. The `--dashboard` flag was removed.
- In every mode, the first SIGINT or SIGTERM cancels the running turn. A second one, or one while no turn runs, stops the CLI, and `runCli` resolves to 130 (SIGINT) or 143 (SIGTERM). The CLI closes a host that it made with `recoverable: true`, or releases its session handle on your own host. The `signals` option takes another signal source, for example an `EventEmitter` in a test.
- `host` and `principal` options let the CLI use your durable host and run as your user. Every mode except `--dashboard` opens its sessions as that principal. On a host with `access`, `--serve` reads the user of each request with `identify`, and `--mcp` does not start, because stdio has no login.
- `host` and `principal` options let the CLI use your durable host and run as your user. Every mode opens its sessions as that principal. On a host with `access`, `--serve` reads the user of each request with `identify`, and `--mcp` does not start, because stdio has no login.
8 changes: 4 additions & 4 deletions .changeset/ai-harness-coding-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Add coding tools in the new Node entry `@tanstack/ai-harness/plugins/coding`:

- `workspaceTools({ root })` gives `read_file` (text pages, images, PDFs), `write_file`, `edit_file` or `patch` (`editStyle`), `list_files`, `grep`, `bash` (with `background`), `webfetch`, and `websearch` (with a `SearchProvider`). With `permissions()`, edits, `bash`, and `webfetch` ask first, and the rules also apply to the real path of a link. `outside: 'ask'` makes a path outside `root` need an approval. `permissions()` decides outside paths and the real paths of links before the call runs, so a tool never asks while it runs. The `cwd` thread setting gives each thread its own working folder.
- `workspaceTools({ workspace: { bind, reattach? } })` gives each thread its own workspace. The root of the workspace is the boundary of every tool. The session log keeps where the workspace is (`harness.workspace.bound`). On another host, `reattach` gives the workspace back (for example a resumed sandbox), or `'pinned'` or `'gone'`. A gone workspace sends `tanstack/workspace-tools:stranded`.
- `workspaceTools({ workspace: { bind, reattach? } })` gives each thread its own workspace. The root of the workspace is the boundary of every tool. The session log keeps where the workspace is (`harness.workspace.bound`). On another host, `reattach` gives the workspace back (for example a resumed sandbox), or `'pinned'` or `'gone'`. A gone workspace sends `tanstack/workspace-tools:stranded`. With this plugin first, `snapshots()` and `formatter()` use the workspace of each thread, and their `root` is optional.
- `createHarnessHost({ ownership: { hostId } })` gives a host a stable id. Then a thread that uses a folder on that host is pinned to it, and other hosts refuse its tool calls. Without `hostId`, threads are not pinned.
- Commands, `rg`, `git`, and the formatters get only a short list of variables of the server process, plus the `env` of the backend call. Git runs without the hooks and the `fsmonitor` of the project.
- The tools resolve links with `realpath` under the file lock, and fail when a backend cannot resolve a path. A write fails when the file changed after the tool read it. The file locks are shared by the sessions of a host.
- Background jobs: at most 8 per session (`maxJobs`), the newest 1 MiB of output per job, and the events `tanstack/workspace-tools:job-started`, `job-output`, and `job-ended`. A job that an earlier host left running is stopped at the next tool call, with a note to the model.
- Background jobs: at most 8 per session (`maxJobs`), the newest 1 MiB of output per job, and the events `tanstack/workspace-tools:job-started`, `job-output`, and `job-ended`. A job that an earlier host left running is stopped at the next tool call, with a note to the model. The wake turn of a job runs as the user who started it. The `bash` spill file has `[redacted]` in place of each secret of the session.
- `read_file` sends images and PDFs up to 256 KB. `webfetch` gives the model the first 256 KB of a page.
- A `WorkspaceBackend` runs the tools. `hostBackend` is the default. `realpath` is required. `spawn` can give a `jobId`, and `killJob`, `binding`, and `close` are optional. On Windows, commands do not run a program from the working folder by its name.
- `snapshots()` adds `/undo` and `/redo`, which restore only the files of the turn (git 2.26 or newer), file restore for `session.revert()`, and a diff for each model step. Each thread of each tenant has its own shadow repository, which never stores `.env` files. A step keeps only the files of its own run. The steps and the undo point are in the session log, so another host with the same repository can undo. A revert uses the last 500 steps. `purgeSnapshots()` removes the repository of a thread. A failed snapshot sends `tanstack/snapshots:failed`.
- `formatter()` formats each written file with the formatter of the project. With `permissions()`, the rules decide the formatter with the call that writes, before that call runs: one approval covers both, and the formatter runs after the write.
- `snapshots()` adds `/undo` and `/redo`, which restore only the files of the turn (git 2.26 or newer), file restore for `session.revert()`, and a diff for each model step. Each thread of each tenant has its own shadow repository, which never stores `.env` files. A step keeps only the files of its own run. The steps and the undo point are in the session log, so another host with the same repository can undo. A revert uses the last 500 steps. `host.sessions.delete` removes the repository of the thread, and `purgeSnapshots()` does it from your own code. A failed snapshot sends `tanstack/snapshots:failed`.
- `formatter()` formats each written file with the formatter of the project. With `permissions()`, the rules decide the formatter with the call that writes, before that call runs: one approval covers both, and the formatter runs after the write. An `always` answer is tied to a hash of the formatter config files. A write of a formatter config below the root, or under `node_modules`, asks each time, and an `always` answer saves no rule for that formatter run.
- `WorkspaceHooks` lets other plugins run code after a read or a write.

`@vscode/ripgrep` and `turndown` are optional peer dependencies.
Loading
Loading