@tshu-w/pi-agents 0.0.0-stage → 0.1.0

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.
Files changed (48) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +81 -2
  3. package/bin/daemon.mjs +204 -0
  4. package/bin/host.mjs +170 -0
  5. package/bin/pd.mjs +139 -0
  6. package/docs/PROGRAM.md +310 -0
  7. package/docs/SPEC.md +427 -0
  8. package/docs/WORKBENCH.md +165 -0
  9. package/package.json +62 -4
  10. package/src/agents/agents.ts +882 -0
  11. package/src/agents/registry.ts +257 -0
  12. package/src/agents/scheduler.ts +204 -0
  13. package/src/agents/search.ts +131 -0
  14. package/src/agents/tool.ts +205 -0
  15. package/src/daemon/client.mjs +84 -0
  16. package/src/daemon/router.mjs +103 -0
  17. package/src/daemon/sessions.mjs +129 -0
  18. package/src/daemon/version.mjs +19 -0
  19. package/src/host/client.mjs +139 -0
  20. package/src/host/extension.ts +74 -0
  21. package/src/host/frames.mjs +34 -0
  22. package/src/host/keys.mjs +19 -0
  23. package/src/index.ts +208 -0
  24. package/src/output.ts +56 -0
  25. package/src/params.ts +4 -0
  26. package/src/pi.mjs +16 -0
  27. package/src/programs/codemode.ts +74 -0
  28. package/src/programs/execute.ts +131 -0
  29. package/src/programs/host-patches.ts +128 -0
  30. package/src/programs/loadout.ts +99 -0
  31. package/src/programs/programs.ts +284 -0
  32. package/src/programs/sandbox.ts +90 -0
  33. package/src/programs/scope.ts +94 -0
  34. package/src/programs/tool.ts +198 -0
  35. package/src/render-call.ts +90 -0
  36. package/src/roots/discovery.mjs +113 -0
  37. package/src/roots/guard.mjs +227 -0
  38. package/src/roots/lock-extension.mjs +167 -0
  39. package/src/roots/locks.mjs +167 -0
  40. package/src/roots/paths.mjs +66 -0
  41. package/src/roots/report.ts +156 -0
  42. package/src/roots/runtime.ts +174 -0
  43. package/src/roots/snapshot.mjs +46 -0
  44. package/src/roots/socket-recovery.mjs +21 -0
  45. package/src/roots/transport.mjs +341 -0
  46. package/src/ui/panel.ts +96 -0
  47. package/src/ui/viewer.ts +445 -0
  48. package/src/workbench.mjs +587 -0
