agent-lookout 0.0.0-stage → 0.2.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/DISCLAIMER.md +9 -0
  3. package/LICENSE +21 -0
  4. package/PRIVACY.md +368 -0
  5. package/README.md +225 -2
  6. package/SECURITY.md +105 -0
  7. package/bin/agent-lookout.mjs +45 -0
  8. package/dist/THIRD-PARTY-LICENSES.md +2377 -0
  9. package/dist/assets/HistoryPanel-DfqfRz1u.js +1 -0
  10. package/dist/assets/SessionPanel-Bo6eqCwI.js +1 -0
  11. package/dist/assets/atkinson-hyperlegible-mono-latin-400-normal-CmMGer93.woff2 +0 -0
  12. package/dist/assets/atkinson-hyperlegible-mono-latin-400-normal-Cmjm2dBj.woff +0 -0
  13. package/dist/assets/atkinson-hyperlegible-mono-latin-500-normal-BV5ipwqM.woff +0 -0
  14. package/dist/assets/atkinson-hyperlegible-mono-latin-500-normal-CfDLaOZW.woff2 +0 -0
  15. package/dist/assets/atkinson-hyperlegible-mono-latin-600-normal-BiYyYOKJ.woff2 +0 -0
  16. package/dist/assets/atkinson-hyperlegible-mono-latin-600-normal-CuPF9o0F.woff +0 -0
  17. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-400-normal-CpVFOiMk.woff2 +0 -0
  18. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-400-normal-ITMuFrSK.woff +0 -0
  19. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-500-normal-BiHEOgiK.woff +0 -0
  20. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-500-normal-DE1cvYE7.woff2 +0 -0
  21. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-600-normal-DKhxRsTL.woff2 +0 -0
  22. package/dist/assets/atkinson-hyperlegible-mono-latin-ext-600-normal-ZiH-ObRk.woff +0 -0
  23. package/dist/assets/atkinson-hyperlegible-next-latin-400-normal-DPp7N0cD.woff +0 -0
  24. package/dist/assets/atkinson-hyperlegible-next-latin-400-normal-FfmJh7DR.woff2 +0 -0
  25. package/dist/assets/atkinson-hyperlegible-next-latin-500-normal-B0CYqHl3.woff +0 -0
  26. package/dist/assets/atkinson-hyperlegible-next-latin-500-normal-gHP6TDRs.woff2 +0 -0
  27. package/dist/assets/atkinson-hyperlegible-next-latin-600-normal-BBuhw47K.woff2 +0 -0
  28. package/dist/assets/atkinson-hyperlegible-next-latin-600-normal-BhgCag5w.woff +0 -0
  29. package/dist/assets/atkinson-hyperlegible-next-latin-ext-400-normal-BalHKn7d.woff2 +0 -0
  30. package/dist/assets/atkinson-hyperlegible-next-latin-ext-400-normal-qxMiU51n.woff +0 -0
  31. package/dist/assets/atkinson-hyperlegible-next-latin-ext-500-normal-CUASR3PD.woff2 +0 -0
  32. package/dist/assets/atkinson-hyperlegible-next-latin-ext-500-normal-oZS3KxrF.woff +0 -0
  33. package/dist/assets/atkinson-hyperlegible-next-latin-ext-600-normal-BBAQIogt.woff +0 -0
  34. package/dist/assets/atkinson-hyperlegible-next-latin-ext-600-normal-kZZEtAc7.woff2 +0 -0
  35. package/dist/assets/beatWorker-zHI4d3P9.js +1 -0
  36. package/dist/assets/index-BEOekdx1.js +2 -0
  37. package/dist/assets/index-CtnpRdvw.css +1 -0
  38. package/dist/assets/motion-BAhdRuCw.js +9 -0
  39. package/dist/assets/radix-27o0jeIu.js +41 -0
  40. package/dist/assets/react-C2DiJaR6.js +9 -0
  41. package/dist/cli/agent-lookout.js +216 -0
  42. package/dist/cli/chunk-PQ3B5TN6.js +392 -0
  43. package/dist/cli/chunk-UYGESSPG.js +141 -0
  44. package/dist/cli/chunk-XJM5RZNV.js +36 -0
  45. package/dist/cli/mcpServer-CGLIGL4X.js +246 -0
  46. package/dist/cli/startCommand-6YPFCG3S.js +5104 -0
  47. package/dist/favicon.svg +7 -0
  48. package/dist/index.html +40 -0
  49. package/docs/API.md +209 -0
  50. package/docs/GUIDE.md +976 -0
  51. package/package.json +108 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ Changes that a user of Agent Lookout would notice, newest first.
