@timqi/pier 0.1.0 → 0.1.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/README.md +58 -125
- package/dist/agent/config.js +6 -15
- package/dist/agent/credentials.js +11 -23
- package/dist/agent/events.js +42 -64
- package/dist/agent/listing.js +39 -92
- package/dist/agent/pi.js +103 -252
- package/dist/boards/boards.js +19 -29
- package/dist/channels/attach.js +14 -42
- package/dist/channels/chains.js +33 -37
- package/dist/channels/chunk.js +8 -28
- package/dist/channels/commands.js +3 -14
- package/dist/channels/config.js +33 -52
- package/dist/channels/control.js +4 -13
- package/dist/channels/conversations.js +8 -25
- package/dist/channels/dedup.js +8 -17
- package/dist/channels/gatekeeper.js +13 -23
- package/dist/channels/lark-api.js +23 -63
- package/dist/channels/lark-outbound.js +12 -44
- package/dist/channels/lark-panel.js +7 -23
- package/dist/channels/lark-render.js +18 -62
- package/dist/channels/lark.js +52 -141
- package/dist/channels/lines.js +13 -15
- package/dist/channels/panel.js +16 -36
- package/dist/channels/receipts.js +29 -52
- package/dist/channels/routes.js +3 -9
- package/dist/channels/runtime.js +12 -23
- package/dist/channels/slack-api.js +34 -86
- package/dist/channels/slack-directory.js +7 -23
- package/dist/channels/slack-outbound.js +12 -56
- package/dist/channels/slack-panel.js +4 -13
- package/dist/channels/slack-render.js +23 -91
- package/dist/channels/slack-tool.js +48 -171
- package/dist/channels/slack.js +73 -239
- package/dist/channels/telegram-api.js +8 -20
- package/dist/channels/telegram-panel.js +5 -21
- package/dist/channels/telegram-render.js +13 -40
- package/dist/channels/telegram.js +54 -146
- package/dist/channels/types.js +5 -16
- package/dist/cli.js +17 -41
- package/dist/config-sync.js +87 -4
- package/dist/core/hub.js +7 -20
- package/dist/core/identity.js +20 -59
- package/dist/core/inbound-file.js +15 -49
- package/dist/core/inbox.js +12 -34
- package/dist/core/queue.js +3 -5
- package/dist/core/reply.js +41 -142
- package/dist/core/router.js +209 -264
- package/dist/core/types.js +4 -0
- package/dist/db.js +88 -272
- package/dist/drain.js +57 -50
- package/dist/extensions/index.js +3 -11
- package/dist/extensions/web/anthropic.js +3 -9
- package/dist/extensions/web/artifacts.js +2 -5
- package/dist/extensions/web/content.js +4 -12
- package/dist/extensions/web/http.js +2 -6
- package/dist/extensions/web/language.js +8 -18
- package/dist/extensions/web/openai.js +1 -1
- package/dist/extensions/web/provider.js +5 -18
- package/dist/extensions/web/tools.js +19 -63
- package/dist/lock.js +98 -0
- package/dist/log.js +9 -26
- package/dist/main.js +84 -183
- package/dist/paths.js +10 -26
- package/dist/secrets.js +18 -45
- package/dist/service.js +31 -73
- package/dist/settings.js +19 -63
- package/dist/tasks/agent.js +24 -45
- package/dist/tasks/callbacks.js +8 -16
- package/dist/tasks/command.js +2 -6
- package/dist/tasks/definitions.js +39 -62
- package/dist/tasks/execution.js +41 -39
- package/dist/tasks/groups.js +8 -11
- package/dist/tasks/messages.js +88 -155
- package/dist/tasks/outbox.js +33 -54
- package/dist/tasks/routes.js +4 -7
- package/dist/tasks/runs.js +4 -9
- package/dist/tasks/service.js +29 -42
- package/dist/tasks/store.js +36 -27
- package/dist/tasks/tool.js +59 -60
- package/dist/tools-task.js +20 -60
- package/dist/tools.js +98 -325
- package/dist/update.js +20 -43
- package/dist/web/auth.js +118 -179
- package/dist/web/config-sync.js +2 -2
- package/dist/web/config.js +3 -7
- package/dist/web/explorer.js +10 -21
- package/dist/web/fs.js +20 -42
- package/dist/web/instance.js +35 -82
- package/dist/web/providers.js +5 -11
- package/dist/web/public/assets/{activity-Bl3vZukb.js → activity-B89_hH7q.js} +1 -1
- package/dist/web/public/assets/activity-B89_hH7q.js.br +0 -0
- package/dist/web/public/assets/activity-B89_hH7q.js.gz +0 -0
- package/dist/web/public/assets/{boards-DYuf4Mlj.js → boards-BeKW0ZXK.js} +1 -1
- package/dist/web/public/assets/boards-BeKW0ZXK.js.br +0 -0
- package/dist/web/public/assets/boards-BeKW0ZXK.js.gz +0 -0
- package/dist/web/public/assets/explorer-DIuMlaV3.js +4 -0
- package/dist/web/public/assets/explorer-DIuMlaV3.js.br +0 -0
- package/dist/web/public/assets/explorer-DIuMlaV3.js.gz +0 -0
- package/dist/web/public/assets/index-DzXDXra_.js +85 -0
- package/dist/web/public/assets/index-DzXDXra_.js.br +0 -0
- package/dist/web/public/assets/index-DzXDXra_.js.gz +0 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css +2 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css.br +0 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css.gz +0 -0
- package/dist/web/public/assets/{runs-BLJu7EXN.js → runs-Cwy0mN8i.js} +1 -1
- package/dist/web/public/assets/runs-Cwy0mN8i.js.br +0 -0
- package/dist/web/public/assets/runs-Cwy0mN8i.js.gz +0 -0
- package/dist/web/public/assets/{settings-BrdVh-Zi.js → settings-DzZLmujq.js} +1 -1
- package/dist/web/public/assets/settings-DzZLmujq.js.br +0 -0
- package/dist/web/public/assets/settings-DzZLmujq.js.gz +0 -0
- package/dist/web/public/assets/{task-runs-CeQS1rxa.js → task-runs-BCakxFk8.js} +1 -1
- package/dist/web/public/assets/task-runs-BCakxFk8.js.br +0 -0
- package/dist/web/public/assets/task-runs-BCakxFk8.js.gz +0 -0
- package/dist/web/public/assets/tasks-BlzEbk11.js +4 -0
- package/dist/web/public/assets/tasks-BlzEbk11.js.br +0 -0
- package/dist/web/public/assets/tasks-BlzEbk11.js.gz +0 -0
- package/dist/web/public/index.html +30 -16
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/sw.js +14 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/push.js +55 -77
- package/dist/web/route.js +3 -7
- package/dist/web/server.js +109 -190
- package/dist/web/session-state.js +13 -53
- package/dist/web/types.js +2 -4
- package/dist/web/webpush.js +10 -25
- package/docs/deploy.md +115 -330
- package/package.json +1 -1
- package/skills/pier-boards/SKILL.md +81 -160
- package/skills/pier-help/SKILL.md +23 -20
- package/skills/pier-slack/SKILL.md +2 -2
- package/skills/pier-tasks/SKILL.md +23 -15
- package/dist/config-sync-fetch.js +0 -84
- package/dist/limits.js +0 -14
- package/dist/web/public/assets/activity-Bl3vZukb.js.br +0 -0
- package/dist/web/public/assets/activity-Bl3vZukb.js.gz +0 -0
- package/dist/web/public/assets/boards-DYuf4Mlj.js.br +0 -0
- package/dist/web/public/assets/boards-DYuf4Mlj.js.gz +0 -0
- package/dist/web/public/assets/explorer-qJH_9nTE.js +0 -4
- package/dist/web/public/assets/explorer-qJH_9nTE.js.br +0 -0
- package/dist/web/public/assets/explorer-qJH_9nTE.js.gz +0 -0
- package/dist/web/public/assets/index-Dqdb-Eqt.js +0 -85
- package/dist/web/public/assets/index-Dqdb-Eqt.js.br +0 -0
- package/dist/web/public/assets/index-Dqdb-Eqt.js.gz +0 -0
- package/dist/web/public/assets/index-DzmMzvi_.css +0 -2
- package/dist/web/public/assets/index-DzmMzvi_.css.br +0 -0
- package/dist/web/public/assets/index-DzmMzvi_.css.gz +0 -0
- package/dist/web/public/assets/runs-BLJu7EXN.js.br +0 -0
- package/dist/web/public/assets/runs-BLJu7EXN.js.gz +0 -0
- package/dist/web/public/assets/settings-BrdVh-Zi.js.br +0 -0
- package/dist/web/public/assets/settings-BrdVh-Zi.js.gz +0 -0
- package/dist/web/public/assets/task-runs-CeQS1rxa.js.br +0 -0
- package/dist/web/public/assets/task-runs-CeQS1rxa.js.gz +0 -0
- package/dist/web/public/assets/tasks-bcb3fYdK.js +0 -4
- package/dist/web/public/assets/tasks-bcb3fYdK.js.br +0 -0
- package/dist/web/public/assets/tasks-bcb3fYdK.js.gz +0 -0
|
@@ -5,10 +5,9 @@ description: Publish a Board — a folder of static HTML Pier serves at a stable
|
|
|
5
5
|
|
|
6
6
|
# Building a Pier board
|
|
7
7
|
|
|
8
|
-
A **board**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
session may read or rewrite any board, and closing this one changes nothing.
|
|
8
|
+
A **board** lives in the exact boards folder named under "This Pier instance"
|
|
9
|
+
in `<pier>/AGENTS.md`; do not assume `~/.pier`. Only `<board>/site/` is served.
|
|
10
|
+
Boards survive sessions; any session may read or update them.
|
|
12
11
|
|
|
13
12
|
## Create one
|
|
14
13
|
|
|
@@ -27,13 +26,9 @@ session may read or rewrite any board, and closing this one changes nothing.
|
|
|
27
26
|
}
|
|
28
27
|
```
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- `
|
|
33
|
-
not add random characters of your own; publishing adds them (see below).
|
|
34
|
-
- `description` is the Console list entry: write it for someone who has
|
|
35
|
-
forgotten this conversation.
|
|
36
|
-
- `sessions`: append your own id, never replace — other ids are provenance too.
|
|
29
|
+
- `slug`: `[a-z0-9][a-z0-9-]{0,63}`; keep it short and stable, without random suffixes.
|
|
30
|
+
- `description`: Console list text, understandable without this conversation.
|
|
31
|
+
- `sessions`: append your id; preserve others as provenance.
|
|
37
32
|
|
|
38
33
|
## Publish, then hand over the link
|
|
39
34
|
|
|
@@ -42,40 +37,25 @@ asked for a public or shareable board *in this request*; otherwise leave it
|
|
|
42
37
|
`false` and say the board is private. Never publish personal data or anything
|
|
43
38
|
the user has not seen.
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
in the same message.
|
|
52
|
-
|
|
53
|
-
Asked to make an existing board public? Set `"public": true` and a fresh
|
|
54
|
-
`token` in `board.json`, then reply with the `/p/<slug>-<token>/` link — that is
|
|
55
|
-
the whole answer. Already has a token? Keep it: the link may be out there. No
|
|
56
|
-
verification step, no narrating the edit, no restating what the page holds.
|
|
57
|
-
|
|
58
|
-
The message announcing the board carries **one bare URL** — paste the address
|
|
59
|
-
itself, never `[title](url)`: link labels get mangled or truncated on some chat
|
|
60
|
-
surfaces, and the title is already on the page. No filesystem paths either —
|
|
61
|
-
`…/boards/<slug>/board.json` means nothing to the reader.
|
|
62
|
-
`<pier>/AGENTS.md` gives you the address, so there is nothing to look up:
|
|
63
|
-
|
|
64
|
-
| The user asked for | Send |
|
|
40
|
+
For publishing, preserve the manifest's `token` or add eight hex characters
|
|
41
|
+
from `openssl rand -hex 4`; never invent or reuse another board's token.
|
|
42
|
+
|
|
43
|
+
Use the instance address from `<pier>/AGENTS.md` plus:
|
|
44
|
+
|
|
45
|
+
| Visibility | Path |
|
|
65
46
|
| --- | --- |
|
|
66
|
-
|
|
|
67
|
-
|
|
|
47
|
+
| Private (default) | `/boards/<slug>/` — password required; Console → Boards can publish |
|
|
48
|
+
| Public | `/p/<slug>-<token>/` — copy the token verbatim; 404 unless `public: true` |
|
|
68
49
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
50
|
+
Return **one bare URL**, never both, Markdown link labels or filesystem paths.
|
|
51
|
+
If no address is configured, give the path and point to Console → Settings;
|
|
52
|
+
never guess a host. For a publish-only request, set `public: true` and a token
|
|
53
|
+
if missing, then return only the public URL; no page verification or narration.
|
|
73
54
|
|
|
74
55
|
## Writing the page
|
|
75
56
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
build, no npm, no framework:
|
|
57
|
+
Let the question and evidence determine layout; no sections are required.
|
|
58
|
+
Use semantic HTML without a framework. Adapt this shell's language and content:
|
|
79
59
|
|
|
80
60
|
```html
|
|
81
61
|
<!doctype html>
|
|
@@ -87,143 +67,84 @@ build, no npm, no framework:
|
|
|
87
67
|
<link rel="stylesheet" href="/p/_assets/pier.css">
|
|
88
68
|
</head>
|
|
89
69
|
<body>
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
<p class="muted">18 Feb 2026 · covers 12 repos</p>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<div class="grid">
|
|
97
|
-
<div class="card"><span class="kpi good">12</span> PRs merged <span class="muted">+3 vs last week</span></div>
|
|
98
|
-
<div class="card"><span class="kpi warn">2</span> awaiting decision</div>
|
|
99
|
-
<div class="card"><span class="kpi">98.9%</span> uptime <span class="muted">30 d</span></div>
|
|
100
|
-
</div>
|
|
101
|
-
|
|
102
|
-
<div class="callout warn">
|
|
103
|
-
<strong>Needs you:</strong> the payments migration blocks two teams — approve or defer by Friday.
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<h2>Payments is the only degraded service</h2>
|
|
107
|
-
<table>
|
|
108
|
-
<thead><tr><th>Service</th><th>Status</th><th class="num">p95</th><th>Error budget</th></tr></thead>
|
|
109
|
-
<tbody>
|
|
110
|
-
<tr><td><strong>payments</strong></td><td><span class="tag bad">degraded</span></td><td class="num">910 ms</td>
|
|
111
|
-
<td><span class="bar bad" style="--v:18%"></span></td></tr>
|
|
112
|
-
<tr><td>api</td><td><span class="tag good">healthy</span></td><td class="num">142 ms</td>
|
|
113
|
-
<td><span class="bar good" style="--v:82%"></span></td></tr>
|
|
114
|
-
</tbody>
|
|
115
|
-
</table>
|
|
116
|
-
|
|
117
|
-
<details><summary>All 12 merged PRs</summary>
|
|
118
|
-
<table><thead><tr><th>PR</th><th>Author</th><th>Merged</th></tr></thead>
|
|
119
|
-
<tbody><tr><td>#412</td><td>ana</td><td>Mon</td></tr></tbody></table>
|
|
120
|
-
</details>
|
|
121
|
-
|
|
122
|
-
<footer>Written by Pier · data as of 18 Feb 09:00 · ask for an update to refresh</footer>
|
|
70
|
+
<h1>Weekly digest — infra</h1>
|
|
71
|
+
<p class="lede">The main finding, grounded in the available evidence.</p>
|
|
72
|
+
<footer>Data as of … · sources … · how to refresh …</footer>
|
|
123
73
|
</body>
|
|
124
74
|
</html>
|
|
125
75
|
```
|
|
126
76
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reader who stops after the hero must still leave with the point.
|
|
132
|
-
- **Headings are findings.** "Payments is the only degraded service", not
|
|
133
|
-
"Services". A heading that could top any report — Overview, Summary, Details —
|
|
134
|
-
says nothing about this one.
|
|
135
|
-
- **Every number carries its unit and its baseline**: "142 ms p95, was 120",
|
|
136
|
-
"12 of 40". A bare number is decoration; so is fake precision (98.8724% →
|
|
137
|
-
98.9%).
|
|
138
|
-
- **End at the last useful block.** No closing summary, no filler section. The
|
|
139
|
-
footer holds provenance: data as-of, source, how to refresh.
|
|
77
|
+
Lead with the verdict; headings state findings ("Payments alone is degraded").
|
|
78
|
+
Numbers carry units and baselines ("142 ms p95, was 120", "12 of 40"), without
|
|
79
|
+
false precision. End without filler or a closing summary; put the data
|
|
80
|
+
timestamp, sources and refresh instructions in the footer.
|
|
140
81
|
|
|
141
82
|
## What `pier.css` gives you
|
|
142
83
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
sizes itself: a wide desktop canvas (62rem, 76rem on a very large screen) that
|
|
146
|
-
reflows to one column on a phone, prose held to a readable measure while tables,
|
|
147
|
-
`.grid`, `.split` and `.hero` use the full width — don't add a `max-width` of
|
|
148
|
-
your own. Zebra striping, tables that scroll inside themselves, and `<details>`
|
|
149
|
-
that print open are free. On top of that:
|
|
84
|
+
Classless defaults include readable prose, responsive widths, dark mode,
|
|
85
|
+
scrolling zebra tables and print styles. Override widths only as needed.
|
|
150
86
|
|
|
151
87
|
| Class | Use it for |
|
|
152
88
|
| --- | --- |
|
|
153
|
-
| `.lede` |
|
|
154
|
-
| `.
|
|
155
|
-
| `.
|
|
156
|
-
| `.
|
|
157
|
-
| `.
|
|
158
|
-
| `.
|
|
159
|
-
| `.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Need something it lacks? A `<style>` block or your own CSS file inside `site/`
|
|
167
|
-
is normal, and so is a custom colour or a hand-written layout when the content
|
|
168
|
-
calls for one.
|
|
89
|
+
| `.lede` / `.hero` | opening answer / tinted opening panel |
|
|
90
|
+
| `.grid` + `.card` + `.kpi` | responsive cards with headline numbers |
|
|
91
|
+
| `.callout` / `.tag` | takeaway or ask / status pill |
|
|
92
|
+
| `.num` | right-aligned tabular numbers |
|
|
93
|
+
| `.bar` | proportion: `style="--v:62%"` |
|
|
94
|
+
| `.split` | two columns, stacking on phones |
|
|
95
|
+
| `.muted` | dates, deltas, units, scope |
|
|
96
|
+
|
|
97
|
+
Status modifiers: `.good` healthy/done, `.warn` attention/pending, `.bad`
|
|
98
|
+
broken/blocked; express status in words too. These colour text, cards,
|
|
99
|
+
callouts, tags and bars; cards and tags also accept `.info` for neutral emphasis.
|
|
100
|
+
All helpers are optional. Custom `<style>` or CSS under `site/` may change
|
|
101
|
+
layout and palette while preserving contrast and phone reflow.
|
|
169
102
|
|
|
170
103
|
## Pick the form from the content
|
|
171
104
|
|
|
172
|
-
|
|
|
105
|
+
| Content | Possible form |
|
|
173
106
|
| --- | --- |
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
|
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
| 1–2 data points | a sentence or a `.callout` — a one-row table is a table costume |
|
|
185
|
-
| Long raw output, logs, code | `<details>` at the bottom, or `<pre><code>` trimmed to the lines that matter |
|
|
186
|
-
| Nothing to report | say so in the lede ("all 14 checks green") and stop — short is finished, not thin |
|
|
187
|
-
|
|
188
|
-
Vary the forms: never two blocks of the same kind in a row, and prefer number →
|
|
189
|
-
ask → proof → fold over six paragraphs. A status or decision board earns one
|
|
190
|
-
screenful before the first `<details>`; only a handover or postmortem earns a
|
|
191
|
-
long scroll. Colour is part of the message — green healthy/done, amber
|
|
192
|
-
attention/pending, red broken/blocked, `.info` neutral emphasis — on the numbers
|
|
193
|
-
and status cells that carry the point, never on ordinary prose; that contrast is
|
|
194
|
-
what makes it read as signal.
|
|
107
|
+
| One finding or 1–2 values | sentence; if nothing changed, say so and stop |
|
|
108
|
+
| Headline metrics | KPI cards with deltas; progress includes the total |
|
|
109
|
+
| Required action | prominent ask with deadline |
|
|
110
|
+
| States, rankings or shared comparison criteria | table; worst states first |
|
|
111
|
+
| Alternatives needing separate explanations | parallel sections; recommendation first |
|
|
112
|
+
| Events / trends | dated list / endpoints and delta; chart when shape explains the finding |
|
|
113
|
+
| Large datasets or raw output | aggregate, show relevant rows, fold the rest with a count; trim code/logs |
|
|
114
|
+
|
|
115
|
+
Repeat forms for comparable items; vary them when information changes. Put
|
|
116
|
+
findings and actions first, then evidence; fold only supplementary detail.
|
|
195
117
|
|
|
196
118
|
## Rules
|
|
197
119
|
|
|
198
|
-
- **No secrets
|
|
199
|
-
private paths
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
120
|
+
- **No secrets:** no tokens, API keys, credentials, internal hostnames or
|
|
121
|
+
private paths in page content, including folds and code samples; private
|
|
122
|
+
boards can become public with one Console toggle.
|
|
123
|
+
- **Self-contained:** assets live under `site/` with relative paths, except
|
|
124
|
+
the shipped stylesheet; no CDN, external fonts, analytics or `fetch()`
|
|
125
|
+
(blocked by the public CSP).
|
|
126
|
+
- **Interaction:** prefer `<details>` and anchors; local JS may sort, filter or
|
|
127
|
+
inspect embedded data, without network calls or server runtime. With JS off,
|
|
128
|
+
keep the answer and evidence readable; hide or disable unavailable controls.
|
|
129
|
+
- **Charts:** use when data shape explains the finding; prefer inline SVG,
|
|
130
|
+
a small bundled library only for needed interaction, with text/table fallback.
|
|
131
|
+
- **Real data:** every figure traces to evidence seen this session; expose
|
|
132
|
+
gaps and uncertainty ("no data since Tue", "~3 weeks", "n=3").
|
|
133
|
+
- **Update in place:** edit the existing board; preserve its URL.
|
|
134
|
+
- Write in the user's language, with short headings and no emoji chrome.
|
|
135
|
+
|
|
136
|
+
## Check the page
|
|
137
|
+
|
|
138
|
+
For new pages or layout/interaction changes, run the checks below; for content
|
|
139
|
+
edits, check the affected content and links. Report actual coverage or gaps.
|
|
140
|
+
|
|
141
|
+
- Phone/desktop, light/dark: readable contrast, no page overflow, tables scroll internally.
|
|
142
|
+
- Keyboard/touch: working links and controls, visible focus, usable targets, text status labels.
|
|
143
|
+
- Assets load without console errors; answer and evidence remain readable with JS off.
|
|
220
144
|
|
|
221
145
|
## If a board needs a build
|
|
222
146
|
|
|
223
|
-
Pier ships no toolchain
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
command, output path, where the data came from. Never leave `site/`
|
|
228
|
-
inconsistent with its sources; on an existing board look for that README first
|
|
229
|
-
and rebuild, because hand-patching `site/` is lost on the next build.
|
|
147
|
+
Pier ships no toolchain. If a build is needed, keep sources outside `site/`,
|
|
148
|
+
emit into `site/`, and document install/build commands, output path and data
|
|
149
|
+
sources in `<board>/README.md`. Before editing an existing board, check for
|
|
150
|
+
that README; update sources and rebuild so `site/` stays consistent.
|
|
@@ -6,15 +6,15 @@ description: How Pier itself works — durable sessions, what survives a restart
|
|
|
6
6
|
# How Pier works
|
|
7
7
|
|
|
8
8
|
Pier is the workspace this session runs in: agent sessions behind chat
|
|
9
|
-
surfaces — a web workbench and IM channels (Slack
|
|
10
|
-
scheduled tasks, subagents and boards. Answer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
surfaces — a web workbench and IM channels (Slack, Telegram, Lark) — plus
|
|
10
|
+
scheduled tasks, subagents and boards. Answer from the facts below. If the
|
|
11
|
+
answer is not here, say you do not know how this instance is configured rather
|
|
12
|
+
than guessing: the Console (Pier's admin web UI) is the operator's source of
|
|
13
|
+
truth.
|
|
14
14
|
|
|
15
15
|
## Sessions and persistence
|
|
16
16
|
|
|
17
|
-
- One durable session per conversation: a web chat, a Slack thread, a
|
|
17
|
+
- One durable session per conversation: a web chat, a Slack or Lark thread, a
|
|
18
18
|
Telegram chat or topic. The mapping survives restarts — the next message
|
|
19
19
|
lands in the same transcript with its context intact.
|
|
20
20
|
- Idle sessions leave memory but keep their transcript; they resume
|
|
@@ -48,7 +48,8 @@ operator's source of truth.
|
|
|
48
48
|
into the running turn as a steer.
|
|
49
49
|
- From an IM chat, every mid-turn message steers the running turn directly —
|
|
50
50
|
no `!` needed, and a leading `!` is just content.
|
|
51
|
-
- `/stop` aborts the current turn outright
|
|
51
|
+
- `/stop` from an IM chat aborts the current turn outright; the web has a Stop
|
|
52
|
+
button.
|
|
52
53
|
|
|
53
54
|
## In-chat commands and the settings panel
|
|
54
55
|
|
|
@@ -61,12 +62,13 @@ operator's source of truth.
|
|
|
61
62
|
|
|
62
63
|
## What a turn looks like from outside
|
|
63
64
|
|
|
64
|
-
-
|
|
65
|
-
off when the turn settles; a restart and a periodic sweep clear stragglers.
|
|
65
|
+
- IM channels put a 👀 (Lark: "OnIt") on the message that started a turn and
|
|
66
|
+
take it off when the turn settles; a restart and a periodic sweep clear stragglers.
|
|
66
67
|
A 👀 that never clears means the turn died, not that you are still thinking.
|
|
67
68
|
- Every finished reply carries its cost: elapsed time and the context size at
|
|
68
69
|
completion (`1m14s · 32K tok`) — a running total, not this turn's spend. IM
|
|
69
|
-
shows it as a footer line
|
|
70
|
+
shows it as a footer line; the web shows the duration in the reply's activity
|
|
71
|
+
headline and the context size in the session header.
|
|
70
72
|
- A reply past the platform's message cap is split across several messages
|
|
71
73
|
(Telegram ~3.8k chars); the footer and the next-step buttons ride the last
|
|
72
74
|
one.
|
|
@@ -96,16 +98,17 @@ operator's source of truth.
|
|
|
96
98
|
|
|
97
99
|
## Service restart, reload and update
|
|
98
100
|
|
|
99
|
-
- `pier restart
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- `pier reload
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- `pier update
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
- `pier restart`: refuses new work, waits up to five minutes for active turns
|
|
102
|
+
and Task runs, then restarts. If the deadline aborts an IM turn, the next
|
|
103
|
+
process tells that conversation.
|
|
104
|
+
- `pier reload`: channel adapters re-read configuration and idle, unwatched
|
|
105
|
+
sessions reopen with current agent files on their next message. Streaming or
|
|
106
|
+
watched sessions are not interrupted.
|
|
107
|
+
- `pier update`: a separate updater backs up the database and installs the new
|
|
108
|
+
package while Pier is still up, then hard-stops and starts the service. From
|
|
109
|
+
the shell it does not drain, so it can interrupt active work; the Console's
|
|
110
|
+
Update and auto-update drain first. All three are operator shell commands for an installed Linux systemd
|
|
111
|
+
service, not tools available to the agent.
|
|
109
112
|
|
|
110
113
|
## Only the Console can change
|
|
111
114
|
|
|
@@ -30,8 +30,8 @@ Four things markdown cannot express:
|
|
|
30
30
|
thread. Asking a human to paste their own user ID is never acceptable.
|
|
31
31
|
- Escape `&` `<` `>` when they are text, not markup: `&` `<` `>`.
|
|
32
32
|
- Emoji as `:white_check_mark:`, not the raw glyph.
|
|
33
|
-
-
|
|
34
|
-
rather than truncating.
|
|
33
|
+
- 11,000 chars per message; the tool refuses longer `text`. Split longer
|
|
34
|
+
content across replies in one thread rather than truncating.
|
|
35
35
|
|
|
36
36
|
## Targeting
|
|
37
37
|
|
|
@@ -25,6 +25,8 @@ status query. The receipt's `next` tells you where/how delivery happens:
|
|
|
25
25
|
not for work you launch and forget. Receipt echoes `callbackMode`.
|
|
26
26
|
- `callback:"none"`: no delivery; means you do not want the result, not pull later.
|
|
27
27
|
- Single-run `callback_session_id`: deliver to another existing session.
|
|
28
|
+
Top-level sessions only, and never with `callback:"none"` or `tasks[]`;
|
|
29
|
+
inside a run it is refused, so your result always returns to you.
|
|
28
30
|
|
|
29
31
|
Receipts include `runId`, `taskId`, state. Keep IDs: the callback also names
|
|
30
32
|
`Run:`, and there is no lookup by task. `triggerSource` names the actual invoker
|
|
@@ -47,20 +49,21 @@ policy (`manual` means on demand).
|
|
|
47
49
|
- Stored role: `run` with `task_id` and optional `input`;
|
|
48
50
|
`session_mode:"fresh"` overrides a stored reuse policy. Archived tasks refuse.
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
`timeoutSeconds` also works in the shorthand and in a `tasks[]` entry; use a
|
|
53
|
+
`task` draft for reuse:
|
|
51
54
|
|
|
52
55
|
```json
|
|
53
|
-
{"operation":"run","task":{"timeoutSeconds":
|
|
56
|
+
{"operation":"run","task":{"timeoutSeconds":7200,"action":{"type":"agent","session":{"mode":"reuse","sessionId":"..."},"prompt":"Check the result"}}}
|
|
54
57
|
```
|
|
55
58
|
|
|
56
|
-
Inline drafts may omit `trigger`; only `manual` is allowed.
|
|
57
|
-
`callback` is
|
|
58
|
-
matter for saved schedules (below).
|
|
59
|
+
Inline drafts may omit `trigger`; only `manual` is allowed. A nested
|
|
60
|
+
`callback` is refused: use the top-level delivery options above. Nested
|
|
61
|
+
callbacks matter for saved schedules (below).
|
|
59
62
|
|
|
60
63
|
## Groups and chains
|
|
61
64
|
|
|
62
|
-
`tasks[]` needs 2+ entries: prompt strings,
|
|
63
|
-
drafts, or `{task_id}`. Do not combine it with `task`, `task_id` or `session_mode`.
|
|
65
|
+
`tasks[]` needs 2+ entries: prompt strings,
|
|
66
|
+
`{prompt,cwd?,launch?,name?,timeoutSeconds?}`, full drafts, or `{task_id}`. Do not combine it with `task`, `task_id` or `session_mode`.
|
|
64
67
|
|
|
65
68
|
```json
|
|
66
69
|
{"operation":"run","tasks":["Review correctness","Review test gaps"],"join":"all"}
|
|
@@ -93,13 +96,15 @@ available IDs by callback; invalid `launch.thinking` fails the call.
|
|
|
93
96
|
|
|
94
97
|
## Control and decisions
|
|
95
98
|
|
|
96
|
-
`message` must be non-empty,
|
|
99
|
+
`message` must be non-empty, at most 16 KiB. Controls require ownership: your delegated
|
|
97
100
|
trees, or only descendants when you are a subagent.
|
|
98
101
|
|
|
99
102
|
- `steer`: interrupt a child with corrections.
|
|
100
103
|
- `follow_up`: queue guidance after its current turn.
|
|
101
104
|
- `resume`: terminal run only; same session, new run ID/callback, same depth,
|
|
102
|
-
message as prompt. Expires its unanswered decision.
|
|
105
|
+
message as prompt. Expires its unanswered decision. Being a new run, it takes
|
|
106
|
+
the same `callback` and `callback_session_id` as `run`, under the same rule:
|
|
107
|
+
a subagent may not redirect them.
|
|
103
108
|
- `cancel`: run or group; cascades to descendants, terminal runs unchanged.
|
|
104
109
|
|
|
105
110
|
`steer`/`follow_up` require a non-terminal run; undelivered guidance expires when
|
|
@@ -151,12 +156,15 @@ draft, including `trigger`. Schedules notify only with nested
|
|
|
151
156
|
which scheduled runs lack.
|
|
152
157
|
|
|
153
158
|
- Inside a run: Agent actions only (stored/inline), no reuse or create/update.
|
|
154
|
-
- Depth 0–2:
|
|
155
|
-
root allows 16 descendant runs (depth ≥1, resumes included). Your direct
|
|
159
|
+
- Depth 0–2: three levels of nesting below the invoking session; a fourth
|
|
160
|
+
level errors. Each root allows 16 descendant runs (depth ≥1, resumes included). Your direct
|
|
156
161
|
children are separate roots and do not count toward that limit.
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
- Default timeout
|
|
160
|
-
|
|
162
|
+
- 6 Agent runs execute instance-wide; others queue without error. A queued run
|
|
163
|
+
waits **unbounded**, ended only by cancellation or a restart.
|
|
164
|
+
- Default timeout 3600s; `timeoutSeconds:1–86400` in a draft, the `prompt`
|
|
165
|
+
shorthand or a `tasks[]` entry. It starts when the run does, not at enqueue,
|
|
166
|
+
and reports `failed / task timed out`.
|
|
161
167
|
- Restart marks queued/running runs `interrupted`; callbacks still apply.
|
|
162
168
|
During drain, new roots are refused: retry after restart.
|
|
169
|
+
- Watch probes that matched nothing are kept only 50 deep per watch; older
|
|
170
|
+
ones are deleted unless a message, a pending callback or a resume needs them.
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// Fetch one configuration document from the operator's source URL: HTTPS with
|
|
2
|
-
// no credentials, redirects followed while they stay HTTPS, JSON capped at
|
|
3
|
-
// 1 MiB so a hostile or broken source cannot exhaust memory.
|
|
4
|
-
export const CONFIG_SYNC_BYTES = 1024 * 1024;
|
|
5
|
-
const MAX_REDIRECTS = 5;
|
|
6
|
-
export function configSourceUrl(raw) {
|
|
7
|
-
let url;
|
|
8
|
-
try {
|
|
9
|
-
url = new URL(raw.trim());
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
throw new Error("A valid HTTPS source URL is required");
|
|
13
|
-
}
|
|
14
|
-
if (url.protocol !== "https:" || url.username || url.password || url.hash || raw.length > 4096) {
|
|
15
|
-
throw new Error("Source must be HTTPS, without credentials or a fragment");
|
|
16
|
-
}
|
|
17
|
-
return url;
|
|
18
|
-
}
|
|
19
|
-
export async function downloadConfig(raw, etag, signal) {
|
|
20
|
-
let url = configSourceUrl(raw);
|
|
21
|
-
for (let hop = 0;; hop++) {
|
|
22
|
-
signal.throwIfAborted();
|
|
23
|
-
let res;
|
|
24
|
-
try {
|
|
25
|
-
res = await fetch(url, {
|
|
26
|
-
method: "GET", redirect: "manual", signal,
|
|
27
|
-
headers: { accept: "application/json", ...(etag ? { "if-none-match": etag } : {}) },
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
throw new Error(signal.aborted ? "Configuration download timed out or was cancelled" : "Could not connect to source");
|
|
32
|
-
}
|
|
33
|
-
const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
34
|
-
if (location) {
|
|
35
|
-
await res.body?.cancel().catch(() => { });
|
|
36
|
-
if (hop >= MAX_REDIRECTS)
|
|
37
|
-
throw new Error("Source redirected too many times");
|
|
38
|
-
try {
|
|
39
|
-
url = configSourceUrl(new URL(location, url).href);
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
throw new Error("Source redirected to a location that is not HTTPS");
|
|
43
|
-
}
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (res.status !== 200 && res.status !== 304) {
|
|
47
|
-
await res.body?.cancel().catch(() => { });
|
|
48
|
-
throw new Error(res.status === 404 || res.status === 410
|
|
49
|
-
? "Source link was revoked or does not exist"
|
|
50
|
-
: `Source returned HTTP ${String(res.status)}`);
|
|
51
|
-
}
|
|
52
|
-
const status = res.status === 304 ? 304 : 200;
|
|
53
|
-
if (status === 200 && !/^application\/json(?:\s*;|$)/i.test(res.headers.get("content-type") ?? "")) {
|
|
54
|
-
await res.body?.cancel().catch(() => { });
|
|
55
|
-
throw new Error("Source did not return JSON");
|
|
56
|
-
}
|
|
57
|
-
return { status, etag: res.headers.get("etag"), body: await read(res, signal) };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async function read(res, signal) {
|
|
61
|
-
const reader = res.body?.getReader();
|
|
62
|
-
if (!reader)
|
|
63
|
-
return "";
|
|
64
|
-
const chunks = [];
|
|
65
|
-
let size = 0;
|
|
66
|
-
try {
|
|
67
|
-
for (;;) {
|
|
68
|
-
const { done, value } = await reader.read();
|
|
69
|
-
if (done)
|
|
70
|
-
break;
|
|
71
|
-
size += value.length;
|
|
72
|
-
if (size > CONFIG_SYNC_BYTES)
|
|
73
|
-
throw new Error("Configuration exceeds 1 MiB");
|
|
74
|
-
chunks.push(Buffer.from(value));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
await reader.cancel().catch(() => { });
|
|
79
|
-
if (err instanceof Error && err.message === "Configuration exceeds 1 MiB")
|
|
80
|
-
throw err;
|
|
81
|
-
throw new Error(signal.aborted ? "Configuration download timed out or was cancelled" : "Configuration download failed");
|
|
82
|
-
}
|
|
83
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
84
|
-
}
|
package/dist/limits.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// The numbers more than one area has to agree on.
|
|
2
|
-
//
|
|
3
|
-
// Not policy or behaviour — a value several modules must spell the same way,
|
|
4
|
-
// and a wrong copy makes two surfaces disagree about one session: a title
|
|
5
|
-
// truncated to a different length depending on which path derived it.
|
|
6
|
-
//
|
|
7
|
-
// Here because a leaf may be imported by every area and depends on nothing
|
|
8
|
-
// itself, which is the only shape that fits: agent/ derives a title and must
|
|
9
|
-
// not import core/, web/ derives one too and must not import agent/, and the
|
|
10
|
-
// browser needs the same numbers with no runtime behind them.
|
|
11
|
-
/** How much of a message becomes a title, wherever one is derived: the listing
|
|
12
|
-
* reading a transcript (agent/listing.ts), a rename's fallback (agent/pi.ts),
|
|
13
|
-
* the fill at first prompt and the rename boundary (web/). */
|
|
14
|
-
export const SESSION_TITLE_MAX = 80;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|