# ship-pr

Adopt this skill: https://proskills.md/skills/other/parcha-ai-agentrun-agents-skills-ship-pr

Page: https://proskills.md/skills/other/parcha-ai-agentrun-agents-skills-ship-pr
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: .agents/skills/ship-pr/SKILL.md
size: 19364
sha: a0c2bd713938966547e1dfcc1cb6afa3e2d09ce8
upstream: https://github.com/parcha-ai/agentrun/blob/main/.agents/skills/ship-pr/SKILL.md

````text
---
name: ship-pr
description: "The one gate every pull request to Parcha-ai/agentrun passes before it goes to the maintainer: fresh base, local checks, the changes fragment check, the stranger test, each package's live checks run on a maintainer's machine, CI, zero unresolved threads, and a stack trial against every approved and queued pull request. Then a batch line for the maintainer, who approves; the queue merges. Use for /ship-pr PR[ PR...], 'gate #N', 'is #N ready', or before adding any PR to an approval batch."
---

# Ship a pull request

One gate, the same for every package and every author. It ends in a batch line the maintainer
approves; nothing here approves. Main stays green because every merge goes through the merge queue,
pinned to the head that was gated.

`AGENTS.md`, `CONTRIBUTING.md` and `changes/README.md` on `origin/main` are the authority. Where this
file disagrees with them, they win: fix this file in the same session.

Machine-specific facts (where credentials live, each credential wrapper, the approval command, a shared
slot for paid runs, a model gateway) live outside the repository: in the file `SHIP_PR_LOCAL` names, else
`local.md` beside this file, which is gitignored. Read it first when it exists; it never enters a commit.

## Repo facts (re-check when a step disagrees)

| Fact | Value |
|---|---|
| Protection | ruleset `main`: pull request, 1 approval, stale approvals dismissed on push, threads resolved, linear history; merge queue, ALLGREEN, up to 5 entries, squash |
| Required checks | `verify (22.19.0)` and `verify (22.23.2)`; `gh api repos/{owner}/{repo}/rulesets` is the authority. Every other job (stranger, floors, `pi-1-2`, `real-chrome`, `transport-docker`, `lean`) is a merge condition by this skill, not by the ruleset: a red one is fixed or named with its cause in the batch line |
| Toolchain | Node 22.19 or later on 22, or 24.2 or later; npm 11. Regenerate `package-lock.json` only with npm 11 (`--package-lock-only`), never by hand |
| Changelog | a fragment in `changes/` per pull request with a user-visible change; no pull request edits an `Unreleased` section (`changes/README.md`) |
| Identity | the machine identity authors, pushes, replies, resolves and arms auto-merge; only the maintainer approves |

## Step 1: facts, pinned to one head

```bash
R=Parcha-ai/agentrun
gh pr view <n> -R $R --json number,title,isDraft,baseRefName,headRefOid,mergeable,mergeStateStatus,reviewDecision,files,body
git fetch -q origin && git rev-list --count <head>..origin/main
```

Record the head SHA from `headRefOid`. Every later step is about that SHA; never type one by hand.
A draft is its author's: gate it only when it is marked ready.

## Step 2: fresh base

If the pull request conflicts with `origin/main`, or main changed a file it touches, rebase it
(an approved pull request is never pushed: its approval would be dismissed and it goes back to the
maintainer). Regenerate generated files with their generators (`npm run export:source`, the lock with
npm 11), never by hand. Push with `--force-with-lease=<branch>:<old-head>`.

## Step 3: local checks on the head

```bash
npm ci --ignore-scripts && npm run build
npm run check
node scripts/changes.mjs --check --base origin/main
npm run export:source && node scripts/check-generated.mjs
npm run verify:source
npm run verify:packages          # when a package's files, manifest or the lock change
```

Then `node scripts/stranger.mjs <name>` for every package whose README, `bin`, exports or demo the
pull request touches (specs in `scripts/stranger/`).

A test that fails on `origin/main` too is not the pull request's: show the same failure on main and
name it in the batch line.

