@tshu-w/pi-agents 0.1.0 → 0.2.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.
package/docs/PROGRAM.md CHANGED
@@ -25,7 +25,7 @@ target is an ID or a unique ID prefix.
25
25
  Agents created with `agent()` belong to the Program: it gets their results
26
26
  from `send()`, without `wait` or notifications, and only it can abort
27
27
  them. Each of these Agents and the Agents under it see only one another,
28
- and no other Agent sees them. The user sees them in `/tasks` and `/agents`,
28
+ and no other Agent sees them. The user sees them in the task panel and `/tasks`,
29
29
  and can talk to them or interrupt them in the viewer; an interrupted input
30
30
  makes its `send()` throw. When the Program ends, their unfinished inputs end
31
31
  as `aborted`, and the Agents go offline for good, from the bottom up.
@@ -45,7 +45,8 @@ A Program is `running` until it ends in one of three ways:
45
45
  - `stop` stops a running Program.
46
46
 
47
47
  When a background Program ends and its caller is not waiting for it, the
48
- caller gets a notification, as in SPEC §5.
48
+ caller gets a notification, as in SPEC §5. A steer input ends `wait` early,
49
+ as in SPEC §5.
49
50
 
50
51
  ## 3. Program API
51
52
 
@@ -54,39 +55,23 @@ can use is checked at each call. If a Program fails or is stopped, the
54
55
  error or stop reason follows its output. Its `store()` writes are kept
55
56
  only if it completes.
56
57
 
57
- A Program also creates Agents with `agent()`:
58
-
59
- ```ts
60
- agent(options?: {
61
- name?: string;
62
- cwd?: string;
63
- context?: "fresh" | "fork";
64
- model?: string;
65
- thinkingLevel?: string;
66
- }): AgentHandle
67
-
68
- interface AgentHandle {
69
- readonly id: string;
70
- readonly name: string;
71
- send(message: string, options?: {
72
- deliverAs?: "followUp" | "steer" | "write";
73
- schema?: JsonSchema;
74
- }): Promise<unknown>;
75
- abort(): Promise<void>;
76
- }
77
- ```
78
-
79
- `agent()` creates an idle Agent. Options work like `agent` `spawn`; an
80
- omitted name is generated, unique within the Program. `send()` sends a
81
- message and returns its result. `abort()` works like `agent` `abort`.
82
- It gets its tools as if the caller had spawned it.
58
+ A Program also creates idle Agents with `agent()`, typed in the Agent API
59
+ (§6). Options work like `agent` `spawn`; an omitted name is generated,
60
+ unique within the Program. `send()` sends a message and returns its result.
61
+ `abort()` works like `agent` `abort`. The Agent gets its tools as if the caller had spawned it.
83
62
 
84
63
  A result is the Agent's answer. If the input fails or is aborted, the
85
64
  result throws an Error. A write returns `undefined` once accepted.
86
65
 
87
66
  With `schema`, the Agent must call `submit_result` with a value that
88
67
  matches the schema, and the result is that value. If it does not, the
89
- input fails. `send()` throws if `schema` is used without `followUp`.
68
+ input fails.
69
+
70
+ With `timeout`, an input that has not ended within that many seconds of
71
+ `send()`, including time queued for a slot, is aborted; the Agent's queued
72
+ inputs are unaffected. The result throws a timeout Error.
73
+
74
+ `send()` throws if `schema` or `timeout` is used without `followUp`.
90
75
 
91
76
  ## 4. Lifecycle
92
77
 
@@ -94,8 +79,7 @@ When a Program ends, it cancels its unfinished tool calls and aborts its
94
79
  Agents' unfinished inputs, and waits for its Agents to go offline. It
95
80
  returns its result after cleanup. Work not stopped within the cleanup
96
81
  timeout is abandoned and may keep running; the Program's result and `stop`
97
- report it as an error.
98
- Stopping a Program does not undo completed tool calls.
82
+ report it as an error, and the Program's state is unchanged.
99
83
 
100
84
  When the caller goes offline, its running Programs stop. Their results
101
85
  remain available when the caller is loaded again.
@@ -153,11 +137,13 @@ interface SendOptions {
153
137
  deliverAs?: "followUp" | "steer" | "write";
154
138
  /** `followUp` only. The Agent must submit a value matching it. */
155
139
  schema?: JsonSchema;
140
+ /** `followUp` only. Seconds before the input is aborted. Default: none. */
141
+ timeout?: number;
156
142
  }
