agent-lookout 0.2.6 → 0.2.8
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/CHANGELOG.md +34 -0
- package/PRIVACY.md +151 -26
- package/README.md +190 -160
- package/SECURITY.md +24 -9
- package/dist/assets/{HistoryPanel-Cwmn7YtJ.js → HistoryPanel-CQ7zovsM.js} +1 -1
- package/dist/assets/{SessionPanel-BJ5t_CPi.js → SessionPanel-DS29Z04P.js} +1 -1
- package/dist/assets/index-BWFkRW4g.js +3 -0
- package/dist/assets/motion-DlYOyClL.js +9 -0
- package/dist/cli/agent-lookout.js +2 -2
- package/dist/cli/{mcpServer-HJSH3WIS.js → mcpServer-BMSF2TXC.js} +2 -2
- package/dist/cli/{startCommand-PR5JPWCT.js → startCommand-ANC7NVAO.js} +2496 -474
- package/dist/index.html +2 -2
- package/docs/API.md +102 -46
- package/docs/GUIDE.md +250 -147
- package/docs/INSTALL.md +177 -0
- package/package.json +6 -1
- package/dist/assets/index-QSfCI-L_.js +0 -3
- package/dist/assets/motion-BAhdRuCw.js +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
Changes that a user of Agent Lookout would notice, newest first.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.2.8 - 2026-10-07
|
|
8
|
+
|
|
9
|
+
Allow or Deny a Claude Code prompt from the Mac app's notification and menu bar, pushes to your phone through ntfy or Pushover, and Antigravity CLI sessions, which need you while agy asks you to approve a tool.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Allow or Deny a Claude Code permission prompt from the Mac app's own notification and from its menu bar, with the plugin. While Agent Lookout holds a session's request, the notification of its wait, or of a reminder of it, has Deny, and Allow too when the whole request fits in it as written: one shell command with no other input, on one line of at most 40 characters, which is then the notification's whole text. In the menu bar, the session opens a submenu with Open Details, every line of what it asks, any other input after a separator, Deny and, when each line is drawn as written, Allow. Anything else, an edit, a plan or a question among them, offers Deny alone with a line saying why. A press goes through the same checks as a press on the dashboard, is kept in the Events log as an answer from Agent Lookout, and is never taken in the first second the request is shown, or while the Mac is locked; one that sent nothing says why and what to do, in a notification or in the menu. With the window open the page shows its own notifications, which have no buttons, and nothing changes in a browser or with `npx agent-lookout`. [In the Mac app](docs/GUIDE.md#in-the-mac-app) has the details, and [PRIVACY.md](PRIVACY.md#in-the-mac-app) says what a notification and the menu now show, and how to keep them off the lock screen.
|
|
14
|
+
- Pushes to your phone, through ntfy or Pushover, each off until you set it up. Set `AGENT_LOOKOUT_NTFY_URL` to the address of an ntfy topic, on ntfy.sh or a server of your own, with an access token in `AGENT_LOOKOUT_NTFY_TOKEN` if you like, or set `AGENT_LOOKOUT_PUSHOVER_TOKEN` and `AGENT_LOOKOUT_PUSHOVER_USER` to a Pushover application's token and your user key. Each pushes what the webhook posts and no more: a wait once it has lasted the delay, its reminder and each repeat, the summary when quiet hours end, and, if you choose them, sessions that finish, fail or end, by the same rules, so quiet hours, reminders and waits answered from Agent Lookout hold for pushes as for every channel. A push names the session and what happened, and under that how long it waited, its folder's name, its app and its agent. What a waiting session is asking goes only with `AGENT_LOOKOUT_NTFY_ASKING=on` or `AGENT_LOOKOUT_PUSHOVER_ASKING=on`, and each has its own `_EVENTS` and `_AFTER`, as email and the webhook do. A wait, its reminders and the test go at a higher priority than the rest. The certificate is always checked, nothing is tried twice, at most 20 an hour go each way, and the topic, the token and the key are never shown, printed or kept. [ntfy](docs/GUIDE.md#ntfy) and [Pushover](docs/GUIDE.md#pushover) in the guide have the setup, and [PRIVACY.md](PRIVACY.md#ntfy) what a push holds and where it goes.
|
|
15
|
+
- An ntfy card and a Pushover card in Settings say whether each is set up, what it sends, whether an ntfy topic has an access token, and how the last push went, with Send a test, which sends one push that says only that it is a test. `GET /api/ntfy` and `GET /api/pushover` say the same, and `POST /api/phone/test`, with `X-Agent-Lookout-Action: phone-test`, sends the test: the eighth route that acts.
|
|
16
|
+
- Antigravity CLI sessions appear with no setup. Agent Lookout reads the transcripts `agy` keeps in `~/.gemini/antigravity-cli`, keeping only each conversation's last step, and asks `ps` which `agy` programs run, to show each conversation as working, idle, failed or finished. A session needs you while agy asks you to approve a tool, which the log each running agy program keeps says, and Agent Lookout reads only those lines of it, the folder the program works in and the conversation it has open. A session is named by the title agy gives the conversation, else its folder, else its conversation ID, and has the branch of its folder. A question agy asks you still shows as working, so watch its Quiet for line. `AGENT_LOOKOUT_ANTIGRAVITY_HOME` names another folder to read. This is built from agy 1.3.1's documentation and program, and checked against one conversation: [docs/adapters/antigravity.md](docs/adapters/antigravity.md) says what is still unverified. The Antigravity desktop app's conversations do not appear.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Clicking the Mac app's own notification of a wait, or of a reminder of one, now opens the session's details, not just the window.
|
|
21
|
+
- The README, [docs/INSTALL.md](docs/INSTALL.md) and [Desktop app](docs/GUIDE.md#desktop-app) in the guide say which macOS the Mac app needs: macOS 13 Ventura or later, the oldest its Electron runs on. On macOS 11 or 12, `npx agent-lookout` still runs.
|
|
22
|
+
- The dashboard's first load is about 15 KB smaller, gzipped, with `npx agent-lookout` and in the Mac app alike: it takes only the part of Motion that fades the Overview in and out, and leaves out its gestures, layout animations and dragging. The fade is as it was.
|
|
23
|
+
|
|
24
|
+
## 0.2.7 - 2026-10-07
|
|
25
|
+
|
|
26
|
+
Remind again every few minutes while a session keeps waiting, and read a README rewritten for people and coding agents.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Remind again while it waits, under Remind me of a long wait in Time rules. Turn it on, and a session that still waits is reminded of again each time the minutes you set pass, 30 unless you change them, from 5 to 1,440: with a reminder after 10 minutes and again every 30, at 10 minutes, 40 minutes, 1 hour 10 minutes and so on. Each goes by the same channels as the first, as a notification, an email and a webhook post, and says how long the session has waited by then, as in "checkout-flow has waited 40 minutes for permission". None comes once the wait is answered or the session ends. Quiet hours hold them like any notice: a wait still open when they end is told of once, and one answered in them is one item of the summary, however many came due. After the computer has slept, one comes, not one for each interval missed. It is off until you turn it on. [Time rules](docs/GUIDE.md#time-rules) has the details.
|
|
31
|
+
- A webhook post for a repeat has `repeat` beside `reminder: true`. It counts the intervals since the first reminder's time, so after a sleep it can skip numbers. `longWait` in `GET /api/settings`, in `POST /api/settings/time-rules` and in each snapshot's `timeRules` can have `repeat`, as `{ "on", "minutes" }`. A settings file without it reads as before, with no repeat.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- The README is rewritten for people and for coding agents, with install three ways and a prompt to hand an agent, and a new install guide, [docs/INSTALL.md](docs/INSTALL.md), takes the details.
|
|
36
|
+
- About sources, in Sources, now names every way Agent Lookout can send something off this computer: email, the webhook, pull requests through your own `gh`, other machines through your own `ssh`, and the Mac app's daily check for a newer version.
|
|
37
|
+
- A long wait reminder that falls due in quiet hours, for a session told of before they began, now holds its wait as any notice is held: if the wait is answered before they end, it is an item of the summary. Before, it was left out.
|
|
38
|
+
|
|
5
39
|
## 0.2.6 - 2026-10-07
|
|
6
40
|
|
|
7
41
|
Set rules that answer Claude Code's permission prompts for you, run Agent Lookout on Windows, and see a prompt you answered leave Needs you at once.
|
package/PRIVACY.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Privacy
|
|
2
2
|
|
|
3
|
-
Agent Lookout runs on your machine and reads a small amount of metadata about your Claude Code and
|
|
3
|
+
Agent Lookout runs on your machine and reads a small amount of metadata about your Claude Code, Codex and Antigravity CLI 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. Pushes to your phone, through ntfy or Pushover, are off unless you set one up. Once you do, it sends the same notices as short pushes through that service, to the one topic or the one user you name, and nowhere else. [ntfy](#ntfy) and [Pushover](#pushover) say what one holds. The Mac app, and only the Mac app, asks GitHub about once a day whether a newer version of it has been released, unless you turn that off in Settings. It sends nothing about your sessions: [Updates (Mac app only)](#updates-mac-app-only) says what it sends. The npm package, `npx agent-lookout` and the repository never check for anything. Pull requests are off unless you start Agent Lookout with `AGENT_LOOKOUT_PULL_REQUESTS=on`. Then it runs your own GitHub CLI, `gh`, which sends GitHub the owner and name of a session's repository and the name of its branch, with `gh`'s own login, which Agent Lookout never reads: [`gh`](#gh) says what it sends and when. Other machines are read only once you name them in `AGENT_LOOKOUT_REMOTES`. Then it runs your own `ssh` to each and asks the Agent Lookout running there for its sessions, and sends nothing of this computer's: [Another machine over SSH](#another-machine-over-ssh) says what is read. 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
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, and keeps nothing of it once the wait ends. It sends that line off the machine only if you turn it on for email, with `AGENT_LOOKOUT_EMAIL_ASKING=on`,
|
|
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, and keeps nothing of it once the wait ends. It sends that line off the machine only if you turn it on for email, with `AGENT_LOOKOUT_EMAIL_ASKING=on`, for the webhook, with `AGENT_LOOKOUT_WEBHOOK_ASKING=on`, or for a push, with `AGENT_LOOKOUT_NTFY_ASKING=on` or `AGENT_LOOKOUT_PUSHOVER_ASKING=on`, and then only in the email, post or push for that wait and its reminders. It also goes to another computer that names this one in `AGENT_LOOKOUT_REMOTES` and reads its sessions over SSH. That computer shows it, and its own email, webhook and push settings decide whether it is sent on, as [Another machine over SSH](#another-machine-over-ssh) says. `AGENT_LOOKOUT_WAITING_TEXT=off` on this computer keeps it from there too. No 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
6
|
|
|
7
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
8
|
|
|
9
|
+
For the Antigravity CLI, `agy`, Agent Lookout opens the transcripts agy keeps, which hold the whole conversation. It reads the end of each to find the conversation's last step, and keeps only that step's kind, its status, whether it asks for a tool and its time. It keeps no prompt, reply, command or output. It also reads the command lines of the `agy` programs that are running, which can hold a prompt given with `-p`, only to tell a session from one of agy's own commands and to find the conversation one names, and keeps nothing else of them. It reads the log each running agy program keeps only for the lines that say which folder it works in, which conversation it has open and whether it waits for your approval of a tool, and keeps nothing else of it, and it reads the short title agy gives each conversation. [The Antigravity CLI's files](#the-antigravity-clis-files) says exactly what is read.
|
|
10
|
+
|
|
9
11
|
Agent Lookout stops a Claude Code session only when you press Stop in its details and confirm, or end the sessions left running after a confirmation that lists each one. It answers a Claude Code permission prompt only when you press Allow or Deny, or when a [permission rule](#permission-rules) you added in Settings decides it, and only with its Claude Code plugin installed. It never acts on a session on its own. [Stopping a session](#stopping-a-session) and [The Claude Code plugin and permission prompts](#the-claude-code-plugin-and-permission-prompts) say what it checks first and what it runs.
|
|
10
12
|
|
|
11
13
|
## What it reads and runs
|
|
@@ -31,7 +33,7 @@ From those bytes it takes the last tool the session asked to use that has no ans
|
|
|
31
33
|
|
|
32
34
|
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.
|
|
33
35
|
|
|
34
|
-
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, in the Mac app is in the menu that its icon in the menu bar opens, 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 answer of `agent-lookout mcp`. It is put in an email
|
|
36
|
+
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, in the Mac app is in the menu that its icon in the menu bar opens, 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 answer of `agent-lookout mcp`. It is put in an email, a webhook post or a push only when you turn that on, as [Email](#email), [Webhook](#webhook), [ntfy](#ntfy) and [Pushover](#pushover) say. It also goes to another computer that names this one in `AGENT_LOOKOUT_REMOTES` and reads its sessions over SSH, which shows it, and whose own email, webhook and push settings decide whether it is sent on, as [Another machine over SSH](#another-machine-over-ssh) says. Set `AGENT_LOOKOUT_WAITING_TEXT=off` and no transcript is opened at all, so there is no line to send either.
|
|
35
37
|
|
|
36
38
|
### `claude agents --json --all`
|
|
37
39
|
|
|
@@ -129,6 +131,26 @@ From the `session_meta` line it keeps `id`, `timestamp`, `cwd`, `source`, `threa
|
|
|
129
131
|
|
|
130
132
|
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.
|
|
131
133
|
|
|
134
|
+
### The Antigravity CLI's files
|
|
135
|
+
|
|
136
|
+
Agent Lookout reads the Antigravity CLI's folder: the one `AGENT_LOOKOUT_ANTIGRAVITY_HOME` names, otherwise `~/.gemini/antigravity-cli`. It runs no agy program, changes no agy setting and installs no hook or status line. agy documents the transcript's place and format for its own agents and hooks, not for other programs, and the rest of what is listed here is not documented. [docs/adapters/antigravity.md](docs/adapters/antigravity.md) records where each comes from. It was written from agy 1.3.1's documentation and program, and checked against one conversation run with it.
|
|
137
|
+
|
|
138
|
+
When the folder is not there, Agent Lookout looks for it again once a minute, or on every poll when `AGENT_LOOKOUT_ANTIGRAVITY_HOME` names it, and reads nothing else for the Antigravity CLI and runs no `ps` for it.
|
|
139
|
+
|
|
140
|
+
When the folder is there, every 2 seconds Agent Lookout:
|
|
141
|
+
|
|
142
|
+
- lists `brain/`, where agy keeps one folder for each conversation, named by the conversation's ID, and `conversations/`, where it keeps each conversation's SQLite database, `<conversation ID>.db`, and its write-ahead log, `<conversation ID>.db-wal`. Listing reads names only.
|
|
143
|
+
- looks up, without opening them, when three files of each conversation were last changed: its transcript, `brain/<conversation ID>/.system_generated/logs/transcript.jsonl`, its database and its database's log. For a conversation not changed for a day, which no agy program could have open, it looks them up again only every 30 seconds. The newest of the three times is passed to the dashboard as the time agy last wrote to the session, to say how long a working session has been quiet.
|
|
144
|
+
- opens, read-only, the transcript of each conversation whose transcript changed in the last day or that an agy program could have open. It reads up to the first 256 KiB to find the end of its first line, the first 4 KiB again to notice when agy has rewritten the file, as it does when it compacts a conversation, and up to the last 2 MiB to find the last steps. After that it reads only what agy has added, and only when the file's size or modified time has changed.
|
|
145
|
+
- looks up, for each conversation it shows, `annotations/<conversation ID>.pbtxt`, a file of a few bytes in which agy keeps the conversation's title, and opens it, read-only, when it has changed and is an ordinary file of at most 4 KiB. It keeps the `title` and nothing else. The title becomes the session's name.
|
|
146
|
+
- while `ps` shows an agy program running, lists `log/`, where each agy program writes a log of its own, named for the time it started, `cli-<date>_<time>.log`, and opens, read-only, the log of each agy program that runs a session, when only one such program started in the 10 seconds before the time its name gives, or up to 2 seconds after it. It lists `log/` again every 10 seconds. It reads at most the last 4 MiB of it, and after that only what agy has added. Of each line it keeps what four kinds of line say and drops the line at once: the folder the program works in, from the first line that starts it; the ID of each conversation it opens; that it asks for your approval of a tool, with the tool's name as agy writes it, such as `RunCommand`, the step and the time; and that such an approval was answered. The log also holds the addresses agy contacts, errors and what it is doing, which are never kept. The folder becomes the session's folder, which is looked in for a git repository as every session's is, and the approval makes the session need you until the transcript holds the step that waited, or the log says it was answered.
|
|
147
|
+
|
|
148
|
+
Each line of a transcript is one step. Agent Lookout parses each line it reads and keeps four of its fields, `type`, `status`, `step_index` and `created_at`, and whether its `tool_calls` list is empty, and drops the rest at once: `content`, which holds your prompts, the agent's replies and what tools returned, `thinking`, `media`, `source`, the tool calls and their arguments. Of the steps it keeps, it holds only the last that says how the conversation stands, when the steps before it that say the same began, when the last step of any kind was made, the highest `step_index`, and when the first step was made. The bytes it reads are held in memory only while the file is read.
|
|
149
|
+
|
|
150
|
+
It never opens a conversation's database or its log, `conversation_summaries.db`, `transcript_full.jsonl`, `cli.log`, the log of an agy program `ps` no longer shows or that it cannot tell apart, or anything else in the folder. `ps` is asked at most every 10 seconds, so the log of a program that has just ended can be read once more before it is. A transcript that is a link, a named pipe or a device is never opened. The folder and the folders inside it are followed when they are links.
|
|
151
|
+
|
|
152
|
+
To tell a conversation agy has open from one that has ended, while there is a conversation to show, Agent Lookout runs `ps -A -o pid=,ppid=,lstart=,comm=`, in UTC, at most every 10 seconds, and sooner, though not within 2 seconds of the last run, when a conversation is written that no agy program it knows of could have open. It prints four things about every process on this machine: its ID, its parent's ID, when it started and the name its program gives itself, and nothing else. Agent Lookout keeps only the processes whose program is named `agy`, and drops the rest as it reads them. When it finds an agy program it has not seen before, it runs `ps -o pid=,args= -p <their IDs>`, which prints the command lines of those programs and of no other. A command line can hold a prompt, given with `-p`. Agent Lookout reads each only to tell whether the program is one of agy's own commands, such as `agy remote-control`, which is not a session, and which conversation it names with `--conversation`, keeps only those two things for as long as the program runs, and drops the line at once. It keeps when each agy program started, by its process ID. `ps` is run directly from `/usr/bin` or `/bin`, never through a shell, with a 2 second timeout, and never on Windows, where no Antigravity CLI conversation is shown as finished.
|
|
153
|
+
|
|
132
154
|
### Status files
|
|
133
155
|
|
|
134
156
|
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.
|
|
@@ -178,7 +200,7 @@ where `<branch>` is the branch's name, or `<owner>:<branch>` for a branch pushed
|
|
|
178
200
|
|
|
179
201
|
From the answer it keeps the pull request's number, its title, whether it is open, a draft, merged or closed, and how many of its checks are failing, pending and passing. It keeps no check's name, link or log, no body, comment, author or review. The title is cleaned of control characters and of the characters that change the direction of text, and cut to 200 characters. The link to the pull request is made from the repository and the number, not taken from GitHub's answer. All of it is kept in memory only, for as long as a session is on that branch, and is never written to disk, to the history or to the Events log. When `gh` is not there, is not signed in, or does not answer, nothing of what it printed is kept or shown: Settings says which, in words of Agent Lookout's own.
|
|
180
202
|
|
|
181
|
-
The pull request is in the session list the API serves on this computer, and so on the dashboard, and in answers of `agent-lookout mcp` and `agent-lookout status --json` as its number and how its checks stand, never its title, as [The MCP server](#the-mcp-server) says. No email, webhook post or notification holds it.
|
|
203
|
+
The pull request is in the session list the API serves on this computer, and so on the dashboard, and in answers of `agent-lookout mcp` and `agent-lookout status --json` as its number and how its checks stand, never its title, as [The MCP server](#the-mcp-server) says. No email, webhook post, push or notification holds it.
|
|
182
204
|
|
|
183
205
|
### `osascript`
|
|
184
206
|
|
|
@@ -198,9 +220,13 @@ The plugin is one hook, a short `sh` script, which Claude Code runs as it is abo
|
|
|
198
220
|
|
|
199
221
|
Agent Lookout listens on that socket while it runs, unless you start it with `AGENT_LOOKOUT_ANSWER=off`, or with `AGENT_LOOKOUT_CLAUDE_HOME` set and `AGENT_LOOKOUT_ANSWER_SOCKET` not. It makes the folder with mode 700 if it is not there, and gives its own folder, `~/.agent-lookout`, mode 700 if others could open it. A folder that `AGENT_LOOKOUT_ANSWER_SOCKET` names, which it did not make, it never changes: if others can open it, answering is off, and Settings says why. It gives the socket mode 600, so only your user can connect. A browser cannot reach a socket. It removes the socket when it stops, and one left by a copy that crashed is replaced. A second copy that finds the first answering there leaves it be, and its Settings say so.
|
|
200
222
|
|
|
201
|
-
While the session waits, Agent Lookout holds the request. It reads the session's registry file, `~/.claude/sessions/<pid>.json`, again every half second, as an ordinary file it opens without following a link, and lets the request go, answering nothing, as soon as the file no longer says the session is waiting for permission, or says it is waiting again in a later wait, which is what happens when you answer in the session. It also lets it go when the hook's connection closes, when a newer request of the same session arrives, and after `AGENT_LOOKOUT_ANSWER_WAIT` seconds, 300 unless you set it. The prompt in the session then decides, as it would without the plugin. What it shows of the request, the whole command or the tool and its inputs, is held in memory while the request is held. It goes to the dashboard in the session list, and nowhere else: it is never written to disk and never put in an event, the history,
|
|
223
|
+
While the session waits, Agent Lookout holds the request. It reads the session's registry file, `~/.claude/sessions/<pid>.json`, again every half second, as an ordinary file it opens without following a link, and lets the request go, answering nothing, as soon as the file no longer says the session is waiting for permission, or says it is waiting again in a later wait, which is what happens when you answer in the session. It also lets it go when the hook's connection closes, when a newer request of the same session arrives, and after `AGENT_LOOKOUT_ANSWER_WAIT` seconds, 300 unless you set it. The prompt in the session then decides, as it would without the plugin. What it shows of the request, the whole command or the tool and its inputs, is held in memory while the request is held. It goes to the dashboard in the session list, and, in the Mac app, to the app's own notification of the wait and to its menu bar, and nowhere else: it is never written to disk and never put in an event, the history, the page's notifications, an email, a webhook post, a push, `agent-lookout status` or an answer of `agent-lookout mcp`. The browser and `npx agent-lookout` show it on the dashboard alone.
|
|
224
|
+
|
|
225
|
+
It answers when you press Allow or Deny on the dashboard, or in the Mac app on its notification of the wait or in its menu bar, or when a [permission rule](#permission-rules) you added decides. The page sends the session's ID, Agent Lookout's own ID for the request, and the answer, and the Mac app hands the same three to the same answer in its own process, with no request at all. Agent Lookout reads the registry file once more, and answers only if it still says the session is waiting for permission, in the same wait the request was shown in. The answer is one of two fixed texts, allow, or deny with a fixed sentence that says you denied it from Agent Lookout. It never changes the command or the tool's input, and never saves a permission rule, so each Allow allows that one request. Allow is offered only when the whole of what it allows is shown, and not for an edit or a new file, a plan or a question, a request too long to show whole or with more than two blank lines in a row, one with characters that cannot be shown as they are, or a command with right-to-left letters. The Events log keeps one event for each answer, with the session's ID and name, and whether it was allowed or denied, and nothing of what was asked. Claude Code's registry file can say the session waits for a second or two after the answer, so Agent Lookout keeps the session's ID, when that wait began and when it answered, in memory only, until the file says the session has moved on or for 10 seconds at most, and takes the wait as over from the answer.
|
|
226
|
+
|
|
227
|
+
#### In the Mac app
|
|
202
228
|
|
|
203
|
-
|
|
229
|
+
The Mac app's own notification of a wait, or of a reminder of one, which it shows while its window is closed, holds what Agent Lookout shows of a held request: for one short shell command on one line, the whole command as its text, under "Asks to run", with Deny and Allow, and for anything else the start of it, its first line, cut, with Deny. The menu bar's menu holds the same in a session's submenu, every line of the command and each other input, cut only when Allow is not offered. macOS keeps a notification in Notification Centre, and can show it on the lock screen, until you clear it or the app takes it down, which it does once a button on it is pressed, or once the request has been answered or let go. Before a press is answered, the app asks macOS whether the screen is locked, with `powerMonitor`, which reads it from macOS and sends nothing anywhere, and answers nothing while it is. [What is on screen](#what-is-on-screen) says how to keep these off the lock screen.
|
|
204
230
|
|
|
205
231
|
#### Permission rules
|
|
206
232
|
|
|
@@ -218,11 +244,11 @@ For each machine it runs `ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes -o
|
|
|
218
244
|
|
|
219
245
|
Through that port, every 2 seconds, it sends two requests and no others: `GET /api/health`, whose answer is the version of Agent Lookout there, and `GET /api/sessions`, whose answer is that machine's sessions and sources, as [docs/API.md](docs/API.md#get-apisessions) describes. The requests carry nothing of this computer's: no session, no setting, and not the `X-Agent-Lookout-Notifications` header, so they change nothing that machine's Agent Lookout believes about its own pages. Of the answer it keeps, in memory, what this computer shows of its own sessions: each session's name, folder, status, reason, the vendor's wording of a wait, its times, its app, its branch and repository name, its agent's name and whether its process still runs. It drops each session's process ID, Jump, Stop and links, and any permission request that machine holds for it, with what the request would run, as the answer is read, so nothing about a session there can act on this computer. It keeps each source there by its name and its state, and what each of its agents can report. It does not take that machine's time rules, or its word on whether it is in quiet hours: this computer's own decide when a session there is stale, and when a wait there is reminded of or held back here.
|
|
220
246
|
|
|
221
|
-
That includes what a waiting session there is asking, such as `Run: npm test`, when that machine's Agent Lookout sends it, which it does unless `AGENT_LOOKOUT_WAITING_TEXT=off` is set there. With `AGENT_LOOKOUT_WAITING_TEXT=off` set on this computer, it is dropped as it arrives, and a waiting session there shows its reason alone, as one here does. Otherwise it is shown here as a waiting session's is, and in the notifications on this computer, and is kept no longer than the wait, as here. Like a session's on this computer, a session there and its events go into the history kept on this computer, without what it is asking, and into an email
|
|
247
|
+
That includes what a waiting session there is asking, such as `Run: npm test`, when that machine's Agent Lookout sends it, which it does unless `AGENT_LOOKOUT_WAITING_TEXT=off` is set there. With `AGENT_LOOKOUT_WAITING_TEXT=off` set on this computer, it is dropped as it arrives, and a waiting session there shows its reason alone, as one here does. Otherwise it is shown here as a waiting session's is, and in the notifications on this computer, and is kept no longer than the wait, as here. Like a session's on this computer, a session there and its events go into the history kept on this computer, without what it is asking, and into an email, a webhook post or a push as the settings for those say.
|
|
222
248
|
|
|
223
249
|
### Settings
|
|
224
250
|
|
|
225
|
-
It reads
|
|
251
|
+
It reads forty-three 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_ANTIGRAVITY_HOME`, `AGENT_LOOKOUT_STATUS_DIR`, `AGENT_LOOKOUT_HISTORY`, `AGENT_LOOKOUT_HISTORY_DIR`, `AGENT_LOOKOUT_SETTINGS_FILE`, `AGENT_LOOKOUT_NOTIFICATIONS`, `AGENT_LOOKOUT_TMUX`, `AGENT_LOOKOUT_TERMINAL_JUMP`, `AGENT_LOOKOUT_STOP`, `AGENT_LOOKOUT_PULL_REQUESTS`, `AGENT_LOOKOUT_ANSWER`, `AGENT_LOOKOUT_ANSWER_SOCKET`, `AGENT_LOOKOUT_ANSWER_WAIT`, `AGENT_LOOKOUT_PORT`, `AGENT_LOOKOUT_HOST`, `AGENT_LOOKOUT_REMOTES`, `AGENT_LOOKOUT_SSH_BIN`, Codex's own `CODEX_HOME`, the six email settings, `AGENT_LOOKOUT_EMAIL_TO`, `AGENT_LOOKOUT_SMTP_URL`, `AGENT_LOOKOUT_EMAIL_FROM`, `AGENT_LOOKOUT_EMAIL_AFTER`, `AGENT_LOOKOUT_EMAIL_EVENTS` and `AGENT_LOOKOUT_EMAIL_ASKING`, the four webhook settings, `AGENT_LOOKOUT_WEBHOOK_URL`, `AGENT_LOOKOUT_WEBHOOK_EVENTS`, `AGENT_LOOKOUT_WEBHOOK_AFTER` and `AGENT_LOOKOUT_WEBHOOK_ASKING`, the five ntfy settings, `AGENT_LOOKOUT_NTFY_URL`, `AGENT_LOOKOUT_NTFY_TOKEN`, `AGENT_LOOKOUT_NTFY_EVENTS`, `AGENT_LOOKOUT_NTFY_AFTER` and `AGENT_LOOKOUT_NTFY_ASKING`, and the five Pushover settings, `AGENT_LOOKOUT_PUSHOVER_TOKEN`, `AGENT_LOOKOUT_PUSHOVER_USER`, `AGENT_LOOKOUT_PUSHOVER_EVENTS`, `AGENT_LOOKOUT_PUSHOVER_AFTER` and `AGENT_LOOKOUT_PUSHOVER_ASKING`. 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, and `AGENT_LOOKOUT_ANTIGRAVITY_HOME` replaces `~/.gemini/antigravity-cli`. [Email](#email) says how the email settings are kept, [Webhook](#webhook) how the webhook's are, and [ntfy](#ntfy) and [Pushover](#pushover) how theirs are. The time rules and the permission rules are not in the environment: Agent Lookout keeps them in a file of its own, as [The settings file](#the-settings-file) says. The [guide](docs/GUIDE.md#settings-you-can-change) says what each setting does.
|
|
226
252
|
|
|
227
253
|
## What it never reads
|
|
228
254
|
|
|
@@ -230,16 +256,17 @@ It reads thirty-two settings from the environment: `AGENT_LOOKOUT_CLAUDE_HOME`,
|
|
|
230
256
|
- 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.
|
|
231
257
|
- Claude Code's settings, credentials, history and memory files.
|
|
232
258
|
- 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`.
|
|
259
|
+
- In `~/.gemini`: `oauth_creds.json`, `google_accounts.json`, `settings.json`, `config/hooks.json`, `history/` and any `history.jsonl`, and in the Antigravity CLI's folder its SQLite files, `conversation_summaries.db` and `conversations/*.db` with their `-wal` and `-shm` files, which are only looked up for when they were last changed, `transcript_full.jsonl`, `cli.log` and the logs in `log/` of agy programs `ps` does not show, `jetski_state.pbtxt`, `jetbox_summaries_proto.pb`, `installation_id`, `updater/`, `presence/`, any file in `annotations/` but a conversation's `.pbtxt`, `implicit/`, `crashes/` and the other files in each conversation's folder. Nor the Antigravity desktop app's folder, `~/.gemini/antigravity`, or the Antigravity IDE's.
|
|
233
260
|
- In a git repository, anything but the `.git` file, `HEAD` and a worktree's `commondir` described under [Git repositories](#git-repositories), and with `AGENT_LOOKOUT_PULL_REQUESTS=on`, the `config` and the remote's `HEAD` described under [`gh`](#gh).
|
|
234
261
|
- `gh`'s configuration, its login and any token, and your global git configuration.
|
|
235
262
|
- Your `~/.ssh` folder: keys, `known_hosts` and ssh config. For the machines `AGENT_LOOKOUT_REMOTES` names, `ssh` reads them itself, as it does in a terminal.
|
|
236
263
|
- The files of any other application.
|
|
237
264
|
|
|
238
|
-
It runs no program but the `claude` binary, for `claude agents` and, only when you stop a background job, `claude stop`, `ps`, `tmux`, on macOS `osascript`, to show a notification and to bring a tab of Terminal or iTerm2 forward, only for `--open`, `open`, `xdg-open` or `rundll32.exe`, only with `AGENT_LOOKOUT_PULL_REQUESTS=on`, `gh`, and, only for the machines `AGENT_LOOKOUT_REMOTES` names, `ssh`, as [Another machine over SSH](#another-machine-over-ssh) describes. The Mac app also runs `ditto` and `plutil` to unpack and check a newer version of itself, and, only when you press Install and Restart, a short `sh` script that puts it in place, as [Updates (Mac app only)](#updates-mac-app-only) describes. It runs no git command. The plugin's hook, which Claude Code runs and not Agent Lookout, runs `curl`. 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. It changes three things outside itself, and each only when you ask: which tmux pane, window and session are selected, or which tab of Terminal or iTerm2 is in front, when you press Jump, when you press Stop and confirm, whether a Claude Code session's process runs, as [Stopping a session](#stopping-a-session) describes, and, when you press Allow or Deny, or a [permission rule](#permission-rules) you added decides it, the answer to that one permission prompt, as [The Claude Code plugin and permission prompts](#the-claude-code-plugin-and-permission-prompts) describes. It never sends a session anything else.
|
|
265
|
+
It runs no program but the `claude` binary, for `claude agents` and, only when you stop a background job, `claude stop`, `ps`, `tmux`, on macOS `osascript`, to show a notification and to bring a tab of Terminal or iTerm2 forward, only for `--open`, `open`, `xdg-open` or `rundll32.exe`, only with `AGENT_LOOKOUT_PULL_REQUESTS=on`, `gh`, and, only for the machines `AGENT_LOOKOUT_REMOTES` names, `ssh`, as [Another machine over SSH](#another-machine-over-ssh) describes. The Mac app also runs `ditto` and `plutil` to unpack and check a newer version of itself, and, only when you press Install and Restart, a short `sh` script that puts it in place, as [Updates (Mac app only)](#updates-mac-app-only) describes. It runs no git command. The plugin's hook, which Claude Code runs and not Agent Lookout, runs `curl`. It never writes to `~/.claude`, to the Codex folder, to `~/.gemini`, 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. It changes three things outside itself, and each only when you ask: which tmux pane, window and session are selected, or which tab of Terminal or iTerm2 is in front, when you press Jump, when you press Stop and confirm, whether a Claude Code session's process runs, as [Stopping a session](#stopping-a-session) describes, and, when you press Allow or Deny, or a [permission rule](#permission-rules) you added decides it, the answer to that one permission prompt, as [The Claude Code plugin and permission prompts](#the-claude-code-plugin-and-permission-prompts) describes. It never sends a session anything else.
|
|
239
266
|
|
|
240
267
|
## Network
|
|
241
268
|
|
|
242
|
-
With email, the webhook and other machines 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. With `AGENT_LOOKOUT_PULL_REQUESTS=on`, it runs `gh`, which connects to GitHub, at most once every 2 minutes for each repository and branch your sessions are on, as [`gh`](#gh) describes. With other machines named in `AGENT_LOOKOUT_REMOTES`, your own `ssh` connects to each, and Agent Lookout sends its two requests to Agent Lookout there through that connection, as [Another machine over SSH](#another-machine-over-ssh) describes. Agent Lookout itself connects only to the end of the tunnel on this computer's `127.0.0.1`. 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 account. There is no update check either, except in the Mac app, which asks GitHub about once a day whether a newer version of it is out, as [Updates (Mac app only)](#updates-mac-app-only) describes. Fonts and scripts are bundled, so the page loads nothing from the internet.
|
|
269
|
+
With email, the webhook, ntfy, Pushover and other machines 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. With an ntfy topic set, it also connects to that topic's server, once for each push, as [ntfy](#ntfy) describes, and with a Pushover token and key set, to `api.pushover.net`, once for each push, as [Pushover](#pushover) describes. A push that Send a test in Settings asks for is one more of these. With `AGENT_LOOKOUT_PULL_REQUESTS=on`, it runs `gh`, which connects to GitHub, at most once every 2 minutes for each repository and branch your sessions are on, as [`gh`](#gh) describes. With other machines named in `AGENT_LOOKOUT_REMOTES`, your own `ssh` connects to each, and Agent Lookout sends its two requests to Agent Lookout there through that connection, as [Another machine over SSH](#another-machine-over-ssh) describes. Agent Lookout itself connects only to the end of the tunnel on this computer's `127.0.0.1`. 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 account. There is no update check either, except in the Mac app, which asks GitHub about once a day whether a newer version of it is out, as [Updates (Mac app only)](#updates-mac-app-only) describes. Fonts and scripts are bundled, so the page loads nothing from the internet.
|
|
243
270
|
|
|
244
271
|
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.
|
|
245
272
|
|
|
@@ -249,11 +276,11 @@ The `claude agents` command is Claude Code's own program, and it may contact Ant
|
|
|
249
276
|
|
|
250
277
|
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.
|
|
251
278
|
|
|
252
|
-
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, and the request the Clear history button in Settings sends carries `X-Agent-Lookout-Action: clear-history`, with an empty body. The request Stop session sends carries `X-Agent-Lookout-Action: stop` and the session's ID, and the one that ends the sessions left running carries `X-Agent-Lookout-Action: clean-up`, with each session's ID and the moment its idle began. The one Allow and Deny send carries `X-Agent-Lookout-Action: answer`, with the session's ID, Agent Lookout's own ID for the request and the answer. The one the Time rules card sends carries `X-Agent-Lookout-Action: time-rules`, with the three rules, and the one the Permission rules card sends carries `X-Agent-Lookout-Action: permission-rules`, with one change to the rules. In the Mac app, the Updates card also sends `X-Agent-Lookout-Action: check-for-updates` and `install-update`, each with the body `{}`, and `update-setting`, with `{"automatic": true}` or `false`. The Menu bar card sends `menu-bar-setting`, with `{"show": true}` or `false`.
|
|
279
|
+
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, and the request the Clear history button in Settings sends carries `X-Agent-Lookout-Action: clear-history`, with an empty body. The request Stop session sends carries `X-Agent-Lookout-Action: stop` and the session's ID, and the one that ends the sessions left running carries `X-Agent-Lookout-Action: clean-up`, with each session's ID and the moment its idle began. The one Allow and Deny send carries `X-Agent-Lookout-Action: answer`, with the session's ID, Agent Lookout's own ID for the request and the answer. The one the Time rules card sends carries `X-Agent-Lookout-Action: time-rules`, with the three rules, and the one the Permission rules card sends carries `X-Agent-Lookout-Action: permission-rules`, with one change to the rules. Send a test, on the ntfy and Pushover cards, sends `X-Agent-Lookout-Action: phone-test`, with `{"channel": "ntfy"}` or `{"channel": "pushover"}`. In the Mac app, the Updates card also sends `X-Agent-Lookout-Action: check-for-updates` and `install-update`, each with the body `{}`, and `update-setting`, with `{"automatic": true}` or `false`. The Menu bar card sends `menu-bar-setting`, with `{"show": true}` or `false`.
|
|
253
280
|
|
|
254
|
-
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, to clear the history, as the Clear history button does, to stop a Claude Code session, or end the sessions left running, as Stop and End all… do, with every check of [Stopping a session](#stopping-a-session), to answer a permission prompt Agent Lookout is holding, as Allow and Deny do,
|
|
281
|
+
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, to clear the history, as the Clear history button does, to stop a Claude Code session, or end the sessions left running, as Stop and End all… do, with every check of [Stopping a session](#stopping-a-session), to answer a permission prompt Agent Lookout is holding, as Allow and Deny do, to change the time rules or the permission rules, as Settings does, which includes adding an allow rule that then answers prompts without a press, and to send a test push, as Send a test does, which counts toward the hour's 20 for that channel. A program that can do that can already type into your terminal, but on a shared computer another account could not. The Mac app opens no port, so there only its own window can ask any of this. It shares the settings file, though. A program running as you can write `~/.agent-lookout/settings.json`, or start `npx agent-lookout` beside the app and ask that copy to change a rule, and the Mac app puts the time rules and permission rules the file then holds in force at its next poll, an allow rule included. `AGENT_LOOKOUT_STOP=off` takes stopping away, and `AGENT_LOOKOUT_ANSWER=off` takes answering away.
|
|
255
282
|
|
|
256
|
-
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, the one Clear history sends, the ones Stop session and End send, the one Allow and Deny send
|
|
283
|
+
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, the one Clear history sends, the ones Stop session and End send, the one Allow and Deny send, the ones the Time rules and Permission rules cards send and the one Send a test sends are the eight that change anything, with the Mac app's four for its updates and its menu bar. The server refuses them from any website, though not from another program on this machine, as the paragraph above says, and the Mac app answers its four only for its own window. [SECURITY.md](SECURITY.md) lists its checks.
|
|
257
284
|
|
|
258
285
|
## Notifications
|
|
259
286
|
|
|
@@ -286,11 +313,11 @@ The local server shows notifications on this machine too. When one of the events
|
|
|
286
313
|
- the session's name, as its title, with the machine's name for a session on another machine
|
|
287
314
|
- what happened, as its text, in the same wordings, with what a waiting session is asking
|
|
288
315
|
|
|
289
|
-
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. In the Mac app, the server shows them as the app's own notifications instead, and runs no `osascript` for them. macOS files them under Agent Lookout, and a click on one brings the app's window forward.
|
|
316
|
+
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. In the Mac app, the server shows them as the app's own notifications instead, and runs no `osascript` for them. macOS files them under Agent Lookout, and a click on one brings the app's window forward, on the session's details for a wait. For a wait whose permission request Agent Lookout holds, the app's notification says what the request asks instead, with Deny and Allow, as [In the Mac app](#in-the-mac-app) says.
|
|
290
317
|
|
|
291
318
|
### Reminders and summaries
|
|
292
319
|
|
|
293
|
-
With the [time rules](docs/GUIDE.md#time-rules) on, the page and the server make two more kinds of notification, in the same two ways and by the same switches. A reminder of a long wait has the session's name as its title, with the machine's name for a session on another machine, and as its text how long it has waited and for what, with what it is asking after it, as in "Has waited 10 minutes for permission: Run: npm test". On the page it takes the place of the wait's own, with its tag. The summary made when quiet hours end has "While quiet" as its title, and as its text the names of the sessions that waited, finished, failed or ended while they held, each with the machine's name for a session on another machine, with how long each waited, as in "checkout-flow waited 25 minutes and billing-webhooks finished". It holds nothing a session was asking. The page's has the tag `agent-lookout:while-quiet`, and stays until you clear it. During quiet hours neither the page nor the server makes any notification.
|
|
320
|
+
With the [time rules](docs/GUIDE.md#time-rules) on, the page and the server make two more kinds of notification, in the same two ways and by the same switches. A reminder of a long wait has the session's name as its title, with the machine's name for a session on another machine, and as its text how long it has waited and for what, with what it is asking after it, as in "Has waited 10 minutes for permission: Run: npm test". On the page it takes the place of the wait's own, with its tag. With Remind again on, the same reminder is made again at each interval while the session still waits, with how long it has waited by then, on the page each taking the place of the last, and none once the wait is answered or the session ends. The summary made when quiet hours end has "While quiet" as its title, and as its text the names of the sessions that waited, finished, failed or ended while they held, each with the machine's name for a session on another machine, with how long each waited, as in "checkout-flow waited 25 minutes and billing-webhooks finished". It holds nothing a session was asking. The page's has the tag `agent-lookout:while-quiet`, and stays until you clear it. During quiet hours neither the page nor the server makes any notification.
|
|
294
321
|
|
|
295
322
|
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.
|
|
296
323
|
|
|
@@ -299,8 +326,8 @@ While a page that has notifications on is open and asking every 2 seconds, the p
|
|
|
299
326
|
A notification the server shows differs from the browser's:
|
|
300
327
|
|
|
301
328
|
- Outside the Mac app, macOS shows it as coming from Script Editor, which is how it labels whatever `osascript` shows. In the Mac app, macOS shows it as coming from Agent Lookout. 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.
|
|
302
|
-
- Outside the Mac app, it holds nothing that could open the session or the dashboard, and what a click on it does has not been checked. In the Mac app a click brings the window forward.
|
|
303
|
-
-
|
|
329
|
+
- Outside the Mac app, it holds nothing that could open the session or the dashboard, and what a click on it does has not been checked. In the Mac app a click brings the window forward, on the session's details for a wait or a reminder of one.
|
|
330
|
+
- Outside the Mac app, 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. The Mac app takes down one with Deny and Allow once a button on it is pressed, or once its request has been answered or let go.
|
|
304
331
|
|
|
305
332
|
## Email
|
|
306
333
|
|
|
@@ -317,7 +344,7 @@ An email is plain text. Its subject is the session's name followed by the reason
|
|
|
317
344
|
- the agent, such as Claude Code or Codex, or the agent a status file names
|
|
318
345
|
- one line saying Agent Lookout sent it and how to stop these emails
|
|
319
346
|
|
|
320
|
-
With the [time rules](docs/GUIDE.md#time-rules) on, two more kinds of email can be sent. A reminder of a long wait is the email of a wait with a subject that says how long it has waited, such as "checkout-flow has waited 10 minutes for permission", and a line on how often reminders come. The summary sent when quiet hours end has the subject "While quiet:" followed by the first few sessions' names and what happened, and in its body, one line for each session that waited, finished, failed or ended while they held, with how long it waited and from when, or when it finished, failed or ended, at most 50, and a line on where quiet hours are set. A summary holds the sessions' names alone, nothing of what they were asking and no folder.
|
|
347
|
+
With the [time rules](docs/GUIDE.md#time-rules) on, two more kinds of email can be sent. A reminder of a long wait is the email of a wait with a subject that says how long it has waited, such as "checkout-flow has waited 10 minutes for permission", and a line on how often reminders come. With Remind again on, the same email goes again at each interval while the session still waits, saying how long by then, and holds nothing more than the first. The summary sent when quiet hours end has the subject "While quiet:" followed by the first few sessions' names and what happened, and in its body, one line for each session that waited, finished, failed or ended while they held, with how long it waited and from when, or when it finished, failed or ended, at most 50, and a line on where quiet hours are set. A summary holds the sessions' names alone, nothing of what they were asking and no folder.
|
|
321
348
|
|
|
322
349
|
It holds no folder path, no prompt, none of Claude Code's own wording for the wait, no link, no image, nothing that reports back when it is opened and, unless you set `AGENT_LOOKOUT_EMAIL_ASKING=on` (below), nothing of what a waiting session is asking. 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.
|
|
323
350
|
|
|
@@ -340,7 +367,7 @@ For each email Agent Lookout looks up the server's name, opens a connection to i
|
|
|
340
367
|
|
|
341
368
|
### When
|
|
342
369
|
|
|
343
|
-
`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, apart from reminders. With the long wait reminder on, a wait is also reminded of once it has waited the rule's minutes, unless its email went only after that. That includes a wait already open when Agent Lookout started, if it had not waited that long yet. Each change to the minutes while it waits can bring one more, once it has waited the new number. 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.
|
|
370
|
+
`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, apart from reminders. With the long wait reminder on, a wait is also reminded of once it has waited the rule's minutes, unless its email went only after that. That includes a wait already open when Agent Lookout started, if it had not waited that long yet. Each change to the minutes while it waits can bring one more, once it has waited the new number. With Remind again on, a wait still open is reminded of again each time the interval passes after that, and never once the wait is answered or the session ends. After the computer has slept, or when quiet hours end, one email goes, not one for each interval missed. A wait already past the minutes when Agent Lookout started is reminded of at the next interval. 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.
|
|
344
371
|
|
|
345
372
|
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`.
|
|
346
373
|
|
|
@@ -385,7 +412,7 @@ It holds:
|
|
|
385
412
|
- `at`: when it happened, as a time in UTC: when the wait began, or when Agent Lookout saw the session finish, fail or end.
|
|
386
413
|
- `waitedSeconds`, for a wait only: how long it had waited when it was posted.
|
|
387
414
|
|
|
388
|
-
With the [time rules](docs/GUIDE.md#time-rules) on, two more kinds of post can be sent. A reminder of a long wait is the post of a wait, with the line saying how long it has waited, such as "checkout-flow has waited 10 minutes for permission", and one more field, `"reminder": true`. The summary sent when quiet hours end looks like this:
|
|
415
|
+
With the [time rules](docs/GUIDE.md#time-rules) on, two more kinds of post can be sent. A reminder of a long wait is the post of a wait, with the line saying how long it has waited, such as "checkout-flow has waited 10 minutes for permission", and one more field, `"reminder": true`. With Remind again on, the same post goes again at each interval while the session still waits, saying how long by then, with one field more, `"repeat"`, a count of the intervals since the first reminder's time, which can skip numbers after a sleep. The summary sent when quiet hours end looks like this:
|
|
389
416
|
|
|
390
417
|
```json
|
|
391
418
|
{
|
|
@@ -443,7 +470,7 @@ Each post goes to the one address in `AGENT_LOOKOUT_WEBHOOK_URL`, and to no othe
|
|
|
443
470
|
|
|
444
471
|
### When
|
|
445
472
|
|
|
446
|
-
`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, apart from reminders. With the long wait reminder on, a wait is also reminded of once it has waited the rule's minutes, unless its post went only after that. That includes a wait already open when Agent Lookout started, if it had not waited that long yet. Each change to the minutes while it waits can bring one more, once it has waited the new number. 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.
|
|
473
|
+
`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, apart from reminders. With the long wait reminder on, a wait is also reminded of once it has waited the rule's minutes, unless its post went only after that. That includes a wait already open when Agent Lookout started, if it had not waited that long yet. Each change to the minutes while it waits can bring one more, once it has waited the new number. With Remind again on, a wait still open is reminded of again each time the interval passes after that, and never once the wait is answered or the session ends. After the computer has slept, or when quiet hours end, one post goes, not one for each interval missed. A wait already past the minutes when Agent Lookout started is reminded of at the next interval. 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.
|
|
447
474
|
|
|
448
475
|
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`.
|
|
449
476
|
|
|
@@ -455,6 +482,104 @@ Through `GET /api/webhook`, the dashboard learns whether the webhook is on, the
|
|
|
455
482
|
|
|
456
483
|
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.
|
|
457
484
|
|
|
485
|
+
## ntfy
|
|
486
|
+
|
|
487
|
+
Pushes through [ntfy](https://ntfy.sh) are off until you set them up, and they can be set up only in the environment Agent Lookout starts with: `AGENT_LOOKOUT_NTFY_URL`, the address of one ntfy topic, on ntfy.sh or on a server of your own. `AGENT_LOOKOUT_NTFY_TOKEN`, an access token for the topic, `AGENT_LOOKOUT_NTFY_EVENTS`, what is pushed, `AGENT_LOOKOUT_NTFY_AFTER`, how long a wait lasts before it is pushed, and `AGENT_LOOKOUT_NTFY_ASKING`, whether the push for a wait says what the session is asking, can be left out. While the address is unset, nothing is pushed 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#ntfy) says how to set it up and how to turn it off.
|
|
488
|
+
|
|
489
|
+
### What a push holds
|
|
490
|
+
|
|
491
|
+
A push is one request with a body of JSON, such as:
|
|
492
|
+
|
|
493
|
+
```json
|
|
494
|
+
{
|
|
495
|
+
"topic": "your-topic",
|
|
496
|
+
"title": "checkout-flow is waiting for permission",
|
|
497
|
+
"message": "4m 12s · storefront · VS Code · Claude Code",
|
|
498
|
+
"priority": 4,
|
|
499
|
+
"tags": ["hourglass"]
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
It holds:
|
|
504
|
+
|
|
505
|
+
- `topic`: the topic at the end of `AGENT_LOOKOUT_NTFY_URL`.
|
|
506
|
+
- `title`: the session's name and what happened, in the words the dashboard uses: "checkout-flow is waiting for permission", "checkout-flow asked you a question" or "checkout-flow is waiting for you", and "billing-webhooks finished", "billing-webhooks failed" or "billing-webhooks ended".
|
|
507
|
+
- `message`: for a wait, how long it had waited when it was pushed, and for a session that finished, failed or ended, the time on this computer's clock when Agent Lookout saw it, as in "Seen at 14:02"; then the name of the session's project folder, which is the last part of its path and never the path, the app it runs in and its agent, each left out when it is not known.
|
|
508
|
+
- `priority`: 4, ntfy's high, for a wait, a reminder of one and the test, which the ntfy app shows with a long vibration, and 3, ntfy's default, for the rest.
|
|
509
|
+
- `tags`: the name of one emoji ntfy shows before the title: `hourglass` for a wait, a reminder or the test, `white_check_mark` for finished, `x` for failed and `stop_sign` for ended. The summary has none.
|
|
510
|
+
|
|
511
|
+
With the [time rules](docs/GUIDE.md#time-rules) on, two more kinds of push can be sent. A reminder of a long wait has a title that says how long it has waited, such as "checkout-flow has waited 10 minutes for permission", and a message that says since when, as in "Since 14:01 · storefront · VS Code · Claude Code". With Remind again on, the same push goes again at each interval while the session still waits, saying how long by then. The summary sent when quiet hours end has the title "While quiet", and as its message the names of the first four sessions that waited, finished, failed or ended while they held, with how long each waited, and how many more there were, as in "checkout-flow waited 25 minutes and billing-webhooks finished". It holds nothing a session was asking and no folder.
|
|
512
|
+
|
|
513
|
+
It holds no folder path, no prompt, none of Claude Code's own wording for the wait, no session ID or process ID, no link, not the dashboard's address, nothing about this computer and, unless you set `AGENT_LOOKOUT_NTFY_ASKING=on` (below), nothing of what a waiting session is asking. Its headers are `Content-Type: application/json`, `Content-Length`, `User-Agent: Agent Lookout/` followed by the version, `Authorization: Bearer` followed by the token only when `AGENT_LOOKOUT_NTFY_TOKEN` is set, `Host` and `Connection: close`. It carries no cookie and no other header. 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. The title is cut to 250 characters and the message to 1,024. The JSON is written whole by Node's own writer, so nothing in a name can add a field, and it is not marked as Markdown, so ntfy shows the words as they are. The ntfy apps may show a web address written out in a name as a link.
|
|
514
|
+
|
|
515
|
+
#### What a waiting session is asking, in a push
|
|
516
|
+
|
|
517
|
+
Start Agent Lookout with `AGENT_LOOKOUT_NTFY_ASKING=on`, and the push for a wait, and each reminder of it, begins its message with one more line: `Asking:` followed by what the session is asking, such as `Asking: Run: npm test`. It is the line the Needs you panel shows, as [Claude Code's transcripts](#claude-codes-transcripts) describes, cleaned and cut in the same way: control characters taken out, made one line, and cut to 200 characters. It can hold a command, which can name any path, a server or a password typed on the command line, a web address, a file's full path when the file is outside the session's folder, or the question the session put to you. It is never in the title, never in a push for a session that finished, failed or ended, and never in the summary. Only a Claude Code session has one, and only when its transcript says. It is taken from the session list at the moment the push is sent, and is not kept for it, as for email. Everyone who can read the topic sees it, and the server keeps it as long as it keeps the push. `AGENT_LOOKOUT_NTFY_ASKING=off`, or leaving it out, puts nothing of it in a push, and anything else turns ntfy off until it is corrected. It is separate from the other channels' settings for it.
|
|
518
|
+
|
|
519
|
+
### Where it goes, and how
|
|
520
|
+
|
|
521
|
+
Each push is posted to the server named in `AGENT_LOOKOUT_NTFY_URL`, at the address before the topic, which is `https://ntfy.sh/` for a topic on ntfy.sh, with the topic in the body, and to no other address. For each push Agent Lookout looks up the server's host, opens a connection to it, sends the push, reads the status of the answer and closes the connection. The rest of the answer, which repeats the push, is read and dropped. A redirect is not followed, and a push is never tried again. The address must begin `https://`, so the connection is encrypted with TLS, and the certificate is always checked, even when `NODE_TLS_REJECT_UNAUTHORIZED=0` is set. Only to a server on this computer, at `127.0.0.1` or `localhost`, may it begin `http://`. It may hold no user name, password, query or fragment, so the token never goes in an address, where a log could keep it: it goes in the `Authorization` header alone. The server sees the network address the connection comes from, as any server you connect to does.
|
|
522
|
+
|
|
523
|
+
The server then hands the push to every phone and browser subscribed to the topic. Anyone who knows a topic's name can subscribe to it and read every push sent there, on ntfy.sh and on any server without access control, so the topic's name is a secret, like a password: the guide shows how to make one no one can guess, and an access token keeps out those who guess it only where the topic is guarded: on a server of your own with access control, or on ntfy.sh for a topic reserved for your account, which only its paid plans allow. On ntfy.sh's free plan the name is the only guard. ntfy's own documentation says that ntfy.sh keeps a push for 12 hours by default, so a phone that was off can fetch it, records topic names and network addresses, and does not encrypt a push from end to end, and that its Android and iPhone apps from the app stores may have a push carried by Google's or Apple's own push services. A server of your own keeps what its own settings say.
|
|
524
|
+
|
|
525
|
+
### When
|
|
526
|
+
|
|
527
|
+
`AGENT_LOOKOUT_NTFY_EVENTS` names the events that are pushed, from `needs-you`, `finished`, `failed` and `ended`, and is `needs-you` alone unless you set it. A push goes for a wait that begins after Agent Lookout started, once it has lasted the delay, which is one minute unless `AGENT_LOOKOUT_NTFY_AFTER` says otherwise, if the session is still waiting then, by the rules the [webhook](#when-1) follows: one at most for each wait apart from reminders and their repeats, none for a wait answered before then, none for what was already true when Agent Lookout started, nothing during quiet hours but one summary when they end, and nothing for a wait Agent Lookout answered. A push that could not be sent is not tried again. At most 20 are tried in any hour, whatever they are for, a test among them, counted apart from emails, posts and Pushover's pushes. Past that, none goes until the hour has passed.
|
|
528
|
+
|
|
529
|
+
### Send a test
|
|
530
|
+
|
|
531
|
+
While ntfy is on, its card in Settings has Send a test. Pressing it sends one push, titled "Agent Lookout test", with the message "Pushes from Agent Lookout reach this device.", and nothing of any session. It goes during quiet hours too, since you asked for it, counts toward the 20 an hour, and is not tried while those are spent. The request the page sends Agent Lookout's own server carries `X-Agent-Lookout-Action: phone-test` and the body `{"channel": "ntfy"}`, and nothing else. The card says when it went, or why it did not; the line on the last push is about notices alone.
|
|
532
|
+
|
|
533
|
+
### What is kept
|
|
534
|
+
|
|
535
|
+
Anyone who has the topic's address can read the pushes, and anyone who has the token can send and read on the topic as you, so both are kept like passwords. They live 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 them to a file, never sends them to the dashboard, and never prints them. When an ntfy 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 them out of it.
|
|
536
|
+
|
|
537
|
+
Through `GET /api/ntfy`, the dashboard learns whether ntfy is on, the host of the server and nothing else of the address, such as `ntfy.sh`, whether a token is set and never the token, the events that are pushed, the delay, whether the push for a wait says what the session is asking, never what it was asking, and when the last push of a notice was tried, with whether it was sent or a short reason why not. That, and the times of the pushes of the last hour, are held in memory and are gone when Agent Lookout stops.
|
|
538
|
+
|
|
539
|
+
Once a push has been sent, Agent Lookout has no hold on it. The server and every device subscribed to the topic keep it for as long as their own settings say. Agent Lookout cannot recall or delete it.
|
|
540
|
+
|
|
541
|
+
The button and the switches for notifications in Settings do not turn pushes on or off, or choose what is pushed. To stop them, start Agent Lookout again without `AGENT_LOOKOUT_NTFY_URL`.
|
|
542
|
+
|
|
543
|
+
## Pushover
|
|
544
|
+
|
|
545
|
+
Pushes through [Pushover](https://pushover.net) are off until you set them up, and they can be set up only in the environment Agent Lookout starts with: `AGENT_LOOKOUT_PUSHOVER_TOKEN`, the API token of a Pushover application you make, and `AGENT_LOOKOUT_PUSHOVER_USER`, your user key or a group key. Both are needed. `AGENT_LOOKOUT_PUSHOVER_EVENTS`, `AGENT_LOOKOUT_PUSHOVER_AFTER` and `AGENT_LOOKOUT_PUSHOVER_ASKING` can be left out, and work as ntfy's do. While either of the first two is unset, nothing is pushed 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#pushover) says how to set it up and how to turn it off.
|
|
546
|
+
|
|
547
|
+
### What a Pushover push holds
|
|
548
|
+
|
|
549
|
+
A push is one request with a body of JSON, such as:
|
|
550
|
+
|
|
551
|
+
```json
|
|
552
|
+
{
|
|
553
|
+
"token": "your application's token",
|
|
554
|
+
"user": "your user key",
|
|
555
|
+
"title": "checkout-flow is waiting for permission",
|
|
556
|
+
"message": "4m 12s · storefront · VS Code · Claude Code",
|
|
557
|
+
"priority": 0
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
`token` and `user` say which application sends it and to whom. `title` and `message` are those of an ntfy push, as [What a push holds](#what-a-push-holds) describes, for reminders, their repeats and the summary of quiet hours too, and with `AGENT_LOOKOUT_PUSHOVER_ASKING=on` the push for a wait, and each reminder of it, begins its message with the `Asking:` line, as [ntfy's](#what-a-waiting-session-is-asking-in-a-push) does. `priority` is 0, Pushover's normal, for a wait, a reminder of one and the test, which sounds as your device is set to, and -1, quiet, for the rest, which shows without a sound. It is never 1, which would sound through the quiet hours you set in Pushover's own app, or 2, which repeats until you acknowledge it. It holds no address to open, no sound, no device and no HTML, and nothing more than an ntfy push holds. Its headers are `Content-Type: application/json`, `Content-Length`, `User-Agent: Agent Lookout/` followed by the version, `Host` and `Connection: close`.
|
|
562
|
+
|
|
563
|
+
### Where a Pushover push goes, and how
|
|
564
|
+
|
|
565
|
+
Each push is posted to `https://api.pushover.net/1/messages.json`, Pushover's own address, and to no other: no setting changes it. The token and the key go in the body alone, never in the address. As for ntfy, it is one request on a connection of its own, encrypted with TLS, with the certificate always checked, even when `NODE_TLS_REJECT_UNAUTHORIZED=0` is set. No redirect is followed, the answer is read and dropped, and a push is never tried again. Pushover sees the network address the connection comes from.
|
|
566
|
+
|
|
567
|
+
Pushover then delivers it to the devices of that user or group, through Apple's and Google's push services for its iPhone and Android apps. Pushover's privacy policy says it deletes a message once its delivery has been confirmed, and otherwise after 21 days, and that it may record network addresses. Pushover's own documentation says an account may send 10,000 messages a month for free, shared by all its applications; past that Pushover refuses them, and the card says so.
|
|
568
|
+
|
|
569
|
+
### When a Pushover push goes
|
|
570
|
+
|
|
571
|
+
By the rules an ntfy push goes by, under Pushover's own settings, and with its own count of 20 an hour, a test among them.
|
|
572
|
+
|
|
573
|
+
### Send a test through Pushover
|
|
574
|
+
|
|
575
|
+
While Pushover is on, its card in Settings has Send a test, which sends the same test push ntfy's does, with `{"channel": "pushover"}`. It counts as ntfy's does.
|
|
576
|
+
|
|
577
|
+
### What is kept of Pushover
|
|
578
|
+
|
|
579
|
+
Whoever has the application's token and your user key can push to your devices, so both are kept like passwords, as ntfy's topic and token are: in the environment alone, never written to a file, sent to the dashboard or printed. When a Pushover setting cannot be read or is missing, Agent Lookout prints one line that names the setting and never its value. Through `GET /api/pushover`, the dashboard learns whether Pushover is on, the events, the delay, whether the push for a wait says what the session is asking, and when the last push of a notice was tried, with whether it was sent or a short reason why not. It never learns the token or the key. That is held in memory and is gone when Agent Lookout stops. Once a push has been sent, Agent Lookout cannot recall or delete it.
|
|
580
|
+
|
|
581
|
+
The button and the switches for notifications in Settings do not turn pushes on or off. To stop them, start Agent Lookout again without `AGENT_LOOKOUT_PUSHOVER_TOKEN` or `AGENT_LOOKOUT_PUSHOVER_USER`.
|
|
582
|
+
|
|
458
583
|
## Updates (Mac app only)
|
|
459
584
|
|
|
460
585
|
The Mac app checks whether a newer version of it has been released. Nothing else does: the npm package, `npx agent-lookout`, `npm start`, `npm run dev` and a development run of the app, `npm run dev:desktop`, never check by themselves.
|
|
@@ -515,7 +640,7 @@ Nothing. Each answer is worked out from one reading of the list and is gone once
|
|
|
515
640
|
|
|
516
641
|
Agent Lookout keeps one thing about your sessions on disk: the history behind the Events log and the charts, so that they are still there after it restarts. [The history](#the-history) says what it holds, where it is, how long it is kept, how to clear it and how to turn it off. It also keeps its time rules and permission rules in a file, which holds nothing about your sessions: [The settings file](#the-settings-file). Apart from those, its own code writes no files, except the Mac app's, below.
|
|
517
642
|
|
|
518
|
-
The latest session list is held in memory only, and is gone when Agent Lookout stops. What a waiting session is asking is in that list only while it waits, and in no event or point of history, on disk or in memory. So is a permission request held for the dashboard, which is also kept in memory only while it is held. With `AGENT_LOOKOUT_EMAIL_ASKING=on` or `
|
|
643
|
+
The latest session list is held in memory only, and is gone when Agent Lookout stops. What a waiting session is asking is in that list only while it waits, and in no event or point of history, on disk or in memory. So is a permission request held for the dashboard, which is also kept in memory only while it is held. With `AGENT_LOOKOUT_EMAIL_ASKING=on`, `AGENT_LOOKOUT_WEBHOOK_ASKING=on`, `AGENT_LOOKOUT_NTFY_ASKING=on` or `AGENT_LOOKOUT_PUSHOVER_ASKING=on`, it is also in the email, post or push for that wait from the moment it is written until it is handed over, and nowhere else. So are the tmux panes and terminal tabs it last found, the branches and repositories it last read, with pull requests on the pull requests `gh` last gave and where each branch's is looked for, and what the dashboard pages last said about notifications.
|
|
519
644
|
|
|
520
645
|
### The history
|
|
521
646
|
|
|
@@ -527,7 +652,7 @@ It writes there what it already holds in memory for the Events log and the chart
|
|
|
527
652
|
- Each history point, one for each poll: its time, and how many sessions were waiting on you, working, idle, and in all.
|
|
528
653
|
- When Agent Lookout started writing, and when the history was cleared.
|
|
529
654
|
|
|
530
|
-
Each event holds the session's name as the dashboard shows it: the name its agent gave it, from Claude Code's session file or the `claude` command's list, from Codex's `session_index.jsonl
|
|
655
|
+
Each event holds the session's name as the dashboard shows it: the name its agent gave it, from Claude Code's session file or the `claude` command's list, from Codex's `session_index.jsonl`, from the Antigravity CLI's title of the conversation or from a status file, or else the name of its folder, which for a Codex session is read from the start of its session file, and for an Antigravity CLI session from its program's log, or else, for an Antigravity CLI session, its conversation ID, the name of its folder in `brain/`. Nothing else from a transcript, from Codex's session files or from agy's transcripts and logs is written, and no folder path, branch, prompt or anything a waiting session is asking.
|
|
531
656
|
|
|
532
657
|
What it learns is written every 5 seconds, and once more as it stops. The files are JSON lines, one file for each day by the UTC clock, such as `v1-2026-10-06.jsonl`. A day that grows past 2 MB goes on in `v1-2026-10-06-2.jsonl`, and so on. Each day's file is deleted once that day ended more than 8 days ago, and the files hold 20 MB at most: before writing more than that, Agent Lookout deletes the oldest. When it starts, it reads the files back, and keeps in memory the last 1,000 events and the last six hours of points, as it does while it runs. For the Waits card it also keeps in memory, for 9 days, the events of sessions starting and stopping waiting on you, and the stretches of time it was measuring, which it works the card's totals out from. It reads only ordinary files in that folder of about 2 MB or less, does not follow a link, and passes over a line it cannot read. A file whose name says it was written by a later version of Agent Lookout, such as `v2-2026-10-06.jsonl`, is not read, written or deleted, for its age or by Clear history. A line dated more than a minute after the computer's clock was written while the clock was wrong, and is not read back.
|
|
533
658
|
|
|
@@ -539,11 +664,11 @@ To keep nothing on disk, start Agent Lookout with `AGENT_LOOKOUT_HISTORY=off`. I
|
|
|
539
664
|
|
|
540
665
|
### The settings file
|
|
541
666
|
|
|
542
|
-
Agent Lookout keeps the [time rules](docs/GUIDE.md#time-rules) and the [permission rules](docs/GUIDE.md#permission-rules) you set in Settings in `~/.agent-lookout/settings.json`, beside the history, or in the file `AGENT_LOOKOUT_SETTINGS_FILE` names, so they hold with no dashboard open and after a restart. It holds those rules and nothing else of Agent Lookout's: whether each time rule is on, the minutes before a reminder, the hours before a session is stale, and the times and days of quiet hours with the switch for the summary, and each permission rule in its order, with an ID Agent Lookout gave it, its decision, its tool and, for Bash, the command you wrote. It holds nothing about a session. A command you write in a rule is in the file as you wrote it, so leave out of a rule anything you would not keep in a file.
|
|
667
|
+
Agent Lookout keeps the [time rules](docs/GUIDE.md#time-rules) and the [permission rules](docs/GUIDE.md#permission-rules) you set in Settings in `~/.agent-lookout/settings.json`, beside the history, or in the file `AGENT_LOOKOUT_SETTINGS_FILE` names, so they hold with no dashboard open and after a restart. It holds those rules and nothing else of Agent Lookout's: whether each time rule is on, the minutes before a reminder and, once you set it, whether it goes again and the minutes between, the hours before a session is stale, and the times and days of quiet hours with the switch for the summary, and each permission rule in its order, with an ID Agent Lookout gave it, its decision, its tool and, for Bash, the command you wrote. It holds nothing about a session. A command you write in a rule is in the file as you wrote it, so leave out of a rule anything you would not keep in a file.
|
|
543
668
|
|
|
544
669
|
Agent Lookout reads it as it starts and again just before each change, and at each poll, every 2 seconds, it looks at the file's size and times to read it again when another copy of Agent Lookout, such as the Mac app beside `npx agent-lookout`, has written it. It writes it only when you change a rule in Settings, through `POST /api/settings/time-rules` or `POST /api/settings/permission-rules`, which the dashboard's page sends, and which another program on this computer can send too, as [Network](#network) says. It writes the file whole: to a new file of mode 600, so only your user can read or change it, in the same folder, which then takes the old one's place, and makes the folder with mode 700 when it is not there. It neither reads nor writes through a link, at the file's name or the folder's, and reads no file larger than 64 KB. Anything else the file holds, such as a later version's settings, is written back as it was. A file it finds but cannot read, such as one larger than 64 KB or one it may not read, it never writes over: a change is not saved until the file is mended or removed. Permission rules it cannot read whole, such as a later version's, are no rule at all: none of them answers anything, Settings says so, and they stay in the file as they were until you change a permission rule, which writes the list again with the rules Settings shows. Until you change a rule, nothing is written and no folder is made. To go back to every time rule off and no permission rule, delete the file while Agent Lookout is stopped.
|
|
545
670
|
|
|
546
|
-
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 with `AGENT_LOOKOUT_EMAIL_ASKING=on` the email for a wait holds what the session was asking, and the mail server and the mailbox keep it, as [Email](#email) says. With a webhook set up, each post holds the same, with `AGENT_LOOKOUT_WEBHOOK_ASKING=on` for what a session was asking, and the service it went to keeps it, as [Webhook](#webhook) says.
|
|
671
|
+
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 with `AGENT_LOOKOUT_EMAIL_ASKING=on` the email for a wait holds what the session was asking, and the mail server and the mailbox keep it, as [Email](#email) says. With a webhook set up, each post holds the same, with `AGENT_LOOKOUT_WEBHOOK_ASKING=on` for what a session was asking, and the service it went to keeps it, as [Webhook](#webhook) says. With ntfy or Pushover set up, each push holds a session's name and its folder's name, with that channel's own setting for what a session was asking, and the service and the devices it reaches keep it, as [ntfy](#ntfy) and [Pushover](#pushover) say.
|
|
547
672
|
|
|
548
673
|
The tools that run it write files of their own. None of these holds session data.
|
|
549
674
|
|
|
@@ -563,7 +688,7 @@ Session names, folder paths, repository names, branch names, pull request titles
|
|
|
563
688
|
|
|
564
689
|
Resume puts a session's folder path and its ID on the clipboard, and only when you press it. From there, anything that reads your clipboard can see them, such as a clipboard manager, and on a Mac with Handoff on, Universal Clipboard makes them available on your other Apple devices. The page never reads the clipboard.
|
|
565
690
|
|
|
566
|
-
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 (in the Mac app, both kinds come from Agent Lookout, so change Agent Lookout's own entry there), or leave notifications off. In the Mac app, the number of sessions that need you is shown in the menu bar, beside its icon, and the menu it opens lists their names and what each is asking, with the agent, project, branch or app of two that share a name. Both show while you share or record the screen. To keep them off the menu bar, set Show in menu bar to Off in Settings. An email shows the same name, and the folder's, wherever that mailbox is read, including the notifications a phone shows for it, and so does what a waiting session is asking, with `AGENT_LOOKOUT_EMAIL_ASKING=on`. A webhook post shows them to everyone who can read the channel it goes to, and in the notifications their apps show for it, and with `AGENT_LOOKOUT_WEBHOOK_ASKING=on` shows what a waiting session is asking there too.
|
|
691
|
+
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 (in the Mac app, both kinds come from Agent Lookout, so change Agent Lookout's own entry there), or leave notifications off. In the Mac app, the number of sessions that need you is shown in the menu bar, beside its icon, and the menu it opens lists their names and what each is asking, with the agent, project, branch or app of two that share a name, and, for a permission request Agent Lookout holds, every line of the command or the tool's inputs. Both show while you share or record the screen. To keep them off the menu bar, set Show in menu bar to Off in Settings. The app's own notification of a wait can hold the whole command a session asks to run, and macOS can show it on the lock screen. To keep it off the lock screen, open System Settings, then Notifications, choose Agent Lookout, and turn off its notifications on the lock screen, or set Show previews to When Unlocked or Never. An email shows the same name, and the folder's, wherever that mailbox is read, including the notifications a phone shows for it, and so does what a waiting session is asking, with `AGENT_LOOKOUT_EMAIL_ASKING=on`. A webhook post shows them to everyone who can read the channel it goes to, and in the notifications their apps show for it, and with `AGENT_LOOKOUT_WEBHOOK_ASKING=on` shows what a waiting session is asking there too. A push shows them on your phone, on its lock screen as its settings allow, and on every device subscribed to the ntfy topic or signed in to the Pushover account, and with that channel's own setting on, what a waiting session is asking.
|
|
567
692
|
|
|
568
693
|
## Changes
|
|
569
694
|
|