pi-advisor-flow 0.11.2-dev.1 → 0.11.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
@@ -4,11 +4,16 @@ 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/).
6
6
 
7
- ## Unreleased
7
+ ## 0.11.2 - 2026-10-07
8
8
 
9
9
  ### Fixed
10
10
 
11
11
  - Avoided a startup crash when a host omits `systemPromptOptions` from `before_agent_start`.
12
+ - Prevented OMP's legacy session-history fallback from disclosing entries or images before the latest reset boundary or outside the latest compaction's retained range.
13
+
14
+ ### Changed
15
+
16
+ - Documented support for OMP session managers without Pi's `buildContextEntries()` projection.
12
17
 
13
18
  ## 0.11.1 - 2026-10-04
14
19
 
package/README.md CHANGED
@@ -151,7 +151,7 @@ Advisor Scout is off by default. When enabled in `/advisor-settings` or via `"ad
151
151
 
152
152
  Advisor requests can include user messages, tool calls, tool results, targeted questions, trusted project and global `AGENTS.md` rules, and repository information. `advisorAgentsMdContext` is on by default and can be disabled in `/advisor-settings`; rules are sent as origin-labelled, capped, redacted, untrusted review guidance only. Untrusted projects withhold both rule files and tell the Advisor that rules were withheld. Repository context is configurable from no access through changed-file summaries to a capped patch; when it is disabled, the Advisor is told so rather than shown an apparently clean tree. Images from disclosed conversation and full-policy tool results can be sent as pixels only to image-capable Advisor models; Scout sees markers, not pixels. Exact tracked and untracked image files can be attached using `includeTrackedFiles` and `includeUntracked` under their existing separate global consent rules. Images are limited to four and 8 MiB total, with a 4 MiB per-image cap; unsupported, missing, or oversized images are reported as withheld, not reviewed. Explicit tracked and untracked file contents require separate global opt-ins and are sent as untrusted data. Secret redaction is off by default; when enabled, credential-shaped values in targeted questions are redacted before the provider request. Tools without an explicit policy use full context. Settings are global, so a project cannot silently change them.
153
153
 
154
- When Scout is enabled, the Executor model provider also receives bounded Advisor-eligible conversation history. Read [Privacy and data handling](https://github.com/philipbrembeck/pi-advisor/blob/main/docs/privacy.md) before using pi-advisor with sensitive work.
154
+ When Scout is enabled, the Executor model provider also receives bounded Advisor-eligible conversation history. Pi's `buildContextEntries()` projection is used when available. OMP-compatible session managers without that API are supported through the active branch, with the latest reset boundary and compaction's retained range applied before history or images are disclosed; cleared and compacted-out entries are not forwarded. Read [Privacy and data handling](https://github.com/philipbrembeck/pi-advisor/blob/main/docs/privacy.md) before using pi-advisor with sensitive work.
155
155
 
156
156
  ## Documentation
157
157
 
package/dist/index.js CHANGED
@@ -2403,6 +2403,35 @@ var detectImageFormat = (bytes) => {
2403
2403
  }
2404
2404
  };
2405
2405
 
2406
+ // src/session-context.ts
2407
+ var projectBranch = (source) => {
2408
+ const resetBoundaryIndex = source.findLastIndex((entry) => Object.is(entry.type, "reset_boundary"));
2409
+ const branch = source.slice(resetBoundaryIndex + 1);
2410
+ const compactionIndex = branch.findLastIndex((entry) => entry.type === "compaction");
2411
+ if (compactionIndex === -1) {
2412
+ return branch;
2413
+ }
2414
+ const compaction = branch[compactionIndex];
2415
+ if (compaction.type !== "compaction") {
2416
+ return branch;
2417
+ }
2418
+ const firstKeptIndex = branch.findIndex((entry, index) => index < compactionIndex && entry.id === compaction.firstKeptEntryId);
2419
+ const projected = [compaction];
2420
+ if (firstKeptIndex !== -1) {
2421
+ for (let index = firstKeptIndex;index < compactionIndex; index += 1) {
2422
+ const entry = branch[index];
2423
+ if (entry.type !== "compaction" && !(entry.type === "message" && entry.message.role === "system")) {
2424
+ projected.push(entry);
2425
+ }
2426
+ }
2427
+ }
2428
+ return [...projected, ...branch.slice(compactionIndex + 1)];
2429
+ };
2430
+ var sessionContextEntries = (ctx) => {
2431
+ const sessionManager = ctx.sessionManager;
2432
+ return sessionManager.buildContextEntries ? sessionManager.buildContextEntries() : projectBranch(sessionManager.getBranch());
2433
+ };
2434
+
2406
2435
  // src/images.ts
2407
2436
  var ADVISOR_IMAGE_MAX_BYTES = 4 * 1024 * 1024;
2408
2437
  var ADVISOR_IMAGES_TOTAL_MAX_BYTES = 8 * 1024 * 1024;
@@ -2465,12 +2494,12 @@ var selectedConversationImages = (ctx, conversation, policies, selectedEntryIds,
2465
2494
  if (!conversation) {
2466
2495
  return { imagePartsSeen: 0, selected: [] };
2467
2496
  }
2468
- const entries = selectedEntryIds ? ctx.sessionManager.buildContextEntries() : ctx.sessionManager.getBranch();
2497
+ const entries = sessionContextEntries(ctx);
2469
2498
  const images = [];
2470
2499
  const seen = new Set;
2471
2500
  let imagePartsSeen = 0;
2472
2501
  for (const [index, entry] of entries.entries()) {
2473
- if (entry.type !== "message" || selectedEntryIds && !selectedEntryIds.has(entry.id ?? String(index))) {
2502
+ if (entry.type !== "message" || selectedEntryIds !== undefined && !selectedEntryIds.has(entry.id ?? String(index))) {
2474
2503
  continue;
2475
2504
  }
2476
2505
  const { message } = entry;
@@ -2832,7 +2861,7 @@ var recentConversation = (ctx, maxChars = 15000, toolResultMaxLines = advisorToo
2832
2861
  if (maxChars === 0) {
2833
2862
  return "";
2834
2863
  }
2835
- const entries = ctx.sessionManager.getBranch().map((entry) => conversationEntry(entry, toolResultMaxLines, toolResultMaxBytes, policies, redact, describeImages, imageNonce)).filter((entry) => entry !== undefined);
2864
+ const entries = sessionContextEntries(ctx).map((entry) => conversationEntry(entry, toolResultMaxLines, toolResultMaxBytes, policies, redact, describeImages, imageNonce)).filter((entry) => entry !== undefined);
2836
2865
  return selectRecentEntries(entries, maxChars);
2837
2866
  };
2838
2867
 
@@ -3245,7 +3274,7 @@ var fitToBudget = (built, caps) => {
3245
3274
  };
3246
3275
  };
3247
3276
  var buildScoutManifest = (ctx, options = {}) => {
3248
- const entries = ctx.sessionManager.buildContextEntries();
3277
+ const entries = sessionContextEntries(ctx);
3249
3278
  const caps = resolveCaps(options);
3250
3279
  const indexed = indexToolCalls(entries);
3251
3280
  if (!indexed.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-advisor-flow",
3
- "version": "0.11.2-dev.1",
3
+ "version": "0.11.2",
4
4
  "description": "Advanced Executor/Advisor flow for Pi, fully configurable and extendable.",
5
5
  "keywords": [
6
6
  "advisor",
@@ -11,6 +11,7 @@ import { contentParts, isRecordOf, isString } from "./content-utils.ts";
11
11
  import type { RecordValue } from "./content-utils.ts";
12
12
  import { imageMarker } from "./images.ts";
13
13
  import { redactSecrets } from "./redaction.ts";
14
+ import { sessionContextEntries } from "./session-context.ts";
14
15
  import { capToolResult } from "./tool-result-cap.ts";
15
16
 
16
17
  const textFromPart = <Part>(part: Part): string => {
@@ -204,8 +205,7 @@ export const recentConversation = (
204
205
  if (maxChars === 0) {
205
206
  return "";
206
207
  }
207
- const entries = ctx.sessionManager
208
- .getBranch()
208
+ const entries = sessionContextEntries(ctx)
209
209
  .map((entry) =>
210
210
  conversationEntry(
211
211
  entry,
package/src/images.ts CHANGED
@@ -7,6 +7,7 @@ import type { AdvisorToolPolicies } from "./config/types.ts";
7
7
  import { contentParts, isRecordOf, isString } from "./content-utils.ts";
8
8
  import type { RecordValue } from "./content-utils.ts";
9
9
  import { detectImageFormat } from "./image-validation.ts";
10
+ import { sessionContextEntries } from "./session-context.ts";
10
11
 
11
12
  export const ADVISOR_IMAGE_MAX_BYTES = 4 * 1024 * 1024;
12
13
  export const ADVISOR_IMAGES_TOTAL_MAX_BYTES = 8 * 1024 * 1024;
@@ -120,16 +121,15 @@ export const selectedConversationImages = (
120
121
  if (!conversation) {
121
122
  return { imagePartsSeen: 0, selected: [] };
122
123
  }
123
- const entries = selectedEntryIds
124
- ? ctx.sessionManager.buildContextEntries()
125
- : ctx.sessionManager.getBranch();
124
+ const entries = sessionContextEntries(ctx);
126
125
  const images: SelectedImage[] = [];
127
126
  const seen = new Set<string>();
128
127
  let imagePartsSeen = 0;
129
128
  for (const [index, entry] of entries.entries()) {
130
129
  if (
131
130
  entry.type !== "message" ||
132
- (selectedEntryIds && !selectedEntryIds.has(entry.id ?? String(index)))
131
+ (selectedEntryIds !== undefined &&
132
+ !selectedEntryIds.has(entry.id ?? String(index)))
133
133
  ) {
134
134
  continue;
135
135
  }
@@ -19,6 +19,7 @@ import type {
19
19
  ScoutContextGroup,
20
20
  ScoutManifestResult,
21
21
  } from "./scout-types.ts";
22
+ import { sessionContextEntries } from "./session-context.ts";
22
23
 
23
24
  // Re-exports preserve scout-context's historical public import surface.
24
25
  export { reconstructScoutConversation } from "./scout-reconstruct.ts";
@@ -174,7 +175,7 @@ export const buildScoutManifest = (
174
175
  ctx: ExtensionContext,
175
176
  options: BuildScoutManifestOptions = {}
176
177
  ): ScoutManifestResult => {
177
- const entries = ctx.sessionManager.buildContextEntries();
178
+ const entries = sessionContextEntries(ctx);
178
179
  const caps = resolveCaps(options);
179
180
  const indexed = indexToolCalls(entries);
180
181
  if (!indexed.ok) {
@@ -0,0 +1,52 @@
1
+ import type {
2
+ ExtensionContext,
3
+ SessionEntry,
4
+ } from "@earendil-works/pi-coding-agent";
5
+
6
+ type SessionManagerWithContextEntries = ExtensionContext["sessionManager"] & {
7
+ buildContextEntries?: () => SessionEntry[];
8
+ };
9
+
10
+ const projectBranch = (source: SessionEntry[]): SessionEntry[] => {
11
+ const resetBoundaryIndex = source.findLastIndex((entry) =>
12
+ Object.is(entry.type, "reset_boundary")
13
+ );
14
+ const branch = source.slice(resetBoundaryIndex + 1);
15
+ const compactionIndex = branch.findLastIndex(
16
+ (entry) => entry.type === "compaction"
17
+ );
18
+ if (compactionIndex === -1) {
19
+ return branch;
20
+ }
21
+ const compaction = branch[compactionIndex];
22
+ if (compaction.type !== "compaction") {
23
+ return branch;
24
+ }
25
+ const firstKeptIndex = branch.findIndex(
26
+ (entry, index) =>
27
+ index < compactionIndex && entry.id === compaction.firstKeptEntryId
28
+ );
29
+ const projected: SessionEntry[] = [compaction];
30
+ if (firstKeptIndex !== -1) {
31
+ for (let index = firstKeptIndex; index < compactionIndex; index += 1) {
32
+ const entry = branch[index];
33
+ if (
34
+ entry.type !== "compaction" &&
35
+ !(entry.type === "message" && entry.message.role === "system")
36
+ ) {
37
+ projected.push(entry);
38
+ }
39
+ }
40
+ }
41
+ return [...projected, ...branch.slice(compactionIndex + 1)];
42
+ };
43
+
44
+ export const sessionContextEntries = (
45
+ ctx: ExtensionContext
46
+ ): SessionEntry[] => {
47
+ // SAFETY: older hosts lack projection; retain reset and compaction boundaries before disclosure.
48
+ const sessionManager = ctx.sessionManager as SessionManagerWithContextEntries;
49
+ return sessionManager.buildContextEntries
50
+ ? sessionManager.buildContextEntries()
51
+ : projectBranch(sessionManager.getBranch());
52
+ };