# @parcha/agentrun-harness A reference harness: the host we use to run AgentRun demos and tests, meant to be read and forked. It is one pi-durable host that runs an agentrun job on this machine, with the recorder and fakes, so an example runs from an npm install. ## What the agent can do The agent runs commands with **your user's rights**: a live demo can read and write whatever your account can. Run live demos in a container or a VM, not on a machine that holds anything you would not hand the agent. There is no command sandbox yet. Where agent commands run as the host's user, receipts, the journal and the recording record what the host did; they are not tamper-proof against the agent. Run untrusted inputs with runAs or in a box. ## Run a demo from an install, with no key ```sh npm install @parcha/agentrun-harness@beta ``` npm installs the pi peers it needs (`@earendil-works/pi-ai`, `pi-durable`, `chord`, `pi-codemode` and `typebox`). `@parcha/pi-transport` is optional: only a demo that declares `boxes` needs it (`npm install @parcha/pi-transport@beta`). Save this as `demo.mjs`: ```js import { demo } from '@parcha/agentrun-harness'; // One tool call and one step of code, on a scripted tool: no model, no key, no network. const workflow = { v: 2, name: 'first-demo', schemas: { Any: { type: 'object' }, Found: { type: 'object', required: ['id', 'score'], properties: { id: { type: 'string' }, score: { type: 'number' } } }, }, output: { schemaId: 'Any', path: 'final' }, root: { node: 'chain', steps: [ { node: 'call', label: 'search', via: 'tool', tool: 'lookup', args: { id: 'x' }, out: 'Found', as: 'search', deadline_s: 30 }, { node: 'code', label: 'finish', code: 's => ({ final: { id: s.search.id, score: s.search.score * 2 } })' }, ], }, }; const fakes = { model: () => { throw new Error('this job asks no model'); }, tools: { lookup: (args) => ({ id: String(args.id), score: 21 }) }, judge: async () => { throw new Error('this job asks no judge'); }, pages: {}, start: '', }; await demo({ self: import.meta.url, spec: { name: 'first-demo' }, job: { workflow, input: {} }, fakes }); ``` ```sh node demo.mjs ``` It prints one line: ```text first-demo: complete ``` The fakes make no priced call, so there is no cost line. Node 22 also prints its SQLite `ExperimentalWarning` on stderr. The run's record, `{"id":"x","score":42}`, is in the whole result that `--json` prints. `node demo.mjs --record ./rec` keeps the run as an `agentrun.recording/1` folder. Open it in the hosted run viewer, https://parcha-ai.github.io/agentrun/: choose or drop the folder, and it plays in your browser, with nothing uploaded. `pi-durable-disk run --app` takes this package's child app as a file: `import.meta.resolve('@parcha/agentrun-harness/child-app')`. ## The reference assembly One program that puts the open packages together and runs one job end to end on pi-durable. A host supplies seven functions; `assemble.ts` wires them into a durable run, the recovery driver, the node runner, the browser and vision. The defaults need no key and no network: a scripted model, an in-process tool, the browser's fake backend. ```sh npm ci --ignore-scripts && npm run build && npm run demo:reference ``` It prints the record, the receipts it cites, what each package did and the usage, and exits 0. `-- --json` prints the same as one JSON object. `-- --dir ` keeps the run's directory. `-- --chrome` drives a local Chrome instead of the fake (`CHROME_PATH`, default `/usr/bin/google-chrome`): this process serves the job's pages as Chrome's proxy, and every other host fails to resolve. `-- --box local` or `-- --box docker` runs the job's tools in a box of `@parcha/pi-transport` (`boxEnv`) while the run's store stays on the disk: a local box's workspace is the run's `work/`; a Docker box is a container (`debian:bookworm-slim`, which Docker pulls if it is not there) that the job's files are uploaded to. The box is destroyed when the run ends. `-- --record ` keeps the run as an `agentrun.recording/1` directory the run viewer (`apps/viewer`) plays. Before it finishes one, the recorder scans every line, every text blob and the manifest with `scan.ts`, the one scanner the repository's recording validator and the hosted gallery's gate import: a credential's shape or a machine path stops it with what it found and where, and the directory is left without a manifest, so it is never a finished recording. ## The job `job.ts`, small and fixed so the output is a constant: 1. `call search`: the host's tool returns two URLs. The tool door keeps each answer in `/answers/`, under the admitted effect's `argsHash` (its input, node and idempotency key, so two steps that call one tool alike are two answers), before the result is committed (see [Recovery](#recovery)). 2. `agent investigate`: the lead opens the first result with `run`, reads it with `browser_read`, reads `filing.png` from the workspace with `read` (vision serves the image), and submits `{ answer, citations }` citing the two receipt paths its tools returned. Like a model, it asks a read that errs again, three attempts in all. A page it did not open is not read: the record then cites only the scan and says whether the page could not be opened or its outcome is unknown (see [Recovery](#recovery)). 3. `judge verdict`: a scripted judge says whether the receipts support the answer. 4. `code finish`: the output. ## The host functions `host.ts` declares them as one value, `Host`. `localHost()` gives the no-key defaults. | # | Function | Who asks for it | `localHost()` passes | |---|---|---|---| | 1 | `models` | pi-durable's `models`, the node runner's agent | pi-ai's faux provider, answering from the job's script | | 2 | `tools` | the workflow's `call` (`runEffect`), a later process's lookup of a call an earlier one left unknown (`answered`), the agent's extensions, the browser's provider and driver | the job's `search` and the answers it kept, no extra extensions, `FakeBackend` from `@parcha/pi-browser/testing` | | 3 | `evidence` | the browser and vision, one sink for both record types | a JSON file per record under `/evidence/