pi-squire 0.18.1 → 0.18.3
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 +39 -1
- package/README.md +30 -26
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/result-message.ts +5 -13
- package/src/tmux.ts +13 -5
- package/src/tool-names.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@ follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.18.3] - 2026-11-01
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Model-facing delegate completion messages link the task copy instead of
|
|
14
|
+
quoting it, omit the workspace launch summary, and keep only the checkout
|
|
15
|
+
summary without path categories. Result bodies, errors, identity and file
|
|
16
|
+
pointers are unchanged. Task previews, workspace facts, and full snapshots
|
|
17
|
+
remain in saved message details and the expanded view. Tests that pinned the
|
|
18
|
+
previous model-facing wording now assert this shorter projection.
|
|
19
|
+
|
|
20
|
+
## [0.18.2] - 2026-11-01
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Agents without a `tools:` field inherit available deferred and codemode tools
|
|
25
|
+
from root sessions, just as they already did from nested delegates. Inactive
|
|
26
|
+
direct tools and hidden tools remain excluded. Pi initially declares the
|
|
27
|
+
selected tools in the child; extensions that enable optional tools on demand
|
|
28
|
+
must deselect them during child startup. This can increase initial declarations
|
|
29
|
+
for root-launched delegates whose extensions do not deselect those tools.
|
|
30
|
+
- Creating a companion session no longer saves the first delegate's environment,
|
|
31
|
+
including `PI_UNATTENDED` and API keys, into the session for later windows to
|
|
32
|
+
inherit. Each delegate still receives its own launch environment. Existing
|
|
33
|
+
polluted sessions are not changed. The fresh-companion argument test now pins
|
|
34
|
+
window-local environment delivery instead of session-level `-e` options.
|
|
35
|
+
Launches still pass only Squire's environment and `session:launch` additions,
|
|
36
|
+
never a copy of the parent's full process environment.
|
|
37
|
+
- tmux failures report a bounded subcommand error instead of raw process errors
|
|
38
|
+
or stderr that could expose launch arguments or environment values. The two
|
|
39
|
+
failed ownership-recheck tests now expect the bounded error wording.
|
|
40
|
+
- The discovery-count regression uses a controlled clock so additional parallel
|
|
41
|
+
test load does not turn its structural count assertion into a timing failure.
|
|
42
|
+
The separate real-time discovery deadline test and 200 ms product bound are
|
|
43
|
+
unchanged.
|
|
44
|
+
|
|
9
45
|
## [0.18.1] - 2026-11-01
|
|
10
46
|
|
|
11
47
|
### Changed
|
|
@@ -1024,7 +1060,9 @@ author's dotfiles; the changes below are against that copy.
|
|
|
1024
1060
|
|
|
1025
1061
|
- The published file list includes `child.ts`, which `index.ts` imports.
|
|
1026
1062
|
|
|
1027
|
-
[Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.18.
|
|
1063
|
+
[Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.18.3...HEAD
|
|
1064
|
+
[0.18.3]: https://github.com/gvanderclay/pi-squire/compare/v0.18.2...v0.18.3
|
|
1065
|
+
[0.18.2]: https://github.com/gvanderclay/pi-squire/compare/v0.18.1...v0.18.2
|
|
1028
1066
|
[0.18.1]: https://github.com/gvanderclay/pi-squire/compare/v0.18.0...v0.18.1
|
|
1029
1067
|
[0.18.0]: https://github.com/gvanderclay/pi-squire/compare/v0.17.0...v0.18.0
|
|
1030
1068
|
[0.17.0]: https://github.com/gvanderclay/pi-squire/compare/v0.16.0...v0.17.0
|
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ Then, from a Pi session running in tmux, hand it a task:
|
|
|
76
76
|
|
|
77
77
|
A detached tmux window named `scout-first` opens in your working directory.
|
|
78
78
|
When the delegate finishes, its answer reaches the parent as one `[delegate]`
|
|
79
|
-
message that
|
|
79
|
+
message that links the task copy (see [Results](#results)), and the delegate then
|
|
80
80
|
closes its own window ([auto-exit](#auto-exit)).
|
|
81
81
|
|
|
82
82
|
`/delegate` with no arguments opens a form instead: pick an agent, write the
|
|
@@ -992,11 +992,14 @@ are not checked against the parent's tools, at top level or nested: normal Pi
|
|
|
992
992
|
extension discovery or `session:launch` customization can provide tools in the
|
|
993
993
|
child.
|
|
994
994
|
|
|
995
|
-
- No `tools` field: the delegate gets the parent session's tools
|
|
996
|
-
when the launch (or resume) starts: active tools
|
|
997
|
-
|
|
998
|
-
`--tools`, plus the `delegateTools` entries.
|
|
999
|
-
|
|
995
|
+
- No `tools` field: the delegate gets the parent session's available tools
|
|
996
|
+
when the launch (or resume) starts, at any depth: active tools plus registered
|
|
997
|
+
codemode/deferred operations, excluding hidden tools and inactive direct tools,
|
|
998
|
+
passed as `--tools`, plus the `delegateTools` entries. Pi initially declares
|
|
999
|
+
these selected tools in the child, even when their exposure is codemode or
|
|
1000
|
+
deferred. An extension can deselect optional tools during child startup and
|
|
1001
|
+
enable them later. A parent with only `read` available gives such a delegate
|
|
1002
|
+
only `read`.
|
|
1000
1003
|
- An empty `tools` field, `tools:` or `tools: []`: the delegate gets no tools
|
|
1001
1004
|
except the `delegateTools` entries. With none of those either, it starts
|
|
1002
1005
|
with `--no-tools`.
|
|
@@ -1447,16 +1450,9 @@ the parent one message labelled delegate output:
|
|
|
1447
1450
|
```
|
|
1448
1451
|
[delegate] Result from scout (provider/model), delegation <id>, request <request id>.
|
|
1449
1452
|
Status: done
|
|
1450
|
-
Task
|
|
1451
|
-
> the task
|
|
1453
|
+
Task: <sent/ copy>
|
|
1452
1454
|
Working directory: <the directory its window opened in>
|
|
1453
|
-
Workspace: <how the directory was chosen and its Git checkout at launch>
|
|
1454
1455
|
Checkout when the result arrived: <branch, head, launch commit, commits since launch, clean or dirty>
|
|
1455
|
-
committed since launch: <paths>
|
|
1456
|
-
staged: <paths>
|
|
1457
|
-
unstaged: <paths>
|
|
1458
|
-
untracked: <paths>
|
|
1459
|
-
conflicted: <paths>
|
|
1460
1456
|
Envelope: <cur/ path>
|
|
1461
1457
|
Delegate session: <session file or id>
|
|
1462
1458
|
|
|
@@ -1498,19 +1494,22 @@ Delegate session: <session file or id>
|
|
|
1498
1494
|
delegations skip it.` (a model scope reads `on model provider/id`; a mark
|
|
1499
1495
|
that has since cleared reads `it was marked until …`). A failed reply with
|
|
1500
1496
|
no such mark is unchanged.
|
|
1501
|
-
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1497
|
+
- `Task:` points to the full request copy in `sent/`. The message details
|
|
1498
|
+
and expanded view retain a task preview capped at 2 KiB. A request with no
|
|
1499
|
+
copy gets a diagnostic with its request id.
|
|
1504
1500
|
- The working directory is the one recorded at launch, or
|
|
1505
|
-
`unknown (not recorded)` for a delegation recorded before it was kept.
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
- `Checkout when the result arrived:`
|
|
1510
|
-
taken as the reply was claimed
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1501
|
+
`unknown (not recorded)` for a delegation recorded before it was kept.
|
|
1502
|
+
The recorded [workspace](#working-directory) remains in message details
|
|
1503
|
+
and the expanded view, but the model-facing result omits its launch summary.
|
|
1504
|
+
Closed-without-a-result messages still include that summary when recorded.
|
|
1505
|
+
- `Checkout when the result arrived:` summarizes the
|
|
1506
|
+
[snapshot](#checkout-snapshots) taken as the reply was claimed. Git summaries
|
|
1507
|
+
keep the branch or detached HEAD, full head and launch commit, commits since
|
|
1508
|
+
launch, and working-tree state. Path categories remain in the saved snapshot,
|
|
1509
|
+
message details, and expanded view, not the model-facing result.
|
|
1510
|
+
The reply is claimed first, so a slow or failing Git cannot lose or duplicate
|
|
1511
|
+
it. An unknown snapshot includes the reason. A non-Git directory reads
|
|
1512
|
+
`not a Git checkout`.
|
|
1514
1513
|
- The envelope path is the claimed reply in the parent's `cur/`; the
|
|
1515
1514
|
delegate's session path is found by the delegation id in the session
|
|
1516
1515
|
directory recorded at launch (the parent's current one for older records),
|
|
@@ -1692,6 +1691,11 @@ An empty final set also receives `--no-tools` and an empty allowlist so earlier
|
|
|
1692
1691
|
selection flags cannot restore tools. Do all work synchronously: the emitter
|
|
1693
1692
|
reads the payload the moment `emit` returns.
|
|
1694
1693
|
|
|
1694
|
+
Environment additions are not a secret transport. They can be visible in tmux
|
|
1695
|
+
command arguments and a fresh companion's first pane start command to anyone
|
|
1696
|
+
with access to its socket. pi-squire never copies the parent's full process
|
|
1697
|
+
environment into these arguments.
|
|
1698
|
+
|
|
1695
1699
|
`cwd`, the trust flag (`--approve` or `--no-approve`), `--session-dir` and the
|
|
1696
1700
|
session choice (`--session-id` for a new delegate, `--session` for a [resumed](#resuming-a-delegation)
|
|
1697
1701
|
one) are launch invariants that `pi-squire` chooses. The window opens in its own `cwd`,
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -807,13 +807,13 @@ export default function delegate(
|
|
|
807
807
|
/**
|
|
808
808
|
* The delegate's tools for a launch or resume starting now: the agent's
|
|
809
809
|
* named tools, or the parent's current tools (including indirect callable
|
|
810
|
-
* tools
|
|
810
|
+
* tools, less any `-name` entries), then `delegateTools`.
|
|
811
811
|
* Throws, naming the file, on an invalid setting.
|
|
812
812
|
*/
|
|
813
813
|
function childTools(agent: Agent): string[] {
|
|
814
814
|
const selected = toolSelection(
|
|
815
815
|
agent,
|
|
816
|
-
agent.tools === undefined ?
|
|
816
|
+
agent.tools === undefined ? availableTools(pi) : [],
|
|
817
817
|
delegateTools(getAgentDir()),
|
|
818
818
|
);
|
|
819
819
|
if (grantsNesting(agent, (identity.current()?.depth ?? 0) + 1)) selected.push(...DELEGATION_TOOLS);
|
package/src/result-message.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The text and details of the one message the parent receives for a settled delegation.
|
|
2
2
|
import { readMarks } from "./limits.ts";
|
|
3
|
-
import { cwdLine, modelLabel, snapshotLines
|
|
3
|
+
import { cwdLine, modelLabel, snapshotLines } from "./render.ts";
|
|
4
4
|
import { sessionIdOf } from "./result-sessions.ts";
|
|
5
5
|
import type { StartedRecorded } from "./result-types.ts";
|
|
6
6
|
import type { Inbound, MessageDetails, TaskQuote } from "./results.ts";
|
|
@@ -88,29 +88,21 @@ export function resultDetails(
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/** The snapshot lines of the result text: a heading line, then its path lists indented. */
|
|
92
91
|
function checkoutText(snapshot: WorkspaceSnapshot): string[] {
|
|
93
|
-
const [first
|
|
94
|
-
return [`Checkout when the result arrived: ${first}
|
|
92
|
+
const [first] = snapshotLines(snapshot);
|
|
93
|
+
return [`Checkout when the result arrived: ${first}`];
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
/** The one message the parent model reads for a settled delegation. */
|
|
98
97
|
export function resultText(details: ResultDetails): string {
|
|
99
98
|
const { task } = details;
|
|
100
|
-
const
|
|
101
|
-
"note" in task
|
|
102
|
-
? task.note
|
|
103
|
-
: `Task, quoted from ${task.path}:\n${task.text
|
|
104
|
-
.split("\n")
|
|
105
|
-
.map((line) => `> ${line}`)
|
|
106
|
-
.join("\n")}`;
|
|
99
|
+
const taskLine = "note" in task ? task.note : `Task: ${task.path}`;
|
|
107
100
|
return [
|
|
108
101
|
`[delegate] Result from ${details.agent} (${modelLabel(details.model, details.finishedModel)}), delegation ${details.id}${details.resumedFrom === undefined ? "" : ` (resumed from ${details.resumedFrom})`}, request ${details.requestId}.`,
|
|
109
102
|
`Status: ${details.status}`,
|
|
110
103
|
...(details.limit === undefined ? [] : [details.limit]),
|
|
111
|
-
|
|
104
|
+
taskLine,
|
|
112
105
|
`Working directory: ${cwdLine(details.cwd)}`,
|
|
113
|
-
...(details.workspace === undefined ? [] : [`Workspace: ${workspaceLine(details.workspace)}`]),
|
|
114
106
|
...(details.workspaceSnapshot === undefined ? [] : checkoutText(details.workspaceSnapshot)),
|
|
115
107
|
`Envelope: ${details.envelopePath}`,
|
|
116
108
|
`Delegate session: ${details.sessionPath ?? details.sessionId ?? details.id}`,
|
package/src/tmux.ts
CHANGED
|
@@ -18,7 +18,6 @@ export type TmuxWindow = { id: string; session: string; name: string; exited: bo
|
|
|
18
18
|
/** A window just opened: its id and the name of the session it is in, when known. */
|
|
19
19
|
export type OpenedWindow = { id: string; session: string | undefined };
|
|
20
20
|
|
|
21
|
-
/** The oldest tmux delegate windows need: `new-session -e` arrived in 3.2. */
|
|
22
21
|
export const MIN_TMUX = { major: 3, minor: 2 } as const;
|
|
23
22
|
|
|
24
23
|
export type TmuxVersion = { text: string; major: number; minor: number };
|
|
@@ -124,8 +123,8 @@ function runTmux(args: string[], timeout: number): Promise<string> {
|
|
|
124
123
|
reject(new Error(`tmux ${args[0]} timed out after ${timeout / 1000} s`));
|
|
125
124
|
return;
|
|
126
125
|
}
|
|
127
|
-
const
|
|
128
|
-
reject(new Error(
|
|
126
|
+
const reason = ["duplicate session", "can't find session"].find((text) => stderr.includes(text));
|
|
127
|
+
reject(new Error(`tmux ${args[0]} failed${reason === undefined ? "" : `: ${reason}`}`));
|
|
129
128
|
return;
|
|
130
129
|
}
|
|
131
130
|
resolve(stdout);
|
|
@@ -215,8 +214,17 @@ async function openCompanion(run: Run, spec: WindowSpec, base: string): Promise<
|
|
|
215
214
|
continue;
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
|
-
const args = ["new-session", "-d", "-s", session, "-n", spec.name, "-c", spec.cwd
|
|
219
|
-
|
|
217
|
+
const args = ["new-session", "-d", "-s", session, "-n", spec.name, "-c", spec.cwd];
|
|
218
|
+
// new-session -e persists in the session; env confines these values to its first window.
|
|
219
|
+
args.push(
|
|
220
|
+
"-P",
|
|
221
|
+
"-F",
|
|
222
|
+
"#{window_id}",
|
|
223
|
+
"--",
|
|
224
|
+
"env",
|
|
225
|
+
...Object.entries(spec.env).map(([key, value]) => `${key}=${value}`),
|
|
226
|
+
...spec.argv,
|
|
227
|
+
);
|
|
220
228
|
args.push(";", "set-option", "-t", `=${session}:`, COMPANION_OPTION, "1");
|
|
221
229
|
args.push(";", "set-option", "-t", `=${session}:`, "detach-on-destroy", "off");
|
|
222
230
|
certainty = "unknown";
|
package/src/tool-names.ts
CHANGED
|
@@ -12,7 +12,7 @@ export function toolMatches(pattern: string, name: string): boolean {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The operations this session can reach: active tools plus codemode and
|
|
15
|
-
* deferred ones, never hidden ones. A
|
|
15
|
+
* deferred ones, never hidden ones. A delegate with no `tools` field
|
|
16
16
|
* inherits these. Includes active model-only operations, though
|
|
17
17
|
* ctx.executeTool cannot call those.
|
|
18
18
|
*/
|