## Step 4: live checks, per package

Run on a maintainer's machine whose environment already holds the credentials. Never in GitHub
Actions, never with a secret added to the repository. A package's section says when its check is
required, the command, the pass rule and the cost. Its lead owns that section.

### `@parcha/agentrun-dsl`, `@parcha/agentrun-jev`, `@parcha/agentrun-pi`

- **When:** any change to `packages/dsl`, `packages/jev` or `packages/pi`.
- **pi versions:** each version in its own disposable checkout, because `--scratch` rewrites the manifests
  and the lockfile: `git worktree add ../leg-<version> <head>`, then in it `npm ci --ignore-scripts` and
  `node scripts/pi-leg.mjs --pi <version> --scratch --npm "npx --yes npm@11.21.0"`, at the lowest version the
  peers accept (CI's floor jobs run 1.0.4) and at the newest minor they accept (the `pi-1-2` job runs 1.2.0).
  `--plan` only prints the leg. Pass: build, type-check and suite green, one copy of each pi package at the
  leg's version. Remove the checkout by its exact path afterwards.
- **Recovery or durable behaviour** (the driver, the journal, reconcile, effects, `./durable`): the PR
  body carries a kill-storm proof on the head: seeds, kills, every outside call applied once, every
  record equal to the uncut run's, no stop. Cost: $0 (scripted providers).
- **Recording format:** a change to what `./durable` exports for `agentrun.recording/1` validates
  with `apps/viewer`'s `validate` on a fresh recording.

### `@parcha/pi-durable-disk`

- **When:** the diff touches `packages/pi-durable-disk/src/**`, `bin/**`, `package.json` or
  `test/live/**`. A diff of only examples, the README or `changes/` skips it.
- **Pending:** `test/live/ci.ts` lands with pull request #472; until it is on `main`, this check is not run and the
  batch line says so.
- **Command,** from the repository root at the head, under your scratch-disk credential wrapper, which
  exports `ARCHIL_API_KEY`, `PDA_LIVE_DISK` and `PDA_LIVE_REGION` (default `aws-us-east-1`):

  ```bash
  npm run build && node packages/pi-durable-disk/test/live/ci.ts all
  ```

  `all` runs a preflight (it refuses without the key, a `dsk-` disk id or a region), the host suites
  (claim, env, run, store, supervise, lifecycle), then the Docker suites, so the two never share the
  disk, then a sweep that always runs and must leave 0 run directories and 0 token users, then a summary.
- **Pass:** exit code 0 and a last stdout line that starts with `ok: live disk check passed`. Anything
  else blocks: a non-zero exit, a last line `not ok: <step>: <reason>`, or no last line (killed or timed
  out). Put the summary it prints above that line in the pull request body.
- **Needs:** Docker, FUSE and the Archil client on the machine (the suites need root through
  `archil-scoped`), and a SCRATCH key and disk only. The suites delete runs and token users on the disk
  they are given: never run them with a production key.

### `@parcha/pi-browser`

- **When:**
  - **Real Chrome:** any change to `packages/pi-browser/src/**`, `package.json`, `scripts/**`, `test/**` or `examples/**`, or to `package-lock.json` when it moves a pi-browser dependency.
  - **Package contents:** a change to the manifest (`exports`, `files`, `bin`, `peerDependencies`) or to anything the tarball ships.
  - **Provider live check:** a change to `src/providers/**` (Browserbase, Kernel, the shared HTTP and CDP code), `src/recorder/**`, or the session lifecycle in `src/core/lease.ts` and `src/store.ts`.
  - A diff of only the README or `changes/` runs the stranger spec and nothing else here.
- **Real Chrome,** from the repository root at the head, with `CHROME_PATH` naming Chrome for Testing at the version CI pins (`CHROME_FOR_TESTING` in the workflow):

  ```bash
  npm ci --ignore-scripts && npm run build
  npm run test:chrome -w @parcha/pi-browser
  node scripts/check-reference-chrome.mjs
  npm test -w @parcha/pi-browser
  ```

  **Pass:** each command exits 0. The suites fail a file that leaves a process or a directory behind, so a teardown leak is a failure, not a warning.

  A fix for a flaky test also runs that file 16 times, four at a time, on Node 22.19 and on the newest Node CI uses, plus a forced version of the race it fixes. The body gives the counts. A flake fixed only by hoping is not fixed.
- **Package contents:** `npm run verify:tarball -w @parcha/pi-browser`. **Pass:** exit 0. Then `node scripts/stranger.mjs pi-browser`, whose spec runs the README's no-key command and must print the README's block byte for byte.
- **Provider live check,** once per provider the change can reach, with that provider's key already in the environment (`BROWSERBASE_API_KEY` for Browserbase, plus the Browserbase SDK installed; `KERNEL_API_KEY` for Kernel):

  ```bash
  PI_BROWSER_PROVIDER=browserbase node packages/pi-browser/examples/one-script-three-providers.mjs https://example.com
  PI_BROWSER_PROVIDER=kernel node packages/pi-browser/examples/one-script-three-providers.mjs https://example.com
  ```

  **Pass:** exit 0, and one stdout line that starts with the provider's name and quotes the page's first snapshot line. The script releases its session in a `finally`. Confirm in the provider's console, or with its session list, that no session it made is still running. A change to the recorder also records that session and shows frames within the caps, a `credential_on_page` gap on a password page, and `session_released` last, with no token in any line.

  Paste the provider lines and the session check into the body. Never paste a session's connect or live-view URL: they carry tokens.
- **Needs:**
  - Chrome for Testing at CI's pinned version. On Ubuntu 24.04, an unprivileged Chrome needs `kernel.apparmor_restrict_unprivileged_userns=0`, the runner's documented switch; the sandbox stays on.
  - Node 22.19 or later.
  - For the live check, a Browserbase or Kernel key whose plan allows at least one concurrent session.
  - No other credential, and no secret ever goes into the repository.
- **Cost:** the Chrome suites and the stranger spec are $0. A provider live check is one session of about a minute. At Browserbase's list price ($0.12 per session-hour, one-minute minimum) that is under $0.01; Kernel is on the same order.

### `@parcha/pi-transport`

Three live checks: Docker (free, on every source change), Daytona and Modal (paid, when the code they reach
changes). Each suite labels every box it makes with a label of its own run, lists that label at the provider at the
end, and fails if a box is left. All three run from the repository root at the head after `npm run build`.

- **When:**
  - **Docker:** the diff touches `packages/pi-transport/src/**`, `package.json`, or a `test/*docker.live.test.ts`
    file. A diff of only the README, `changes/` or offline tests skips it.
  - **Daytona:** the diff touches `src/box/daytona.ts`, the shared box code (`src/box/box.ts`, `files.ts`,
    `child.ts`), `src/hands/**`, or `test/box-*daytona*.live.test.ts`.
  - **Modal:** the diff touches `src/box/modal.ts`, the shared box code, `src/hands/**`, or
    `test/*modal*.live.test.ts`.
  - A change under `src/home/**` runs the Docker check (the home suites are Docker files) and, when the change moves
    a session between providers, Daytona and Modal too.
- **Command:**

  ```bash
  npm run build
  npm run test:live:docker -w @parcha/pi-transport
  # Daytona, under the credential wrapper that exports DAYTONA_API_KEY, DAYTONA_API_URL, DAYTONA_TARGET:
  PI_TRANSPORT_LIVE_DAYTONA=1 npm run test:live -w @parcha/pi-transport
  # Modal, under the wrapper that exports MODAL_TOKEN_ID and MODAL_TOKEN_SECRET:
  npm install --no-save --ignore-scripts modal@0.11.0
  PI_TRANSPORT_LIVE_MODAL=1 npm run test:live -w @parcha/pi-transport
  ```

  `test:live` loads every live file; each provider's own variable picks the files that run, and the others skip.
  `PI_TRANSPORT_LIVE_MODAL_KINDS` picks Modal kinds (`sandbox,vm` by default; add `gpu` only when the change
  touches GPU handling). `PI_TRANSPORT_LIVE_PREFIX` and `PI_TRANSPORT_LIVE_LABELS` (`a=b,c=d`) name and label the
  run's boxes, so two runs never touch each other's boxes.
- **Pass:** each command exits 0 and its Vitest summary shows `0 failed` (skipped files are fine). A non-zero exit,
  a failed test, or a run killed before its summary blocks. The suites' own last test counts the run's boxes still
  at the provider; a non-zero count fails the run. Put each summary line in the pull request body.
- **Needs:** a Docker daemon and Node 22.19 or later; for Daytona, a key for a TEST organization (never a
  production key) and its API URL and target; for Modal, a token pair and the `modal` SDK installed with `--no-save`
  (the package never depends on it). No credential enters a box, a log or the pull request.
- **Cost:** Docker $0. Daytona under $0.05 a run (about 60 short one-vCPU boxes, seconds each). Modal sandbox and VM
  under $0.10 a run; the T4 kind adds about $0.05 when asked for. Estimates from list prices.

### `@parcha/pi-vision`

Two live checks. Both run from the repository root at the head, after `npm ci --ignore-scripts && npm run build`.

**1. Programs on this machine** (ffmpeg, ffprobe, poppler, LibreOffice: the versions a host really has, not CI's).

- **When:** the diff touches `packages/pi-vision/src/core/{read,readers,video,pdf,office,derive,image}.ts`,
  `packages/pi-vision/src/providers/**`, `packages/pi-vision/src/node.ts`, `packages/pi-vision/test/binaries/**` or
  `packages/pi-vision/package.json`.
- **Command:**

  ```bash
  npm run test:binaries -w @parcha/pi-vision
  ```

- **Pass:** exit code 0 and `# fail 0` in its summary. A skip is allowed only for a program the machine does not
  have, and the body names it (for example "LibreOffice absent: 1 skipped"). A missing ffmpeg, ffprobe or poppler is a
  fail, not a skip.
- **Needs:** ffmpeg and ffprobe, poppler-utils, optionally LibreOffice (`soffice`) on PATH. No key. Cost: $0.

**2. Real eyes** (a few paid looks through pi-ai, the path no fake covers).

- **When:** the diff touches `packages/pi-vision/src/eyes/**`, `src/core/guard.ts`, `src/core/evidence.ts`,
  `src/core/limits.ts`, `src/core/see.ts`, `src/core/plan.ts`, `src/durable.ts`, or the `@earendil-works/*` peer or
  dev ranges in `packages/pi-vision/package.json`.
- **Command:**

  ```bash
  node packages/pi-vision/test/live/eyes.mjs
  ```

  It reads `PI_VISION_LIVE_EYES` (`<provider>/<model id>`, the public id pi-ai's catalog uses) and that provider's
  key from the variable pi-ai itself reads; `PI_VISION_LIVE_BASE_URL` points it at a gateway when the machine has one
  (a machine fact: it goes in `local.md`). It refuses to run without them. It makes four checks, each printed as a
  numbered line: `scanned letter` (a synthetic scan built by `./testing`, whose identifier the eyes must read exactly);
  `page 2` (a two-page PDF through `read` with `page`); `vision.see` (the same letter asked by path through the
  code-mode global: exact identifier, one look filed, a price above 0); and `receipts` (every paid look has `cost_usd`
  as a number and tokens above 0, and a forced no-eyes miss has `cost_usd: 0`).
- **Pass:** exit code 0 and a last stdout line that starts with `ok: pi-vision live eyes passed`. Anything else
  blocks: a non-zero exit, a last line `not ok: <step>: <reason>`, or no last line. Put the summary it prints (model,
  read rate, tokens, dollars) in the pull request body.
- **Needs:** one key for an eyes route that takes images. Use a model that reads identifiers reliably; the package's
  eyes bench measured some routes dropping digits, and this check would then fail as designed.
- **Cost:** about $0.01 a run.

## Step 5: CI and threads

- Every check run on the head is green. A red non-required job is either fixed, or shown to fail the
  same way on `origin/main` or on an unrelated head, with its run id and cause in the batch line.
  The machine identity cannot re-run a job; push a new head (only before approval) or name it.
- Zero unresolved review threads, counted by GraphQL over every page (`--paginate` follows `endCursor`):

```bash
out=$(gh api graphql --paginate -f query='query($endCursor:String){repository(owner:"Parcha-ai",name:"agentrun"){pullRequest(number:<n>){reviewThreads(first:100,after:$endCursor){pageInfo{hasNextPage endCursor} nodes{isResolved}}}}}' \
  --jq '.data.repository.pullRequest.reviewThreads.nodes[]|select(.isResolved==false)|1') && printf '%s' "$out" | awk 'END{print NR}'
```

A failed query prints nothing and exits non-zero; that is not a count of zero: fix the credential and ask again.

Every thread is fixed or declined with reasons, answered, and resolved. The review bot reviews when a
pull request opens or is marked ready; ask it again (`@greptile-apps please review again`) only after
a push that fixed a P0 or P1, or when it has not reviewed after 15 minutes, at most twice per pull
request.

## Step 6: stack trial

Before a pull request joins a batch, list the changed files of every pull request that is approved
or in the merge queue, from every author, and of every other pull request in the batch:

```bash
gh pr list -R $R --state open --json number,reviewDecision --jq '.[]|select(.reviewDecision=="APPROVED")|.number'
gh api graphql -f query='{repository(owner:"Parcha-ai",name:"agentrun"){mergeQueue(branch:"main"){entries(first:20){nodes{pullRequest{number}}}}}}'
comm -12 <(gh pr view <n> -R $R --json files --jq '.files[].path' | sort) <(gh pr view <m> -R $R --json files --jq '.files[].path' | sort)
```

For each one that shares a file, merge them as a stack in queue order and show it is clean:

```bash
t=$(git merge-tree --write-tree origin/main <first-head>) && c=$(git commit-tree "$t" -p origin/main -p <first-head> -m trial)
git merge-tree --write-tree "$c" <next-head>
```

A conflict means the later pull request waits for the earlier one to merge, then rebases. Never
claim "no overlap" from one pull request's file list alone.

## Step 7: the batch line

A gated pull request goes to the maintainer as one line in the batch file named in `local.md`:
`<number> <full head SHA from gh> <one line: what it does>`, with comment lines above it naming

- its hard blocks, if any: public API, stored format, peer ranges or dependency pins, durability
  semantics, release process, CI (`.github/`), anything a published package's user must change;
- the proof (step 4) and the CI and thread state;
- the stack trial result.

The maintainer approves at that SHA; the machine identity then arms auto-merge with
`gh pr merge <n> --auto --squash --match-head-commit <sha>`, so a later push never lands. A head that
moved after the gate is skipped and gated again.

## Step 8: after it merges

Watch the queue by GraphQL (`isInMergeQueue`, `mergeQueueEntry{state position}`); `autoMergeRequest`
reads null while a pull request is queued, so never re-arm on that. A pull request dropped from the
queue goes back to step 2. After the merge, check the `push` run on main for the merge commit; a red
required job caused by the merge gets a revert pull request at once, gated like any other.
Tell every lead whose pull request touches the same files.

## Hard rules

1. Nothing here approves: the maintainer does, at the gated SHA.
2. No secret, credential, machine name, internal host or path in any file, test, fixture, commit or
   pull request text of this public repository.
3. Live checks run on a maintainer's machine, never in GitHub Actions.
4. A gate result is true as of the moment it was computed: re-gate before acting on a stale one.
````

## File: README.md
upstream: https://github.com/parcha-ai/agentrun/blob/main/README.md

````text
# AgentRun

**AgentRun is an open platform to manage your agents.**

Create an agent, change it, and run it. Every change is a revision you can go back to, and every run names the
revision it ran, so you can open any run of any agent and see what it did. Host it yourself with AgentRun Server, on
your machine or your own server, or with us when hosted AgentRun opens.

1. **The platform.** Your agents, each one's revisions, and every run, behind one API (`/v2`) and one page. You run
   it yourself today; hosted by us is coming. An agent runs on pi, the default, or names Claude (the Claude Agent SDK) or
   Codex as its harness; `GET /v2/capabilities` says which of them the server runs.
2. **[AgentRun Server](packages/serve/README.md).** The part you host: one container, `ghcr.io/parcha-ai/agentrun-server`,
   that serves the `/v2` API and the page and keeps everything in one data directory. Apache-2.0.
3. **[Building blocks](#building-blocks).** The open packages underneath, each usable on its own in your pi agent:
   `@parcha/agentrun-dsl` and `@parcha/agentrun-jev` (typed workflows and the decisions in them),
   `@parcha/agentrun-pi` (workflows inside pi), `@parcha/pi-durable-agent` (the agent store),
   `@parcha/pi-durable-disk` and `@parcha/pi-transport` (where an agent and its tools run),
   `@parcha/pi-browser` and `@parcha/pi-vision` (what an agent can see and do). On npm, Apache-2.0.

## Start it

```text
Ask your coding agent: "Read agentrun.ai/SKILL.md and set up AgentRun for me"
npx @parcha/agentrun
```

Either one starts AgentRun on this machine and opens its console: your coding agent follows [the skill](docs/SKILL.md),
or [`npx @parcha/agentrun`](packages/agentrun/README.md) does it in one command (Node 22.19+).
One model key covers every harness: `OPENROUTER_API_KEY` gives pi, Claude Code and Codex agents each a current model.
Save it in the console (Settings → Tool keys → Model keys), which takes effect at once, or set it where AgentRun starts.

## Run it on a server

1. **Free**: run the public image, `ghcr.io/parcha-ai/agentrun-server`, with `docker run` on your own machine or server, with your own model key. An OpenRouter key with a credit limit caps what your agents can spend.

   ```sh
   docker run -d --name agentrun -p 127.0.0.1:7340:7340 -v agentrun:/data \
     -e OPENROUTER_API_KEY \
     ghcr.io/parcha-ai/agentrun-server:beta
   docker logs agentrun | grep Open
   ```

   With the key in your environment, `-e OPENROUTER_API_KEY` passes it on and it never sits on the command line. A credit
   limit on the key caps what your agents can spend. Open the link it prints; the volume `agentrun` keeps every agent and
   run. For a provider's own key, pass `-e ANTHROPIC_API_KEY` or `-e OPENAI_API_KEY` instead; any
   OpenAI Responses gateway works through `AGENT_MODEL_BASE_URL` and `AGENT_MODEL_API_KEY`, with its model named in
   `AGENT_MODEL`.

2. **About $7 a month**: deploy it to Render in one click: a Starter instance with a 1 GB disk, always on, with a URL. A Starter's 512 MB runs 3 pi agents or 2 Codex runs at once; a start that doesn't fit is refused with a retry-after until a run ends. A 2 GB instance holds 10 Codex runs. The image leaves Claude Code out for now, so run Claude Code agents with `npx @parcha/agentrun`. Render's free instance works for a try, but it has no disk, so it loses every run when it sleeps or restarts.

   [Deploy to Render](https://render.com/deploy?repo=https://github.com/Parcha-ai/agentrun) from [`render.yaml`](render.yaml),
   then set `OPENROUTER_API_KEY` in the service's Environment (or another provider's key, or a gateway's
   `AGENT_MODEL_BASE_URL` and `AGENT_MODEL_API_KEY` with its model in `AGENT_MODEL`). Render generates the token,
   `AGENTRUN_TOKEN`: open `https://<your service>.onrender.com/?token=<it>`.

Then **New agent**, **Start a run**, and **All runs**. [AgentRun Server](packages/serve/README.md) has the API, the models, what
a run may reach, and the container.

An agent with a shell runs it in a Docker container of its own: [Sandbox in 60 seconds](packages/serve/README.md#sandbox-in-60-seconds), from a checkout on a machine with Docker (the image above cannot host one yet).

## Building blocks

The open packages underneath the platform, each usable on its own in your pi agent. Install the ones you need.

**pi-vision** lets a pi agent answer questions about documents, images and video, and show the page, second or region each answer rests on. Try it free, with no key (the models are scripted), in a checkout built as under [Run the support example](#run-the-support-example): `npm run demo:vision -- examples/document-check`. It needs ffmpeg, ffprobe, pdftoppm and pdftotext (`brew install ffmpeg poppler`, or `sudo apt-get install ffmpeg poppler-utils`). See [pi-vision](packages/pi-vision/README.md).

[Quickstart](#quickstart) · [Packages](#use-one-piece-or-all-of-it) · [Documentation](docs/guide.md) · [Examples](docs/examples.md) · [Pi extension](#use-it-in-pi) · [Agent instructions](docs/agent-instructions.md)

![Support workflow: search for an answer, check it with Jev, and return it or ask an agent to investigate. Check the investigation before returning it or escalating for review.](docs/assets/support-workflow.gif)

[View the static diagram](docs/assets/support-workflow.svg) · [Run this example](#run-the-support-example)

The support example below follows this workflow with scripted tools and model responses.

## Quickstart

The DSL defines repeatable steps, uses [Jev](https://docs.typesafe.ai/) for focused decisions, and calls an agent when the work needs investigation. Your application keeps its tools, model access, permissions and budgets.

Requires Node 22.19+ and npm. In your project:

```sh
npm install @parcha/agentrun-dsl@latest
npx @parcha/agentrun-dsl@latest demo
```

This ticket-routing demo uses scripted decisions and needs no API key. Next, [run and change a workflow in your own app](examples/starter/README.md). To run the support workflow shown above, use the checkout below. To build workflows with your agent, [install the Pi extension](#use-it-in-pi).

### Run the support example

To run the workflow in the animation, clone the examples and build:

```sh
git clone --branch main --single-branch https://github.com/Parcha-ai/agentrun.git
cd agentrun
# With nvm: nvm install && nvm use
npm ci --ignore-scripts
npm run build
npm run demo:support
```

This runs the interpreter with scripted tools, Jev answers, and agent responses. It needs no API key and sends no customer replies.

The command prints a report for each case:

| Request | Agent calls | Decision calls | Result |
| --- | ---: | ---: | --- |
| Reset a password | 0 | 1 | Return the help answer |
| Find an invoice | 0 | 1 | Return the help answer |
| Investigate a failed payment | 1 | 2 | Return the investigation answer |
| Payment still unresolved | 1 | 2 | Escalate for review |

```sh
npm run demo:support -- payment
npm run test:support
```

Try `npm run demo:support -- unresolved` to see an escalation. It exits with code `2`. The responses are scripted; changing a prompt does not change them.

[Connect live Jev and your agent](docs/support-quickstart.md), or [give these instructions to your coding agent](docs/agent-instructions.md).

## Use one piece, or all of it

### Use one piece

Each package works on its own. [Pi](https://pi.dev) is an open-source coding agent; pi-durable is
Pi's runtime that commits each turn and tool call to storage; chord is the plugin and service runtime pi-durable is built
on; [Archil](https://archil.com) is a network disk you mount on any Linux host.

| Package | What it does | What it needs | First command with no key | Status |
| --- | --- | --- | --- | --- |
| [`@parcha/agentrun-dsl`](packages/dsl/README.md) | Define, validate, inspect and run workflows | Node 22.19+ | `npx @parcha/agentrun-dsl@latest demo` | published (`@latest`) |
| [`@parcha/agentrun-jev`](packages/jev/README.md)
````
