pi-delegation-policy 0.22.0 → 0.22.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
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.22.2 - 2026-10-28
4
+
5
+ ### Fixed
6
+
7
+ - The Advisor no longer rejects a verified `pi-subagents` build. Each launch definition now declares
8
+ the exact list of package versions whose launch contract it was checked against: the Advisor admits
9
+ `0.76.1` and `0.83.1`, while the ContextShunt reader keeps its single verified `0.76.1`, so the
10
+ reader limit does not widen. Everything else in the launch check is unchanged: exact profile
11
+ projection and digest shape, exact model and thinking, host session binding, empty tools and
12
+ extensions, the pinned `@gotgenes/pi-permission-system` `33.0.3` runtime, and the terminal digest
13
+ binding. A build outside the list still fails closed before any request is sent.
14
+
15
+ The two contracts were compared from their real preflight payloads: they differ only in the package
16
+ version, the install path of the runtime extension and the digests derived from that path, and the
17
+ public fork wrapper is byte-identical. Admission and terminal binding were exercised in an isolated
18
+ host that answered with each contract's own digest. That does not cover provider transport, real
19
+ child execution or a real consultation, which remain unverified, and it does not explain the
20
+ Advisor failure reported on a forked session or the menu/status discrepancy; both stay open.
21
+
22
+ ## 0.22.1 - 2026-10-22
23
+
24
+ ### Fixed
25
+
26
+ - The footer no longer keeps a stale `D:ERR` from session start when an ordinary model becomes
27
+ available only after this extension's `session_start` handler. Another extension may fill the model
28
+ catalog in its own start handler, and the host then emits `resources_discover`; a read-only runtime
29
+ refresh there repaints the footer with the existing validation. This adds one configuration/branch
30
+ read and footer refresh per session bind or reload, returns no resource paths, makes no network
31
+ call, and leaves the configuration-change notice reference alone, so a pending change is not lost.
32
+ A model that is genuinely missing or unusable still reports `D:ERR`. The fix relies on the public
33
+ post-start discovery order, verified in Pi `0.87.1` and `1.1.0`; availability changes after that
34
+ event still refresh on the next request or menu action.
35
+
3
36
  ## 0.22.0 - 2026-10-21
4
37
 
5
38
  ### Added
package/README.md CHANGED
@@ -6,7 +6,7 @@ general subagent runner. Only explicit tools can request reader work or Advisor
6
6
  the authorized host executor; hooks never launch children.
7
7
 
