agent-lookout 0.2.3 → 0.2.4
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 +18 -0
- package/PRIVACY.md +59 -15
- package/README.md +10 -6
- package/SECURITY.md +16 -6
- package/dist/THIRD-PARTY-LICENSES.md +195 -0
- package/dist/assets/{HistoryPanel-DMnhIoFB.js → HistoryPanel-CIjshvhu.js} +1 -1
- package/dist/assets/SessionPanel-eBOdTLD9.js +1 -0
- package/dist/assets/index-B-myfVQS.js +2 -0
- package/dist/assets/index-Cceyz9yZ.css +1 -0
- package/dist/assets/radix-BH0JH8UU.js +41 -0
- package/dist/cli/agent-lookout.js +4 -4
- package/dist/cli/{chunk-GOX5UB5L.js → chunk-63XPRBHW.js} +85 -3
- package/dist/cli/{chunk-54ODUY75.js → chunk-KNOLJ3IC.js} +1 -1
- package/dist/cli/{chunk-4C34YX6S.js → chunk-XVXSGVEW.js} +6 -2
- package/dist/cli/mcpServer-HGH6PY4W.js +23664 -0
- package/dist/cli/{startCommand-H6O35VYR.js → startCommand-KNXMVM2U.js} +3996 -2822
- package/dist/index.html +3 -3
- package/docs/API.md +169 -62
- package/docs/GUIDE.md +127 -58
- package/package.json +5 -5
- package/dist/assets/SessionPanel-BNm9wCzf.js +0 -1
- package/dist/assets/atkinson-hyperlegible-mono-latin-400-normal-Cmjm2dBj.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-mono-latin-500-normal-BV5ipwqM.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-mono-latin-600-normal-CuPF9o0F.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-mono-latin-ext-400-normal-ITMuFrSK.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-mono-latin-ext-500-normal-BiHEOgiK.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-mono-latin-ext-600-normal-ZiH-ObRk.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-400-normal-DPp7N0cD.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-500-normal-B0CYqHl3.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-600-normal-BhgCag5w.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-ext-400-normal-qxMiU51n.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-ext-500-normal-oZS3KxrF.woff +0 -0
- package/dist/assets/atkinson-hyperlegible-next-latin-ext-600-normal-BBAQIogt.woff +0 -0
- package/dist/assets/index-Ckda8OV4.js +0 -2
- package/dist/assets/index-DFR77qn5.css +0 -1
- package/dist/assets/radix-27o0jeIu.js +0 -41
- package/dist/cli/mcpServer-QQFQR6DC.js +0 -246
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-B-myfVQS.js"></script>
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/react-C2DiJaR6.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="/assets/motion-BAhdRuCw.js">
|
|
34
|
-
<link rel="modulepreload" crossorigin href="/assets/radix-
|
|
35
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
34
|
+
<link rel="modulepreload" crossorigin href="/assets/radix-BH0JH8UU.js">
|
|
35
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Cceyz9yZ.css">
|
|
36
36
|
</head>
|
|
37
37
|
<body>
|
|
38
38
|
<div id="root"></div>
|
package/docs/API.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API
|
|
2
2
|
|
|
3
|
-
Agent Lookout's server answers a small HTTP API under `/api/`. The dashboard reads it, and so do `agent-lookout status` and `agent-lookout mcp`. It is meant for this computer only: it listens on a loopback address, and it answers only requests made to a loopback name. Every route returns JSON, and every route but
|
|
3
|
+
Agent Lookout's server answers a small HTTP API under `/api/`. The dashboard reads it, and so do `agent-lookout status` and `agent-lookout mcp`. It is meant for this computer only: it listens on a loopback address, and it answers only requests made to a loopback name. Every route returns JSON, and every route but four only reads.
|
|
4
4
|
|
|
5
5
|
The answers hold your session names and folder paths. Check one before you share it.
|
|
6
6
|
|
|
@@ -31,27 +31,30 @@ 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 `windowMs` could not be read
|
|
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`.
|
|
40
|
-
| 500 | Something went wrong that the server did not expect
|
|
34
|
+
| Status | When |
|
|
35
|
+
| ------ | ------------------------------------------------------------------------------------------------------ |
|
|
36
|
+
| 400 | The address, `since` or `windowMs` could not be read |
|
|
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 four 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 | Answers
|
|
45
|
-
| ---------------------------------- |
|
|
46
|
-
| `GET /api/health` | `{ ok: true, version }`
|
|
47
|
-
| `GET /api/sessions` | The latest snapshot: `{ generatedAt, sources, sessions }`
|
|
48
|
-
| `GET /api/events?since=<epoch ms>` | `{ events }`, newest first, at most 200
|
|
49
|
-
| `GET /api/history?windowMs=<ms>` | `{ points, startedAt, since, kept, restarts }`, one point for each poll, oldest first
|
|
50
|
-
| `GET /api/email` | `{ on, to, events, afterMs, asking, problem, last, limitedUntil }`
|
|
51
|
-
| `GET /api/webhook` | `{ on, host, events, afterMs, asking, problem, last, limitedUntil }`
|
|
52
|
-
| `GET /api/
|
|
53
|
-
| `
|
|
54
|
-
| `POST /api/
|
|
44
|
+
| Route | Answers |
|
|
45
|
+
| ---------------------------------- | --------------------------------------------------------------------------------------- |
|
|
46
|
+
| `GET /api/health` | `{ ok: true, version }` |
|
|
47
|
+
| `GET /api/sessions` | The latest snapshot: `{ generatedAt, sources, sessions }` |
|
|
48
|
+
| `GET /api/events?since=<epoch ms>` | `{ events }`, newest first, at most 200 |
|
|
49
|
+
| `GET /api/history?windowMs=<ms>` | `{ points, startedAt, since, kept, restarts }`, one point for each poll, oldest first |
|
|
50
|
+
| `GET /api/email` | `{ on, to, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
51
|
+
| `GET /api/webhook` | `{ on, host, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
52
|
+
| `GET /api/pull-requests` | `{ on, problem, gh, last }`: whether each branch's pull request is shown |
|
|
53
|
+
| `GET /api/waits` | `{ at, today, sevenDays, since, where }`: how long sessions waited on you |
|
|
54
|
+
| `POST /api/jump` | `{ ok: true, kind, place }`, with `app` for a terminal tab. One of four routes that act |
|
|
55
|
+
| `POST /api/history/clear` | `{ ok: true, clearedAt }`. A route that acts |
|
|
56
|
+
| `POST /api/sessions/stop` | `{ ok: true }` once the session's process has ended. A route that acts |
|
|
57
|
+
| `POST /api/sessions/clean-up` | `{ results }`, what became of each session left running. A route that acts |
|
|
55
58
|
|
|
56
59
|
Times are milliseconds since 1970, and lengths of time are milliseconds.
|
|
57
60
|
|
|
@@ -101,45 +104,76 @@ The snapshot the collector made at its last poll, two seconds apart at most:
|
|
|
101
104
|
|
|
102
105
|
Sessions come in this order: those that need you first, longest wait first, then working, idle, finished, failed and unknown, each with the most recent change first.
|
|
103
106
|
|
|
104
|
-
| Session field
|
|
105
|
-
|
|
|
106
|
-
| `id`
|
|
107
|
-
| `source`
|
|
108
|
-
| `agent`
|
|
109
|
-
| `surface`
|
|
110
|
-
| `name`
|
|
111
|
-
| `cwd`
|
|
112
|
-
| `project`
|
|
113
|
-
| `git`
|
|
114
|
-
| `git.repository`
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
|
|
127
|
-
|
|
|
128
|
-
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
107
|
+
| Session field | Holds |
|
|
108
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `id` | Stable across polls: the source, a colon, and the agent's own id for the session |
|
|
110
|
+
| `source` | `claude-code`, `codex` or `status-files` |
|
|
111
|
+
| `agent` | The agent's own name, for a session from a status file. Left out for the others, whose agent is the source's `label` |
|
|
112
|
+
| `surface` | `terminal`, `vscode`, `desktop`, `cloud`, `browser` or `unknown` |
|
|
113
|
+
| `name` | What the dashboard calls it |
|
|
114
|
+
| `cwd` | The folder it works in, or `null` |
|
|
115
|
+
| `project` | The last part of `cwd`, or `null` |
|
|
116
|
+
| `git` | `{ branch }`, or `{ commit }` with seven characters when no branch is checked out, each with `repository`. Left out when the folder is in no repository that could be read |
|
|
117
|
+
| `git.repository` | `{ id, name }`: the repository the folder belongs to, the same for its main folder and every worktree of it. `name` is the main folder's name. `id` is 16 hexadecimal characters, a hash of the path of the repository's own git folder, which its worktrees share, for telling repositories apart. Left out for a repository with no name, at the root, or a worktree whose `commondir` cannot be read |
|
|
118
|
+
| `git.pullRequest` | With `AGENT_LOOKOUT_PULL_REQUESTS=on` only, for a branch whose pull request on github.com your own `gh` found: `{ number, title, state, checks, url }`. Described below. Left out with the setting off, for a commit, for the default branch, for a branch named with digits alone, such as `51`, for a repository with no remote on github.com, and for a branch with no pull request |
|
|
119
|
+
| `status` | `needs-you`, `working`, `idle`, `finished`, `failed` or `unknown` |
|
|
120
|
+
| `waitingReason` | For `needs-you` only: `permission`, `question` or `other` |
|
|
121
|
+
| `waitingDetail` | The agent's own words for the wait, when it gives any |
|
|
122
|
+
| `waitingText` | For `needs-you` Claude Code sessions only: what the session is asking, in a line or two, read from the last message of its transcript, such as `Run: npm test`, `Edit: src/app.ts` or the question it put. Plain text of at most 200 characters. Left out when the transcript does not say, or with `AGENT_LOOKOUT_WAITING_TEXT=off`. No event in `/api/events`, and no history point, holds it. An email or webhook post holds it only with `AGENT_LOOKOUT_EMAIL_ASKING=on` or `AGENT_LOOKOUT_WEBHOOK_ASKING=on` |
|
|
123
|
+
| `startedAt` | When it started, or `null` |
|
|
124
|
+
| `statusSince` | When its status last changed, or `null`. It does not move while a wait goes on |
|
|
125
|
+
| `lastWriteAt` | When its agent last wrote the file it is read from, for Codex and status files |
|
|
126
|
+
| `pid`, `alive` | Its process and whether that still runs, when known |
|
|
127
|
+
| `links.open` | A `vscode://` address that opens it, for a session in VS Code |
|
|
128
|
+
| `jump` | `{ kind: "tmux", place }` or `{ kind: "terminal", app, place }` when `POST /api/jump` can take you to it |
|
|
129
|
+
| `stop` | `{ how: "signal" }` or `{ how: "background" }` when `POST /api/sessions/stop` can stop it: a Claude Code session in a terminal or VS Code, or a background job. It holds nothing else |
|
|
130
|
+
| `stale` | `true` once it has been idle for 24 hours |
|
|
131
|
+
|
|
132
|
+
| Source field | Holds |
|
|
133
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `id`, `label` | `claude-code` and `Claude Code`, `codex` and `Codex`, or `status-files` and `Status files` |
|
|
135
|
+
| `state` | `ok`, `searching`, `unavailable`, `not-set-up` or `error` |
|
|
136
|
+
| `detail` | One plain sentence: how it is being read, or what went wrong |
|
|
137
|
+
| `advice` | One plain sentence saying what you can do about a problem, when the collector knows |
|
|
138
|
+
| `watching` | `[{ label, value }]`: what it reads and runs, and how often |
|
|
139
|
+
| `basis` | Which way the sessions were read, for a source that has more than one |
|
|
140
|
+
| `capabilities` | What its agent can report, and whether its sessions can be stopped: for each of eight things, `{ level: "yes" }`, or `{ level: "no" }` or `"partly"` with a `reason` |
|
|
141
|
+
| `checkedAt` | When it was last read |
|
|
137
142
|
|
|
138
143
|
The [guide](GUIDE.md#what-each-agent-can-report) has the table `capabilities` holds.
|
|
139
144
|
|
|
145
|
+
With `AGENT_LOOKOUT_PULL_REQUESTS=on`, a session on a branch that has a pull request carries it in `git`:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
"git": {
|
|
149
|
+
"branch": "checkout-flow",
|
|
150
|
+
"repository": { "id": "ad1b8c7a6a7a8b36", "name": "storefront" },
|
|
151
|
+
"pullRequest": {
|
|
152
|
+
"number": 51,
|
|
153
|
+
"title": "Show the pull request and its checks",
|
|
154
|
+
"state": "open",
|
|
155
|
+
"checks": { "state": "failing", "passing": 4, "failing": 2, "pending": 1 },
|
|
156
|
+
"url": "https://github.com/example-org/storefront/pull/51"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
| Pull request field | Holds |
|
|
162
|
+
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `number` | Its number on github.com |
|
|
164
|
+
| `title` | Its title, as someone wrote it on GitHub, cleaned of control characters and of the characters that change the direction of text, and cut to 200 characters, as a session's name is. Plain text |
|
|
165
|
+
| `state` | `open`, `draft`, `merged` or `closed` |
|
|
166
|
+
| `checks` | `{ state, passing, failing, pending }`, from each check on its latest commit, a check run again counted once. `state` is `failing` when any failed, `pending` when none failed and some have not finished, `passing`, or `none` with no checks |
|
|
167
|
+
| `checks.passing` | Checks that passed, were skipped or ended neutral |
|
|
168
|
+
| `checks.failing` | Checks that failed, timed out, were cancelled, went stale or need an action |
|
|
169
|
+
| `checks.pending` | Checks queued or still running, and commit statuses still pending |
|
|
170
|
+
| `url` | Its page, `https://github.com/<owner>/<repository>/pull/<number>`, which the collector builds from the remote and the number rather than take from GitHub |
|
|
171
|
+
|
|
172
|
+
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.
|
|
173
|
+
|
|
140
174
|
### `GET /api/events?since=<epoch ms>`
|
|
141
175
|
|
|
142
|
-
`{ "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` or `
|
|
176
|
+
`{ "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` or `stopped`, `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. 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.
|
|
143
177
|
|
|
144
178
|
### `GET /api/history?windowMs=<ms>`
|
|
145
179
|
|
|
@@ -251,9 +285,26 @@ Whether [email](GUIDE.md#email) or a [webhook](GUIDE.md#webhook) is set up, and
|
|
|
251
285
|
|
|
252
286
|
Neither ever holds the mail server's address, its user name or its password, the webhook's path, or anything a session was asking.
|
|
253
287
|
|
|
288
|
+
### `GET /api/pull-requests`
|
|
289
|
+
|
|
290
|
+
Whether Agent Lookout shows each session's [pull request](GUIDE.md#pull-requests), with `AGENT_LOOKOUT_PULL_REQUESTS=on`, and whether the GitHub CLI, `gh`, can be asked for them. It only reads.
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{ "on": true, "problem": null, "gh": "ready", "last": { "at": 1791205920000, "ok": true } }
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
| Field | Holds |
|
|
297
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
298
|
+
| `on` | Whether pull requests are asked for |
|
|
299
|
+
| `problem` | While off because the setting is neither `on` nor `off`, one sentence naming it |
|
|
300
|
+
| `gh` | While on, what `gh` was last found to be: `unknown` before it was needed, `ready`, `not-found` when there is no `gh` on `PATH` or in the usual places, or `signed-out` when it is not signed in. `null` while off |
|
|
301
|
+
| `last` | `{ at, ok: true }` or `{ at, ok: false, reason }` for the last question `gh` was asked, or `null`. A `reason` is one of Agent Lookout's own sentences, such as `gh did not answer within 15 seconds`, never what `gh` printed |
|
|
302
|
+
|
|
303
|
+
It never holds a token: `gh` signs in with its own login, which Agent Lookout never reads.
|
|
304
|
+
|
|
254
305
|
### `POST /api/jump`
|
|
255
306
|
|
|
256
|
-
One of the
|
|
307
|
+
One of the four routes that act. It selects the tmux pane a session runs in, or brings its tab of Terminal or iTerm2 to the front, as the Jump button does. Its checks, on top of the ones every request passes, are `jumpRefusalFor` in `src/collector/jumpRoute.ts`, which makes those `actionRefusalFor` in `src/collector/handler.ts` makes for every route that acts. It answers only a request that:
|
|
257
308
|
|
|
258
309
|
- is a `POST`. Any other method gets 405, with `Allow: POST`.
|
|
259
310
|
- has an `Origin` that names this computer. A request with no `Origin` gets 403 here, though a read may go without one.
|
|
@@ -277,7 +328,7 @@ Each of these has an `error` sentence beside its `reason`. [SECURITY.md](../SECU
|
|
|
277
328
|
|
|
278
329
|
### `POST /api/history/clear`
|
|
279
330
|
|
|
280
|
-
|
|
331
|
+
A route that acts. It deletes the files the history is kept in, except any that a later version of Agent Lookout wrote, and empties the Events log and the history in memory, as Clear history in Settings does. The history then begins again from that moment, with `since` set to `{ at, by: "cleared" }`. It makes the same checks as `POST /api/jump`, with its own action, in `src/collector/history/clearRoute.ts`, and answers only a request that:
|
|
281
332
|
|
|
282
333
|
- is a `POST`. Any other method gets 405, with `Allow: POST`.
|
|
283
334
|
- has an `Origin` that names this computer, or gets 403.
|
|
@@ -297,6 +348,60 @@ Nothing in the request names a file: the files are the ones in the history's own
|
|
|
297
348
|
|
|
298
349
|
Each of these has an `error` sentence beside its `reason`.
|
|
299
350
|
|
|
351
|
+
### `POST /api/sessions/stop`
|
|
352
|
+
|
|
353
|
+
A route that acts. It ends one Claude Code session's process, as Stop session does in the session's details. It makes the same checks as `POST /api/jump`, with its own action, in `src/collector/actions/stopRoute.ts`, and answers only a request that:
|
|
354
|
+
|
|
355
|
+
- is a `POST`. Any other method gets 405, with `Allow: POST`.
|
|
356
|
+
- has an `Origin` that names this computer, or gets 403.
|
|
357
|
+
- is marked `same-origin` in `Sec-Fetch-Site`, when that header is sent, or gets 403.
|
|
358
|
+
- carries `X-Agent-Lookout-Action: stop`, or gets 403.
|
|
359
|
+
- has `Content-Type: application/json`, or gets 415.
|
|
360
|
+
- has a body of 1,024 bytes or less, or gets 413, that is exactly `{ "sessionId": "..." }`, or gets 400.
|
|
361
|
+
|
|
362
|
+
The server looks the session up in its own latest snapshot, and stops only one with a `stop` field, by what it found for it itself: its process, or its background job. Before it acts it reads the session's registry file again, which must still name that session, that process, the same kind, `interactive` or `bg`, and the same start time, and asks `ps` for the process's start time again, which must be the same, to the second. It never stops process 1, its own process, the one it was started from or any process above that. Then it sends SIGTERM, and waits up to 10 seconds for the process to end, or runs `claude stop` with the job's ID. Nothing in the request reaches a signal or a command. A stop adds a `stopped` event, and the server reads the sessions again before it answers. It makes one stop a second, and one at a time, with the clean-up's.
|
|
363
|
+
|
|
364
|
+
| Status | Body |
|
|
365
|
+
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
366
|
+
| 200 | `{ "ok": true }`: the process has ended, or the background job was stopped |
|
|
367
|
+
| 202 | `{ "ok": false, "reason": "still-running" }`: SIGTERM was sent, and the process is still running 10 seconds later. Nothing more is sent |
|
|
368
|
+
| 404 | `reason: "gone"`: the session is not listed, or its process has already ended |
|
|
369
|
+
| 409 | `reason: "unsupported"`: Agent Lookout does not stop this session, such as one in the desktop app or one whose kind is not known |
|
|
370
|
+
| 409 | `reason: "cannot-confirm"`: its registry file changed, or `ps` did not give the same start time |
|
|
371
|
+
| 403 | `reason: "not-allowed"`: the process is another user's, or one Agent Lookout never stops |
|
|
372
|
+
| 429 | `reason: "too-soon"`, with `Retry-After: 1` |
|
|
373
|
+
| 500 | `reason: "failed"`: the signal could not be sent, or `claude stop` failed |
|
|
374
|
+
|
|
375
|
+
Each of these has an `error` sentence beside its `reason`. With `AGENT_LOOKOUT_STOP=off` there is no such route, and a POST to it gets 405.
|
|
376
|
+
|
|
377
|
+
### `POST /api/sessions/clean-up`
|
|
378
|
+
|
|
379
|
+
A route that acts. It ends the sessions left running that you chose, as End does under Left running in the Sessions card. It makes the same checks as `POST /api/sessions/stop`, with `X-Agent-Lookout-Action: clean-up`, and takes a body of 8 KiB or less that is exactly:
|
|
380
|
+
|
|
381
|
+
```json
|
|
382
|
+
{
|
|
383
|
+
"sessions": [
|
|
384
|
+
{
|
|
385
|
+
"sessionId": "claude-code:00000000-0000-4000-8000-000000000004",
|
|
386
|
+
"statusSince": 1791118800000
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
with 1 to 20 sessions, none named twice, each with the `statusSince` the page showed for it. Anything else gets 400. For each session in turn the server makes every check of `POST /api/sessions/stop`, and then reads in the session's registry file, and in its own latest list of sessions, that it is still idle, since that same `statusSince`, and that this was 24 hours or more ago. A session that has done anything since is left running. Then it stops each one that passed, as the stop route does, and waits up to 10 seconds for them all.
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"results": [
|
|
397
|
+
{ "sessionId": "claude-code:00000000-0000-4000-8000-000000000004", "outcome": "ended" },
|
|
398
|
+
{ "sessionId": "claude-code:00000000-0000-4000-8000-000000000005", "outcome": "became-active" }
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The answer is 200 with an `outcome` for each session asked for, in order: `ended`, `became-active`, `not-stale`, `gone`, `unsupported`, `cannot-confirm`, `not-allowed`, `still-running` or `failed`, which mean what the stop route's reasons do. One `stopped` event is added for each session ended. A clean-up while a stop or another clean-up is under way, or less than a second after one began, gets 429 with `reason: "too-soon"`.
|
|
404
|
+
|
|
300
405
|
## In the Mac app only
|
|
301
406
|
|
|
302
407
|
The Mac app serves the dashboard and this API from its own address, `agent-lookout://app/`, with no port, and answers six more routes there, for its updates and its menu bar item. `npm start`, `npx agent-lookout` and `npm run dev` do not have them: there, each answers 404.
|
|
@@ -320,18 +425,20 @@ A program that only reads should not send it. `agent-lookout status` and `agent-
|
|
|
320
425
|
|
|
321
426
|
## For this computer only
|
|
322
427
|
|
|
323
|
-
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. [SECURITY.md](../SECURITY.md) lists that as a known limit.
|
|
428
|
+
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, and `AGENT_LOOKOUT_STOP=off` takes stopping away.
|
|
324
429
|
|
|
325
|
-
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, 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.
|
|
430
|
+
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.
|
|
326
431
|
|
|
327
432
|
## The MCP server
|
|
328
433
|
|
|
329
434
|
`agent-lookout mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server for an agent that keeps track of your other agents. The agent's app starts it and speaks to it over stdin and stdout. For each tool call it reads `GET /api/sessions` once, exactly as `agent-lookout status` does: from a loopback address only, with no `Origin` and no notifications header. It has three tools, and none of them acts:
|
|
330
435
|
|
|
331
|
-
| Tool | Answers
|
|
332
|
-
| ---------------------- |
|
|
333
|
-
| `list_sessions` | Every session, or those with the `status` given, with its id, name, agent, status, reason, folder, branch, app, since and how long it has been quiet |
|
|
334
|
-
| `sessions_needing_you` | The waiting sessions, longest wait first, with how long each has waited, and a sentence that sums them up
|
|
335
|
-
| `sources` | Each source's state in words, and its row of what its agent can and cannot report
|
|
436
|
+
| Tool | Answers |
|
|
437
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
438
|
+
| `list_sessions` | Every session, or those with the `status` given, with its id, name, agent, status, reason, folder, branch, pull request, app, since and how long it has been quiet |
|
|
439
|
+
| `sessions_needing_you` | The waiting sessions, longest wait first, with how long each has waited, and a sentence that sums them up |
|
|
440
|
+
| `sources` | Each source's state in words, and its row of what its agent can and cannot report |
|
|
441
|
+
|
|
442
|
+
A session's `pullRequest` is `{ number, checks }`, its number and the state of its checks, `failing`, `pending`, `passing` or `none`, only with `AGENT_LOOKOUT_PULL_REQUESTS=on` and for a branch that has one, and `null` otherwise. It never holds the title or the address. `agent-lookout status --json` gives a waiting session the same `pullRequest`, and leaves it out when there is none.
|
|
336
443
|
|
|
337
444
|
The [guide](GUIDE.md#for-your-agents) says how to add it to an agent's app and what each answer holds.
|