Run your first agent
Execute an agent run end to end on the local demo company, first simulated, then with Claude Code.
This tutorial follows one piece of work through SAT on your local demo company: you assign a task to Toni, the NoteFlow content writer, a runner claims the queued run, the agent works, and the result lands back on the task with its cost on Toni's budget. You do it twice: first with the built-in simulator, which calls no model and costs nothing real, then with Claude Code.
Before you start
-
SAT running locally with the demo company seeded. See Run SAT locally.
-
The CLI signed in to the
localprofile with NoteFlow as the default company:bun run sat -- login --email demo@example.com bun run sat -- company use NoteFlow -
For the second part: Claude Code installed, signed in, and on your
PATHasclaude.
The examples write sat for bun run sat --. Toni is a good first agent: Toni's adapter is claude_code, Toni has no run in progress, and the task below has no project, so the runner uses a scratch folder instead of cloning the demo's placeholder repository.
Steps
Create an API key for the runner
Runners authenticate with an API key, not a password session, because agents use the runner's credential through sat.
sat apikey create runner --storeThe key (sat_live_…) is printed once. --store saves it as the local profile's credential, replacing your password session, so from now on the profile signs in with the key. A key can do everything a session can except create or revoke keys; run sat login again when you need to manage keys.
Assign a task
sat tasks create "Draft the v1 release notes" --assignee Toni \
--description "Five short bullet points on what NoteFlow v1 includes, written for the waitlist."
sat runner statusThe task gets the next key; with a fresh seed that is NF-21. Because the task is todo and Toni is available (not paused, pending_approval or terminated), SAT queues a run with trigger assignment, and sat runner status lists it under Queued. In the web app, open the task: its thread shows the queued run.
Run it with the simulator
sat runner start --simulate --once--simulate replaces every adapter with a built-in echo adapter, and --once executes what is queued now and then exits. The log looks like this:
10:42:01 runner my-laptop:41233 serving NoteFlow · simulate · concurrency 2
10:42:01 ▶ Toni · NF-21
10:42:03 ✓ Toni · NF-21 succeeded
10:42:03 runner done: 1 claimed, 1 succeeded, 0 failed, 0 stoppedHere is what happened, in order:
- The runner claimed the run (
POST /runs/claim). The run becamerunningand Toni becamerunning. - The runner moved
NF-21fromtodotoin_progress, as Toni. - It prepared Toni's workspace, a scratch folder at
~/.local/share/sat/workspaces/nf/agents/toni. - The echo adapter wrote three steps to the transcript and reported 100 input tokens, 50 output tokens and a cost of 1 cent.
- The runner posted the final reply as a comment from Toni, then moved the task to
in_review. - It reported the run as
succeeded. Toni went back toidle, and the 1 cent counted toward Toni's spend this month.
Look at the result:
sat tasks show NF-21 # status in_review, Toni's comment, the run
sat runs list --agent Toni --limit 1
sat runs show <run-id-prefix> # tokens, cost, summary and transcriptThe transcript reads (each line is prefixed with its time):
system Assigned NF-21
system Claimed by runner my-laptop:41233
system Runner my-laptop:41233 · scratch folder /Users/you/.local/share/sat/workspaces/nf/agents/toni
agent Toni picked up NF-21 in /Users/you/.local/share/sat/workspaces/nf/agents/toni
tool step 1/3
tool step 2/3
tool step 3/3The comment on the task, and the run's summary, is "Echo: NF-21 Draft the v1 release notes done."
The seeded running runs
If the seed ran more than five minutes ago, this first claim also marks the seed's three running runs (Grace, Ken and Maya) as failed with "The process running it stopped reporting (lease expired)", and sets those agents to error. That is the lease rule freeing agents whose runner disappeared. They take work again on their next run.
Run it with Claude Code
Check that the runner can see Claude Code:
sat runner adaptersclaude_code should show ready: yes. Then give Toni a real task and run only Toni's work:
sat tasks create "Write a 100-word announcement for the NoteFlow waitlist" --assignee Toni \
--description "Save it as announcement.md and summarise it in your final message."
sat runner start --once --agents Toni --max-turns 15 \
--claude-settings '{"permissions": {"allow": ["Bash(sat:*)"]}}'What the flags do:
--agents Toniclaims only Toni's runs, so nothing else in the demo starts.--max-turns 15caps the agent's turns for this run.--claude-settingsis passed toclaude --settings. The example allows the agent to runsatcommands without asking. Claude Code runs non-interactively here, in the default permission modeacceptEdits: file edits are allowed, but shell commands that are not allowed by your settings are refused. Without this rule the agent can still finish the task; it just cannot comment or move tasks itself, and the runner does that for it.
The runner starts claude -p --output-format stream-json in Toni's scratch folder, with Toni's identity (name, title, manager Maya, company mission, instructions) as the system prompt and the task, its goal chain, project and discussion as the prompt. While it works:
- Transcript. Every 2 seconds the runner appends what the agent did: its messages (
agent), tool calls such as→ Write announcement.mdand their results (tool). Watch it live withsat runs tail <run-id-prefix>or on the run page in the web app. - Lease. The runner heartbeats every 30 seconds. If you cancel the run (
sat runs cancel <run-id-prefix>or Cancel run on the run page in the web app), the runner stops Claude Code immediately. - Agent tools. Inside the run,
satacts as Toni (the runner setsSAT_RUN_ID), so comments and task moves made by the agent are attributed to Toni in the activity log.
Review the result
When Claude Code finishes, the runner:
- posts Toni's final message as a comment on the task,
- moves the task to
in_review(unless Toni already moved it, for example toblocked), - reports the run
succeededwith input and output tokens and the cost Claude Code reported, rounded up to the next cent.
sat tasks show NF-22
sat agents show Toni # budget line shows spend this month against Toni's $60.00
ls ~/.local/share/sat/workspaces/nf/agents/toniIn the web app, the task appears in Inbox under "Blocked or waiting for review". Read Toni's comment, then move the task to Done, or comment with changes and wake Toni again (sat agents wake Toni --task NF-22).
If a run brings Toni's month-to-date spend to the $60 budget, SAT pauses Toni when the run finishes and opens a budget_override approval in the inbox. Approve it with a new limit (sat approvals approve <id-prefix> --budget 100) to resume Toni.
Troubleshooting
Run sat runner status; the Queued table shows each run's agent, adapter and agent status. Then check:
- Is a runner serving the agent's adapter? Without
--adapters, a runner serves only the adapters it detects on the machine (sat runner adapters). Agents withcursor,gemini,opencodeorhttpadapters (Maya and Margaret in the demo) are never served by default. - Is the agent dormant? Runners skip
paused,pending_approvalandterminatedagents.sat inboxshows budget overrides and hire requests waiting for you. - Is the agent busy? An agent runs one run at a time. A
runningrun without a live runner blocks the agent until its lease expires (RUN_LEASE_SECONDS, 300 seconds by default), or until you cancel it withsat runs cancel. - Did the runner exit first?
--oncedrains what is queued when it starts. Start it again, or run without--once. - Is
--agentsexcluding the agent?
Agents call the API through sat at the runner's API URL, here http://localhost:8080. If your Claude Code configuration sandboxes shell commands or restricts network access, the agent's sat calls fail. Pass --claude-settings with a settings file or JSON that allows the agent to run sat and reach localhost:8080, or point the runner at a remote API. The runner itself is not affected: it still posts the final comment and moves the task.
The runner refuses password sessions. Run sat apikey create runner --store, or pass a key for one process with SAT_API_KEY=sat_live_… sat runner start.
The task is in a project whose repository the runner cannot clone, such as the demo's "Web app" (https://github.com/acme/noteflow-web). Clear the task's project (sat tasks edit NF-22 --project none) or set a real repository on the project, then wake the agent again.
Without --simulate, the runner needs claude or codex on its PATH. Install one, or use --simulate.