package/docs/SPEC.md ADDED
@@ -0,0 +1,427 @@
1
+ # Pi Agents Specification
2
+
3
+ Pi Agents lets Agents in Pi discover, message, and wait for each other.
4
+ Programs built on these operations are specified in PROGRAM.md.
5
+
6
+ Terms follow Pi's
7
+ [Pico5 specification](https://github.com/earendil-works/pi/blob/main/packages/durable/docs/pico-v5.md).
8
+
9
+ ## 1. Semantic model
10
+
11
+ An **Agent** is an addressable conversation with a stable identity. The Agent
12
+ that spawns another Agent is its **owner**. A root Agent has no owner and is
13
+ usually opened by a user. Ownership forms trees.
14
+
15
+ An Agent is shown by its name and **short ID**: the shortest prefix of its
16
+ ID, at least 8 characters, that no other Agent visible to the reader has.
17
+ An address is an ID, a name, or a unique ID prefix, tried in that order.
18
+
19
+ A **message** carries text from one Agent to another. An **input** asks the
20
+ recipient to act. A **write** only adds to the recipient's conversation.
21
+
22
+ A **turn** is one continuous run of an Agent's model and tools. Its final
23
+ assistant message is the **answer** to the inputs it handled.
24
+
25
+ An owner can **wait** for answers to the inputs it sends to its owned Agents.
26
+
27
+ **Visibility** determines which Agents an Agent can discover and message.
28
+
29
+ ## 2. Operations
30
+
31
+ ### 2.1 spawn
32
+
33
+ `spawn` creates a named, owned Agent and sends it a first input. It
34
+ accepts an optional working directory, model, thinking level, and forked
35
+ context, and returns the Agent's ID. Names are unique among siblings.
36
+
37
+ ### 2.2 send
38
+
39
+ `send` sends a message to a visible Agent by address. An ambiguous or
40
+ unknown address is rejected.
41
+
42
+ An input to an Agent with a turn in progress is either answered by the current turn
43
+ (`steer`) or by a new turn after it (`followUp`, the default).
44
+
45
+ A write is added at the recipient's next tool boundary, or at once if it is
46
+ idle. A write can go to several Agents at once.
47
+
48
+ ### 2.3 wait
49
+
50
+ `wait` returns results of inputs sent to owned Agents. It can target
51
+ specific Agents and take a timeout.
52
+
53
+ ### 2.4 list
54
+
55
+ `list` returns visible Agents with their ID, name, working directory, and
56
+ state.
57
+
58
+ ### 2.5 abort
59
+
60
+ `abort` stops an owned Agent's current turn and withdraws its queued
61
+ inputs. The Agent stays available.
62
+
63
+ ## 3. Lifecycle
64
+
65
+ An Agent is `running` while a turn is in progress, `waiting` while its turn
66
+ waits in `wait` (§4), `queued` while its next turn waits for a slot (§4),
67
+ and `idle` otherwise. An Agent goes offline when its runtime unloads it; it
68
+ stays `idle`.
69
+
70
+ `send` returns once the recipient's runtime accepts the message, waking it
71
+ if needed. Acceptance does not mean the recipient has handled it; a message
72
+ not yet handled is lost if the recipient exits.
73
+
74
+ An input to an offline Agent wakes it. A write never wakes an Agent.
75
+
76
+ Owned Agents run independently of their owner's turns. When their owner
77
+ goes offline, they go offline too, from the bottom of the tree up, and their
78
+ unfinished inputs end as `aborted`. They return as `idle` when their owner
79
+ is loaded again.
80
+
81
+ On abort or when going offline, work that has not stopped within a timeout
82
+ is abandoned: it no longer counts as live work and may keep running until
83
+ the process exits; the timeout is reported.
84
+
85
+ A root Agent is `running` while it has a turn in progress or pending messages,
86
+ or while its tree has live work (§7 Task panel); otherwise it is `idle`.
87
+
88
+ ## 4. Visibility and limits
89
+
90
+ An Agent sees every Agent in its own tree. A root Agent also sees other root
91
+ Agents.
92
+
93
+ Limits apply to owned Agents in each tree.
94
+
95
+ An owned Agent needs a slot to run a turn. `maxConcurrent` (default 3) is
96
+ the number of slots. An input that would start a turn waits for a free
97
+ slot, and `spawn` or `send` reports that it is queued. A `steer` input to
98
+ an Agent with a turn in progress joins it and needs no new slot. An Agent gives
99
+ up its slot while it waits in `wait`, and takes one again before
100
+ continuing, ahead of inputs that have not started.
101
+
102
+ An input is outstanding from acceptance until its turn ends or it is
103
+ withdrawn. `maxOutstanding` (default 8) limits outstanding inputs; an input
104
+ beyond it is rejected. `maxConcurrent` cannot exceed `maxOutstanding`.
105
+
106
+ ## 5. Results and notifications
107
+
108
+ An input sent to an owned Agent ends as `completed` when its turn answers
109
+ it, `failed` when its turn fails, or `aborted` when it is aborted or
110
+ withdrawn. Its result is the answer or the error. Only inputs from the
111
+ owner have results; the recipient replies to other senders with `send`.
112
+
113
+ `wait` returns unread results in order and marks them read. On timeout, it
114
+ returns the results so far and names the Agents still pending. It can also
115
+ return read results again.
116
+
117
+ When an input ends and its owner is not waiting for that Agent, the owner
118
+ receives a notification, unless the owner aborted it. The notification is an input delivered as `steer`,
119
+ and it names the Agent and how the input ended. It does not include the
120
+ result; the owner reads the result with `wait`.
121
+
122
+ A recipient sees each message with its sender's name and ID.
123
+
124
+ ## 6. Model-facing tool
125
+
126
+ ### `agent`
127
+
128
+ description:
129
+
130
+ Create, message, and coordinate Agents. `spawn` creates an owned Agent and
131
+ sends its first input; `send` sends an input or a write to a visible Agent;
132
+ `wait` waits for owned Agents and returns unread results; `list` finds
133
+ visible Agents; `abort` stops an owned Agent's current turn and queued
134
+ inputs while keeping it available. When an owned Agent finishes an input
135
+ while its owner is not waiting for it, the owner receives a notification
136
+ naming the Agent and outcome; `wait` returns the result. Owned Agents under
137
+ the same root share `${maxConcurrent}` execution slots and a limit of
138
+ `${maxOutstanding}` inputs that have not ended. Inputs that start a new
139
+ turn queue when all slots are busy; new inputs are rejected at the input
140
+ limit.
141
+
142
+ promptSnippet:
143
+
144
+ Create, message, and coordinate Agents
145
+
146
+ promptGuidelines:
147
+
148
+ - Use `agent(action='spawn', name=..., message=...)` for concrete, bounded
149
+ work with a clear expected result. Keep the first message self-contained;
150
+ use `context='fork'` only when the Agent needs the caller's conversation.
151
+ - For independent work, start the Agents before waiting and partition tasks
152
+ into non-overlapping responsibilities.
153
+ - Use `agent(action='wait', ...)` when the next step depends on Agent
154
+ results. Prefer one longer wait over repeated short polling.
155
+
156
+ parameters:
157
+
158
+ - `action`: Operation and applicable parameters: `spawn(name, message, cwd?, context?, model?, thinkingLevel?)`, `send(target, message, deliverAs?)`, `wait(target?, history?, timeout?)`, `list(query?, state?, limit?, offset?)`, or `abort(target)`.
159
+ - `name`: Name of the new Agent for `spawn`; unique among the caller's Agents.
160
+ - `message`: Non-empty text for `spawn` or `send`.
161
+ - `cwd`: Working directory for `spawn` (default: the caller's current working directory).
162
+ - `context`: Context for `spawn` (default: fresh). fresh starts without the caller's conversation; fork snapshots it.
163
+ - `model`: Model for `spawn` as provider/modelId (default: the caller's model).
164
+ - `thinkingLevel`: Thinking level for `spawn`: off, minimal, low, medium, high, or xhigh (default: the caller's level).
165
+ - `target`: Agent ID, name, or unique ID prefix for `send`, `wait`, or `abort`. `wait` and `send` with `deliverAs='write'` also accept an array. When omitted for `wait`, selects all owned Agents with pending or unread results.
166
+ - `deliverAs`: Delivery for `send` (default: followUp). followUp waits until the recipient's current turn ends; steer delivers after its current tool calls, before the next model call; write delivers like steer but never starts a turn and has no result.
167
+ - `history`: Number of most recent read results to return again per selected Agent for `wait` (default: 0).
168
+ - `timeout`: Maximum seconds for `wait` (default: 30, min: 10, max: 3600). Timeout does not abort Agents.
169
+ - `query`: Search query for `list`, matching ID, name, cwd, summaries, and user messages. Case-insensitive; spaces mean AND; `|` means OR.
170
+ - `state`: State for `list`: running, waiting, queued, or idle.
171
+ - `limit`: Maximum Agents returned by `list` (default: 20, max: 200).
172
+ - `offset`: Number of Agents to skip for `list` (default: 0).
173
+
174
+ results:
175
+
176
+ In these formats, `<id>` is the Agent's short ID. Details use full IDs;
177
+ arrays remain present when empty.
178
+
179
+ `spawn`:
180
+
181
+ ```text
182
+ Agent <name> (<id>) started.
183
+ Agent <name> (<id>) queued: all <N> slots are busy.
184
+ ```
185
+
186
+ Details:
187
+
188
+ ```text
189
+ { id, name, queued }
190
+ ```
191
+
192
+ `send` returns acceptance, not completion:
193
+
194
+ ```text
195
+ Input accepted by <name> (<id>).
196
+ Input accepted by <name> (<id>), queued: all <N> slots are busy.
197
+ Write accepted by <name> (<id>), ...
198
+ ```
199
+
200
+ Details:
201
+
202
+ ```text
203
+ followUp / steer: { id, queued }
204
+ write: { ids }
205
+ ```
206
+
207
+ `wait` returns one block per result, followed by pending Agents on timeout:
208
+
209
+ ```text
210
+ <agent-result name="<name>" id="<id>" status="<outcome>">
211
+ <answer or error>
212
+ </agent-result>
213
+
214
+ Still pending: <name> (<id>), ...
215
+ ```
216
+
217
+ A result returned again through `history` adds `history="true"`. A single
218
+ unread completed result, with nothing pending, is returned as the answer
219
+ alone. When there are no results or pending inputs:
220
+
221
+ ```text
222
+ No results.
223
+ ```
224
+
225
+ Details:
226
+
227
+ ```text
228
+ { results: [{ id, name, state, history, result? }], pending: [id] }
229
+ ```
230
+
231
+ `state` is the input outcome (§5); `history` marks a previously read result.
232
+
233
+ `list` returns one entry per Agent. With a query, entries are ordered by
234
+ their most recent match. When a summary or user message matches, the entry
235
+ shows the latest matching excerpt and its time:
236
+
237
+ ```text
238
+ <name> (<id>) <state> <cwd>
239
+ YYYY-MM-DD HH:MM …<matching excerpt>…
240
+ ```
241
+
242
+ When more entries remain:
243
+
244
+ ```text
245
+ [N more results. Use offset=X to continue.]
246
+ ```
247
+
248
+ When no Agents match:
249
+
250
+ ```text
251
+ No matching Agents.
252
+ ```
253
+
254
+ Details:
255
+
256
+ ```text
257
+ { total, agents: [{ id, name?, ownerId?, state }] }
258
+ ```
259
+
260
+ `agents` is the current page; `total` counts all matches. `state` is the
261
+ Agent's current state (§3).
262
+
263
+ `abort`:
264
+
265
+ ```text
266
+ Agent <name> (<id>) aborted.
267
+ Agent <name> (<id>) has no turn or queued inputs.
268
+ ```
269
+
270
+ Details:
271
+
272
+ ```text
273
+ { id, aborted }
274
+ ```
275
+
276
+ For unnamed root Agents, show the ID without a name or parentheses.
277
+
278
+ errors:
279
+
280
+ Invalid calls fail with an error. Errors that need a follow-up say what to
281
+ do:
282
+
283
+ ```text
284
+ No visible Agent matches "<target>". Use list to find Agents.
285
+ ```
286
+
287
+ ```text
288
+ "<target>" matches several Agents:
289
+ <name> (<id>) <state> <cwd>
290
+ ...
291
+
292
+ Retry with a longer ID prefix.
293
+ ```
294
+
295
+ ```text
296
+ Name "<name>" is already used by <id>.
297
+ ```
298
+
299
+ ```text
300
+ Input rejected: <N> inputs have not ended. Wait for results or abort an Agent.
301
+ ```
302
+
303
+ ```text
304
+ <name> (<id>) is not owned by the caller.
305
+ ```
306
+
307
+ truncation:
308
+
309
+ Text beyond Pi's output limits is truncated with Pi's native notice, and
310
+ the full text is saved to a file whose path appears in the notice. `wait`
311
+ adds results in order; the result that crosses the limit is truncated but
312
+ keeps its closing tag, and later results are omitted. A truncated result
313
+ counts as read. Omitted results stay unread, and the text ends with:
314
+
315
+ ```text
316
+ [Results omitted: <name> (<id>), ... Use wait with fewer targets.]
317
+ ```
318
+
319
+ Details retain metadata, `truncation`, and `fullOutputPath`.
320
+ `wait.results[].result` contains only retained text and is absent for
321
+ omitted results.
322
+
323
+ messages:
324
+
325
+ A notification to the owner:
326
+
327
+ ```text
328
+ Agent <name> (<id>) <outcome>.
329
+ ```
330
+
331
+ A recipient sees each message in an `agent-message` element naming the
332
+ sender. An input from a sender other than the owner also carries a note
333
+ asking for a reply:
334
+
335
+ ```text
336
+ <agent-message from="<name>" id="<id>">
337
+ <body>
338
+ </agent-message>
339
+ ```
340
+
341
+ ```text
342
+ <agent-message from="<name>" id="<id>" note="Reply with send">
343
+ <body>
344
+ </agent-message>
345
+ ```
346
+
347
+ ## 7. Human interface
348
+
349
+ ### Task panel
350
+
351
+ A panel above the editor shows the current Agent's live work:
352
+
353
+ Tasks (<N> live, /tasks to hide)
354
+ Agent <name> (<id>) <state> <N> pending
355
+ Agent <name> (<id>) <state>
356
+ Program <id> running
357
+ Agent <name> (<id>) <state>
358
+ +<N> more
359
+
360
+ The live work is the owned Agents that are not `idle`, and the running
361
+ background Programs with their Agents that are not `idle`. The rows list
362
+ them as a tree in the order they started, each
363
+ under its owner and each Program under its caller, with the owners needed to
364
+ keep the tree. Rows that do not fit are counted in the last row.
365
+
366
+ The panel updates as states change and is shown while it has a row.
367
+ `/tasks` hides or shows it.
368
+
369
+ ### Agent viewer
370
+
371
+ `/agents` lists the Agents in the current Agent's tree, including Programs'
372
+ Agents, those not `idle` first and newest first within each group, with their state
373
+ and latest activity, and opens the selected one in a viewer. An Agent not owned
374
+ by the current Agent is named with its owners up to it, as
375
+ `<name> ‹ <owner> ‹ …`, where a Program is `Program <id>`. The viewer shows the
376
+ Agent's conversation and updates while it works; the current Session keeps
377
+ running behind it.
378
+
379
+ Text entered in the viewer is the user's input to that Agent and is sent as
380
+ in Pi's editor.
381
+
382
+ `app.interrupt` stops the Agent's current turn, as `abort` does.
383
+ `app.clear` clears the input, and closes the viewer when the input is empty.
384
+
385
+ ### Workbench
386
+
387
+ `pd agents` opens the workbench, which lists every root Session on this
388
+ machine and lets the user open and message them. Interactive Sessions keep
389
+ running after their terminal closes. See [WORKBENCH.md](WORKBENCH.md).
390
+
391
+ ## 8. Configuration
392
+
393
+ Settings live under `"pi-agents"` in the global `settings.json`:
394
+
395
+ ```json
396
+ {
397
+ "pi-agents": {
398
+ "maxConcurrent": 3,
399
+ "maxOutstanding": 8,
400
+ "extensions": []
401
+ }
402
+ }
403
+ ```
404
+
405
+ - `maxConcurrent`, `maxOutstanding`: limits from §4. Both are positive
406
+ integers with `maxConcurrent <= maxOutstanding`.
407
+ - `extensions`: extension paths loaded by owned Agents, relative to Pi's
408
+ agent directory. An owned Agent can use only tools its owner can use.
409
+
410
+ Invalid settings produce a startup warning, and the defaults are used.
411
+
412
+ ## 9. Runtime
413
+
414
+ Each Agent is a persistent Pi Session. A root Agent is an ordinary Session.
415
+ An owned Agent is a child Session that records its owner and root, and runs
416
+ in its owner's process.
417
+
418
+ A delivered message enters the recipient's current branch. An offline root
419
+ Agent is started in a host, detached, with its saved working directory and
420
+ model and the current configuration (WORKBENCH §4).
421
+ If a root process cannot finish cleanup within a grace period on exit, it is
422
+ forcibly terminated.
423
+ Before shutting down an idle Session, another extension can emit
424
+ `busy:query` on `pi.events` with `{ busy: false }`; it is set to `true`
425
+ while the tree has live work.
426
+ One runtime uses a Session at a time. If a Session is occupied at interactive
427
+ startup, its lock error stays visible until the user dismisses it, then Pi exits.
@@ -0,0 +1,165 @@
1
+ # Pi Agents Workbench
2
+
3
+ ## 1. Overview
4
+
5
+ The workbench shows the user's root Sessions in one terminal view. From it the
6
+ user opens a Session, reads its latest result, or sends it a message. Sessions
7
+ started with `pd` keep running after their terminal closes.
8
+
9
+ Three kinds of process take part:
10
+
11
+ - **Host**: one per Session. It runs Pi's full TUI in a pseudo-terminal and
12
+ relays it to whichever terminal is attached.
13
+ - **Daemon**: one per user. It tracks Sessions, delivers messages, and starts
14
+ hosts.
15
+ - **Workbench**: the daemon's terminal view, opened with `pd agents`.
16
+
17
+ Every running root Session reports its state to the daemon. The daemon starts
18
+ when a Session or the workbench first needs it, and exits 30 seconds after its
19
+ last connection closes.
20
+
21
+ Files live in `${XDG_STATE_HOME:-~/.local/state}/pi/agents/`:
22
+
23
+ - `workbench.json`: seen marks and which Sessions are listed. The file is
24
+ versioned.
25
+ - `host.log`, `daemon.log`: logs.
26
+
27
+ Sockets and Session locks go in `$XDG_RUNTIME_DIR/pi-agents/`. macOS has no
28
+ `XDG_RUNTIME_DIR`, so there they go in `runtime/` under the state directory.
29
+
30
+ ## 2. Setup
31
+
32
+ Install with npm to get the `pd` command, also named `pi-agents`, then have Pi
33
+ load the extension from the same copy, so the command and the extension are
34
+ always the same version:
35
+
36
+ npm install -g @tshu-w/pi-agents
37
+ pi install "$(npm root -g)/@tshu-w/pi-agents"
38
+
39
+ `pd` runs whichever `pi` is on `PATH`. Sessions started with plain `pi` also
40
+ load the extension, but run outside hosts and end with their terminal.
41
+
42
+ ## 3. Commands
43
+
44
+ - `pd [pi options]` starts Pi interactively in a new host and attaches the
45
+ terminal. If the Session is already running in a host, `pd` attaches to it
46
+ instead.
47
+ - `pd agents` opens the workbench.
48
+ - `pd attach <session>` attaches to a Session, starting it first if it is not
49
+ running.
50
+ - Non-interactive starts (`-p`, `--mode`, piped stdin) and package commands
51
+ (`pd install`, `pd update`, ...) run Pi directly.
52
+
53
+ A host inherits the environment of the process that asked for it, because Pi
54
+ reads terminal features from the environment once, at startup.
55
+
56
+ A Session running outside a host still appears in the workbench, but can be
57
+ attached only after it exits.
58
+
59
+ ## 4. Attach and detach
60
+
61
+ Only one terminal is attached at a time. A new terminal takes over, and the
62
+ previous one is told how to reattach.
63
+
64
+ On attach, the host restores the screen and the terminal modes Pi has set
65
+ (keyboard protocols, bracketed paste, mouse, title, program status). Images
66
+ come back when the host then has Pi redraw.
67
+
68
+ A host started from a terminal waits for that terminal to attach before it
69
+ starts Pi, so Pi detects the terminal's colors and keyboard protocol just as it
70
+ would without a host. A host started in the background passes the queries Pi
71
+ sent at startup to the first terminal that attaches, and Pi finishes detecting
72
+ then.
73
+
74
+ Closing the window, pressing `Ctrl+Z` (the `pi-agents.detach` keybinding), or
75
+ running `/detach` detaches. The Session keeps running, and the terminal prints:
76
+
77
+ Pi keeps running. To reattach, run pd attach <session>
78
+
79
+ A host disables suspend, since a suspended Pi would stay stopped; `Ctrl+Z`
80
+ detaches in its place.
81
+
82
+ A detached Session exits once its tree has had no live work for 5 minutes. A
83
+ message to a Session that is not running starts it in a host, detached.
84
+
85
+ ## 5. Workbench
86
+
87
+ The list shows running Sessions, Sessions that ran without a terminal (started
88
+ here, woken by a message, or detached), Sessions opened here, and Sessions
89
+ with an unseen turn. Older Sessions are reached with `/resume`.
90
+
91
+ Each row shows a root Session's name and what it did last. The name is the
92
+ Session name, or else the first line of its first message. While the Session
93
+ works, the row shows the running tool; otherwise, the start of its last reply.
94
+ Running owned Agents and Programs are counted, as in `3 agents running`.
95
+
96
+ Rows are grouped by state, newest first in each group:
97
+
98
+ - **Needs input**: a dialog waiting for the user, or a finished turn the user
99
+ has not seen. A turn that failed after Pi's retries, or a host that exited
100
+ abnormally, is marked failed.
101
+ - **Working**: the Session or its tree has live work.
102
+ - **Completed**: everything else.
103
+
104
+ While a Session works, its icon spins. Otherwise the shape tells whether Pi is
105
+ still running (`●`) or has exited (`○`), and the color marks a finished (green)
106
+ or failed (red) turn, or a dialog waiting for the user (yellow). The header
107
+ shows Pi's logo, the size of each group, and key hints.
108
+
109
+ A turn counts as seen once it has been shown in an attached terminal. Sessions
110
+ from before the workbench start out seen. A Session that holds its lock but is
111
+ not reporting to the daemon, as while its Pi exits, shows as unknown (`?`); the
112
+ daemon checks it again every second until that changes.
113
+
114
+ Keys:
115
+
116
+ - `Enter` attaches; detaching returns here.
117
+ - `Space` previews the selected Session below the list: the conversation
118
+ since the last compaction, with thinking left out and each run of tool
119
+ calls folded to one line, such as `... (6 tool calls: bash, read, edit)`.
120
+ The preview follows the Session as it works.
121
+ The input keeps `@<session>` at its start, so a message goes to the
122
+ previewed Session. In the preview:
123
+ - `Enter` sends a typed message; with none typed, it attaches.
124
+ - `Ctrl+O` (`app.tools.expand`) attaches.
125
+ - The page keys of fullscreen mode (`tui.altScreen.pageUp`/`pageDown`)
126
+ scroll; with no message typed, `↑`/`↓` scroll too.
127
+ - `Esc` clears a typed message; with none typed, it closes the preview.
128
+ - `Ctrl+X` stops the current turn, as `app.interrupt` does.
129
+ - `Ctrl+D` removes the selected Session from the list. Its file stays, and
130
+ `/resume` brings it back. A running Session must quit first.
131
+ - `Ctrl+Z` (`pi-agents.detach`) exits the workbench, as detaching leaves a
132
+ Session; every Session keeps running.
133
+
134
+ The input at the bottom:
135
+
136
+ - `@` on an empty input opens Pi's session picker. The chosen Session is
137
+ selected in the list, and the input starts with `@<session>`; the text after
138
+ it goes to that Session as a user message. After `/`, Tab completes a
139
+ command.
140
+ - `/resume` opens Pi's session picker over all directories, newest first, and
141
+ attaches the chosen Session. `Ctrl+D` in the picker deletes a Session file.
142
+ - `/quit` exits the workbench, as do `Esc` and `Ctrl+C` twice on an empty
143
+ input.
144
+ - Any other text starts a new Session in the current directory with Pi's
145
+ default settings, detached.
146
+
147
+ ## 6. Upgrades
148
+
149
+ A host keeps running the version it started with until its Session exits.
150
+ When the daemon sees a newer pi-agents version, it restarts once idle, and
151
+ Sessions reconnect to the new daemon.
152
+
153
+ ## 7. Platform
154
+
155
+ macOS and Linux. Every dependency is pure JavaScript or prebuilt: hosts use
156
+ `@lydell/node-pty`, and a Session lock is a file that records the process
157
+ holding it.
158
+
159
+ ## Not in this version
160
+
161
+ Attaching several terminals to one Session, Sessions on other machines,
162
+ model-written summaries, and a confirmation before quitting while work is
163
+ running. Keeping Sessions alive across host crashes and reboots needs a
164
+ durable runtime, which [Pi Durable](https://earendil.com/posts/pi-durable/)
165
+ explores.
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "@tshu-w/pi-agents",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Multi-agent runtime for Pi: async agents, programmatic delegation, and inter-agent messaging",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/tshu-w/pi-agents.git"
9
+ },
10
+ "homepage": "https://github.com/tshu-w/pi-agents#readme",
11
+ "type": "module",
12
+ "bin": {
13
+ "pd": "bin/pd.mjs",
14
+ "pi-agents": "bin/pd.mjs"
15
+ },
16
+ "files": [
17
+ "bin",
18
+ "src",
19
+ "docs"
20
+ ],
21
+ "keywords": [
22
+ "pi-package",
23
+ "pi-extension",
24
+ "agents",
25
+ "multi-agent"
26
+ ],
27
+ "pi": {
28
+ "extensions": [
29
+ "./src/index.ts"
30
+ ]
31
+ },
32
+ "scripts": {
33
+ "test": "node --test --test-timeout=30000 tests/",
34
+ "typecheck": "tsc -p tsconfig.json"
35
+ },
36
+ "dependencies": {
37
+ "@lydell/node-pty": "^1.2.0-beta.15",
38
+ "@xterm/addon-serialize": "^0.14.0",
39
+ "@xterm/headless": "^6.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@earendil-works/pi-ai": "*",
43
+ "@earendil-works/pi-coding-agent": "*",
44
+ "@earendil-works/pi-tui": "*",
45
+ "typebox": "*"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@earendil-works/pi-ai": {
49
+ "optional": true
50
+ },
51
+ "@earendil-works/pi-coding-agent": {
52
+ "optional": true
53
+ },
54
+ "@earendil-works/pi-tui": {
55
+ "optional": true
56
+ },
57
+ "typebox": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "devDependencies": {
62
+ "typescript": "5.9.3"
63
+ }
64
+ }