Paseo: Coding Agents That Keep Running When You Close the Laptop
Paseo puts a small daemon between you and your coding agents, so Claude Code, Codex, Copilot or OpenCode run on a machine you choose and every screen you own (desktop, phone, browser, terminal) is just a window onto them. What Paseo is, how the daemon model works, worktrees and paseo.json, permissions from your phone, structured output and schedules for automation, the TypeScript SDK, the security model, and how it compares with Orca.

Bản tiếng Việt: Paseo: coding agent vẫn chạy khi bạn gập laptop. Part 11 of the series. Previous: Orca. Next: n8n.
The most common way I lost agent work this year was not a bad model or a bad prompt. It was the laptop lid. I would start a long refactor at six in the evening, close the laptop to go home, and find the next morning that the agent had stopped forty minutes in, waiting for a permission prompt nobody saw, or simply died with the terminal that owned it.
The fix is old and obvious: the process that runs the agent should not live in the terminal you happen to be looking at. It should live on a machine that stays up, and you should be able to look at it, answer it and steer it from wherever you are. That is the idea behind Paseo.
What Paseo is
Paseo is an open-source agentic development environment, Apache-2.0, with a desktop app, native iOS and Android apps, a web app and a CLI. It drives the coding agents you already have installed and logged in: Claude Code, Codex, GitHub Copilot, OpenCode, Pi, Antigravity and Muse Code. Each runs as itself, with your configuration, your skills and your credentials. Paseo does not replace them or proxy their model calls.
Two facts from the README shape how I think about adopting it. It has no telemetry, no tracking and no forced log-in. And it is built by one person, funded by sponsors, and used (by its own account) by tens of thousands of developers a day. The first is unusual and welcome. The second is a risk you should weigh, which I come back to at the end.
The daemon model
Everything in Paseo goes through a local server called the daemon. The daemon starts and supervises agent processes, owns their workspaces and worktrees, keeps their history, and exposes a WebSocket API. The desktop app, the phone app, the web app and the CLI are all clients of that daemon, and they can connect to more than one daemon at once.
That split is what fixes the laptop-lid problem. Run the daemon on a workstation under your desk, a build server, or a VM, and the agents live there. Your laptop is one window onto them, your phone another. Close either and nothing stops.
The desktop app starts a daemon for you. On a server, install the CLI and start it directly:
npm install -g @getpaseo/cli
paseo # first run: starts the daemon and offers to set up device pairing
paseo status
listen: 127.0.0.1:6767
daemonVersion: 0.11.2
providers: [{"provider":"claude","available":true}, {"provider":"codex","available":false}, ...]
relay: {"enabled":false,"endpoint":"relay.paseo.sh:443", ...}
connectedDaemon: reachable
Two defaults in that output are worth noticing. The daemon listens on 127.0.0.1 only, so nothing outside the machine can reach it until you decide how. And the relay used for phone pairing is off. Both are the right defaults. providers tells you which agent CLIs Paseo found on this machine; on this box only Claude Code was installed.
There is also a container image that runs the daemon and the web UI together, which is how we run it on shared build machines:
docker run -d --name paseo \
-p 6767:6767 \
-e PASEO_PASSWORD=change-me \
-v "$PWD/paseo-home:/home/paseo" \
-v "$PWD:/workspace" \
ghcr.io/getpaseo/paseo:latest
The base image has no agent CLIs in it. You extend it with the ones you use and pass their credentials through environment variables or the persistent /home/paseo volume. Mount only the repositories the agents should see. Everything you mount, they can read.
Running an agent
The CLI does everything the apps do. The smallest useful command:
paseo run --provider claude --title smoke "Reply with the single word: pong. Do not use tools."
paseo ls
paseo logs ce5a01c
AGENT ID NAME PROVIDER STATUS CWD
ce5a01c smoke claude/claude-opus-5-5 idle /tmp/paseo-demo
[User] Reply with the single word: pong. Do not use tools.
pong
--provider takes a provider or provider/model (codex/gpt-5.5, for instance), so the same command line can start any of the supported agents. Add -d to run in the background, then use paseo attach <id> to stream its output, paseo send <id> "also add tests" to give it a follow-up, paseo wait <id> to block until it is idle, and paseo stop, archive or delete when you are done. Agent ids accept prefixes, so the first seven characters are enough.
Worktrees and paseo.json
For anything beyond a one-line question, an agent should get its own worktree, for the reasons in the previous post: parallel agents must not share files. Paseo creates and manages them:
paseo run -d --provider claude --title add-hello \
--new-workspace worktree --new-branch add-hello --base origin/main \
"Create hello.txt containing the word hi, then commit it with message 'add hello'."
Paseo keeps its worktrees under $PASEO_HOME/worktrees/ (set worktrees.root in config.json to move them), and archiving a workspace removes the worktree once nothing else uses it. Other worktree modes check out an existing branch (--worktree-mode checkout-branch --branch ...) or a pull request (--worktree-mode checkout-pr --pr-number 123). One warning: the README's example still shows an older --worktree feature-x flag. The CLI help is the source of truth. Run paseo run --help against the version you installed.
A fresh worktree has no dependencies and no ignored files, so the repository needs to describe how to prepare one. That goes in a paseo.json at the root:
{
"worktree": {
"setup": "npm ci && cp \"$PASEO_SOURCE_CHECKOUT_PATH/.env.development\" .env.local",
"teardown": "rm -rf .next/cache"
},
"scripts": {
"web": { "type": "service", "command": "npm run dev -- --port $PASEO_PORT" },
"test": { "command": "npm test" }
}
}
worktree.setup runs once after the worktree is created, teardown runs when it is archived. Both see $PASEO_SOURCE_CHECKOUT_PATH (your original checkout), $PASEO_WORKTREE_PATH and $PASEO_BRANCH_NAME. A script with "type": "service" is a long-running process Paseo supervises, and if you leave out port Paseo assigns one and passes it as $PASEO_PORT. That solves the port-3000 fight from the Orca post properly: five worktrees, five dev servers, five ports, nobody has to think about it. paseo script start web starts it from the CLI.
There is one behaviour that will confuse you the first time. Paseo reads paseo.json as committed on the base branch you start from, not your working copy. Edit it, forget to commit, and new worktrees ignore the change. On a test repository with a setup step of echo "setup ran on $PASEO_BRANCH_NAME" > .setup-marker, the file appeared in the new worktree only after paseo.json was committed to main.
Permissions, from wherever you are
An agent in Paseo still asks before it does something its permission mode does not allow. Paseo turns those prompts into first-class objects that any client can answer. When I ran the worktree example above, the agent stopped before its first shell command:
paseo wait b977bc6
paseo permit ls
paseo permit allow b977bc6
AGENT ID STATUS MESSAGE
b977bc6 permission Agent is waiting for permission: tool
AGENT TOOL DESCRIPTION
b977bc6 Bash -
After allow, it wrote the file, committed it on its branch and finished:
[Shell] echo hi > hello.txt && git add hello.txt && git commit -q -m 'add hello' && git log --oneline -1
I created `hello.txt` containing `hi` and committed it as `d364317` with the message "add hello".
There was already an untracked file called `.setup-marker` in the worktree. I didn't include it in the commit.
The same prompt shows up as a notification on the phone, and that is the feature that changed my evenings. An agent blocked on a permission at 9pm used to mean a lost night. Now it means looking at a phone for ten seconds. It also means I no longer feel pushed to run agents in "allow everything" mode just so they will not block while I am away, which was the worst habit the laptop-lid problem taught me. Each provider has its own permission modes (paseo provider ls lists them) and --mode picks one per run. On machines with credentials worth stealing, keep the prompts.
Agents in pipelines: structured output and schedules
Two CLI features turn Paseo from a remote control into an automation tool.
--output-schema makes the run return JSON that matches a schema, instead of prose. This is the same idea as structured outputs in the API, applied to a whole coding agent:
{
"type": "object",
"properties": {
"risk": { "type": "string", "enum": ["low", "medium", "high"] },
"reason": { "type": "string" }
},
"required": ["risk", "reason"]
}
paseo run --provider claude --wait-timeout 90s --output-schema verdict.schema.json \
"The README.md in this repo contains one heading. Rate the risk of deleting README.md and give a one-sentence reason."
{
"risk": "low",
"reason": "README.md contains only the heading \"# demo\", nothing else in the repo references it, and it is tracked in git, so deleting it loses no real content and is easy to restore."
}
That is something a shell script or a CI job can branch on: pipe it to jq -r .risk, fail the job on high. We use this shape for a pre-merge check that asks an agent to rate migration risk in a pull request and posts the JSON to the PR.
paseo schedule runs a prompt on a cadence, on the daemon, with no cron job of your own:
paseo schedule create --name nightly-deps --provider codex \
--cron "0 2 * * 1-5" --timezone Asia/Ho_Chi_Minh --cwd /workspace/blog \
"Check for outdated npm dependencies with known vulnerabilities. If any, open a branch with the minimal upgrade and summarise the changelog risk."
paseo schedule ls
paseo schedule logs <id>
--max-runs and --expires-in keep a schedule from outliving its purpose. Give scheduled agents narrow tasks and a worktree, never write access to main.
Agents that start agents
The daemon also exposes Paseo's own operations to the agents it runs, so an agent can create worktrees, launch other agents (across providers) and talk to them. The friendliest way in is the skills:
npx skills add getpaseo/paseo
They add three slash commands to any agent conversation. /paseo-handoff passes the current work to another agent; the author uses it to plan with Claude and hand implementation to Codex, which matches what we do. /paseo-advisor starts a second agent purely as an advisor, for a second opinion without delegating the work. /paseo-committee puts two contrasting agents on a problem to do root-cause analysis and produce a plan, which is the "different models disagree, look there" idea from part 9 packaged as a command.
For anything more structured, the TypeScript SDK talks to the daemon directly:
import { createPaseoClient } from "@getpaseo/client";
const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" });
await client.connect();
const agent = await client.agents.create({
config: { provider: "codex/gpt-5.5" },
cwd: "/workspace/storefront",
prompt: "Review the current diff and name the riskiest change.",
});
const result = await agent.waitForFinish();
console.log(result.lastMessage);
await client.close();
The client also has agents.list() and agents.subscribe(). We have a small service built on it that listens for GitHub issues labelled agent-ok, creates a worktree agent for each, and comments the result back on the issue. It is about a hundred lines, most of them GitHub API calls.
Reaching the daemon, safely
A daemon that runs coding agents with your credentials is one of the most valuable things on your network to an attacker. Paseo's security docs are short and sensible, and I follow them as written.
Default to local. The daemon binds to 127.0.0.1:6767. Leave it that way unless you have a reason.
From your phone, use the relay or a VPN. The relay is off until you enable it during pairing. When on, it is designed to be untrusted: the daemon keeps a persistent keypair, the pairing QR code or link carries its public key, the phone does a handshake, and messages are end-to-end encrypted with NaCl box. The relay sees IP addresses, timing, message sizes and session ids, not content. Treat the pairing link like a password. The alternative is a direct connection over Tailscale: set daemon.listen in ~/.paseo/config.json to the machine's Tailscale IP and add it as a host on the phone.
Never bind to a network address without a password. The docs put it as "never bind to 0.0.0.0 without a password", and they are right. paseo daemon set-password stores a bcrypt hash in config.json; clients then send it as a Bearer token. A password controls access, it does not encrypt, so pair it with Tailscale or TLS.
Plugins run unsandboxed. Paseo has a plugin system for UI, agent lifecycle hooks and new providers, and plugins run on the daemon with its access. The CLI calls them "trusted, unsandboxed plugins", which is accurate. Install only code you would run as yourself.
Agents are not sandboxed either. They run in your user context with your credentials, or with whatever you mounted into the container. Part 4 still applies: scope credentials, use permission modes, put risky work in a container or a throwaway VM.
The trade-offs
One maintainer. A tool your team depends on daily, maintained by one person, has a bus factor of one. That does not stop me using it. It means we pin versions, keep our paseo.json and SDK usage small enough to replace, and sponsor the project, which is cheaper than maintaining a fork.
It is 0.x and moves quickly. Flags change, as the README example shows. Read --help for your installed version, and upgrade the daemon and clients together.
It does not review for you. Paseo has diffs, a file view, terminals and a browser in the desktop app, and it is perfectly usable for review. But its centre of gravity is running and steering agents, not reading their output side by side.
Paseo or Orca
The two tools solve overlapping problems from opposite ends, and we use both.
| Orca | Paseo | |
|---|---|---|
| Centre of gravity | A desktop workspace for reviewing many agents | A daemon that runs agents anywhere |
| Where agents run | Your machine, SSH hosts, or an orca serve box | Wherever the daemon runs: laptop, server, container |
| Phone | Companion app for monitoring and steering | Full app, same features as desktop |
| Automation | CLI that agents can drive, experimental orchestration | CLI with JSON-schema output, schedules, TypeScript SDK |
| Telemetry | Anonymous, opt-out | None |
| Licence and team | MIT, a company | Apache-2.0, one maintainer |
My rule of thumb: if I am at the desk reviewing, I am in Orca. If the work should run while I am not, it goes through Paseo.
The last post in this batch steps back from coding agents to the place where most companies will meet agents first: business workflows, with n8n.
References
- Paseo on GitHub (Apache-2.0) and documentation
- Paseo docs, Worktrees and paseo.json, Connectivity, Security, CLI reference
- Paseo TypeScript SDK quickstart