agent-lookout 0.2.8 → 0.2.10
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 +35 -1
- package/PRIVACY.md +39 -23
- package/README.md +7 -6
- package/SECURITY.md +12 -7
- package/dist/assets/{HistoryPanel-CQ7zovsM.js → HistoryPanel-C5-cd24R.js} +1 -1
- package/dist/assets/SessionPanel-CO5qG2uH.js +1 -0
- package/dist/assets/index-BRNSMV9A.css +1 -0
- package/dist/assets/index-dMRHt2Y8.js +9 -0
- package/dist/cli/agent-lookout.js +4 -4
- package/dist/cli/{chunk-JSBQMY3Q.js → chunk-HHZWG5HI.js} +1 -1
- package/dist/cli/{chunk-I6Q35DHC.js → chunk-ME7TBH3B.js} +19 -0
- package/dist/cli/{chunk-LZ62RW3N.js → chunk-WKRBVBCR.js} +1 -1
- package/dist/cli/{mcpServer-BMSF2TXC.js → mcpServer-7VLC6LZN.js} +6 -5
- package/dist/cli/{startCommand-ANC7NVAO.js → startCommand-FV2NXVQR.js} +657 -287
- package/dist/index.html +2 -2
- package/docs/API.md +71 -41
- package/docs/GUIDE.md +100 -64
- package/docs/INSTALL.md +5 -1
- package/package.json +1 -1
- package/dist/assets/SessionPanel-DS29Z04P.js +0 -1
- package/dist/assets/index-BWFkRW4g.js +0 -3
- package/dist/assets/index-B_1Its0R.css +0 -1
package/dist/index.html
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
document.documentElement.setAttribute("data-theme", theme);
|
|
29
29
|
})();
|
|
30
30
|
</script>
|
|
31
|
-
<script type="module" crossorigin src="/assets/index-
|
|
31
|
+
<script type="module" crossorigin src="/assets/index-dMRHt2Y8.js"></script>
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/react-C2DiJaR6.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="/assets/motion-DlYOyClL.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="/assets/radix-BH0JH8UU.js">
|
|
35
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
35
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BRNSMV9A.css">
|
|
36
36
|
</head>
|
|
37
37
|
<body>
|
|
38
38
|
<div id="root"></div>
|
package/docs/API.md
CHANGED
|
@@ -31,37 +31,38 @@ curl -s http://127.0.0.1:4777/api/sessions
|
|
|
31
31
|
|
|
32
32
|
Every answer has `Content-Type: application/json; charset=utf-8`, `Cache-Control: no-store`, `X-Content-Type-Options: nosniff` and `Cross-Origin-Resource-Policy: same-origin`. Every answer that is not a 200 has the body `{ "error": "<one sentence>" }`.
|
|
33
33
|
|
|
34
|
-
| Status | When
|
|
35
|
-
| ------ |
|
|
36
|
-
| 400 | The address, `since` or `
|
|
37
|
-
| 403 | The `Host`, the `Origin` or `Sec-Fetch-Site` above
|
|
38
|
-
| 404 | There is no route at that path
|
|
39
|
-
| 405 | Any method but `GET`, with `Allow: GET`. The eight routes that act take only `POST`, with `Allow: POST`
|
|
40
|
-
| 500 | Something went wrong that the server did not expect
|
|
34
|
+
| Status | When |
|
|
35
|
+
| ------ | ------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| 400 | The address, `since`, `windowMs` or the `id` of `GET /api/sessions/last-message` could not be read |
|
|
37
|
+
| 403 | The `Host`, the `Origin` or `Sec-Fetch-Site` above |
|
|
38
|
+
| 404 | There is no route at that path, or no session listed with the `id` `GET /api/sessions/last-message` was given |
|
|
39
|
+
| 405 | Any method but `GET`, with `Allow: GET`. The eight routes that act take only `POST`, with `Allow: POST` |
|
|
40
|
+
| 500 | Something went wrong that the server did not expect |
|
|
41
41
|
|
|
42
42
|
## Routes
|
|
43
43
|
|
|
44
|
-
| Route
|
|
45
|
-
|
|
|
46
|
-
| `GET /api/health`
|
|
47
|
-
| `GET /api/sessions`
|
|
48
|
-
| `GET /api/
|
|
49
|
-
| `GET /api/
|
|
50
|
-
| `GET /api/
|
|
51
|
-
| `GET /api/
|
|
52
|
-
| `GET /api/
|
|
53
|
-
| `GET /api/
|
|
54
|
-
| `GET /api/
|
|
55
|
-
| `GET /api/
|
|
56
|
-
| `GET /api/
|
|
57
|
-
| `
|
|
58
|
-
| `POST /api/
|
|
59
|
-
| `POST /api/
|
|
60
|
-
| `POST /api/sessions/
|
|
61
|
-
| `POST /api/
|
|
62
|
-
| `POST /api/
|
|
63
|
-
| `POST /api/settings/
|
|
64
|
-
| `POST /api/
|
|
44
|
+
| Route | Answers |
|
|
45
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| `GET /api/health` | `{ ok: true, version }` |
|
|
47
|
+
| `GET /api/sessions` | The latest snapshot: `{ generatedAt, sources, sessions, answering, timeRules, quiet }` |
|
|
48
|
+
| `GET /api/sessions/last-message?id=<id>` | `{ message: { text, cut } }`, what one session last said, or `{ message: null, reason }` |
|
|
49
|
+
| `GET /api/events?since=<epoch ms>` | `{ events }`, newest first, at most 200 |
|
|
50
|
+
| `GET /api/history?windowMs=<ms>` | `{ points, startedAt, since, kept, restarts }`, one point for each poll, oldest first |
|
|
51
|
+
| `GET /api/email` | `{ on, to, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
52
|
+
| `GET /api/webhook` | `{ on, host, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
53
|
+
| `GET /api/ntfy` | `{ on, host, tokenSet, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
54
|
+
| `GET /api/pushover` | `{ on, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
55
|
+
| `GET /api/pull-requests` | `{ on, problem, gh, last }`: whether each branch's pull request is shown |
|
|
56
|
+
| `GET /api/waits` | `{ at, today, sevenDays, since, where }`: how long sessions waited on you |
|
|
57
|
+
| `GET /api/settings` | `{ timeRules, file, problem, permissionRules, permissionRulesProblem, ruleAnswers, ruleAnswersSince }`: the time rules, the permission rules, the file they are kept in, and what the rules answered |
|
|
58
|
+
| `POST /api/jump` | `{ ok: true, kind, place }`, with `app` for a terminal tab. One of eight routes that act |
|
|
59
|
+
| `POST /api/history/clear` | `{ ok: true, clearedAt }`. A route that acts |
|
|
60
|
+
| `POST /api/sessions/stop` | `{ ok: true }` once the session's process has ended. A route that acts |
|
|
61
|
+
| `POST /api/sessions/clean-up` | `{ results }`, what became of each session left running. A route that acts |
|
|
62
|
+
| `POST /api/permission/answer` | `{ ok: true, decision }` once a permission prompt was answered. A route that acts |
|
|
63
|
+
| `POST /api/settings/time-rules` | `{ ok: true, timeRules }` once the time rules are saved. A route that acts |
|
|
64
|
+
| `POST /api/settings/permission-rules` | `{ ok: true, permissionRules }` once one change to the permission rules is saved. A route that acts |
|
|
65
|
+
| `POST /api/phone/test` | `{ ok: true, channel, sentAt }` once a test push went through ntfy or Pushover. A route that acts |
|
|
65
66
|
|
|
66
67
|
Times are milliseconds since 1970, and lengths of time are milliseconds.
|
|
67
68
|
|
|
@@ -145,6 +146,7 @@ Sessions come in this order: those that need you first, longest wait first, then
|
|
|
145
146
|
| `startedAt` | When it started, or `null` |
|
|
146
147
|
| `statusSince` | When its status last changed, or `null`. It does not move while a wait goes on |
|
|
147
148
|
| `lastWriteAt` | When its agent last wrote the file it is read from, for Codex, the Antigravity CLI and status files |
|
|
149
|
+
| `tokens` | For Codex sessions, and sessions on another machine whose Agent Lookout sent them: `{ input, cached, output }`, the token counts its agent recorded for the session's newest reply. `input` is the whole prompt, cached tokens included, and is above 0. `cached` is the part of it read from a cache, left out when not recorded. `output` includes reasoning. Left out when there are none that could be right, never 0. No event, no point of history, no email, post or push, no answer of `agent-lookout mcp` and no output of `agent-lookout status` holds them |
|
|
148
150
|
| `pid`, `alive` | Its process and whether that still runs, when known |
|
|
149
151
|
| `links.open` | A `vscode://` address that opens it, for a session in VS Code |
|
|
150
152
|
| `jump` | `{ kind: "tmux", place }` or `{ kind: "terminal", app, place }` when `POST /api/jump` can take you to it |
|
|
@@ -161,12 +163,12 @@ Sessions come in this order: those that need you first, longest wait first, then
|
|
|
161
163
|
| `advice` | One plain sentence saying what you can do about a problem, when the collector knows |
|
|
162
164
|
| `watching` | `[{ label, value }]`: what it reads and runs, and how often |
|
|
163
165
|
| `basis` | Which way the sessions were read, for a source that has more than one |
|
|
164
|
-
| `capabilities` | What its agent can report, and whether its sessions can be stopped and answered: for each of
|
|
166
|
+
| `capabilities` | What its agent can report, and whether its sessions can be stopped and answered: for each of ten things, `{ level: "yes" }`, or `{ level: "no" }` or `"partly"` with a `reason` |
|
|
165
167
|
| `checkedAt` | When it was last read |
|
|
166
168
|
| `machine` | For another machine: its name, which its sessions carry as their `machine` |
|
|
167
169
|
| `agents` | For another machine: `[{ label, capabilities }]`, what each agent found there can report, as last read from it, with `jump`, `stop` and `answer` always `no` |
|
|
168
170
|
|
|
169
|
-
The [guide](GUIDE.md#what-each-agent-can-report) has the table `capabilities` holds.
|
|
171
|
+
The [guide](GUIDE.md#what-each-agent-can-report) has the table `capabilities` holds. An agent on another machine whose Agent Lookout sends no `tokens` has `tokens` as `no`.
|
|
170
172
|
|
|
171
173
|
Another machine that `AGENT_LOOKOUT_REMOTES` names is a source of its own, after this computer's three. Its `state` is `ok` while its sessions are read, `searching` while ssh signs in the first time, for 10 seconds at most, and `unavailable`, with the reason in `detail` and what to do in `advice`, for anything else: still connecting after those 10 seconds, ssh not found, the machine turned ssh away, the connection dropped, no Agent Lookout answering there, or an answer that cannot be read. A machine that is switched off is no fault of this computer, so it is not `error`. Its `watching` gives the ssh target, the command, the routes asked for, how often, the version there and the state of each source there, the first 20. When `AGENT_LOOKOUT_REMOTES` is set but cannot be read, one source stands for the machines instead: `remote:`, with no name after it, labelled `Other machines`, `not-set-up`, with what is wrong in `detail` and how to correct it in `advice`, and no sessions. Its sessions are listed with this computer's, in the same order, and counted in the same way. [Another machine over SSH](GUIDE.md#another-machine-over-ssh) has the rest.
|
|
172
174
|
|
|
@@ -200,6 +202,32 @@ With `AGENT_LOOKOUT_PULL_REQUESTS=on`, a session on a branch that has a pull req
|
|
|
200
202
|
It is what `gh` last said, asked at most once every 2 minutes for each repository and branch, so it can be up to about 2 minutes old. [Pull requests](GUIDE.md#pull-requests) in the guide says when it is asked for.
|
|
201
203
|
`answering` says whether permission prompts can be answered from the dashboard: `{ state, plugin, holdMs, problem }`. `state` is `on` while Agent Lookout listens for the plugin's requests, `off` with `AGENT_LOOKOUT_ANSWER=off`, and `unavailable` when it could not listen or does not, as on Windows, which has no Unix socket for the plugin's hook, with `problem` saying why in one sentence. `plugin` is `seen` once a request has arrived since the last permission prompt it missed, `missed` when a Claude Code session waited for permission and no request arrived, as for a session without the plugin, and `unknown` before either. `holdMs` is how long a request is held.
|
|
202
204
|
|
|
205
|
+
### `GET /api/sessions/last-message?id=<id>`
|
|
206
|
+
|
|
207
|
+
What one session on this computer last said, read from the end of its transcript when it is asked for. The dashboard asks for it only while that session's details are open. Only Claude Code sessions are read for now. It only reads.
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{ "message": { "text": "The tests pass now.\n\nI changed two files in src/app.", "cut": false } }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
| Field | Holds |
|
|
214
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
215
|
+
| `message.text` | The text of the session's newest reply, its parts joined with a blank line between. Line breaks are kept, a tab is two spaces, any other control character is a space, and the characters that change the direction of text are taken out. At most its last 2,000 characters. Plain text, with Markdown left as it was written |
|
|
216
|
+
| `message.cut` | `true` when the start of a longer message is left out |
|
|
217
|
+
|
|
218
|
+
When there is nothing to show, `message` is `null` and `reason` says why:
|
|
219
|
+
|
|
220
|
+
| `reason` | When |
|
|
221
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
222
|
+
| `off` | `AGENT_LOOKOUT_LAST_MESSAGE=off`, or `AGENT_LOOKOUT_WAITING_TEXT=off` for a Claude Code session. `setting` names the one that is off |
|
|
223
|
+
| `not-read` | The session is not a Claude Code session on this computer: a Codex, Antigravity CLI or status-file session, or one on another machine |
|
|
224
|
+
| `not-found` | Its transcript was not found. A link or a pipe where it should be does not count, and a background job known only by its job ID has none to look for |
|
|
225
|
+
| `unreadable` | Its transcript could not be read, as when a link or a pipe has taken its place since it was found |
|
|
226
|
+
| `nothing-yet` | The whole transcript was read, and the session has said nothing yet |
|
|
227
|
+
| `too-far-back` | Its last message is further back than the last 256 KB of the transcript, which is all that is read |
|
|
228
|
+
|
|
229
|
+
The query is one `id` of 1 to 300 characters, a session's `id` from `GET /api/sessions` written with `encodeURIComponent`, and nothing else, or the answer is 400. A request a browser marks with any `Sec-Fetch-Site` but `same-origin` gets 403, so a page at another port of this computer, or a link opened from anywhere, reads nothing. A session that is not listed gets 404. At most four transcripts are read in a second, for every session together, and above that the answer is 429 with `Retry-After: 1`. An answer is given again from memory for a second. With `AGENT_LOOKOUT_LAST_MESSAGE=off` every `id` is answered `off`, and nothing is looked up. [A session's last message](../PRIVACY.md#a-sessions-last-message) in PRIVACY.md says what is read and kept.
|
|
230
|
+
|
|
203
231
|
### `GET /api/events?since=<epoch ms>`
|
|
204
232
|
|
|
205
233
|
`{ "events": [...] }`: what changed, newest first, at most 200, and with `since` only those after it. Each event is `{ id, at, sessionId, sessionName, kind, from, to, severity }`, where `kind` is `appeared`, `status-changed`, `ended`, `stopped` or `answered`, `from` and `to` are statuses, and `severity` is `advisory`, `warning` for a change to `needs-you` or `critical` for a change to `failed`. A `stopped` event says that Agent Lookout stopped the session, when you pressed Stop or ended the sessions left running: it has `from`, the status the session had, no `to`, and `by: "agent-lookout"`. Its leaving the list is an `ended` event of its own. An `answered` event says that Agent Lookout answered a permission prompt of the session's, when you pressed Allow or Deny, or by a permission rule: it has `from`, `by: "agent-lookout"` and `decision`, `allow` or `deny`, and nothing of what was asked. One a rule gave also has `tool`, the tool's name as Claude Code gave it, and `rule`, `{ tool, command }`, the rule as you wrote it, with `command` only for a Bash rule that names one; its decision is the event's. Neither holds the command the session asked to run. A reader should pass over a `kind` it does not know: later versions may add others. The collector keeps the last 1,000 in memory, and with history kept on disk it reads them back when it starts again.
|
|
@@ -608,18 +636,20 @@ The plugin's hook reaches Agent Lookout another way, not through this API: a Uni
|
|
|
608
636
|
|
|
609
637
|
## In the Mac app only
|
|
610
638
|
|
|
611
|
-
The Mac app serves the dashboard and this API from its own address, `agent-lookout://app/`, with no port, and answers
|
|
639
|
+
The Mac app serves the dashboard and this API from its own address, `agent-lookout://app/`, with no port, and answers eight more routes there, for its updates, its menu bar item and its own notifications. `npm start`, `npx agent-lookout` and `npm run dev` do not have them: there, a GET answers 404 and a POST 405.
|
|
612
640
|
|
|
613
|
-
| Route
|
|
614
|
-
|
|
|
615
|
-
| `GET /api/app/update`
|
|
616
|
-
| `POST /api/app/update/check`
|
|
617
|
-
| `POST /api/app/update/install`
|
|
618
|
-
| `POST /api/app/update/setting`
|
|
619
|
-
| `GET /api/app/menu-bar`
|
|
620
|
-
| `POST /api/app/menu-bar/setting`
|
|
641
|
+
| Route | Body | Does |
|
|
642
|
+
| ---------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
643
|
+
| `GET /api/app/update` | | Where updates stand: the version, the switch, the last check, what was found and how far a download has got |
|
|
644
|
+
| `POST /api/app/update/check` | `{}` | Checks GitHub now, and answers once it has |
|
|
645
|
+
| `POST /api/app/update/install` | `{}` | Installs the version that is ready, then quits and opens it. 409 when none is ready, or it cannot be installed where the app runs |
|
|
646
|
+
| `POST /api/app/update/setting` | `{ "automatic": true }` or `false` | Turns the daily check on or off |
|
|
647
|
+
| `GET /api/app/menu-bar` | | Whether the menu bar item is shown: `{ "show": true }` or `false` |
|
|
648
|
+
| `POST /api/app/menu-bar/setting` | `{ "show": true }` or `false` | Shows the menu bar item or takes it away |
|
|
649
|
+
| `GET /api/app/notifications` | | The reason macOS gave the last time it would not show a notification the app showed itself, a test or one while its window was closed: `{ "lastRefusal": "…" }`, with `null` when there is none |
|
|
650
|
+
| `POST /api/app/notifications/test` | `{}` | Shows one test notification and answers with what macOS made of it |
|
|
621
651
|
|
|
622
|
-
Each POST passes the checks `POST /api/jump` does, with its own `X-Agent-Lookout-Action` and a body that is exactly what the table says. For updates, the action is `check-for-updates`, `install-update` or `update-setting`, the body is 256 bytes or less, and each answers with the status `GET /api/app/update` gives, `AppUpdateStatus` in `src/core/appUpdate.ts`. For the menu bar item, the action is `menu-bar-setting`, the body is 64 bytes or less, and it answers with what `GET /api/app/menu-bar` gives, `MenuBarStatus` in `src/core/appMenuBar.ts`. That says `false` while the item could not be put in the menu bar, even with the switch on. Nothing in a request reaches a command or a
|
|
652
|
+
Each POST passes the checks `POST /api/jump` does, with its own `X-Agent-Lookout-Action` and a body that is exactly what the table says. For updates, the action is `check-for-updates`, `install-update` or `update-setting`, the body is 256 bytes or less, and each answers with the status `GET /api/app/update` gives, `AppUpdateStatus` in `src/core/appUpdate.ts`. For the menu bar item, the action is `menu-bar-setting`, the body is 64 bytes or less, and it answers with what `GET /api/app/menu-bar` gives, `MenuBarStatus` in `src/core/appMenuBar.ts`. That says `false` while the item could not be put in the menu bar, even with the switch on. For the test notification, the action is `notification-test`, the body is 64 bytes or less, and it answers with `NotificationTestOutcome` in `src/core/notices/appNotifications.ts`: `{ "outcome": "shown" }` once macOS took it, `{ "outcome": "refused", "reason": "…" }` with macOS's reason, `{ "outcome": "unsupported" }`, or `{ "outcome": "no-answer" }` when macOS said nothing within 5 seconds, as while it asks whether Agent Lookout may show notifications. A refusal is kept until a notification is shown, and `GET /api/app/notifications` gives it. The test notification says only that it is a test. Nothing in a request reaches a command, a path or a notification.
|
|
623
653
|
|
|
624
654
|
## The notifications header
|
|
625
655
|
|
|
@@ -629,7 +659,7 @@ A program that only reads should not send it. `agent-lookout status` and `agent-
|
|
|
629
659
|
|
|
630
660
|
## For this computer only
|
|
631
661
|
|
|
632
|
-
The API has no token or password. The `Host` and `Origin` rules keep websites, and other computers on the network, away from it: it listens on loopback, and a browser cannot be made to read it from another site. What they do not stop is another program on this computer, which can read it as `curl` does, and on a shared computer that includes other user accounts. Such a program can also send the headers a route that acts asks for, since no browser is involved, so another account can ask the server to stop your Claude Code sessions, which it could not do itself. [SECURITY.md](../SECURITY.md) lists that as a known limit. The Mac app opens no port,
|
|
662
|
+
The API has no token or password. The `Host` and `Origin` rules keep websites, and other computers on the network, away from it: it listens on loopback, and a browser cannot be made to read it from another site. What they do not stop is another program on this computer, which can read it as `curl` does, and on a shared computer that includes other user accounts. Such a program can also send the headers a route that acts asks for, since no browser is involved, so another account can ask the server to stop your Claude Code sessions, which it could not do itself. It can also read up to 2,000 characters of what a listed Claude Code session last said, from `GET /api/sessions/last-message`. [SECURITY.md](../SECURITY.md) lists that as a known limit. The Mac app opens no port, `AGENT_LOOKOUT_STOP=off` takes stopping away, and `AGENT_LOOKOUT_LAST_MESSAGE=off` takes last messages away.
|
|
633
663
|
|
|
634
664
|
A token would not keep out programs running as you, which could read it as easily as the API. It could keep out other accounts on a shared computer, stopping your sessions included, the known limit above, and is left for a later version. The MCP server changes none of this: it opens no port of its own, speaks to the app that started it over stdin and stdout, and reads the same loopback API the dashboard and `agent-lookout status` read without one. A token is also for an API that can be reached some other way, such as from another computer, and Agent Lookout has none.
|
|
635
665
|
|