pi-squire 0.18.1 → 0.18.2

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,31 @@ follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.18.2] - 2026-11-01
10
+
11
+ ### Fixed
12
+
13
+ - Agents without a `tools:` field inherit available deferred and codemode tools
14
+ from root sessions, just as they already did from nested delegates. Inactive
15
+ direct tools and hidden tools remain excluded. Pi initially declares the
16
+ selected tools in the child; extensions that enable optional tools on demand
17
+ must deselect them during child startup. This can increase initial declarations
18
+ for root-launched delegates whose extensions do not deselect those tools.
19
+ - Creating a companion session no longer saves the first delegate's environment,
20
+ including `PI_UNATTENDED` and API keys, into the session for later windows to
21
+ inherit. Each delegate still receives its own launch environment. Existing
22
+ polluted sessions are not changed. The fresh-companion argument test now pins
23
+ window-local environment delivery instead of session-level `-e` options.
24
+ Launches still pass only Squire's environment and `session:launch` additions,
25
+ never a copy of the parent's full process environment.
26
+ - tmux failures report a bounded subcommand error instead of raw process errors
27
+ or stderr that could expose launch arguments or environment values. The two
28
+ failed ownership-recheck tests now expect the bounded error wording.
29
+ - The discovery-count regression uses a controlled clock so additional parallel
30
+ test load does not turn its structural count assertion into a timing failure.
31
+ The separate real-time discovery deadline test and 200 ms product bound are
32
+ unchanged.
33
+
9
34
  ## [0.18.1] - 2026-11-01
10
35
 
11
36
  ### Changed
@@ -1024,7 +1049,8 @@ author's dotfiles; the changes below are against that copy.
1024
1049
 
1025
1050
  - The published file list includes `child.ts`, which `index.ts` imports.
1026
1051
 
1027
- [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.18.1...HEAD
1052
+ [Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.18.2...HEAD
1053
+ [0.18.2]: https://github.com/gvanderclay/pi-squire/compare/v0.18.1...v0.18.2
1028
1054
  [0.18.1]: https://github.com/gvanderclay/pi-squire/compare/v0.18.0...v0.18.1
1029
1055
  [0.18.0]: https://github.com/gvanderclay/pi-squire/compare/v0.17.0...v0.18.0
1030
1056
  [0.17.0]: https://github.com/gvanderclay/pi-squire/compare/v0.16.0...v0.17.0
package/README.md CHANGED
@@ -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 as they are
996
- when the launch (or resume) starts: active tools for a top-level launch, or
997
- available tools including codemode/deferred operations for a nested one, passed as
998
- `--tools`, plus the `delegateTools` entries. A parent limited to `read`
999
- gives such a delegate only `read`.
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`.
@@ -1692,6 +1695,11 @@ An empty final set also receives `--no-tools` and an empty allowlist so earlier
1692
1695
  selection flags cannot restore tools. Do all work synchronously: the emitter
1693
1696
  reads the payload the moment `emit` returns.
1694
1697
 
1698
+ Environment additions are not a secret transport. They can be visible in tmux
1699
+ command arguments and a fresh companion's first pane start command to anyone
1700
+ with access to its socket. pi-squire never copies the parent's full process
1701
+ environment into these arguments.
1702
+
1695
1703
  `cwd`, the trust flag (`--approve` or `--no-approve`), `--session-dir` and the
1696
1704
  session choice (`--session-id` for a new delegate, `--session` for a [resumed](#resuming-a-delegation)
1697
1705
  one) are launch invariants that `pi-squire` chooses. The window opens in its own `cwd`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-squire",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
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",
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 when nested, less any `-name` entries), then `delegateTools`.
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 ? (identity.current() ? availableTools(pi) : pi.getActiveTools()) : [],
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/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 detail = typeof stderr === "string" && stderr.trim() !== "" ? stderr.trim() : error.message;
128
- reject(new Error(detail));
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, ...envArgs(spec)];
219
- args.push("-P", "-F", "#{window_id}", "--", ...spec.argv);
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 nested delegate with no `tools` field
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
  */