@alphacoder-v0/pi-loops 0.22.8 → 0.24.1
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 +717 -0
- package/CONTRACT.md +7 -7
- package/README.md +114 -78
- package/README.zh-CN.md +62 -41
- package/docs/cli.md +352 -91
- package/docs/configuration.md +86 -24
- package/docs/design.md +404 -61
- package/docs/downstream.md +2 -2
- package/docs/goal.md +12 -3
- package/docs/hooks.md +6 -7
- package/docs/loops.md +288 -59
- package/docs/mcp.md +22 -14
- package/docs/recipes.md +6 -0
- package/docs/screenshot.png +0 -0
- package/docs/triggers.md +120 -26
- package/docs/troubleshooting.md +41 -28
- package/docs/web-ui-parity.md +702 -65
- package/package.json +4 -3
- package/recipes/autoresearch/recipe.toml +1 -0
- package/recipes/changelog-draft/recipe.toml +1 -0
- package/recipes/ci-sweeper/recipe.toml +1 -0
- package/recipes/daily-digest/recipe.toml +1 -0
- package/recipes/deps-sweeper/recipe.toml +1 -0
- package/recipes/ecosystem/recipe.toml +1 -0
- package/recipes/issue-loop/recipe.toml +1 -0
- package/recipes/pr-watch/recipe.toml +1 -0
- package/skills/pi-loops/SKILL.md +6 -4
- package/src/activity.ts +199 -0
- package/src/archive.ts +21 -9
- package/src/args.ts +6 -2
- package/src/browser-open.ts +169 -0
- package/src/cli-entry.mjs +12 -3
- package/src/cli.ts +960 -143
- package/src/config.ts +53 -5
- package/src/conversation-rows.ts +469 -0
- package/src/danger.ts +227 -6
- package/src/engine.ts +1240 -0
- package/src/goal.ts +71 -0
- package/src/held-read.ts +27 -0
- package/src/hooks.ts +49 -18
- package/src/host-control-channel.ts +455 -40
- package/src/host-control.ts +602 -111
- package/src/host-entry.mjs +5 -3
- package/src/host-runtime.ts +455 -42
- package/src/host.ts +659 -123
- package/src/inbox-card.ts +278 -0
- package/src/inbox.ts +139 -62
- package/src/job-edit.ts +7 -1
- package/src/job-health.ts +14 -2
- package/src/job-lines.ts +201 -13
- package/src/job-owner.ts +74 -13
- package/src/job-tune.ts +110 -0
- package/src/lease.ts +537 -0
- package/src/lock.ts +7 -2
- package/src/mcp-pool.ts +66 -14
- package/src/mcp.ts +1 -1
- package/src/open-file.ts +54 -0
- package/src/page-children.ts +1507 -0
- package/src/page-devices.ts +192 -0
- package/src/page-server.ts +3150 -0
- package/src/path-precheck.ts +215 -0
- package/src/paths.ts +24 -0
- package/src/pi-floor.ts +10 -0
- package/src/pi-loops.ts +1370 -473
- package/src/pi-resolver.d.mts +1 -0
- package/src/pi-resolver.mjs +110 -10
- package/src/presence.ts +12 -63
- package/src/protocol.ts +10 -1
- package/src/recipe.ts +43 -1
- package/src/requests.ts +354 -0
- package/src/rule-lines.ts +68 -0
- package/src/run-card.ts +105 -0
- package/src/runner.ts +18 -1
- package/src/schedule.ts +1 -2
- package/src/scheduler.ts +595 -245
- package/src/sdk-runner.ts +321 -27
- package/src/seen.ts +278 -0
- package/src/session-head.ts +21 -0
- package/src/share.ts +12 -2
- package/src/snapshot.ts +3 -2
- package/src/store.ts +81 -8
- package/src/tools.ts +196 -65
- package/src/trigger-runtime.ts +164 -127
- package/src/triggers.ts +39 -7
- package/src/trust.ts +46 -2
- package/src/ui-prefs.ts +22 -7
- package/src/watch-plan.ts +174 -0
- package/src/web.d.mts +3 -0
- package/src/web.mjs +2261 -2340
- package/src/window-executor.ts +459 -0
- package/src/window-guard.ts +158 -0
- package/docs/agents/issue-tracker.md +0 -34
package/CONTRACT.md
CHANGED
|
@@ -37,7 +37,7 @@ talks to it only through a recipe directory, `hooks.toml`, and the `pi-loops inb
|
|
|
37
37
|
|
|
38
38
|
### 2.2 Unit formats
|
|
39
39
|
|
|
40
|
-
- **Recipe**: `recipe.toml` is TOML. Required: `name` (a lowercase letter, then lowercase letters, digits and hyphens), `summary`, `levels` (non-empty, from the three levels), at least one `[[job]]`. Each `[[job]]` requires `name`, `schedule` (a recurring schedule) and `playbook` (a file in the same directory). Optional keys: `needs_tracker`, `setup`, `files`, `tier`, `useful_when`, `needs`, `needs_propose`, `budget_hint_usd`; per job `verify`, `timeout`, `thinking`, `tools`, `prompt`.
|
|
40
|
+
- **Recipe**: `recipe.toml` is TOML. Required: `name` (a lowercase letter, then lowercase letters, digits and hyphens), `summary`, `levels` (non-empty, from the three levels), at least one `[[job]]`. Each `[[job]]` requires `name`, `schedule` (a recurring schedule) and `playbook` (a file in the same directory). Optional keys: `line`, `needs_tracker`, `setup`, `files`, `tier`, `useful_when`, `needs`, `needs_propose`, `budget_hint_usd`; per job `verify`, `timeout`, `thinking`, `tools`, `prompt`.
|
|
41
41
|
- **Playbook**: Markdown with pi's skill frontmatter (`name`, `description`), one level line, and one `## Never` section.
|
|
42
42
|
- **Finding**: one JSON object with the fields `id`, `created_at` (RFC 3339), `status`, `kind` (`checkpoint` | `news`), `source`, `run_id`, `cwd`, `text`, `verified` (`true` | `false` | `null`), `dismiss_reason` (string | `null`). Fields are only added at the end; none is renamed, removed or changed in type.
|
|
43
43
|
- **Event**: `run_start` and `run_end`. The payload has `run_job` and `run_id`; on `run_end` also `run_ok`, `run_findings`, `run_error`, `run_cost_usd`. Each field also enters the command's environment as `PI_<FIELD IN UPPER CASE>`, unset when the payload has `null`.
|
|
@@ -59,7 +59,7 @@ A finding is written only by a run, and its `text` is what the run said. `status
|
|
|
59
59
|
|
|
60
60
|
### 2.6 Deterministic rules
|
|
61
61
|
|
|
62
|
-
- `kind`: `text` contains `· waits:` (preceded by the start of the text or whitespace, case-insensitive) → `checkpoint
|
|
62
|
+
- `kind`: a finding filed from a rule result is always `news`; otherwise `text` contains `· waits:` (preceded by the start of the text or whitespace, case-insensitive) → `checkpoint`, else `news`.
|
|
63
63
|
- Level line: the first line that is exactly `Autonomy: <one word>`. When that word is one of the three levels, it is the level; otherwise the playbook has no level.
|
|
64
64
|
- Manifest validity: the required keys and values of 2.2. TOML is parsed as standard TOML.
|
|
65
65
|
|
|
@@ -90,9 +90,9 @@ Three sentences each: what it is, what it depends on, and why the contract does
|
|
|
90
90
|
- **An install leaves an `.orig/` copy and `.recipe.json` in the project and lists the directory in `.git/info/exclude`.** Depends on the three-way merge of `/recipe update` and on keeping playbooks out of the repository's history. Another merge strategy leaves 2.1 and 2.3 as they are.
|
|
91
91
|
- **The values of `needs` and `needs_propose` are the preflight checks this version performs (`gh`, `git-remote`, `ci-workflows`, `lockfile`, `tracker-github`).** Depends on this version's set of checks. A recipe that names a check this version does not have is refused; that binds the recipe to a version and does not change the contract.
|
|
92
92
|
- **A recipe name is at most 64 characters.** Depends on the install directory name staying readable.
|
|
93
|
-
- **A rule's command times out after 5000 ms, its stdout is cut at 4000 characters, summaries at 2000, and
|
|
93
|
+
- **A rule's command times out after 5000 ms, its stdout is cut at 4000 characters, summaries at 2000, and pi-loops drains for 3 seconds before it exits; output goes to `logs/pi-<pid>.log` or `host.log`.** Depends on log rotation. Changing any of these numbers leaves the event fields of 2.2 as they are.
|
|
94
94
|
- **Creating a job needs a pi (interactive, or `pi --mode rpc`).** `pi-loops recipe add` in a terminal only copies files and prints the `/cron add` lines. Depends on the decision that a job belongs to a session. The "create the jobs" of 2.3 is done by `/recipe add` inside pi.
|
|
95
|
-
- **
|
|
95
|
+
- **An always-on pi-loops runs every loop and rule check, whether or not a pi is open.** Depends on pi-loops starting on demand, and on the session lease. Events and findings have the same shape in both processes.
|
|
96
96
|
|
|
97
97
|
## 4. Not done
|
|
98
98
|
|
|
@@ -108,9 +108,9 @@ Three sentences each: what it is, what it depends on, and why the contract does
|
|
|
108
108
|
The scripts are in `test/contract/`. `sh test/contract/run.sh` runs all five; each also runs alone.
|
|
109
109
|
|
|
110
110
|
1. **Second implementation** `test/contract/1-second-implementation.sh`: `test/downstream/closed-loop.sh` (sh and jq install a recipe, let it run, find its finding by `run_id` and claim it, touching no private file), then python3 `tomllib` reads every packaged `recipe.toml` and checks the required keys of 2.2; then every packaged recipe is installed with `pi-loops recipe add` and its files are searched for the names of the loops directory's files. Pass: every step exits 0 and no playbook names a private file. A failure means private state has leaked into the contract, or a recipe depends on it.
|
|
111
|
-
2. **Stopped machine** `test/contract/2-stopped.sh`: with no pi and
|
|
112
|
-
3. **Cross-check** `test/contract/3-cross-check.sh`: four rules, each run through two implementations that share nothing, over the same samples; pass when the outputs are equal line for line. TOML: `src/toml.ts` against python3 `tomllib`, over every `recipe.toml` in the repository and `examples/mcp.toml`. `kind`: `src/protocol.ts` against `grep -E`, over every `<inbox>` text the repository has shown. Level line: `src/recipe.ts` against `awk`, over every playbook in `recipes/` and in this repository's `.agents/skills/`. Finding: `pi-loops inbox list --all --json` against `jq` reading the ten fields of 2.2 off each line of `inbox.jsonl` directly. Event: the same loop run once by
|
|
113
|
-
4. **Private state cleared** `test/contract/4-private-state.sh`: after one run, delete everything in the loops directory that 2.1 does not name as instance data and start
|
|
111
|
+
2. **Stopped machine** `test/contract/2-stopped.sh`: with no pi and pi-loops not running, write a `hooks.toml` rule, install a directory with `pi-loops recipe add`, and dismiss a finding with `pi-loops inbox dismiss`. Pass: all three succeed. A failure means a write depends on a mechanism.
|
|
112
|
+
3. **Cross-check** `test/contract/3-cross-check.sh`: four rules, each run through two implementations that share nothing, over the same samples; pass when the outputs are equal line for line. TOML: `src/toml.ts` against python3 `tomllib`, over every `recipe.toml` in the repository and `examples/mcp.toml`. `kind`: `src/protocol.ts` against `grep -E`, over every `<inbox>` text the repository has shown. Level line: `src/recipe.ts` against `awk`, over every playbook in `recipes/` and in this repository's `.agents/skills/`. Finding: `pi-loops inbox list --all --json` against `jq` reading the ten fields of 2.2 off each line of `inbox.jsonl` directly. Event: the same loop run once by pi-loops started directly and once by pi-loops started on demand by a pi driven over rpc; the two `run_end` payloads have the same fields and the same run values.
|
|
113
|
+
4. **Private state cleared** `test/contract/4-private-state.sh`: after one run, delete everything in the loops directory that 2.1 does not name as instance data and start pi-loops again. Pass: it runs the loop again and the earlier findings are unchanged field for field.
|
|
114
114
|
5. **Contract size** `test/contract/5-size.sh`: section 2 has no more non-empty lines than the count frozen in this file's header. Pass when it does not. A line added there must name, in the CHANGELOG, the mechanism it replaced.
|
|
115
115
|
|
|
116
116
|
## 6. Mapping of requirements and features
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ English · [中文](README.zh-CN.md)
|
|
|
5
5
|
An automation layer for [pi](https://github.com/earendil-works/pi), shipped as a plain pi extension.
|
|
6
6
|
Nothing in pi is patched.
|
|
7
7
|
|
|
8
|
-

|
|
9
9
|
|
|
10
10
|
A loop wakes up with the notes its last run left, does the work in a sub-agent that starts clean,
|
|
11
11
|
and files what it found. You read the findings when you want to, and claim the ones worth a turn.
|
|
@@ -31,8 +31,9 @@ reaches you is what changed rather than everything that is there.
|
|
|
31
31
|
|
|
32
32
|
Node ≥ 22.6 (pi loads the TypeScript sources directly) and pi ≥ 0.84.3 to load it into — 0.85 is
|
|
33
33
|
what this is tested against, and an older pi is refused at load with a line that names the version
|
|
34
|
-
it needs, rather than a link error naming one missing export.
|
|
35
|
-
|
|
34
|
+
it needs, rather than a link error naming one missing export. The process that runs your loops always
|
|
35
|
+
runs on node, also when pi runs under bun: it then uses the `node` on your PATH. Nothing else:
|
|
36
|
+
pi-loops has no runtime dependencies. What you do want first is a provider you can actually talk to — run `pi`, send
|
|
36
37
|
one message, make sure you get an answer. pi-loops runs sub-agents while you are not watching, and
|
|
37
38
|
the first sign of credentials that do not work should not be an empty inbox tomorrow morning.
|
|
38
39
|
|
|
@@ -45,11 +46,11 @@ one run with `pi -e .`, which installs nothing. Keep one copy, not two: two copi
|
|
|
45
46
|
same tools, and pi refuses to load the second — `Tool "cron_create" conflicts with …`, and it
|
|
46
47
|
exits.
|
|
47
48
|
|
|
48
|
-
Then
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
Then start `pi` once, and that is the whole install. That first session puts the `pi-loops` command
|
|
50
|
+
on your `PATH` and says so in one line, so from then on `pi-loops` works from any shell (see
|
|
51
|
+
[The browser window, and the command line](#the-browser-window-and-the-command-line)). `/cron`,
|
|
52
|
+
`/inbox`, `/triggers` and `/goal` are registered by the extension itself, so they work with or
|
|
53
|
+
without that command. The package also ships a skill (`skills/pi-loops`), so the agent knows
|
|
53
54
|
when to reach for `cron_create`, `new_trigger` and the inbox on its own.
|
|
54
55
|
|
|
55
56
|
## Your first loop
|
|
@@ -60,36 +61,28 @@ when to reach for `cron_create`, `new_trigger` and the inbox on its own.
|
|
|
60
61
|
|
|
61
62
|
Every morning a fresh sub-agent runs with the notes it wrote last time, does the work, and hands
|
|
62
63
|
back two things: the notes for tomorrow, and any finding worth your attention. The notes go to a
|
|
63
|
-
Markdown file
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
Markdown file and the findings go to the inbox. (The run says which is which in tags that a program
|
|
65
|
+
reads rather than you — [docs/loops.md](docs/loops.md) has them.) Your conversation gets one line
|
|
66
|
+
for each of the loop's first three runs, so you see that it works, and one for any run that fails;
|
|
67
|
+
every other run is a line in the panel above the editor and in the page's loop activity strip. Here
|
|
68
|
+
are two runs of that loop, half an hour apart, as the panel lists them:
|
|
66
69
|
|
|
67
70
|
```text
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
Runs
|
|
72
|
+
09:30 todo · 4s · 1 finding
|
|
73
|
+
09:00 todo · 5s · 0 findings
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-
The second
|
|
74
|
-
|
|
75
|
-
```text
|
|
76
|
-
cron todo · 4s · $0.000 · 1 finding · state updated
|
|
77
|
-
md5 changed (06ff2ec8… → fe00aaad…). One new unchecked item appeared.
|
|
78
|
-
• TODO.md: new unchecked item — cache the /search results for 60s (commit 395a124 "todo: cache search results")
|
|
79
|
-
/cron trace todo · /inbox
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The first run found nothing worth saying and said nothing; the second noticed one change and
|
|
83
|
-
reported only that. That is what the notes between runs buy — without them every run reports the
|
|
76
|
+
The first run found nothing worth saying and filed nothing; the second noticed one change, a new
|
|
77
|
+
unchecked item in TODO.md, and filed only that. That is what the notes between runs buy — without them every run reports the
|
|
84
78
|
whole file every morning, and you stop reading it by Thursday. What they filed waits in the inbox:
|
|
85
79
|
|
|
86
80
|
```text
|
|
87
81
|
/inbox
|
|
88
82
|
Inbox (/tmp/acme-api, 3 new, times +00:00):
|
|
89
|
-
1. [inb-aae794d5] TODO: rate-limit the /search endpoint (unchecked, no owner) (acme-api, cron
|
|
90
|
-
2. [inb-3216a92d] TODO: retry the payment webhook on 5xx (unchecked, no owner) (acme-api, cron
|
|
91
|
-
3. [inb-74b73c72] TODO.md: new unchecked item — cache the /search results for 60s (commit 395a124 "todo: cache search results") (acme-api,
|
|
92
|
-
claim with /inbox claim <n>, dismiss with /inbox dismiss <n>
|
|
83
|
+
1. [inb-aae794d5] TODO: rate-limit the /search endpoint (unchecked, no owner) (acme-api, cron-b91def87, 2026-09-12 05:25)
|
|
84
|
+
2. [inb-3216a92d] TODO: retry the payment webhook on 5xx (unchecked, no owner) (acme-api, cron-b91def87, 2026-09-12 05:25)
|
|
85
|
+
3. [inb-74b73c72] TODO.md: new unchecked item — cache the /search results for 60s (commit 395a124 "todo: cache search results") (acme-api, todo, 2026-09-12 05:28)
|
|
93
86
|
```
|
|
94
87
|
|
|
95
88
|
The third finding is the run above; the two over it came from another loop in the same checkout,
|
|
@@ -175,31 +168,39 @@ Set a cap in `~/.pi/agent/loops/config.toml` before you rely on it:
|
|
|
175
168
|
daily_budget_usd = 5.0
|
|
176
169
|
```
|
|
177
170
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
171
|
+
A pi window starts pi-loops in the background when there is work to run. It runs every loop, rule
|
|
172
|
+
check and push, whether or not a window is open. It also runs the plain jobs of closed
|
|
173
|
+
conversations, in those conversations; each such run is a model turn. A window runs the plain jobs
|
|
174
|
+
of the conversation it holds. After a reboot, work resumes when a pi window opens, or at login if
|
|
175
|
+
you run `pi-loops start --foreground` from your own systemd unit ([docs/cli.md](docs/cli.md)).
|
|
176
|
+
`/pi-loops status` shows its status; `[host] auto = false` disables automatic starts.
|
|
181
177
|
|
|
182
178
|
## The browser window, and the command line
|
|
183
179
|
|
|
184
180
|
`pi-loops` is the part that needs a launcher: `pi install` puts the package under pi's managed
|
|
185
|
-
directory rather than on your `PATH
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
181
|
+
directory rather than on your `PATH`. You do not fix that by hand. The first interactive pi session
|
|
182
|
+
after `pi install` writes `pi-loops` into the first of `~/.local/bin` and `/usr/local/bin` that is
|
|
183
|
+
already on your `PATH`, says so in one line, and keeps that launcher working afterwards if pi ever
|
|
184
|
+
moves the package:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
[pi-loops] added pi-loops to ~/.local/bin; /pi-loops uninstall-launcher removes it
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
It never replaces a `pi-loops` it did not write. When neither directory is on your `PATH`, or on a
|
|
191
|
+
machine where pi itself is never opened, write it yourself and pass `--dir <dir>` to say where:
|
|
189
192
|
|
|
190
193
|
```text
|
|
191
|
-
/pi-loops install-launcher
|
|
194
|
+
/pi-loops install-launcher --dir ~/bin # from inside pi, where the extension is already loaded
|
|
192
195
|
```
|
|
193
196
|
|
|
194
197
|
```bash
|
|
195
|
-
# or from a shell
|
|
198
|
+
# or from a shell — the same command, by absolute path
|
|
196
199
|
cd ~/.pi/agent/npm/node_modules/@alphacoder-v0/pi-loops # installed from npm
|
|
197
200
|
cd ~/.pi/agent/git/github.com/alphacoder-v0/pi-loops # installed from GitHub
|
|
198
|
-
node src/cli-entry.mjs install-launcher
|
|
201
|
+
node src/cli-entry.mjs install-launcher --dir ~/bin
|
|
199
202
|
```
|
|
200
203
|
|
|
201
|
-
Any of them writes a launcher into the first of `~/.local/bin` and `/usr/local/bin` that is already
|
|
202
|
-
on your `PATH`, and refuses rather than guessing if neither is — pass `--dir <dir>` to say where.
|
|
203
204
|
After that, `pi-loops` works from anywhere:
|
|
204
205
|
|
|
205
206
|
```bash
|
|
@@ -208,17 +209,17 @@ pi-loops --tui # the terminal one, when you want it
|
|
|
208
209
|
pi-loops --continue # pick up the newest session in this directory
|
|
209
210
|
```
|
|
210
211
|
|
|
211
|
-
`pi-loops sessions|inspect|export|import` and `pi-loops
|
|
212
|
-
open at all. They are for backups from cron or CI, restoring on a fresh machine, and
|
|
213
|
-
the
|
|
212
|
+
`pi-loops sessions|inspect|export|import` and `pi-loops start|stop|restart|status|abort` need no
|
|
213
|
+
pi session open at all. They are for backups from cron or CI, restoring on a fresh machine, and
|
|
214
|
+
looking in on pi-loops, which the page also shows as its Background view (`pi-loops status --url`). See [docs/cli.md](docs/cli.md).
|
|
214
215
|
|
|
215
|
-
At a local terminal the bare command opens the browser
|
|
216
|
+
At a local terminal the bare command opens the browser page; over ssh, or with no terminal at
|
|
216
217
|
all, it runs pi itself, because a browser on the far machine helps nobody. Pass `--web` or `--tui`
|
|
217
218
|
when that guess is wrong. Anything else you pass goes straight to pi (`pi-loops --model
|
|
218
219
|
anthropic/claude-opus-5 -e .`).
|
|
219
220
|
|
|
220
|
-
Both windows are complete pi sessions — the
|
|
221
|
-
session file, `--resume`, your models, tools and extensions are the same either way, and the model
|
|
221
|
+
Both windows are complete pi sessions — each conversation in the page is a `pi --mode rpc` that
|
|
222
|
+
pi-loops runs for it — so the session file, `--resume`, your models, tools and extensions are the same either way, and the model
|
|
222
223
|
and thinking level you last chose start the next session whichever window it opens in. The browser
|
|
223
224
|
one is a session and not a viewer: what a person can still do after the window changed is a release
|
|
224
225
|
gate, and [docs/web-ui-parity.md](docs/web-ui-parity.md) has it line by line.
|
|
@@ -228,15 +229,21 @@ earlier one in this project, **compact** summarises what is there and says what
|
|
|
228
229
|
or as `/clear`, `/new`, `/resume` and `/compact <what to keep>` in the composer. None of them
|
|
229
230
|
deletes anything; the session you leave is a file that `resume` lists.
|
|
230
231
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
The page is served by pi-loops in the background on **`http://127.0.0.1:4173/`**, so `pi-loops`
|
|
233
|
+
opens it and exits, and the page stays open when that terminal closes. Its token lives in a file, so
|
|
234
|
+
the address is the same one tomorrow and is worth bookmarking (`[page] port` in `config.toml` moves
|
|
235
|
+
it if 4173 is spoken for). `pi-loops` prints one link with a one-use key and opens your browser on
|
|
236
|
+
it, on macOS, Windows, WSL and Linux; when no browser opens, paste that link. The first visit leaves
|
|
237
|
+
a cookie and you never see the token again. There is no way to drop the token: the page is always
|
|
238
|
+
on, so it is always behind it.
|
|
239
|
+
|
|
240
|
+
A browser that shows "This browser has not been here before." needs a fresh link: run
|
|
241
|
+
`pi-loops open` in a terminal, or `/pi-loops web` inside pi, which also copies the link to the
|
|
242
|
+
clipboard and needs no `pi-loops` on your PATH.
|
|
236
243
|
|
|
237
244
|
From a phone, the best route is `tailscale serve --bg 4173`: this server stays on loopback and the
|
|
238
|
-
tailnet does TLS and identity. On the same wifi, `
|
|
239
|
-
|
|
245
|
+
tailnet does TLS and identity. On the same wifi, `[page] host = "0.0.0.0"` in `config.toml` works
|
|
246
|
+
too. Press **add device** in a browser that is already signed in and point the phone at the
|
|
240
247
|
QR it shows: nothing to type, that device stays signed in, and the page can go on the home screen.
|
|
241
248
|
|
|
242
249
|
### Keeping it up to date
|
|
@@ -262,32 +269,58 @@ says what is in each one.
|
|
|
262
269
|
|
|
263
270
|
### Uninstall
|
|
264
271
|
|
|
272
|
+
Stop pi-loops and remove the `pi-loops` launcher before removing the extension. pi-loops keeps
|
|
273
|
+
running in the background after the extension is removed, and nothing else stops it; the launcher
|
|
274
|
+
needs the package to run. `uninstall-launcher` removes only a launcher that pi-loops wrote:
|
|
275
|
+
|
|
265
276
|
```bash
|
|
277
|
+
pi-loops stop
|
|
278
|
+
pi-loops uninstall-launcher # add --dir <dir> if install-launcher --dir <dir> wrote it
|
|
266
279
|
pi remove npm:@alphacoder-v0/pi-loops # or git:github.com/alphacoder-v0/pi-loops, or the checkout path — whatever you installed
|
|
267
280
|
```
|
|
268
281
|
|
|
269
|
-
State stays in `~/.pi/agent/loops` until you delete it.
|
|
282
|
+
State stays in `~/.pi/agent/loops` until you delete it. After a reinstall, `/pi-loops install-launcher`
|
|
283
|
+
in pi puts the command back, because a removed launcher is not written again by itself.
|
|
284
|
+
|
|
285
|
+
To go back to 0.22.8, stop pi-loops while this version is still installed, then install 0.22.8:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
pi-loops stop
|
|
289
|
+
pi install npm:@alphacoder-v0/pi-loops@<version> # <version> is 0.22.8
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
0.22.8 reads your jobs and rules as they are. It does not run the plain jobs of closed
|
|
293
|
+
conversations, and it ignores the files that only later versions write, so nothing still waiting
|
|
294
|
+
for a conversation is delivered: rule results, plain-job prompts waiting for their conversation, claimed
|
|
295
|
+
findings, `/goal` continuations, recipe hand-offs, `inject_and_run` pushes and messages sent from
|
|
296
|
+
the browser. Let them drain first (`pi-loops status` lists them). It also ignores the built-in
|
|
297
|
+
tools later versions record on a loop, so a loop made in a chat with fewer of them runs with pi's
|
|
298
|
+
default tools; and it has no window guard, so a plain job's prompt in an open window runs as your
|
|
299
|
+
own message would, with no question before a dangerous command.
|
|
300
|
+
|
|
301
|
+
The stop is remembered in `~/.pi/agent/loops/ui.json`, and 0.22.8 keeps it. After you upgrade again,
|
|
302
|
+
pi-loops stays stopped until `pi-loops start`, or until `pi-loops` opens the browser page, which starts it.
|
|
270
303
|
|
|
271
304
|
## Commands
|
|
272
305
|
|
|
273
306
|
| Command | What it does |
|
|
274
307
|
|---|---|
|
|
275
|
-
| `/cron add [--stateful] [--verify] "<schedule>" <prompt>` | Schedule a job: a plain job belongs to this chat and injects its result here
|
|
308
|
+
| `/cron add [--stateful] [--verify] "<schedule>" <prompt>` | Schedule a job: a plain job belongs to this chat and injects its result here; `--stateful` makes a loop that runs in the background, with memory and inbox routing; `--verify` adds the checker |
|
|
276
309
|
| `/cron`, `/cron all` | This project's jobs, or every project on this machine |
|
|
277
310
|
| `/cron enable\|disable\|remove <ref>` | Pause, resume or delete one job — and write an audit entry into the session, as `add` does. `remove` takes a name of this project or the exact id of a job anywhere, so a name never reaches a checkout you are not in |
|
|
278
311
|
| `/cron disable --all` | Pause every job in this project (`--all-projects` for the machine); `/cron enable --all` resumes |
|
|
279
|
-
| `/cron run <ref>` |
|
|
312
|
+
| `/cron run <ref>` | Ask pi-loops to run a loop now; plain jobs run now only in the window holding their conversation |
|
|
280
313
|
| `/cron state <ref>` | The loop's notes — the Markdown it carries from one run to the next |
|
|
281
314
|
| `/cron runs [ref]` | The run log: when it fired, what it cost, what it found |
|
|
282
315
|
| `/cron trace <job> [k] [checker]` | The k-th latest run's full sub-agent transcript, maker or checker |
|
|
283
316
|
| `/cron set <job> …` | Change a job in place — `--prompt`, `--schedule`, model, thinking, timeout, name — keeping its id, and therefore its notes |
|
|
284
317
|
| `/cron cost [today\|7d\|all]` | What automation has cost, against `[limits] daily_budget_usd` |
|
|
285
|
-
| `/cron scheduler` |
|
|
286
|
-
| `/
|
|
318
|
+
| `/cron scheduler` | This window, its conversation, and pi-loops status |
|
|
319
|
+
| `/pi-loops start\|stop\|restart\|status` | Start, stop, restart or show pi-loops |
|
|
287
320
|
| `/cron clear <ref>` | Release a run marker left behind by a process that is gone |
|
|
288
321
|
| `/cron gc` | Remove plain jobs whose session was deleted (they are parked as disabled first) |
|
|
289
|
-
| `/cron panel on\|off` | The side panel above the editor:
|
|
290
|
-
| `/cron snapshot` | Write what only this process knows — connected MCP servers and their tools, active tools, hooks,
|
|
322
|
+
| `/cron panel on\|off` | The side panel above the editor: Rules, Loops, Runs, MCP |
|
|
323
|
+
| `/cron snapshot` | Write what only this process knows — connected MCP servers and their tools, active tools, hooks, whether pi-loops is running and its pid — into the session as a `pi_loops_snapshot` entry, for a front end that is not a terminal |
|
|
291
324
|
| `/inbox [list\|all\|claim <n>\|dismiss <n> [reason]\|clear] [--all]` | Triage findings from stateful loops. This project's by default, `--all` for every project — the same scoping `/cron` and `/triggers` use. A reason given with `dismiss` is shown to the loop's next run |
|
|
292
325
|
| `/goal [<condition>]`, `/goal pause\|resume\|clear` | Hold the session to a stop condition: after every turn an evaluator with no tools decides whether it is met, and sends the agent back to work if not (max 8 continuations). Bare `/goal` shows the one in force |
|
|
293
326
|
| `/new-trigger <natural language>` | Create a condition-based rule ("when ~/build.done exists, run cargo test") |
|
|
@@ -299,7 +332,8 @@ State stays in `~/.pi/agent/loops` until you delete it.
|
|
|
299
332
|
| `/session-export [path]`, `/session-import <path>` | Portable `.pisession` archive: transcript + jobs + rules + loop state (in the browser front end, `/session export` and `/session import` too; `/sessions` lists) |
|
|
300
333
|
| `/session-share [--public]` | Upload a redacted transcript as a GitHub gist via `gh`, after showing you what it contains. (pi has its own `/share`, which sends the raw session elsewhere first — see [docs/session-archive.md](docs/session-archive.md)) |
|
|
301
334
|
| `/recipe [list\|show\|add\|update\|remove <name>]` | Install a packaged way of running this project on loops — the issue tracker as a state machine, or one research experiment per run — with one question and one confirmation ([docs/recipes.md](docs/recipes.md)) |
|
|
302
|
-
| `/pi-loops
|
|
335
|
+
| `/pi-loops web` | Open this conversation in the browser page, and copy its one-use link |
|
|
336
|
+
| `/pi-loops [install-launcher \| uninstall-launcher]` | Version and paths; `install-launcher` puts the `pi-loops` command on your `PATH`, `uninstall-launcher` removes it |
|
|
303
337
|
|
|
304
338
|
A schedule is a 5-field cron expression, or one of `hourly` / `daily` / `weekly` (also `每天`), or
|
|
305
339
|
`every 30m`, `in 10m`, `at <ISO>`. The `@hourly` / `@daily` / `@weekly` / `@monthly` spellings work
|
|
@@ -329,7 +363,7 @@ notifications, and lifecycle hooks — each has a page here.
|
|
|
329
363
|
- [docs/goal.md](docs/goal.md) — `/goal`: holding a session to a stop condition
|
|
330
364
|
- [docs/recipes.md](docs/recipes.md) — `/recipe`: a project run on loops, installed in one command; the issue loop and autoresearch, and how to write your own
|
|
331
365
|
- [docs/session-archive.md](docs/session-archive.md) — `/session-export`, `/session-import`
|
|
332
|
-
- [docs/cli.md](docs/cli.md) — the `pi-loops` command line: export, import, the inbox, and looking in on
|
|
366
|
+
- [docs/cli.md](docs/cli.md) — the `pi-loops` command line: export, import, the inbox, and looking in on pi-loops
|
|
333
367
|
- [docs/downstream.md](docs/downstream.md) — what a program may depend on: a recipe directory, `run_start` / `run_end`, `pi-loops inbox --json`; nothing else
|
|
334
368
|
- [docs/web-ui-parity.md](docs/web-ui-parity.md) — what the browser front end owes you, line by line
|
|
335
369
|
- [docs/configuration.md](docs/configuration.md) — paths, `config.toml`, flags, environment
|
|
@@ -349,9 +383,11 @@ notifications, and lifecycle hooks — each has a page here.
|
|
|
349
383
|
| `~/.pi/agent/loops/logs/pi-<pid>.log` | what each pi process's automation did |
|
|
350
384
|
| `~/.pi/agent/loops/triggers.json`, `triggers-audit.jsonl` | dynamic rules and trigger audit |
|
|
351
385
|
| `~/.pi/agent/loops/{config,mcp,hooks}.toml` | configuration |
|
|
352
|
-
| `~/.pi/agent/loops/
|
|
386
|
+
| `~/.pi/agent/loops/host-service.json`, `host.log` | pi-loops record and log |
|
|
387
|
+
| `~/.pi/agent/loops/leases/`, `requests/` | where each conversation is open, and what waits to be delivered to it |
|
|
388
|
+
| `~/.pi/agent/loops/pages/` | one record per conversation open in the browser page: its pi's pid, and when a tab last used it |
|
|
353
389
|
|
|
354
|
-
Set `PI_LOOPS_DIR` to relocate all of it. The notes are yours to edit when the agent got something
|
|
390
|
+
[docs/configuration.md](docs/configuration.md) lists every file. Set `PI_LOOPS_DIR` to relocate all of it. The notes are yours to edit when the agent got something
|
|
355
391
|
wrong, and `logs/pi-<pid>.log` is the file to read after an overnight failure.
|
|
356
392
|
|
|
357
393
|
## Automation outlives the window it was set up in
|
|
@@ -359,19 +395,19 @@ wrong, and `logs/pi-<pid>.log` is the file to read after an overnight failure.
|
|
|
359
395
|
Scheduled work is only worth trusting if it survives you closing the editor. So "pi was restarted"
|
|
360
396
|
is treated here as the normal case rather than the exception.
|
|
361
397
|
|
|
362
|
-
Jobs are machine-global and never expire.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
398
|
+
Jobs are machine-global and never expire. A window starts pi-loops in the background when there is work to run. It runs stateful loops, rule checks and MCP pushes;
|
|
399
|
+
a window runs the plain jobs of the conversation open in it, and pi-loops runs those of
|
|
400
|
+
closed conversations. Rule results go to the conversation that created the rule; if it is closed,
|
|
401
|
+
pi-loops appends the result to it. The inbox gets a result only when that conversation cannot be
|
|
402
|
+
written. A tick a loop
|
|
403
|
+
missed while pi-loops was not running is caught up once, collapsed rather than replayed; a plain
|
|
404
|
+
job's is not, because its prompt was written for a conversation that has moved on (`--catchup` and
|
|
405
|
+
`--no-catchup` override either way).
|
|
406
|
+
|
|
407
|
+
Sub-agents are sessions opened through pi's SDK inside the process that runs them, not child
|
|
408
|
+
processes. Loops and rule checks run in the background, which has no window to share: their runs connect
|
|
409
|
+
their own MCP clients from `mcp.toml` and the project's own servers, so a server that is live only
|
|
410
|
+
because you had a window open is not live to a loop at 3am.
|
|
375
411
|
|
|
376
412
|
[docs/design.md](docs/design.md) has the architecture and the reasoning behind each of these.
|
|
377
413
|
|