Paseo: coding agent vẫn chạy khi bạn gập laptop
Paseo đặt một daemon nhỏ giữa bạn và các coding agent, để Claude Code, Codex, Copilot hay OpenCode chạy trên một máy bạn chọn, còn mọi màn hình bạn có (desktop, điện thoại, trình duyệt, terminal) chỉ là cửa sổ nhìn vào chúng. Paseo là gì, mô hình daemon hoạt động ra sao, worktree và paseo.json, duyệt quyền từ điện thoại, output có cấu trúc và lịch chạy cho tự động hoá, TypeScript SDK, mô hình bảo mật, và so sánh với Orca.

English version: Paseo: Coding Agents That Keep Running When You Close the Laptop. Phần 11 của series. Phần trước: Orca. Phần sau: n8n.
Năm nay, cách tôi mất công việc của agent thường xuyên nhất không phải do model tệ hay prompt tệ. Mà do cái nắp laptop. Tôi khởi động một đợt refactor dài lúc sáu giờ chiều, gập laptop để về nhà, và sáng hôm sau phát hiện agent đã dừng sau bốn mươi phút, đứng chờ một permission prompt không ai thấy, hoặc đơn giản là chết theo cái terminal đang giữ nó.
Cách sửa thì cũ và hiển nhiên: process chạy agent không nên sống trong cái terminal bạn tình cờ đang nhìn. Nó nên sống trên một máy luôn bật, và bạn phải nhìn được, trả lời được, điều khiển được nó từ bất cứ đâu. Đó là ý tưởng đằng sau Paseo.
Paseo là gì
Paseo là một môi trường phát triển cho agent, mã nguồn mở, giấy phép Apache-2.0, có app desktop, app iOS và Android native, web app và CLI. Nó điều khiển những coding agent bạn đã cài và đã đăng nhập sẵn: Claude Code, Codex, GitHub Copilot, OpenCode, Pi, Antigravity và Muse Code. Mỗi agent chạy đúng là chính nó, với cấu hình, skill và credential của bạn. Paseo không thay thế chúng và không làm proxy cho các lời gọi model.
Hai sự thật trong README định hình cách tôi nghĩ về việc áp dụng nó. Nó không có telemetry, không tracking, không bắt đăng nhập. Và nó do một người làm, sống nhờ nhà tài trợ, và (theo lời chính dự án) được hàng chục nghìn lập trình viên dùng mỗi ngày. Điều đầu hiếm gặp và đáng hoan nghênh. Điều sau là một rủi ro bạn nên cân nhắc, tôi sẽ quay lại ở cuối bài.
Mô hình daemon
Mọi thứ trong Paseo đi qua một server local gọi là daemon. Daemon khởi động và giám sát các process agent, sở hữu workspace và worktree của chúng, giữ lịch sử, và mở một WebSocket API. App desktop, app điện thoại, web app và CLI đều là client của daemon đó, và chúng kết nối được tới nhiều daemon cùng lúc.
Sự tách bạch này chính là thứ giải quyết chuyện cái nắp laptop. Chạy daemon trên một máy trạm dưới gầm bàn, một build server hay một VM, và các agent sống ở đó. Laptop của bạn là một cửa sổ nhìn vào chúng, điện thoại là cửa sổ khác. Đóng cửa sổ nào cũng không có gì dừng lại.
App desktop tự khởi động một daemon cho bạn. Trên server, cài CLI rồi khởi động trực tiếp:
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
Có hai giá trị mặc định đáng để ý trong output đó. Daemon chỉ lắng nghe ở 127.0.0.1, nên không gì bên ngoài máy chạm tới được cho tới khi bạn quyết định cách mở. Và relay dùng để ghép cặp điện thoại đang tắt. Cả hai đều là mặc định đúng. providers cho biết Paseo tìm thấy những agent CLI nào trên máy này; trên máy thử nghiệm chỉ có Claude Code được cài.
Còn có một container image chạy daemon cùng web UI, đây là cách chúng tôi chạy nó trên các build server dùng chung:
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
Image gốc không có agent CLI nào. Bạn mở rộng nó với những agent bạn dùng và truyền credential qua biến môi trường hoặc volume /home/paseo lưu bền. Chỉ mount những repository mà agent nên thấy. Thứ gì bạn mount, chúng đều đọc được.
Chạy một agent
CLI làm được mọi thứ các app làm. Lệnh nhỏ nhất mà vẫn có ích:
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 nhận một provider hoặc provider/model (ví dụ codex/gpt-5.5), nên cùng một dòng lệnh khởi động được bất kỳ agent nào được hỗ trợ. Thêm -d để chạy nền, rồi dùng paseo attach <id> để xem output trực tiếp, paseo send <id> "also add tests" để giao việc tiếp, paseo wait <id> để chờ tới khi agent rảnh, và paseo stop, archive hay delete khi xong. Agent id nhận tiền tố, nên bảy ký tự đầu là đủ.
Worktree và paseo.json
Với bất cứ việc gì lớn hơn một câu hỏi một dòng, agent nên có worktree riêng, vì những lý do ở bài trước: các agent song song không được dùng chung file. Paseo tạo và quản lý worktree:
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 giữ worktree trong $PASEO_HOME/worktrees/ (đặt worktrees.root trong config.json để chuyển chỗ), và archive một workspace sẽ xoá worktree khi không còn gì dùng nó. Các chế độ worktree khác checkout một branch có sẵn (--worktree-mode checkout-branch --branch ...) hoặc một pull request (--worktree-mode checkout-pr --pr-number 123). Một lưu ý: ví dụ trong README vẫn dùng flag cũ --worktree feature-x. Help của CLI mới là nguồn chuẩn. Hãy chạy paseo run --help trên đúng phiên bản bạn đã cài.
Worktree mới tinh không có dependency và không có các file bị ignore, nên repository cần mô tả cách chuẩn bị một worktree. Việc đó nằm trong paseo.json ở gốc repo:
{
"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 chạy một lần sau khi worktree được tạo, teardown chạy khi nó bị archive. Cả hai thấy được $PASEO_SOURCE_CHECKOUT_PATH (checkout gốc của bạn), $PASEO_WORKTREE_PATH và $PASEO_BRANCH_NAME. Một script có "type": "service" là process chạy dài được Paseo giám sát, và nếu bỏ trống port thì Paseo tự cấp một cổng và truyền vào qua $PASEO_PORT. Thế là chuyện tranh cổng 3000 ở bài Orca được giải quyết tử tế: năm worktree, năm dev server, năm cổng, không ai phải nghĩ. paseo script start web khởi động nó từ CLI.
Có một hành vi sẽ làm bạn bối rối lần đầu. Paseo đọc paseo.json theo bản đã commit trên base branch bạn xuất phát, không phải bản trong thư mục làm việc. Sửa file mà quên commit thì worktree mới bỏ qua thay đổi đó. Trên một repository thử nghiệm với bước setup là echo "setup ran on $PASEO_BRANCH_NAME" > .setup-marker, file đó chỉ xuất hiện trong worktree mới sau khi paseo.json đã được commit lên main.
Duyệt quyền, từ bất cứ đâu
Agent trong Paseo vẫn hỏi trước khi làm điều mà chế độ quyền của nó không cho phép. Paseo biến các lời hỏi đó thành đối tượng hạng nhất mà client nào cũng trả lời được. Khi tôi chạy ví dụ worktree ở trên, agent dừng lại trước lệnh shell đầu tiên:
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 -
Sau allow, nó tạo file, commit trên branch của nó và hoàn tất:
[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.
Cùng lời hỏi đó hiện thành thông báo trên điện thoại, và đây là tính năng đã thay đổi các buổi tối của tôi. Một agent kẹt ở permission lúc chín giờ tối từng nghĩa là mất trắng một đêm. Giờ nó nghĩa là nhìn điện thoại mười giây. Nó cũng có nghĩa là tôi không còn bị đẩy tới chuyện chạy agent ở chế độ "cho phép tất cả" chỉ để chúng không bị kẹt khi tôi vắng mặt, thói quen tệ nhất mà vấn đề cái nắp laptop từng dạy tôi. Mỗi provider có các chế độ quyền riêng (paseo provider ls liệt kê chúng) và --mode chọn một chế độ cho từng lần chạy. Trên máy có credential đáng bị đánh cắp, hãy giữ các lời hỏi.
Agent trong pipeline: output có cấu trúc và lịch chạy
Hai tính năng của CLI biến Paseo từ một cái điều khiển từ xa thành một công cụ tự động hoá.
--output-schema khiến lần chạy trả về JSON khớp một schema, thay vì văn xuôi. Đây là cùng ý tưởng structured output trong API, áp dụng cho cả một 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."
}
Đó là thứ một shell script hay một job CI có thể rẽ nhánh theo: pipe qua jq -r .risk, cho job fail khi gặp high. Chúng tôi dùng đúng hình dạng này cho một bước kiểm tra trước khi merge, yêu cầu agent chấm mức rủi ro migration trong một pull request và đăng JSON lên PR.
paseo schedule chạy một prompt theo nhịp, ngay trên daemon, không cần cron job riêng:
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 và --expires-in giữ cho một lịch chạy không sống lâu hơn mục đích của nó. Giao cho agent chạy theo lịch những việc hẹp và một worktree, không bao giờ cho quyền ghi vào main.
Agent khởi động agent
Daemon cũng mở các thao tác của chính Paseo cho những agent nó đang chạy, nên một agent có thể tạo worktree, khởi động agent khác (khác provider cũng được) và nói chuyện với chúng. Cách dễ nhất để bắt đầu là các skill:
npx skills add getpaseo/paseo
Chúng thêm ba slash command vào bất kỳ cuộc hội thoại agent nào. /paseo-handoff chuyển việc đang làm sang một agent khác; tác giả dùng nó để lên kế hoạch với Claude rồi giao phần cài đặt cho Codex, khớp với cách chúng tôi làm. /paseo-advisor khởi động agent thứ hai chỉ để làm cố vấn, lấy ý kiến thứ hai mà không giao việc. /paseo-committee đặt hai agent có cách nhìn trái ngược vào một vấn đề để phân tích nguyên nhân gốc và ra kế hoạch, tức là ý "các model khác nhau bất đồng thì nhìn vào chỗ đó" ở phần 9, đóng gói thành một lệnh.
Với việc cần cấu trúc chặt hơn, TypeScript SDK nói chuyện trực tiếp với daemon:
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();
Client còn có agents.list() và agents.subscribe(). Chúng tôi có một service nhỏ dựng trên nó, lắng nghe các GitHub issue gắn nhãn agent-ok, tạo một agent trong worktree cho mỗi issue, rồi comment kết quả ngược lại lên issue. Khoảng một trăm dòng, phần lớn là lời gọi GitHub API.
Kết nối tới daemon, một cách an toàn
Một daemon chạy coding agent với credential của bạn là một trong những thứ giá trị nhất trên mạng của bạn trong mắt kẻ tấn công. Tài liệu bảo mật của Paseo ngắn và hợp lý, và tôi làm đúng như họ viết.
Mặc định là local. Daemon bind vào 127.0.0.1:6767. Cứ để vậy trừ khi bạn có lý do.
Từ điện thoại, dùng relay hoặc VPN. Relay tắt cho tới khi bạn bật lúc ghép cặp. Khi bật, nó được thiết kế để không cần tin tưởng: daemon giữ một cặp khoá cố định, mã QR hay đường link ghép cặp mang public key của nó, điện thoại thực hiện handshake, và message được mã hoá đầu cuối bằng NaCl box. Relay chỉ thấy địa chỉ IP, thời điểm, kích thước message và session id, không thấy nội dung. Hãy coi link ghép cặp như một mật khẩu. Cách còn lại là kết nối trực tiếp qua Tailscale: đặt daemon.listen trong ~/.paseo/config.json thành IP Tailscale của máy và thêm nó làm host trên điện thoại.
Không bao giờ bind vào địa chỉ mạng mà không đặt mật khẩu. Docs viết thẳng "never bind to 0.0.0.0 without a password", và họ đúng. paseo daemon set-password lưu một hash bcrypt trong config.json; client sau đó gửi nó dưới dạng Bearer token. Mật khẩu kiểm soát truy cập, không mã hoá, nên hãy đi kèm Tailscale hoặc TLS.
Plugin chạy không sandbox. Paseo có hệ plugin cho UI, hook vòng đời agent và provider mới, và plugin chạy trên daemon với đúng quyền của daemon. CLI gọi chúng là "trusted, unsandboxed plugins", chính xác. Chỉ cài code mà bạn sẵn sàng tự chạy dưới tên mình.
Agent cũng không được sandbox. Chúng chạy trong ngữ cảnh user của bạn với credential của bạn, hoặc với bất cứ thứ gì bạn mount vào container. Phần 4 vẫn áp dụng: thu hẹp phạm vi credential, dùng chế độ quyền, đưa việc rủi ro vào container hoặc VM dùng một lần.
Những đánh đổi
Một người bảo trì. Một công cụ team bạn phụ thuộc hằng ngày mà chỉ một người bảo trì thì bus factor bằng một. Điều đó không ngăn tôi dùng nó. Nó có nghĩa là chúng tôi ghim phiên bản, giữ paseo.json và phần dùng SDK đủ nhỏ để thay được, và tài trợ cho dự án, rẻ hơn nhiều so với tự duy trì một bản fork.
Nó đang ở 0.x và đổi nhanh. Flag thay đổi, như ví dụ trong README cho thấy. Đọc --help của phiên bản đã cài, và nâng cấp daemon cùng client một lượt.
Nó không review hộ bạn. App desktop của Paseo có diff, khung file, terminal và trình duyệt, dùng để review hoàn toàn được. Nhưng trọng tâm của nó là chạy và điều khiển agent, không phải đọc output của chúng đặt cạnh nhau.
Paseo hay Orca
Hai công cụ giải những bài toán chồng lấn từ hai đầu ngược nhau, và chúng tôi dùng cả hai.
| Orca | Paseo | |
|---|---|---|
| Trọng tâm | Không gian làm việc desktop để review nhiều agent | Một daemon chạy agent ở bất cứ đâu |
| Agent chạy ở đâu | Máy bạn, host SSH, hoặc một máy chạy orca serve | Bất cứ đâu daemon chạy: laptop, server, container |
| Điện thoại | App đồng hành để theo dõi và điều khiển | App đầy đủ, tính năng như desktop |
| Tự động hoá | CLI để agent điều khiển, orchestration thử nghiệm | CLI có output theo JSON schema, lịch chạy, TypeScript SDK |
| Telemetry | Ẩn danh, có thể tắt | Không có |
| Giấy phép và đội ngũ | MIT, một công ty | Apache-2.0, một người bảo trì |
Quy tắc kinh nghiệm của tôi: nếu tôi đang ngồi ở bàn để review, tôi ở trong Orca. Nếu công việc nên chạy trong lúc tôi vắng mặt, nó đi qua Paseo.
Bài cuối của đợt này lùi lại khỏi coding agent để tới nơi phần lớn doanh nghiệp sẽ gặp agent đầu tiên: các quy trình nghiệp vụ, với n8n.
Tài liệu tham khảo
- Paseo trên GitHub (Apache-2.0) và tài liệu
- Paseo docs, Worktrees and paseo.json, Connectivity, Security, CLI reference
- Paseo TypeScript SDK quickstart