pi-advisor-flow 0.5.2 → 0.5.4

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
@@ -4,6 +4,19 @@ All notable changes to this project are documented here.
4
4
 
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 0.5.4 - 2026-09-08
8
+
9
+ ### Fixed
10
+
11
+ - Pinned the Socket security dependency overrides for `is-unicode-supported` and `safe-buffer` to exact published versions instead of floating `^1` ranges, keeping the resolved dependency tree unchanged while avoiding floating ranges in the published manifest.
12
+
13
+ ## 0.5.3 - 2026-09-08
14
+
15
+ ### Fixed
16
+
17
+ - Kept a malformed `advisor.json` from blocking every tool call while the Advisor flow is active: the `tool_call` handler now skips Advisor gating, notifies once per distinct configuration error, and resumes gating once the configuration is valid again.
18
+ - Preserved the one-shot tracked-file handoff across rejected follow-up calls: disabled `advisorTrackedFileContent` consent or an exhausted Advisor call budget now fails the `ask_advisor` call up front without consuming the handoff from the prior Advisor response.
19
+
7
20
  ## 0.5.2 - 2026-09-05
8
21
 
9
22
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-advisor-flow",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Advanced Executor/Advisor flow for Pi, fully configurable and extendable.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -47,13 +47,13 @@
47
47
  "*.{json,jsonc,ts}": "bun run lint:fix --"
48
48
  },
49
49
  "resolutions": {
50
- "is-unicode-supported": "npm:@socketregistry/is-unicode-supported@^1",
51
- "safe-buffer": "npm:@socketregistry/safe-buffer@^1"
50
+ "is-unicode-supported": "npm:@socketregistry/is-unicode-supported@1.0.5",
51
+ "safe-buffer": "npm:@socketregistry/safe-buffer@1.0.9"
52
52
  },
53
53
  "overrides": {
54
54
  "brace-expansion": "5.0.9",
55
- "is-unicode-supported": "npm:@socketregistry/is-unicode-supported@^1",
56
- "safe-buffer": "npm:@socketregistry/safe-buffer@^1",
55
+ "is-unicode-supported": "npm:@socketregistry/is-unicode-supported@1.0.5",
56
+ "safe-buffer": "npm:@socketregistry/safe-buffer@1.0.9",
57
57
  "undici": "8.9.0"
58
58
  },
59
59
  "devDependencies": {
@@ -3,6 +3,7 @@ import { Type } from "typebox";
3
3
  import {
4
4
  advisorRef,
5
5
  getAdvisorMaxCallsPerSession,
6
+ getAdvisorSettings,
6
7
  isSimpleMode,
7
8
  } from "../config/state.js";
8
9
  import { herdrAdvisorActivity, notifyHerdrAdvisorFailure } from "../herdr.js";
@@ -28,6 +29,30 @@ import type {
28
29
  ToolRegistrationContext,
29
30
  } from "./types.js";
30
31
 
32
+ /**
33
+ * Validates a tracked-file follow-up and consumes the one-shot handoff only
34
+ * when the call is worth it: consent must be enabled and the session budget
35
+ * was checked by the caller. A rejected call never burns the claim.
36
+ */
37
+ const claimTrackedHandoff = (
38
+ session: ToolRegistrationContext["session"],
39
+ includeTrackedFiles: string[] | undefined
40
+ ) => {
41
+ if (!includeTrackedFiles?.length) {
42
+ return;
43
+ }
44
+ if (!getAdvisorSettings().trackedFileContent) {
45
+ throw new Error(
46
+ "Tracked file attachments are disabled: enable the global advisorTrackedFileContent setting (Tracked file content in /advisor-settings) and retry."
47
+ );
48
+ }
49
+ if (!session.claimTrackedFiles(includeTrackedFiles)) {
50
+ throw new Error(
51
+ "Tracked file handoff requires a prior Advisor response that explicitly names every requested path and is consumed once."
52
+ );
53
+ }
54
+ };
55
+
31
56
  export const registerAskAdvisorTool = ({
32
57
  consult: requestAdvisor,
33
58
  pi,
@@ -39,18 +64,15 @@ export const registerAskAdvisorTool = ({
39
64
  "Consult the on-demand Advisor model for strategic guidance. Call with an empty object for a contextual review; attach an optional draft for concrete plan or completion review. If the Advisor explicitly names a missing file, you may make a sequential follow-up call with includeTrackedFiles when enabled and relevant.",
40
65
  async execute(_id, params, signal, onUpdate, ctx) {
41
66
  reservedCalls.delete(_id);
67
+ // The budget check precedes the handoff claim so a rejected call never
68
+ // consumes the one-shot tracked-file handoff.
42
69
  if (
43
- params.includeTrackedFiles?.length &&
44
- !session.claimTrackedFiles(params.includeTrackedFiles)
70
+ !(isSimpleMode() || session.canConsult(getAdvisorMaxCallsPerSession()))
45
71
  ) {
46
- throw new Error(
47
- "Tracked file handoff requires a prior Advisor response that explicitly names every requested path and is consumed once."
48
- );
72
+ throw new Error("Advisor call budget exhausted for this session.");
49
73
  }
74
+ claimTrackedHandoff(session, params.includeTrackedFiles);
50
75
  if (!isSimpleMode()) {
51
- if (!session.canConsult(getAdvisorMaxCallsPerSession())) {
52
- throw new Error("Advisor call budget exhausted for this session.");
53
- }
54
76
  session.consumeCall();
55
77
  }
56
78
  herdrAdvisorActivity.start();
@@ -17,6 +17,32 @@ export const registerToolLifecycle = ({
17
17
  scoutStatus,
18
18
  session,
19
19
  }: ToolRegistrationContext): void => {
20
+ // A throwing tool_call handler makes pi block that tool call (fail-safe), so
21
+ // a malformed advisor.json must not escape this handler: gating is skipped
22
+ // for that call and the failure is surfaced once per distinct outage. Config
23
+ // errors stay an Advisor concern instead of escalating into a session-wide
24
+ // outage.
25
+ let lastConfigErrorNotified: string | undefined;
26
+ const loadConfigOrSkipGating = (ctx: Parameters<typeof loadConfig>[0]) => {
27
+ try {
28
+ loadConfig(ctx);
29
+ lastConfigErrorNotified = undefined;
30
+ return true;
31
+ } catch (error) {
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ if (message !== lastConfigErrorNotified) {
34
+ lastConfigErrorNotified = message;
35
+ if (ctx.hasUI) {
36
+ ctx.ui.notify(
37
+ `Advisor gating skipped; configuration is invalid. ${message} Fix advisor.json.`,
38
+ "error"
39
+ );
40
+ }
41
+ }
42
+ return false;
43
+ }
44
+ };
45
+
20
46
  pi.on("session_start", (_event, ctx) => {
21
47
  session.resetTask();
22
48
  reservedCalls.clear();
@@ -57,7 +83,11 @@ export const registerToolLifecycle = ({
57
83
  if (!pi.getActiveTools().includes("ask_advisor")) {
58
84
  return;
59
85
  }
60
- loadConfig(ctx);
86
+ if (!loadConfigOrSkipGating(ctx)) {
87
+ // The ask_advisor execute path surfaces its own configuration errors as
88
+ // tool errors; every other tool must proceed without Advisor gating.
89
+ return;
90
+ }
61
91
  const reservation = reserveAdvisorCall(event, ctx, session, reservedCalls);
62
92
  if (event.toolName === "ask_advisor") {
63
93
  return reservation;