4
+
5
+ ## 0.2.0 - 2026-10-06
6
+
7
+ The first version on npm. It shows Claude Code and Codex sessions, and those of any agent that writes a status file. The one thing it changes on the machine is which tmux pane, or which tab of Terminal or iTerm2, is in front, when you press Jump.
8
+
9
+ ### Added
10
+
11
+ #### Sessions
12
+
13
+ - Claude Code sessions on this machine appear on one dashboard with no setup, whether they run in a terminal, in VS Code or in the desktop app.
14
+ - Codex sessions appear beside Claude Code's with no setup, including the Codex desktop app's, and each row names its agent once both are found.
15
+ - A Codex session shows as working, idle or finished, never as needing you, because Codex does not record in its files when it is waiting for approval.
16
+ - Any other agent, including one you wrote yourself, can show its sessions by writing one small JSON file for each into `~/.agent-lookout/sessions`, or the folder `AGENT_LOOKOUT_STATUS_DIR` names. A session appears within 2 seconds under the agent's own name, needs you and sends a notification when its file says `waiting`, and goes when its file is deleted or its process ends. The guide's Your own agents part has the format. Agent Lookout only reads the folder.
17
+ - Each session shows its status: needs you, working or idle.
18
+ - A session that needs you says whether it is waiting for permission or asked you a question.
19
+ - A Claude Code session that needs you also says what it is asking, under its reason in the Needs you panel, in its details and in the notification of its wait: the command it wants to run, as in `Run: npm test`, the file it wants to edit, write or read, the page or search it wants, a plan to approve, the tool it wants to use, or the question it put to you, with how many more it has. It is read from the last message of the session's transcript only while the session waits, is cut to two lines, and is forgotten when the wait ends. Emails, webhook posts and `agent-lookout mcp` never carry it. `AGENT_LOOKOUT_WAITING_TEXT=off` stops any transcript being read.
20
+ - A session that has been idle for 24 hours or more is marked stale.
21
+ - Background sessions that finished or failed stay on the dashboard for 24 hours, marked Finished or Failed.
22
+ - A session whose folder is in a git repository shows the branch it has checked out, under the folder in the Sessions list and in the Needs you panel, as in `storefront on checkout-flow`, so sessions in worktrees of one repository can be told apart. With no branch checked out, it shows the first seven characters of the commit instead. It works the same for every agent, reads only the repository's `.git` file and `HEAD` for it, for each session's folder at most every 10 seconds, writes nothing and runs no git command.
23
+ - Sessions in worktrees of one repository can be seen together. Choose Repos in the switch at the top of the Sessions list, beside List and Board, and the list is grouped under one heading for each repository, as in `storefront 3`, with sessions in no repository last, under `No repository`, and the status order kept inside each group. The choice is kept in the browser. On the board, a worktree's card names its repository before its folder, as in `storefront · storefront-checkout on checkout-flow`, and a session's details name its repository. A worktree belongs to the repository it was made from, and a submodule is a repository of its own. Agent Lookout works this out from the `.git` file and `HEAD` it already reads and, for a worktree, the `commondir` file beside its `HEAD`, which names the repository's own git folder, so worktrees kept beside a bare repository, as in `storefront/.bare`, group together too. The page is sent the repository's name and a hash of its git folder's path, never the path.
24
+ - A working session whose agent has written nothing to its session file for 5 minutes or more says how long in the Sessions list, as in `quiet for 12m`, with the time of the last write when you hover over it or move to it with Tab. It is a measurement beside the status, which stays Working, and it never moves a session to the Needs you panel. A Codex session waiting for your approval shows as working, so a long quiet stretch is the sign to look. Codex sessions and status files give the time, from the file's modified time alone, and a Codex session's time also counts the files of the subagents it started. Claude Code sessions do not, because the file Agent Lookout reads for them is not rewritten as the session works.
25
+ - When a session's app is not known, nothing names it: the header, the Needs you panel, a board card, a search result, an email and a webhook post leave it out, and the Sessions list shows a dash. A status file can name its app with `app`: `terminal`, `vscode` or `desktop`.
26
+
27
+ #### The Overview
28
+
29
+ - Sessions that need you sit in the Needs you panel at the top of the Overview, longest wait first, each with a timer of how long it has waited. When none does, the panel says so and shows the last wait that ended in the last hour.
30
+ - Bars in that panel show how long each session has waited on you, going back at most an hour, and a row under them counts the sessions that are working, idle and stale, and every session found.
31
+ - The Last hour chart shows how many sessions were waiting on you, working and idle, on average, in each five minutes of the last hour.
32
+ - The Needs you panel's heading and its Working and Idle counts open a chart of up to six hours.
33
+ - The Sessions list can be shown as a board, with a column each for Needs you, Working, Idle, and Finished or failed, each with its count. Each card gives the session's name, agent, folder, branch, app and how long it has had its status, with Jump where the list has it. A card moves to its new column by itself within about 2 seconds of its status changing, and cannot be dragged. A switch at the top of the list chooses List or Board, and the choice is kept in the browser.
34
+ - The Events log records when a session appears, changes status or ends, and when Agent Lookout started watching again after a break.
35
+ - Come back to the dashboard's tab after it was in the background, or open the dashboard again, and a line in the Events log marks where you left off, such as New since 14:02:37, with the number of events since beside the log's title, such as 3 new. Both go once the line has been in view for 10 seconds, or when you open another view. With the tab in front all the time, nothing is marked. The only thing kept for it is the time you left off, in the browser.
36
+ - The Timeline shows each session's status over the last hour, with the time Agent Lookout did not measure hatched.
37
+ - Find a session from the keyboard. `/`, or `Cmd+K` on a Mac and `Ctrl+K` elsewhere, or the magnifier in the header opens a search over each session's name, folder, branch and agent, with every word typed found in any order. Sessions that need you are listed first, longest wait first, then the rest in the order of the Sessions list. Up and Down move through them, and Enter presses the session's Jump when it has one, or takes you to its row on the Overview. `?` lists every shortcut, including the arrow keys the charts, the Timeline and the switches already had.
38
+ - Open a session to see everything Agent Lookout knows about it. Click its row in the Sessions list or its card on the board, or move to its name with Tab and press Enter, and its details open over the Overview: its status with the reason and how long, its agent, app, full folder path, branch, start time and process ID, how many times and how long it waited for you, its own events and its row of the Timeline, with its Jump button where it has one. Each session's details have their own address, so a bookmark, a reload and the browser's Back button land on them, and closing them puts focus back on the row or card.
39
+ - A session that needs you opens its details from its name in the Needs you panel, and Enter in the search opens the details of a session with no Jump button.
40
+
41
+ #### Jump
42
+
43
+ - A Jump button opens a Claude Code session that runs in VS Code.
44
+ - A Claude Code session that runs inside tmux has a Jump button too. It selects the session's pane, the pane's window and, on any attached terminal that is showing another tmux session, the pane's session. The row says what happened: Selected in tmux, That pane has closed or tmux has stopped. It does not bring your terminal to the front. `AGENT_LOOKOUT_TMUX=off` stops Agent Lookout running tmux, and takes the button off sessions in tmux.
45
+ - On a Mac, a Claude Code session in a tab of Terminal or iTerm2, outside tmux, has a Jump button too. It brings that tab to the front, in its window, with the app. The first time, macOS asks once whether the program Agent Lookout runs in may control the app, and the row says so while it asks. If macOS did not allow it, the row says where to allow it: System Settings, Privacy & Security, Automation. Sessions in other terminals, such as Warp, Ghostty or the VS Code terminal, have no button. `AGENT_LOOKOUT_TERMINAL_JUMP=off` stops Agent Lookout looking for tabs of Terminal and iTerm2, and takes the button off sessions there.
46
+
47
+ #### Notifications
48
+
49
+ - Turn on notifications in Settings, and your browser shows a system notification when a session starts waiting for you. It names the session and the reason, and is cleared when the session moves on.
50
+ - With notifications on, Agent Lookout goes on sending them on a Mac after the dashboard tab is closed, for as long as it keeps running. It shows those itself, so they come from Script Editor and stay until you clear them. An open tab that is polling and Agent Lookout do not both send one for the same wait. `AGENT_LOOKOUT_NOTIFICATIONS=on` turns these on from the moment it starts, without the dashboard being opened.
51
+ - Choose what sends a notification. While notifications are on, Settings lists four events, each with its own switch: Needs you, Finished, Failed and Ended, which is a session leaving the list without having finished or failed. Needs you is on and the others are off until you change them. A notification names the session and what happened, and one that says Finished, Failed or Ended stays until you clear it. The choice is kept in the browser, and Agent Lookout's own notifications, with no tab open, follow it too. Nothing is sent for what was already true when the page opened or Agent Lookout started.
52
+
53
+ #### Email and webhooks
54
+
55
+ - Agent Lookout can email you when a session has waited for you for a minute, or as long as `AGENT_LOOKOUT_EMAIL_AFTER` says. It is off unless you start it with `AGENT_LOOKOUT_EMAIL_TO` and `AGENT_LOOKOUT_SMTP_URL`, and by default it sends nothing anywhere. Once set up, it sends one short plain-text email for each wait, through the mail server you name, to the one address you name, over TLS, at most 20 in an hour. The Email card in Settings says whether it is on and how the last email went.
56
+ - `AGENT_LOOKOUT_EMAIL_EVENTS` chooses what is emailed: any of `needs-you`, `finished`, `failed` and `ended`, separated by commas. It is `needs-you` unless you set it. A session that finishes, fails or ends is emailed at once, within the same 20 an hour, with a subject such as "billing-webhooks finished". The Email card names the events it sends.
57
+ - Agent Lookout can post to a webhook, such as a Slack channel's incoming webhook, when a session has waited for you for a minute, or as long as `AGENT_LOOKOUT_WEBHOOK_AFTER` says, and, with `AGENT_LOOKOUT_WEBHOOK_EVENTS`, when one finishes, fails or ends. It is off unless you start it with `AGENT_LOOKOUT_WEBHOOK_URL`, and by default it sends nothing anywhere. Once set, it sends one short JSON post for each, to that one address and no other, over HTTPS, at most 20 in an hour, counted apart from emails. Each post names the session, what happened and when, and its folder's name, app and agent, and nothing in a session's name can mention someone, ping a channel or make a link that shows other words than its address. A Slack incoming webhook takes the posts with nothing more to set up, and Discord takes them with `/slack` added to its address. The Webhook card in Settings says whether it is on, names the host alone and says how the last post went.
58
+
59
+ #### Sources, Settings and the page
60
+
61
+ - A rail down the left edge moves between the Overview, Sources and Settings, and its mark lights up while any session needs you.
62
+ - The browser tab's title shows how many sessions need you, so it can be read while the tab is in the background.
63
+ - The Sources view says whether Claude Code and Codex were found, what Agent Lookout reads and runs for each, and how often. A card for status files says whether their folder is there, how often it is read, and how many files were read and skipped.
64
+ - Under the source cards, What each agent can report says Yes, No or Partly for each agent and each thing it can show, such as needs you, finished, Jump and quiet for, with the reason for each No and Partly. The guide has the same table.
65
+ - Night and Day themes: panels of tinted glass over a warm black at Night or a warm stone by Day, with amber kept for the sessions that need you. Settings can make the theme follow the computer's setting.
66
+
67
+ #### Outside the dashboard
68
+
69
+ - `npm run build` followed by `npm start` serves the dashboard from one local process at `127.0.0.1:4777`.
70
+ - `agent-lookout`, or `agent-lookout start`, starts Agent Lookout as `npm start` does and prints its address. `--port` chooses the port, and `--open` opens the address in the default browser. The package for npm holds the built dashboard and the command as plain JavaScript, so `npx agent-lookout` runs it with nothing to clone and no TypeScript. It also holds `dist/THIRD-PARTY-LICENSES.md`, with the licences of the libraries and fonts the dashboard includes.
71
+ - `agent-lookout --version`, or `-v`, prints the version.
72
+ - `agent-lookout status` prints in the terminal which sessions need you: the counts, then each waiting session's name, the reason and how long it has waited. `--count` prints only the number, for a tmux status line or a shell prompt, and `--json` prints the same as JSON. The exit code is 0 when nothing needs you, 1 when a session does and 2 when it cannot tell, such as when Agent Lookout could not be reached or read. It reads the Agent Lookout already running on this machine, at a loopback address only, and starts nothing. Run it with `npx agent-lookout status`, as `agent-lookout status` after `npm install -g agent-lookout`, or in a clone with `npm run --silent status`.
73
+ - `agent-lookout mcp` lets an AI agent keep track of your other agents. It is a Model Context Protocol server that the agent's app starts and speaks to over stdin and stdout, so it listens on no port, and it reads the Agent Lookout already running on this machine, at a loopback address only, as `agent-lookout status` does. Its three tools only read: `list_sessions` gives every session with its status, the reason it waits, its folder's name, branch, app and how long it has been quiet, or only those with one status; `sessions_needing_you` gives the waits, longest first, with how long each has waited and one sentence that sums them up; and `sources` says whether each agent is being read and what it can and cannot report. Each tool says that session names are untrusted text written by other programs, and anything a terminal would act on, and any character that shows nothing, is taken out of them. When Agent Lookout is not running, each tool says so and how to start it. Add it to Claude Code with `claude mcp add agent-lookout -- npx -y agent-lookout mcp`, or in a clone with `<path to the clone>/bin/agent-lookout.mjs mcp` in place of `npx -y agent-lookout mcp`.
74
+ - docs/API.md describes the local API: every route and its answer, the `Host` and `Origin` rules, the notifications header and the checks on `POST /api/jump`. The API is for this machine only, and has no token.
75
+
76
+ #### On Linux
77
+
78
+ - Agent Lookout runs on Linux, started the same way and reading the same folders. It has been checked in CI on Ubuntu by starting it with `npm start` and reading back its sessions, and not yet by a person on a Linux desktop. The guide's On Linux part says what works there.
79
+ - When it is not on `PATH`, the `claude` command is also looked for in `~/.npm-global/bin` and `/usr/bin`, after the places it was looked for on a Mac.
80
+ - A Claude Code session whose start time `ps` now gives up to a minute away from the one in its registry file is still shown. Linux's `ps` gives another start time after the clock is set, as it often is after waking from sleep.
81
+ - Notifications with no dashboard tab open, and Jump to a tab of Terminal or iTerm2, are macOS only. Started with `AGENT_LOOKOUT_NOTIFICATIONS=on` anywhere else, Agent Lookout prints a line saying it shows no notifications itself.
package/DISCLAIMER.md ADDED
@@ -0,0 +1,9 @@
1
+ # Disclaimer
2
+
3
+ Agent Lookout is an independent, unofficial project. It is not affiliated with, endorsed by or sponsored by Anthropic, OpenAI or any other company whose tools it works with.
4
+
5
+ Claude and Claude Code are trademarks of Anthropic. Codex is a product of OpenAI. Other product names belong to their owners. These names appear in this project only to say which tools Agent Lookout is compatible with. The app uses no vendor logos or brand colours.
6
+
7
+ For Claude Code, Agent Lookout runs the documented session listing and reads session files whose format is undocumented. Claude Code can change either one without notice. For Codex it reads only files that Codex does not document, and Codex can change them in any release. When that happens, Agent Lookout may show less, or show no sessions, until it is updated.
8
+
9
+ The software is provided as is, without warranty of any kind, under the [MIT licence](LICENSE).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Idris Olubisi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PRIVACY.md ADDED
@@ -0,0 +1,368 @@
1
+ # Privacy
2
+
3
+ Agent Lookout runs on your machine and reads a small amount of metadata about your Claude Code and Codex sessions, and about the sessions of any agent that writes a status file for Agent Lookout to read, and which git branch each session's folder has checked out. By default Agent Lookout itself sends nothing anywhere. Email notifications are off unless you set them up. Once you do, it sends a short email when a session has waited, and, if you choose, when one finishes, fails or ends, through the mail server you name, to the address you name, and nothing else. [Email](#email) says what one holds. Webhook posts are off unless you set an address for them. Once you do, it sends the same notices as short JSON posts to that one address, such as a Slack channel's incoming webhook, and nowhere else. [Webhook](#webhook) says what one holds. It does run Claude Code's own listing command, which may contact Anthropic the way Claude Code normally does. Its MCP server, `agent-lookout mcp`, runs only once you add it to an agent's app, and answers that app and nothing else. That app may pass what it learns to its model: [The MCP server](#the-mcp-server) says what an answer holds.
4
+
5
+ For a Claude Code session that is waiting for you, Agent Lookout reads the last message of its transcript, to show what the session is asking, such as the command it wants to run or the question it put to you. It reads nothing of a transcript while the session is not waiting, keeps nothing of it once the wait ends, and never sends it off the machine: no email, webhook post or answer of `agent-lookout mcp` holds it. [Claude Code's transcripts](#claude-codes-transcripts) says exactly what is read, and `AGENT_LOOKOUT_WAITING_TEXT=off` turns it off.
6
+
7
+ For Codex, Agent Lookout opens Codex's session files, which hold the whole conversation. It reads them to find when each turn started and ended, and keeps only that and the few fields listed below. It keeps no prompt, reply, command or output.
8
+
9
+ ## What it reads and runs
10
+
11
+ ### Claude Code's session registry
12
+
13
+ Every 2 seconds Agent Lookout lists the folder `~/.claude/sessions/` and reads each file in it whose name ends in `.json`. Claude Code keeps one small file there for each running session, named `<pid>.json`. Agent Lookout reads only ordinary files of 256 KB or less, and on macOS and Linux it does not follow a link. From each file it keeps these fields and drops the rest: `pid`, `sessionId`, `cwd`, `startedAt`, `kind`, `entrypoint`, `name`, `status`, `waitingFor`, `state`, `statusUpdatedAt` and `procStart`. `entrypoint` says whether the session runs in a terminal, in VS Code or in the desktop app, `statusUpdatedAt` says when its status last changed, and `procStart` says when its process started. It makes no use of when these files were last written.
14
+
15
+ ### Claude Code's transcripts
16
+
17
+ Claude Code keeps each session's conversation in a transcript, `~/.claude/projects/<folder>/<session ID>.jsonl`, where the folder is named after the session's folder. Agent Lookout reads one only while its session is waiting for you, as the registry or the `claude` command says, and reads nothing of any transcript otherwise.
18
+
19
+ For a waiting session it looks for the file in the folder named after the session's folder, and, when it is not there, in each folder in `~/.claude/projects/`, by name, which lists those folders. It uses the session ID only once it has checked that it is an ID, and opens only an ordinary file: on macOS and Linux it does not follow the transcript file when it is a link, and it never reads a named pipe or a device. The folders above the file are followed when they are links: a folder in `~/.claude/projects/` that is a link, or `projects` itself. It reads at most the last 256 KB of the file, by position, and reads it again only when the file's size or modified time has changed. While a wait goes on it does not look for a file again, except every 10 seconds when none was found.
20
+
21
+ From those bytes it takes the last tool the session asked to use that has no answer yet, leaving out the lines of the session's subagents, and keeps one line of plain text of at most 200 characters about it, cleaned of control characters:
22
+
23
+ - for a question the session put to you, the first question, and how many more there are
24
+ - for a command, `Run:` and the command's first line
25
+ - for a file it would edit, write or read, `Edit:`, `Write:` or `Read:` and the file's path, relative to the session's folder when it is inside it
26
+ - for a web page or a search, `Fetch:` and the address, or `Search:` and the words
27
+ - for a plan, "Approve the plan"
28
+ - for any other tool, `Use:` and the tool's name, except a tool that starts a subagent, for which there is no line
29
+
30
+ Everything else in those bytes, which can include your prompts, the agent's replies, the code it wrote and the output of tools, is dropped as soon as the line is made. When nothing in the end of the file fits, there is no line, and nothing is guessed.
31
+
32
+ That line is shown in the Needs you panel and in the session's details, is put in the notification of that wait that the dashboard page or the server shows on this machine, and is in the session list the API serves on this computer. It is kept in memory beside the place of the file and its size and modified time, only while the session waits, and is forgotten on the first poll after the wait ends. It is not written to disk, not kept in the event log or the history, and not put in an email, a webhook post or an answer of `agent-lookout mcp`. Set `AGENT_LOOKOUT_WAITING_TEXT=off` and no transcript is opened at all.
33
+
34
+ ### `claude agents --json --all`
35
+
36
+ This is the session listing command that Claude Code documents for outside tools. Agent Lookout runs it once when it starts and every 30 seconds after that, and checks the registry against its answer. While the registry cannot be relied on, it runs the command every 5 seconds instead. That happens when the folder cannot be read, when a file in it has a status Agent Lookout does not know, or when the command lists a running session that the folder lacks. A Claude Code too old to know `--all` refuses it, and is then asked with `claude agents --json`.
37
+
38
+ From each entry it keeps these fields and drops the rest: `pid`, `cwd`, `kind`, `startedAt`, `sessionId`, `name`, `status`, `waitingFor`, `id` and `state`.
39
+
40
+ The command is started directly, with no shell, with stdin closed and a 5 second timeout. It is not run at all when `AGENT_LOOKOUT_CLAUDE_FEED` is `off`, or when `AGENT_LOOKOUT_CLAUDE_HOME` is set and `AGENT_LOOKOUT_CLAUDE_BIN` is not.
41
+
42
+ To find the `claude` binary, Agent Lookout reads the `PATH` environment variable and checks whether a program named `claude` exists in each `PATH` directory, then at `~/.local/bin/claude`, `/opt/homebrew/bin/claude`, `/usr/local/bin/claude`, `~/.npm-global/bin/claude` and `/usr/bin/claude`. When `AGENT_LOOKOUT_CLAUDE_BIN` is set, it checks that path and nothing else.
43
+
44
+ ### `ps`
45
+
46
+ A registry file can outlive a crashed session, and the system can later give its process ID to another program. To catch that, Agent Lookout runs `ps -o pid=,lstart= -p <pids>`, which prints when each of those processes started, and compares the answer with the `procStart` that the registry file recorded. A file whose process started at another time is not shown as a session. One run covers several processes, and each process is asked about at most once every 30 seconds. `ps` is run directly from `/usr/bin` or `/bin`, never through a shell, and never on Windows.
47
+
48
+ ### `tmux`
49
+
50
+ A Claude Code session that runs inside tmux gets a Jump button, and for that Agent Lookout has to know which pane the session's process is in. While at least one Claude Code session is running, it runs:
51
+
52
+ ```sh
53
+ tmux -u list-panes -a -F '#{pane_pid} #{pane_id} #{window_index} #{pane_index} #{session_name}'
54
+ ```
55
+
56
+ It runs this when it first has a session to ask about and every 30 seconds after that. It runs it sooner when a new session's process appears, though never within 5 seconds of the last run, and once more after a Jump has found its pane gone or tmux stopped. For every pane of the tmux server it reads five things: the ID of the process tmux started in the pane, the pane's ID, the numbers of its window and of the pane, and the name of its tmux session. It reads nothing a pane shows, and no pane's title, command or folder. With no Claude Code session running, it does not run tmux.
57
+
58
+ When tmux lists at least one pane, Agent Lookout also runs `ps -A -o pid=,ppid=`, which prints the ID of every process on this machine and the ID of its parent, and nothing else about any of them. It follows each session's process up through its parents until it reaches a pane's process. The list of processes is dropped as soon as that is done.
59
+
60
+ For a session found in a pane, the dashboard is sent the tmux session's name with the window and pane numbers, such as `work:2.1`, to show on the Jump button. The pane's ID stays in the server, in memory.
61
+
62
+ When you press Jump on such a session, the page sends the server the session's ID, and the server runs these, each with the pane's ID that it found itself:
63
+
64
+ - `tmux select-window -t <pane ID>` and `tmux select-pane -t <pane ID>`, which select the pane and its window
65
+ - `tmux -u display-message -p -t <pane ID> '#{session_id} #{window_index} #{pane_index} #{session_name}'`, which prints where the pane is and changes nothing
66
+ - `tmux list-clients -F '#{session_id} #{client_name}'`, which prints each terminal attached to tmux and the ID of the tmux session it is showing
67
+ - `tmux switch-client -c <terminal> -t <pane ID>`, once for each terminal that is showing another tmux session
68
+
69
+ These change which pane, window and tmux session are selected, and nothing else. No keys and no text are sent to any pane.
70
+
71
+ tmux is started directly, never through a shell, with stdin closed and a 2 second timeout, and never on Windows. Agent Lookout looks for it in each `PATH` directory, then at `/opt/homebrew/bin/tmux`, `/usr/local/bin/tmux`, `/opt/local/bin/tmux` and `/usr/bin/tmux`, and runs nothing when it is not there. tmux is given the environment Agent Lookout was started with, so it reaches the tmux server the `tmux` command would reach from there. None of these commands starts a tmux server. When the folder tmux keeps its sockets in is not there yet, `/tmp/tmux-` followed by your user number, tmux itself makes it, empty, the first time it is asked. With `AGENT_LOOKOUT_TMUX=off`, Agent Lookout runs neither tmux nor that `ps`.
72
+
73
+ ### Terminal and iTerm2
74
+
75
+ On a Mac, a Claude Code session that runs in a tab of Terminal or iTerm2 gets a Jump button, and for that Agent Lookout has to know which app the session's process runs in and which terminal it has. When a Claude Code session's process appears, it runs:
76
+
77
+ ```sh
78
+ ps -A -o pid=,ppid=,tty=,comm=
79
+ ```
80
+
81
+ That prints four things about every process on this machine: its ID, its parent's ID, its terminal, such as `ttys004`, and the name its program gives itself, which for an app is the path of its program, such as `/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal`, and for a shell can be `-zsh`. It asks for no other argument, no owner and no environment. Agent Lookout follows each new session's process up through its parents, looking for Terminal's program or iTerm2's, and keeps two things about the session: which of the two apps it is in, and the session's own terminal, such as `/dev/ttys004`. The rest of the list is dropped as soon as that is done. One run covers every session that appeared since the last, and it runs no more than once every 5 seconds. A session is asked about once for as long as its process runs, and once more after a Jump has found its tab closed. With no Claude Code session running, it does not run this.
82
+
83
+ For a session found in a tab, the dashboard is sent the app's name, `Terminal` or `iTerm2`, to show on the Jump button. The terminal stays in the server, in memory.
84
+
85
+ When you press Jump on such a session, the page sends the server the session's ID, and the server runs `/usr/bin/osascript` with a script that never changes, one for each app, then `--` and the terminal it found itself. The script asks the app for the tab or pane that shows that terminal, makes it the selected one, brings its window to the front and brings the app to the front. It reads each tab's terminal to find it, and nothing else of any tab: no title, no contents and no command. It sends no keys and no text. It prints `found` or `missing`, and that is all Agent Lookout reads from it, with the error macOS gives when it has not allowed it.
86
+
87
+ macOS asks you once whether the program Agent Lookout runs in may control the app, and keeps your answer in its own settings, under Privacy & Security, Automation. Agent Lookout does not see or change that setting.
88
+
89
+ `ps` and `osascript` are started directly, never through a shell, with stdin closed. `ps` is run from `/usr/bin` or `/bin` with a 2 second timeout, and `osascript` by its full path with a timeout of one minute, which leaves time to answer macOS's question. Neither is run on any other system. With `AGENT_LOOKOUT_TERMINAL_JUMP=off`, Agent Lookout runs neither for this.
90
+
91
+ ### Whether a process exists
92
+
93
+ Agent Lookout asks the operating system whether each Claude Code session's process still exists with a signal-0 check on the process ID, which sends nothing to the process. It does the same for each status file that names a process.
94
+
95
+ ### Codex's files
96
+
97
+ Agent Lookout reads Codex's folder: the one `AGENT_LOOKOUT_CODEX_HOME` names, otherwise the one Codex's own `CODEX_HOME` names, otherwise `~/.codex`. It runs no Codex program, changes no Codex setting and installs no hook. None of these files is documented by Codex. [docs/adapters/codex.md](docs/adapters/codex.md) records where each one comes from.
98
+
99
+ When the folder is not there, Agent Lookout looks for it again once a minute, or on every poll when `AGENT_LOOKOUT_CODEX_HOME` names it, and reads nothing else for Codex.
100
+
101
+ When the folder is there, every 2 seconds Agent Lookout:
102
+
103
+ - lists `sessions/` and the day folders for today and yesterday, `sessions/YYYY/MM/DD/`. While Codex has a session open whose file is in an older day folder, as a resumed session's can be, it lists every day folder: at most once every 5 seconds when Codex has newly opened such a session, and otherwise at most once every 30 seconds. Listing reads file names only.
104
+ - lists `thread-writer-locks/`, where Codex keeps one `<thread id>.lock` file for each session a Codex program has open. It reads the names and never opens these files.
105
+ - opens, read-only, each session file named `rollout-*.jsonl` in those day folders. It reads up to the first 2 MiB of a file to find its first `session_meta` line, and up to the last 8 MiB to find the last line that says a turn started or ended. After that it reads only what Codex has added, and only when the file's size or modified time has changed. A file not written to for a day, whose session no Codex program has open, is not opened. The file's modified time, which it already looks up to see whether the file has changed, is also passed to the dashboard as the time Codex last wrote to the session, to say how long a working session has been quiet. A subagent's file is one of these session files too, and its modified time counts as a write by the session that started it.
106
+ - opens `session_index.jsonl`, where Codex keeps the names you gave sessions, when its size or modified time has changed, and reads at most its last 4 MiB.
107
+
108
+ The bytes it reads are held in memory only while the file is read. A line is parsed only when it names `session_meta`, or names `event_msg` and looks like a turn line. From every other line, which includes your prompts, Codex's replies, the commands it ran and their output, Agent Lookout takes only the time the line was written.
109
+
110
+ From the `session_meta` line it keeps `id`, `timestamp`, `cwd`, `source`, `thread_source`, `parent_thread_id`, `originator` (the program that started the session, such as the Codex desktop app) and `cli_version` (the Codex version), and drops the rest, which includes Codex's instructions, its list of tools, git details and account IDs. From a turn line it keeps the kind of line, such as `task_started` or `task_complete`, its time, and whether its `turn_id` marks a turn the Codex desktop app imported from another agent. From `session_index.jsonl` it keeps `id` and `thread_name`.
111
+
112
+ A session file or `session_index.jsonl` that is a link, a named pipe or a device is never opened. The Codex folder and the folders inside it are followed when they are links.
113
+
114
+ ### Status files
115
+
116
+ Any agent can write one small JSON file for each of its sessions into a folder for Agent Lookout to read, as the [guide](docs/GUIDE.md#your-own-agents) describes. The folder is `~/.agent-lookout/sessions`, or the one `AGENT_LOOKOUT_STATUS_DIR` names. Agent Lookout never makes it.
117
+
118
+ Every 2 seconds Agent Lookout lists that folder and reads each file directly in it whose name ends in `.json` and does not start with a dot: at most 200 files. When there are more, it looks up when each was last written, without opening it, and reads the 200 written most recently. It reads only ordinary files of 16 KB or less, and on macOS and Linux it does not follow a symbolic link in the folder. The folder itself is followed when it is a link. It does not look in folders inside it. When the folder is not there, it lists nothing else.
119
+
120
+ From each file it keeps these fields and drops the rest: `agent`, `name`, `cwd`, `status`, `reason`, `since`, `pid` and `app`. `agent` is the agent's name, `cwd` the folder the session works in, `since` when its status began, `pid` the ID of its process and `app` the app the session runs in. It also keeps each file's name, which tells one session from another, and the time the file was last written, taken from the file it opened, which says how long a finished session stays and how long a working session has been quiet. The Sources card names the first file it skipped, so you can find it.
121
+
122
+ For a file that names a `pid`, Agent Lookout asks the operating system whether that process still exists, with the same signal-0 check it makes for Claude Code, which sends nothing to the process. Nothing from a file is run or used as a link. The `cwd` is shown, and is looked in for a git repository as it is for every session, as [Git repositories](#git-repositories) describes. Nothing else in it is read.
123
+
124
+ ### Git repositories
125
+
126
+ For every session that has a folder, whatever its agent, Agent Lookout finds the git repository the folder is in and reads which branch is checked out there. It runs no git command, and writes nothing in any repository.
127
+
128
+ It looks for an entry named `.git` in the session's folder, then in each folder above it in turn, and stops at the first it finds. It does not look in your home folder or the root, nor above them, nor in more than 24 folders in all, and it looks nowhere for a folder that does not exist. Looking means asking the system what is at that name, without following a symbolic link: it lists no folder.
129
+
130
+ - When `.git` is a folder, it reads `.git/HEAD`.
131
+ - When `.git` is a file, as in a worktree or a submodule, it reads that file, which names the repository's git folder in one line, `gitdir: <path>`, then reads `HEAD` in the folder it names, and `commondir` beside it when there is one. A worktree's git folder has a `commondir`, one line naming the git folder of the repository the worktree was made from. A submodule's has none.
132
+ - When `.git` is anything else, such as a symbolic link, it reads nothing, and the session has no branch.
133
+
134
+ It reads only ordinary files of 4 KB or less, and on macOS and Linux it does not follow a `HEAD` or a `commondir` that is a symbolic link. `HEAD` says either the name of the branch that is checked out, which Agent Lookout keeps, cleaned of control characters and of the characters that change the direction of text, and cut to 200 characters, or the ID of a commit when no branch is, of which it keeps the first seven characters. It keeps nothing else from these files. For each session's folder it reads them at most once every 10 seconds, and it forgets a folder when no session is in it.
135
+
136
+ With the branch it keeps which repository the folder belongs to, so the dashboard can group the worktrees of one repository together. A repository is known by its own git folder, which all its worktrees share: a `.git` folder is its own, a worktree's `commondir` names it, and any other `.git` file, such as a submodule's, names it in its one line. Agent Lookout reads nothing more for this. It keeps two things about the repository: its name, cleaned and cut as a branch is, and an id to tell repositories apart, the first 16 characters of the SHA-256 hash of the git folder's path. The name is that of the folder that holds `.git`, such as `storefront`, and for a worktree the one its repository's git folder gives: `storefront` for `storefront/.git`, `storefront/.bare` or `storefront.git`. Both are in the session list the API serves on this computer, beside the branch, and nowhere else: no email, post, notification or answer of `agent-lookout mcp` holds them. The path itself is not sent. The id is never shown, and holds no part of the path, though someone who guessed a path could check the guess against it.
137
+
138
+ It reads nothing else in a repository: no other branch or reference, no `packed-refs`, no configuration, no index, no logs, no objects, and none of the files you work on.
139
+
140
+ ### `osascript`
141
+
142
+ With notifications on, and no dashboard page open to show one, Agent Lookout shows a notification itself when a session starts waiting for you, or, if you chose those too, finishes, fails or ends. On macOS it does that by running `/usr/bin/osascript`, the program macOS provides for running AppleScript. It gives it a script that never changes, which shows a notification, and two arguments for that script: the session's name and the reason, with what the session is asking when that is known, or what happened. The name is handed over as text to be shown. It is never made part of the script, so nothing in a name can run as AppleScript or be read as an option.
143
+
144
+ `osascript` is started directly, by that full path, never through a shell, with stdin closed and a 5 second timeout. It is not run on any other system, and never while notifications are off. [Terminal and iTerm2](#terminal-and-iterm2) says when it is also run for Jump. While it runs, for a fraction of a second, the name is one of that program's arguments, which other programs on this machine can read from the list of running processes. [Notifications](#notifications) says when notifications are on and what one holds.
145
+
146
+ ### `open` and `xdg-open`
147
+
148
+ Only when you start it with `agent-lookout --open`, Agent Lookout runs `/usr/bin/open` on macOS, or `xdg-open` on Linux, once, to open its own address in your default browser. It gives it one argument, the address it listens on, such as `http://127.0.0.1:4777`, and nothing else. The program is started directly, never through a shell, with nothing connected to its input or output. Agent Lookout waits up to 10 seconds for it to end, to tell whether it worked, and never stops it: `xdg-open` can keep running until the browser it started is closed. Without `--open`, neither is ever run.
149
+
150
+ ### Settings
151
+
152
+ It reads twenty settings from the environment: `AGENT_LOOKOUT_CLAUDE_HOME`, `AGENT_LOOKOUT_CLAUDE_BIN`, `AGENT_LOOKOUT_CLAUDE_FEED`, `AGENT_LOOKOUT_WAITING_TEXT`, `AGENT_LOOKOUT_CODEX_HOME`, `AGENT_LOOKOUT_STATUS_DIR`, `AGENT_LOOKOUT_NOTIFICATIONS`, `AGENT_LOOKOUT_TMUX`, `AGENT_LOOKOUT_TERMINAL_JUMP`, `AGENT_LOOKOUT_PORT`, `AGENT_LOOKOUT_HOST`, Codex's own `CODEX_HOME`, the five email settings, `AGENT_LOOKOUT_EMAIL_TO`, `AGENT_LOOKOUT_SMTP_URL`, `AGENT_LOOKOUT_EMAIL_FROM`, `AGENT_LOOKOUT_EMAIL_AFTER` and `AGENT_LOOKOUT_EMAIL_EVENTS`, and the three webhook settings, `AGENT_LOOKOUT_WEBHOOK_URL`, `AGENT_LOOKOUT_WEBHOOK_EVENTS` and `AGENT_LOOKOUT_WEBHOOK_AFTER`. The `agent-lookout` command reads one more, `AGENT_LOOKOUT_URL`, the address of the Agent Lookout to ask, and `agent-lookout status` reads tmux's own `TMUX`, only to tell whether tmux is running it. `AGENT_LOOKOUT_CLAUDE_HOME` replaces `~/.claude` in everything above. [Email](#email) says how the email settings are kept, and [Webhook](#webhook) how the webhook's are. The [guide](docs/GUIDE.md#settings-you-can-change) says what each setting does.
153
+
154
+ ## What it never reads
155
+
156
+ - The `.key` files that sit beside the registry files in `~/.claude/sessions/`.
157
+ - Claude Code's transcripts, in `~/.claude/projects/` or anywhere else, beyond the end of a waiting session's own, as [Claude Code's transcripts](#claude-codes-transcripts) describes. Of what it reads there, it keeps one line about the tool the session is asking to use, and none of your prompts, the agent's replies, your code or the output of tools.
158
+ - Claude Code's settings, credentials, history and memory files.
159
+ - Codex's `auth.json`, `config.toml`, `history.jsonl`, its SQLite files (`*.sqlite`), its `log/` folder, `archived_sessions/` and compressed session files (`*.jsonl.zst`), and the Codex desktop app's `external_agent_session_imports.json` and `.codex-global-state.json`.
160
+ - In a git repository, anything but the `.git` file, `HEAD` and a worktree's `commondir` described under [Git repositories](#git-repositories).
161
+ - The files of any other application.
162
+
163
+ It runs no program but the `claude` binary, `ps`, `tmux`, on macOS `osascript`, to show a notification and to bring a tab of Terminal or iTerm2 forward, and, only for `--open`, `open` or `xdg-open`. It runs no git command. It never writes to `~/.claude`, to the Codex folder, to the folder of status files, to any git repository or to any agent tool's files, and it never sends input to a session. It never makes, renames or deletes a status file. The one thing it changes outside itself is which tmux pane, window and session are selected, or which tab of Terminal or iTerm2 is in front, and only when you press Jump.
164
+
165
+ ## Network
166
+
167
+ With email and the webhook off, which is the default, the only network traffic Agent Lookout's own code makes is between its own server and the dashboard in your browser, or the `agent-lookout status` and `agent-lookout mcp` commands, on the same machine. With email set up, it also connects to the mail server you named, once for each email, as [Email](#email) describes. With a webhook address set, it also connects to that address, once for each post, as [Webhook](#webhook) describes. One more connection never leaves the machine either: before `npm start` or `agent-lookout` begins listening, it connects once to its own address and port to see whether another program already answers there, and sends nothing over that connection. There is no telemetry, no analytics, no crash reporting and no update check, and there is no account. Fonts and scripts are bundled, so the page loads nothing from the internet.
168
+
169
+ Started with `npx agent-lookout`, npm itself contacts its registry: to download the package the first time, and each time after to ask whether a newer version is out, as it does for any package it runs. That is npm's own traffic, not Agent Lookout's. Installed with `npm install -g agent-lookout` and started as `agent-lookout`, npm is not involved.
170
+
171
+ The `claude agents` command is Claude Code's own program, and it may contact Anthropic the way it does for anyone who runs it. Agent Lookout sets `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` and `DISABLE_AUTOUPDATER` for each run, to ask Claude Code to skip its update check, usage reporting and error reporting, and runs the command seldom. Anything else that program does is governed by Claude Code's settings and terms. To stop Agent Lookout running it, set `AGENT_LOOKOUT_CLAUDE_FEED=off`. Sessions then come from the registry alone, and background jobs whose process has ended are not listed.
172
+
173
+ `npm start` and `agent-lookout` listen on a loopback address only: `127.0.0.1`, or `::1` if you set `AGENT_LOOKOUT_HOST`. They refuse to start on any other address. `npm run dev` uses Vite's dev server, which listens on localhost unless you pass it `--host`.
174
+
175
+ The API refuses any request whose `Host` header is not `localhost`, `127.0.0.1` or `[::1]`, any request whose `Origin` header names another site, and any request the browser marks as cross-site. It sends no CORS headers. Together these stop a website you visit from reading your session names and paths through your browser.
176
+
177
+ Each request the dashboard makes carries one header of Agent Lookout's own, `X-Agent-Lookout-Notifications`, which says which events that page has notifications on for: `off`, `on`, which means a session starting to wait, or `on; events=` followed by the names of the events, such as `on; events=needs-you,finished`. It holds nothing else. It goes to Agent Lookout's own server and nowhere else. [Notifications](#notifications) says what the server does with it. The request the Jump button sends for a session in tmux, Terminal or iTerm2 carries one more, `X-Agent-Lookout-Action: jump`, which names what is asked for.
178
+
179
+ The local server has no password. While Agent Lookout is running, another program on the same machine can request the same data, and on a shared computer so can another user account. Such a program can also send that header, and so turn the notifications the server shows on or off. It can also ask the server to select a tmux pane, or bring a tab of Terminal or iTerm2 forward, as the Jump button does.
180
+
181
+ For a session in VS Code, the Jump button opens a `vscode://` address that contains the session ID. Your operating system hands it to VS Code. For a session in tmux, the Jump button sends a request that holds the session's ID to Agent Lookout's own server, which selects the pane as described under [`tmux`](#tmux). For a session in Terminal or iTerm2 it sends the same request, and the server brings the tab forward as described under [Terminal and iTerm2](#terminal-and-iterm2). That request is the only one that changes anything, and the server answers it only for a page served from this machine. [SECURITY.md](SECURITY.md) lists its checks.
182
+
183
+ ## Notifications
184
+
185
+ Notifications are off until you turn them on in Settings, or start Agent Lookout with `AGENT_LOOKOUT_NOTIFICATIONS=on`, which turns on only the ones the server shows, and only for a session starting to wait. Pressing Turn on notifications is the only thing in Agent Lookout that asks your browser for permission to show them.
186
+
187
+ Once they are on, Settings lists four events, each with its own switch: a session starting to wait for you, finishing, failing, and ending, which is leaving the list without having finished or failed. The first is on and the other three are off until you change them. A notification is made only for the events switched on.
188
+
189
+ A notification is made in one of two ways. While a dashboard page is open, the page makes it through your browser. With no page open, the local server shows it on this machine itself. A page that is open shows it and the server does not, unless the page's requests are held up for more than about 4 seconds, when both can show one. The [guide](docs/GUIDE.md#notifications) says when that happens.
190
+
191
+ ### From the dashboard page
192
+
193
+ With notifications on, when one of the events switched on happens, the dashboard page makes a notification through the browser's Notifications API, and the browser hands it to the operating system to show. It holds:
194
+
195
+ - the session's name, as its title
196
+ - what happened, as its text: for a session that starts waiting, the reason, "Waiting for permission", "Asked you a question" or "Waiting for you", followed, for a Claude Code session, by what it is asking when its transcript says, as in "Waiting for permission: Run: npm test", and otherwise "Finished", "Failed" or "Ended"
197
+ - a tag that is not shown, which the browser uses to keep one notification for each session: `agent-lookout:claude-code:` followed by the session's ID, or by its job ID or process ID when it has no session ID, for Codex `agent-lookout:codex:` followed by the session's ID, and for a status file `agent-lookout:status-files:` followed by the file's name
198
+
199
+ It holds none of Claude Code's own wording for the wait, and not the session's folder. What a session is asking can hold a command, which can name any path, a web address, or a file: relative to the session's folder when the file is inside it, and the file's full path when it is outside it, which can name your home folder. No push service, no service worker and no network request is involved. The page that is open in your browser makes the notification, and can do so only while it is open.
200
+
201
+ The page closes the notification of a wait when its session stops waiting, when you turn notifications or that event off, and when the page is closed or reloaded. It cannot close one if the browser crashes or is forced to quit first. It never closes one that says Finished, Failed or Ended: that stays until you clear it, or until a later notification for the same session takes its place. While Agent Lookout is stopped, or cannot read Claude Code's sessions, the page cannot tell that a session has moved on, so a notification already showing stays. A notification stays in the system's notification list, Notification Centre on macOS, until the page closes it or you clear it. What the browser and the operating system keep of a notification, in memory or on disk, is theirs, and Agent Lookout cannot read it back.
202
+
203
+ When a dashboard tab is closed or reloaded, it tells the other dashboard tabs open at the same address, in the same browser, which sessions' notifications it closed, so that one of them can show them again. That message holds session IDs, goes over the browser's `BroadcastChannel` and does not leave the browser.
204
+
205
+ The browser gives its permission to the address, such as `localhost:5173`, not to Agent Lookout. Another program served at the same address later can show notifications without asking, and can read or change the values listed under Storage. To take the permission back, remove it for that address in the browser's site settings.
206
+
207
+ ### From the server
208
+
209
+ The local server shows notifications on this machine too. When one of the events switched on happens and no dashboard page is open to show it, the server runs `osascript`, as [described above](#osascript), and macOS shows the notification. It holds:
210
+
211
+ - the session's name, as its title
212
+ - what happened, as its text, in the same wordings, with what a waiting session is asking
213
+
214
+ It holds none of Claude Code's own wording and no tag. As in the page's, what a session is asking can hold a command, a web address, or a file's full path when the file is outside the session's folder. No network request is involved. On any system but macOS the server shows nothing.
215
+
216
+ The server shows them only while notifications are on, and only for the events switched on, and it learns both from the dashboard. Each request a dashboard page makes says, in the `X-Agent-Lookout-Notifications` header, which events are on when that page's choice is on and the browser allows notifications, and `off` otherwise. The server keeps the last thing a page said, in memory, for as long as it runs, and never writes it to disk. Before any page has said anything it is off, unless `AGENT_LOOKOUT_NOTIFICATIONS=on` was set when Agent Lookout started, which turns it on for a session starting to wait. A page that says `off` after that turns it off all the same. The header is listened to only on a request that passes the checks described under [Network](#network), so a page at another address cannot set it.
217
+
218
+ While a page that has notifications on is open and asking every 2 seconds, the page shows each notification and the server does not. The server holds its own back for a few seconds when such a page has just been asking, and drops it once that page has fetched the sessions.
219
+
220
+ A notification the server shows differs from the browser's:
221
+
222
+ - macOS shows it as coming from Script Editor, which is how it labels whatever `osascript` shows. The browser's permission and its notification settings do not apply to it. On macOS 26.5, where this was checked in the system's log, macOS delivered it without first asking whether Script Editor may show notifications.
223
+ - It holds nothing that could open the session or the dashboard. What a click on it does has not been checked.
224
+ - The server cannot take it down. It stays in Notification Centre after the session stops waiting, and after Agent Lookout stops, until you clear it.
225
+
226
+ ## Email
227
+
228
+ Email notifications are off until you set them up, and they can be set up only in the environment Agent Lookout starts with: `AGENT_LOOKOUT_EMAIL_TO`, the one address emails go to, and `AGENT_LOOKOUT_SMTP_URL`, the mail server they go through, with the user name and password to sign in with. `AGENT_LOOKOUT_EMAIL_FROM`, the sender's address, `AGENT_LOOKOUT_EMAIL_AFTER`, how long a wait lasts before it is emailed, and `AGENT_LOOKOUT_EMAIL_EVENTS`, what is emailed, can be left out. While either of the first two is unset, no email is sent, the mail library is not loaded and no connection is opened. The same holds when any of the five is set to something Agent Lookout cannot read. The [guide](docs/GUIDE.md#email) says how to set them up and how to turn them off.
229
+
230
+ ### What an email holds
231
+
232
+ An email is plain text. Its subject is the session's name followed by the reason, in the words the dashboard uses, such as "checkout-flow is waiting for permission", or by what happened, such as "billing-webhooks finished", "billing-webhooks failed" or "billing-webhooks ended". Its body holds:
233
+
234
+ - that same sentence
235
+ - how long the session has waited, and the time on this computer's clock when it began, or the time on that clock when Agent Lookout saw it finish, fail or end
236
+ - the name of the session's project folder: the last part of its path, never the path
237
+ - the app it runs in, such as VS Code or Terminal, when it is known
238
+ - the agent, such as Claude Code or Codex, or the agent a status file names
239
+ - one line saying Agent Lookout sent it and how to stop these emails
240
+
241
+ It holds no folder path, no prompt, none of Claude Code's own wording for the wait, nothing of what a waiting session is asking, no link, no image and nothing that reports back when it is opened. What a session is asking is read from its transcript and can name a command or a file, so in this version it stays on this machine. Its headers are the ones the mail library writes for every email: From, with the name Agent Lookout and the sender's address, To, Subject, Date, Message-ID, and three that say it is plain text in UTF-8. A session's name and its folder's name are cut to 80 characters, and anything in them that would end a line becomes a space, so nothing in a name can add a header or a recipient.
242
+
243
+ ### Where it goes, and how
244
+
245
+ Each email goes to the one address in `AGENT_LOOKOUT_EMAIL_TO`, and to no other. It comes from the address in `AGENT_LOOKOUT_EMAIL_FROM`, or from the same address when that is not set. It goes through the mail server in `AGENT_LOOKOUT_SMTP_URL`, which then delivers it the way it delivers any email.
246
+
247
+ For each email Agent Lookout looks up the server's name, opens a connection to it, signs in with the user name and password from `AGENT_LOOKOUT_SMTP_URL`, hands the email over and closes the connection. It greets the server as `[127.0.0.1]`, not by this computer's name. The server sees the network address the connection comes from, as any server you connect to does. The connection is encrypted with TLS: from the first byte when the address begins `smtps://`, and when it begins `smtp://`, the server must offer to switch to TLS before anything is sent, or nothing is sent. The server's certificate is always checked. Only to a mail server on this machine, at `127.0.0.1` or `localhost`, does `smtp://` go without TLS, since nothing leaves the machine on the way to it.
248
+
249
+ ### When
250
+
251
+ `AGENT_LOOKOUT_EMAIL_EVENTS` names the events that are emailed, from `needs-you`, `finished`, `failed` and `ended`, and is `needs-you` alone unless you set it. An email is sent for a wait that begins after Agent Lookout started, once it has lasted the delay, which is one minute unless `AGENT_LOOKOUT_EMAIL_AFTER` says otherwise, if the session is still waiting then. A wait answered before then sends nothing, and so does a session that was already waiting when Agent Lookout started. Each wait sends one email at most. With the others set, an email is sent as soon as a session is seen to finish, fail or end, and none for a session that had already finished or failed when Agent Lookout started. An email that could not be sent is not tried again. At most 20 are sent in any hour, whatever they are for. Past that, none goes until the hour has passed. Claude Code sessions and sessions from a status file can be seen waiting; a Codex session never can, so it never sends one for a wait.
252
+
253
+ The button and the switches for notifications in Settings do not turn emails on or off, or choose what is emailed. To stop them, start Agent Lookout again without `AGENT_LOOKOUT_EMAIL_TO`.
254
+
255
+ ### What is kept
256
+
257
+ The settings, password included, live in the environment of the Agent Lookout process, as every setting on this page does. Programs running as your user on this machine can read a process's environment. Agent Lookout never writes them to a file, never sends them to the dashboard, and never prints them. When one cannot be read, it prints one line that names the setting and never its value. A setting typed in front of the command can be kept by your shell in its history file. The guide shows how to keep the password out of it.
258
+
259
+ Through `GET /api/email`, the dashboard learns whether email is on, the address with all but its first letter before the @ hidden, such as `n…@example.com`, the events that are emailed, the delay, and when the last email was tried, with whether it was sent or a short reason why not. It never learns the server, the user name or the password. That, and the times of the emails of the last hour, are held in memory and are gone when Agent Lookout stops.
260
+
261
+ Once an email has been handed to the mail server, Agent Lookout has no hold on it. The mail server you named and the mailbox it is delivered to keep the email, and whatever they record about it, for as long as their own settings and terms say. Agent Lookout cannot recall or delete it.
262
+
263
+ ## Webhook
264
+
265
+ Webhook posts are off until you set them up, and they can be set up only in the environment Agent Lookout starts with: `AGENT_LOOKOUT_WEBHOOK_URL`, the one address posts go to. `AGENT_LOOKOUT_WEBHOOK_EVENTS`, what is posted, and `AGENT_LOOKOUT_WEBHOOK_AFTER`, how long a wait lasts before it is posted, can be left out. While the address is unset, nothing is posted and no connection is opened. The same holds when any of the three is set to something Agent Lookout cannot read. The [guide](docs/GUIDE.md#webhook) says how to set it up and how to turn it off.
266
+
267
+ ### What a post holds
268
+
269
+ A post is one request with a body of JSON, such as:
270
+
271
+ ```json
272
+ {
273
+ "text": "checkout-flow is waiting for permission (4m 12s, storefront, VS Code, Claude Code)",
274
+ "event": "needs-you",
275
+ "reason": "permission",
276
+ "session": {
277
+ "name": "checkout-flow",
278
+ "agent": "Claude Code",
279
+ "folder": "storefront",
280
+ "app": "VS Code"
281
+ },
282
+ "at": "2026-10-05T14:01:05.000Z",
283
+ "waitedSeconds": 252
284
+ }
285
+ ```
286
+
287
+ It holds:
288
+
289
+ - `text`: one line for a person to read, which is the line Slack shows. It is the session's name and what happened, in the words the dashboard uses, then in brackets how long the session has waited, for a wait, the name of its project folder, its app and its agent, leaving out any that is not known.
290
+ - `event`: what happened, `needs-you`, `finished`, `failed` or `ended`.
291
+ - `reason`, for a wait only: `permission`, `question` or `other`.
292
+ - `session`: the session's name, its agent, such as Claude Code or the agent a status file names, the name of its project folder, which is the last part of its path and never the path, and the app it runs in, such as VS Code. Any of the last three is `null` when it is not known.
293
+ - `at`: when it happened, as a time in UTC: when the wait began, or when Agent Lookout saw the session finish, fail or end.
294
+ - `waitedSeconds`, for a wait only: how long it had waited when it was posted.
295
+
296
+ It holds no folder path, no prompt, none of Claude Code's own wording for the wait, nothing of what a waiting session is asking, no session ID or process ID, and nothing about this computer. What a session is asking stays on this machine, as it does for email. Its headers are `Content-Type: application/json`, `Content-Length`, `User-Agent: Agent Lookout/` followed by the version, `Host` and `Connection: close`. It carries no cookie and no other header.
297
+
298
+ A session's name, its folder's name and its agent's are each cut to 80 characters, and anything in them that would end a line becomes a space. In `text`, `<`, `>` and `&` are written as `&lt;`, `&gt;` and `&amp;`, which Slack shows as the characters themselves, and each `@` is followed by a space of no width, so nothing in a name can mention someone, ping a channel or make a link that shows other words than its address. The JSON is written whole by Node's own writer, so nothing in a name can add a field. Slack does show a web address written out in a name as a link to that address.
299
+
300
+ ### Where it goes, and how
301
+
302
+ Each post goes to the one address in `AGENT_LOOKOUT_WEBHOOK_URL`, and to no other. For each post Agent Lookout looks up the address's host, opens a connection to it, sends the post, reads the status of the answer and closes the connection. The rest of the answer is read and dropped. A redirect is not followed, and a post is never tried again. The address must begin `https://`, so the connection is encrypted with TLS, and the certificate is always checked. Only to an address on this computer, at `127.0.0.1` or `localhost`, may it begin `http://`, since nothing leaves the machine on the way to it. The service at the address sees the network address the connection comes from, as any server you connect to does.
303
+
304
+ ### When
305
+
306
+ `AGENT_LOOKOUT_WEBHOOK_EVENTS` names the events that are posted, from `needs-you`, `finished`, `failed` and `ended`, and is `needs-you` alone unless you set it. A post goes for a wait that begins after Agent Lookout started, once it has lasted the delay, which is one minute unless `AGENT_LOOKOUT_WEBHOOK_AFTER` says otherwise, if the session is still waiting then. A wait answered before then sends nothing, and so does a session that was already waiting when Agent Lookout started. Each wait sends one post at most. With the others set, a post goes as soon as a session is seen to finish, fail or end, and none for a session that had already finished or failed when Agent Lookout started. A post that could not be sent is not tried again. At most 20 posts are tried in any hour, whatever they are for, counted apart from emails. Past that, none goes until the hour has passed. These are the rules emails follow, and the two share one copy of them.
307
+
308
+ The button and the switches for notifications in Settings do not turn posts on or off, or choose what is posted. To stop them, start Agent Lookout again without `AGENT_LOOKOUT_WEBHOOK_URL`.
309
+
310
+ ### What is kept
311
+
312
+ Anyone who has the address can post to the channel behind it, so it is kept like a password. It lives in the environment of the Agent Lookout process, as every setting on this page does, and programs running as your user on this machine can read a process's environment. Agent Lookout never writes it to a file, never sends it to the dashboard, and never prints it. When a webhook setting cannot be read, it prints one line that names the setting and never its value. A setting typed in front of the command can be kept by your shell in its history file. The guide shows how to keep the address out of it.
313
+
314
+ Through `GET /api/webhook`, the dashboard learns whether the webhook is on, the host the posts go to and nothing else of the address, such as `hooks.slack.com`, the events that are posted, the delay, and when the last post was tried, with whether it was sent or a short reason why not. That, and the times of the posts of the last hour, are held in memory and are gone when Agent Lookout stops. Settings shows the host in full, so with a service that puts its secret in the host, keep Settings out of screenshots too.
315
+
316
+ Once a post has been sent, Agent Lookout has no hold on it. The service at the address, such as Slack or Discord, keeps the message, and whatever it records about it, for as long as its own settings and terms say, and shows it to everyone who can read that channel. Agent Lookout cannot recall or delete it.
317
+
318
+ ## The MCP server
319
+
320
+ `agent-lookout mcp` does nothing until you add it to an agent's app, such as Claude Code, as the [guide](docs/GUIDE.md#for-your-agents) shows. The app then starts it, and speaks to it over its stdin and stdout. It opens no port.
321
+
322
+ ### What it reads
323
+
324
+ For each tool the agent calls, it asks Agent Lookout's own server for the list of sessions, `GET /api/sessions`, once, at a loopback address only, as `agent-lookout status` does. The request carries no `Origin` and no notifications header, so it changes nothing in Agent Lookout. Otherwise it reads only `AGENT_LOOKOUT_URL` from the environment, and Agent Lookout's own `package.json`, for its version number. It reads no other file, runs no program and makes no other connection.
325
+
326
+ ### What it sends, and to whom
327
+
328
+ It answers the app that started it, over stdout, and nothing and nobody else. An answer is one of three:
329
+
330
+ - For each session: its ID, its name, its agent, its status, why it waits, the name of its folder and never the rest of the path, its branch or commit, its app, when its status began, and how long its agent has written nothing. Never what a waiting session is asking, which is read from its transcript.
331
+ - For each session that needs you: the same, with how long it has waited, and one sentence naming each of them and any agent that could not be read.
332
+ - For each source: its state, the sentence that says how it is read or what went wrong, which can name the folders Agent Lookout reads, such as `~/.claude/sessions`, and what its agent can report.
333
+
334
+ When Agent Lookout cannot be reached, the answer is one sentence that says so, with the address it tried.
335
+
336
+ ### What happens to an answer
337
+
338
+ What the app does with an answer is up to the app. An agent's app normally hands each answer to its model, and for most agents the model runs on the vendor's servers, so the names of your sessions, their folders and their branches can leave this computer that way, under that app's own settings and terms. Add the server only to an app you would show your session names to, and remove it from the app to stop it: for Claude Code, `claude mcp remove agent-lookout`.
339
+
340
+ ### What is kept
341
+
342
+ Nothing. Each answer is worked out from one reading of the list and is gone once it has been sent. The server stops when the app closes it.
343
+
344
+ ## Storage
345
+
346
+ Agent Lookout stores no session data on disk, and its own code writes no files. The latest session list, the last 1,000 events and the last six hours of history are held in memory and are gone when Agent Lookout stops. What a waiting session is asking is in the latest session list only while it waits, and in no event or point of history. So are the tmux panes and terminal tabs it last found, the branches and repositories it last read, and what the dashboard pages last said about notifications.
347
+
348
+ With notifications on, each notification holds a session's name, and for a wait what the session is asking, and the operating system keeps it in its notification list, as does the browser for one it made. [Notifications](#notifications) says what it holds and how long it stays. With email set up, each email holds a session's name and its folder's name, and the mail server and the mailbox keep it, as [Email](#email) says. With a webhook set up, each post holds the same, and the service it went to keeps it, as [Webhook](#webhook) says.
349
+
350
+ The tools that run it write files of their own. None of these holds session data.
351
+
352
+ - npm keeps a short log of each command it runs in `~/.npm/_logs/`. The log names the command and the project folder. It does not hold the program's output.
353
+ - In a clone, `npm start` and the `agent-lookout` command run through tsx, which keeps compiled copies of Agent Lookout's own source files in the system's temporary directory, in a folder named `tsx-` followed by your user ID. Installed from npm, the command is plain JavaScript and runs without tsx.
354
+ - `npx agent-lookout` keeps the package it downloads in npm's cache, in `~/.npm/_npx/`.
355
+ - `npm run dev` runs through Vite, which keeps pre-bundled copies of the dependencies in `node_modules/.vite/`.
356
+ - `npm run build` writes the built dashboard to `dist/` and the type checker's records to `node_modules/.tmp/`. `npm run build:package` also writes the bundled command to `dist/cli/`.
357
+
358
+ The dashboard saves six values in your browser's local storage. Your theme choice is under the key `agent-lookout-theme`. Whether notifications are on is under the key `agent-lookout-notifications`, as `on` or `off`, and is written only when you turn them on or off. The events that send one are under the key `agent-lookout-notification-events`, as their names separated by commas, such as `needs-you,finished`, and are written only when you switch one. The apps the page has said macOS will ask about, when you first jumped to a tab of one, are under the key `agent-lookout-automation-note`, as `Terminal`, `iTerm2` or both separated by a comma. The last time the Events log was on screen is under the key `agent-lookout-last-looked`, as a number of milliseconds since 1970, and is written when the page goes out of sight or is closed. It is what the line in the Events log that marks where you left off is drawn from, and while that line still shows, the time kept is the line's own. It holds nothing about any session or event. Whether the Sessions card shows the list, the list grouped by repository or the board is under the key `agent-lookout-sessions-layout`, as `list`, `repositories` or `board`, and is written only when you choose one. All six belong to one browser at one address.
359
+
360
+ ## What is on screen
361
+
362
+ Session names, folder paths, repository names, branch names and what a waiting session is asking, such as a command or a file, can show what you are working on. Check a screenshot before you share it.
363
+
364
+ With notifications on, a session's name, and what a waiting session is asking, also appear in a system notification, outside the dashboard: over other apps, in Notification Centre and, depending on your system's settings, on the lock screen and while you mirror, share or record the screen. One the dashboard page made for a wait stays there until the session stops waiting or you clear it, and one that says Finished, Failed or Ended stays until you clear it. One the server showed stays until you clear it. To keep names off those, open Notifications in System Settings on macOS and change what your browser's notifications may show, which does not cover the ones the server shows, or leave notifications off. An email shows the same name, and the folder's, wherever that mailbox is read, including the notifications a phone shows for it. A webhook post shows them to everyone who can read the channel it goes to, and in the notifications their apps show for it.
365
+
366
+ ## Changes
367
+
368
+ This file lists every command the app runs and every file it reads outside its own folder. A change that adds to either list, stores anything on disk or talks to a network updates this file in the same pull request.