pi-smart-compact 10.1.5 → 10.2.0

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
@@ -2,6 +2,34 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [10.2.0] - 2026-10-05
6
+
7
+ ### Release approval
8
+
9
+ - Minor release approved by the release owner after the deterministic gates
10
+ on the exact candidate. No version-specific applied-canary cohort or live
11
+ provider run exists for `10.2.0`; the feature is evidenced by unit tests
12
+ only. The note is gated by `Navigation guide` (`contextGuidanceEnabled`)
13
+ and turns off with it.
14
+
15
+ ### Added
16
+
17
+ - **Context attention.** Context pressure used to reach the model only through
18
+ the human status line and `smart_context status` results, so the anchor,
19
+ checkpoint/rewind, trim and compaction tools sat idle until a person asked
20
+ for them. The extension now sends the model one short note when usage
21
+ enters the cleanup band and one more at the compaction band, naming only
22
+ the actions available right now: `smart_navigation anchor` (with the lazy
23
+ `smart_tools` load step when needed), `smart_context checkpoint`/`rewind`
24
+ and `trim` only when history edits are possible on this model (signed
25
+ Anthropic thinking blocks them), plus whether automatic compaction will
26
+ prepare. Mid-turn the note rides along with the next tool batch; at a turn
27
+ end a cleanup note waits for the next prompt and a compaction note is
28
+ skipped under auto-trigger because settled compaction runs first. One note
29
+ per band per session, re-armed once pressure clears; the note is a tail
30
+ message, so the cached prefix is untouched. Gated by `Navigation guide`
31
+ (`contextGuidanceEnabled`), tool loading and agent tool access.
32
+
5
33
  ## [10.1.5] - 2026-10-05
6
34
 
7
35
  ### Release approval
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Context attention: one short note to the model when context pressure enters
3
+ * a band, naming only the actions that are available right now.
4
+ *
5
+ * Pressure used to reach the model only through the human status line and
6
+ * `smart_context status` results, so the navigation, history and compaction
7
+ * tools sat idle until a person asked for them. The note rides along with the
8
+ * next tool batch (steer) during a turn; when a turn ends in the cleanup band
9
+ * it waits for the next prompt instead. One note per band per session,
10
+ * re-armed once pressure clears (compaction, trim, rewind). The note is a
11
+ * tail custom message, so the cached prefix is untouched.
12
+ */
13
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
14
+ import type { CompactConfig } from "../types.ts";
15
+ export declare const ATTENTION_CUSTOM_TYPE = "smart-compact-attention";
16
+ type AttentionConfig = Pick<CompactConfig, "contextNavigationEnabled" | "contextGuidanceEnabled" | "toolLoading" | "autoTrigger" | "minContextPercent" | "prepareContextPercent" | "maxContextTokens">;
17
+ export interface ContextAttentionDeps {
18
+ config: () => AttentionConfig;
19
+ /** Agent-driven context changes are permitted and nothing (pivot, running compaction) takes priority. */
20
+ canAgentAct: (ctx: ExtensionContext) => boolean;
21
+ /** A lazy group can still be loaded through `smart_tools`. */
22
+ reachable: (group: "navigation" | "history") => boolean;
23
+ }
24
+ export declare function registerContextAttention(pi: Pick<ExtensionAPI, "on" | "sendMessage" | "getActiveTools">, deps: ContextAttentionDeps): void;
25
+ export {};
26
+ //# sourceMappingURL=context-attention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-attention.d.ts","sourceRoot":"","sources":["../../src/app/context-attention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAK/D,KAAK,eAAe,GAAG,IAAI,CAAC,aAAa,EACvC,0BAA0B,GAAG,wBAAwB,GAAG,aAAa,GAAG,aAAa,GACnF,mBAAmB,GAAG,uBAAuB,GAAG,kBAAkB,CAAC,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,eAAe,CAAC;IAC9B,yGAAyG;IACzG,WAAW,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC;IAChD,8DAA8D;IAC9D,SAAS,EAAE,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,KAAK,OAAO,CAAC;CACzD;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,aAAa,GAAG,gBAAgB,CAAC,EAC/D,IAAI,EAAE,oBAAoB,GACzB,IAAI,CAoCN"}
@@ -8,7 +8,7 @@ import type { CompressionProfile, ProfileConfig } from "./types.ts";
8
8
  * `package.json#version`. Do not hand-edit this line for releases; bump
9
9
  * package.json and run `bun run sync-version`.
10
10
  */
