Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3906d0a
feat(ai-harness): serve sessions over HTTP and WebSocket, with a type…
AlemTuzlak Oct 13, 2026
d23434e
feat(ai-harness): build a harness into a worker artifact, and run it …
AlemTuzlak Oct 13, 2026
cac83c0
Merge remote-tracking branch 'origin/feat/harness-core' into feat/har…
AlemTuzlak Oct 13, 2026
178a4d6
fix(ai-harness): add back sessionOfTurn for the run route, and the OA…
AlemTuzlak Oct 13, 2026
effa81f
fix(ai-harness): import ./session/index, so the built types resolve H…
AlemTuzlak Oct 13, 2026
f919d91
test(ai-harness): the stub worker ignores cancel and reads content parts
AlemTuzlak Oct 13, 2026
e2a4094
Merge remote-tracking branch 'origin/feat/harness-transport' into fea…
AlemTuzlak Oct 13, 2026
10c9cba
Merge remote-tracking branch 'origin/feat/harness-core' into feat/har…
AlemTuzlak Oct 13, 2026
fb113b1
Merge remote-tracking branch 'origin/feat/harness-transport' into fea…
AlemTuzlak Oct 13, 2026
4a95c9e
test(ai-harness): allow Node modules in the build and worker entries
AlemTuzlak Oct 13, 2026
9242847
test(ai-harness): startLoopbackReceiver cases from #1555
AlemTuzlak Oct 13, 2026
010af53
Merge remote-tracking branch 'origin/feat/harness-transport' into fea…
AlemTuzlak Oct 13, 2026
c051417
Merge branch 'feat/harness-core' into feat/harness-transport
AlemTuzlak Oct 14, 2026
cbc4184
feat(ai-harness, ai-mcp): MCP servers in a harness, and a harness as …
AlemTuzlak Oct 14, 2026
5d9ccf9
feat(ai-harness): GET agent-transcript and client.agentTranscript
AlemTuzlak Oct 14, 2026
28d86c6
Merge branch 'feat/harness-core' into feat/harness-transport
AlemTuzlak Oct 14, 2026
9e88d6c
docs(ai-harness): list GET agent-transcript in the handler routes
AlemTuzlak Oct 14, 2026
fb14c5d
Merge branch 'feat/harness-core' into feat/harness-transport
AlemTuzlak Oct 14, 2026
6b15ac2
Merge branch 'feat/harness-core' into feat/harness-transport
AlemTuzlak Oct 14, 2026
f6561e4
docs(ai-harness): describe the card in client.agentTranscript
AlemTuzlak Oct 14, 2026
9f5ae29
Merge branch 'feat/harness-core' into feat/harness-transport
AlemTuzlak Oct 14, 2026
1b255c8
docs(ai-harness): read a child's messages from the browser
AlemTuzlak Oct 14, 2026
8264c81
Merge remote-tracking branch 'origin/feat/harness-transport' into fea…
AlemTuzlak Oct 14, 2026
0c62cdf
feat(ai-harness): createSessionView, one live store for a session in …
AlemTuzlak Oct 14, 2026
4348362
Merge remote-tracking branch 'origin/feat/harness-core' into feat/har…
AlemTuzlak Oct 14, 2026
18ad485
fix(ai-harness, ai-mcp): adopt object params from #1779
AlemTuzlak Oct 14, 2026
322b81b
docs(ai-harness): object params in the transport, view, MCP, and auth…
AlemTuzlak Oct 14, 2026
1e85a42
test(ai-harness): rename, update, and fork a session through the client
AlemTuzlak Oct 14, 2026
fa2076f
Merge remote-tracking branch 'origin/feat/harness-transport' into fea…
AlemTuzlak Oct 14, 2026
d932ea6
test(ai-harness): a remote harness response with no body fails the run
AlemTuzlak Oct 14, 2026
5f40cef
test(ai-harness): pass canAccess in the remote harness tests (#1783)
jherr Oct 15, 2026
7f137ea
Merge remote-tracking branch 'origin/feat/harness-core' into feat/har…
AlemTuzlak Oct 15, 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
5 changes: 5 additions & 0 deletions .changeset/harness-build-worker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/ai-harness': minor
---

`@tanstack/ai-harness/build` and `@tanstack/ai-harness/worker`: `buildHarness` bundles a harness with Bun into a worker artifact (`harness.js` and a `harness.manifest.json` with the sha256 digest), and can also build a single executable. `artifactText(dir)` checks the digest and uses the artifact as the model of a `chat()` call, with one worker process per thread. `runHarnessWorker` runs a harness on stdin and stdout with the session frames of the harness protocol.
5 changes: 5 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,11 @@
"addedAt": "2026-10-13",
"updatedAt": "2026-10-14"
},
{
"label": "Deploy a harness",
"to": "harness/deploy",
"addedAt": "2026-10-13"
},
{
"label": "Send inputs safely",
"to": "harness/inputs",
Expand Down
93 changes: 93 additions & 0 deletions docs/harness/deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: Deploy a harness
id: harness-deploy
order: 6
description: "Run a harness in your server, as a worker process, on another machine, or as a single executable."
keywords:
- tanstack ai
- harness
- deploy
- build
- worker
---

Your harness works on your laptop. Now it needs to run next to your API, in its own process, on a different machine, or as a CLI your team can download. You pick the place. The harness definition stays the same.

| Place | How |
|---|---|
| Your server | `createHarnessHost` and `createHarnessHandler` in your routes. No build step. |
| A worker process | `buildHarness`, then `artifactText(dir)` as the model of a `chat()` call. |
| Another machine | `createHarnessHandler` there, then `harnessText({ url, token })` here. |
| A single executable | `buildHarness({ compile })` with Bun. |

## Build a worker artifact

`buildHarness` bundles the harness with Bun and writes a manifest next to it. Install [Bun](https://bun.sh) on the build machine.

```ts group=harness-deploy
import { artifactText, buildHarness } from '@tanstack/ai-harness/build'

const { manifest } = await buildHarness({
entry: './src/studio.ts',
export: 'studio',
outDir: './dist/studio',
})
console.log(manifest.name, manifest.digest)
```

- `dist/studio/harness.js` starts the harness as a worker. It reads session frames on stdin and writes them on stdout.
- `dist/studio/harness.manifest.json` names the harness, its agents and plugins, what it needs (file system, processes, network), and the sha256 digest of the bundle.

The build imports your entry in a child process to read the harness name, agents, and plugins. Importing a module runs its code.

## Run the artifact as a model

`artifactText` starts one worker process per outer thread and checks the digest first. A changed bundle is refused.

```ts group=harness-deploy
import { chat } from '@tanstack/ai'

const model = await artifactText('./dist/studio')
const stream = chat({
adapter: model,
messages: [{ role: 'user', content: 'Plan the launch post.' }],
threadId: 'thread-1',
})
// When you are done with the workers:
model.dispose()
```

The worker keeps the conversation of its thread between calls. Workers get the environment of the process that starts them, so they can read your provider keys.

## Use a harness on another machine

On the other machine, serve the harness with `createHarnessHandler`, and check a bearer token in `authorize`. See [Serve the session over HTTP](./connect#serve-the-session-over-http).

Here, use it as a model:

```ts group=harness-deploy
import { harnessText } from '@tanstack/ai-harness'

const remote = harnessText({ url: 'http://build-box:8787', token: 'your-token' })
```

The remote session keeps the conversation, so each call sends only the new message.

## Ship a single executable

Point `compile.entry` at the file that starts your app, for example a server or a command-line tool that imports the harness:

```ts group=harness-deploy
await buildHarness({
entry: './src/studio.ts',
export: 'studio',
outDir: './dist/studio',
compile: { entry: './src/main.ts', outfile: './dist/studio-app', target: 'bun-linux-x64' },
})
```

Bun can build for another platform with `target`, for example `bun-darwin-arm64` or `bun-windows-x64`.

## What you have now

- The same harness in your server, in a worker process, on another machine, or as a binary.
8 changes: 8 additions & 0 deletions packages/ai-harness/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
"types": "./dist/esm/client.d.ts",
"import": "./dist/esm/client.js"
},
"./build": {
"types": "./dist/esm/build.d.ts",
"import": "./dist/esm/build.js"
},
"./worker": {
"types": "./dist/esm/worker.d.ts",
"import": "./dist/esm/worker.js"
},
"./view": {
"types": "./dist/esm/view/index.d.ts",
"import": "./dist/esm/view/index.js"
Expand Down
Loading
Loading