agent-lookout 0.2.5 → 0.2.7

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/docs/GUIDE.md CHANGED
@@ -2,13 +2,7 @@
2
2
 
3
3
  ## Requirements
4
4
 
5
- - macOS or Linux. Agent Lookout is developed and tested on macOS. On Linux it has been checked in CI by starting it with `npm start`, and not yet by a person on a Linux desktop. [On Linux](#on-linux) says what differs there. Windows is untested.
6
- - Node.js 22.12 or newer. The [Mac app](#desktop-app) needs no Node.js.
7
- - Claude Code, Codex or both. Neither needs any setup. Any other agent can appear too, by writing a status file: see [Your own agents](#your-own-agents).
8
- - For Claude Code, a version that has the `claude agents` command. `claude agents --help` should print `Usage: claude agents`. Without that command Agent Lookout still reads the session files, but cannot list background jobs that have finished or failed.
9
- - For Codex, version 0.155 or later, so that Agent Lookout can tell a session that has ended from one that is idle.
10
-
11
- The [README](../README.md#install) has the steps to install and start it.
5
+ Node.js 22.12 or newer, or the Mac app, which needs none, and Claude Code, Codex or both. [INSTALL.md](INSTALL.md) has the versions each needs, how to check them, and the steps to install and start Agent Lookout.
12
6
 
13
7
  ## The screen
14
8
 
@@ -39,7 +33,7 @@ The panel ends with four counts.
39
33
  | Stale | Sessions idle for 24 hours or more without a break |
40
34
  | Sessions | Every session found, split into open, finished and failed |
41
35
 
42
- Each session is counted once. A stale session is counted under Stale and not under Idle. A dash in place of a number means nothing could be counted, because no agent could be read yet. It does not mean zero.
36
+ Each session is counted once. A stale session is counted under Stale and not under Idle, and a session whose permission prompt was just answered is counted under Working, not as needing you. A dash in place of a number means nothing could be counted, because no agent could be read yet. It does not mean zero.
43
37
 
44
38
  Click Working or Idle to open a chart of how many sessions had that status over the last 15 minutes, hour or 6 hours. Click the panel's heading for the same chart of the sessions that needed you.
45
39
 
@@ -53,7 +47,7 @@ The Last hour chart has a bar for each five minutes of the last hour, on the clo
53
47
 
54
48
  #### Sessions
55
49
 
56
- The Sessions list holds every session that is not waiting for you, grouped in this order: Working, Idle, then sessions that finished or failed, then any whose status is unknown. Stale sessions are listed under Idle and counted beside it. The number by the list's title counts every session, including those in the Needs you panel. Each row gives:
50
+ The Sessions list holds every session that is not waiting for you, grouped in this order: Working, Idle, then sessions that finished or failed, then any whose status is unknown. Stale sessions are listed under Idle and counted beside it. A Claude Code session whose permission prompt you or a [permission rule](#permission-rules) just answered is listed first under Working, as Answered and with no time, for the second or two before Claude Code says what it does next. The number by the list's title counts every session, including those in the Needs you panel. Each row gives:
57
51
 
58
52
  - the session's name
59
53
  - the agent it belongs to, such as Claude Code, Codex or the name a [status file](#your-own-agents) gives, once there is more than one
@@ -235,14 +229,14 @@ Why, for each No and Partly:
235
229
  - Claude Code, Jump: In VS Code, in tmux, and in a tab of Terminal or iTerm2 on a Mac. Not in the desktop app or another terminal.
236
230
  - Claude Code, Quiet for: The file Agent Lookout reads is not rewritten as a session works.
237
231
  - Claude Code, Stop: In a terminal, in VS Code and for background jobs. Not in the desktop app.
238
- - Claude Code, Answer: With the Agent Lookout plugin installed. Allow only when all it allows is shown, so edits, plans and questions can only be denied.
232
+ - Claude Code, Answer: With the Agent Lookout plugin, by Allow, Deny or a permission rule. Allow only if all is shown: never edits, plans or questions.
239
233
  - Codex, Needs you: Codex does not record approval waits, so a session waiting for you shows as working.
240
234
  - Codex, Finished: From Codex 0.155 on, once no Codex program has the session open.
241
235
  - Codex, Failed: Codex does not record errors in its files.
242
236
  - Codex, Names: The desktop app does not keep its titles in the names file Agent Lookout reads, so its sessions take their folder's name.
243
237
  - Codex, Jump: Codex's files name no process to find, and Codex documents no link to a session.
244
238
  - Codex, Stop: Codex's files name no process that Agent Lookout could confirm and stop.
245
- - Codex, Answer: Codex records no approval waits, so there is nothing to answer from here.
239
+ - Codex, Answer: Codex records no approval waits, so there is nothing to answer from here, by hand or by a permission rule.
246
240
  - Status files, Working and idle: If the agent writes working and idle.
247
241
  - Status files, Needs you: If the agent writes waiting.
248
242
  - Status files, Finished: If the agent writes finished.
@@ -251,13 +245,13 @@ Why, for each No and Partly:
251
245
  - Status files, Jump: Nothing in a status file is used to reach a session.
252
246
  - Status files, Quiet for: If the agent writes its file again as it works.
253
247
  - Status files, Stop: Any program can write a status file, so nothing in one is used to stop a session.
254
- - Status files, Answer: A status file only says a session waits, and holds nothing to answer it through.
248
+ - Status files, Answer: A status file only says a session waits, and holds nothing to answer it through, by hand or by a permission rule.
255
249
 
256
250
  Each [other machine](#another-machine-over-ssh) adds a row for each agent found there, such as Claude Code on devbox, with what that machine's Agent Lookout says its agent can report, and No for Jump, Stop and Answer, which act on this computer only.
257
251
 
258
252
  ### Settings
259
253
 
260
- Settings has nine cards, and in the Mac app two more, [Menu bar](#menu-bar) and [Updates](#updates). Theme chooses Night, which is the default, Day, or System, which follows your computer's setting. Notifications turns notifications on and off, for the dashboard page and for Agent Lookout itself, and chooses what sends one. [Time rules](#time-rules) reminds you of a long wait, sets how long a session is idle before it is stale, and holds everything back during quiet hours. History says where the Events log and the charts are kept, how much they hold and since when, and clears them. Email says whether Agent Lookout emails you, and for what, and Webhook whether it posts to a webhook, such as a Slack channel's, and for what. Pull requests says whether each session's [pull request](#pull-requests) is shown, and whether `gh` can be asked for them. All three are set up when you start it, not here. Permission prompts says whether Allow and Deny are offered, and whether the [plugin](#install-the-plugin)'s requests reach Agent Lookout. This copy shows the version you are running and where Agent Lookout sends your data: nowhere, or only in the emails and webhook posts you set up, and with pull requests on, the names of repositories and branches to GitHub, through `gh`, unless `gh` was not found or is not signed in, when nothing goes to GitHub.
254
+ Settings has ten cards, and in the Mac app two more, [Menu bar](#menu-bar) and [Updates](#updates). Theme chooses Night, which is the default, Day, or System, which follows your computer's setting. Notifications turns notifications on and off, for the dashboard page and for Agent Lookout itself, and chooses what sends one. [Time rules](#time-rules) reminds you of a long wait, sets how long a session is idle before it is stale, and holds everything back during quiet hours. History says where the Events log and the charts are kept, how much they hold and since when, and clears them. Email says whether Agent Lookout emails you, and for what, and Webhook whether it posts to a webhook, such as a Slack channel's, and for what. Pull requests says whether each session's [pull request](#pull-requests) is shown, and whether `gh` can be asked for them. All three are set up when you start it, not here. Permission prompts says whether Allow and Deny are offered, and whether the [plugin](#install-the-plugin)'s requests reach Agent Lookout, and [Permission rules](#permission-rules), under it, keeps the rules that answer a permission prompt for you, and lists what they answered. This copy shows the version you are running and where Agent Lookout sends your data: nowhere, or only in the emails and webhook posts you set up, and with pull requests on, the names of repositories and branches to GitHub, through `gh`, unless `gh` was not found or is not signed in, when nothing goes to GitHub.
261
255
 
262
256
  #### Notifications
263
257
 
@@ -320,17 +314,19 @@ The browser gives its permission to the address, not to Agent Lookout. Another p
320
314
 
321
315
  The Time rules card has three rules, each with an Off and On switch, and all three are off until you turn them on. Agent Lookout keeps them in `~/.agent-lookout/settings.json`, so they hold with no dashboard tab open and after a restart. A change takes effect at once. The card shows what Agent Lookout saved: if a change could not be saved, a note says why and the switch stays as it was. A field is read when you leave it or press Enter, and Escape puts back what it held.
322
316
 
323
- Remind me of a long wait sends a reminder once a session has waited the minutes you set: 10 unless you change them, from 1 to 1,440. It goes once for each wait, the way the wait's first notice went: as a notification while Needs you is switched on, as an email or a webhook post while `needs-you` is among the events those send. It says how long the session has waited. The notification reads "Has waited 10 minutes for permission" under the session's name, takes the place of the wait's own, and is cleared when the session moves on. The email's subject and the post's line say "checkout-flow has waited 10 minutes for permission", and the post has `reminder: true`. A wait that had already gone on longer when the page opened, or when Agent Lookout started, is not reminded of, and nor is one whose first email or post went only once it had waited that long, since that one says how long already. Set more minutes while a session waits, and it is reminded of again once it has waited those.
317
+ Remind me of a long wait sends a reminder once a session has waited the minutes you set: 10 unless you change them, from 1 to 1,440. It goes once for each wait, the way the wait's first notice went: as a notification while Needs you is switched on, as an email or a webhook post while `needs-you` is among the events those send. Remind again, below, sends it again while the wait goes on. It says how long the session has waited. The notification reads "Has waited 10 minutes for permission" under the session's name, takes the place of the wait's own, and is cleared when the session moves on. The email's subject and the post's line say "checkout-flow has waited 10 minutes for permission", and the post has `reminder: true`. A wait that had already gone on longer when the page opened, or when Agent Lookout started, is not reminded of at those minutes, and nor is one whose first email or post went only once it had waited that long, since that one says how long already. Set more minutes while a session waits, and it is reminded of again once it has waited those.
318
+
319
+ Under it, Remind again while it waits sends the reminder again while the session still waits: every 30 minutes unless you change them, from 5 to 1,440. It is off until you turn it on. With a reminder after 10 minutes and again every 30, a session that keeps waiting is reminded of at 10 minutes, 40 minutes, 1 hour 10 minutes and so on, each time by the same channels as the first, saying how long it has waited by then, as in "checkout-flow has waited 40 minutes for permission". On the page each notification takes the place of the last. The ones Agent Lookout shows itself, with no dashboard tab open, cannot be taken down, so each stays in Notification Centre until you clear it: with Remind again set to 5 minutes, that can be 12 an hour for one wait. The email's subject is worded as the first reminder's, and the post has `reminder: true` and `repeat`, which counts the intervals since the first reminder's time: 1 at 40 minutes, 2 at 1 hour 10 minutes. After a sleep it can skip numbers. No reminder comes once the wait is answered, from the session itself, with Allow or Deny or by a permission rule, or once the session ends. The times go by how long the wait has lasted, so each channel, and a dashboard tab opened later, reminds at the same moments. A wait has one reminder at a time: after the computer has slept, or when quiet hours end, one comes, saying how long the session has waited by then, and none for the moments missed, and a moment that falls less than half the interval after it is skipped. A wait that had already passed its minutes when Agent Lookout started has its next reminder at the next of its moments, not at once.
324
320
 
325
321
  Mark idle sessions stale sets how long a session is idle before it is stale, in hours or days, from an hour to 30 days: 2 days unless you change them. A stale session is counted under Stale rather than Idle, its idle line in the Timeline turns to dots where it became stale, and a Claude Code session among them whose process still runs is listed under [Left running](#sessions-left-running), where it can be ended. With the rule off, a session is stale after a day.
326
322
 
327
- Quiet hours hold back every notification, email and post between two times on your computer's clock: 22:00 to 08:00 unless you change them. When the end comes before the start, they run past midnight, and the days you tick are the days they begin on: with Monday to Friday ticked, Friday night is quiet until Saturday morning, and Saturday night is not. With no day ticked they never begin. When they end, a session still waiting is notified as usual, and everything else held comes as one summary on each channel, such as "While quiet: checkout-flow waited 25 minutes, docs-site waited 1 hour 5 minutes over 2 waits and billing-webhooks finished": one notification headed While quiet, which stays until you clear it, one email and one webhook post. A session that waited and then finished, failed or ended is one item, as in "checkout-flow waited 25 minutes then ended". Each summary holds what its channel would have sent, so a wait answered before the email's delay was up is in no email. Turn on Leave answered waits out of the summary and it names only the sessions that finished, failed or ended. With nothing to say, there is no summary. Turning quiet hours off while they hold ends them at once. While they hold, a line under the times says so: "Quiet now, until 08:00. Notifications, emails and posts are held." In the Mac app, the notice of a new version waits for them to end too.
323
+ Quiet hours hold back every notification, email and post between two times on your computer's clock: 22:00 to 08:00 unless you change them. When the end comes before the start, they run past midnight, and the days you tick are the days they begin on: with Monday to Friday ticked, Friday night is quiet until Saturday morning, and Saturday night is not. With no day ticked they never begin. When they end, a session still waiting is notified as usual, or reminded of if it was told of before they began and its reminder came due in them, and everything else held comes as one summary on each channel, such as "While quiet: checkout-flow waited 25 minutes, docs-site waited 1 hour 5 minutes over 2 waits and billing-webhooks finished": one notification headed While quiet, which stays until you clear it, one email and one webhook post. A session that waited and then finished, failed or ended is one item, as in "checkout-flow waited 25 minutes then ended". A wait whose reminders came due in quiet hours and that was answered before they ended is one item too, however many came due. Each summary holds what its channel would have sent, so a wait answered before the email's delay was up is in no email. Turn on Leave answered waits out of the summary and it names only the sessions that finished, failed or ended. With nothing to say, there is no summary. Turning quiet hours off while they hold ends them at once. While they hold, a line under the times says so: "Quiet now, until 08:00. Notifications, emails and posts are held." In the Mac app, the notice of a new version waits for them to end too.
328
324
 
329
325
  The rules apply on top of the switches under Notifications, and of the settings for [email](#email) and the [webhook](#webhook), which still choose what goes where. Quiet hours go by the clock and time zone of the computer Agent Lookout runs on, and a dashboard tab follows what Agent Lookout says, so a tab in another time zone holds and sums up at the same moments. A dashboard tab opened during quiet hours sums up what happened after it opened. With no tab open, Agent Lookout shows its own summary on a Mac, and it covers all of the quiet hours. The rules set here cover the sessions of [another machine](#another-machine-over-ssh) too, as they cover this computer's.
330
326
 
331
- What quiet hours hold is kept in memory only. If Agent Lookout is stopped or restarted while they hold, what it held so far is forgotten: no summary comes of those hours, and a session that began waiting before the restart and still waits is not notified when they end. With the long wait reminder on, it is still reminded of, if it had not yet waited the reminder's minutes at the restart.
327
+ What quiet hours hold is kept in memory only. If Agent Lookout is stopped or restarted while they hold, what it held so far is forgotten: no summary comes of those hours, and a session that began waiting before the restart and still waits is not notified when they end. With the long wait reminder on, it is still reminded of, if it had not yet waited the reminder's minutes at the restart, or with Remind again on, at its next moment.
332
328
 
333
- `AGENT_LOOKOUT_SETTINGS_FILE` keeps the rules in another file. Agent Lookout reads the file as it starts and writes it whole when you change a rule, with mode 600, in a folder it makes with mode 700, and never through a link. If it cannot read the file, the rules it holds are off, the card says why in a note, and the terminal you started Agent Lookout in prints the same line. A file that is not JSON is written again by your first change. One it could not read at all, such as one larger than 64 KB, is never written over: a change is not saved until you mend or remove the file and start Agent Lookout again. [PRIVACY.md](../PRIVACY.md#the-settings-file) says what the file holds.
329
+ `AGENT_LOOKOUT_SETTINGS_FILE` keeps the rules in another file. Agent Lookout reads the file as it starts, and again just before each change, which it makes to what the file holds then. It writes it whole when you change a rule, with mode 600, in a folder it makes with mode 700, and never through a link. If it cannot read the file, the rules it holds are off, the card says why in a note, and the terminal you started Agent Lookout in prints the same line. A file that is not JSON is written again by your first change. One it could not read at all, such as one larger than 64 KB, is never written over: a change is not saved until you mend or remove the file. Two copies running at once, such as the Mac app and `npx agent-lookout`, share the file. Each looks at it at every poll and reads it again when the other has saved a change, so a change made in one is in force in the other by its next poll, usually within 2 seconds. A change to the permission rules is made to the list the file holds just then, so it keeps the other copy's. The Time rules card saves all three rules at once, so a time rule changed in one copy before the other copy's change has reached it puts that change back. After changing the time rules in one copy, wait until the other copy's card shows the change before changing them there. [PRIVACY.md](../PRIVACY.md#the-settings-file) says what the file holds.
334
330
 
335
331
  #### History
336
332
 
@@ -613,6 +609,7 @@ Agent Lookout then checks again that the process is that session: it reads the s
613
609
  - A Claude Code background job, while Agent Lookout runs the `claude` command: not with `AGENT_LOOKOUT_CLAUDE_FEED=off`, and not with `AGENT_LOOKOUT_CLAUDE_HOME` set and `AGENT_LOOKOUT_CLAUDE_BIN` not.
614
610
  - Not a session in Claude Code's desktop app, which looks after its own processes: its details say to stop it there. Not a session whose file does not say what kind it is, or which app it runs in.
615
611
  - Not a Codex session, and not a session from a status file.
612
+ - No session on Windows: [On Windows](#on-windows) says why.
616
613
 
617
614
  ### Sessions left running
618
615
 
@@ -639,15 +636,15 @@ Paste it in a terminal and run it. It goes to the session's folder first, becaus
639
636
  - A Claude Code background job that Claude Code lists as finished, failed or stopped, with its process gone, for as long as its row stays: 24 hours. These come from the `claude` command, so they are not listed with `AGENT_LOOKOUT_CLAUDE_FEED=off`.
640
637
  - A Claude Code session you stopped with [Stop](#stop-a-session), in its details, once Stop has said it stopped, until you close them, or until the list shows it running again, as a background job does once `claude attach` opens it.
641
638
  - Only when Claude Code gave the session's ID, a UUID, and its folder is known as a whole path. A job listed without its session ID has no Resume, and nor does a folder holding a line break or another control character, or a backslash, which fish would read inside the quotes.
642
- - Not a session whose process still runs: resuming it would open a second copy of the same conversation. Not one that left the list on its own, since Agent Lookout cannot tell whether its process ended. Not a Codex session, and not a session from a status file.
639
+ - Not a session whose process still runs: resuming it would open a second copy of the same conversation. Not one that left the list on its own, since Agent Lookout cannot tell whether its process ended. Not a Codex session, and not a session from a status file. No session on Windows has Resume, as [On Windows](#on-windows) says.
643
640
 
644
641
  ## Answer a permission prompt
645
642
 
646
- With the Agent Lookout plugin installed in Claude Code, a Claude Code session that waits for your permission shows Deny and Allow in the [Needs you](#needs-you) panel and at the top of its [details](#a-sessions-details). Above them is the whole of what it asks: for a shell command, every line of the command, in the mono, with any other input such as `run_in_background` and then the description Claude gave it, and for any other tool, its name and each of its inputs. While Allow is offered, all of it is drawn, however many lines it has. Press Allow to let that one request go ahead, or Deny to refuse it. Deny comes first, so it is in the same place whether Allow is offered or not. Neither button has focus until you move to it, and neither takes a press in the first second a request is shown, so a click meant for what was there before is not taken as an answer. A line then says what came of it, and the Events log says the session had a request allowed or denied from Agent Lookout. When the session asks again, its next request is shown under that line.
643
+ With the Agent Lookout plugin installed in Claude Code, a Claude Code session that waits for your permission shows Deny and Allow in the [Needs you](#needs-you) panel and at the top of its [details](#a-sessions-details). Above them is the whole of what it asks: for a shell command, every line of the command, in the mono, with any other input such as `run_in_background` and then the description Claude gave it, and for any other tool, its name and each of its inputs. While Allow is offered, all of it is drawn, however many lines it has. Press Allow to let that one request go ahead, or Deny to refuse it. Deny comes first, so it is in the same place whether Allow is offered or not. Neither button has focus until you move to it, and neither takes a press in the first second a request is shown, so a click meant for what was there before is not taken as an answer. A line then says what came of it, and the Events log says the session had a request allowed or denied from Agent Lookout. From that moment the wait is over: no reminder, email or webhook post follows for it. Claude Code can take a second or two to say the session is working again, but the session leaves Needs you at once, and until then is listed with the working sessions as Answered. If its file still says it waits 10 seconds after the answer, Agent Lookout takes it that the session needs you again, and tells you so. When the session asks again, its next request is shown under that line.
647
644
 
648
645
  The prompt in the session is still there, and still works: whichever you answer first wins. Answered in the session, the request leaves the dashboard within a few seconds. A Deny from the dashboard lets Claude carry on without the tool, and tells it that you denied it from Agent Lookout, while No in the terminal stops the turn.
649
646
 
650
- Allow is offered only when the whole of what it allows is shown. Deny alone is offered, with a line that says why, for an edit, a new file or a notebook edit, since the change itself is not shown, for a plan or a question, which take more than yes or no, for a request longer than 4,000 characters or 40 lines, for one that holds characters that cannot be shown as they are, which are written as their codes, for one with more than two blank lines in a row, which could push what follows out of sight, and for a command holding right-to-left letters, such as Hebrew or Arabic, which can draw it in another order than it runs. Agent Lookout allows or denies that one request and nothing more: it never changes the command and never saves a rule, so "Yes, and don't ask again" is answered in the session. Claude Code's own deny rules still apply after an Allow.
647
+ Allow is offered only when the whole of what it allows is shown. Deny alone is offered, with a line that says why, for an edit, a new file or a notebook edit, since the change itself is not shown, for a plan or a question, which take more than yes or no, for a request longer than 4,000 characters or 40 lines, for one that holds characters that cannot be shown as they are, which are written as their codes, for one with more than two blank lines in a row, which could push what follows out of sight, and for a command holding right-to-left letters, such as Hebrew or Arabic, which can draw it in another order than it runs. Agent Lookout allows or denies that one request and nothing more: it never changes the command and never saves a rule in Claude Code, so "Yes, and don't ask again" is answered in the session. Claude Code's own deny rules still apply after an Allow. To have Agent Lookout answer a kind of request for you every time, add a [permission rule](#permission-rules).
651
648
 
652
649
  A request waits for an answer for 5 minutes, or `AGENT_LOOKOUT_ANSWER_WAIT` seconds, and then leaves the dashboard, and the prompt in the session decides. It also leaves when the session stops waiting, or ends. With Agent Lookout not running, the plugin does nothing and Claude Code asks as usual.
653
650
 
@@ -669,12 +666,71 @@ The plugin is one hook that Claude Code runs as it is about to ask you for permi
669
666
  /plugin install agent-lookout@agent-lookout
670
667
  ```
671
668
 
672
- From a shell, `claude plugin marketplace add Olanetsoft/agent-lookout` and `claude plugin install agent-lookout@agent-lookout` do the same. Claude Code records the plugin in its own settings, and runs its hook wherever Claude Code runs: in a terminal, in VS Code and in the desktop app. Agent Lookout itself writes nothing under `~/.claude`. To take it away, run `/plugin uninstall agent-lookout@agent-lookout`. The hook needs `curl`, which macOS has, and most Linux systems do.
669
+ From a shell, `claude plugin marketplace add Olanetsoft/agent-lookout` and `claude plugin install agent-lookout@agent-lookout` do the same. Claude Code records the plugin in its own settings, and runs its hook wherever Claude Code runs: in a terminal, in VS Code and in the desktop app. Agent Lookout itself writes nothing under `~/.claude`. To take it away, run `/plugin uninstall agent-lookout@agent-lookout`. The hook needs `curl`, which macOS has, and most Linux systems do. On Windows the plugin does nothing, and Claude Code asks as usual: [On Windows](#on-windows) says why.
673
670
 
674
671
  Settings, under Permission prompts, says whether the plugin's requests reach Agent Lookout. When a Claude Code session waits for permission and no request comes, it says the plugin may not be installed.
675
672
 
676
673
  To take Allow and Deny away altogether, start Agent Lookout with `AGENT_LOOKOUT_ANSWER=off`. [PRIVACY.md](../PRIVACY.md#the-claude-code-plugin-and-permission-prompts) says what the plugin sends, what Agent Lookout keeps and what it checks before it answers.
677
674
 
675
+ ## Permission rules
676
+
677
+ Most permission prompts are routine. A permission rule answers one kind for you, every time: always allow, always ask or always deny. Rules answer Claude Code sessions with the [plugin](#install-the-plugin) installed, the one agent Agent Lookout can answer for. Codex records no approval waits, and a status file holds nothing to answer through, so neither has rules. There are none until you add one, and with none, nothing is answered on its own. No rule answers a prompt on Windows, as [On Windows](#on-windows) says.
678
+
679
+ The Permission rules card in Settings, under Permission prompts, lists the rules in their order. To add one, choose Allow, Ask or Deny, name a tool and, for Bash, a command, then press Add rule. Each rule has Move up, Move down, Edit and Remove, each a button you can reach with Tab, and focus stays with the rule you moved. Every change goes to Agent Lookout, which checks the rule again, saves the list in `~/.agent-lookout/settings.json`, or the file `AGENT_LOOKOUT_SETTINGS_FILE` names, and puts it in force for the next request. The change is made to the list the file holds then, and a second copy running beside it, such as the Mac app beside `npx agent-lookout`, has it by its next poll, as [Time rules](#time-rules) says of the file. The card shows what was saved: a change that was not saved leaves the list as it was, and the line under Add rule, or for a rule's own buttons a note above the rules, says why. A list holds 100 rules at most.
680
+
681
+ A rule names a tool as Claude Code names it, the whole name in the same capitals, such as `Bash`, `Read`, `WebFetch` or `mcp__docs__search`, or `*` for every tool. A deny or an ask rule can also name an MCP server alone, `mcp__docs`, for every tool of that server, as Claude Code's own rules do. A rule for Bash can also name a command: one command exactly, `npm test`, or a prefix written as Claude Code writes one, `npm test:*`, which matches every command whose first words are its words, each word whole. `npm test:*` matches `npm test` and `npm test --watch`, and not `npm testing` or `npm run test`. A command in a rule is plain ASCII, one space between words, with no `;`, `&`, `|`, `<`, `>`, parentheses, quotes, backslashes, `$`, braces, and no `*` but the `:*` that ends a prefix.
682
+
683
+ | Decision | What happens to a request it matches |
684
+ | -------- | --------------------------------------------------------------------------------------- |
685
+ | Deny | Agent Lookout denies it at once, as Deny does, and Claude carries on without it. |
686
+ | Ask | The prompt waits for you, as with no rule, even when an allow rule matches it too. |
687
+ | Allow | Agent Lookout allows it at once, as Allow does: Claude Code runs it without asking you. |
688
+
689
+ Deny goes first, then ask, then allow, as in Claude Code's own rules. A deny rule that matches decides, whatever else matches, and an ask rule wins over an allow rule, however much narrower the allow rule is. Of the rules of one kind, the first in the list is the one that decides, and the one the log names. A request no rule matches waits for you, as before.
690
+
691
+ An allow rule is narrower than it looks, on purpose:
692
+
693
+ - It answers only what you could allow by hand on the dashboard at that moment, by the same check: never an edit, a new file, a plan or a question, and never a request too long to show whole, with characters that cannot be shown as they are, with more than two blank lines in a row, or a command with right-to-left letters.
694
+ - For Bash, it answers one plain command only: words of ASCII letters, digits and `_ - . , / : = + @ %`, one space apart. A line with `;`, `&`, `|`, `&&`, `||` or a line break, a backtick, `$( )`, `$VAR` or `${ }`, a redirection such as `>`, `>>` or `<`, a here-document, parentheses or braces, a glob such as `*` or `?`, quotes, a backslash, a `~`, a `#`, a `!`, a tab, a space at either end or two in a row, a variable set before the command, or a letter outside plain ASCII is left to you. So `npm test:*` never answers `npm test && curl example.com | sh`.
695
+ - For Bash, a request with any input but the command, its description, `timeout` and `run_in_background`, such as one asking to run outside Claude Code's sandbox, is left to you.
696
+ - For any tool but Bash, it allows every request of that tool, whatever it asks: an allow rule for `Read` lets Claude read any file, one for `WebFetch` fetch any address, and one for an MCP tool use it with any input. The card says so as you write one.
697
+ - It cannot name every tool, every Bash command, every tool of an MCP server, an edit, a plan or a question: the card refuses such a rule.
698
+ - It cannot begin with a program whose work is to run another command, such as a shell, `sh`, `bash` or `zsh`, or `env`, `sudo`, `xargs`, `exec`, `eval`, `source`, `time`, `nice`, `nohup` or `timeout`, a program that fetches and runs another, `npx`, `pnpx`, `bunx` or `uvx`, `nix-shell`, PowerShell and `cmd`, and on a Mac `caffeinate` and `xcrun`. `sudo:*` or `bash -c:*` would let Claude Code run anything without asking you, so the card refuses it, whatever its capitals or folder, with `.exe` or `.com` after it, as a Windows program is run from WSL, and with a version or anything else after the name that does not begin with a letter, such as `bash5`, `ksh93` or `zsh-5.9`. `COMMAND_RUNNERS` in `src/core/permission-rules/commandWords.ts` is the whole list.
699
+ - It cannot begin with a program that sends requests or runs the code it is given. Claude can reach Agent Lookout itself, on this computer's own address, like any program you run, so `curl:*` or `python:*` would let it add a rule here, or answer its own prompt, without asking you. These are the network clients `curl`, `wget`, `nc`, `ncat`, `netcat`, `socat`, `telnet`, `http`, `https`, `httpx`, `xh`, `xhs`, `grpcurl`, `websocat`, `aria2c`, `ftp`, `sftp`, `scp`, `ssh`, `lynx`, `links`, `elinks` and `w3m`, and the interpreters `node`, `nodejs`, `deno`, `bun`, `tsx`, `ts-node`, `esno`, `vite-node`, `zx`, `python`, `ipython`, `bpython`, `pypy`, `ruby`, `irb`, `perl`, `php`, `lua`, `luajit`, `java`, `jshell`, `jrunscript`, `kotlin`, `scala`, `groovy`, `clojure`, `clj`, `dart`, `elixir`, `iex`, `erl`, `escript`, `ghci`, `runghc`, `runhaskell`, `ocaml`, `racket`, `guile`, `sbcl`, `raku`, `rakudo`, `R`, `Rscript`, `julia`, `swift`, `osascript`, `tclsh`, `wish`, `expect`, `awk`, `gawk`, `mawk` and `nawk`, whatever their capitals or folder, with `.exe` or `.com` after them, and with anything after the name that begins with a character other than a letter, such as a digit, a dot, a dash or an underscore, as in `python3.12`, `python3-intel64`, `nc.openbsd` or `perl5.34`. That also refuses a few that only share the name, such as `ssh-keygen` or `node_exporter`. An exact command is refused too, such as `python -m pytest`. Answer such commands by hand, or allow a script the project owns, such as `./scripts/test.sh`, knowing Claude can edit it. `NETWORK_CLIENTS_AND_INTERPRETERS` in the same file is the whole list.
700
+ - A rule for a package manager or a build tool names its subcommand: `npm test:*`, never `npm:*` or `npm --yes:*`. These are `npm`, `pnpm`, `yarn` and `yarnpkg`, `bundle` and `bundler`, `cargo`, `go`, `dotnet`, `uv`, `pipx`, `poetry`, `nix`, `docker`, `docker-compose` and `podman`. Matched word by word, the rule answers that subcommand and no other, whatever shortened form or second name the tool would also take, so `npm test:*` never answers `npm exe`, `npm innit` or `npm -- exec`.
701
+ - It cannot name a subcommand that fetches code, or runs code it is pointed at, and no command it answers holds one, wherever it comes before the arguments begin: `npm exec`, `x`, `init`, `innit`, `create` and `explore`, and for npm any start of their names two letters or longer, such as `exe`; `pnpm dlx`, `exec`, `create` and `x`; `yarn` and `yarnpkg` `dlx`, `exec`, `create` and `node`; `bundle` and `bundler` `exec`, `e`, `ex` and `exe`; `cargo run` and `r`; `go run`, `generate` and `tool`; `dotnet run`, `exec` and `tool`; `uv run` and `tool`; `pipx run`; `poetry run`; `nix run`, `shell` and `develop`; `docker` and `podman` `run`, `exec`, `create`, `start`, `build`, `buildx`, `compose` and `container`; and `docker-compose up`, `run`, `exec`, `start` and `create`. `SUBCOMMANDS_THAT_RUN_CODE` in the same file is the whole list.
702
+ - It never answers a command with an option through which its program runs another: `git -c`, `--config-env`, `--exec-path`, `--upload-pack` and `--receive-pack`, `go -exec`, `-toolexec` and `-vettool`, and `npm --script-shell`, `--node-options` and `--call`, with one dash or two and with or without a value.
703
+ - It never answers a command that names Agent Lookout's own files or a folder above them: a word with `.agent-lookout` in it, in any capitals; a path, or an option's value such as `--directory=…`, that is the settings file, the socket or any folder above them, up to your home folder and `/`, also when written with `..`, `//` or `/./`; the settings file's own name where a setting put it outside `.agent-lookout`; and any path under `/.vol`, which reaches a file by number on a Mac. Paths are also read from the session's folder, so `../..` from a project is your home folder, and no command of a session whose folder is your home folder, or a folder above it, is answered at all. So `sed:*` never answers `sed -i s/deny/allow/` on the settings file, nor `rsync:*` a copy into your home folder. The card refuses a rule that names the folder.
704
+
705
+ The rules are read whole or not at all. A saved list that holds a rule Agent Lookout refuses, such as an allow rule for `curl` saved before it was refused, is read as no rule at all, as a list it cannot read is: none of its rules is used, and a note on the card says so. Changing a rule in Settings then writes the list again with only the rules the card shows. To keep the others, take the refused rule out of the file instead, and they are in force again by the next poll.
706
+
707
+ An allow rule trusts what it names, and those lists cannot hold every program that can run another or send a request. A prefix trusts the program with every word after its own, but for the subcommands, options and paths above: `git:*` allows `git config`, which can set a program for git to run later, and `npm install:*` runs the install scripts of every package it fetches, as `pip install:*` does. A rule for a file Claude can edit, such as `./scripts/test.sh`, or for a program that runs one, such as `npm test` or `make`, lets Claude run whatever it writes into that file, and what it writes can reach Agent Lookout too: it can add a rule here, or answer Claude's own prompt, without asking you, and the rule it adds stays. Name the words that make a command one you want run without asking, `git status` or `npm test`, rather than the program alone, and keep allow rules for what you would allow every time. Reading the words cannot catch what a command does with no word that names it: an archive or a patch that holds full paths, a script or a settings file Claude can edit, a package's install scripts. Claude Code's sandbox, which an allow rule never lets a command leave, is the fence for those.
708
+
709
+ A deny or an ask rule only holds a request back, so it matches more loosely. A rule for a tool matches every request of that tool. A rule for a command matches when any command in the line has its words:
710
+
711
+ - After `;`, `&&`, `|` or a line break, inside `$( )` or backticks, after `then`, `else` or `do`, with quotes, backslashes and the `$` of `$'…'` taken out, and variables set before it passed over, so `rm:*` denies `cd build && rm -rf dist`.
712
+ - The program by its name, whatever its capitals or its folder, so `rm:*` denies `RM -rf dist` and `/bin/rm -rf dist`. A Mac's disk finds `RM` as `rm`.
713
+ - The rule's other words in order, with any words between them, so an option before them does not step around the rule: `git push:*` holds back `git -C . push` and `git --no-pager push`, and `git push --force:*` holds back `git push origin main --force`. That also holds back `git commit -m push`, which asks or denies more than the rule says, never less. An exact command, `git push`, holds back no command with a word after `push`.
714
+ - After a program that runs another, such as `sudo`, `xargs`, `env`, `time` or `bash -c`, so `rm:*` denies `ls | xargs rm` and `sudo rm -rf dist`.
715
+
716
+ It still goes by the words as written, in the first 4,000 characters, which is what the dashboard shows. A command that a program not in that list runs, such as `npm exec rm`, or that a script, an alias or a git setting runs, is not caught, and neither is a letter that only looks like another. A deny rule here is a convenience, not a fence. Claude Code's own deny rules and its sandbox are the places to fence a command off.
717
+
718
+ A rule answers by the same path as Allow and Deny. Agent Lookout waits for the session's file to say it waits for permission, reads it again, answers only in that same wait, and hands the plugin one of the same two answers. It never rewrites a command and never saves a rule in Claude Code. The prompt in the session may show for the moment that takes, and Claude Code's file can say the session waits for a second or two after the answer. Agent Lookout knows it answered, or that a rule is about to, so it takes that wait as over: no notification, email, webhook post or reminder says the session needs you, the Events log shows no wait, only the answer, and [Waits](#waits) does not count it. Only a read of the file in the instant before the plugin's request reaches Agent Lookout can still see the wait begin. Nor does the session light the lamp or come into the Needs you panel, its count, the tab's title, or the Dock icon and menu bar of the Mac app, and the history, its charts and the Timeline show no wait. For that second or two it is listed with the working sessions as Answered, and [`agent-lookout status`](#in-the-terminal) and [`agent-lookout mcp`](#for-your-agents) count it as working too. With `AGENT_LOOKOUT_ANSWER=off`, no rule answers anything, and the card says so.
719
+
720
+ Each answer a rule gives is an event in the Events log, such as "checkout-flow had Bash allowed by the rule Bash(npm test:*)", and the card lists the last 100 since Agent Lookout started, under Recent automatic answers, with the session, the time, the tool, the decision and the rule. Neither holds the command or anything else of what was asked. The Events log is kept in the [history](#history), so its rule answers are kept for 8 days, as every event is. [PRIVACY.md](../PRIVACY.md#the-settings-file) says what the rules file holds.
721
+
722
+ ### Claude Code's own rules come first
723
+
724
+ A deny or an allow rule in Claude Code's own settings, such as one in `~/.claude/settings.json` or in a project's `.claude/settings.json`, decides before Agent Lookout is asked. Claude Code runs the plugin's hook only when it shows you a permission prompt. A call its own rules already deny or allow shows no prompt, so the hook does not run and no rule here is consulted. A deny rule there wins over an allow rule here, and an allow rule there wins over a deny rule here.
725
+
726
+ An ask rule there is different. It is what makes Claude Code show the prompt, and so what runs the hook. An allow rule here then answers that prompt as Allow would. An ask rule in Claude Code's settings does not stop an allow rule here. To be asked every time, add the ask rule here.
727
+
728
+ How this was checked, on 2026-10-07, and only this far:
729
+
730
+ - Claude Code's [hooks reference](https://code.claude.com/docs/en/hooks) says a PermissionRequest hook runs when the user is shown a permission dialog, and also in a session started with `-p`, where Claude Code would otherwise deny the call. Its [permissions page](https://code.claude.com/docs/en/permissions) says rules are evaluated deny, then ask, then allow, and that an ask rule prompts for confirmation. The same page says Claude Code evaluates deny and ask rules whatever a PreToolUse hook returns. That is about PreToolUse, which runs before the prompt; neither page says the same of a PermissionRequest hook's allow.
731
+ - The Claude Code 2.1.292 program, read with `strings` and not run, describes the PermissionRequest hook as running "When a permission dialog is displayed".
732
+ - The tests run the plugin's own hook script against Agent Lookout's socket, with no Claude Code. No real Claude Code session was started to watch a deny rule there keep the hook from running, or an ask rule there let it run. If Claude Code changes when it runs the hook, this section changes with it.
733
+
678
734
  ## Keyboard
679
735
 
680
736
  Every view answers to a few keys. Press `?` to see them all.
@@ -728,7 +784,7 @@ checkout-flow Waiting for permission 4m 12s
728
784
  search-indexing Asked you a question 31s
729
785
  ```
730
786
 
731
- When nothing needs you it prints one line, such as `Nothing needs you · 4 working · 2 idle`. Sessions are counted as the Overview counts them: a stale session is counted as stale and not as idle, and stale is left out while there are none. A wait whose start is not known shows a dash. A name is cut at 40 columns, and anything in it that a terminal would act on, such as an escape sequence or a line break, is taken out before it is printed. Until an agent has been read, it says so in place of the counts, and exits with 2.
787
+ When nothing needs you it prints one line, such as `Nothing needs you · 4 working · 2 idle`. Sessions are counted as the Overview counts them: a stale session is counted as stale and not as idle, and a session whose permission prompt you or a [permission rule](#permission-rules) just answered as working and not as needing you. Stale is left out while there are none. A wait whose start is not known shows a dash. A name is cut at 40 columns, and anything in it that a terminal would act on, such as an escape sequence or a line break, is taken out before it is printed. Until an agent has been read, it says so in place of the counts, and exits with 2.
732
788
 
733
789
  | Option | What it does |
734
790
  | ----------------- | ----------------------------------------------------------------------------------------------- |
@@ -871,7 +927,7 @@ In a clone without `npm link`, give the path to yours:
871
927
  claude mcp add agent-lookout -- /path/to/agent-lookout/bin/agent-lookout.mjs mcp
872
928
  ```
873
929
 
874
- Claude Code adds it for the folder you run that in. Put `--scope user` after `add` to have it in every folder. Any other app that takes MCP servers starts it with the same command: the program `npx` with the arguments `-y`, `agent-lookout` and `mcp`, or `agent-lookout` with the one argument `mcp`. Many take it as JSON in this shape:
930
+ Claude Code adds it for the folder you run that in. Put `--scope user` after `add` to have it in every folder. `claude mcp list` then lists `agent-lookout`. Any other app that takes MCP servers starts it with the same command: the program `npx` with the arguments `-y`, `agent-lookout` and `mcp`, or `agent-lookout` with the one argument `mcp`. Many take it as JSON in this shape:
875
931
 
876
932
  ```json
877
933
  {
@@ -925,7 +981,7 @@ Each answers in JSON. `sessions_needing_you` gives, for example:
925
981
  }
926
982
  ```
927
983
 
928
- Times are in ISO 8601. A field that is not known is `null`. A session on [another machine](#another-machine-over-ssh) has that machine's name in `machine`, and the summary says it after the name, `"checkout-flow" on devbox`: its `folder` is on that machine, not this one. `counted` is `false` until Agent Lookout has read an agent, when an empty list says nothing, and the summary says so. When an agent's sessions could not be read, such as Claude Code's, the summary names that agent and says its sessions are not counted. `pullRequest` is `{ "number": 51, "checks": "failing" }` for a branch whose pull request Agent Lookout found, with `checks` one of `failing`, `pending`, `passing` and `none`, and `null` for every other session and whenever `AGENT_LOOKOUT_PULL_REQUESTS` is not `on`. It never holds the title, which someone else may have written. `quietFor` is how long a working session's agent has written nothing to the file it is read from, given whenever the agent's source gives that time. From 5 minutes it is what the Sessions list shows as [Quiet for](#quiet-for). Not every agent can report a wait: a Codex session waiting for your approval shows as working. `sources` says which can report what.
984
+ Times are in ISO 8601. A field that is not known is `null`. A session on [another machine](#another-machine-over-ssh) has that machine's name in `machine`, and the summary says it after the name, `"checkout-flow" on devbox`: its `folder` is on that machine, not this one. `counted` is `false` until Agent Lookout has read an agent, when an empty list says nothing, and the summary says so. When an agent's sessions could not be read, such as Claude Code's, the summary names that agent and says its sessions are not counted. `pullRequest` is `{ "number": 51, "checks": "failing" }` for a branch whose pull request Agent Lookout found, with `checks` one of `failing`, `pending`, `passing` and `none`, and `null` for every other session and whenever `AGENT_LOOKOUT_PULL_REQUESTS` is not `on`. It never holds the title, which someone else may have written. `quietFor` is how long a working session's agent has written nothing to the file it is read from, given whenever the agent's source gives that time. From 5 minutes it is what the Sessions list shows as [Quiet for](#quiet-for). A Claude Code session whose permission prompt you or a [permission rule](#permission-rules) just answered is `working`, with `since` as `null`, for the second or two before Claude Code says what it does next, and `sessions_needing_you` leaves it out. Not every agent can report a wait: a Codex session waiting for your approval shows as working. `sources` says which can report what.
929
985
 
930
986
  A session's name, its agent's name, its folder and its branch are written by other programs, such as an agent that writes a [status file](#your-own-agents). The tools say so in their descriptions and in each answer, the summary puts each name in quotation marks, and anything a terminal would act on is taken out of them, as `agent-lookout status` does, and so is any character that shows nothing, in which words could be hidden from you but not from a model. An agent should still treat them as data, never as instructions.
931
987
 
@@ -996,8 +1052,10 @@ def report(status, reason=None):
996
1052
 
997
1053
  atexit.register(lambda: status_file.unlink(missing_ok=True))
998
1054
  # On kill or a closed terminal, end the program as on Ctrl-C, so the file is deleted then too.
999
- for signum in (signal.SIGTERM, signal.SIGHUP):
1000
- signal.signal(signum, lambda number, frame: sys.exit(128 + number))
1055
+ # Windows has no SIGHUP.
1056
+ for name in ("SIGTERM", "SIGHUP"):
1057
+ if hasattr(signal, name):
1058
+ signal.signal(getattr(signal, name), lambda number, frame: sys.exit(128 + number))
1001
1059
 
1002
1060
  report("working") # As the work starts.
1003
1061
  time.sleep(8)
@@ -1115,7 +1173,7 @@ ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=15 -o
1115
1173
  It forwards that port on this computer's loopback to Agent Lookout's on the other machine's loopback, and runs no command there. ssh signs in with your ssh config and your agent. BatchMode stops it asking anything. `ControlMaster=no` and `ControlPath=none` make it a connection of its own every time, even when your ssh config shares connections, so it never joins an ssh you have open, never leaves one running in the background, and its forward ends when it ends.
1116
1174
 
1117
1175
  - Every 2 seconds, Agent Lookout asks the other machine's Agent Lookout for two things through the tunnel, `GET /api/health` and `GET /api/sessions`, and nothing else. This computer installs nothing there and leaves nothing there. The Agent Lookout you started there keeps its own Events log and history, as it does on any machine, unless it was started with `AGENT_LOOKOUT_HISTORY=off`.
1118
- - Its sessions appear in the same lists as this computer's, each with the machine's name beside its own, and so do its waits and its events in the charts and the Events log, so two sessions of one name, one here and one there, are told apart. A waiting one is in Needs you and is counted there, and its notification names the machine after the session, `docs-site on devbox`. What a waiting session is asking shows when the other machine sends it, as it does, unless `AGENT_LOOKOUT_WAITING_TEXT=off` is set here, which turns it off for every machine. `agent-lookout status` and `agent-lookout mcp` name the machine too.
1176
+ - Its sessions appear in the same lists as this computer's, each with the machine's name beside its own, and so do its waits and its events in the charts and the Events log, so two sessions of one name, one here and one there, are told apart. A waiting one is in Needs you and is counted there, and its notification names the machine after the session, `docs-site on devbox`. A prompt answered there, by Allow, Deny or a permission rule set there, is taken as answered here too, as that machine's Agent Lookout says: it is listed as Answered and sends nothing here. As on this computer, only a read in the instant before the plugin's request reached Agent Lookout there can still see the wait begin. What a waiting session is asking shows when the other machine sends it, as it does, unless `AGENT_LOOKOUT_WAITING_TEXT=off` is set here, which turns it off for every machine. `agent-lookout status` and `agent-lookout mcp` name the machine too.
1119
1177
  - Its sessions have no Jump, no Stop and no Allow or Deny. Each acts on this computer only.
1120
1178
  - The [time rules](#time-rules) set on this computer cover its sessions too. A wait there is reminded of and held through quiet hours here by the rules set here, its reminder and its line in the summary naming the machine as its notification does, and a session there idle longer than Mark idle sessions stale says here is counted as stale here. The rules set on the other machine go with what that machine sends itself, and are not read here.
1121
1179
  - Sources has a card for each machine. It says Connected, with the version of Agent Lookout there and what each of its sources is doing; Connecting, while ssh signs in the first time; or Not connected, with the reason and what to do: ssh was not found, the machine turned ssh away and ssh's own words for why, the connection dropped, no Agent Lookout is answering on its port, or what answers there is not Agent Lookout or sends a list this version cannot read. A first connection that has not answered within 10 seconds is Not connected, still connecting, until ssh connects or gives up. [What each agent can report](#what-each-agent-can-report) has a row for each agent found there, such as Claude Code on devbox.
@@ -1149,9 +1207,38 @@ If the clock is set by more than a minute while a Claude Code session runs, that
1149
1207
 
1150
1208
  Agent Lookout runs `ps` from `/usr/bin` or `/bin`. Where it is not there, as on NixOS, no session in tmux has a Jump button, and a registry file left behind by a session that crashed shows as a session if another program is given its process ID.
1151
1209
 
1210
+ ## On Windows
1211
+
1212
+ Agent Lookout is installed and started on Windows as on a Mac, with `npx agent-lookout` in PowerShell or the Command Prompt, and reads the same folders in your user folder, `%USERPROFILE%`: `.claude\sessions`, `.codex` and `.agent-lookout\sessions`. It keeps its history and its settings in `.agent-lookout` there too. A path in your user folder is shown with `/`, as `~/.claude/sessions`. CI runs the tests on Windows, less those of what Windows does not have, such as the POSIX signals and the Unix socket below, and there it also starts the built app with `npm start` and checks that it lists a Claude Code session, Codex sessions and a session from a status file, each with the right status. No one has yet used it on a Windows desktop.
1213
+
1214
+ These are the same as on a Mac:
1215
+
1216
+ - The dashboard, its notifications while a dashboard tab is open, [email](#email), the [webhook](#webhook), `agent-lookout status` and `agent-lookout mcp`.
1217
+ - The `claude agents --json --all` command, run every 30 seconds, which lists the background jobs that have finished or failed.
1218
+ - Jump for a session in VS Code, which is a `vscode://` link, where VS Code has registered itself to open those links.
1219
+ - `--open`, which hands the address to Windows' own `rundll32.exe` to open in your default browser.
1220
+
1221
+ The `claude` command is looked for on your `PATH` as `claude.exe`, then in `%USERPROFILE%\.local\bin`, where Claude Code's own installer puts it, then in npm's folder, `%APPDATA%\npm`. Agent Lookout runs a program only, never a script that needs a shell. So for the `claude.cmd` that npm makes, it runs the `claude.exe` that the script runs, which npm keeps under the same folder, in `node_modules\@anthropic-ai\claude-code\bin`. Claude Code installed from npm before version 2.1.113 has no `claude.exe`, and has no `claude agents` command either: its sessions are read from `.claude\sessions` alone. `AGENT_LOOKOUT_CLAUDE_BIN` must name a program, a `.exe` or a `.com`.
1222
+
1223
+ These are not on Windows:
1224
+
1225
+ - [Stop](#stop-a-session), and ending the [sessions left running](#sessions-left-running). Agent Lookout stops a session only once `ps` has confirmed, by its start time, that the process is still the session's, and it stops it with a POSIX signal. Windows has neither, and Claude Code there records no start time to compare. No session has Stop, and the routes that stop one are not there.
1226
+ - [Allow and Deny](#answer-a-permission-prompt), and the [permission rules](#permission-rules). The plugin's hook is a POSIX sh script, and it reaches Agent Lookout through a Unix socket. Agent Lookout opens no socket on Windows, so an installed plugin does nothing, no rule answers a prompt, and Settings, under Permission prompts, says so.
1227
+ - [Jump to a tmux pane](#a-session-in-tmux), or [to a tab of Terminal or iTerm2](#a-session-in-a-tab-of-terminal-or-iterm2). tmux is never run on Windows, and the two terminals are Mac apps.
1228
+ - [Resume](#resume-a-session). The command it copies is for sh, bash and zsh, and is given only for a folder that starts with `/` and holds no backslash, so no session on Windows has Resume.
1229
+ - Notifications with no dashboard tab open, which Agent Lookout shows itself with `osascript` on a Mac alone. Keep a dashboard tab open to be notified.
1230
+
1231
+ On Windows, [What each agent can report](#what-each-agent-can-report) says No for Stop and Answer and Partly for Jump, with why. The others have no column there.
1232
+
1233
+ With no `ps`, Agent Lookout cannot tell a registry file left behind by a Claude Code session that crashed from a live one when Windows has given its process ID to another program. Such a file shows as a session until that program ends.
1234
+
1235
+ [Another machine over SSH](#another-machine-over-ssh) and [pull requests](#pull-requests) run the `ssh.exe` and the `gh.exe` on your `PATH`, such as the OpenSSH client that comes with Windows. CI checks pull requests on Windows against a stand-in `gh.exe`, and neither has yet been tried there with the real program.
1236
+
1237
+ The settings in this guide are written for a shell such as zsh or bash. In PowerShell, set one before the command, `$env:AGENT_LOOKOUT_CLAUDE_FEED = "off"; npx agent-lookout`, and in the Command Prompt, run `set AGENT_LOOKOUT_CLAUDE_FEED=off` and then `npx agent-lookout`.
1238
+
1152
1239
  ## What it does not do yet
1153
1240
 
1154
- It does not resume a session itself: for a Claude Code session that has ended, [Resume](#resume-a-session) copies the command that does, and you run it. It cannot send a session a message. It [stops](#stop-a-session) a Claude Code session only when you press Stop and confirm, and not one in the desktop app, a Codex session or a session from a status file. It [answers a permission prompt](#answer-a-permission-prompt) only for a Claude Code session with the plugin installed, allows or denies that one request only, and offers Deny alone for an edit, a plan or a question, and for a request it cannot show whole and as it is. An agent through [`agent-lookout mcp`](#for-your-agents), whose tools only read, can do none of these. It covers Claude Code and Codex, and any agent that writes a [status file](#your-own-agents), on this computer and on [another machine](#another-machine-over-ssh) running Agent Lookout that you reach over SSH. Cloud sessions, Codex cloud tasks and browser chats do not appear. [What each agent can report](#what-each-agent-can-report) has a table of what each agent can and cannot show.
1241
+ It does not resume a session itself: for a Claude Code session that has ended, [Resume](#resume-a-session) copies the command that does, and you run it, on macOS and Linux. It cannot send a session a message. It [stops](#stop-a-session) a Claude Code session only when you press Stop and confirm, and not one in the desktop app, a Codex session, a session from a status file or any session on Windows. It [answers a permission prompt](#answer-a-permission-prompt) only for a Claude Code session with the plugin installed, and not on Windows, when you press Allow or Deny or a [permission rule](#permission-rules) you added matches, allows or denies that one request only, and offers Deny alone for an edit, a plan or a question, and for a request it cannot show whole and as it is. An allow rule never allows what you could not allow by hand, or a Bash line of more than one plain command. An agent through [`agent-lookout mcp`](#for-your-agents), whose tools only read, can do none of these. It covers Claude Code and Codex, and any agent that writes a [status file](#your-own-agents), on this computer and on [another machine](#another-machine-over-ssh) running Agent Lookout that you reach over SSH. Cloud sessions, Codex cloud tasks and browser chats do not appear. [What each agent can report](#what-each-agent-can-report) has a table of what each agent can and cannot show.
1155
1242
 
1156
1243
  A notification, an email or a post is sent for four events: a session starting to wait, finishing, failing or ending, and, with the [time rules](#time-rules) on, also a reminder of a long wait and a summary of quiet hours. Only Claude Code sessions and sessions from a status file can be seen waiting. A Claude Code session that is not a background job does not say how it ended, so it sends Ended, never Finished or Failed. A Claude Code background job that starts and ends between two runs of the `claude` command, which is run every 30 seconds, leaves the list before the command lists it as finished, so it too sends Ended. A session from a Codex older than 0.155 is never shown as finished, so it sends Ended when it leaves the list, a day after it was last used. With no dashboard tab open, notifications are shown on a Mac only. Those come from Script Editor, unless Agent Lookout runs as the [desktop app](#desktop-app), cannot open the session, and are not cleared when the session moves on.
1157
1244
 
@@ -1190,12 +1277,12 @@ AGENT_LOOKOUT_CLAUDE_FEED=off npx agent-lookout
1190
1277
  | `AGENT_LOOKOUT_STATUS_DIR` | A folder of [status files](#your-own-agents) to read in place of `~/.agent-lookout/sessions`. |
1191
1278
  | `AGENT_LOOKOUT_HISTORY` | Set to `off` and Agent Lookout keeps the Events log and the charts in memory only and writes nothing to disk, so they start empty each time it starts. See [History](#history). |
1192
1279
  | `AGENT_LOOKOUT_HISTORY_DIR` | A folder to keep the [history](#history) in, in place of `~/.agent-lookout/history`. |
1193
- | `AGENT_LOOKOUT_SETTINGS_FILE` | A file to keep the [time rules](#time-rules) in, in place of `~/.agent-lookout/settings.json`. |
1280
+ | `AGENT_LOOKOUT_SETTINGS_FILE` | A file to keep the [time rules](#time-rules) and the [permission rules](#permission-rules) in, in place of `~/.agent-lookout/settings.json`. |
1194
1281
  | `AGENT_LOOKOUT_NOTIFICATIONS` | Set to `on` and, on a Mac, Agent Lookout shows notifications of waits itself from the moment it starts. A dashboard page that has notifications off turns them off again. Anywhere else it prints a line saying it shows none itself. |
1195
1282
  | `AGENT_LOOKOUT_TMUX` | Set to `off` and Agent Lookout never runs `tmux`. Sessions in tmux are still listed, without a Jump button. |
1196
1283
  | `AGENT_LOOKOUT_TERMINAL_JUMP` | Set to `off` and Agent Lookout never looks for, or brings forward, a tab of Terminal or iTerm2. Sessions there are still listed, without a Jump button. |
1197
1284
  | `AGENT_LOOKOUT_STOP` | Set to `off` and Agent Lookout never stops a session: no session has a Stop button, Left running has no End all…, and the server answers no request to stop one. |
1198
- | `AGENT_LOOKOUT_ANSWER` | Set to `off` and Agent Lookout never answers a permission prompt: it opens no socket for the plugin, no session shows Allow or Deny, and the server answers no request to answer one. |
1285
+ | `AGENT_LOOKOUT_ANSWER` | Set to `off` and Agent Lookout never answers a permission prompt: it opens no socket for the plugin, no session shows Allow or Deny, no [permission rule](#permission-rules) answers one, and the server answers no request to answer one. |
1199
1286
  | `AGENT_LOOKOUT_ANSWER_SOCKET` | The socket the [plugin](#install-the-plugin) sends permission requests to, in place of `~/.agent-lookout/answer.sock`. Set the same in the environment Claude Code starts with, so its hook finds it. With `AGENT_LOOKOUT_CLAUDE_HOME` set, nothing is answered unless this is set too. |
1200
1287
  | `AGENT_LOOKOUT_ANSWER_WAIT` | How many seconds a permission request waits for Allow or Deny before the prompt in the session is left to decide, from 5 to 540. The default is 300. |
1201
1288
  | `AGENT_LOOKOUT_EMAIL_TO` | The one address emails go to. With `AGENT_LOOKOUT_SMTP_URL` set too, it turns [email](#email) on. |
@@ -1223,60 +1310,17 @@ npm run dev -- --port 5180
1223
1310
 
1224
1311
  ## Run the built version
1225
1312
 
1226
- In a clone, `npm run dev` runs Agent Lookout with its development tools. To run it from built files instead:
1227
-
1228
- ```sh
1229
- npm run build
1230
- npm start
1231
- ```
1232
-
1233
- `npm start` prints `Agent Lookout is running at http://127.0.0.1:4777`. Open that address. To check it from a terminal:
1234
-
1235
- ```sh
1236
- curl -s http://127.0.0.1:4777/api/health
1237
- ```
1238
-
1239
- It prints `{"ok":true,"version":"0.2.1"}`, or a later version number. If you run `npm start` before `npm run build`, it stops and tells you to build first. After you pull new code, run `npm run build` again.
1313
+ In a clone, `npm run build` and then `npm start` run Agent Lookout from built files, at `http://127.0.0.1:4777`. [From the repository](INSTALL.md#from-the-repository) has the steps.
1240
1314
 
1241
1315
  ## Start it with one command
1242
1316
 
1243
- `agent-lookout` on its own starts Agent Lookout from the built files, as `npm start` does: the dashboard and its API at `http://127.0.0.1:4777`, on this computer only. It prints `Agent Lookout is running at http://127.0.0.1:4777` and keeps running until you press Ctrl+C. `agent-lookout start` does the same.
1244
-
1245
- | Option | What it does |
1246
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1247
- | `--port <number>` | Listens on this port in place of 4777, such as `--port 4778`. `AGENT_LOOKOUT_PORT` does the same, and `--port` wins when both are given |
1248
- | `--open` | Opens the address in your default browser once it is listening, with `open` on macOS or `xdg-open` on Linux. If that cannot be done, it prints one line with the address and keeps running |
1249
- | `--help` | Prints the options of `agent-lookout`, `agent-lookout status` and `agent-lookout mcp` |
1250
- | `--version` | Prints the version of Agent Lookout, such as `0.2.1`, and nothing else |
1251
-
1252
- Every other setting under [Settings you can change](#settings-you-can-change) works with it as with `npm start`. It ends with 0 when you press Ctrl+C, and with 1 when it cannot start, such as when the port is in use. Under `npx agent-lookout`, the shell reports the interrupt instead, as exit code 130.
1253
-
1254
- Agent Lookout is on npm, so `npx agent-lookout` starts it with nothing to clone or install first, and `npx agent-lookout --open` opens it in the browser as well. npm downloads the package the first time, under 2 MB with what it needs and about 4 MB once installed, and keeps it in its cache. The package holds the built dashboard and the collector as plain JavaScript, so it needs Node.js and nothing else. Each time, npx asks npm's registry whether a newer version is out, as it does for any package it runs. To start it without that, install it once with `npm install -g agent-lookout` and run `agent-lookout`.
1255
-
1256
- In a clone, run `npm run build` and `npm link` once in the `agent-lookout` folder, then `agent-lookout` from any folder.
1317
+ `npx agent-lookout`, or `agent-lookout` once it is installed, starts Agent Lookout at `http://127.0.0.1:4777`, on this computer only. [With npx](INSTALL.md#with-npx) has its options, and how to run it in the background, stop it and update it.
1257
1318
 
1258
1319
  ## Desktop app
1259
1320
 
1260
- Agent Lookout can also run as a Mac app: the same dashboard in a window of its own, with the collector running inside the app. The app opens no port, so nothing else on this computer can reach it. It needs no Node.js.
1261
-
1262
- To install it, download the disk image for your Mac from the [latest release](https://github.com/Olanetsoft/agent-lookout/releases/latest) on GitHub: `Agent-Lookout-<version>-mac-arm64.dmg` for Apple silicon, or `Agent-Lookout-<version>-mac-x64.dmg` for Intel. About This Mac, in the Apple menu, shows which you have: a chip such as Apple M1 is Apple silicon, and a processor named Intel is Intel. Open the disk image and drag Agent Lookout to Applications. Keep it there: the app [updates itself](#updates) only from a folder it can change.
1321
+ Agent Lookout can also run as a Mac app: the same dashboard in a window of its own, with the collector running inside the app. The app opens no port, so nothing else on this computer can reach it. It keeps its rules in the same [settings file](#time-rules) as `npx agent-lookout`, though, and takes up a change to that file at its next poll, so a program running as you can change them by writing the file. It needs no Node.js.
1263
1322
 
1264
- The app is not yet signed with an Apple Developer ID, so the first time a downloaded copy is opened, macOS does not open it. Its message is titled “Agent Lookout” Not Opened and says that Apple could not verify “Agent Lookout” is free of malware. To open it anyway:
1265
-
1266
- 1. Press Done. Do not press Move to Bin, or Move to Trash, which deletes the app.
1267
- 2. Open System Settings › Privacy & Security.
1268
- 3. Under Security, next to the line that says Agent Lookout was blocked, press Open Anyway, and confirm with your password or Touch ID. The button is there for about an hour after macOS stopped the app. If it has gone, open the app again, press Done, and go back to Privacy & Security.
1269
-
1270
- macOS remembers the choice.
1271
-
1272
- To build the app yourself instead, from a clone, on a Mac:
1273
-
1274
- ```sh
1275
- npm install
1276
- npm run dist:mac
1277
- ```
1278
-
1279
- It takes a minute or two, and the first time it downloads Electron for each kind of Mac. It puts a disk image and a zip for each in `release/`, with the same names as on the release. Open the disk image for your Mac and drag Agent Lookout to Applications. A copy you built yourself on this Mac opens without the steps above.
1323
+ To install it, download it from the [latest release](https://github.com/Olanetsoft/agent-lookout/releases/latest) and drag it to Applications, or build it yourself: [Mac app](INSTALL.md#mac-app) has the steps, and what to do the first time macOS will not open it. Keep it in Applications: the app [updates itself](#updates) only from a folder it can change.
1280
1324
 
1281
1325
  It behaves as a Mac app does:
1282
1326
 
@@ -1350,6 +1394,12 @@ To see which program holds a port, on macOS or on Linux with `lsof` installed:
1350
1394
  lsof -nP -iTCP:4777 -sTCP:LISTEN
1351
1395
  ```
1352
1396
 
1397
+ On Windows, the process ID is in the last column of:
1398
+
1399
+ ```text
1400
+ netstat -ano | findstr :4777
1401
+ ```
1402
+
1353
1403
  ### No sessions appear
1354
1404
 
1355
1405
  1. Start a session and wait a few seconds. For Claude Code, run `claude` in a terminal. For Codex, run `codex` and send a prompt: a Codex session appears once its first prompt is sent.
@@ -1429,7 +1479,7 @@ The program serving the page has stopped. The page keeps the last thing it saw,
1429
1479
 
1430
1480
  ## How it finds sessions
1431
1481
 
1432
- For Claude Code, Agent Lookout reads the small file Claude Code keeps for each running session in `~/.claude/sessions/`, every 2 seconds. That starts no program and uses no network. When it starts, and every 30 seconds after that, it also runs `claude agents --json --all`, the command Claude Code [documents](https://code.claude.com/docs/en/agent-view) for listing its sessions. That answer decides which sessions exist, and it adds background jobs that have finished or failed. If the command cannot be found or fails, Agent Lookout uses the files alone. While a session is waiting for you, Agent Lookout also reads the end of its transcript, in `~/.claude/projects/`, to say what it is asking, and forgets it when the wait ends. Claude Code does not document its transcripts, so a new version can change them, and then the reason shows alone.
1482
+ For Claude Code, Agent Lookout reads the small file Claude Code keeps for each running session in `~/.claude/sessions/`, every 2 seconds, whether the session runs in a terminal, in VS Code or in the desktop app. That starts no program and uses no network. When it starts, and every 30 seconds after that, it also runs `claude agents --json --all`, the command Claude Code [documents](https://code.claude.com/docs/en/agent-view) for listing its sessions. That answer decides which sessions exist, and it adds background jobs that have finished or failed. If the command cannot be found or fails, Agent Lookout uses the files alone. While a session is waiting for you, Agent Lookout also reads the end of its transcript, in `~/.claude/projects/`, to say what it is asking, and forgets it when the wait ends. Claude Code does not document its transcripts, so a new version can change them, and then the reason shows alone.
1433
1483
 
1434
1484
  For Codex it runs nothing. Every 2 seconds it reads what Codex has added to the session files under `~/.codex/sessions/` and finds the last line that says a turn started or ended: a session with a turn under way is working, and one whose last turn ended is idle. A session that no Codex program has open is finished. Those files hold your conversations with Codex. Agent Lookout keeps only when each turn started and ended and a few details, such as the session's folder, and when each file was last changed, which says how long a working session has been [quiet](#quiet-for). Codex documents none of these files, so a new Codex version can change them.
1435
1485
 
@@ -1439,7 +1489,7 @@ While a Claude Code session is running, Agent Lookout also asks tmux, if it is i
1439
1489
 
1440
1490
  For every session, whatever its agent, Agent Lookout looks for the git repository the session's folder is in: it looks for `.git` in the folder, then in each folder above it, and stops at the first, or before your home folder. It reads which [branch](#branches) is checked out from the repository's `HEAD` file, following the `.git` file of a worktree or a submodule to the folder that holds it. Which [repository](#repositories) the folder belongs to it works out from where those files are and, for a worktree, from the `commondir` file beside its `HEAD`, which names the repository's own git folder. It reads nothing else in the repository.
1441
1491
 
1442
- Agent Lookout never writes to `~/.claude`, `~/.codex`, `~/.agent-lookout` or any git repository. [PRIVACY.md](../PRIVACY.md) lists every file it reads and every command it runs, and what it keeps from each. [ARCHITECTURE.md](ARCHITECTURE.md) explains how the files and the command are checked against each other.
1492
+ Agent Lookout never writes to `~/.claude`, `~/.codex`, the folder of status files (`~/.agent-lookout/sessions`) or any git repository. In `~/.agent-lookout` it writes only its own history, `settings.json` and the socket `answer.sock`. [PRIVACY.md](../PRIVACY.md) lists every file it reads and every command it runs, and what it keeps from each. [ARCHITECTURE.md](ARCHITECTURE.md) explains how the files and the command are checked against each other.
1443
1493
 
1444
1494
  ## For contributors
1445
1495