157
143
  interface AgentHandle {
158
144
  readonly id: string;
159
145
  readonly name: string;
160
- /** Resolves with the answer, or with the submitted value when `schema` is set; a write resolves with undefined once accepted. Throws if the input fails or is aborted. */
146
+ /** Resolves with the answer, or with the submitted value when `schema` is set; a write resolves with undefined once accepted. Throws if the input fails, is aborted, or times out. */
161
147
  send(message: string, options?: SendOptions): Promise<unknown>;
162
148
  /** Aborts the Agent's current turn and queued inputs; the Agent stays usable. */
163
149
  abort(): Promise<void>;
@@ -202,6 +188,8 @@ In these formats, `<id>` is the Program's short ID.
202
188
 
203
189
  `run` in the foreground returns the Program result, as an error if the
204
190
  Program failed or was stopped.
191
+ A Program result lists its `send()` calls with its tool calls, and names the
192
+ calls still running when the script returned, which were cancelled.
205
193
 
206
194
  `run` in the background:
207
195
 
@@ -220,6 +208,8 @@ timeout:
220
208
  Still running: <id>, ...
221
209
  ```
222
210
 
211
+ When a steer input ended the wait, the line of SPEC §6 follows.
212
+
223
213
  A single completed result, with nothing running, is returned alone. When
224
214
  there are no results or running Programs:
225
215
 
@@ -253,7 +243,7 @@ Program <id> has already ended.
253
243
  ```
254
244
 
255
245
  When some of the Program's work does not stop within the timeout, `stop`
256
- fails with:
246
+ fails with the following; only restarting Pi stops it.
257
247
 
258
248
  ```text
259
249
  Program <id> did not stop within the timeout and may still be running.
@@ -300,10 +290,17 @@ Program <id> <outcome>.
300
290
  ## 7. Human interface
301
291
 
302
292
  The task panel (SPEC §7) lists running background Programs.
293
+ `/tasks` opens a background Program in the task viewer (SPEC §7).
294
+
295
+ `/program <path> [args]` runs the script in a file as a foreground Program of
296
+ the current Agent, with the text after the path as the global `args`. Esc
297
+ stops it. Like a `!` command, it and its result join the context without
298
+ starting a turn. With `pi -p`, the result is printed, and Pi exits with
299
+ status 1 if the run fails.
303
300
 
304
301
  ## 8. Configuration
305
302
 
306
- `program` is inactive by default, like `codemode`; enable it with `"defaultTools": ["+program"]` or `--tools`.
303
+ `program` is inactive by default, like `codemode`; enable it with `"defaultTools": ["+program"]` or `--tools`. `/program` does not need it.
307
304
  MCP treats `program` as `codemode`: it activates `program` when its servers need a script tool and neither is active, unless `autoEnableCodemode` is false, and a Program waits for the MCP servers its code names.
308
305
  When both `program` and `codemode` are active, Pi warns once per Session, since they do a similar job.
309
306
  `codemode.mode` decides how the tool list is presented, as for `codemode`.
package/docs/SPEC.md CHANGED
@@ -4,7 +4,7 @@ Pi Agents lets Agents in Pi discover, message, and wait for each other.
4
4
  Programs built on these operations are specified in PROGRAM.md.
5
5
 
6
6
  Terms follow Pi's
7
- [Pico5 specification](https://github.com/earendil-works/pi/blob/main/packages/durable/docs/pico-v5.md).
7
+ [Pi Durable specification](https://github.com/earendil-works/pi/blob/main/packages/durable/docs/spec.md).
8
8
 
9
9
  ## 1. Semantic model
10
10
 
@@ -45,6 +45,11 @@ An input to an Agent with a turn in progress is either answered by the current t
45
45
  A write is added at the recipient's next tool boundary, or at once if it is
46
46
  idle. A write can go to several Agents at once.
47
47
 
48
+ When the user of a root Agent clears its queued messages, messages from other
49
+ Agents stay queued. After an Agent's turn is stopped, messages from other
50
+ Agents, notifications included, become writes until its next input from its
51
+ user or owner.
52
+
48
53
  ### 2.3 wait
49
54
 
50
55
  `wait` returns results of inputs sent to owned Agents. It can target
@@ -52,8 +57,8 @@ specific Agents and take a timeout.
52
57
 
53
58
  ### 2.4 list
54
59
 
55
- `list` returns visible Agents with their ID, name, working directory, and
56
- state.
60
+ `list` returns visible Agents with their ID, name, working directory,
61
+ state, and latest request: the last user message or input it received.
57
62
 
58
63
  ### 2.5 abort
59
64
 
@@ -71,7 +76,7 @@ stays `idle`.
71
76
  if needed. Acceptance does not mean the recipient has handled it; a message
72
77
  not yet handled is lost if the recipient exits.
73
78
 
74
- An input to an offline Agent wakes it. A write never wakes an Agent.
79
+ An input to an offline Agent wakes it; a write to one is rejected.
75
80
 
76
81
  Owned Agents run independently of their owner's turns. When their owner
77
82
  goes offline, they go offline too, from the bottom of the tree up, and their
@@ -101,7 +106,7 @@ continuing, ahead of inputs that have not started.
101
106
 
102
107
  An input is outstanding from acceptance until its turn ends or it is
103
108
  withdrawn. `maxOutstanding` (default 8) limits outstanding inputs; an input
104
- beyond it is rejected. `maxConcurrent` cannot exceed `maxOutstanding`.
109
+ beyond it is rejected. Notifications (§5) do not count.
105
110
 
106
111
  ## 5. Results and notifications
107
112
 
@@ -111,11 +116,12 @@ withdrawn. Its result is the answer or the error. Only inputs from the
111
116
  owner have results; the recipient replies to other senders with `send`.
112
117
 
113
118
  `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.
119
+ returns the results so far and names the Agents still pending. A steer input
120
+ to the waiting Agent, from any sender, ends `wait` the same way; a write does
121
+ not. It can also return read results again.
116
122
 
117
123
  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`,
124
+ receives a notification, unless the owner aborted it. The notification is an input delivered as `steer` (a write after the owner's turn is stopped, §2.2),
119
125
  and it names the Agent and how the input ended. It does not include the
120
126
  result; the owner reads the result with `wait`.
121
127
 
@@ -214,6 +220,13 @@ write: { ids }
214
220
  Still pending: <name> (<id>), ...
215
221
  ```
216
222
 
223
+ When a steer input ended the wait, a line follows:
224
+
225
+ ```text
226
+ Still pending: <name> (<id>), ...
227
+ Stopped waiting for a new message. Wait again to keep waiting.
228
+ ```
229
+
217
230
  A result returned again through `history` adds `history="true"`. A single
218
231
  unread completed result, with nothing pending, is returned as the answer
219
232
  alone. When there are no results or pending inputs:
@@ -225,18 +238,19 @@ No results.
225
238
  Details:
226
239
 
227
240
  ```text
228
- { results: [{ id, name, state, history, result? }], pending: [id] }
241
+ { results: [{ id, name, state, history, result? }], pending: [id], interrupted? }
229
242
  ```
230
243
 
231
- `state` is the input outcome (§5); `history` marks a previously read result.
244
+ `state` is the input outcome (§5); `history` marks a previously read result;
245
+ `interrupted` is `true` when a steer input ended the wait.
232
246
 
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:
247
+ `list` returns one entry per Agent, followed by its latest request, if any,
248
+ and its time. With a query, entries are ordered by their most recent match,
249
+ and a matching summary or user message shows its latest excerpt instead:
236
250
 
237
251
  ```text
238
252
  <name> (<id>) <state> <cwd>
239
- YYYY-MM-DD HH:MM …<matching excerpt>…
253
+ YYYY-MM-DD HH:MM <latest request or matching excerpt>
240
254
  ```
241
255
 
242
256
  When more entries remain:
@@ -304,6 +318,13 @@ Input rejected: <N> inputs have not ended. Wait for results or abort an Agent.
304
318
  <name> (<id>) is not owned by the caller.
305
319
  ```
306
320
 
321
+ When an aborted turn does not stop within the timeout (§3), `abort` fails
322
+ with the following; only restarting Pi stops it.
323
+
324
+ ```text
325
+ Agent <name> (<id>) did not stop within the timeout and may still be running.
326
+ ```
327
+
307
328
  truncation:
308
329
 
309
330
  Text beyond Pi's output limits is truncated with Pi's native notice, and
@@ -350,43 +371,59 @@ asking for a reply:
350
371
 
351
372
  A panel above the editor shows the current Agent's live work:
352
373
 
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>
374
+ Tasks (<N> live, /tasks to view)
375
+ Agent <name> (<id>) <state> <time> <cost> <N> pending
376
+ Agent <name> (<id>) <state> <time> <cost>
377
+ Program <id> running <time> <cost>
378
+ Agent <name> (<id>) <state> <time> <cost>
358
379
  +<N> more
359
380
 
360
381
  The live work is the owned Agents that are not `idle`, and the running
361
382
  background Programs with their Agents that are not `idle`. The rows list
362
383
  them as a tree in the order they started, each
363
384
  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.
385
+ keep the tree. Rows that do not fit are counted in the last row. The time is
386
+ how long the Agent's current turn or the Program has run, and the cost what the
387
+ replies in that turn have cost, or for a Program, those of all the Agents under
388
+ it. When a reply has no price, the cost is the input and output tokens instead,
389
+ like `7.3M tok`.
390
+
391
+ The panel updates as states change, and its times each second. It is shown
392
+ while it has a row.
393
+ `/tasks panel` hides or shows it.
394
+
395
+ ### Queued messages
396
+
397
+ Messages from other Agents that wait in the Session's queue, as during a turn,
398
+ are listed above the editor, below the user's queued messages, until the
399
+ Session takes them in:
365
400
 
366
- The panel updates as states change and is shown while it has a row.
367
- `/tasks` hides or shows it.
401
+ Steering: [<sender>] <body>
402
+ Write: [<sender>] <body>
403
+ Follow-up: [<sender>] <body>
368
404
 
369
- ### Agent viewer
405
+ ### Task viewer
370
406
 
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.
407
+ `/tasks` lists the Agents in the current Agent's tree, including its Programs'
408
+ Agents, and its background Programs, live ones first and newest first, with
409
+ their state and latest activity, and opens the selected one in a viewer. An
410
+ Agent not owned by the current Agent is named with its owners, as
411
+ `<name> ‹ <owner> ‹ …`, where a Program is `Program <id>`. The viewer follows
412
+ an Agent's conversation, or a Program as it shows in the foreground, while the
413
+ current Session keeps running.
378
414
 
379
- Text entered in the viewer is the user's input to that Agent and is sent as
380
- in Pi's editor.
415
+ Text entered in an Agent's viewer is the user's input to it, sent as in Pi's
416
+ editor; a Program takes no input.
381
417
 
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.
418
+ `app.interrupt` stops an Agent's current turn, as `abort` does, or a Program,
419
+ as `stop` does. `app.clear` clears the input, and closes the viewer when the
420
+ input is empty.
384
421
 
385
422
  ### Workbench
386
423
 
387
424
  `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).