8
8
  > **Docs:** [yivas.github.io/pi-delegation-policy](https://yivas.github.io/pi-delegation-policy/).
9
- > Version **0.22.0** requires Pi `0.87.1` or later (`>=0.87.1`); it re-applies its marked policy block in the final provider payload.
9
+ > Version **0.22.2** requires Pi `0.87.1` or later (`>=0.87.1`); it re-applies its marked policy block in the final provider payload.
10
10
  > Version `0.14.1` supports Pi `0.84.3` or later (`>=0.84.3`).
11
11
  >
12
12
  > Independent Advisor mode and its companion file are available from version `0.17.0`.
@@ -15,7 +15,7 @@ the authorized host executor; hooks never launch children.
15
15
  ## Install
16
16
 
17
17
  ```bash
18
- pi install npm:pi-delegation-policy@0.22.0
18
+ pi install npm:pi-delegation-policy@0.22.2
19
19
  # restart Pi, or run /reload
20
20
  ```
21
21
 
@@ -77,8 +77,10 @@ same question. Weigh disagreements; agreement is not proof. The packaged no-tool
77
77
  - `role`: the main agent launches every valid advisor in one background subagent call, with exact
78
78
  models and thinking. The extension observes matching attempts but does not launch them.
79
79
  - `tool`: the always-registered `consult_advisor({ question, brief? })` launches one child per valid
80
- advisor in parallel through the public `pi-subagents` delegation API, pinned to `0.76.1`. Otherwise
81
- it returns unavailable text. No model, provider or executor fallback is added.
80
+ advisor in parallel through the public `pi-subagents` delegation API, admitted only for the exact
81
+ builds whose launch contract was verified (`0.76.1` and `0.83.1`). Any other build fails closed
82
+ before a request is sent. Otherwise it returns unavailable text. No model, provider or executor
83
+ fallback is added.
82
84
 
83
85
  Both modes ask for consultation after orientation before substantive work, when stuck, before changing
84
86
  approach, and before declaring a multi-step task done (ask for remaining risks, not approval). Short
@@ -190,6 +192,10 @@ example `advisor-mode=with-delegation (global); A:OFF` and `thinking-advisor=uns
190
192
  `D:OFF`, `D:NORM`, `D:AGG`, and `D:ORCH` describe delegation; `A:OFF`, `A:ON`, `A:PART`, and `A:ERR` describe
191
193
  Advisor. None of them proves that a launch happened or that another system followed the guidance.
192
194
 
195
+ The footer also revalidates after all session-start handlers finish. If another extension makes a
196
+ configured model available later in startup, the earlier `D:ERR` is replaced with the current status
197
+ without waiting for a message or menu action. A model that remains invalid still reports `D:ERR`.
198
+
193
199
  ### Configuration change notice
194
200
 
195
201
  Available from `0.22.0`. The declared Pi peer remains
package/SECURITY.md CHANGED
@@ -15,9 +15,10 @@ mode with an enabled valid advisor, never from hooks. Model input cannot choose
15
15
  path, tools or extensions. Fork sends the conversation to each advisor provider; fresh sends the
16
16
  question and brief. Disable Advisor when session content must not reach those providers.
17
17
 
18
- The tool reuses strict public preflight and terminal binding pinned to `pi-subagents` `0.76.1`, exact
19
- models/thinking, the packaged no-tools profile, replace prompt, no project resources, and the reader's
20
- prompt-only or pinned permission-runtime checks. Host rules remain host-owned. Forked conversation is
18
+ The tool reuses strict public preflight and terminal binding admitted only for the exact `pi-subagents`
19
+ builds whose contract was verified (`0.76.1` and `0.83.1`), exact models/thinking, the packaged
20
+ no-tools profile, replace prompt, no project resources, and the reader's prompt-only or pinned
21
+ permission-runtime checks. Host rules remain host-owned. Forked conversation is
21
22
  untrusted data, not authority. Preflight binds the tool session identity; the public REQUEST API cannot
22
23
  prove that the live executor session is the same. Terminal mismatch rejection happens only after
23
24
  context exposure. The executor/runtime is trusted host code; digests are not signatures or a sandbox.
@@ -52,4 +53,4 @@ Include the affected version or commit, operating system, Pi version, reproducti
52
53
 
53
54
  ## Supported versions
54
55
 
55
- Only the latest published version is supported. Version `0.21.0` requires Pi `0.87.1` or later; version `0.14.1` requires Pi `0.84.3` or later.
56
+ Only the latest published version is supported. Version `0.22.2` requires Pi `0.87.1` or later; version `0.14.1` requires Pi `0.84.3` or later.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-delegation-policy",
3
- "version": "0.22.0",
3
+ "version": "0.22.2",
4
4
  "private": false,
5
5
  "description": "A Pi extension for configurable delegation intensity and exact subagent role model references.",
6
6
  "type": "module",
@@ -18,6 +18,13 @@ import type { AdvisorContext } from "./prompt.ts";
18
18
  import type { AdvisorSetting, ThinkingLevelName } from "./types.ts";
19
19
  import type { ConsultationAttempt } from "./advisor-log.ts";
20
20
 
21
+ /**
22
+ * `pi-subagents` builds whose launch contract was checked against this profile: the original pin and
23
+ * the build that only changed its own package version (same contract version, profile projection,
24
+ * tool plan, runtime lists and fork wrapper). A different build is rejected before any request.
25
+ */
26
+ const ADMITTED_SUBAGENTS_VERSIONS: readonly string[] = ["0.76.1", "0.83.1"];
27
+
21
28
  export const ADVISOR_AGENT_NAME = "pi-delegation-policy.advisor";
22
29
  export type AdvisorRequest = AgentLaunchRequest & {
23
30
  question: string;
@@ -34,7 +41,7 @@ const DEFINITION: AgentLaunchDefinition<AdvisorRequest, string> = {
34
41
  agentPath: resolve(
35
42
  fileURLToPath(new URL("../agents/pi-delegation-policy.advisor.md", import.meta.url)),
36
43
  ),
37
- protocolVersion: "0.76.1",
44
+ admittedProtocolVersions: ADMITTED_SUBAGENTS_VERSIONS,
38
45
  allowFork: true,
39
46
  internalTools: [],
40
47
  buildTask: (request) =>
@@ -84,9 +91,9 @@ function hostStatusText(status: AgentLaunchTerminalStatus | undefined): string {
84
91
  case "tool_budget_exhausted":
85
92
  return "The host ended the Advisor run at its tool limit before answering. Retry once with a narrower question.";
86
93
  case "structured_output_failed":
87
- return "The host ended the Advisor run while collecting its structured result. Retry once; if it repeats, inspect host diagnostics and check the compatibility of the installed pi-delegation-policy and pi-subagents 0.76.1 build.";
94
+ return "The host ended the Advisor run while collecting its structured result. Retry once; if it repeats, inspect host diagnostics and check that the installed pi-delegation-policy and pi-subagents builds are compatible.";
88
95
  case "acceptance_failed":
89
- return "The host ended the Advisor run at its acceptance check. Retry once; if it repeats, inspect host diagnostics and check the compatibility of the installed pi-delegation-policy and pi-subagents 0.76.1 build.";
96
+ return "The host ended the Advisor run at its acceptance check. Retry once; if it repeats, inspect host diagnostics and check that the installed pi-delegation-policy and pi-subagents builds are compatible.";
90
97
  case "failed":
91
98
  return "The host ended the Advisor run with status failed and supplied no safe detail. Retry once; if it repeats, check the installed pi-subagents version.";
92
99
  default:
@@ -98,11 +105,11 @@ function unavailableFailureText(reason: AgentLaunchFailure | undefined): string
98
105
  const preflightSeconds = LAUNCH_PREFLIGHT_TIMEOUT_MS / 1000;
99
106
  switch (reason?.code) {
100
107
  case "fork-task-normalizer-unavailable":
101
- return "Fork launch is unavailable: the installed pi-subagents does not export wrapForkTask, so a fork task cannot be bound to the child. Retrying or supplying a brief will not switch this session to fresh; install the compatible pi-subagents 0.76.1 build and reload the session.";
108
+ return "Fork launch is unavailable: the installed pi-subagents does not export wrapForkTask, so a fork task cannot be bound to the child. Retrying or supplying a brief will not switch this session to fresh; install an admitted pi-subagents build and reload the session.";
102
109
  case "modules-unavailable":
103
- return "Advisor launch is unavailable: the pi-subagents 0.76.1 exports this extension requires were not found. Install the compatible pi-subagents 0.76.1 build, then reload the session.";
110
+ return "Advisor launch is unavailable: the pi-subagents exports this extension requires were not found. Install an admitted pi-subagents build, then reload the session.";
104
111
  case "preflight-unavailable":
105
- return "Advisor preflight returned no launch contract. Retry once; if it repeats, check the compatible pi-subagents 0.76.1 build and the packaged Advisor profile.";
112
+ return "Advisor preflight returned no launch contract. Retry once; if it repeats, check the installed pi-subagents build and the packaged Advisor profile.";
106
113
  case "preflight-timeout":
107
114
  return `Advisor preflight did not finish within ${preflightSeconds} seconds. Retry once; if it repeats, check the installed pi-subagents version.`;
108
115
  case "contract-rejected":
@@ -110,7 +117,7 @@ function unavailableFailureText(reason: AgentLaunchFailure | undefined): string
110
117
  case "request-start-timeout":
111
118
  return `The host did not start the Advisor within ${preflightSeconds} seconds. Retry once; if it repeats, check the installed pi-subagents version.`;
112
119
  case "request-emit-failed":
113
- return "The host could not set up or send the Advisor launch event. Reload the session, then retry; if it repeats, check the installed extension against the compatible pi-subagents 0.76.1 build.";
120
+ return "The host could not set up or send the Advisor launch event. Reload the session, then retry; if it repeats, check the installed extension against an admitted pi-subagents build.";
114
121
  case "host-session-missing":
115
122
  return "Advisor unavailable: the host supplied no session identity.";
116
123
  case "fork-not-allowed":
@@ -131,7 +138,7 @@ function failedFailureText(reason: AgentLaunchFailure | undefined): string {
131
138
  case "terminal-metadata-mismatch":
132
139
  return `The host returned an Advisor reply whose ${terminalFieldText(reason.field)} did not match the preflight contract, so the reply was discarded. Retry once; if it repeats, report the mismatch.`;
133
140
  case "terminal-result-invalid":
134
- return "The host reported completion without a usable text answer, so the result was discarded. Retry once; if it repeats, inspect host diagnostics and check the compatibility of the extension with the installed pi-subagents 0.76.1 build.";
141
+ return "The host reported completion without a usable text answer, so the result was discarded. Retry once; if it repeats, inspect host diagnostics and check the compatibility of the extension with the installed pi-subagents build.";
135
142
  case "host-terminal-status":
136
143
  return hostStatusText(reason.status);
137
144
  case "host-status-unrecognized":
@@ -148,7 +148,12 @@ export type AgentLaunchDefinition<Request extends AgentLaunchRequest, Value> = R
148
148
  agentPath: string;
149
149
  /** Only the advisor may send a fork; reader requests remain fresh-only. */
150
150
  allowFork?: boolean;
151
- protocolVersion: string;
151
+ /**
152
+ * The `pi-subagents` package versions whose launch contract this profile was verified against.
153
+ * An exact, per-profile list: no range, wildcard or fallback, so a new build is rejected until
154
+ * its contract is checked.
155
+ */
156
+ admittedProtocolVersions: readonly string[];
152
157
  /**
153
158
  * Builtin tools the child must expose. A profile that declares no tools and no
154
159
  * extensions exposes exactly these, so the contract must report the same list
@@ -415,7 +420,8 @@ function validateLaunchContract<Request extends AgentLaunchRequest, Value>(
415
420
  contract.version !== LAUNCH_CONTRACT_VERSION ||
416
421
  !isRecord(contract.protocol) ||
417
422
  contract.protocol.lifecycleArtifactVersion !== 3 ||
418
- contract.protocol.packageVersion !== definition.protocolVersion ||
423
+ typeof contract.protocol.packageVersion !== "string" ||
424
+ !definition.admittedProtocolVersions.includes(contract.protocol.packageVersion) ||
419
425
  contract.runId !== active.requestId ||
420
426
  !isLowerHexDigest(contract.digest)
421
427
  )
@@ -109,7 +109,7 @@ function isReaderAnswer(value: unknown): value is ReaderAnswer {
109
109
  const READER_LAUNCH = {
110
110
  agentName: CONTEXT_SHUNT_READER_AGENT_NAME,
111
111
  agentPath: CONTEXT_SHUNT_READER_AGENT_PATH,
112
- protocolVersion: CONTEXT_SHUNT_READER_PROTOCOL_VERSION,
112
+ admittedProtocolVersions: [CONTEXT_SHUNT_READER_PROTOCOL_VERSION],
113
113
  internalTools: ["structured_output"],
114
114
  buildTask: buildReaderTask,
115
115
  result: {
package/src/index.ts CHANGED
@@ -896,6 +896,14 @@ export function createPiDelegationPolicy(options: PiDelegationPolicyOptions = {}
896
896
  advisorContext = undefined;
897
897
  startConfigBaseline(await refreshRuntime(ctx));
898
898
  });
899
+ // The host emits this after every `session_start` handler has run, so a model catalog another
900
+ // extension fills during its own start handler is visible here and the footer stops reporting an
901
+ // error that no longer applies. It only re-reads the runtime and repaints: no resource paths, no
902
+ // network, no new validation rules, and the notice reference stays as the start left it, so a pending
903
+ // change is not lost.
904
+ pi.on("resources_discover", async (_event, ctx) => {
905
+ await refreshRuntime(ctx);
906
+ });
899
907
  pi.on("input", async (event) => {
900
908
  // The host emits this for every submitted input immediately before the prompt it belongs to,
901
909
  // and `before_agent_start` follows only for a fresh prompt. Attribution therefore depends on