11
- export declare const VERSION = "10.1.5";
11
+ export declare const VERSION = "10.2.0";
12
12
  /**
13
13
  * Opt-in components that are not installed with the extension: optional peer
14
14
  * dependencies in package.json, pinned to the versions the release audit
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEN,KAAK,YAAY,EAEjB,MAAM,iCAAiC,CAAC;AAkEzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAyDtE,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QA8lB7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEN,KAAK,YAAY,EAEjB,MAAM,iCAAiC,CAAC;AAmEzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAyDtE,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QAomB7D"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "@earendil-works/pi-coding-agent";
6
6
 
7
7
  // src/constants.ts
8
- var VERSION = "10.1.5";
8
+ var VERSION = "10.2.0";
9
9
  var OPTIONAL_COMPONENTS = {
10
10
  mnemopi: { name: "@oh-my-pi/pi-mnemopi", version: "18.3.1", enables: "Mnemopi memory store" },
11
11
  bun: { name: "bun", version: "1.4.2", enables: "Mnemopi worker runtime when no Bun 1.3.14+ is on PATH" },
@@ -16807,6 +16807,85 @@ function createSettledAutoTrigger(options = {}) {
16807
16807
  return { request, noteCompaction, clear };
16808
16808
  }
16809
16809
 
16810
+ // src/app/context-attention.ts
16811
+ var ATTENTION_CUSTOM_TYPE = "smart-compact-attention";
16812
+ var CONTEXT_TOOL_NAME = "smart_context";
16813
+ var LOADER_TOOL_NAME = "smart_tools";
16814
+ function registerContextAttention(pi, deps) {
16815
+ let noted = null;
16816
+ const clear = () => {
16817
+ noted = null;
16818
+ };
16819
+ pi.on("session_start", clear);
16820
+ pi.on("session_before_switch", clear);
16821
+ pi.on("session_before_fork", clear);
16822
+ pi.on("session_tree", clear);
16823
+ pi.on("message_end", (event, ctx) => {
16824
+ const message = event.message;
16825
+ if (message.role !== "assistant")
16826
+ return;
16827
+ const settings = deps.config();
16828
+ if (!settings.contextNavigationEnabled || !settings.contextGuidanceEnabled || settings.toolLoading === "off")
16829
+ return;
16830
+ const sessionId = resolveSessionId(ctx);
16831
+ const pressure = contextPressure(ctx, settings);
16832
+ const band = pressure.compaction ? "compaction" : pressure.cleanup ? "cleanup" : null;
16833
+ if (!band) {
16834
+ if (noted?.sessionId === sessionId)
16835
+ noted = null;
16836
+ return;
16837
+ }
16838
+ if (noted?.sessionId === sessionId && (noted.band === band || noted.band === "compaction"))
16839
+ return;
16840
+ if (message.content.some((block) => block.type === "toolCall" && (block.name === NAVIGATION_TOOL_NAME || block.name === CONTEXT_TOOL_NAME)))
16841
+ return;
16842
+ const midTurn = message.stopReason === "toolUse";
16843
+ if (!midTurn && band === "compaction" && settings.autoTrigger)
16844
+ return;
16845
+ if (!deps.canAgentAct(ctx))
16846
+ return;
16847
+ const active = new Set(pi.getActiveTools());
16848
+ const content = renderAttention(band, pressure.percent, settings, ctx, active, deps.reachable);
16849
+ if (!content)
16850
+ return;
16851
+ noted = { sessionId, band };
16852
+ pi.sendMessage({ customType: ATTENTION_CUSTOM_TYPE, content, display: true, details: { band, percent: pressure.percent } }, { deliverAs: midTurn ? "steer" : "nextTurn" });
16853
+ });
16854
+ }
16855
+ function renderAttention(band, percent, settings, ctx, active, reachable) {
16856
+ const lines = [];
16857
+ const load = (group, tool) => active.has(tool) ? "" : reachable(group) && active.has(LOADER_TOOL_NAME) ? ` Load it first: ${LOADER_TOOL_NAME} load ${group}.` : null;
16858
+ const navigation = load("navigation", NAVIGATION_TOOL_NAME);
16859
+ const history = load("history", CONTEXT_TOOL_NAME);
16860
+ if (navigation !== null) {
16861
+ lines.push((band === "compaction" ? `- Anchor now: ${NAVIGATION_TOOL_NAME} anchor with a name and a concise handoff (done / in progress / next step). The summary starts from it and the prompt cache stays warm up to it.` : `- Finishing a unit of work? ${NAVIGATION_TOOL_NAME} anchor with a concise handoff; safe cleanup is queued automatically and the prefix up to the anchor stays cached.`) + navigation);
16862
+ }
16863
+ if (history !== null) {
16864
+ const branch = ctx.sessionManager.getBranch();
16865
+ let trimmable = 0;
16866
+ let blocked = false;
16867
+ try {
16868
+ const plan = planContextTrim(branch, inspectContext(branch, resolveSessionId(ctx)).checkpoint?.originId, { readerApi: ctx.model?.api });
16869
+ blocked = Boolean(plan.blockedReason);
16870
+ trimmable = plan.entries.filter((entry) => entry.type === "context_edit").length;
16871
+ } catch {
16872
+ blocked = true;
16873
+ }
16874
+ if (!blocked) {
16875
+ if (trimmable > 0)
16876
+ lines.push(`- ${CONTEXT_TOOL_NAME} trim: ${trimmable} archived tool output${trimmable === 1 ? "" : "s"} eligible now.${history}`);
16877
+ lines.push(`- Starting a large read-only detour? ${CONTEXT_TOOL_NAME} checkpoint first, then rewind(report) when done keeps the findings and drops the research.${trimmable > 0 ? "" : history}`);
16878
+ }
16879
+ }
16880
+ if (!lines.length)
16881
+ return null;
16882
+ const where = percent === null ? "" : ` ${Math.round(percent)}% of the policy window`;
16883
+ const head = band === "compaction" ? `Context attention:${where} (compaction band). ` + (settings.autoTrigger ? "Compaction prepares now and applies when the turn settles." : "Automatic compaction is off here; the user runs /smart-compact.") : `Context attention:${where} (cleanup band). ` + (settings.autoTrigger ? `Automatic compaction prepares at ${settings.minContextPercent}%.` : "Automatic compaction is off here.");
16884
+ return `${head}
16885
+ ${lines.join(`
16886
+ `)}`;
16887
+ }
16888
+
16810
16889
  // src/app/host-cache-ledger.ts
16811
16890
  var FOREIGN_REBUILD_WARN_COUNT = 3;
16812
16891
  var num = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
@@ -25023,6 +25102,11 @@ function smartCompactExtension(pi) {
25023
25102
  hostCache.noteCacheWarm(at);
25024
25103
  }
25025
25104
  });
25105
+ registerContextAttention(pi, {
25106
+ config: automaticConfig,
25107
+ canAgentAct: (ctx) => policy.snapshot().agentToolAccess !== "disabled" && !pivotQueued(ctx) && !isRunning.isSessionActive(resolveSessionId(ctx)),
25108
+ reachable: (group) => toolExposure.reachable(group)
25109
+ });
25026
25110
  registerSmartCompactCommand(pi, {
25027
25111
  pendingRef,
25028
25112
  runLock: isRunning,
package/dist/rtk.js CHANGED
@@ -198,7 +198,7 @@ class SecretScrubber {
198
198
  }
199
199
 
200
200
  // src/constants.ts
201
- var VERSION = "10.1.5";
201
+ var VERSION = "10.2.0";
202
202
  var OPTIONAL_COMPONENTS = {
203
203
  mnemopi: { name: "@oh-my-pi/pi-mnemopi", version: "18.3.1", enables: "Mnemopi memory store" },
204
204
  bun: { name: "bun", version: "1.4.2", enables: "Mnemopi worker runtime when no Bun 1.3.14+ is on PATH" },
@@ -454,7 +454,7 @@ Settings → **Agent tools & navigation**.
454
454
  | `contextPivotEnabled` | `Return to an anchor` | `true` | Returning to an anchor on a new branch with a required carryover. |
455
455
  | `contextAnchorCacheEnabled` | `Anchor prompt cache` | `true` | Anthropic models: keeps a prompt-cache marker on the newest anchor. |
456
456
  | `contextAnchorStatusEnabled` | `Anchor status` | `true` | Shows the newest anchor on this branch in Pi's footer; display only. |
457
- | `contextGuidanceEnabled` | `Navigation guide` | `true` | Lets you or the agent open the navigation guide on request; it is never added to a request otherwise. |
457
+ | `contextGuidanceEnabled` | `Navigation guide` | `true` | Lets you or the agent open the navigation guide on request; it is never added to a request otherwise. Also enables context attention: one short note to the model when context usage enters the cleanup band and again at the compaction band, naming only the context tools available right now (anchor; checkpoint/rewind and trim when history edits are possible). Re-armed once pressure clears. |
458
458
 
459
459
  Group permissions apply in both `lazy` and `eager` modes: `compaction` follows
460
460
  `agentToolAccess`, `memory` needs `contextGraphEnabled` or a non-local
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-smart-compact",
3
- "version": "10.1.5",
3
+ "version": "10.2.0",
4
4
  "description": "Pi Continuity — context hygiene and session continuity for Pi Coding Agent: recoverable cleanup, checkpoints, memory and verified compaction.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.4.2",