pi-squire 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/CHANGELOG.md CHANGED
@@ -6,6 +6,53 @@ follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.0] - 2026-10-02
10
+
11
+ ### Breaking
12
+
13
+ - A model without configured credentials is refused, by `/delegate` and the
14
+ `delegate` tool alike, instead of failing in the delegate's window.
15
+ - A reply counts as a delegation's result only when it comes from the delegate
16
+ (`envelope.from` equals the delegation's id); a reply to its request from any
17
+ other sender is left to the provider as ordinary mail.
18
+ - Providers must set `envelope.from` to the sender's address.
19
+ - The `reply()` fixture in `test/poll.test.ts` now sends the delegation id as
20
+ `from` instead of `"d"`, to match.
21
+
22
+ ### Changed
23
+
24
+ - The sources moved to `src/`, so load the package directory (`pi -e .`), not
25
+ `index.ts`. Installs through npm or git are unaffected.
26
+ - Each check (a poll tick, a `delegation_status` call, a `delegation_close`
27
+ call) asks tmux once, however many delegations run, instead of once per
28
+ delegation.
29
+ - The appended system prompt reaches the delegate as a path to a private file
30
+ under the system temporary folder, so its text stays off the command line
31
+ and out of `ps`. A `session:launch` listener now sees that path where the
32
+ prompt text was. The argv tests in `test/delegate.test.ts` read the prompt
33
+ from the file instead of from argv, a deliberate behaviour change.
34
+
35
+ ### Fixed
36
+
37
+ - `delegation_status` details name a labelled delegation's session
38
+ (`scout-roster`), as its text does, instead of `<agent>-<first 8 of the id>`.
39
+ - A recycled tmux window id is no longer mistaken for a delegate's window, nor
40
+ killed: a window counts as the delegate's only when its id and its name both
41
+ match.
42
+ - A delegate whose program exited in a window kept open (`remain-on-exit`)
43
+ reads as gone instead of running.
44
+
45
+ ### Added
46
+
47
+ - `session:launch` payloads carry the agent's name as `agent`.
48
+ - The README's Install section names its requirements: tmux 3.0 or later, Pi
49
+ 0.80.5 or later, the first installable release with `agent_settled` (tested
50
+ with Pi 1.0.0), macOS and Linux, and
51
+ `pi install npm:pi-session-mail` as an example `message:*` provider.
52
+ - A README "Compatibility" section names the contracts other extensions rely
53
+ on, the below-1.0 rule that breaking one bumps the minor version, and what a
54
+ `message:*` provider must do.
55
+
9
56
  ## [0.1.0] - 2026-10-02
10
57
 
11
58
  The first public release. Before this repository the package lived in its
@@ -22,5 +69,6 @@ author's dotfiles; the changes below are against that copy.
22
69
 
23
70
  - The published file list includes `child.ts`, which `index.ts` imports.
24
71
 
25
- [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.1.0...HEAD
72
+ [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.2.0...HEAD
73
+ [0.2.0]: https://github.com/gvanderclay/pi-squire/compare/v0.1.0...v0.2.0
26
74
  [0.1.0]: https://github.com/gvanderclay/pi-squire/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,73 +1,114 @@
1
1
  # pi-squire
2
2
 
3
- Hands a task to a delegate Pi session in a background tmux window, through the
4
- `/delegate` command and the `delegate` tool.
3
+ Starts a delegate Pi session in a background tmux window and shows you its result.
5
4
 
6
- `/delegate <agent> [--model <provider/id>] [--thinking <level>] [--label <name>] [--auto-exit | --no-auto-exit] <task…>` reads
7
- the agent from `<agent dir>/agents/<name>/AGENT.md`, writes the task to the
8
- delegate's inbox through `message:send`, emits `session:launch` so other
9
- extensions can shape the launch, then opens a detached window running the
10
- parent's own Pi in the parent's working directory and agent root. The window
11
- and the child's Pi session share one name: `<agent>-<label>`, or
12
- `<agent>-<first 8 characters of the id>` without a label. A label is at most
13
- 32 letters, digits, `_` or `-`, starting with a letter or digit; `-` rather
14
- than `:` joins it, since tmux reads `:` in a target as session and window.
15
- Labels need not be unique: the delegation id stays the handle for
16
- `delegation_status`, `delegation_close` and mail. The delegate id is fresh, so a
17
- delegate never reopens a live session, and it is also the delegate's mailbox
18
- address: the task waits in its inbox and is delivered at session start, with
19
- no handshake.
20
-
21
- Starting a delegation adds nothing to the parent's context — the model sees
22
- neither the command nor the task. The model can also start one itself with the
23
- `delegate` tool, which starts the delegation at once without opening a
24
- dialog, so several calls in one message each start their own;
25
- `delegation_status` and `delegation_close` let it check on a delegation
26
- and end one. When the delegate settles, its answer is taken over on
27
- `message:inbound` and shown to the parent as one result message that quotes
28
- the task. By default the delegate then closes its own window
29
- ([auto-exit](#auto-exit)); with auto-exit off the window stays open so you can
30
- read it or keep working in it. Delegations are recorded in the parent session
31
- without the task text.
32
-
33
- Inside a delegate (`PI_DELEGATE_PARENT` set) the extension registers only
34
- auto-exit's side, the `/auto-exit` command and its handlers, so a delegate
35
- cannot start delegates of its own. Without a UI the command
36
- and the `delegate` tool still work; it needs tmux and a provider of `message:*`, and refuses with a
37
- message when either is missing.
5
+ You give a task to a named agent with the `/delegate` command, or the model does
6
+ it with the `delegate` tool. The delegate works in its own tmux window and
7
+ answers with one message, which the parent session shows as a result.
38
8
 
39
9
  ## Install
40
10
 
41
- Install the package from npm:
11
+ You need:
12
+
13
+ - Pi 0.80.5 or later, on macOS or Linux (not Windows). Pi 1.0.0 is the version
14
+ pi-squire is tested with. The floor is the first installable release with
15
+ the `agent_settled` event (added in 0.80.4, which was never published to
16
+ npm); `--exclude-tools` needs 0.77.0 and `--session-id`
17
+ 0.76.0. The extension uses the `pi.events` bus, `pi.registerCommand` and
18
+ `pi.registerTool`; Pi supplies the peer dependencies
19
+ `@earendil-works/pi-coding-agent` and `typebox`.
20
+ - tmux 3.0 or later, with the Pi session running in a tmux pane. That is the
21
+ first version with `new-window -e`. A window is opened in the pane's
22
+ session, so a client attached elsewhere does not matter.
23
+ - A provider of `message:*`;
24
+ [pi-session-mail](https://github.com/gvanderclay/pi-session-mail) is one
25
+ example. Without a provider there is no way to send the task or get an
26
+ answer, so `/delegate` refuses rather than start a delegate that cannot
27
+ report back.
28
+
29
+ With pi-session-mail as the provider, for example, install both from npm:
42
30
 
43
31
  ```bash
32
+ pi install npm:pi-session-mail
44
33
  pi install npm:pi-squire
45
34
  ```
46
35
 
47
- To follow the latest commit instead, install it from GitHub:
36
+ To follow the latest commit of pi-squire instead, install it from GitHub:
48
37
 
49
38
  ```bash
50
39
  pi install git:github.com/gvanderclay/pi-squire
51
40
  ```
52
41
 
53
- The package has no runtime dependencies and no build step. The `pi` manifest loads only
54
- `./index.ts`, and the tests under `test/` are neither loaded by Pi nor included
55
- in the npm tarball.
56
- Run them with `pnpm install && pnpm test`.
57
-
58
- ## Requirements
59
-
60
- - Pi, with the `pi.events` bus, `pi.registerCommand` and `pi.registerTool`.
61
- - `@earendil-works/pi-coding-agent` for `getAgentDir()` and the frontmatter
62
- parser, declared as a peer dependency and supplied by Pi.
63
- - `typebox` for the tools' parameter schema, a host-provided package declared
64
- as a peer dependency and supplied by Pi.
65
- - tmux, with the session running in a tmux pane. A window is opened in the
66
- pane's session, so a client attached elsewhere does not matter.
67
- - A provider of `message:*`, such as
68
- [pi-session-mail](https://github.com/gvanderclay/pi-session-mail). Without one there is no way to send the task or get an
69
- answer, so `/delegate` refuses rather than starting a delegate that cannot
70
- report back.
42
+ The package has no runtime dependencies and no build step.
43
+
44
+ ## First use
45
+
46
+ The package ships no agents, so create one first. `<agent dir>` is Pi's agent
47
+ directory: `~/.pi/agent`, or `PI_CODING_AGENT_DIR` when that is set. Save this
48
+ as `<agent dir>/agents/scout/AGENT.md`, replacing `provider/model-id` with a
49
+ model your Pi can use:
50
+
51
+ ```markdown
52
+ ---
53
+ description: Looks things up and answers in one message
54
+ model: provider/model-id
55
+ thinking: medium
56
+ exclude-tools: edit, write
57
+ ---
58
+
59
+ You research one question at a time and answer concisely, citing the files
60
+ you read.
61
+ ```
62
+
63
+ The roster is read at call time, so no reload is needed. Then, from a Pi
64
+ session running in tmux, hand it a task:
65
+
66
+ ```text
67
+ /delegate scout --label first Which files define the command-line entry point?
68
+ ```
69
+
70
+ A detached tmux window named `scout-first` opens in your working directory.
71
+ When the delegate finishes, its answer reaches the parent as one `[delegate]`
72
+ message that quotes the task (see [Results](#results)), and the delegate then
73
+ closes its own window ([auto-exit](#auto-exit)).
74
+
75
+ ## How a delegation works
76
+
77
+ `/delegate <agent> [--model <provider/id>] [--thinking <level>] [--label <name>] [--auto-exit | --no-auto-exit] <task…>`
78
+ reads the agent from `<agent dir>/agents/<name>/AGENT.md`, writes the task to
79
+ the delegate's inbox through `message:send`, emits `session:launch` so other
80
+ extensions can shape the launch, then opens a detached window running the
81
+ parent's own Pi in the parent's working directory and agent root. The agent's
82
+ prompt reaches the child as a path to a private file under the system
83
+ temporary folder, not as text on the command line.
84
+
85
+ - The window and the child's Pi session share one name: `<agent>-<label>`, or
86
+ `<agent>-<first 8 characters of the id>` without a label. A label is at most
87
+ 32 letters, digits, `_` or `-`, starting with a letter or digit. A `-` joins
88
+ it rather than `:`, since tmux reads `:` in a target as session and window.
89
+ - Labels need not be unique. The delegation id stays the handle for
90
+ `delegation_status`, `delegation_close` and mail.
91
+ - The delegate id is fresh, so a delegate never reopens a live session. It is
92
+ also the delegate's mailbox address: the task waits in its inbox and is
93
+ delivered at session start, with no handshake.
94
+ - Starting a delegation adds nothing to the parent's context: the model sees
95
+ neither the command nor the task.
96
+ - The model can start one itself with the `delegate` tool, which starts the
97
+ delegation at once without opening a dialog, so several calls in one message
98
+ each start their own. `delegation_status` and `delegation_close` let it check
99
+ on a delegation and end one.
100
+ - When the delegate settles, its answer is taken over on `message:inbound` and
101
+ shown to the parent as one result message that quotes the task. By default
102
+ the delegate then closes its own window ([auto-exit](#auto-exit)). With
103
+ auto-exit off the window stays open so you can read it or keep working in it.
104
+ - Delegations are recorded in the parent session without the task text.
105
+ - Inside a delegate (`PI_DELEGATE_PARENT` set) the extension registers only
106
+ auto-exit's side, the `/auto-exit` command and its handlers, so a delegate
107
+ cannot start delegates of its own.
108
+ - Without a UI the command and the `delegate` tool still work. They need tmux
109
+ and a provider of `message:*`, and refuse with a message when either is
110
+ missing. In print mode (`pi -p`) Pi does not show the refusal notices, so
111
+ the refusal is silent there.
71
112
 
72
113
  ## Agents
73
114
 
@@ -75,8 +116,10 @@ The roster is read at call time from `<agent dir>/agents/<name>/AGENT.md`, one
75
116
  directory per agent, so a new agent is usable without a reload. Frontmatter
76
117
  carries `description`, `model` (`provider/id`) and `thinking`, and optionally
77
118
  `auto-exit` (`true` or `false`, default `true`; see [Auto-exit](#auto-exit))
78
- and `exclude-tools`; the body is the delegate's system prompt, appended to
79
- Pi's own prompt.
119
+ and `exclude-tools`. The body is the delegate's system prompt, appended to
120
+ Pi's own prompt; it reaches the child as a path to a private file under the
121
+ system temporary folder, so the text stays off the command line. The
122
+ [first-use example](#first-use) is a complete `AGENT.md`.
80
123
 
81
124
  `exclude-tools` names the tools the delegate goes without, as a
82
125
  comma-separated list (`exclude-tools: edit, write`) or a YAML list
@@ -85,42 +128,30 @@ comma-separated list (`exclude-tools: edit, write`) or a YAML list
85
128
  otherwise turn on. It is a denylist only: without it the delegate gets every
86
129
  tool, and no flag is passed. There is no per-call override. Each name is
87
130
  checked against the tools the parent session has registered
88
- (`pi.getAllTools()`), since Pi ignores an unknown name without a word; the
89
- delegate's result reaches the parent through the `message:*` provider's reply when its run
90
- settles, not through a tool, so excluding tools cannot stop it answering.
91
-
92
- The body is followed by one fixed final-message line and a paragraph naming
93
- the parent session's address: the delegate's task arrives as a message from
94
- that session, that message and every later one from the same address are
95
- instructions to follow as given, and other sessions are colleagues: the
96
- delegate may answer them, but checks with the parent before doing work the
97
- parent did not ask for. It carries no distrust wording, which made models
98
- refuse every request from a peer. The paragraph names no provider, package or tool, so it holds
131
+ (`pi.getAllTools()`), since Pi ignores an unknown name without a word. The
132
+ delegate's result reaches the parent through the `message:*` provider's reply
133
+ when its run settles, not through a tool, so excluding tools cannot stop it
134
+ answering.
135
+
136
+ The body is followed by one fixed final-message line and a paragraph naming the
137
+ parent session's address. The delegate's task arrives as a message from that
138
+ session, and that message and every later one from the same address are
139
+ instructions to follow as given. Other sessions are colleagues: the delegate
140
+ may answer them, but checks with the parent before doing work the parent did
141
+ not ask for. The paragraph names no provider, package or tool, so it holds
99
142
  whatever `message:*` provider delivers the task.
100
143
 
101
- ```markdown
102
- ---
103
- description: Looks things up and answers in one message
104
- model: provider/model-id
105
- thinking: medium
106
- exclude-tools: edit, write
107
- ---
108
-
109
- You research one question at a time and answer concisely, citing the files
110
- you read.
111
- ```
112
-
113
- An `AGENT.md` that cannot be used — missing fields, a model that is not
114
- `provider/id`, an unknown thinking level, an `auto-exit` that is not `true` or
115
- `false`, an `exclude-tools` that is not a list of names or names a tool the
116
- session does not have, or an empty body — is left out of the
117
- roster and named in a warning. With no agents at all, `/delegate` says how to
118
- add one. The package ships no agents.
144
+ An `AGENT.md` that cannot be used is left out of the roster and named in a
145
+ warning. That covers missing fields, a model that is not `provider/id`, an
146
+ unknown thinking level, an `auto-exit` that is not `true` or `false`, an
147
+ `exclude-tools` that is not a list of names or names a tool the session does
148
+ not have, and an empty body. With no agents at all, `/delegate` says how to
149
+ add one.
119
150
 
120
151
  ## Configuration
121
152
 
122
153
  None beyond the roster. `pi-squire` reads no settings file and no environment
123
- variable of its own; `PI_DELEGATE_PARENT` and `PI_DELEGATE_AUTO_EXIT` are set
154
+ variable of its own. `PI_DELEGATE_PARENT` and `PI_DELEGATE_AUTO_EXIT` are set
124
155
  for the child, never read as configuration by the parent. The window starts
125
156
  with the tmux server's environment plus only these from the parent:
126
157
  `PI_CODING_AGENT_DIR`, `PI_CODING_AGENT_SESSION_DIR` (when the parent has it
@@ -128,13 +159,16 @@ set), `PI_DELEGATE_PARENT` and `PI_DELEGATE_AUTO_EXIT`. Anything else the
128
159
  parent process has, such as a `CLAUDE_CONFIG_DIR` set by a shell alias,
129
160
  reaches the child only through a `session:launch` contributor. `--model` and
130
161
  `--thinking` override the agent's defaults for one call, and an unknown value
131
- is refused with close matches; `--auto-exit` and `--no-auto-exit` override the
162
+ is refused with close matches, and so is a model with no configured
163
+ credentials. An API key that exists only as an environment variable in the
164
+ parent's shell does not reach the delegate, so log in through Pi or make the
165
+ variable part of the tmux server's environment. `--auto-exit` and `--no-auto-exit` override the
132
166
  agent's `auto-exit`.
133
167
 
134
168
  ## Tools
135
169
 
136
170
  The model gets three tools. They share the command's launch path and the
137
- session's delegation records; none of them is registered inside a delegate.
171
+ session's delegation records. None of them is registered inside a delegate.
138
172
 
139
173
  | Tool | Parameters | What it does |
140
174
  | --- | --- | --- |
@@ -142,9 +176,9 @@ session's delegation records; none of them is registered inside a delegate.
142
176
  | `delegation_status` | optional `id` | Reports each delegation as running, done, closed or closed without a result, with the window, the delegate's session and the result envelope's path. |
143
177
  | `delegation_close` | `id` | Kills the window through tmux and records the close, so `delegation_status` reports it closed. |
144
178
 
145
- The `delegate` tool's description lists the current roster — each agent's name,
146
- description, default model and thinking, auto-exit when it is off, and its
147
- excluded tools — one line per agent:
179
+ The `delegate` tool's description lists the current roster, one line per agent:
180
+ its name, description, default model and thinking, auto-exit when it is off,
181
+ and its excluded tools.
148
182
 
149
183
  ```text
150
184
  - scout — Read-only recon (default provider/model-id, thinking low, auto-exit off, no edit/write)
@@ -154,10 +188,11 @@ The model picks an agent from these lines without reading files. The
154
188
  description also tells the model to turn `auto_exit` off when it means to keep
155
189
  talking to the delegate by mail after its result. A tool's description is
156
190
  fixed when it is registered, so the description is rebuilt at every session
157
- start; a roster change shows up in the next session. Pi cannot list the
158
- session's tools while extensions load, so the description built then skips
159
- the `exclude-tools` check, and the one built at session start applies it. The roster itself is read at call time, and an unknown agent,
160
- model or thinking level is refused with close matches and nothing starts.
191
+ start, and a roster change shows up in the next session. Pi cannot list the
192
+ session's tools while extensions load, so the description built then skips the
193
+ `exclude-tools` check, and the one built at session start applies it. The
194
+ roster itself is read at call time, and an unknown agent, model or thinking
195
+ level is refused with close matches and nothing starts.
161
196
 
162
197
  `delegation_status` reports one state per delegation:
163
198
 
@@ -167,21 +202,21 @@ model or thinking level is refused with close matches and nothing starts.
167
202
  <id> scout (provider/model, thinking low)
168
203
  name: scout-<first 8 of the id>
169
204
  state: running — the window is open and no result has arrived
170
- window: scout-<id> (@1)
205
+ window: scout-<first 8 of the id> (@1)
171
206
  auto-exit: on — the delegate closes its window after a normal completion unless the user took over there
172
207
  session: <the delegate's session file, or its id when there is none yet>
173
208
 
174
209
  <id> researcher (provider/other, thinking high)
175
210
  name: researcher-<first 8 of the id>
176
211
  state: done — result status: done
177
- window: researcher-<id> (@2)
212
+ window: researcher-<first 8 of the id> (@2)
178
213
  auto-exit: off — the window stays open after the result
179
214
  session: <session file>
180
215
  envelope: <the reply's path in the parent's cur/>
181
216
  ```
182
217
 
183
218
  - **Name** is the delegate's Pi session name, its agent and the first 8
184
- characters of its delegation id; the window keeps the full id.
219
+ characters of its delegation id.
185
220
  - **Running** means the window is open and no result has arrived. An open
186
221
  window is connection, not task state: the delegate may still be working, or
187
222
  waiting for the user, and only a result means done.
@@ -195,13 +230,14 @@ model or thinking level is refused with close matches and nothing starts.
195
230
  longer there. A recorded close wins over a result, and a result wins over a
196
231
  window that is gone.
197
232
  - **Closed without a result** is what the poll records when a window is gone
198
- and no reply arrived (see Results). A result outranks it, so a late reply
199
- turns it into **done**; a close from `delegation_close` outranks both.
233
+ and no reply arrived (see [Results](#results)). A result outranks it, so a
234
+ late reply turns it into **done**; a close from `delegation_close` outranks
235
+ both.
200
236
  - When tmux cannot be asked about a window, the state is `unknown`, never
201
237
  `closed`, because a failed check is not evidence the window is gone.
202
238
 
203
239
  `delegation_close` kills the window and records the close even when the
204
- result has already arrived; when the window is already gone it only records
240
+ result has already arrived. When the window is already gone it only records
205
241
  the close. An unknown or already closed id returns a message and kills
206
242
  nothing. The delegate's session file and result envelope stay on disk.
207
243
 
@@ -222,15 +258,13 @@ The parent resolves each delegation's auto-exit from the call
222
258
  - `/auto-exit` inside the delegate turns it on again for the next normal
223
259
  completion, whatever the launch said.
224
260
 
225
- The name and the take-over rule follow edxeth/pi-subagents' `auto-exit`
226
- (README "Child lifecycle"); unlike it, the default is on, and there is no
227
- `subagent_done` tool: auto-exit off only means the window stays.
261
+ There is no `subagent_done` tool: auto-exit off only means the window stays.
228
262
 
229
263
  ## Results
230
264
 
231
- A delegate answers its task with one reply through the `message:*` provider. `pi-squire` takes that
232
- reply over before the provider can inject it, and sends the parent one
233
- message labelled delegate output:
265
+ A delegate answers its task with one reply through the `message:*` provider.
266
+ `pi-squire` takes that reply over before the provider can inject it, and sends
267
+ the parent one message labelled delegate output:
234
268
 
235
269
  ```
236
270
  [delegate] Result from scout (provider/model), delegation <id>, request <request id>.
@@ -263,17 +297,19 @@ Delegate session: <session file or id>
263
297
  - The footer counts the running delegations (`⇄ N running`), clears one entry
264
298
  per result or close and hides itself at zero.
265
299
  - Each result and each close is recorded in the parent session, and the
266
- records are rebuilt synchronously at session start; a resume still
267
- recognises replies — including one that arrived while the parent was
268
- closed — and still reports a recorded close as closed. A result is shown
300
+ records are rebuilt synchronously at session start. A resume still
301
+ recognises replies, including one that arrived while the parent was
302
+ closed, and still reports a recorded close as closed. A result is shown
269
303
  once and never again.
270
304
  - Plain replies, and every request, are left to the provider.
271
305
 
272
306
  While any delegation is running (no result, no close), the parent polls every
273
307
  5 seconds; the timer does not keep the process alive and stops when nothing is
274
308
  running and on `session_shutdown`. Each tick first emits `message:scan`, so the
275
- provider claims waiting replies, and then asks tmux about each still-running
276
- window. A delegation whose window is gone and still has no result after that
309
+ provider claims waiting replies, and then lists the tmux windows once and
310
+ matches each still-running delegation to a window by id and name (window ids
311
+ restart with every tmux server). A window whose program has exited counts as
312
+ gone. A delegation whose window is gone and still has no result after that
277
313
  scan is recorded as closed without a result, a record separate from
278
314
  `delegation_close`'s, and the parent gets one `[delegate]` message naming the
279
315
  delegation, with the delegate session path when one is found, sent as a
@@ -292,13 +328,14 @@ that block.
292
328
 
293
329
  ### `session:launch`
294
330
 
295
- `pi-squire` provides this hook. It emits `{ args, env }` after the task has been
296
- sent and just before it opens the delegate's window.
331
+ `pi-squire` provides this hook. It emits `{ args, env, agent }` after the task
332
+ has been sent and just before it opens the delegate's window.
297
333
 
298
334
  | Field | Meaning |
299
335
  | --- | --- |
300
336
  | `args` | the child `pi`'s complete argument list, launcher first |
301
337
  | `env` | the child's environment, as `KEY`/`value` pairs |
338
+ | `agent` | the agent's name, as in `<agent dir>/agents/<name>/AGENT.md`; read it, do not change it |
302
339
 
303
340
  Listeners may only append to `args` and add keys to `env`; there is no veto,
304
341
  and `pi-squire` passes both to tmux unchanged. Do all work synchronously: the
@@ -324,12 +361,14 @@ provider's own README carries the contract.
324
361
  ### `message:inbound`
325
362
 
326
363
  `pi-squire` consumes this hook for answers: a reply whose `in_reply_to` names a
327
- recorded delegation's request is taken over — `pi-squire` sets `handled`, so
364
+ recorded delegation's request is taken over. `pi-squire` sets `handled`, so
328
365
  the provider injects nothing, and shows the parent the result message above.
329
366
  The reply's request copies fill the quoted task and its `cur/` path is the
330
367
  envelope it names. A reply to a request this session did not delegate, and
331
- every request, is left to the provider. The provider's README carries the
332
- contract.
368
+ every request, is left to the provider. `pi-squire` reads `from` and requires
369
+ it to be the sender's address, which for a delegate is its session id; a reply
370
+ from any other sender is left to the provider, even when it answers a recorded
371
+ request. The provider's README carries the contract.
333
372
 
334
373
  `pi-squire` rebuilds its records synchronously in its own `session_start`, so it
335
374
  relies on the provider not emitting `message:inbound` from inside a
@@ -349,3 +388,41 @@ written just before a window closed is never mistaken for no reply. **If
349
388
  `scanned` is not set, nobody answered**, and the tick declares nothing closed,
350
389
  because a waiting reply cannot be ruled out. The provider's README carries the
351
390
  contract.
391
+
392
+ ## Compatibility
393
+
394
+ While pi-squire is below 1.0, breaking any contract below bumps the minor
395
+ version and is listed under "Breaking" in [CHANGELOG.md](CHANGELOG.md).
396
+
397
+ Other extensions may rely on:
398
+
399
+ - the `session:launch` payload `{ args, env, agent }`, emitted synchronously
400
+ after the task is sent and before the window opens (see [Hooks](#hooks));
401
+ - the `message:send`, `message:inbound` and `message:scan` hooks, consumed as
402
+ their sections document;
403
+ - the `delegate`, `delegation_status` and `delegation_close` tool names and
404
+ parameters;
405
+ - the `/delegate` syntax and flags;
406
+ - the `[delegate]` result text and the closed-without-a-result text;
407
+ - `AGENT.md`'s frontmatter keys and its location,
408
+ `<agent dir>/agents/<name>/AGENT.md`;
409
+ - the child environment variables `PI_DELEGATE_PARENT` and
410
+ `PI_DELEGATE_AUTO_EXIT`.
411
+
412
+ A `message:*` provider must:
413
+
414
+ - implement `message:send`, `message:inbound` and `message:scan` as documented
415
+ under [Hooks](#hooks);
416
+ - set a reply's `from` to the sender's address, so the delegate's reply can be
417
+ matched to its task;
418
+ - reply on its own when the delegate's run settles, since that reply is what
419
+ becomes the result.
420
+
421
+ ## Contributing
422
+
423
+ See [CONTRIBUTING.md](CONTRIBUTING.md) to set up, run the tests and open a pull
424
+ request. Changes by release are in [CHANGELOG.md](CHANGELOG.md).
425
+
426
+ ## License
427
+
428
+ MIT, as stated in [LICENSE](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-squire",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Starts a delegate Pi session in a background tmux window, sends it the task over message:*, and shows the parent its result",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -24,15 +24,11 @@
24
24
  "packageManager": "pnpm@10.33.0",
25
25
  "pi": {
26
26
  "extensions": [
27
- "./index.ts"
27
+ "./src/index.ts"
28
28
  ]
29
29
  },
30
30
  "files": [
31
- "index.ts",
32
- "child.ts",
33
- "agents.ts",
34
- "tmux.ts",
35
- "results.ts",
31
+ "src",
36
32
  "README.md",
37
33
  "LICENSE",
38
34
  "CHANGELOG.md"