pi-squire 0.13.1 → 0.15.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,144 @@ follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.15.0] - 2026-10-30
10
+
11
+ ### Breaking
12
+
13
+ - Agents move to one file per agent in the agents folders Pi agent tooling
14
+ shares: `<agent dir>/agents/<name>.md` and, when the project is trusted,
15
+ `<cwd>/.pi/agents/<name>.md`, which replaces a global file of the same name.
16
+ `<agent dir>/squire/agents/<name>/AGENT.md` is no longer read; while that
17
+ folder exists, a warning says so. `squire/delegation_prompt.md` stays where
18
+ it is.
19
+ - The body is now wrapped in `<agent_instructions>` lines by default
20
+ (`prompt_mode: append`). `prompt_mode: replace` builds the delegate's prompt
21
+ exactly as before.
22
+ - `exclude-tools` is removed: a file that still has it gets a warning and the
23
+ field is ignored, so the delegate's tools follow the usual rules (its
24
+ `tools` line, or the parent session's active tools when there is none, plus
25
+ the `delegateTools` setting) with nothing taken off. List the delegate's
26
+ tools in `tools` instead.
27
+ - A `model` or `fallback` entry the model registry does not know is no longer
28
+ skipped for the next candidate: it goes to the child's `--model` unchanged,
29
+ and Pi reports it if it cannot resolve it.
30
+ - A delegate whose agent has no `tools` field now gets the parent session's
31
+ active tools when the launch or resume starts, passed as `--tools`, instead
32
+ of Pi's default tools. An empty `tools` field (`tools:` or `tools: []`) now
33
+ means no tools except the `delegateTools` setting, and `--no-tools` when that
34
+ is empty too; it used to mean the parent's tools.
35
+ - A delegate now gets exactly its selected tools, MCP tools included. Pi
36
+ kept every MCP tool registered, callable from `codemode` and loadable by
37
+ `tool_search`, unless a `--tools` entry started with `mcp__`; pi-squire now
38
+ adds the entry `mcp__`, which names no tool, to a list with no such entry,
39
+ so an MCP tool reaches a delegate only when its tools (or the parent's, for
40
+ an agent without `tools`) name or match it. Name a server's tools, such as
41
+ `mcp__docs__*`, to keep them. Tests changed with it: the argv tests in
42
+ `test/agent-files.test.ts`, `test/delegate.test.ts` and
43
+ `test/resume.test.ts` now expect the extra `mcp__` entry.
44
+ - Migration: move each `<agent dir>/squire/agents/<name>/AGENT.md` to
45
+ `<agent dir>/agents/<name>.md`; add `prompt_mode: replace` to keep today's
46
+ prompt; replace `exclude-tools: a, b` with a `tools` line naming the tools
47
+ the delegate should have.
48
+
49
+ ### Added
50
+
51
+ - An agent whose file is invalid is disabled: it is left out of the roster
52
+ and of the `delegate` tool's description, a warning names the file and the
53
+ reason when the session starts (and before a run, for a file that turns
54
+ invalid meanwhile), and a call or resume that names it is refused with that
55
+ reason instead of "no agent named".
56
+ - Every agent field is optional: `description` defaults to the name, an absent
57
+ `model` or `thinking` runs on the parent session's current model or level,
58
+ and an unknown `thinking` is dropped with a warning instead of leaving the
59
+ agent out. `model` and `fallback` entries may be aliases such as `sonnet`,
60
+ each resolving to one model; a `*` pattern makes the file invalid, so the
61
+ agent is disabled. A body may be empty.
62
+ - New agent fields: `display_name` (shown on the delegate card, in
63
+ `/delegates` and in the window title; it may hold only ASCII letters,
64
+ digits, `-` and `_`, and any other value, a space, `#`, `.`, a non-ASCII
65
+ letter or an empty value among them, makes the file invalid, so the agent is
66
+ disabled), `enabled: false` (leaves the agent
67
+ out; a project file hides a global agent of the same name), `locked` (`true`
68
+ or a list of `model` and `thinking`: a call or resume that passes a locked
69
+ field runs on the agent's value, and the result says which agent locked
70
+ which field; passing the agent's own value is the same as passing nothing,
71
+ so its fallbacks still apply; a resume runs a locked field on the file's
72
+ current value, not on what the delegate last ran on) and `prompt_mode`.
73
+ Keys pi-squire does not use, such as `max_turns` or another extension's
74
+ policy, are ignored without a warning.
75
+ - The `delegateTools` setting in `<agent dir>/squire/settings.json`: tool
76
+ names or `*` patterns added to every delegate's tools, from its agent's
77
+ `tools` or the parent's, for the tools a `message:*` provider gives a
78
+ delegate to reach its parent. Empty by default.
79
+ - A `tools` line of only `-name` entries (`tools: -bash, -write`) gives the
80
+ delegate the parent session's tools less those (a `-name` entry may be a `*`
81
+ pattern), then the `delegateTools` setting. A `+name` entry, or tool names
82
+ and `-name` entries mixed, make the agent file invalid, so the agent is
83
+ disabled. pi-squire always passes the child a plain `--tools` list.
84
+ - A `delegateTools` entry that starts with `+` or `-`, or holds a comma,
85
+ refuses every launch and resume with an error naming the settings file,
86
+ before the task is sent or a window opens.
87
+ - A `tools` or `fallback` YAML list entry holding a comma, such as
88
+ `tools: ["read,-bash"]`, makes the agent file invalid, so the agent is
89
+ disabled with a warning naming the file and the reason. A comma-separated
90
+ line (`tools: read, bash`) and a list of single names still work.
91
+ - A resume without `thinking`, for an agent that does not lock it, now runs
92
+ on the level the delegate last ran on, read from its session (the last
93
+ thinking-level change on the active branch, such as a change the user made
94
+ in the delegate's window or a clamp to a new model), instead of the level
95
+ recorded when the delegation started; the recorded level remains the
96
+ fallback when the session has none.
97
+ - A tool that only an agent's own `extensions` register reaches its delegate
98
+ only when the agent's `tools` list or the `delegateTools` setting names (or
99
+ matches) it. With no `tools` field, or only `-name` entries, the baseline
100
+ is the parent session's active tools alone; pi-squire never loads an
101
+ agent's extensions in the parent. (An unreleased change on this branch had
102
+ loaded them in the parent to add their tools to that baseline; its tests in
103
+ `test/agent-files.test.ts` and `test/pi-tools.test.ts` now pin this rule
104
+ instead.)
105
+ - A running delegate that hits a usage limit now resolves a `fallback` entry
106
+ that is an alias or bare id (such as `sonnet`) to one model with Pi's
107
+ `--model` resolver, in the listed order, instead of skipping every entry
108
+ that is not an exact `provider/id`. Each model is tried once, never the one
109
+ that just failed.
110
+
111
+ ## [0.14.0] - 2026-10-27
112
+
113
+ ### Removed
114
+
115
+ - The delegate popup: `enter` in `/delegates` and `/delegate-open --popup` no
116
+ longer open a tmux popup, and the popup title from 0.13.1 (the close key in
117
+ its title) goes with it. `--popup` is now an unknown-flag usage error. The
118
+ poll runs again only while a delegation is running. The close hint was hard to
119
+ see in some themes, and a switch covers the need.
120
+
121
+ ### Changed
122
+
123
+ - `enter` in the `/delegates` panel, and in its peek, now switches your tmux
124
+ client to the delegate's window (the notice says your tmux last-session key returns you); `/delegate-open
125
+ <id>` with no flag switches too. The `s` key is gone. With no client attached,
126
+ switch still refuses; peek and close still work. tmux 3.2 stays the minimum,
127
+ for `new-session -e`.
128
+ - Tests changed with it: in `test/navigator.test.ts` the panel hint, `enter`,
129
+ peek, no-client, failure, `/delegate-open` parsing, default and completion
130
+ tests now expect a switch, and the popup lifecycle tests are replaced by one
131
+ test that the poll runs only while a delegation does; `test/tmux.test.ts` no
132
+ longer tests `popup` and `closePopup`, and `test/popup-parent.ts` and the
133
+ fake's popup support are gone.
134
+ - Tests changed with the agent files: the harness writes `agents/<name>.md`
135
+ and the retired fixture to `squire/agents/<name>/AGENT.md`; prompt
136
+ assertions expect the `<agent_instructions>` wrapper; the roster, empty-roster
137
+ and malformed-file tests in `test/delegate.test.ts` and `test/tools.test.ts`
138
+ follow the optional fields and new location; the fallback render test skips
139
+ a model without credentials rather than an unknown one; the `display_name`
140
+ test names its agent `Code_Reviewer-2`, since a space is no longer accepted;
141
+ and `test/exclude-tools.test.ts` becomes `test/agent-files.test.ts`.
142
+ - Tests changed with tools inheritance: the test that pinned an empty `tools`
143
+ field as the parent's tools now pins it as no tools; the argv tests in
144
+ `test/delegate.test.ts` expect the parent's tools as `--tools`; and an
145
+ invalid `delegateTools` now refuses an agent without a `tools` field too.
146
+
9
147
  ## [0.13.1] - 2026-10-27
10
148
 
11
149
  ### Changed
@@ -520,7 +658,9 @@ author's dotfiles; the changes below are against that copy.
520
658
 
521
659
  - The published file list includes `child.ts`, which `index.ts` imports.
522
660
 
523
- [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.13.1...HEAD
661
+ [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.15.0...HEAD
662
+ [0.15.0]: https://github.com/gvanderclay/pi-squire/compare/v0.14.0...v0.15.0
663
+ [0.14.0]: https://github.com/gvanderclay/pi-squire/compare/v0.13.1...v0.14.0
524
664
  [0.13.1]: https://github.com/gvanderclay/pi-squire/compare/v0.13.0...v0.13.1
525
665
  [0.13.0]: https://github.com/gvanderclay/pi-squire/compare/v0.12.0...v0.13.0
526
666
  [0.12.0]: https://github.com/gvanderclay/pi-squire/compare/v0.11.0...v0.12.0
package/README.md CHANGED
@@ -17,7 +17,7 @@ You need:
17
17
  on. Pi supplies the peer dependencies `@earendil-works/pi-coding-agent` and
18
18
  `typebox`.
19
19
  - tmux 3.2 or later, with the Pi session running in a tmux pane. That is the
20
- first version with `new-session -e` and `display-popup`. Windows are placed
20
+ first version with `new-session -e`. Windows are placed
21
21
  relative to the pane's session (see [Where delegate windows
22
22
  open](#where-delegate-windows-open)), so a client attached elsewhere does
23
23
  not matter.
@@ -46,22 +46,24 @@ The package has no runtime dependencies and no build step.
46
46
 
47
47
  The package ships no agents, so create one first. `<agent dir>` is Pi's agent
48
48
  directory: `~/.pi/agent`, or `PI_CODING_AGENT_DIR` when that is set. Save this
49
- as `<agent dir>/squire/agents/scout/AGENT.md`, replacing
50
- `provider/model-id` with a model your Pi can use:
49
+ as `<agent dir>/agents/scout.md`, replacing `provider/model-id` with a model
50
+ your Pi can use:
51
51
 
52
52
  ```markdown
