@saadnvd1/agent-os 0.3.538 → 0.3.541

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,130 +12,30 @@ and check on all of it from your phone.
12
12
 
13
13
  ## What it does
14
14
 
15
- ### An orchestrator for each workspace
16
-
17
- Every workspace has one orchestrator, a chat pinned at the top of its list.
18
- It starts tasks, answers agents that are blocked, and reviews and merges
19
- pull requests through gates: CI green and settled, an independent review of
20
- the exact commit (big diffs are reviewed in parts), nothing blocked, and the
21
- diff in scope. Anything that is yours to
22
- decide (publishing, money, something irreversible, credentials) waits as an
23
- ask with Approve, Decline and Reply. Turn on **Require my approval** and
24
- sensitive or large PRs wait for you too. [More](#orchestrator)
25
-
26
- ![The Work orchestrator's chat, with two asks waiting: a merge gate and a public post](screenshots/orchestrator.png)
27
-
28
- ### Tasks that end in a pull request
29
-
30
- A task gets its own git worktree, branch and ports, opens as a chat, runs
31
- the project's code review before it opens its PR, and waits for your
32
- sign-off. A workspace's running task limit (Settings → Workspaces) caps how many run at once; the rest
33
- wait under **Queued** with their place in line and start on their own, and
34
- `aos task --after` holds one until another finishes. [More](#tasks)
35
-
36
- ### Each kind of work on the right model
37
-
38
- Planning, gate reviews and the orchestrator run on Opus, implementation and
39
- scope checks on Sonnet, titles and summaries on Haiku, each editable in
40
- Settings → Models and per workspace. A task plans on one model and
41
- implements on another in the same conversation, can be resumed on any
42
- model, and can run on another provider (DeepSeek, GLM, any
43
- Anthropic-compatible endpoint) whose key stays a reference.
44
- **Usage → Compare** shows Claude-only and routed tasks side by side.
45
- [More](#model-routing)
46
-
47
- ![A task's chat: the edit, the tests, the code review and the PR it opened](screenshots/task-chat.png)
48
-
49
- ![Two queued tasks in the sidebar, one in line and one waiting on another task, with its menu open](screenshots/queued.png)
50
-
51
- ### Nothing starts until you send
52
-
53
- ⌥N opens a draft in the project you're in. Project, machine, agent, worktree
54
- and base branch are chips on it, model and access sit under the composer, and
55
- **Open a PR when done** makes it a task. Unsent drafts wait in the sidebar.
56
- [More](#new-sessions)
57
-
58
- ![A new task draft with its project, machine, branch and PR chips](screenshots/draft.png)
59
-
60
- ### Other machines in the same list
61
-
62
- Link another machine that runs AgentOS and its sessions show up next to
63
- yours, with the same rows, menu and address. Their chats and terminals are
64
- relayed through this server. Pick the machine on a draft to start a session
65
- or task there, even for a project that only lives here (it finds or clones
66
- the repository), and move a running terminal task between machines.
67
- [More](#machines)
68
-
69
- ![A linked machine's sessions listed under its name in the sidebar](screenshots/machines.png)
70
-
71
- ### Built for your phone
72
-
73
- The whole app works on a phone, not a cut-down view. Every session has its
74
- own link (`/?session=<id>`) you can bookmark or share between devices, and
75
- pairing a new device is a QR code. Tailscale gives you an encrypted address
76
- that works from anywhere. [More](#mobile-access)
77
-
78
- ![AgentOS on a phone: a chat and the session list](screenshots/mobile.png)
79
-
80
- ### Agents that talk to each other
81
-
82
- Sessions find and message each other with `aos peers`, `aos send` and
83
- `aos inbox`, start tasks and sessions, and schedule check-ins. You see every
84
- conversation in **Messages** and can join in. [More](#agent-network)
85
-
86
- ![Messages between agent sessions, and one from you](screenshots/messages.png)
87
-
88
- ### And the rest
89
-
90
- - **Chat or terminal, same conversation.** Streaming replies, steps and
91
- diffs, plan mode, a queue you can steer, slash commands and skills,
92
- `@file`, undo, and inline charts and pages. ⌘F (Ctrl+F) finds text
93
- anywhere in the conversation, tool output and older pages included, and
94
- opens the step it's in. One click hands the conversation to the agent's
95
- own terminal. [More](#chat)
96
- - **Project config.** `agentos.json` (or an existing `.dispatch.json`) gives
97
- each session its own ports, environment, copied files, cloned
98
- dependencies, a private Postgres copy, and a "Running this project"
99
- section in the agent's brief. [More](#project-config-agentosjson)
100
- - **Schedules.** Start tasks, chats or orchestrator prompts on a timer, or
101
- check in on a session, with run history. [More](#schedules)
102
- - **Recipes.** Save a job that comes up again (a build, an audit, a report);
103
- each run is a fresh task or session that is told what the last run
104
- reported. [More](#recipes)
105
- - **Plans in pieces.** A plan splits into several PRs only for a real reason,
106
- checked by AgentOS; later pieces start by themselves. [More](#plans-in-pieces)
107
- - **Stacks.** Run a board's cards as stacked PRs that merge bottom-up.
108
- [More](#stacks)
109
- - **Machine load.** A gauge in the sidebar, each session's share of CPU and
110
- memory, and a note to agents before they start a full test suite or build
111
- on a busy machine. [More](#machine-load)
112
- - **Status from the screen and the agent.** Every session moves between
113
- Needs you, Working and Done as it changes, including programs that report
114
- their own state (OSC 7501). [More](#the-sidebar)
115
- - **Also:** a ⌘K palette for every session and action, code search, a file
116
- picker with uploads from a phone, a git panel (diffs, commits, PRs), dev
117
- servers (Node and Docker), up to four panes side by side, dictation in the
118
- terminal toolbar, and tmux sessions you started yourself listed under their
119
- project.
120
-
121
- ![The slash-command menu open in a chat](screenshots/commands.png)
122
-
123
- ![A session in terminal view, with the agent asking before it runs a command](screenshots/terminal.png)
124
-
125
- **Guides:** step-by-step how-tos are in [`docs/guides/`](docs/guides/), starting with [using AgentOS from your phone](docs/guides/phone.md).
15
+ - **An orchestrator for each workspace.** A chat that starts tasks, answers blocked agents, and merges pull requests only through gates. Anything that is yours to decide waits as an ask. [More](docs/reference/orchestrator.md)
16
+ - **Tasks that end in a pull request.** Each gets its own worktree, branch and ports, runs the project's code review, and waits for your sign-off. [More](docs/guides/tasks.md)
17
+ - **Each kind of work on the right model.** Planning, review, implementation and titles each run on their own model, set in Settings → Models. [More](docs/reference/model-routing.md)
18
+ - **Nothing starts until you send.** ⌥N opens a draft with project, machine, agent and branch as chips. [More](docs/guides/sessions.md)
19
+ - **Other machines in the same list.** Link another machine that runs AgentOS and start or move work there. [More](docs/reference/machines.md)
20
+ - **Built for your phone.** The whole app works on a phone; pair it with a QR code. [More](docs/guides/phone.md)
21
+ - **Agents that talk to each other.** Sessions message each other with `aos`, and you see every conversation in Messages. [More](docs/guides/agent-network.md)
22
+ - **Chat or terminal, same conversation.** Streaming replies, steps and diffs, plan mode, slash commands, `@file`, undo. [More](docs/guides/chat.md)
23
+ - **Project config.** `agentos.json` gives each session its own ports, environment, files and a private Postgres copy. [More](docs/reference/project-config.md)
24
+ - **Schedules and recipes.** Start work on a timer, or save a job that comes up again. [More](docs/guides/schedules.md)
25
+ - **Plans in pieces and stacks.** A plan splits into several PRs only for a real reason; a board can run as stacked PRs. [More](docs/guides/tasks.md#plans-in-pieces)
26
+ - **Machine load and status.** A load gauge, each session's share of CPU and memory, and Needs you, Working and Done as it changes. [More](docs/guides/sessions.md)
27
+ - **Workspaces.** Group projects, finish work with Done, and get a log of every merged task. [More](docs/reference/workspaces.md)
28
+ - **LumifyHub (optional).** Boards, cards and docs for a workspace, if you want them. [More](docs/lumifyhub.md)
29
+ - **Also:** a ⌘K palette, code search, a file picker, a git panel, dev servers, up to four panes, and dictation.
30
+
31
+ ## Documentation
32
+
33
+ - [Guides](docs/guides/README.md): [install](docs/guides/install.md), [phone](docs/guides/phone.md), [sessions](docs/guides/sessions.md), [chat](docs/guides/chat.md), [tasks](docs/guides/tasks.md), [schedules](docs/guides/schedules.md) and the [agent network](docs/guides/agent-network.md).
34
+ - [Reference](docs/reference/README.md): how each part works in detail, from the orchestrator's gates to the development workflow.
126
35
 
127
36
  ## Install
128
37
 
129
- You need Node.js 20+, tmux, and at least one agent CLI:
130
- [Claude Code](https://github.com/anthropics/claude-code),
131
- [Codex](https://github.com/openai/codex),
132
- [OpenCode](https://github.com/anomalyco/opencode),
133
- Pi,
134
- [Kilo Code CLI](https://kilo.ai/docs/cli),
135
- [Gemini CLI](https://github.com/google-gemini/gemini-cli),
136
- [Aider](https://aider.chat/), [Cursor CLI](https://cursor.com/cli), Amp or
137
- [GitHub Copilot CLI](https://github.com/github/copilot-cli) or
138
- [Kiro CLI](https://kiro.dev/docs/cli/).
38
+ You need Node.js 20+, tmux, and at least one agent CLI (the installer offers to set up what's missing).
139
39
 
140
40
  ```bash
141
41
  npm install -g @saadnvd1/agent-os
@@ -150,1890 +50,23 @@ curl -fsSL https://raw.githubusercontent.com/saadnvd1/agent-os/main/scripts/inst
150
50
  agent-os start
151
51
  ```
152
52
 
153
- From source:
154
-
155
- ```bash
156
- git clone https://github.com/saadnvd1/agent-os
157
- cd agent-os
158
- npm install
159
- npm run dev # http://localhost:3011
160
- ```
161
-
162
- Then open AgentOS, add a project from ⌘K, and press ⌥N. To use it from your
163
- phone, see [Mobile access](#mobile-access).
164
-
165
- ### Windows (WSL2)
166
-
167
- AgentOS runs on Windows inside WSL 2, as a Linux install. WSL 1 isn't
168
- supported (`wsl --set-version <distro> 2`). In an Ubuntu or Debian distro,
169
- run the install script above: it installs Node.js, tmux, ripgrep, lsof, a
170
- compiler for the native modules, and `wslu`, then prints the WSL next steps.
171
- The installer and the server both detect WSL the same way (the kernel
172
- release names Microsoft, or `WSL_DISTRO_NAME` is set).
173
-
174
- - **Open it from Windows** at `http://localhost:3011`. WSL 2 forwards
175
- localhost to the distro by default, and the server's loopback address is
176
- what's forwarded. Use `localhost`, not `127.0.0.1`: passkeys only work on
177
- localhost or https. With `networkingMode=mirrored` in `.wslconfig`
178
- (Windows 11), Windows and the distro share localhost directly. From a
179
- phone or another machine, install Tailscale inside the distro and use
180
- [Mobile access](#mobile-access) as on Linux.
181
- - **Keep projects in the Linux filesystem** (`~/code/...`), not on the
182
- Windows drive (`/mnt/c/...`). Git, npm and file watching are many times
183
- slower there, and changes made from Windows aren't seen by file watchers
184
- (inotify doesn't cross the boundary), so the "/" menu only picks up new
185
- skills after its cache expires. The folder picker warns on `/mnt/<drive>`,
186
- and the server log lists projects that live there.
187
- - **Links** open in the Windows browser through `wslview` (from `wslu`), or
188
- `explorer.exe` when it isn't installed. The server never touches the
189
- clipboard; copy buttons use the browser's.
190
- - **Approvals with a passkey** use Windows Hello in the Windows browser,
191
- registered for `localhost` (a passkey made on the tailnet https address is
192
- a separate one).
193
- - **Start at login**: `agent-os enable` writes a systemd user service, which
194
- needs systemd in the distro. Add this to `/etc/wsl.conf`, then run
195
- `wsl --shutdown` from Windows and reopen the terminal:
196
-
197
- ```ini
198
- [boot]
199
- systemd=true
200
- ```
201
-
202
- WSL still stops an idle distro, service and all, a few seconds after its
203
- last terminal closes. To keep AgentOS up without a terminal open, add this
204
- to `%UserProfile%\.wslconfig` (Windows 11) and start the distro at Windows
205
- login, for example with a Task Scheduler task running
206
- `wsl.exe -d <distro> -- sleep infinity`:
207
-
208
- ```ini
209
- [general]
210
- instanceIdleTimeout=-1
211
- [wsl2]
212
- vmIdleTimeout=-1
213
- ```
214
-
215
- What's been verified: the WSL detection and the installer's WSL paths have
216
- unit tests that run on any OS, and the manual [WSL workflow](.github/workflows/wsl.yml)
217
- ran on a GitHub-hosted Windows runner (Windows Server 2025, Ubuntu 24.04
218
- under WSL 2): the installer detected WSL 2 and installed what was missing
219
- through apt, tmux, lsof and node-pty worked, the server started and logged
220
- its WSL note, and Windows reached it at both `localhost:3011` and
221
- `127.0.0.1:3011`. Untested: Windows Hello approvals, links opening through
222
- `wslview`/`explorer.exe`, mirrored networking, `agent-os enable` under
223
- systemd, the idle-timeout settings above, and Debian or other distros.
224
-
225
- ### First run
226
-
227
- On a fresh install (no sessions, no projects of your own) the web app opens a
228
- short setup at `/?welcome`: it shows each agent CLI as ready, needing a
229
- sign-in, or missing, installs or signs one in from an embedded terminal, adds
230
- a first project (a folder, a clone or an empty one), then opens a session in
231
- it. A devices step offers to pair a phone, link another machine and set up
232
- notifications. Every step after the first can be skipped. It is remembered on the server,
233
- so a phone you pair later doesn't see it, and an install that already has work
234
- never does. Reopen it from Settings → Setup (**Run setup again**) or at
235
- `/welcome`; `AGENTOS_SKIP_WELCOME=1` turns it off. Settings → Setup & health
236
- also lists what this machine needs (Node, git, tmux, ripgrep, lsof, an agent
237
- CLI) with the fix for anything missing (`/api/doctor`).
238
-
239
- ### Supported agents
240
-
241
- | Agent | Chat | Resume | Fork | Auto-approve |
242
- | -------------- | ---- | ------ | ---- | -------------------------------------------- |
243
- | Claude Code | ✅ | ✅ | ✅ | `--dangerously-skip-permissions` |
244
- | Codex | ✅ | ✅ | ✅ | `--dangerously-bypass-approvals-and-sandbox` |
245
- | OpenCode | ✅ | ✅ | ✅ | `--auto` |
246
- | Pi | ✅ | ✅ | ✅ | Never asks |
247
- | Kilo Code | ❌ | ✅ | ✅ | Config file |
248
- | Gemini CLI | ❌ | ❌ | ❌ | `--yolo` |
249
- | Aider | ❌ | ❌ | ❌ | `--yes` |
250
- | Cursor CLI | ❌ | ✅ | ❌ | `--force` |
251
- | Amp | ❌ | ✅ | ❌ | `--dangerously-allow-all` |
252
- | Oh My Pi | ❌ | ❌ | ❌ | N/A |
253
- | GitHub Copilot | ✅ | ✅ | ❌ | `--allow-all` |
254
- | Kiro CLI | ❌ | ❌ | ❌ | `--trust-all-tools` |
255
- | Amazon Q (old) | ❌ | ❌ | ❌ | `--trust-all-tools` |
256
-
257
- Agents without chat run in the terminal. The new-session picker says when an
258
- agent isn't installed or needs a sign-in first, and what to run.
259
-
260
- Kiro CLI (and the older Amazon Q CLI, `q`, shown only where it is installed)
261
- signs in on its own: a session first checks `whoami`, and if you're signed out
262
- runs `login --use-device-flow`. AgentOS reads the link and one-time code off
263
- the screen and shows them in a card above the terminal, with a push when it's
264
- waiting, so you can finish on your phone. No credential passes through
265
- AgentOS. Identity Center sign-in (including Microsoft or Okta federation) works
266
- this way; a login through an external identity provider doesn't support the
267
- device flow, so sign in once on the machine itself, or set `KIRO_API_KEY`.
268
-
269
- ### CLI
270
-
271
- ```bash
272
- agent-os run # start and open the browser
273
- agent-os start # start in the background
274
- agent-os stop # stop the server
275
- agent-os restart # stop, then start
276
- agent-os status # show its addresses
277
- agent-os pair # add a phone, tablet or laptop (prints a QR code)
278
- agent-os logs # tail the logs
279
- agent-os update # update to the latest version
280
- agent-os enable # start at login (disable turns it off)
281
- agent-os passkeys reset # when every passkey is lost (see Orchestrator)
282
- agent-os uninstall
283
- ```
284
-
285
- # Reference
286
-
287
- Everything below is how each part works, in detail.
288
-
289
- ## Chat
290
-
291
- Sessions open as a chat by default: streaming replies, tool calls folded into
292
- expandable steps, inline diffs for edits, a plan checklist, highlighted code
293
- and mermaid diagrams, subagent cards, image attachments (pick, paste or drag
294
- them in) and a Stop button, all readable on a phone. Chat drives the same
295
- agent as the terminal, with your own logins: Claude Code through the Agent
296
- SDK, Codex through `codex app-server`, OpenCode through its own `opencode
297
- serve` (loopback only, behind a per-conversation password) and Pi through its
298
- RPC mode. Pi never asks before a tool, so AgentOS loads a small Pi extension
299
- that asks the chat as the access setting says. GitHub Copilot runs through
300
- `copilot --acp` (the Agent Client Protocol): its permission prompts become
301
- approval cards under the same access setting, and a conversation resumes with
302
- `session/load` after a restart. It signs in with `copilot login` or a
303
- `COPILOT_GITHUB_TOKEN`, `GH_TOKEN` or `GITHUB_TOKEN`; it has no plan mode,
304
- undo or in-chat model list yet, so it runs on its own default model unless one
305
- is named. The **Chat /
306
- Terminal** switch in the tab bar hands the same conversation between the two:
307
- the terminal resumes it (`claude --resume`, `codex resume`, `opencode
308
- --session`, `pi --session`, `copilot --resume`), and switching back closes the
309
- terminal so only one side drives it. Each chat conversation runs in its own
310
- worker process (in tmux, like terminal sessions), so restarting or updating
311
- AgentOS never cuts off a turn: the server reconnects to running workers when
312
- it starts. A worker from an older build finishes its turn, and any
313
- background work (subagents, shells), then hands over at the next turn
314
- boundary: messages that arrive meanwhile wait in the queue, and a current
315
- worker resumes the same conversation with this build's tools and sends them.
316
- An orchestrator fed events mid-turn picks up new tools this way with nobody
317
- restarting it. A turn whose worker did go away mid-step is resumed
318
- once, with a note telling the agent to check what it was doing, and a task's
319
- orchestrator hears about it. History is kept in AgentOS.
320
-
321
- The composer formats markdown as you type: `code`, **bold**, _italic_, lists,
322
- `- [ ]` task lists, and ` ```ts ` fenced blocks with syntax highlighting.
323
- What it sends is the markdown you typed, character for character. Code pasted
324
- from an editor (or anything multi-line that reads like code) lands in a code
325
- block, prose stays prose, and a paste of 32 KB or more becomes an attachment
326
- sent as a fenced block. Cmd/Ctrl+Shift+V pastes as plain text, and the **T**
327
- button (Cmd/Ctrl+/) turns live formatting off for this browser. Enter sends on
328
- a keyboard and is a newline on a phone; Cmd/Ctrl+Enter always sends.
329
- Shift+Enter is always a newline, and three of them at the end of a code block
330
- leave it.
331
-
332
- Type `/` in the composer for every slash command and skill the agent knows,
333
- yours included, filtered as you type. A skill or command you add or edit
334
- (in `~/.claude/skills`, `~/.claude/commands` or the project's own) shows up
335
- on its own; the menu's refresh button reloads the list now. Commands such as `/compact`, `/usage`
336
- and `/context` run in chat and show their output inline; skills the agent
337
- invokes on its own appear as a chip. `/model` (or the picker under the
338
- composer) switches the model mid-conversation. `/mcp` lists the session's MCP
339
- servers with their status and tools, without spending a turn. Commands that
340
- only make sense in a terminal, such as `/vim`, are left out, and say so if
341
- typed anyway.
342
-
343
- Type `@` for files and folders, matched the way Claude Code matches them
344
- (ripgrep over the folder until the agent is running), and pick one to insert
345
- `@path`. After each turn but the first, the agent's guess at your next message
346
- shows in the empty composer: **Tab** or **→** takes it, typing or **Esc** sets
347
- it aside; on a phone it's a chip to tap. **↑** and **↓** in an empty composer
348
- walk back through what you've sent. Select text in a reply and **Quote** puts
349
- it in the composer as a blockquote.
350
-
351
- A message sent while the agent is working waits in a queue above the
352
- composer, kept on the server so a reload doesn't lose it, and goes when the
353
- turn ends. Queued messages can be edited, moved or removed, and **Send now**
354
- stops the turn (as Esc does) and sends that one with the stop, so it's the
355
- very next thing the agent reads, ahead of a background task's notice it had
356
- waiting. A message typed right after Esc goes the same way.
357
-
358
- **Plan mode** (the **Plan** toggle under the composer, or Shift+Tab) has the
359
- agent read and plan without changing anything; it's remembered per session.
360
- When the agent has a plan it shows up as a card: **Carry it out** leaves plan
361
- mode and starts it, **Keep planning** goes back to the composer.
362
-
363
- The ring in the tab bar (in the composer on a phone) is how full the
364
- conversation's context is, measured against the room before auto-compact:
365
- amber past 60%, red past 85%. Tap it for the breakdown and **Compact now**.
366
-
367
- Two thresholds (Settings → Workspaces → Compact; 0 turns one off). Mid-turn,
368
- the agent compacts itself at 300k by default: AgentOS launches Claude Code
369
- with `CLAUDE_CODE_AUTO_COMPACT_WINDOW` set to that size (its floor is 100k),
370
- high enough that a long job isn't cut in half. Between
371
- turns AgentOS catches what it didn't: a chat over its 150k threshold is compacted
372
- before the next message, and so is one idle past its prompt cache (5 minutes
373
- or an hour, read from the agent's own usage), whose next message would
374
- re-read the whole conversation at the cache-write price. A chip above the
375
- composer says "Idle 47m · 412k context · Compact first?": Send compacts and
376
- then sends, **Send as-is** skips it for that one message. Messages nobody is
377
- typing (the orchestrator's `send`, AgentOS events and wakes, `aos send`,
378
- schedules) compact first on their own, only between turns, and the chat
379
- shows one quiet line with the context before and after. An orchestrator
380
- isn't compacted (a summary would lose the fence around what it read from
381
- tasks and CI): it starts a fresh conversation from its brief and the current
382
- state at its fresh-start threshold, or earlier once idle past its cache.
383
- While a chat compacts (any trigger, Claude, Pi or Codex) it is shown as
384
- compacting, not just working: a pulsing blue dot and a "Compacting" chip in
385
- the sidebar, and "Compacting context… 182k tokens" above the composer. The
386
- status API (`/api/sessions/status`) keeps `status: "running"` and adds
387
- `compacting: { since, before? }`; the orchestrator's `sessions` tool says
388
- "compacting context". It clears when the compaction ends, errors, is
389
- stopped, or the worker goes away.
390
- What survives a compaction: the summary is asked to keep the task, scope
391
- changes, every edited file by path, what's done, in progress and next, and
392
- the decisions. Right after it, AgentOS hands back a note built from its own
393
- records (a task's brief and scope changes, the files it edited, its todos,
394
- the latest message and `git status`), which doesn't depend on what the
395
- summary kept, and tells the agent not to re-read or rewrite whole files.
396
- Terminal agents don't get the note yet.
397
- On a desktop browser the screen button attaches a screenshot of a screen or
398
- window you pick; on a phone, attach a screenshot as a photo.
399
-
400
- **Usage** (the sidebar's ⋯ menu, or the palette) shows the account's 5-hour
401
- and weekly windows, and what chats cost today, over 7 or 30 days, by day,
402
- workspace and session. Costs are the agent's own estimates, recorded as each
403
- turn ends. **Gate reviews** below it shows what the orchestrator's
404
- independent reviewer and scope check cost, by PR: they run as `claude -p`
405
- and leave no transcript, so each run's tokens, model and duration are read
406
- from its JSON result into `check_runs`, and the review's verdict says them
407
- too. Every compaction is kept in `chat_compactions` (who ran it, whether it
408
- fired `mid_turn` or `between_turns`, and the context before and after), so cost per task can be compared by date.
409
- Rows from before the column existed, and any written by a chat worker still
410
- running older code, have a NULL `phase`: read those as unknown
411
- (`COALESCE(phase, 'unknown')`), not as either kind.
412
-
413
- **Esc** stops a running turn, as it does in Claude Code's terminal, unless a
414
- menu, dialog or the command list is open (those close first). On a phone the
415
- Stop button does the same. A stopped turn keeps what it wrote.
416
-
417
- Stop ends the turn even while the model is still thinking, before any tool
418
- call: the turn is interrupted, and if it hasn't ended within a few seconds the
419
- agent process is ended and restarted for the next message (not if the chat is
420
- waiting on an approval or question, or if a new message has started a new
421
- turn meanwhile). The conversation and
422
- worktree are kept, and whatever is sent next (yours, another agent's, the
423
- orchestrator's) runs as the next turn, never queued behind the stopped one.
424
- The UI, the orchestrator's `stop` tool and `aos stop` all take this one path
425
- and report "stopped" or a real error.
426
-
427
- A turn that has produced nothing (no token, tool call or result) for 8 minutes
428
- shows as **Stalled** in the session list, and its workspace's orchestrator gets
429
- one event naming the session, how long it has been silent and its last action.
430
- It fires once per stall and starts over when the session does something. A
431
- foreground tool call gets four times as long, and a session waiting on a
432
- background command it started isn't flagged. Set the time (2 to 120 minutes, 0 for off)
433
- in Settings → Workspaces → Stall alerts.
434
-
435
- The access picker under the composer sets what the agent may do on its own:
436
- **Ask first** (an approval card in the chat for anything not already allowed),
437
- **Accept edits** (edits files, asks before other commands) or **Full access**
438
- (the default, like running with permissions skipped). Questions the agent asks
439
- you come up as cards to answer. **Undo** on a message puts back the files the
440
- agent edited since, rewinds the conversation to before it, and returns the
441
- message to the composer to edit and resend. Changes made by shell commands
442
- aren't tracked, so they stay.
443
-
444
- While a turn runs, a line above the composer says what the agent is doing
445
- and for how long (with Stop), and every step shows its time. Work the agent
446
- starts in the background (shells, subagents, monitors) sits behind a
447
- "running in background" chip: each task with its elapsed time, its live
448
- output, and its own Stop. The session list says the same thing, so a long
449
- wait never looks like nothing is happening.
450
-
451
- **Visuals.** Every chat agent (not an orchestrator, which reads other
452
- sessions' text and gets no browser) has two tools: `html_preview` renders a page in
453
- headless Chrome and hands back a screenshot plus the console, so it can check
454
- its own work, and `html_render` shows the finished page (a chart, a table, a
455
- mockup) inline as its own card, with full screen and "open in new tab". Pages
456
- are kept under `~/.agent-os/artifacts/<session>/` with a row in SQLite, so
457
- they survive reloads and restarts (`GET /api/sessions/<id>/artifacts` lists
458
- them). They're served behind the same device gate with a CSP sandbox and no
459
- same-origin access, so a page can't read AgentOS cookies or call its API; the
460
- preview browser gets an empty profile and a proxy that only reaches public
461
- addresses. An image or SVG file the agent writes shows inline when its reply
462
- links to it by absolute path. The tools read files and reach the web, so
463
- under **Ask first** they ask like any other tool. Previews need Chrome, Chromium, Brave or Edge
464
- installed (or `AGENTOS_CHROME` pointing at one).
465
-
466
- Chat runs on this machine, and on linked machines through their own AgentOS
467
- (see [Machines](#machines)); sessions on machines reached over ssh only use
468
- the terminal.
469
- Drivers for other agent CLIs plug into `lib/chat/drivers`; undo, `@file`
470
- suggestions and visuals are Claude-only for now, and plan mode is Claude and
471
- OpenCode.
472
-
473
- ## Tasks
474
-
475
- Hand off work and keep going. A task is a draft with **Open a PR when
476
- done** turned on (**New task** in ⌘K, the sidebar's New menu or the Tasks
477
- panel opens one). AgentOS creates a git worktree and branch, starts Claude there in tmux
478
- with a brief to finish by pushing and opening a pull request, and tracks it:
479
- Working, Needs input, Blocked, Ready for review, Checks failing or Agent
480
- exited. **Sign off & merge** merges the PR (refused while CI is failing
481
- or pending), then removes the session, worktree and branches. **Drop** closes
482
- the PR and removes everything. Agents never merge their own work.
483
-
484
- **Merging settings.** How a PR merges (sign-off, Done, Land and the
485
- orchestrator's external PRs alike) and what's cleaned up after it are set in
486
- **Settings → Merging** (or ⌘K → Merge settings) for every project,
487
- and overridden per project in its **Project Settings → Merging** or in its
488
- `agentos.json`:
489
-
490
- ```json
491
- {
492
- "merge": {
493
- "method": "rebase",
494
- "delete_remote_branch": true,
495
- "delete_worktree": false
496
- }
497
- }
498
- ```
499
-
500
- `method` is `squash` (the default), `merge` (a merge commit) or `rebase`.
501
- GitHub enforces the repository's allowed methods: if it refuses the one set,
502
- the merge fails with a message naming the method and the repository setting
503
- to turn on (Settings → General → Pull Requests), and AgentOS never falls back
504
- to another method. The project's settings show a method its repository has
505
- turned off. `delete_remote_branch` and `delete_worktree` (both on by default)
506
- delete the branch on origin and the local worktree with its branch after a
507
- merge. With either on, `main`, `master` and `HEAD` are never deleted, nor a
508
- branch a stacked task still targets, nor a worktree holding uncommitted work
509
- or commits the merge didn't include. The most specific setting wins: the
510
- project's, then `agentos.json`, then the global one.
511
-
512
- **Queued tasks.** A workspace can limit how many tasks run at once (none by
513
- default; set it in **Settings → Workspaces**, or `PATCH /api/workspaces/<id>` with `{"maxRunningTasks": 3}`, or
514
- `null` for no limit). A task started over the limit is queued, with no
515
- worktree or agent yet, and starts by itself in line order when one finishes
516
- (merged, dropped or done). `aos task --after <task|any>` (and the
517
- orchestrator's `start_task` with `after`) holds a task until a named task
518
- finishes, or until any task still working now does (a running task whose PR
519
- was closed or merged doesn't count; with none, it starts at once). The
520
- orchestrator's `drop` removes a queued task by its id. Pause stops automatic starts,
521
- and the orchestrator's brakes still apply when its queued tasks start. A task runs on Claude Code unless
522
- `POST /api/tasks` names another `agent` (`"copilot"`): that one runs as a chat on
523
- this machine, with no plan-first step or role models, and is never picked for
524
- you. The
525
- line is kept in the database across restarts (`GET /api/tasks/queue`; start
526
- now, move up or down, or remove with `/api/tasks/queue/<id>`). The sidebar
527
- shows them under **Queued** with their place in line or what they wait on,
528
- and the same actions in each row's menu; a row becomes the task's own row
529
- when it starts. Why it works
530
- this way: [docs/decisions/2026-10-10-queued-tasks.md](docs/decisions/2026-10-10-queued-tasks.md).
531
-
532
- A paired device's token can run the building loop through read-only routes:
533
- `GET /api/start-options?projectId=` (agents and models, the models a task can
534
- run on, machines, branches, whether a task plans first, in one request),
535
- `GET /api/tasks/<id>/diff` (the task's whole change, base to head: a file list,
536
- then `?file=<path>` for one file's patch; capped, with `truncated` when a cap
537
- is hit; read from the worktree, else the PR), and `GET /api/tasks/<id>/checks`
538
- (the PR head's checks, the tail of a failing job's log, and the review gate's
539
- verdict). A task's `plan` in `GET /api/tasks` carries `status` (`writing`,
540
- `ready` or `approved`), a title and capped text. Their shapes are in
541
- `lib/tasks/loop-types.ts`, which has no server imports so a client can share it.
542
-
543
- ## Model routing
544
-
545
- Each kind of work AgentOS launches an agent for runs on its own model, set in
546
- **Settings → Models** (⌘K → Model settings). A model is named
547
- `provider/model`:
548
-
549
- | Role | Default |
550
- | --------------------------------------------------- | ------------------------------------- |
551
- | Task planning (a task's plan step) | `anthropic/claude-opus-5-5` |
552
- | Gate review | `anthropic/claude-opus-5-5` |
553
- | Task implementation | `anthropic/claude-sonnet-5-5` |
554
- | Orchestrator | `anthropic/claude-opus-5-5` |
555
- | Scope check | `anthropic/claude-sonnet-5-5` |
556
- | Interactive sessions | `anthropic/claude-opus-5-5` |
557
- | Chat & summary helpers (titles, summaries, PR text) | `anthropic/claude-haiku-4-5-20251001` |
558
-
559
- The most specific setting wins: a task's own model, then its workspace's
560
- (**Settings → Workspaces → Models**), then Settings, then the default.
561
- `AGENTOS_REVIEW_MODEL` (gate review and scope check) and
562
- `AGENTOS_TITLE_MODEL` (helpers) in the server's environment still replace a
563
- role's default where Settings names none. A
564
- Claude session you start without a model runs on the interactive role's. One
565
- an orchestrator starts (its `start_session`, or `aos spawn` run from an
566
- orchestrator) is recorded as started by it (`started_by`) and runs on the
567
- implementation role instead, so delegated work doesn't use Opus by default;
568
- `start_session` takes a `model` (`"provider/model"`, checked like a task's:
569
- gate roles can't be set, other providers must be enabled). One you pick a
570
- model for keeps it.
571
-
572
- **Plan, then implement.** A chat task whose planning and implementation
573
- models differ starts with a plan step: its first turn runs on the planning
574
- model and writes the plan to `.agentos/plan.md` in its worktree (ignored by
575
- git), changing nothing else. When that turn ends, the same conversation
576
- switches to the implementation model (a live model switch, or a resume with
577
- `--model` when the provider changes) and is told to follow the plan. Turn it
578
- off in Settings, or per task with `plan: false`. Terminal tasks skip it. An
579
- orchestrator decides it for every task: `start_task` requires `plan` and a
580
- short `plan_reason`, stored on the task and shown on its card ("Planned:
581
- touches 6 files across gates and UI" / "No plan: one-line default change"),
582
- and **Usage → Compare** splits cost into planned and not. When the plan step
583
- finishes, the orchestrator gets one event with the plan file's path and a
584
- 3-5 line summary; the handoff to implementation stays automatic.
585
-
586
- **Plan only.** A task started with `plan_only` (`start_task`'s `plan_only`,
587
- `aos task --plan-only`, or `plan_only: true` on `POST /api/tasks`) writes its
588
- plan on the planning model and then stops: no model switch, no implement
589
- prompt, even when one model does every role. It sits in a **Plan ready**
590
- state (`plan_phase` `awaiting`, stored on the task, so it survives a restart)
591
- that isn't blocked, doesn't count as needing you, and holds no agent process
592
- (its worker is released; the conversation resumes on approval). The
593
- orchestrator gets one event with the plan's path, a summary and that the task
594
- is waiting. **Approve** on the task row (plan viewable there), `POST
595
- /api/tasks/<id>/approve-plan`, or the orchestrator's `approve_plan(task,
596
- message?)` continues it on the implementation model, once; a message amends
597
- the plan and is recorded on the task's brief as a scope change, so the review
598
- judges against it. Reject with **Drop**. A chat task on this machine only; it
599
- can't move until approved.
600
-
601
- **Per task.** The task draft has a **Model** chip ("Routed by role", or one
602
- model); `POST /api/tasks` and the orchestrator's `start_task` take `model`, as
603
- `"provider/model"` for the task's own roles or a map such as
604
- `{"implement": "zai/glm-5.3"}`, and `plan`. Its gate review and scope check
605
- never follow a task's model, only Settings and its workspace, so work can't
606
- pick its own reviewer. **Resume on model…** on a task's row
607
- (or `POST /api/tasks/<id>/model`, or the orchestrator's `set_task_model`)
608
- moves a paused task to another model: same conversation, same worktree, and
609
- it keeps that model from then on. Each turn records the model, provider and
610
- role it ran as.
611
-
612
- **Providers.** `anthropic` is your Claude login and needs no key. Others are
613
- Anthropic-compatible endpoints (OpenAI-compatible ones can be defined and
614
- tested, but Claude Code can't run on them): a base URL, where the key lives,
615
- the models, and Claude Code's opus/sonnet/haiku tiers mapped to them so
616
- subagents and background calls stay on that provider. DeepSeek
617
- (`https://api.deepseek.com/anthropic`) and Z.ai GLM
618
- (`https://api.z.ai/api/anthropic`) ship as presets, off until given a key.
619
- Claude stays the default: another provider runs only where a role or a task
620
- picks it. If a role's provider is turned off or its key can't be read,
621
- helper runs, reviews and the orchestrator fall back to the role's default
622
- rather than fail. **Test provider** sends one tiny request and says whether it
623
- answered. Changing models, providers or the test runs only from this machine
624
- or over Tailscale, never from a paired device: a provider's URL and key
625
- reference decide where a key is sent.
626
-
627
- The key is never stored by AgentOS. A provider names where to read it: a
628
- tokenvault entry (`tv get <project> <desc>`), a macOS Keychain item
629
- (`security find-generic-password -s <service> -w`) or an environment
630
- variable named `*_API_KEY` (not `ANTHROPIC_`, `CLAUDE_` or `AGENTOS_`). It's read when an agent launches and goes only into that agent's
631
- environment (`ANTHROPIC_BASE_URL` with `ANTHROPIC_AUTH_TOKEN` or
632
- `ANTHROPIC_API_KEY`), never its command line, the database, a prompt or a
633
- log; a terminal task's shell reads it from the reference itself. Errors that
634
- might echo a key are scrubbed of it.
635
-
636
- **Usage → Daily: the usage ledger and the ratchet.** Every Claude response
637
- an AgentOS chat makes (subagents and the orchestrator included) is written to
638
- `usage_calls` as it arrives, with its input, cache read, 5-minute and 1-hour
639
- cache write and output tokens, its model, its context size and the
640
- session, task, workspace and role it belongs to. Terminal sessions, which no
641
- worker watches, are read from Claude's transcripts, and gate reviews come
642
- from `check_runs`; the same message id is never counted twice. Tokens are
643
- _weighted_ by the API's price ratios (Opus input 1, 5-minute write 1.25,
644
- 1-hour write 2, cache read 0.1, output 5; Sonnet half, Haiku a quarter), an
645
- estimate of how much of the plan's limits a call uses.
646
-
647
- The 5-hour and weekly windows come from the `rate_limit_event`s Claude Code
648
- sends on each response (the `anthropic-ratelimit-unified-*` headers), stored
649
- with their time in `rate_limit_readings` with no extra calls. An event
650
- carries one window, so the weekly figure can go stale: when it is over 6
651
- hours old it is estimated from recorded usage and marked `~`.
652
-
653
- A rollup job inside the server (no model) writes one `usage_rollups` row per
654
- day: weighted total, cost per merged PR, cost per _clean_-merged PR (the
655
- headline: cost against quality), review cost per PR, the share of calls above
656
- 200k context, model mix, orchestrator share, the weekly %, and the quality
657
- readings from existing data: first-try clean-merge rate (the first reviewed
658
- head passed review and CI, no gate failures, no later head), fix rounds per
659
- PR, gate escalations, scope changes, and escapes (a revert or fix PR touching
660
- the same files within 3 days, from `gh`; unknown rather than zero if `gh`
661
- fails). Spans of days are ratios of sums. The baseline is the first week of
662
- rollups and only ever moves to a better value. One phone alert fires (and
663
- stays quiet until it clears) when the 3-day cost per merged PR is over
664
- baseline by 15%, or the clean-merge rate is under it by 10 points, naming the
665
- supporting metric that moved most. The margins, the days averaged and the
666
- minimum merges to judge are settings (`usage.alert_cost_pct`,
667
- `usage.alert_clean_points`, `usage.avg_days`, `usage.alert_min_merges`,
668
- `usage.baseline_days`, `usage.weekly_stale_hours`). A dev server doesn't run
669
- it unless `AGENTOS_USAGE_ROLLUP=on`; `AGENTOS_USAGE_ROLLUP=off` turns it off
670
- in production. `POST /api/usage/rollups` runs the nightly pass now. Task
671
- cards show each task's weighted cost so far.
672
-
673
- **Usage by model.** **Usage → By model** shows tokens and estimated cost per
674
- role and per model in the plan's 5-hour window and the last 24 hours, from
675
- the price table in Settings → Models (dollars per million input, cache
676
- write, cache read and output tokens, editable).
677
-
678
- **How to read the comparison.** **Usage → Compare** (or ⌘K → Model
679
- comparison, or the sidebar menu) puts Claude-only tasks (the baseline,
680
- including everything from before routing) beside routed ones, per provider,
681
- over 7, 30 or 90 days, with a total column. A task counts under the provider
682
- its own planning and implementation turns ran on; its reviews and checks add
683
- to its cost wherever they ran. For each column: tasks, turns, tokens,
684
- estimated dollars (list prices; for Claude, what the API would charge),
685
- Claude window points per task (how far each turn moved the plan's 5-hour
686
- window, an estimate), agent time, CI green on the first push, review passed
687
- on the first try, gate failures and rework commits per task (heads checked
688
- after the first review), and merged or dropped. Outcomes come from the
689
- orchestrator's checks, so tasks it didn't run have cost but no outcome. A
690
- column with fewer than 10 tasks is marked a small sample: a hint, not a
691
- result. Parity means the routed column's outcome rows match the baseline's
692
- while its window points per task drop. Why it works this way:
693
- [docs/decisions/2026-10-11-model-routing.md](docs/decisions/2026-10-11-model-routing.md).
694
-
695
- ## New sessions
696
-
697
- ⌥N (the **+** on a phone) opens a draft: a composer in the current project, or
698
- the one you used last. Nothing exists until you send. Then the session is
699
- made, and with a worktree its setup runs in the chat, stage by stage: fetch,
700
- worktree, env files, dependencies, the project's setup script, with a log
701
- tail. The first message waits in the queue until setup is done, so the agent
702
- never starts on half-installed dependencies, and a restart can't lose it. The
703
- worktree starts on a temporary branch, renamed from the first message once
704
- it has a title (it keeps the temporary name if there's nothing to name it
705
- from).
706
-
707
- ⌥N reuses the project's empty draft; a draft you've typed in stays in the
708
- sidebar's **Drafts** until you send or discard it. A new draft takes the
709
- agent, model and access you were using; a project's own agent and model win.
710
- Chats with no project run in `~/.agent-os/scratch`.
711
-
712
- A task or session started from a prompt gets a short name: the brief's first
713
- heading at once (when the prompt points at a readable `.md` brief), then a
714
- 2-6 word title from a quick Haiku call a few seconds later. A chat opened as
715
- "Session 4" is named after its first message. Anything named by hand, a card's
716
- title, a schedule's name or `--name` is kept as given.
717
-
718
- The agent starts once its worktree has its dependencies. On macOS the main
719
- checkout's `node_modules` (and workspace ones like `apps/web/node_modules`)
720
- are cloned with `cp -Rc` when the lockfile matches, and a spare clone waits in
721
- `~/.agent-os/spare` so the next task takes it by rename, in milliseconds.
722
- Otherwise, and on Linux, the lockfile's frozen install runs (`npm ci`,
723
- `pnpm/yarn/bun --frozen-lockfile`), then a plain install, always with
724
- devDependencies even though the server runs with `NODE_ENV=production`. A
725
- project's `agentos.json` can name exactly what to copy and clone, and its
726
- `setup` commands (without a `clone`) replace the install; see
727
- [Project config](#project-config-agentosjson).
728
- Starting a task returns at once and shows **Setting up** until the agent
729
- launches; a restart in the middle resumes it. A failed setup shows on the task
730
- and is in the agent's first prompt (redacted, as data).
731
-
732
- Tasks don't review their own PRs. The orchestrator's independent review of
733
- the PR's exact head commit is the one review: `sign_off` and a stack's
734
- **Land** refuse a PR without a passing one, and a PR body with AI
735
- attribution. A head AgentOS rebased itself is covered by the review of the
736
- head it rebased while the PR's own patch is unchanged. A task may still run
737
- `/do-code-review` (the project's `.claude/skills/do-code-review`, or Claude
738
- Code's `/code-review`) on a risky change before its PR; it's no longer
739
- required. The review agents are in [.claude/agents](.claude/agents) and the
740
- skill in [.claude/skills](.claude/skills/README.md).
741
-
742
- Requires the GitHub CLI (`gh`) signed in, and a project with a GitHub remote.
743
-
744
- ![Tasks in different states: needs input, working, merged](screenshots/tasks.png)
745
-
746
- Tracking PRs stays inside GitHub's GraphQL quota (5,000 points an hour).
747
- Only running tasks are asked about: a finished task's PR comes from the
748
- database. One `gh pr list --state open` per repository a minute covers every
749
- running task with an open PR, and a branch without one is asked about on its
750
- own at most every 5 minutes. A merge or sign-off always asks fresh. When gh
751
- reports a rate limit, every gh call backs off until the limit resets, and gh
752
- failures are logged as `[gh] ...`, at most once a minute for each kind.
753
-
754
- ### Project config (`agentos.json`)
755
-
756
- A project describes how its sessions run in `agentos.json` at the repository
757
- root. Every field is optional:
758
-
759
- ```json
760
- {
761
- "ports": { "RAILS_PORT": 3000, "VITE_PORT": 3100 },
762
- "env": { "STORAGE_PROVIDER": "disk" },
763
- "copy": [".env", "config/master.key"],
764
- "clone": ["node_modules"],
765
- "setup": ["bin/setup"],
766
- "dev": "bin/dev",
767
- "ready": { "url": "/up", "port": "RAILS_PORT" },
768
- "test": "bundle exec rspec",
769
- "browse": { "login": "/dev/login", "map": "docs/browsing.md" },
770
- "database": { "from": "app_development", "env": "DATABASE_NAME" },
771
- "notes": ["Kill your own server by port: lsof -ti tcp:$RAILS_PORT"],
772
- "merge": { "method": "squash" },
773
- "deploy": {
774
- "deployed": "~/.deploy/deployed",
775
- "health": "https://app.example.com/up"
776
- }
777
- }
778
- ```
779
-
780
- `merge` sets the project's merge method and branch clean-up (see
781
- [Merging settings](#tasks)).
782
-
783
- `deploy` is how [Away mode](#orchestrator) checks a merge went live: `deployed`
784
- is a file your deploy writes the deployed commit to, `failed` (optional) one it
785
- writes a commit whose deploy failed to, and `health` (optional) a URL that
786
- answers 2xx once the server is back (a path alone is this AgentOS server's own,
787
- as this repository's `agentos.json` uses with `scripts/autodeploy`'s state
788
- files). A merge counts as deployed once the `deployed` commit contains it and
789
- `health` answers; as failed when the `failed` commit contains it, the server
790
- doesn't come back within 3 minutes (or, when its health answer has a
791
- `startedAt`, hasn't restarted since the merge), or it isn't deployed `within_minutes`
792
- (30) after the merge. No `deployed` file on the machine means nothing deploys
793
- there, and nothing is checked.
794
-
795
- Every task and worktree session takes a **port slot**, and each named port is
796
- `base + slot` (a project with no `ports` still gets a `PORT` from 3100), so
797
- parallel sessions never share a dev server port. The slot is kept on the
798
- session and freed when it's done or deleted (an archived or merged one's once
799
- its agent has stopped). The ports and
800
- `env` are exported to the setup commands, the agent and its terminals; a port
801
- always wins over an `env` entry of the same name, and `env` can't set names a
802
- shell, runtime, git or the agent reads (`PATH`, `NODE_OPTIONS`, `TERM`,
803
- `GIT_*`, `ANTHROPIC_*`, `AGENTOS_*`…): a config that sets one, such as
804
- `NODE_OPTIONS` or `TERM`, is refused, and the error names the variable and why. The agent's brief gets a
805
- short **Running this project** section: its ports, the dev command, how to
806
- wait for `ready` in one backgrounded call, the test command, the login URL on
807
- its own port, the browsing map and the notes. Only declared fields are
808
- mentioned, and `env` values never are. Keep secrets out of the file: it is
809
- committed.
810
-
811
- With `database`, every task and worktree session gets its **own Postgres
812
- database**, a copy of `from` named `<from>_aos_<hash of the session>`, made
813
- before setup runs and exported as `env` (default `DATABASE_NAME`, plus
814
- `PGPORT`/`DATABASE_PORT` and `PGHOST`/`DATABASE_HOST` when `port`/`host` are
815
- set), so one session's migrations never reach another's. It's copied with
816
- `pg_dump` into a database created from `template0`, not `createdb -T <from>`,
817
- which refuses while the dev server is connected to `from`. Only a local server
818
- is used (`host` may be `localhost`, `127.0.0.1`, `::1` or one socket folder), and
819
- `from` must be a plain database name. A copy left by an earlier start of the
820
- same session is reused. When Postgres isn't answering or the copy fails, the
821
- session still starts, without one, and its brief says so. The copy is dropped
822
- when the session is done, merged, dropped, moved away or deleted, and only one AgentOS made
823
- for that session (its comment names the session): `from` and any other
824
- database are never touched.
825
-
826
- The file is validated against
827
- [agentos.schema.json](lib/project-config/agentos.schema.json); a broken one
828
- fails the setup with every bad field named, rather than being ignored. Without
829
- an `agentos.json`, a `.dispatch.json` is read with the same meanings, then the
830
- legacy `.agent-os/worktrees.json` and `.agent-os.json`. The config is always
831
- read from the main checkout, so an agent can't change its own environment by
832
- editing it, and the merge gates flag an edit to it as agent config. The full
833
- schema, including the fields later releases implement (MCP servers, apps,
834
- recipes), is in
835
- [docs/decisions/2026-10-10-agentos-json.md](docs/decisions/2026-10-10-agentos-json.md).
836
-
837
- ### iOS simulator for tasks
838
-
839
- On a Mac, a task on an iOS app can boot a simulator of its own, put a debug
840
- build of its worktree on it, tap through screens, read the accessibility tree
841
- and take screenshots, to check a UI fix before anyone installs a release
842
- build. A project opts in with a `simulator` block in `agentos.json`:
843
-
844
- ```json
845
- {
846
- "ports": { "METRO_PORT": 8100 },
847
- "simulator": {
848
- "start": "npm run sim",
849
- "app": "com.example.app",
850
- "device": "iPhone 17"
851
- }
852
- }
853
- ```
854
-
855
- `start` runs in the task's worktree with `SIMULATOR_UDID` (and the task's
856
- ports) in its environment. It builds if needed, installs the app on that
857
- device and exits 0 once the app is up; AgentOS builds nothing itself, because
858
- the project knows its native fingerprint and what to cache. `app` is the
859
- bundle id, and `device` the model (default `iPhone 17`).
860
-
861
- A project without the block gets nothing: no brief lines and no tools. A task
862
- declares nothing and chooses nothing; it runs:
863
-
864
- ```bash
865
- aos sim open [--no-wait] # make its simulator, run start; waits until it is up
866
- aos sim status | close
867
- aos sim snapshot -i # accessibility tree with @e1, @e2... refs
868
- aos sim click @e3 | fill @e5 "text" | scroll | swipe | back | home | find | wait
869
- aos sim screenshot [path] # prints a PNG path under ~/.agent-os/simulators/<task>/shots
870
- aos sim logs --last 2m # the app's recent log
871
- ```
872
-
873
- - **Driving** is [agent-device](https://github.com/callstack/agent-device) (MIT),
874
- pinned and installed on first use into `~/.agent-os/tools`, with its own
875
- state dir. Only the commands above are accepted; the device and session are
876
- filled in by the server, so a task can only reach its own simulator.
877
- - **One simulator per task**, cloned from a base device AgentOS makes once on
878
- the newest iOS runtime. At most two are booted at once across all tasks
879
- (`AGENTOS_SIMULATOR_MAX`); one idle for 15 minutes is shut down and boots
880
- again on its next command. The XCTest runner builds the first time per Xcode
881
- version (minutes), then is cached.
882
- - **Cleanup**: a task's simulator is deleted when it is done, signed off,
883
- dropped, deleted or moved away, and shut down when stopped. A sweep at start
884
- and every 5 minutes also removes those of tasks that ended or crashed.
885
- Devices are named with a per-database prefix and only those are ever
886
- touched, so a dev instance and the real one never reap each other's.
887
- - **Mac only.** On any other machine, including a linked one a task was moved
888
- to, the brief says the simulator is unavailable and `aos sim` says the same;
889
- nothing is forwarded between machines.
890
- - **Optional.** No merge, review or release check reads simulator state.
891
- Screenshots are written outside the worktree, and the brief tells the task
892
- not to put them in commits or PR bodies unless they belong there.
893
-
894
- ### Stacks
895
-
896
- A project whose board is linked to LumifyHub can run the whole board as
897
- **stacked** tasks: **Tasks → From the board → Run as stack** shows the plan
898
- first, then starts it. A card starts as soon as every card it's blocked by has
899
- a PR open (not merged), on top of the card it depends on most: its branch is
900
- cut from that card's pushed branch, and its PR targets it, so each PR shows
901
- only its own commits. At most three run at once without a PR (adjustable).
902
-
903
- Merging goes bottom-up. Signing off a card whose parent hasn't merged is
904
- refused; after a parent merges, every card stacked on it (grandchildren
905
- included) is rebased and its PR retargeted, and its agent is told. This works
906
- with every merge method: only the child's own commits are replayed onto the
907
- default branch, whether the parent was squashed, rebased or merged with a
908
- merge commit. **Land**
909
- merges every PR in order after checking all of them are open and green, and
910
- waits for each restacked PR's checks before merging it. A chat card's agent
911
- is stopped between turns before anything merges (Land waits up to 5 minutes
912
- for a running turn, and refuses, naming the card, if it doesn't end), and
913
- what it's sent during the land waits in its queue. A conflict stops on
914
- that card with the exact command to fix it.
915
-
916
- ### Recipes
917
-
918
- A recipe is a saved brief: a project, a kind (a **task**, which ends in a PR
919
- and goes through the merge gates, or a **session**, which has no PR), and a
920
- short brief that says what to run and how to judge it. The steps belong in a
921
- script in the repo; the recipe says "run this, judge the result". Every run
922
- is a fresh task or session, so nothing piles up in one long conversation.
923
-
924
- Open **Settings → Recipes** (the sidebar's **⋯** menu, the workspace menu, or
925
- ⌘K → Recipes or "Run recipe: …"). **Run** takes an optional one-off note and
926
- shows exactly what will be sent. **Save as recipe** on a session's menu or a
927
- finished task prefills the form from what it did, with a suggested brief to
928
- use or ignore.
929
-
930
- - **Carried between runs.** A run ends with one line,
931
- `RESULT: ok|fail key=value … | one sentence`. The next run's brief gets a
932
- short block (at most 600 characters) with the last run's outcome and
933
- those values, such as a build number; after a failure it also carries the
934
- last good run. With no line, a cheap model summarises the run, and with
935
- that unavailable the state alone is recorded. **Start the next run fresh**
936
- drops the block.
937
- - **One run at a time.** A run while another is going is refused, and a
938
- double tap starts one run.
939
- - **Timers.** A recipe can run hourly, daily, on weekdays, weekly or on a
940
- cron expression, never more than once an hour; a timer that fires while a
941
- run is going is skipped. Only you set timers: the orchestrator can't.
942
- - **Orchestrator.** `recipes`, `run_recipe`, `recipe_save` and `recipe_update`
943
- tools, and the workspace's recipes in its brief and digest. A run goes
944
- through the same brakes, Pause and task queue as a task it started
945
- itself, and a recipe is never an approval: it can't pass a gate. The
946
- orchestrator gets an event when a run it started finishes, and for any
947
- failure.
948
- - **History.** Each recipe lists its runs: when, how triggered and by whom,
949
- outcome, the reported values, cost, duration, and links to the session and
950
- PR, with totals.
951
-
952
- ### Plans in pieces
953
-
954
- A plan ends with a `## Pieces` block, one piece by default. AgentOS accepts
955
- more only for one of four reasons, written on the piece: (a) it spans
956
- repositories, (b) one part must be live (merged and deployed) before the
957
- other can merge, (c) one part needs you mid-way, (d) one part must be
958
- revertible on its own (a migration that rewrites rows, or an auth change
959
- mixed with other work). Diff size is never a reason. A piece without a
960
- standing reason is folded into the one before it, and a repository with no
961
- deploy check can't claim (b); this is a deterministic check, not the model's
962
- word.
963
-
964
- A one-piece plan behaves as always. With several, piece 1 continues in the
965
- planning task with only its slice, and the rest wait as rows (no worktree,
966
- port or queue slot) until what they wait on has merged or deployed, then start
967
- as tasks through the usual brakes and queue. A plan waiting for approval
968
- shows its pieces: **Merge into ①** or **Drop** a piece (undoable until you
969
- approve), **Approve with note…**, or **Reject**. Each piece's review judges
970
- its slice; the last also checks the whole plan was built. **Tasks** shows
971
- the feature's pieces and what each waits on, with **Release anyway** for one
972
- held by a dropped or failed predecessor. A plan-only task that reaches
973
- approval sends a "Plan ready: name, N pieces" push, and a finished feature
974
- sends one "Feature merged" push naming it.
975
-
976
- ### Schedules
977
-
978
- A schedule starts agent work at set times without you opening AgentOS:
979
- **Schedules** in the workspace menu, the sidebar's **⋯** menu, or ⌘K. Each has a
980
- name, a time (presets for hourly, daily, weekdays and weekly, or any
981
- five-field cron expression, in America/Chicago unless you pick another zone),
982
- a project and a prompt, and starts one of:
983
-
984
- - **Task** and **Session**: now recipes (Settings > Recipes) with a timer.
985
- Migration 81 turned each existing recurring task or session schedule into a
986
- recipe, with its run history; the Schedules form no longer offers them. A
987
- one-off that still has one of these kinds is left as it was. To undo the
988
- move, stop AgentOS and run
989
- `npx tsx scripts/revert-schedules-move.ts --db <path to agent-os.db>`.
990
- - **Orchestrator**: the prompt, posted to the workspace's orchestrator as a
991
- message from "Schedule <name>", marked as a scheduled prompt so it never
992
- counts as an approval.
993
- - **Message**: the prompt, sent to a session you pick the way `aos send`
994
- does, marked the same way. A chat whose agent has exited (idle chats stop
995
- after 30 minutes) is started again to take it. The run records `delivered`,
996
- `queued` or why it failed. **Schedule check-ins** in a session's **⋯** menu
997
- fills one in. The session is stored by id, so renaming it changes nothing.
998
- An archived session fails the run until it's back. When the session is
999
- deleted or a finished task, the schedule
1000
- turns itself off on the first run that finds it gone and says so once, in
1001
- the orchestrator's chat and on your phone; point it at another session or
1002
- delete it. The orchestrator isn't a target (it has its own kind):
1003
- `aos schedule add --session <the orchestrator>` makes an orchestrator
1004
- schedule, so it follows whichever session is the workspace's orchestrator.
1005
- A schedule an agent made with `aos schedule add` stays in that agent's
1006
- workspace and reaches its target labelled as that agent's request, not
1007
- yours.
1008
-
1009
- The server checks once a minute and claims each run in SQLite before starting
1010
- it, so a time never runs twice. If AgentOS was off when runs were due, it
1011
- runs only the most recent one when it comes back, marked **caught up**. A run
1012
- is **skipped** while the schedule's previous task or session is still working,
1013
- and while the workspace's orchestrator is paused. A run that fails to start is
1014
- recorded with why, noted in the orchestrator's chat and sent to your phone
1015
- (see below), once per failing streak: not again until a run starts. Every run stays in the
1016
- schedule's history with a link to what it started. **Run now** runs one
1017
- immediately, even while the last run is still working (Pause still holds it).
1018
- Task and session runs go through the orchestrator's brakes, like its own
1019
- starts: a braked run is skipped with the brake's reason and tried again on the
1020
- next minute until the brake lifts or a newer run is due (Run now obeys them
1021
- too, once). A schedule whose project has moved to another workspace fails
1022
- until it's edited. Removing a schedule keeps its history.
1023
-
1024
- **One-off schedules.** Pick **Once** in the form (a date and time in the
1025
- schedule's zone), or `aos schedule add --at "tomorrow 10am" ...` (an ISO time,
1026
- `in 2h`, a weekday; America/Chicago unless `--tz`). It works with every kind,
1027
- runs once and then turns itself off; the run stays in its history, marked
1028
- done, and the schedule moves to the **Done** group. Upcoming one-offs are
1029
- listed first with their run time, how far off it is and who made it (you or an
1030
- orchestrator), each with Run now, Edit and Cancel; the count shows in the
1031
- sidebar menu and the orchestrator's header. The same claim-in-SQLite rule
1032
- applies, so it never runs twice, and one AgentOS missed while off runs when it
1033
- returns, marked **caught up**. Pause holds a one-off until you resume rather
1034
- than skipping it, and the brakes apply as for any run. A past time is refused.
1035
- Run now uses up its only run; edit it to a new time to run it again. An
1036
- orchestrator makes its own with `remind_me`: at least ten minutes ahead, 20
1037
- pending at most, in its own workspace, and it arrives as a scheduled prompt,
1038
- never an approval.
1039
-
1040
- Task and session schedules run at most hourly, message schedules at most
1041
- every 10 minutes and skip a run (`still working`) while their session is
1042
- still busy, mid-turn or holding a queued message. A task schedule waits
1043
- while any task it started is unfinished (working, waiting on you, in review
1044
- or failing checks), so it can't stack up pull requests. Only one server
1045
- process runs schedules (it holds a lease in the database); a dev server runs
1046
- none unless `AGENTOS_SCHEDULES=on`, and `AGENTOS_SCHEDULES=off` stops them in
1047
- production.
1048
-
1049
- ### Machine load
1050
-
1051
- The dot in the sidebar header is the machine's load: green, amber (load
1052
- over 1.5 per core, or memory pressure warning) or red (over 3 per core, or
1053
- critical pressure), with some slack before it drops back. Tap it for the
1054
- numbers and the sessions using the most CPU; a busy session's row shows its
1055
- own share ("3.1 cores · 4.2 GB"), summed over its terminal's process tree
1056
- from one `ps` every 30 seconds.
1057
-
1058
- Nothing is ever stopped, paused or queued. Instead, before an agent starts a
1059
- whole test suite, type check, build, repo-wide lint, `xcodebuild` or
1060
- `pytest`, the Claude hook AgentOS installs tells it what heavy commands are
1061
- already running and how loaded the machine is, so it can narrow the run or
1062
- wait. Targeted runs (`vitest run lib/x.test.ts`) are left alone. `aos heavy
1063
- -- <cmd>` does the same for anything else. When the load stays red for two
1064
- minutes, one message goes to your phone and to each workspace orchestrator,
1065
- naming the top sessions and their heavy commands; the next one waits until
1066
- it has been green for ten minutes. `AGENTOS_LOAD=off` turns it all off.
1067
-
1068
- ### Tmux cleanup
1069
-
1070
- Deleting, finishing, dropping or marking a session done ends its tmux session
1071
- and every process its panes started, on the machine it runs on. At start,
1072
- AgentOS also ends tmux sessions it created (its `<agent>-<uuid>` and
1073
- `aos-chat-<uuid>` names, or the `AGENTOS_SESSION_ID` it sets at launch) whose
1074
- session no longer exists, and logs each as `[tmux-sweep]`. Sessions it didn't
1075
- create are never touched. The sweep runs only in production mode (a dev or test instance shares the tmux server but not the database); `AGENTOS_TMUX_SWEEP=off` skips it.
1076
-
1077
- ### Phone notifications
1078
-
1079
- Two things reach your phone: a schedule run that failed, and whatever an
1080
- agent sends on purpose with `aos notify "<text>"` (the orchestrator's morning
1081
- report, a real milestone). Status changes don't; that's the in-app Needs-you
1082
- list. Each source sends at most one a minute: anything more inside the minute
1083
- waits and goes out as one message (kept in the database, so a restart
1084
- doesn't lose it; past 20 waiting, more are dropped), and the same text twice
1085
- goes once.
1086
-
1087
- Set it up in **Settings → Notifications** (⌘K → Phone notifications, or the
1088
- line at the bottom of Schedules), one of:
1089
-
1090
- - **Command**: `AGENTOS_NOTIFY_CMD` in the server's environment, a shell
1091
- command that gets the message on stdin. It wins when set. For example, a
1092
- Telegram sender on another machine:
1093
- `AGENTOS_NOTIFY_CMD='ssh -o BatchMode=yes me@box "NOTIFY_SOURCE=agentos ~/bin/notify"'`.
1094
- - **Telegram**: a bot token and your chat id. The token is kept in the
1095
- database and never shown again, returned by the API or logged.
1096
-
1097
- #### Push to the phone app
1098
-
1099
- A machine linked to Connect sends to the paired phones that have registered
1100
- for push, instead of Telegram. A push is generic: the title "AgentOS", a
1101
- category word and a link to open, with no session or task text (a plan's or
1102
- feature's name is the one exception, scrubbed to a single line) (the payload is a
1103
- versioned envelope, so an encrypted body can replace it later). It goes
1104
- through the Connect relay, signed with the machine key; AgentOS keeps a
1105
- `push_outbox` that retries with backoff, forgets tokens the relay reports as
1106
- dead, and caps alerting pushes at 10 an hour per device (one summary after).
1107
-
1108
- - **What pushes**: an ask raised, a session waiting on you for a minute,
1109
- a task ready for review, CI or a review failing, a plan ready to approve,
1110
- a feature merged, and the Away digest. Merged is off by default. Each device picks its
1111
- categories (`PATCH /api/devices/me/push`). A task mirrored from another
1112
- machine never pushes from here.
1113
- - **Quiet rules**: waiting, review, CI and merged don't push while you're at
1114
- this Mac (keyboard or mouse used in the last 3 minutes). While Away is on,
1115
- everything except asks folds into the 10-minute digest.
1116
- - **Fallback**: agent messages, the Away digest and machine alerts (load,
1117
- schedules) go to Telegram when push isn't configured or fails. The newer
1118
- events are push-only. If Connect says push isn't set up yet, AgentOS
1119
- stops asking for 10 minutes and uses Telegram meanwhile.
1120
- - **Check it**: `aos push test` sends a real push to each registered phone
1121
- and prints what came back; Settings → Devices shows each device's push
1122
- state, categories, last error and a Send test button.
1123
-
1124
- ## Agent network
1125
-
1126
- Sessions started by AgentOS can find and talk to each other without you,
1127
- through the `aos` command on their PATH (any agent CLI can use it; Claude is
1128
- also briefed on it):
1129
-
1130
- In a chat, a message from another session shows as theirs, with the
1131
- sender's name, rather than as one you typed.
1132
-
1133
- ```bash
1134
- aos peers # sessions, their project, what each is doing
1135
- aos send <session> "message" # wakes the other session with the message
1136
- aos inbox # read messages sent to you
1137
- aos history <session> # your conversation with a session
1138
- aos spawn <project> "prompt" # start a new agent session in a project
1139
- aos task <project> "prompt" # start a background task that ends in a PR
1140
- # (both take --name "..." before the prompt;
1141
- # task takes --after <task|any> to queue it;
1142
- # a task opens as a chat, --terminal for tmux)
1143
- aos move <session> <machine> # carry a task on there ("here" brings it back)
1144
- aos stack <project> [--plan] # run the project's board as stacked tasks
1145
- aos stacks # every stack and where each card is
1146
- aos schedules # every schedule, its next run and last outcome
1147
- aos schedule run <name> # run a schedule now
1148
- aos schedule add --session <s> --every 30m "prompt" # check in on a session
1149
- aos notify "text" # push a message to your phone
1150
- aos push test # a real test push to the paired phones, and the result
1151
- aos heavy -- npm test # run it now; says first what else heavy is running
1152
- aos stop <session> # stop its turn now; the next message runs as a new turn
1153
- aos done <session> # finished: merge through the gates, archive
1154
- aos done --all-idle # the same for every idle session around you
1155
- aos docs [query] # LumifyHub pages, when the workspace is linked
1156
- ```
1157
-
1158
- Messages are delivered by typing them into the recipient's terminal, labelled
1159
- as coming from another agent, and a rate limit stops two agents looping.
1160
- Delivery is checked against the pane: the text has to reach the input and
1161
- leave it on Enter (retried once as a bracketed paste), and a menu on screen is
1162
- never typed into. `aos send` says `delivered`, `queued` (the agent was busy and
1163
- will see it after its turn) or `FAILED: <why>` with exit code 2; a chat session
1164
- counts as delivered once its worker records the message. Either way the
1165
- message waits in the recipient's `aos inbox`.
1166
-
1167
- Sessions are addressed by name, `project/name`, id or a unique id prefix, and
1168
- renaming one keeps its old names: a message to "Session 3" still reaches the
1169
- session now called "orchestrator", and `aos peers` shows
1170
- `orchestrator (was Session 3)`. If another session has since taken the old
1171
- name, the current owner wins and `aos send` says so. A name that matches more
1172
- than one session is refused with the candidates listed. The
1173
- **Messages** panel shows every conversation and lets you message any session.
1174
- Sessions on other machines can receive messages but not yet send them.
1175
-
1176
- ## The sidebar
1177
-
1178
- One flat list of every session in the current workspace, each row naming its
1179
- project underneath. The workspace's orchestrator comes first, as a compact row
1180
- with its state and how many of its asks wait on you; it starts pinned there,
1181
- and unpinning it from its ⋯ menu (remembered per workspace) drops it among the
1182
- other sessions. Sessions sit on shelves: **Pinned** (pin from a row's ⋯
1183
- menu), **Needs you** (an approval, a question, a terminal waiting for input, a
1184
- message typed and never sent, a failed task, or the orchestrator's open asks,
1185
- each with a badge), **Working**,
1186
- and **Done**. Done is folded to one row, "3 done today · 12 earlier", until you
1187
- tap it (open or folded is remembered per device); open, it lists newest first,
1188
- ten at a time. The orchestrator's row carries small chips for the work it
1189
- started: running, in review, blocked and queued, each left out at zero. Tap
1190
- them to show only that orchestrator's work on the same shelves; the "Showing
1191
- … work ×" chip at the top clears it (it resets on reload). A session an
1192
- orchestrator started has a small orchestrator icon on its row. A purple dot marks a session that
1193
- finished or changed since you last opened it. Search matches
1194
- titles and project names, and the project filter narrows the list to one
1195
- project and holds that project's own actions: new session, terminal, dev
1196
- server, settings, board, workspace.
1197
-
1198
- Every session has its own address, `/?session=<id>`: open it in a new tab,
1199
- on a phone or from a bookmark and that session opens; **Copy link** in a row's
1200
- ⋯ menu or the pane's menu copies it. The address bar follows the focused
1201
- pane's session, each switch is a history entry so back and forward move
1202
- between sessions, and an address for an archived or unknown session says so
1203
- and opens the usual home. A device that isn't paired is sent through pairing
1204
- and comes back to the same session; the address grants nothing by itself.
1205
-
1206
- **Settings** (the sidebar's ⋯ menu, or ⌘K → Settings) holds every setting in
1207
- one place: **Merging** (the approval switch, merge method and branch
1208
- clean-up, with the projects that override them), **Workspaces** (each one's
1209
- running task limit), **Devices & access** (pairing, passkeys, network),
1210
- **Notifications** (this browser's sound and alerts, and your phone) and
1211
- **Schedules**. Each section has its own address, `/?settings=<section>`
1212
- (`merging`, `workspaces`, `devices`, `notifications`, `schedules`), beside
1213
- `?session=`; on a phone the sections are a list, then the one you open.
1214
-
1215
- Rows move the moment a session changes state: the server pushes every change
1216
- over `/ws/status` (behind the same device gate as the terminal). The same
1217
- stream says when anything else on screen changed: database triggers mark the
1218
- tables written (sessions, stacks, schedules, asks, bus messages and more,
1219
- whoever wrote them), and the git panel's folders are polled once on the
1220
- server for every viewer. The browser refetches only what moved, so an idle tab
1221
- makes about one request a minute instead of forty. Messages are numbered: a
1222
- reconnecting phone picks up what it missed, or takes a fresh snapshot when
1223
- it's been away too long. While the socket is down, the old polling carries on.
1224
- API JSON is gzipped and the sockets use permessage-deflate.
1225
-
1226
- **AgentOS reads OSC 7501**, the [Program Status
1227
- Protocol](https://www.superlogical.com/rex/docs/build/program-status): a
1228
- program in a terminal session can say it's `working`, `blocked` (on a
1229
- permission, a question or a sign-in), `done`, or failed, and that wins over
1230
- reading the screen. Blocked lands on Needs you with Approve, Answer or Sign in
1231
- and the program's message (plain text, capped); done gets the unread dot.
1232
-
1233
- ```sh
1234
- printf '\e]7501;state=blocked:kind=permission:app=me:msg=%s\e\\' "$(printf 'Apply 3 changes?' | base64)"
1235
- ```
1236
-
1237
- tmux drops sequences it doesn't know, so AgentOS watches each local terminal
1238
- session through tmux control mode: one long-lived, read-only `tmux -C`
1239
- client per session (tmux 3.2 or newer) pushes the pane's raw output, which
1240
- carries the reports, and keeps a copy of its screen for reading status, so
1241
- nothing polls `capture-pane` or taps panes with `pipe-pane`. A screen is kept
1242
- only while its pane prints, and it's checked against tmux again whenever
1243
- output settles. A session's terminal views share one `tmux attach` (it runs at
1244
- the smallest view's size), and a view that can't keep up is held back and sent
1245
- the current screen once it catches up. On older tmux, or with
1246
- `AGENTOS_TMUX_CONTROL=off`, AgentOS falls back to copying each session's
1247
- output to itself (`tmux pipe-pane`, through a unix socket only you can reach;
1248
- needs `nc`) and reading screens with `capture-pane`. Claude Code sessions AgentOS starts on this
1249
- machine report through hooks it installs (`~/.agent-os/claude-status-hooks.json`,
1250
- passed with `--settings`). Not covered yet: sessions on other machines (their
1251
- output never passes through this one), Codex and the other agents (Codex only
1252
- notifies when a turn ends, with nothing for "working" or "blocked"; they keep
1253
- reading the screen), and the terminfo
1254
- `Pst` capability (the pane's terminal is tmux's).
1255
-
1256
- The screen covers what no report says. A selection menu at the bottom of a
1257
- terminal (Claude Code's "Enter to select · ↑/↓ to navigate", a Codex "› 1."
1258
- list) lands on Needs you with **Answer** and its question, for sessions
1259
- without the hooks. Text typed into a Claude Code or Codex input box and left
1260
- unchanged for a minute while the agent sits idle lands there with **Unsent**
1261
- and the text, so a message that never got sent doesn't wait for hours. The
1262
- dim suggested prompt in the same box doesn't count. Claude Code fires no hook
1263
- when a question is dismissed with Esc, so a question report clears once its
1264
- input box is back on screen.
1265
-
1266
- ## Workspaces
1267
-
1268
- Group projects into workspaces (e.g. Work, Personal) from the workspace
1269
- switcher at the top of the sidebar, which also picks the workspace the list
1270
- shows and holds the current one's orchestrator, clean-up, Archived and
1271
- LumifyHub link. Move a project in from the project filter's menu. Deleting a
1272
- workspace keeps its projects.
1273
-
1274
- ### Done
1275
-
1276
- **Done** (a session's ⋯ menu; `aos
1277
- done`; or the orchestrator's `done` tool) is for finished work, where
1278
- **Stop** keeps everything and **Drop** rejects it. A task with an open PR
1279
- merges first, only through the orchestrator's gates at the judged commit; a
1280
- failing gate refuses with its name and nothing merges. A task already
1281
- merged, or a session with no PR, just cleans up. If GitHub can't be read, or
1282
- a task that had a PR has none now, done refuses rather than guess.
1283
-
1284
- Cleanup stops the agent and archives the session. The worktree goes only if
1285
- nothing in it would be lost: never with uncommitted changes; after a merge,
1286
- only when every commit on it is in the merged PR or on a remote; otherwise
1287
- only when its branch has no commits of its own. Anything else is kept and the
1288
- reply says why, naming the uncommitted files. Files setup copied in (env
1289
- files, agentos.json's `copy`) don't count while they still hold exactly what
1290
- was copied; setup records them, hashed from the main checkout, in the
1291
- worktree's git directory. Cloned dependencies are gitignored, so never count. A sign-off from Tasks or the orchestrator follows the same
1292
- rule. A branch merged on GitHub is deleted on origin when origin's tip is the
1293
- commit that merged.
1294
-
1295
- Archived sessions leave the sidebar, the needs-you count, session statuses
1296
- and the orchestrator's view, but are never deleted; the **Archived** view
1297
- (the workspace switcher for one workspace, or ⋯ in the sidebar header for
1298
- all of them) lists
1299
- them with Unarchive, which only puts the entry back.
1300
-
1301
- **Clean up idle sessions** (the workspace switcher) first shows what will happen
1302
- to each idle or stopped session, and `aos done --all-idle` does the same
1303
- sweep. A clean-up never merges: open PRs are listed for their own done.
1304
- `aos done` reaches only sessions in the caller's workspace (or project).
1305
-
1306
- ### Session logs
1307
-
1308
- When a task merges, AgentOS writes a log of it. It costs no model tokens: the
1309
- log is a fixed template filled from records AgentOS already holds, and nothing
1310
- that makes it can call a model (a test holds it to that). Dropped tasks get
1311
- none.
1312
-
1313
- A log holds:
1314
-
1315
- - **Header:** task name and id, workspace, project, branch, PR number and
1316
- link, merge commit, start, merge time and duration.
1317
- - **Brief:** the task's prompt and every scope change recorded on it, in
1318
- order.
1319
- - **Plan:** `.agentos/plan.md`, read at the merge before cleanup removes the
1320
- worktree. A task with no plan says so.
1321
- - **Timeline:** PR opened, gate failures per commit (CI with the failing
1322
- check's name, review, scope), each review and scope verdict per commit with
1323
- its text trimmed, escalations to you and your answers, and the merge.
1324
- - **Usage:** tokens, estimated cost and model per role, from the usage ledger.
1325
- - **Outcome:** the task agent's last message, verbatim.
1326
- - **Links:** the PR and the session.
1327
-
1328
- Text from tasks, CI and reviewers is untrusted. It is written inside fences
1329
- or code spans, so it can't add headings or links to the document, and
1330
- credential-shaped strings are redacted.
1331
-
1332
- The database is the record: `session_logs`, one row per task, never deleted.
1333
- Each log is also written to a file, `YYYY-MM-DD-<task>.md`, in the first of:
1334
-
1335
- 1. the project's **Session log folder** (project settings; an absolute or `~`
1336
- path),
1337
- 2. the workspace's **HQ folder** (workspace settings):
1338
- `<hq>/docs/sessions/<project>/`,
1339
- 3. `~/.agent-os/session-logs/<workspace>/<project>/`.
1340
-
1341
- A folder inside the project's repository is refused, because repositories can
1342
- be public, and the files are never written into one.
1343
-
1344
- The HQ folder is a git repo. Saving it checks that it is a repository, that it
1345
- isn't inside or equal to any of the workspace's project repos, and (with one
1346
- `gh repo view --json visibility`) that its GitHub remote isn't public; the
1347
- result is stored. When a log lands in the HQ it is committed there as
1348
- `session log: <task name>`, only that file, whatever else is changed in the
1349
- tree, and pushed to the current branch's upstream only if the repo is
1350
- private. A failed commit or push is logged and never reaches the merge; the
1351
- next push carries the earlier commit. Commits to one HQ run one at a time.
1352
- The database row is the record either way.
1353
-
1354
- The same dialog lists the project's logs, newest first, and a merged task's
1355
- view has a **Log** tab. The orchestrator reads them with its `logs` tool: the
1356
- latest headers of a workspace or project, or one log in full. It only reads.
1357
-
1358
- The log is made once per task, in the background, from wherever the merge
1359
- happens: a sign-off, Done, a stack landing, or a merge seen on GitHub. A
1360
- failure is logged and never reaches the merge. For tasks that merged before
1361
- this existed:
1362
-
1363
- ```bash
1364
- npx tsx scripts/backfill-session-logs.ts --db <path to agent-os.db> [--no-gh] [--dry] [--reexport]
1365
- ```
1366
-
1367
- Their plan files are gone, so those logs say "plan not kept". Files go to the
1368
- same destinations as above; into an HQ the backfill commits once at the end.
1369
- `--reexport` writes no new logs: it rewrites the files of logs already in the
1370
- database to where they'd go now, from the stored markdown (no rebuild, no
1371
- model), leaves the old files in place, and does nothing on a second run.
1372
-
1373
- ### Orchestrator
1374
-
1375
- Each workspace has one standing orchestrator, pinned at the top of its
1376
- section: a chat that runs the work across the workspace's projects. It's made
1377
- the first time you open it, works from a scratch folder in
1378
- `~/.agent-os/orchestrators/<workspace>`, and is never swept with idle sessions.
1379
- Its brief lists the workspace's projects (path, board, default branch), what
1380
- it may do on its own, and the lines that always come back to you as asks.
1381
-
1382
- - **Tools:** `sessions` (every session's status, activity, task, PR, CI and
1383
- stack position), `read` (the end of a terminal or chat) and `cards` (the
1384
- linked boards' cards), plus acting tools: `send`, `start_task`,
1385
- `start_session`, `stack`, `stack_status`, `land`, `drop`, `stop`, `done`, `move`,
1386
- `add_project` (a git repository on the machine becomes a project of the
1387
- workspace, or the project already at that path moves in from no workspace;
1388
- folders that hold secrets and projects of another workspace are refused),
1389
- `note`,
1390
- `rule_add`, `rule_remove`, `review`, `sign_off`, `arm`, `disarm`, `remind_me`, `reminders`, `cancel_reminder`, `ask_saad`, `question` and `resolve_question`. All are served in-process to its chat, refuse
1391
- any target outside its workspace, and validate their arguments. The one
1392
- exception is other orchestrators: `orchestrators` lists every workspace and
1393
- whether it has one, and `message_orchestrator` messages another workspace's
1394
- current orchestrator by workspace name (never any other session there). It
1395
- arrives marked as from that orchestrator and fenced as untrusted, so it can't
1396
- approve, answer an ask or pass a gate. The route
1397
- they call answers only the orchestrator's worker, by a per-orchestrator
1398
- secret.
1399
- - **Shell:** it runs anything without asking, so an orchestrator can read and
1400
- investigate on its own: `gh pr list`, `gh run view --log-failed`, `gh api`
1401
- GETs, `curl`, `sqlite3 -readonly`, git in any repository, a skill's scripts,
1402
- pipes and chains. Its chat worker refuses a deny list before a command runs,
1403
- naming the tool to use instead: merging, closing or retargeting a PR
1404
- (`gh pr merge`, `gh pr close`, `gh pr edit --base`, and `gh api` or `curl`
1405
- calls that merge a pull request or PATCH or PUT one; use `sign_off`, `arm`,
1406
- `land` or `drop`), `aos move`, `aos done` and `aos stop` (use `move`, `done` and
1407
- `stop`, which are scoped to the workspace and held by Pause), `git push` (`quick_pr` pushes), writing in a project
1408
- repository or task worktree other than its own open quick changes (git
1409
- commit, reset, checkout, rebase, merge, stash, clean or rm there, and `rm`,
1410
- `mv`, `cp`, `tee` or a redirect into one), writing AgentOS's state (`sqlite3`
1411
- on its database without `-readonly` or with INSERT, UPDATE, DELETE, DROP,
1412
- ALTER or ATTACH; files under `~/.agent-os` outside its folder), stopping
1413
- AgentOS or its tmux (`pkill`/`kill` aimed at it, `tmux kill-*`, a
1414
- `launchctl` or `serviceman` restart), and reading secret files (the same
1415
- list as the write guard; tokenvault is allowed, through `tv` and its folders). It reads each
1416
- command line the way a shell would, so these are caught inside pipes,
1417
- chains and `$( )`, behind `bash -c`, `sh -c`, `env`, `sudo`, `xargs`,
1418
- `find -exec` or an absolute path, and after a `cd` or `git -C`. **The deny
1419
- list catches honest mistakes, not a determined bypass:** a script or
1420
- `python -c` can do any of these where no parser sees it, and an
1421
- orchestrator reads other sessions' text, which can carry planted
1422
- instructions. The trade-off was chosen on 2026-10-11, for an orchestrator
1423
- that does its own investigating instead of handing it to him.
1424
- - **Triage:** its brief has it decide each job: small and clear is a task with
1425
- `plan: false` and the steps written into the brief; big, unclear or
1426
- cross-cutting is `plan: true`; work outside a repository is a
1427
- `start_session` (Sonnet) with the steps in the prompt. It puts
1428
- implementation on Opus only when the work needs deep reasoning, and says why.
1429
- - **Files:** it reads anywhere and writes (Write, Edit, NotebookEdit) anywhere
1430
- except a project repository, a task worktree (`~/.agent-os/worktrees`),
1431
- AgentOS's own install and state (all of `~/.agent-os` but its own folder),
1432
- secret files (`.env`, keys, `~/.ssh`, `~/.aws`, `gh`'s token, tokenvault and
1433
- the like; the list the merge gates use for secrets handling,
1434
- `lib/secret-paths.ts`), and files that make something run: dotfiles and
1435
- dot-folders at the top of its home (shell, git, Claude and tool config, the
1436
- usage brake's `limits.json`), `~/Library`, `/Library/LaunchAgents` and
1437
- `LaunchDaemons`, git's `core.hooksPath` folders (yours and each project's),
1438
- the folders on `PATH`, and any `.git`, `.claude`, `.mcp.json`, `CLAUDE.md`
1439
- or `AGENTS.md`, since writing one would give it a shell or run code in
1440
- AgentOS's own git; and the temporary checkouts a review or rebase runs in.
1441
- Code changes to a project go through a task, so its PR still passes CI,
1442
- review and scope. It is a list of exceptions, not of allowed places: a
1443
- script a hook or scheduled job runs from an ordinary folder can still be
1444
- written, so an orchestrator misled by text it read could run code that way.
1445
- That's accepted: task agents can already write anywhere, so an allowlist
1446
- on the orchestrator alone would buy little.
1447
- - **MCP servers.** An orchestrator runs without prompts, so an MCP server's
1448
- tools are refused unless its workspace allows the server. Settings →
1449
- Workspaces lists the servers configured on the machine (names only: user
1450
- servers, connectors and plugin servers; never their env, headers or tokens)
1451
- as toggles, and `linear` is allowed by default. A ticked server adds a bare
1452
- `mcp__<server>` rule to the orchestrator's allowed tools, covering all its
1453
- tools. Its worker ends at its next turn boundary when the list changes, and
1454
- the next one starts with the new permissions. Sending a message, email or
1455
- post through a server stays a hard line the brief sends to `ask_saad`.
1456
- Its chat worker checks every write in a PreToolUse hook before it runs, on
1457
- the path with symlinks and `..` resolved as the system will, and refuses
1458
- with the reason; the repositories are read from the database on each write,
1459
- so a project added since counts. A worker from before an update picks the
1460
- new permissions up at its handover to the new build.
1461
- - **Brakes:** every start is refused, with the reason, while 4 sessions run in
1462
- the workspace, after 6 starts in an hour (`orch_max_running` and
1463
- `orch_max_starts_per_hour` on `workspaces`), or when the account's 5-hour
1464
- usage window would run out before it resets (read from the statusline's
1465
- `~/.claude/.context-cost/limits.json`, as dispatch does;
1466
- `AGENTOS_LIMITS_FILE` overrides). A missing file, or one not sampled for 15
1467
- minutes, refuses too. Each card of a stack it starts is braked as its own
1468
- start. A brake writes one note and pauses only new starts.
1469
- - **Review and sign-off:** `review` runs a fresh `claude -p` on a
1470
- symlink-free detached checkout of the PR's exact head commit. It loads no
1471
- settings or MCP servers from anywhere (so nothing the PR ships runs), gets
1472
- an allowlisted environment, can only Read/Grep/Glob inside the checkout,
1473
- and follows the repo's review agents' rules (`.claude/agents/review-*.md`), or its review skill, as the base branch has them. The verdict
1474
- is stored against that sha. A diff over 80k characters is reviewed in up
1475
- to 6 parts of whole files, each against the task, and fails if any part
1476
- does; a single file over 80k, or more than 6 parts, goes to you. A task from a card also gets a scope check against the card.
1477
- When you change a running task's scope, the orchestrator tells it with
1478
- `send` and `scope_change`, which records the message on the task's brief
1479
- (`brief_amendments`, logged in its chat): the review and the card's scope
1480
- check judge the PR against the brief as amended, the amendments winning,
1481
- and a "Scope change" note in the PR body counts only where one backs it.
1482
- `sign_off` merges only that commit (with the project's merge method) when CI is green and settled on
1483
- it (2 minutes with no new check), its review passed, nothing is
1484
- `BLOCKED:` or waiting, the diff stays in scope (no secrets, not only
1485
- lockfiles, nothing outside the repo, within the card) and its stack parent
1486
- has merged, and the PR body carries no AI attribution. `land` judges each item again at its own head right before
1487
- merging it. A gate failing again on a different commit, or a repo with no
1488
- CI, goes to you as an ask, and the orchestrator stops merging that task.
1489
- A failure counts once per task, gate and head commit, however many times
1490
- and from wherever that commit is judged (the armed watcher, CI finishing,
1491
- `sign_off`, `done`, `land`); a pass in between doesn't reset it. A review
1492
- that blocks counts when its verdict arrives, whether or not `sign_off`
1493
- ever reads it.
1494
- - **Arm to merge:** `arm` (or **Arm merge** on a task's row) makes AgentOS
1495
- merge the task's PR itself the moment every `sign_off` gate passes at its
1496
- head, starting the review once CI is green, so nothing polls `sign_off`.
1497
- The orchestrator hears one event: merged, or the failure (counted once per
1498
- head; the task stays armed for the fix, and one that goes to you disarms
1499
- it). The flag is in the database, so it survives a restart; `disarm`
1500
- takes it back.
1501
- - **Automatic rebase:** when the base moves and GitHub says a task's PR now
1502
- conflicts (read in the PR poll AgentOS already makes), `git merge-tree`
1503
- checks it against the base. Clean, and with the task's agent idle and
1504
- nothing uncommitted, AgentOS rebases it in the task's worktree and pushes
1505
- only its branch with `--force-with-lease`, and the orchestrator's review
1506
- carries over when the PR's own patch is
1507
- unchanged (same `git patch-id`; a fresh review starts otherwise). Your
1508
- merge approval carries over a rebase the same way (any rebase, the task's
1509
- own included): when the PR's patch is unchanged it follows the new head,
1510
- else you're asked again, naming the new head and what changed, and an open
1511
- merge ask follows its PR's head. Not
1512
- clean: the task is asked once to rebase. Either way, one event. Main and
1513
- other tasks' branches are never written.
1514
- - **Tasks woken on CI failures and reviews:** a check failing on a task's
1515
- PR head messages the task's own agent once per head, with the check, the
1516
- failing step and about 60 lines of `gh run view --log-failed` around the
1517
- error, telling it to fix the root cause and push. A review or comment from a collaborator (not the account gh runs
1518
- as, which is the task's own) wakes it once per review or comment; one
1519
- GraphQL query per repository a minute fingerprints the open task PRs, and
1520
- only a PR whose fingerprint moved has its reviews read. Comment-only wakes
1521
- stop at 10 a task; changes requested always wake. A task mid-turn gets it
1522
- queued, the orchestrator hears one line per wake, and merged, closed,
1523
- dropped or done tasks aren't watched. On by default; **Settings →
1524
- Workspaces** turns it off per workspace.
1525
- - **External PRs and sessions:** `review` and `sign_off` also take an open
1526
- PR no AgentOS task owns (dispatch, a person, dependabot) by `#N`,
1527
- `owner/repo#N` or URL, as long as its repository is one of the workspace's
1528
- projects; anything else is refused. The same gates apply, except that a
1529
- draft waits and a "blocked", "do not merge" or "wip" label holds it (there's
1530
- no session to read). `sessions` also lists tmux
1531
- sessions AgentOS didn't start that work in a workspace repository or a
1532
- worktree of it, here or on another machine, with their branch's PR; `read`
1533
- reads them, and nothing else acts on them.
1534
- - **Merge approvals** (Settings → Merging, off by default): on, any change to
1535
- CI config, build and hook scripts, agent config, deploy scripts, secrets
1536
- handling or AgentOS's security code, and any diff over 80k characters, goes
1537
- to you as an ask whatever the gates say, and merges once on your passkey.
1538
- Off, those merge through the gates like any other PR. Switching it off
1539
- again needs your passkey.
1540
- - **Decision log:** `note` and the brakes and escalations write to
1541
- `orchestrator_notes`, and each line shows in its chat.
1542
- - **Standing rules:** what it must keep knowing ("never merge #111", "briefs
1543
- must not name third-party products") it keeps with `rule_add` and drops with
1544
- `rule_remove`, in `orchestrator_rules`. You see, add, edit and remove them in
1545
- Settings → Workspaces, each change with your passkey (any process on the
1546
- machine reaches the route, and a rule of yours is your word to it). A rule restricts or informs; it never approves an
1547
- ask, passes a gate or clears a hard line. Yours are marked as from you and
1548
- only you remove them; the ones it kept, like its decision-log lines, come
1549
- back to it fenced as its own notes, so text it read can't pass for your
1550
- word. Rule text is redacted when stored, and removed rules stay on record.
1551
- - **Where things stand:** every start appends a digest read straight from
1552
- the database, no model involved: the standing rules, open asks, running
1553
- tasks with their PR, armed-to-merge and gate state, recently gated external
1554
- PRs, the queue, schedules and the newest decision-log lines, capped at about
1555
- 4k tokens. The brief tells it this is authoritative over what it remembers.
1556
- - **Fresh starts:** fed events nonstop, an orchestrator never idles and can't
1557
- compact itself. Once its context passes a threshold (200k tokens by
1558
- default, Settings → Workspaces → Fresh starts), its worker ends the
1559
- conversation at the next turn boundary and the next one starts a new
1560
- conversation with the brief and digest, telling the agent in one line why.
1561
- Nothing is lost, because its state lives in the database; its chat goes on,
1562
- with a line where it started over. The same can be switched on for chat
1563
- tasks (off by default: a task keeps only its brief and its branch).
1564
- **Start fresh by hand:** after a deploy that changes its permissions or
1565
- brief, use **New conversation (keep brief)** in the orchestrator's row menu or type `/fresh`
1566
- in its chat (task chats have both too). It takes the threshold's path: an
1567
- idle agent starts over at once; a running turn finishes first, with a
1568
- "Starts fresh after this turn" line. The next conversation launches with
1569
- the brief, permissions and workspace state the server has then.
1570
- - **Events:** the server sends it one short line per event (a PR opened, CI
1571
- failed, or green once it has settled so `sign_off`'s ci gate passes, a `BLOCKED:` line, a merge, a session needing input, a
1572
- stack step, a task idle 30 minutes with no PR, a review's verdict). Events
1573
- wait while its turn runs, duplicates fold, and at most one batched message
1574
- goes out per 30 seconds. What it has been told is kept in
1575
- `orchestrator_events`, so a restart resends nothing. Set
1576
- `AGENTOS_ORCHESTRATOR=off` to stop events.
1577
- - **Asks:** what's yours to decide sits on its asks list
1578
- (`orchestrator_asks`): what it raises with `ask_saad` (a decision, or
1579
- something crossing a hard line: public or outbound, money, irreversible,
1580
- credentials, a product call), and every escalated gate and brake. There's
1581
- one open ask per subject (a task, the brakes, a title), so an escalation
1582
- that repeats updates its ask. Each shows as a card under its row and at the
1583
- top of its chat, with Approve, Decline and Reply. Open asks are its
1584
- needs-you: the amber dot, one count each in "N need you", and the
1585
- notifications. Your answer reaches it as an event (`ask "<title>":
1586
- approved`). An approval covers that one item only: a held task's approval
1587
- lets `sign_off` merge it once, at the commit you approved (a new commit
1588
- asks again), and a brake's lets one start through, for that brake only.
1589
- The orchestrator can ask you itself whether to merge a PR the gates won't
1590
- pass, by giving `ask_saad` the task and its head sha: the ask is pinned to
1591
- that commit, opens with what AgentOS knows about it (files changed,
1592
- sensitive files, the review's verdict) and links the PR, and your approval
1593
- lets `sign_off` merge it once, at that commit. It never rewrites an ask the
1594
- gates raised. An approved merge (either kind, and an armed task's) needs no
1595
- review or other gate of that commit: only an AI-attributed PR body, failing
1596
- CI (running CI waits), a head that moved off the commit, and on the
1597
- orchestrator's own ask a stack parent not merged yet, still stop it.
1598
- An ask closes itself when its task is merged or dropped, or the brakes
1599
- lift. At most 10 are open per workspace, titles that say the same thing
1600
- fold into one, a declined subject isn't asked again for 6 hours, and a
1601
- "decision" that reads as money, outbound, irreversible or credentials is
1602
- filed as that hard line.
1603
- - **Readable when busy:** in the orchestrator's chat, anything AgentOS
1604
- sent it (a PR opened, CI green, a task woken, a schedule, a task's message)
1605
- and the orchestrator's reply fold into one line: an icon, the task, how the
1606
- reply starts, the time. Tap to read the whole reply; runs of them group as
1607
- "5 updates". What you typed, and the replies to it, stay whole, and so do
1608
- your own decisions and any turn waiting on an approval or showing a plan or
1609
- an error. Where a message came from is recorded when it is delivered;
1610
- older messages are told apart by their opening text only.
1611
- A **Needs you** strip above the composer lists everything waiting on you
1612
- until it is resolved: open asks (escalated gates and brakes too), questions
1613
- the orchestrator put to you in chat, its chat waiting on an approval, and
1614
- tasks that need you. Each is one line that jumps to the ask, the message or
1615
- the session, with a count when collapsed. The orchestrator marks a chat
1616
- question with `question` (the same words while open are the same
1617
- question) and clears it with `resolve_question`; the strip's Reply quotes
1618
- it into the composer and resolves it when you send. Dismissing an item
1619
- hides it on all your devices and resolves nothing: an ask stays open on
1620
- its list and in the counts. Questions about a task clear when it is merged
1621
- or dropped.
1622
- - **Proof it's you:** only this machine, the tailnet, or a paired device you
1623
- switched to "Can approve" in Devices may answer asks or pause. Approving a
1624
- hard line, a gate, a brake or a new passkey, Resume, changing the
1625
- orchestrator's standing rules, and switching merge approvals off, also
1626
- need a
1627
- passkey (Touch ID or Face ID, user verification required) on a challenge
1628
- bound to that one ask at its current commit or brake: single use, two
1629
- minutes. Agents on this machine reach every route but can't make your
1630
- authenticator sign, and neither `aos` nor the orchestrator's tools can
1631
- answer or resume. Passkeys belong to the host they were made on
1632
- (localhost or the tailnet's https name), so add one on
1633
- each in Devices. The first one on an install is trusted on first use,
1634
- once: every later one needs a code from a device that has one, even after
1635
- every passkey is revoked. Revoking always needs a passkey, the last one
1636
- included, and every add or revoke becomes an ask (declining a new one
1637
- revokes it, with your passkey). If none is left, run `agent-os passkeys
1638
- reset` yourself in a terminal on the machine: it refuses inside an
1639
- AgentOS session or an agent's shell, asks you to type a confirmation, and
1640
- makes the next passkey first-use again. Browsers offer passkeys only over
1641
- https or on localhost.
1642
- - **Header line and Pause:** its row reads like "Orchestrator · 3 running ·
1643
- 1 in review · 1 ask" from live data, and tapping it opens the chat, where
1644
- the same line carries Pause/Resume. While paused it acts on nothing:
1645
- events queue, acting tools refuse, and its stack starts and lands hold.
1646
- It can still read, note and ask. Resume delivers what queued, folded.
1647
- - **Away mode:** turn it on when you step away, from the coffee cup in the
1648
- sidebar header or ⌘K ("Away for 4 hours"), for 1 hour, 4 hours, until
1649
- morning (8 AM) or until you're back. It's kept in `away_periods` (who set
1650
- it, when, when it ends), the cup turns amber while it's on (with an amber dot on the phone's menu button), and it
1651
- ends on its own at its time. It works only through orchestrators that
1652
- had a real turn (yours or theirs, not an AgentOS event or a status poll)
1653
- in the last hour, set in Settings → Workspaces → Away mode; the rest
1654
- aren't told or woken. Each active orchestrator gets an event, and a line
1655
- leading its digest, saying you're away and to follow work through rather
1656
- than only react. Every 10 minutes a sweep, written as code with no model
1657
- scanning, runs a review again when a PR is green and settled but its
1658
- review never started or was lost (once seen on two sweeps, so the
1659
- orchestrator had its turn), checks after each merge whether the project's
1660
- `deploy` went live and the server came back, and sends the orchestrator
1661
- one digest of what's new: tasks quiet 30 minutes with no PR (or failing
1662
- CI), idle tasks with uncommitted work and no PR, and your own sessions
1663
- idle with uncommitted work. Only sessions with a real turn inside the
1664
- window are listed, never stopped, finished or archived ones, and nothing
1665
- goes out when nothing changed. The sweep messages no session itself: the
1666
- orchestrator decides what needs work, and asks one of your sessions at
1667
- most once to wrap up. Away's own messages don't count as activity. Paused
1668
- workspaces aren't swept. Hard lines still become asks, and while you're
1669
- away the asks raised, failed deploys and any `aos notify` an orchestrator
1670
- sends reach your phone as one message per sweep. Turning it off (or its running out) builds "While you were
1671
- away" from the database, for the workspaces Away reached: what merged and
1672
- deployed with PR links, what the sweep flagged that's still in flight and
1673
- which gate or blocker holds it, what failed, was dropped
1674
- or was escalated, and what waits on you (an explicit "none"). It opens on
1675
- whichever device is open until you dismiss it, a short summary goes to
1676
- your phone, and the report stays in Settings → Audits (`away/`).
1677
-
1678
- ## LumifyHub (optional)
1679
-
1680
- AgentOS works fully without a LumifyHub account. Connecting one, from a
1681
- workspace's menu, gives its docs and boards a home you can open anywhere and
1682
- share with anyone:
1683
-
1684
- - A workspace links to a LumifyHub workspace, and a project to a board in it.
1685
- Tasks get a card on the project's board that moves as the task runs, and a
1686
- card in To Do can be started as a task.
1687
- - **Docs** lists the linked workspace's pages and reads them; editing happens
1688
- in LumifyHub.
1689
- - A markdown file open in the file explorer can be published as a page marked
1690
- as coming from the repo. Publishing again updates the same page, and so does
1691
- merging a task that changed the file. It goes one way: edits made in
1692
- LumifyHub are overwritten.
1693
- - Agents can list, read and create those pages with `aos docs`, `aos doc <id>`
1694
- and `aos doc new "<title>" --file <path>`. The token stays in AgentOS.
1695
-
1696
- What maps to what, and the API it uses: [docs/lumifyhub.md](docs/lumifyhub.md).
1697
-
1698
- ## Machines
1699
-
1700
- Open the sidebar menu (⋯) → **Machines** and add one with a name and an ssh
1701
- target (`user@host` or an alias from `~/.ssh/config`). Requirements: key-based
1702
- ssh from the machine running AgentOS (no password prompts), and tmux on the
1703
- remote machine. **Test connection** checks both.
1704
-
1705
- New projects can then pick a machine. Their sessions run in tmux there and
1706
- attach over one reused ssh connection per machine. Folders match across
1707
- machines relative to `~`, so `~/dev/app` on your laptop and on a server are
1708
- the same project.
1709
-
1710
- On other machines, files, git, worktrees, dev servers and summarize are not
1711
- available yet; the terminal, status, rename and send-keys are.
1712
-
1713
- ### Sessions on a linked machine
1714
-
1715
- Once a machine is linked (below), this AgentOS talks to the AgentOS running
1716
- there instead of running tmux over ssh. That machine owns its sessions: their
1717
- processes, chats and state. This one is a client of it, over HTTP and
1718
- WebSockets with the link's device token, and only ever connects out to it.
1719
-
1720
- - The sessions its AgentOS runs are sessions here: each is mirrored (same id)
1721
- as soon as it's listed, with the same row, menu and address as a local one.
1722
- One whose folder is in a project you have for that machine sits with that
1723
- project's sessions; the rest are listed under the machine's name. Its
1724
- orchestrator and its tasks stay with it. **Elsewhere** is only for tmux
1725
- sessions no AgentOS started.
1726
- - From the menu, rename, done (and its undo) and delete run on that machine.
1727
- Fork, fresh start, move to project and check-ins are shown disabled with
1728
- the reason. A new session started here on that machine is started by its
1729
- AgentOS and opens in chat, like a local one.
1730
- - A session for a project here can start on a linked machine too: pick it in
1731
- the draft's Machine chip. That machine finds the project by its repository
1732
- or folder, or clones it, as for tasks. A machine that can't (no remote, a
1733
- folder there that isn't a repository, no clone access, unreachable) is
1734
- greyed out in the chip with the reason. The session sits with the project
1735
- here.
1736
- - Starts are safe to retry: the draft's id is the start's key and the
1737
- session's id, so a resend, or this machine retrying a start whose answer
1738
- was lost, gets the session the first one made instead of a second.
1739
- - Terminals attach through its `/ws/terminal`, relayed by this server; one
1740
- connection per session however many views show it, with flow control
1741
- carried through. A dropped connection retries for two minutes, then says so.
1742
- - Chats open in the chat view through its `/ws/chat`, retried the same way
1743
- while the chat stays open. Messages from agents
1744
- (`aos send`) and schedules aren't relayed to another machine's chats: they'd
1745
- arrive there as yours.
1746
- - The orchestrator asks that machine for a linked task's screen before
1747
- clearing it; a screen it can't get counts as possibly blocked.
1748
- - ssh stays for machines that aren't linked. A row that can't be opened
1749
- (the machine unreachable, a name tmux can't attach) is greyed out and says why.
1750
-
1751
- ### Tasks on another machine
1752
-
1753
- A machine that runs its own AgentOS can take tasks. Run AgentOS there (any
1754
- port behind your own front door, or plain 3011 on loopback plus a proxy), then
1755
- press **Link** (the chain icon) next to it in Machines. Linking asks that
1756
- AgentOS for a pairing code over ssh, where its loopback is trusted, and keeps
1757
- the device token it gets back. The token goes straight into this machine's
1758
- database and is never shown.
1759
-
1760
- Once linked:
1761
-
1762
- - **New task → Run on** picks the machine (`aos task --on devbox <project> ...`
1763
- does the same). That machine creates the worktree, runs the agent and opens
1764
- the PR on its own. This one lists the task in the sidebar and Tasks with its
1765
- live state, opens its chat (or terminal) through that machine's AgentOS,
1766
- and sends sign-off and drop to it (pinned to the reviewed commit). Opened
1767
- while it still sets up, it shows that machine's setup steps and joins the
1768
- agent once it starts. Tasks open as chats unless Terminal is picked.
1769
- - **Move** carries a running task between this machine and a linked one,
1770
- either way. The source stops the agent, commits anything uncommitted as
1771
- `wip: moving to <machine>`, pushes the branch and hands over Claude's
1772
- conversation. The target finds the project by its git remote, then by its
1773
- folder relative to `~`, and clones it when it has neither. It checks the
1774
- branch out (reusing a worktree still on it), rewrites the conversation's
1775
- paths and resumes it with `--resume`. The source keeps its row, marked moved.
1776
- If the target refuses, the agent resumes where it was. If the source can't
1777
- tell whether it arrived (a timeout, a restart), the task stays **Moving**:
1778
- press Move again to finish it, or **Resume here**, which first asks the
1779
- other machine whether it arrived. Tasks waiting on you (an escalated gate or
1780
- an open ask), card tasks and stacked tasks don't move yet.
1781
- - **An orchestrator's task moves too**, by the orchestrator's `move` tool (`task`,
1782
- `machine`; `here` brings it back) or the Move button. Its state is re-keyed to
1783
- the task's new session id in the same step it lands: brief and scope changes,
1784
- plan file and phase, gate checks and failure counts, asks, wakes, queue
1785
- order, arm, model and provider. Both machines need an AgentOS that can do
1786
- it, and the arriving one must have the task's provider. The machine it lands
1787
- on holds it for the orchestrator's machine alone: only that device can
1788
- merge, arm, message, stop, done or read it, and the arriving machine's own
1789
- orchestrator, armed merges and bulk done skip it. The orchestrator keeps
1790
- judging the PR at its head, reads BLOCKED lines and open approvals from the
1791
- task's chat there, and gets PR, CI, idle and plan events by polling that
1792
- machine. Its messages arrive marked as from the orchestrator, never as
1793
- yours. The task can `aos send` its orchestrator back: the machine it runs
1794
- on only stores the message, and the orchestrator's machine pulls it and
1795
- delivers it as that task's own. Moving it back stops its agent there and
1796
- removes its worktree when nothing in it would be lost. Guards (write guard, shell deny list, attribution settings) run in
1797
- the task's worker on either machine, and the task can't merge or push to
1798
- the default branch. Moving counts toward the brakes and Pause refuses the
1799
- tool.
1800
- - **A chat task moves the same way**, between turns: a running turn is let
1801
- finish (up to 3 minutes, else the move is refused and it carries on here),
1802
- and one waiting on your answer isn't moved. What's sent to it while its
1803
- turn finishes waits in its queue and goes with it; once the move has packed
1804
- the queue, sends are refused until it arrives. Its chat history, queue,
1805
- access and plan settings go
1806
- with it, and on arrival a chat worker resumes the same Claude conversation.
1807
- Both machines need an AgentOS that can move chat tasks.
1808
-
1809
- Works for any project. Requests to the other machine carry its device token,
1810
- so it can sit behind a proxy that never trusts loopback. Linking needs this
1811
- machine or the tailnet, and only pairs with the ssh target's own host name.
1812
- The orchestrator starts tasks on its own machine and moves them afterwards.
53
+ Then open AgentOS, add a project from ⌘K, and press ⌥N. The [install guide](docs/guides/install.md) covers Windows (WSL2), the supported agents, first run and the `agent-os` commands. To use it from your phone, see the [phone guide](docs/guides/phone.md).
1813
54
 
1814
55
  ## Security
1815
56
 
1816
- AgentOS gives whoever uses it a terminal as you, so it decides who that is:
1817
-
1818
- - **This machine** (localhost) is always trusted.
1819
- - **Your tailnet** is trusted by default. Turn on "Require pairing on Tailscale
1820
- too" in Devices if other people share it.
1821
- - **Everything else** needs a paired device. That covers Wi-Fi, `AGENTOS_BIND`,
1822
- and any reverse proxy, including `tailscale serve`. A request that arrives
1823
- from localhost with proxy headers (`X-Forwarded-*`, `Forwarded`, `Via`,
1824
- `X-Real-IP`, `CF-*`, `Tailscale-*` and similar) counts as coming from the
1825
- proxy's client, not from this machine. A proxy that sends none of these
1826
- looks like this machine itself, so put such a proxy behind
1827
- `AGENTOS_AUTH=off` only if it does its own login.
1828
- - A request from localhost must also be addressed to `localhost` or
1829
- `127.0.0.1`. This stops a web page that has rebound its own domain to
1830
- 127.0.0.1 from counting as this machine.
1831
-
1832
- Each paired device holds its own token. Only a hash of it is stored, and
1833
- removing the device in Devices cuts its open terminals at once. A paired
1834
- device can use AgentOS but can't add devices or change access; only this
1835
- machine and the tailnet can.
1836
-
1837
- AgentOS also refuses requests addressed to any other host name (DNS rebinding),
1838
- and API calls or terminal connections made by another website's page. That
1839
- Origin check holds whatever `AGENTOS_BIND` says: a page may write to the API
1840
- or open a terminal, chat or status socket only from this machine's own
1841
- addresses (loopback, the addresses it listens on, its tailnet name, its own
1842
- Connect address and `AGENTOS_ALLOWED_HOSTS`). Another Connect machine's page
1843
- is always refused. With `AGENTOS_BIND=0.0.0.0`, reaching it by a DNS name
1844
- other than these needs that name in `AGENTOS_ALLOWED_HOSTS`.
1845
-
1846
- | Variable | Effect |
1847
- | -------------------------------------- | ------------------------------------------------------------------------------------------------------ |
1848
- | `AGENTOS_NETWORK=lan` | Listen on Wi-Fi too. Same as the switch in Devices. |
1849
- | `AGENTOS_REQUIRE_PAIRING_ON_TAILNET=1` | Pairing on the tailnet too. |
1850
- | `AGENTOS_BIND` | Listen on these addresses instead (e.g. `0.0.0.0`). Pairing still applies. |
1851
- | `AGENTOS_ALLOWED_HOSTS` | Extra host names to answer to. |
1852
- | `AGENTOS_TOKEN` | A device token for `aos` and the MCP server when they reach AgentOS over a network that needs pairing. |
1853
- | `AGENTOS_AUTH=off` | No pairing, for when your own proxy does the login. Anyone who reaches the port gets a shell. |
1854
-
1855
- ### Connect
1856
-
1857
- Connect (in development) reaches this machine from anywhere at
1858
- `https://m-<16 hex>.runagentos.com`. `agent-os connect` links the machine with
1859
- a code from runagentos.com: it makes an Ed25519 key in `~/.agent-os/connect`
1860
- that never leaves the machine, and the Connect service makes the machine a
1861
- Cloudflare tunnel. Running it again on a linked machine links the same tunnel
1862
- again, keeping its address. The first-run setup's Devices step can link too: paste
1863
- the code into the field (same code path, `POST /api/devices/connect`, local or
1864
- tailnet callers only). `agent-os disconnect` (or the switch in Devices)
1865
- turns it off.
1866
-
1867
- While Connect is on, the production server:
1868
-
1869
- - downloads cloudflared 2026.10.0 into `~/.agent-os/connect/bin` (never one
1870
- from `PATH`) and checks its SHA-256 before making it executable or running it;
1871
- - asks the Connect service for a fresh connector token on every start and hands
1872
- it to `cloudflared tunnel run` in `TUNNEL_TOKEN` only, never in argv, a file
1873
- or a log;
1874
- - starts cloudflared only once AgentOS holds the tunnel port and has seen a
1875
- connection to it arrive on its own listener, and stops it if the listener
1876
- goes, so it never publishes something else on that port;
1877
- - runs cloudflared in a process group of its own under a small watchdog that
1878
- kills it the moment AgentOS exits, however it exits, and kills one a crash
1879
- left behind (from `cloudflared.pid`) before starting another;
1880
- - restarts cloudflared with backoff if it exits, and stops it when Connect is
1881
- turned off. Devices shows its state.
1882
-
1883
- Cloudflare's edge ends TLS and cloudflared delivers requests to a listener of
1884
- their own on `127.0.0.1:<port + 433>`. That listener never trusts anything as
1885
- this machine, the tailnet or `AGENTOS_AUTH=off`: every request needs a paired
1886
- device. It answers only the machine's own host name and drops
1887
- `X-Forwarded-Host`, `Forwarded` and `X-Original-Host` before anything reads
1888
- them.
1889
-
1890
- | Variable | Effect |
1891
- | ---------------------- | ------------------------------------------------------------------- |
1892
- | `AGENTOS_CONNECT=0` | Never start Connect in this process. |
1893
- | `AGENTOS_CONNECT_PORT` | The tunnel's loopback port (default AgentOS's port + 433). |
1894
- | `AGENTOS_CONNECT_DIR` | Where the key and cloudflared live (default `~/.agent-os/connect`). |
1895
-
1896
- ### Demo mode
1897
-
1898
- `AGENTOS_DEMO=1` runs a server that is safe to show to strangers, over fake
1899
- data. Every request is refused unless it is on a read allowlist
1900
- (`lib/security/demo.ts`): no terminals, no exec or send-keys, no git, tmux or
1901
- file writes, nothing that starts a session, task, dev server or other process.
1902
- Reads of sessions, chats, diffs and PRs work, as does pairing a device from
1903
- this machine. A few writes stay database-only: pinning a session, Done
1904
- (archives, with no merge or clean-up), answering an ask, and removing your own
1905
- device. A chat message gets a short canned reply instead of an agent, and the
1906
- device list and network settings show no addresses. Visitors share one demo,
1907
- so what they type (a chat message, an answer to an ask) is kept and shown as
1908
- fixed text, and the database goes back to its seed every 10 minutes, in
1909
- place, while it keeps serving.
1910
- File reads stay inside the demo's home, and the server refuses to start in
1911
- demo mode unless `HOME` and `DB_PATH` both sit under `AGENTOS_DEMO_ROOT`, so
1912
- it can never serve a real machine's sessions.
1913
-
1914
- ```bash
1915
- npm run demo # seed fake data, serve it on 127.0.0.1:3340
1916
- npm run build && npm run demo -- --prod
1917
- AGENTOS_BIND=0.0.0.0 AGENTOS_ALLOWED_HOSTS=demo.example.com AGENTOS_AUTH=off npm run demo -- --prod
1918
- ```
1919
-
1920
- The seed (`scripts/screenshots/seed.ts`) needs git and tmux, and works on macOS
1921
- and Linux. `AGENTOS_DEMO_ROOT` (default `/tmp/agentos-demo`) and
1922
- `AGENTOS_DEMO_PORT` move it; keep the root short, as tmux socket paths are
1923
- limited to about 100 characters.
57
+ AgentOS gives whoever uses it a terminal as you, so it decides who that is. This machine (localhost) is always trusted. Your tailnet is trusted by default. Everything else, including Wi-Fi and any reverse proxy, needs a paired device. `AGENTOS_AUTH=off` removes pairing and gives anyone who reaches the port a shell, so use it only behind a proxy that does its own login. The full model, Connect and demo mode are in [docs/reference/security.md](docs/reference/security.md).
1924
58
 
1925
59
  ## Development
1926
60
 
1927
61
  ```bash
1928
- npm run screenshots # regenerate screenshots/ from a demo instance with fake data
1929
62
  npm run dev # http://localhost:3011
1930
63
  npm test # vitest
1931
64
  npm run lint # eslint
1932
65
  npm run check # typecheck, lint, format and tests: what CI runs
1933
- scripts/redeploy # pull, install, build; restarts via $AGENTOS_RESTART only if all of it worked
1934
- scripts/redeploy --rollback # put the previous build back and restart
66
+ npm run screenshots # regenerate screenshots/ from a demo instance with fake data
1935
67
  ```
1936
68
 
1937
- One Node process serves every terminal, chat stream and request, so a route
1938
- handler in `app/api/` may not call a `*Sync` child_process or fs function:
1939
- ESLint refuses it. Use the async form with a timeout.
1940
-
1941
- Every git process starts in `lib/git-exec.ts`: `git()`, `gitResult()`,
1942
- `gitOk()` and named operations (`revParse`, `worktreeAdd`, `gitPush`, …). It
1943
- sets one timeout, kills a hung git, never lets git prompt, keeps the server's
1944
- tmux out of hooks and rejects with a `GitError` carrying the exit code and
1945
- stderr. ESLint refuses git spawned anywhere else in `lib/`, `app/` or
1946
- `server.ts`.
1947
-
1948
- `scripts/redeploy` (and `scripts/autodeploy`, which calls it) builds into
1949
- `.next-build` while the live server keeps serving `.next`. The build is swapped
1950
- in by rename only once it finished with a `BUILD_ID`, right before the restart.
1951
- The build it replaced is kept in `.next-prev` for one rollback, and a failed
1952
- build leaves `.next` as it was.
1953
-
1954
- `scripts/autodeploy` waits for a merge to be 2 minutes old
1955
- (`AGENTOS_SETTLE`), so a burst of merges is one build and one restart. Before
1956
- it pulls, `scripts/wait-quiet` waits for the reviews and task setups the
1957
- server runs (`GET /api/busy`, loopback or tailnet only) to finish, for up to
1958
- 10 minutes (`AGENTOS_QUIET_WAIT`), then deploys anyway. A review a restart
1959
- cuts off anyway runs again when the server starts, and its verdict arrives
1960
- as usual.
1961
-
1962
- `scripts/perf/loadtest.mjs` starts a throwaway AgentOS (its own HOME,
1963
- database, port and tmux socket) with N idle sessions and reports RSS, file
1964
- descriptors, child processes and spawns per minute; `scripts/perf/execs.mjs
1965
- --pid <pid>` counts what any server spawns, grouped by command. Both count
1966
- spawns with eslogger (macOS) or strace (Linux) and need passwordless sudo for
1967
- that one number. Run them on a machine with room to spare.
1968
-
1969
- A pre-commit hook (`.husky/pre-commit`, installed by `npm install`) checks
1970
- only what is staged, in well under two seconds: `prettier --check`, failing
1971
- with the `npx prettier --write …` command that fixes it, and a scan of the
1972
- added lines for token-shaped secrets with the merge gate's own rule
1973
- (`scripts/hooks/staged-secrets.ts` reads `secretsIn` from
1974
- `lib/orchestrator/diff.ts`), naming the file and line. Build a test's fake
1975
- token at runtime (`"gh" + "p_" + "a".repeat(36)`) rather than writing it
1976
- whole. It also refuses words that must not land in this public repo
1977
- (`scripts/hooks/banned-terms.ts`): a short built-in list, plus, when it
1978
- exists, a private list of regexes kept outside the repo in
1979
- `~/.config/agent-os/banned-names` (`AGENTOS_BANNED_NAMES` overrides the
1980
- path), checked against added lines and file names; `scripts/check` runs the
1981
- same check over every tracked file. It runs in the worktrees AgentOS makes too, which have no `.husky/_`
1982
- until `npm install` runs in them. Typecheck, lint and tests are left to CI.
1983
- In an emergency, `git commit --no-verify` skips it. CI runs `scripts/check --build` on every pull request and push to main,
1984
- which also holds the app's first-load JavaScript to a budget
1985
- (`scripts/check-bundle.mjs`; `npx next experimental-analyze` shows what's in
1986
- it).
1987
-
1988
- ### Session audits
1989
-
1990
- `/session-audit`, a Claude Code skill kept locally (gitignored, not in this
1991
- repo), audits the sessions AgentOS started (tasks, chats, orchestrators and their review
1992
- agents) for token waste and repeated manual work. Its extractor,
1993
- `scripts/session-audit/audit.ts`, streams the sessions' Claude transcripts and
1994
- AgentOS's own records (checks, notes, asks, messages) into a compact summary
1995
- in a couple of seconds: context read per turn, peak context, the biggest tool
1996
- outputs, repeated commands and errors, files re-read, polling loops, rebases,
1997
- hand-stripped trailers and dev-instance clean-ups. The skill reads only that
1998
- summary, ranks a plan by impact and effort, compares it with the last audit,
1999
- and offers to start the fixes you pick as tasks.
2000
-
2001
- ### Code health audits
2002
-
2003
- `/code-health-audit`, also a local skill, audits this codebase for
2004
- performance, security and duplication. A script does the measuring first:
2005
- `scripts/code-health/prepass.ts` collects `npm audit` and outdated
2006
- packages, first-load bundle sizes from the last build, the largest files and
2007
- functions, copied blocks, ESLint by rule, API routes that are public or
2008
- change state without a `lib/security` guard, commands a shell parses and SQL
2009
- built from strings, secret shapes and machine details (redacted), and slow
2010
- tests from a vitest JSON, into one compact summary, and compares its numbers
2011
- with the last audit's. The skill reads that summary and the lines it points
2012
- at, writes a ranked plan (calling out work a script could do instead of a
2013
- model), and starts the fixes you pick as tasks.
2014
-
2015
- Audits quote session content and name weak spots, so they stay on your
2016
- machine in `~/.agent-os/audits/<kind>/` (session audits in `sessions/`,
2017
- code health in `code-health/`;
2018
- `AGENTOS_AUDITS_DIR` moves the root) and never in a repo. Settings → Audits
2019
- (or ⌘K, "Session audits") lists every kind's markdown reports, newest first,
2020
- and renders them.
2021
-
2022
- ## Mobile access
2023
-
2024
- **At home:** open **Settings → Devices & access** from the menu, turn on "Allow devices on this Wi-Fi",
2025
- tap "Add a device", and scan the QR code with your phone. Laptops open the link
2026
- and type the code. Each code works once, for 10 minutes.
2027
-
2028
- **Anywhere:** install [Tailscale](https://tailscale.com) on this machine and
2029
- your phone and sign in to the same account. Devices then shows a Tailscale
2030
- link, which works from anywhere and is encrypted. If your tailnet has HTTPS
2031
- certificates turned on, AgentOS also serves `https://<machine>.ts.net:3443`
2032
- with Tailscale's own certificate (renewed daily). Passkeys, the clipboard
2033
- and notifications need that secure address. Plain HTTP on 3011 keeps
2034
- working. `AGENTOS_TAILNET_HTTPS=0` turns it off, and
2035
- `AGENTOS_TAILNET_HTTPS_PORT` moves it. Use Wi-Fi access only on
2036
- networks you trust, because traffic on Wi-Fi is not encrypted.
69
+ A pre-commit hook checks formatting, secrets and banned terms on what is staged. Route handlers may not call `*Sync` functions, and git runs only through `lib/git-exec.ts`. More in [docs/reference/development.md](docs/reference/development.md).
2037
70
 
2038
71
  ## License
2039
72