425
+ machine and lets the user open and message them. Sessions started with `pd`
426
+ keep running after their terminal closes. See [HOSTS.md](HOSTS.md).
390
427
 
391
428
  ## 8. Configuration
392
429
 
@@ -417,11 +454,11 @@ in its owner's process.
417
454
 
418
455
  A delivered message enters the recipient's current branch. An offline root
419
456
  Agent is started in a host, detached, with its saved working directory and
420
- model and the current configuration (WORKBENCH §4).
457
+ model and the current configuration (HOSTS §4).
421
458
  If a root process cannot finish cleanup within a grace period on exit, it is
422
459
  forcibly terminated.
423
460
  Before shutting down an idle Session, another extension can emit
424
461
  `busy:query` on `pi.events` with `{ busy: false }`; it is set to `true`
425
462
  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.
463
+ One runtime uses a Session at a time. If a Session is occupied at startup, Pi
464
+ reports it and exits.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tshu-w/pi-agents",
3
- "version": "0.1.0",
4
- "description": "Multi-agent runtime for Pi: async agents, programmatic delegation, and inter-agent messaging",
3
+ "version": "0.2.0",
4
+ "description": "Multi-agent runtime for Pi: persistent async agents, dynamic workflows, and inter-agent messaging",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
7
7
  "type": "git",
@@ -19,10 +19,14 @@
19
19
  "docs"
20
20
  ],
21
21
  "keywords": [
22
- "pi-package",
22
+ "pi",
23
+ "pi-coding-agent",
23
24
  "pi-extension",
24
- "agents",
25
- "multi-agent"
25
+ "pi-package",
26
+ "multi-agent",
27
+ "async-agents",
28
+ "dynamic-workflows",
29
+ "rlm"
26
30
  ],
27
31
  "pi": {
28
32
  "extensions": [
@@ -30,7 +34,7 @@
30
34
  ]
31
35
  },
32
36
  "scripts": {
33
- "test": "node --test --test-timeout=30000 tests/",
37
+ "test": "node --test --test-timeout=30000 \"tests/*.test.mjs\"",
34
38
  "typecheck": "tsc -p tsconfig.json"
35
39
  },
36
40
  "dependencies": {