53
53
  ---
54
54
  description: Looks things up and answers in one message
55
55
  model: provider/model-id
56
56
  thinking: medium
57
- exclude-tools: edit, write
57
+ tools: read, grep, find, ls
58
58
  ---
59
59
 
60
60
  You research one question at a time and answer concisely, citing the files
61
61
  you read.
62
62
  ```
63
63
 
64
- The roster is read at call time, so no reload is needed. Delegation guidance
64
+ Every field is optional: without `model` and `thinking` the delegate runs on
65
+ your session's, and without `tools` it has your session's tools (see
66
+ [Agents](#agents)). The roster is read at call time, so no reload is needed. Delegation guidance
65
67
  for the parent is optional: save Markdown as
66
68
  `<agent dir>/squire/delegation_prompt.md`, and it is read before each run.
67
69
  Then, from a Pi session running in tmux, hand it a task:
@@ -92,7 +94,8 @@ or opened.
92
94
  ## How a delegation works
93
95
 
94
96
  `/delegate <agent> [--model <provider/id>] [--thinking <level>] [--label <name>] [--cwd <dir> | --worktree <branch> [--worktree-source <dir>] [--worktree-base <commit-ish>]] [--auto-exit | --no-auto-exit] <task…>`
95
- reads the agent from `<agent dir>/squire/agents/<name>/AGENT.md`, writes
97
+ reads the agent from `<agent dir>/agents/<name>.md` (or a trusted project's
98
+ `.pi/agents/<name>.md`; see [Agents](#agents)), writes
96
99
  the task to the delegate's inbox through `message:send`, emits
97
100
  `session:launch` so other extensions can shape the launch, then opens a
98
101
  detached window running the parent's own Pi in the parent's working directory,
@@ -102,7 +105,8 @@ prompt reaches the child as a path to a private file under the system
102
105
  temporary folder, not as text on the command line.
103
106
 
104
107
  - The window and the child's Pi session share one name: `<agent>-<label>`, or
105
- `<agent>-<first 8 characters of the id>` without a label. A label is at most
108
+ `<agent>-<first 8 characters of the id>` without a label, where `<agent>` is
109
+ the agent's `display_name` when it has one. A label is at most
106
110
  32 letters, digits, `_` or `-`, starting with a letter or digit. A `-` joins
107
111
  it rather than `:`, since tmux reads `:` in a target as session and window.
108
112
  - Labels need not be unique. The delegation id stays the handle for
@@ -183,30 +187,28 @@ with an error naming the file, before the task is sent or a window opens.
183
187
 
184
188
  Delegate windows sit out of your way, so reach them from the parent. `/delegates`
185
189
  (or `alt+shift+d`) opens a panel of the delegations whose window is open: running,
186
- or done with auto-exit off, newest first. The model has no tool for any of this.
190
+ or done with auto-exit off, newest first. Each row shows the delegate's name,
191
+ its agent (the agent's `display_name` when it had one), its state and its
192
+ window. The model has no tool for any of this.
187
193
 
188
194
  | Key | Does |
189
195
  |---|---|
190
196
  | `↑` / `↓` | select a delegation; `esc` leaves the panel |
191
- | `enter` | open: a popup over your terminal, or a switch when the window is in the parent's own session |
192
- | `p` | peek: a snapshot of the window's pane (`capture-pane`) in an overlay; `r` refreshes, `enter` opens, `esc` returns to the list |
193
- | `s` | switch your tmux client to the window; `prefix L` returns you (tmux's last-session key) |
197
+ | `enter` | switch your tmux client to the window |
198
+ | `p` | peek: a snapshot of the window's pane (`capture-pane`) in an overlay; `r` refreshes, `enter` switches, `esc` returns to the list |
194
199
  | `x` | close the delegation as `delegation_close` does; asks first only while it is running |
195
200
  | `n` | new delegate: the same form `/delegate` opens with no arguments (see [First use](#first-use)) |
196
201
 
197
- Popup and switch act on the most recently active tmux client attached to the
198
- parent's session. With none attached they refuse with a notice; peek and close
199
- still work. The popup attaches to the window's session at that window, and
200
- detaching in it (`prefix d`) closes it; its title names that key with your
201
- prefix. When the delegate's window goes, the parent
202
- closes the popup within one poll (up to 5 s). No tmux key bindings or options are
203
- added.
202
+ Switch acts on the most recently active tmux client attached to the parent's
203
+ session. With none attached it refuses with a notice; peek and close still
204
+ work. No tmux key bindings or options are added.
204
205
 
205
- `/delegate-open <id> [--popup|--switch|--peek]` does one of these on one
206
- delegation without the panel: with no flag or with `--popup`, what `enter` does (a popup, or a
207
- switch for a window in the parent's own session, since a popup of one's own session would show it
208
- inside itself); `--switch` always switches; `--peek` opens the peek overlay. Ids complete from the
209
- open delegations.
206
+ To come back, use your own tmux last-session key (tmux's default is `prefix L`,
207
+ often rebound); the notice after a switch says so.
208
+
209
+ `/delegate-open <id> [--switch|--peek]` does one of these on one delegation
210
+ without the panel: with no flag or with `--switch`, it switches; `--peek` opens
211
+ the peek overlay. Ids complete from the open delegations.
210
212
 
211
213
  ## Working directory
212
214
 
@@ -408,49 +410,123 @@ be read or written also refuses the launch; nothing is granted by default.
408
410
 
409
411
  ## Agents
410
412
 
411
- The roster is read at call time from
412
- `<agent dir>/squire/agents/<name>/AGENT.md`, one directory per agent, so a new
413
- agent is usable without a reload. Frontmatter
414
- carries `description`, `model` (`provider/id`) and `thinking`, and optionally
415
- `auto-exit` (`true` or `false`, default `true`; see [Auto-exit](#auto-exit)),
416
- `exclude-tools`, `tools`, `extensions` and `fallback`. The body is the delegate's system prompt,
417
- appended to Pi's own prompt; it reaches the child as a path to a private file
418
- under the system temporary folder, so the text stays off the command line. The
419
- [first-use example](#first-use) is a complete `AGENT.md`.
420
-
421
- `exclude-tools` names the tools the delegate goes without, as a
422
- comma-separated list (`exclude-tools: edit, write`) or a YAML list
423
- (`exclude-tools: [edit, write]`). It reaches the child as
424
- `--exclude-tools edit,write`, which Pi subtracts from the tools it would
425
- otherwise turn on. It is a denylist only: without it the delegate gets every
426
- tool, and no flag is passed. There is no per-call override. Each name is
427
- checked against the tools the parent session has registered
428
- (`pi.getAllTools()`), since Pi ignores an unknown name without a word. The
429
- delegate's result reaches the parent through the `message:*` provider's reply
430
- when its run settles, not through a tool, so excluding tools cannot stop it
431
- answering.
432
-
433
- `tools` lists tools for the delegate as a comma-separated or YAML list
434
- (`tools: device_run`). It reaches the child as `--tools device_run`, which
435
- covers extension tools too. Without discovery tools such as `codemode` or
436
- `tool_search`, only the listed tools are visible. Pi can keep unlisted MCP
437
- tools registered unless the list includes an `mcp__` entry; discovery tools can
438
- also expose them. Names are not checked against the parent's tools, since a
439
- tool from one of the agent's own `extensions` exists only in the child. Empty or
440
- absent passes no flag. `exclude-tools` still applies alongside.
413
+ An agent is one Markdown file, `<name>.md`, in the agents folders Pi agent
414
+ tooling shares; the file name without `.md` is the agent's name (letters,
415
+ digits, `_` or `-`). The roster is read at call time, so a new or edited file
416
+ is usable without a reload.
417
+
418
+ - `<agent dir>/agents/<name>.md` holds the global agents.
419
+ - `<cwd>/.pi/agents/<name>.md` holds a project's agents, read only when Pi
420
+ reports the parent's project as trusted. A project file replaces the global
421
+ file of the same name. In an untrusted project only the global agents load,
422
+ so a cloned repository cannot change a delegate's prompt, model or tools.
423
+
424
+ The body is the delegate's instructions, appended to Pi's own prompt; it
425
+ reaches the child as a path to a private file under the system temporary
426
+ folder, so the text stays off the command line. Every frontmatter field is
427
+ optional, and the [first-use example](#first-use) is a complete agent file:
428
+
429
+ | Field | Meaning |
430
+ | --- | --- |
431
+ | `description` | Shown in the roster and the `delegate` tool's description. Defaults to the name. |
432
+ | `display_name` | Shown instead of the name on the delegate card, in `/delegates` and in the window title (`<display_name>-<label>`). The agent is still called by its name. It may hold only ASCII letters, digits, `-` and `_`; any other value (a space, `#`, `.`, a non-ASCII letter, or an empty value) makes the file invalid, so the agent is disabled. |
433
+ | `model` | One model: `provider/id` or an alias such as `sonnet`, passed to the child's `--model` unchanged, so Pi resolves it to one model. A `*` pattern makes the file invalid. Absent means the parent session's current model. |
434
+ | `thinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` or `max`. Absent means the parent session's level; an unknown value is dropped with a warning and the parent's level applies. Pi clamps the level to the model. |
435
+ | `tools` | The delegate's tools (see below): tool names for exactly those, or only `-name` entries for the parent session's current tools less those. Absent means the parent session's current tools; empty (`tools:` or `tools: []`) means no tools except the `delegateTools` setting. MCP tools follow the selection like any other, and a tool of the agent's own `extensions` reaches the delegate only when `tools` or `delegateTools` names it. |
436
+ | `extensions` | Extension paths only this agent's delegate loads (see below). |
437
+ | `fallback` | Models to try, in order, when `model` is usage-limited (see [Usage limits](#usage-limits)). Each entry is one model, like `model`; a `*` pattern, or a YAML list entry holding a comma, makes the file invalid. A comma-separated line (`fallback: a, b`) is two entries. |
438
+ | `auto-exit` | `true` or `false`, default `true`; see [Auto-exit](#auto-exit). |
439
+ | `prompt_mode` | `append` (the default) wraps the body in `<agent_instructions>` and `</agent_instructions>` lines; `replace` adds the body unwrapped. An empty body adds nothing either way. |
440
+ | `enabled` | `false` leaves the agent out. A project file with `enabled: false` hides a global agent of the same name in that project. |
441
+ | `locked` | `true`, or a list of `model` and `thinking`. A call to `delegate`, `/delegate` or a resume that passes a locked field does not get it: the agent's value is used (the parent's when the file sets none) and the result says which agent locked which field. Passing the agent's own value is the same as passing nothing: no notice, and its fallbacks still apply. A resume of a locked field runs on the file's value as it is now, not on what the delegate last ran on. `true` locks the fields the file sets. |
442
+
443
+ Any other key, such as `max_turns`, `inherit_context`, `run_in_background` or
444
+ a permission policy for another extension, is ignored without a warning and
445
+ left for whatever else reads the file. `exclude-tools` is no longer read; a
446
+ file that still has it gets a warning (use `tools` instead).
447
+
448
+ `tools` lists the delegate's tools as a comma-separated or YAML list
449
+ (`tools: read, grep` or `tools: [read, grep]`). Each entry of a YAML list
450
+ names one tool: an entry holding a comma, such as `tools: ["read,-bash"]`,
451
+ makes the file invalid. pi-squire works out the delegate's whole selection
452
+ itself and always passes the child a plain list, `--tools read,grep`, which Pi
453
+ treats as the whole selection, extension tools included; entries may be `*`
454
+ patterns. It never passes Pi `+name` or `-name` entries. MCP tools follow the
455
+ list like any other tool: an MCP tool (`mcp__<server>__<tool>`, or one of the
456
+ MCP resource tools) that no entry names or matches is not registered in the
457
+ delegate at all, so it is neither declared nor callable from `codemode` nor
458
+ loadable by `tool_search`, whatever its exposure. Pi filters MCP tools by
459
+ `--tools` only once an entry starts with `mcp__`, so when the list has no
460
+ such entry pi-squire adds the entry `mcp__`, which names no tool; an empty
461
+ selection is `--no-tools`, which removes MCP tools too. To give a delegate an
462
+ MCP server's tools, name them, for example `mcp__docs__*`. An extension that
463
+ turns tools on at run time can still add only tools the list allows. Names
464
+ are not checked against the parent's tools, since a tool from one of the
465
+ agent's own `extensions` exists only in the child.
466
+
467
+ - No `tools` field: the delegate gets the parent session's tools as they are
468
+ when the launch (or resume) starts, the tools active there then, passed as
469
+ `--tools`, plus the `delegateTools` entries. A parent limited to `read`
470
+ gives such a delegate only `read`.
471
+ - An empty `tools` field, `tools:` or `tools: []`: the delegate gets no tools
472
+ except the `delegateTools` entries. With none of those either, it starts
473
+ with `--no-tools`.
474
+ - A `tools` list of tool names: exactly those tools, plus the
475
+ `delegateTools` entries.
476
+ - A `tools` list of only `-name` entries (`tools: -bash, -write`): the
477
+ parent's tools, as with no `tools` field, less those, plus the
478
+ `delegateTools` entries. A `-name` entry may be a `*` pattern
479
+ (`-mcp__docs__*`).
480
+ - A `+name` entry, or tool names and `-name` entries in one list, make the
481
+ file invalid, and the agent is disabled (see below).
482
+
483
+ The tools the agent's own `extensions` register are never in the parent's
484
+ tools, and pi-squire does not load those extensions in the parent to find
485
+ them. Such a tool reaches the delegate only when a plain `tools` list or the
486
+ `delegateTools` setting names it (or matches it with a `*` pattern); with no
487
+ `tools` field, or only `-name` entries, the delegate does not get it.
488
+
489
+ The delegation tools (`delegate`, `delegation_status`, `delegation_close`,
490
+ `delegation_resume`) are never passed and never available in a delegate:
491
+ pi-squire registers none of them there.
492
+
493
+ The delegate's result reaches the parent through the `message:*` provider's
494
+ reply when its run settles, not through a tool, so a short `tools` list cannot
495
+ stop it answering. To let a delegate message its parent mid-task, whatever its
496
+ `tools`, list the tools your `message:*` provider gives a session for that in
497
+ the `delegateTools` setting, in `<agent dir>/squire/settings.json` beside
498
+ `windowPlacement` and `worktreeRoot`:
499
+
500
+ ```json
501
+ { "delegateTools": ["<tool or pattern>"] }
502
+ ```
503
+
504
+ Each entry, a tool name or `*` pattern, is added to every delegate's tools,
505
+ whether they come from its agent's `tools` or from the parent (after any
506
+ `-name` entries are taken off), and passed to `--tools` unchanged. The setting
507
+ is empty by default, is read at each launch and resume, and a value that is
508
+ not a list of names, or an entry that holds a comma or starts with `+` or
509
+ `-`, refuses every launch with an error naming the file, before the task is sent or a window
510
+ opens; it does not disable agents one by one.
441
511
 
442
512
  `extensions` lists extension paths only this agent's delegate loads, as a
443
513
  comma-separated or YAML list. Each reaches the child as a repeated
444
514
  `-e <absolute path>`, on top of its normal extensions, so pi-squire and the
445
515
  `message:*` provider still load and the result still reaches the parent. `~`
446
516
  and `~/` expand to the home directory and a relative path resolves against the
447
- directory holding that `AGENT.md`. A path that does not exist when the roster
448
- is read leaves the agent out, with a warning.
449
-
450
- `fallback` lists models to try, in order, when the agent's `model` is
451
- usage-limited (see [Usage limits](#usage-limits)), as a comma-separated list
452
- or a YAML list of `provider/id`. An entry that is not `provider/id` is dropped
453
- with a warning; the agent still loads.
517
+ folder holding the agent file. A path that does not exist when the roster is
518
+ read leaves the agent out, with a warning.
519
+
520
+ A `model` or `fallback` entry that is an exact `provider/id` in Pi's registry
521
+ is checked as before: a model without credentials or with an active
522
+ usage-limit mark is skipped for the next candidate. Anything else, such as an
523
+ alias, goes to the child's `--model` unchanged, and Pi resolves it to one
524
+ model; if Pi cannot, the child's Pi reports the error and its window closes
525
+ without a result. When a running delegate moves to a fallback after a usage
526
+ limit, it resolves each entry the way Pi resolves `--model`: an exact
527
+ `provider/id`, or a bare id or alias such as `sonnet`, to one model, in the
528
+ order the entries are listed. No entry may be a `*` pattern: a `model` or
529
+ `fallback` entry with `*` makes the file invalid, and the agent is disabled.
454
530
 
455
531
  The body is followed by one fixed final-message line and a paragraph naming the
456
532
  parent session's address. The delegate's task arrives as a message from that
@@ -458,19 +534,32 @@ session, and that message and every later one from the same address are
458
534
  instructions to follow as given. Other sessions are colleagues: the delegate
459
535
  may answer them, but checks with the parent before doing work the parent did
460
536
  not ask for. The paragraph names no provider, package or tool, so it holds
461
- whatever `message:*` provider delivers the task.
462
-
463
- An `AGENT.md` that cannot be used is left out of the roster and named in a
464
- warning. That covers missing fields, a model that is not `provider/id`, an
465
- unknown thinking level, an `auto-exit` that is not `true` or `false`, an
466
- `exclude-tools` that is not a list of names or names a tool the session does
467
- not have, a `tools` or `extensions` that is not a list, an extension path that
468
- does not exist, and an empty body. With no agents at all, `/delegate` says how to
537
+ whatever `message:*` provider delivers the task. `prompt_mode: replace` builds
538
+ the prompt exactly as every delegate was built before `prompt_mode` existed.
539
+
540
+ An agent file that cannot be used is invalid, and its agent is disabled:
541
+ frontmatter that is not readable YAML or not a set of fields, an `enabled` or
542
+ `auto-exit` that is not `true` or `false`, a `tools` or `extensions` that is
543
+ not a list, a `tools` list with a `+name` entry or with tool names and `-name`
544
+ entries mixed, a `tools` or `fallback` YAML list entry holding a comma, a
545
+ `model` or `fallback` entry with a `*` pattern, or an extension path that
546
+ does not exist. A disabled agent is left
547
+ out of the roster and of the `delegate` tool's description, so the model never
548
+ picks it, and a warning names the file and the reason when the session starts
549
+ (and before a run, when a file turns invalid while the session runs). A call
550
+ or resume that names it anyway is refused with that reason. A
551
+ field that cannot be used (a `fallback` that is not a list, an unknown
552
+ `thinking`, `prompt_mode` or `locked` entry) is dropped with a warning and the
553
+ agent still loads. While the retired `<agent dir>/squire/agents/` folder
554
+ exists, a warning says to move each `<name>/AGENT.md` to
555
+ `<agent dir>/agents/<name>.md`. With no agents at all, `/delegate` says how to
469
556
  add one.
470
557
 
471
558
  ## Configuration
472
559
 
473
- `<agent dir>/squire/` holds the roster and the optional parent guidance file.
560
+ `<agent dir>/squire/` holds the optional parent guidance file and
561
+ `settings.json` (`windowPlacement`, `worktreeRoot` and `delegateTools`); the
562
+ agents live in `<agent dir>/agents/` (see [Agents](#agents)).
474
563
  `delegation_prompt.md` is Markdown, inserted unchanged as the
475
564
  `squire_parent_delegation` system-prompt section before each new run, including
476
565
  a run started by an idle delegate result. It is not a conversation or user
@@ -486,7 +575,7 @@ policy.
486
575
 
487
576
  Refresh happens before a new run, not before every model request in a tool
488
577
  loop, and the extension does not watch the filesystem. `pi-squire` reads no
489
- settings file and no environment variable of its own. `PI_DELEGATE_PARENT` and `PI_DELEGATE_AUTO_EXIT` are set
578
+ environment variable of its own. `PI_DELEGATE_PARENT` and `PI_DELEGATE_AUTO_EXIT` are set
490
579
  for the child, never read as configuration by the parent. The window starts
491
580
  with the tmux server's environment plus only these from the parent:
492
581
  `PI_CODING_AGENT_DIR`, `PI_CODING_AGENT_SESSION_DIR` (when the parent has it
@@ -497,7 +586,8 @@ it to skip a dialog or confirmation instead of waiting on one, and any other
497
586
  runner that starts Pi unattended can set it too. Anything else the
498
587
  parent process has, such as a `CLAUDE_CONFIG_DIR` set by a shell alias,
499
588
  reaches the child only through a `session:launch` contributor. `--model` and
500
- `--thinking` override the agent's defaults for one call, and an unknown value
589
+ `--thinking` override the agent's defaults for one call, unless the agent
590
+ locks them (see [Agents](#agents)), and an unknown value
501
591
  is refused with close matches, and so is a model with no configured
502
592
  credentials. An API key that exists only as an environment variable in the
503
593
  parent's shell does not reach the delegate, so log in through Pi or make the
@@ -517,11 +607,13 @@ session's delegation records. None of them is registered inside a delegate.
517
607
  | `delegation_resume` | `id`, `task`, optional `model`, `thinking` and `auto_exit` | Reopens an exited delegate's own session in a new window with a follow-up task, and returns the new delegation's id at once (see [Resuming a delegation](#resuming-a-delegation)). |
518
608
 
519
609
  The `delegate` tool's description lists the current roster, one line per agent:
520
- its name, description, default model and thinking, auto-exit when it is off,
521
- and its excluded tools.
610
+ its name and any display name, description, default model (or this session's)
611
+ and fallbacks, thinking, auto-exit when it is off, its tools (`no tools` for
612
+ an empty `tools` field, `this session's except …` for `-name` entries) and the
613
+ fields it locks.
522
614
 
523
615
  ```text
524
- - scout — Read-only recon (default provider/model-id, thinking low, auto-exit off, no edit/write)
616
+ - scout "Recon" — Read-only recon (default provider/model-id, thinking low, auto-exit off, tools: read/grep, locked: model)
525
617
  ```
526
618
 
527
619
  The model picks an agent from these lines without reading files. The
@@ -533,9 +625,9 @@ merely to wait; a substantive follow-up remains fine. The description is
533
625
  rebuilt at session start and before each new run when the exposed roster
534
626
  changes, so an edit, addition or removal shows up in the run being prepared,
535
627
  including a run woken by an idle delegate result. Unchanged metadata is not
536
- re-registered. Pi cannot list the session's tools while extensions load, so
537
- the description built then skips the `exclude-tools` check, and the one built
538
- after startup applies it. The roster itself is still read at call time, and an
628
+ re-registered. The description built while extensions load lists only the
629
+ global agents, since the project's trust is known only once the session
630
+ starts. The roster itself is still read at call time, and an
539
631
  unknown agent, model or thinking level is refused with close matches and
540
632
  nothing starts.
541
633
 
@@ -651,8 +743,9 @@ The launch also passes the candidates after the chosen one to the delegate as
651
743
  `PI_DELEGATE_FALLBACK` (none for an explicit model). When a delegate's run
652
744
  ends on a usage limit, it switches its session to the first of them that has
653
745
  credentials and is not marked, adds a message saying so, and continues the
654
- task there, so work done so far is kept. Each fallback is tried once; when
655
- none is left, the run fails as before. A `done` result from such a delegate
746
+ task there, so work done so far is kept. An alias resolves to one model, as
747
+ it would in Pi's `--model`. Each model is tried once, never the one that just
748
+ failed; when none is left, the run fails as before. A `done` result from such a delegate
656
749
  carries the `Usage limit:` line too, saying it finished on a fallback.
657
750
 
658
751
  Before a launch, pi-squire also reads the OpenCode Go quota for a paid
@@ -901,7 +994,8 @@ and it resumes as the command does.
901
994
  delegate instead) and tmux can be asked; no window with its id and name still
902
995
  runs a program (an exited one is killed first; a window that merely shares the
903
996
  name is ignored); its session file is found and the working directory in the
904
- file's header still exists; its agent is still in the roster; and no other
997
+ file's header still exists; its agent is still in the roster and not disabled
998
+ (a disabled agent's refusal names its file and the reason); and no other
905
999
  resume of it is under way. A delegate whose window was killed mid-turn can be
906
1000
  resumed.
907
1001
  - A removed working directory, a removed managed worktree included, makes a
@@ -912,9 +1006,17 @@ and it resumes as the command does.
912
1006
  - Without `model` it continues on the model the delegate finished on, moving to
913
1007
  its agent's fallbacks when that one is unavailable or marked (see
914
1008
  [Usage limits](#usage-limits)). An explicit `model` is used alone and refused
915
- rather than substituted. `thinking` and `auto_exit` default to the
916
- delegation's own. The agent's prompt, tools and extensions are those of the
917
- roster now.
1009
+ rather than substituted. Without `thinking` it continues on the level the
1010
+ delegate last ran on: the last thinking-level change on its session's
1011
+ active branch (Pi records one at start, one per change, and one when a
1012
+ model change clamps the level), or the delegation's recorded level when
1013
+ the session has none. `auto_exit` defaults to the delegation's own. A
1014
+ field the agent locks is the exception: it runs on the
1015
+ agent file's current `model` (then its fallbacks, even when the delegate had
1016
+ moved to a fallback) or `thinking`, a value passed for it is not used, with
1017
+ a notice, and passing the file's own value is the same as passing nothing. The agent's prompt, tools and extensions are those of the
1018
+ roster now, and an agent without a `tools` field gets the parent's tools as
1019
+ they are at the resume.
918
1020
  - `session:launch` is emitted as for any launch, with the same payload.
919
1021
  - Not detected: a Pi process you opened yourself on the same session outside
920
1022
  pi-squire's window. Close it before resuming.
@@ -938,7 +1040,7 @@ the window is open (see [Resuming a delegation](#resuming-a-delegation)).
938
1040
  | --- | --- |
939
1041
  | `args` | the child `pi`'s complete argument list, launcher first |
940
1042
  | `env` | the child's environment, as `KEY`/`value` pairs |
941
- | `agent` | the agent's name, as in `<agent dir>/squire/agents/<name>/AGENT.md`; read it, do not change it |
1043
+ | `agent` | the agent's name, as in `<agent dir>/agents/<name>.md` or `<cwd>/.pi/agents/<name>.md` (never its `display_name`); read it, do not change it |
942
1044
  | `cwd` | the directory the window opens in, the parent's or the resolved `--cwd`; read it, do not change it |
943
1045
  | `toolCallId` | the ID of the `delegate` or `delegation_resume` tool call that started the launch; absent when started from the `/delegate` or `/delegate-resume` command; read it, do not change it |
944
1046
 
@@ -1049,10 +1151,13 @@ Other extensions may rely on:
1049
1151
  no-argument forms: `/delegate` opens the new-delegate form and
1050
1152
  `/delegate-resume` the resume picker;
1051
1153
  - the `/delegates` command and its `alt+shift+d` shortcut, and `/delegate-open <id>` with its
1052
- `--popup`, `--switch` and `--peek` flags;
1154
+ `--switch` and `--peek` flags;
1053
1155
  - the `[delegate]` result text and the closed-without-a-result text;
1054
- - `AGENT.md`'s frontmatter keys and its location,
1055
- `<agent dir>/squire/agents/<name>/AGENT.md`;
1156
+ - the agent file's frontmatter keys and its locations,
1157
+ `<agent dir>/agents/<name>.md` and, in a trusted project,
1158
+ `<cwd>/.pi/agents/<name>.md`;
1159
+ - the `delegateTools`, `windowPlacement` and `worktreeRoot` settings in
1160
+ `<agent dir>/squire/settings.json`;
1056
1161
  - the optional `<agent dir>/squire/delegation_prompt.md` file and the
1057
1162
  `squire_parent_delegation` system-prompt section;
1058
1163
  - the child environment variables `PI_DELEGATE_PARENT`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-squire",
3
- "version": "0.13.1",
3
+ "version": "0.15.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",
@@ -54,8 +54,8 @@
54
54
  "@earendil-works/pi-ai": "1.3.0",
55
55
  "@earendil-works/pi-coding-agent": "1.3.0",
56
56
  "@earendil-works/pi-tui": "1.3.0",
57
- "@types/node": "26.6.3",
58
- "typebox": "1.3.27",
57
+ "@types/node": "26.6.5",
58
+ "typebox": "1.4.3",
59
59
  "typescript": "7.0.2"
60
60
  }
61
61
  }