@aeondave/pi-persona 1.16.2 → 1.16.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 CHANGED
@@ -6,6 +6,14 @@ used in its [GitHub releases](https://github.com/AeonDave/pi-persona/releases).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.16.3](https://github.com/AeonDave/pi-persona/releases/tag/v1.16.3) - 2026-10-13
10
+
11
+ ### Fixed
12
+
13
+ - Reject Pi `+name`/`-name` tool modifiers in agent and per-run allowlists before either engine
14
+ starts. Those selectors are literal allowlist entries in pi-persona; Pi 1.2 instead applies them
15
+ to its default tools. Use `deny` or `excludeTools` for denials.
16
+
9
17
  ## [1.16.2](https://github.com/AeonDave/pi-persona/releases/tag/v1.16.2) - 2026-10-08
10
18
 
11
19
  ### Fixed
package/README.md CHANGED
@@ -39,9 +39,9 @@ your customizations.
39
39
  | Install option | Command |
40
40
  |---|---|
41
41
  | This project only | `pi install -l npm:@aeondave/pi-persona` |
42
- | Pin this release | `pi install npm:@aeondave/pi-persona@1.16.2` |
42
+ | Pin this release | `pi install npm:@aeondave/pi-persona@1.16.3` |
43
43
  | Update an unpinned install | `pi update npm:@aeondave/pi-persona` |
44
- | Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.16.2` |
44
+ | Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.16.3` |
45
45
 
46
46
  Restart Pi after updating. For a pinned install, install the new version or tag explicitly.
47
47
  If switching from a Git install, remove that source first with
package/docs/REFERENCE.md CHANGED
@@ -189,9 +189,11 @@ files are seed sources. A fresh install has no installed personas until `/person
189
189
  | Preset | `presets/*.preset.json` | reusable council strategy, roster, and parameter bundle |
190
190
 
191
191
  `tools` accepts an allowlist such as `[read, grep]`, an `allow`/`deny` object, or `[]` for no tools.
192
- Omit it to inherit the session tool set. `model`, `isolation: worktree`, `mcp: true`, and `purpose`
193
- are optional agent metadata. The body is the agent prompt. Inline roster members specialise one
194
- agent with a role, model, or skills without requiring another agent file.
192
+ Entries are plain tool names or `*` patterns; Pi's `+name`/`-name` default-tool modifier
193
+ syntax is not an allowlist and is rejected. Use the `deny` field to remove tools. Omit `tools` to
194
+ inherit the session tool set. `model`, `isolation: worktree`,
195
+ `mcp: true`, and `purpose` are optional agent metadata. The body is the agent prompt. Inline roster
196
+ members specialise one agent with a role, model, or skills without requiring another agent file.
195
197
 
196
198
  ### Multi-agent quick start
197
199
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeondave/pi-persona",
3
- "version": "1.16.2",
3
+ "version": "1.16.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Supervised multi-agent orchestration for Pi — async workers, live steering, cross-session collaboration, and switchable personas.",
@@ -104,7 +104,7 @@
104
104
  "typescript": "^7.0.2"
105
105
  },
106
106
  "pi": {
107
- "image": "https://raw.githubusercontent.com/AeonDave/pi-persona/v1.16.2/assets/banner.png",
107
+ "image": "https://raw.githubusercontent.com/AeonDave/pi-persona/v1.16.3/assets/banner.png",
108
108
  "video": "https://cdn.jsdelivr.net/gh/AeonDave/pi-persona@6655d281ce7965a75303dd704bd29db73db04070/assets/demo1.mp4",
109
109
  "extensions": [
110
110
  "./src/extension.ts"
@@ -18,7 +18,7 @@ import { type ChildEngineOptions, type ChildRunSpec, runChildAgent } from "./chi
18
18
  import { looksLikeProviderError } from "./errors.ts";
19
19
  import { nextChildHandle } from "./handles.ts";
20
20
  import { combineSignals } from "./signals.ts";
21
- import { unknownAgentFailure, unknownContractFailure } from "./spec-preflight.ts";
21
+ import { toolModifierFailure, unknownAgentFailure, unknownContractFailure } from "./spec-preflight.ts";
22
22
  import type { ToolEvent } from "./stream.ts";
23
23
 
24
24
  /** The engine-side face of the cross-process broker (spec B3/B4/B7) — a child-engine
@@ -113,6 +113,9 @@ export function makeEngine(deps: EngineAdapterDeps): StrategyEngine {
113
113
  ): Promise<AgentResult> {
114
114
  const cfg = deps.resolveAgent(spec.agent);
115
115
  if (!cfg) return unknownAgentFailure(spec.agent, deps.listAgents?.() ?? []);
116
+ const tools = spec.tools ?? cfg.tools;
117
+ const invalidTools = toolModifierFailure(spec.agent, tools);
118
+ if (invalidTools) return invalidTools;
116
119
 
117
120
  // Resolved (and pinned) up front: the SAME def both instructs the member and
118
121
  // validates its output — an instruction/validation drift is impossible. A named
@@ -132,7 +135,6 @@ export function makeEngine(deps: EngineAdapterDeps): StrategyEngine {
132
135
  // a child without it defaults to "adaptive", which some models reject.
133
136
  if (model && deps.childThinking && !model.includes(":")) model = `${model}:${deps.childThinking}`;
134
137
  if (model) childSpec.model = model;
135
- const tools = spec.tools ?? cfg.tools;
136
138
  if (tools !== undefined) childSpec.tools = tools;
137
139
  if (cfg.excludeTools && cfg.excludeTools.length > 0) childSpec.excludeTools = cfg.excludeTools;
138
140
  // The shared behavioral layer (docs/SPINE.md) + the agent's own persona + any
@@ -35,7 +35,7 @@ import type { AgentResult } from "../orchestration/types.ts";
35
35
  import { looksLikeProviderError } from "./errors.ts";
36
36
  import { nextChildHandle } from "./handles.ts";
37
37
  import { combineSignals } from "./signals.ts";
38
- import { unknownAgentFailure, unknownContractFailure } from "./spec-preflight.ts";
38
+ import { toolModifierFailure, unknownAgentFailure, unknownContractFailure } from "./spec-preflight.ts";
39
39
  import { applyEvent, createStreamState, emptyUsage, type ProgressSnapshot, snapshot } from "./stream.ts";
40
40
 
41
41
  /** The minimal slice of Pi's AgentSession the engine drives — also the test seam. */
@@ -315,6 +315,9 @@ export function makeInProcessEngine(deps: InProcessDeps): StrategyEngine {
315
315
  async run(spec: AgentRunSpec, onProgress?, callSignal?, onSteerable?): Promise<AgentResult> {
316
316
  const cfg = deps.resolveAgent(spec.agent);
317
317
  if (!cfg) return unknownAgentFailure(spec.agent, deps.listAgents?.() ?? []);
318
+ const tools = spec.tools ?? cfg.tools;
319
+ const invalidTools = toolModifierFailure(spec.agent, tools);
320
+ if (invalidTools) return invalidTools;
318
321
  // A requested contract is a runtime guard, not merely prompt decoration. Resolve it
319
322
  // before model/session construction so a missing name can never run unconstrained.
320
323
  const requestedContract = spec.outputContract?.trim();
@@ -346,7 +349,6 @@ export function makeInProcessEngine(deps: InProcessDeps): StrategyEngine {
346
349
  const rm = model as { provider?: string; id?: string };
347
350
  const resolvedRef = rm.provider && rm.id ? `${rm.provider}/${rm.id}` : ref;
348
351
  const thinkingLevel = isThinkingLevel(deps.childThinking) ? deps.childThinking : undefined;
349
- const tools = spec.tools ?? cfg.tools;
350
352
  const excludeTools = cfg.excludeTools;
351
353
 
352
354
  // Diagnostic tag: which agent + which model ref + whether it was dynamically
@@ -31,3 +31,16 @@ export function unknownAgentFailure(agent: string, installed: string[]): AgentRe
31
31
  export function unknownContractFailure(agent: string, requested: string, installed: string[]): AgentResult {
32
32
  return failure(agent, `[${agent}] output contract "${requested}" not found${installedHint("contracts", installed)}`, "contract");
33
33
  }
34
+
35
+ /** Pi's SDK treats leading `+`/`-` entries in `tools` as default-tool modifiers, not as
36
+ * literal allowlist entries. AgentRunSpec.tools and agent frontmatter use this field as an
37
+ * allowlist, so reject that syntax before either engine reaches Pi. Use excludeTools for denies. */
38
+ export function toolModifierFailure(agent: string, tools: readonly string[] | undefined): AgentResult | undefined {
39
+ const modifier = tools?.find((selector) => /^[+-]/.test(selector.trim()));
40
+ if (modifier === undefined) return undefined;
41
+ return failure(
42
+ agent,
43
+ `[${agent}] unsupported tool modifier "${modifier.trim()}" in tools allowlist; use plain tool names/patterns or excludeTools for denials`,
44
+ "contract",
45
+ );
46
+ }
@@ -4,7 +4,7 @@ export const TELEMETRY_EVENT_NAME = "pi:telemetry" as const;
4
4
  export const LEGACY_TELEMETRY_EVENT_NAME = "pi-persona:telemetry" as const;
5
5
  export const LEGACY_TELEMETRY_VERSION = 1 as const;
6
6
  export const TELEMETRY_PRODUCER_ID = "pi-persona" as const;
7
- export const TELEMETRY_PRODUCER_VERSION = "1.16.2" as const;
7
+ export const TELEMETRY_PRODUCER_VERSION = "1.16.3" as const;
8
8
 
9
9
  export type InstanceStatus = "active" | "idle" | "stopped" | "stale" | (string & {});
10
10
  export type AgentKind = "subagent" | "delegate" | "council" | "flow" | "phase" | (string & {});