billion-context-pi-lean 0.1.69-lean.1 → 0.1.83-lean.10

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.
Files changed (59) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +129 -81
  3. package/dist/abort.d.ts +1 -0
  4. package/dist/branch-guard.d.ts +8 -0
  5. package/dist/cache-tool.d.ts +12 -0
  6. package/dist/chunk-HTPJ5GG7.js +18 -0
  7. package/dist/chunk-HTPJ5GG7.js.map +1 -0
  8. package/dist/commands.d.ts +8 -0
  9. package/dist/compat.d.ts +23 -0
  10. package/dist/compress-model.d.ts +65 -0
  11. package/dist/compress-tool.d.ts +76 -0
  12. package/dist/config-dir.d.ts +32 -0
  13. package/dist/config.d.ts +294 -0
  14. package/dist/contract.d.ts +51 -0
  15. package/dist/contract.js +9 -0
  16. package/dist/contract.js.map +1 -0
  17. package/dist/decompress-tool.d.ts +19 -0
  18. package/dist/degeneration.d.ts +98 -0
  19. package/dist/export.d.ts +20 -0
  20. package/dist/floor-stale.d.ts +31 -0
  21. package/dist/host.d.ts +15 -0
  22. package/dist/index.d.ts +10 -0
  23. package/dist/index.js +21267 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/live-only-tail.d.ts +10 -0
  26. package/dist/log.d.ts +18 -0
  27. package/dist/messages.d.ts +85 -0
  28. package/dist/omp.d.ts +19 -0
  29. package/dist/overflow-selfheal.d.ts +132 -0
  30. package/dist/prompt-pack.d.ts +53 -0
  31. package/dist/proxy-detect.d.ts +21 -0
  32. package/dist/reasoning-drop.d.ts +68 -0
  33. package/dist/rule-tool.d.ts +8 -0
  34. package/dist/runtime.d.ts +199 -0
  35. package/dist/search-index.d.ts +20 -0
  36. package/dist/search-tool.d.ts +10 -0
  37. package/dist/sequence-match.d.ts +7 -0
  38. package/dist/session-log.d.ts +23 -0
  39. package/dist/state-rebuild.d.ts +30 -0
  40. package/dist/state.d.ts +44 -0
  41. package/dist/status-tool.d.ts +13 -0
  42. package/dist/strip-images.d.ts +18 -0
  43. package/dist/summary-sanitize.d.ts +8 -0
  44. package/dist/surface.d.ts +17 -0
  45. package/dist/system-passthrough.d.ts +4 -0
  46. package/dist/system-prompt.d.ts +20 -0
  47. package/dist/tag-tokens.d.ts +3 -0
  48. package/dist/throttle-retry.d.ts +52 -0
  49. package/dist/tokens.d.ts +61 -0
  50. package/dist/tool-guardrails.d.ts +33 -0
  51. package/dist/tool-pair-sanitizer.d.ts +8 -0
  52. package/dist/turn-boundary.d.ts +53 -0
  53. package/dist/update.d.ts +43 -0
  54. package/dist/user-config.d.ts +61 -0
  55. package/lean/index.ts +376 -0
  56. package/package.json +84 -36
  57. package/schema/bcp-block-v1.json +42 -0
  58. package/README.zh-CN.md +0 -79
  59. package/index.ts +0 -266
package/LICENSE CHANGED
@@ -1,5 +1,6 @@
1
1
  MIT License
2
2
 
3
+ Copyright (c) 2026 ranxianglei
3
4
  Copyright (c) 2026 kunkun9527
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
package/README.md CHANGED
@@ -1,81 +1,129 @@
1
- # billion-context-pi-lean
2
-
3
- [简体中文](README.zh-CN.md)
4
-
5
- A lightweight Pi wrapper for [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi). It retains the upstream context compression engine while eliminating persistent system prompt bloat and redundant tool schemas.
6
-
7
- ## Core Features
8
-
9
- * Full compression engine: Keeps upstream context compression, hierarchical summaries, nudges, overflow recovery, retry handling, and tool protection.
10
- * Streamlined tool surface: Exposes the high-frequency `compress` tool directly, while consolidating `decompress`, `search_context`, and `acp_status` into an on-demand `acp_context` interface.
11
- * Focused and clean: Disables built-in delegation and auto-updates by design. For delegation workflows, pair with a dedicated subagent extension.
12
-
13
- Upstream dependency is pinned to `billion-context-pi@0.1.69`; delegation remains disabled in this lean wrapper.
14
-
15
- ## Context Savings
16
-
17
- The benchmark below compares the same delegation-disabled configuration against upstream `0.1.69`.
18
-
19
- ## Installation
20
-
21
- ```bash
22
- pi install git:github.com/kunkun9527/billion-context-pi-lean
23
- ```
24
-
25
- Or install from a local clone:
26
-
27
- ```bash
28
- git clone https://github.com/kunkun9527/billion-context-pi-lean.git
29
- cd billion-context-pi-lean
30
- npm install
31
- pi install ./
32
- ```
33
-
34
- Do not load this wrapper alongside another `billion-context-pi` extension to prevent registering duplicate tools or hooks.
35
-
36
- ## Usage
37
-
38
- The model interacts with two tools:
39
-
40
- ```text
41
- compress
42
- acp_context
43
- ```
44
-
45
- `acp_context` supports `decompress`, `search_context`, `acp_status`, and `help`.
46
-
47
- ```json
48
- {
49
- "op": "search_context",
50
- "args": {
51
- "query": "authentication"
52
- }
53
- }
54
- ```
55
-
56
- Use `help` only when you need to inspect the full upstream schema.
57
-
58
- ## Context Footprint Benchmark
59
-
60
- With only this extension enabled, its recurring initialization overhead in the model context is:
61
-
62
- | Item | Lean | Upstream `billion-context-pi@0.1.69` |
63
- | --- | ---: | ---: |
64
- | `compress` | 231 | 549 |
65
- | Context operations | `acp_context`: 90 | `decompress` + `search_context` + `acp_status`: 1,095 |
66
- | System prompt additions | 369 | 4,158 |
67
- | **Total** | **690** | **5,802** |
68
-
69
- This saves **5,112 tokens (88.1%)** compared to the pinned upstream package.
70
- The benchmark was measured on Pi 0.85.1 with `measure-plugin-tokens-v3.mjs` in a fresh isolated session, with delegation disabled for both packages, excluding built-in tools, skills, context files, and unrelated extensions. Token estimates use `ceil(characters / 4)`. Pure runtime UI elements and slash commands are excluded as they are not sent to the model.
71
-
72
- ## Development
73
-
74
- ```bash
75
- npm ci
76
- npm run check
77
- ```
78
-
79
- ## License
80
-
81
- MIT. This wrapper builds on the MIT-licensed [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi).
1
+ # billion-context-pi-lean
2
+
3
+ <!-- token-benchmark:summary:start -->
4
+ > **Token benchmark: Lean 486, upstream `billion-context-pi@0.1.83` 8,670 — 94.4% fewer.**
5
+ <!-- token-benchmark:summary:end -->
6
+ > [See my full setup for Pi](https://github.com/kunkun9527/my-lean-pi-setup)
7
+
8
+ [中文](https://github.com/kunkun9527/billion-context-pi-lean/blob/main/README_zh-CN.md)
9
+
10
+ Context management for the [Pi coding agent](https://github.com/earendil-works/pi-coding-agent) that lets long sessions keep going: as context fills up, the model folds finished work into summary blocks, and the originals stay recoverable on demand.
11
+
12
+ This is a fork of [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) **v0.1.83** that focuses on runtime stability and on spending fewer tokens. It keeps upstream's compression engine (ACP); the main additions are that **a cheap model can write the summaries** and that **context management itself costs fewer tokens**. Upstream has moved on to [`billion-context`](https://github.com/ranxianglei/billion-context); this fork stays on 0.1.83 and picks upstream fixes by hand. It is not affiliated with upstream.
13
+
14
+ ## Context footprint benchmark
15
+
16
+ With only this extension enabled, its recurring model-facing initialization contribution is:
17
+
18
+ | Variant | Tool and prompt contribution | Total |
19
+ | --- | --- | ---: |
20
+ | Lean `billion-context-pi-lean@0.1.83-lean.10` | `compress` (231) + `acp_context` (87) + prompt (168) | **486** |
21
+ | Upstream `billion-context-pi@0.1.83` with `promptPack: "lean"` | `compress` (389) + `decompress` (271) + `search_context` (90) + `acp_status` (227) + `acp_cache` (204) + `acp_delegate` (1,452) + `acp_delegate_wait` (342) + `acp_delegate_cancel` (83) + prompt (1,819) | **4,877** |
22
+ | Upstream `billion-context-pi@0.1.83` | `compress` (549) + `decompress` (546) + `search_context` (210) + `acp_status` (339) + `acp_cache` (204) + `acp_delegate` (1,452) + `acp_delegate_wait` (342) + `acp_delegate_cancel` (83) + prompt (4,945) | **8,670** |
23
+
24
+ This saves **8,184 tokens (94.4%)** against upstream's default, and 4,391 tokens (90.0%) against its `lean` prompt pack. The three `acp_delegate*` tools (1,877 tokens) belong to sub-agent delegation, which this fork removed; leaving them out, the savings are 92.8% and 83.8%.
25
+
26
+ Compression nudges come on top of this and only when context fills up. With a dedicated summary model, the rules in each nudge drop from about 1,460 tokens to about 220 ([setup](#use-a-cheap-model-for-summaries)).
27
+
28
+ Measured with `npm run measure` on Pi 1.3.0, each variant in a separate temporary process with an empty working directory and configuration. Built-in tools, skills, context files, session history, user messages, unrelated extensions, runtime UI, and slash commands are excluded; `before_agent_start` additions are included. Tokens are a fixed character-proxy estimate using `ceil(characters / 4)`, not provider tokenizer billing.
29
+
30
+ ## Main differences from upstream 0.1.83
31
+
32
+ | | Upstream 0.1.83 | This fork |
33
+ |---|---|---|
34
+ | Who writes summaries | The main model, usually the expensive one | A **dedicated summary model** of your choice, e.g. a cheap one; the main model only picks the ranges |
35
+ | Compression rules | Sent to the main model in every compression nudge (about 1,460 tokens; 720 with `promptPack: "lean"`) | Sent only to the summary model, so they no longer take up the main model's context; its nudge carries only a short range-picking note (about 220 tokens)\* |
36
+ | Summary quality | General compression rules; only length is checked | Also: user requests are kept apart from what tools showed; open items are split into user objectives, suggestions and unverified risks. A failed or malformed summary is not folded; the range waits for a later attempt |
37
+ | Tools and resident prompt | 5 context tools + 3 delegation tools, about 8,700 tokens (4,900 with `promptPack: "lean"`) | `compress` + `acp_context`, 486 tokens ([benchmark](#context-footprint-benchmark)). The resident prompt keeps only rules needed before a call; the rest arrives with nudges, errors and tool results |
38
+ | System prompt | Returns a whole system prompt, which on Pi 1.x drops prompt additions from extensions loaded after it | Added as its own section; other extensions keep theirs |
39
+ | Sub-agent delegation, auto-update | Built in | Removed. Use a dedicated sub-agent extension; ACP still manages their sessions |
40
+
41
+ \* Estimated as `ceil(chars / 4)` from the length of the rule text in the nudge, not billed tokens.
42
+
43
+ <details>
44
+ <summary>Smaller upstream issues fixed in this fork</summary>
45
+
46
+ - Emergency truncation was computed but never reached the request.
47
+ - Emergency truncation could cut the latest user message.
48
+ - The token meter reused an old count and ignored newly added messages.
49
+ - Reasoning inside `compress` calls was not counted.
50
+ - Concurrent state saves could fail (`ENOENT` on a shared temp file), and releasing the session lock could let a later caller jump the queue.
51
+ - After `/tree` into the middle of a block, its summary could show decisions from messages the current branch never reached.
52
+ - Decompressing to a file failed on Windows when the message ref contained `|`.
53
+ - The test suite wrote into the real `~/.pi`.
54
+
55
+ Details: [CHANGELOG.md](CHANGELOG.md), [MAINTENANCE.md](MAINTENANCE.md) (Chinese).
56
+
57
+ </details>
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pi install npm:billion-context-pi-lean
63
+ ```
64
+
65
+ Append `@<version>` to pin a version from [Releases](https://github.com/kunkun9527/billion-context-pi-lean/releases). Installing from git works too: `pi install git:github.com/kunkun9527/billion-context-pi-lean`.
66
+
67
+ - The dedicated summary model needs Pi 1.x or later.
68
+ - Do not enable it together with `billion-context-pi` or `billion-context`: tools and hooks would register twice.
69
+ - Pi's built-in compaction is turned off while this extension is active.
70
+
71
+ ## Use a cheap model for summaries
72
+
73
+ ```text
74
+ /acp compact # show the current setting
75
+ /acp compact <provider/model> # any model in Pi's model registry
76
+ /acp compact session # the session's own model, in a separate call
77
+ /acp compact reset # the main model writes summaries again
78
+ ```
79
+
80
+ The choice is saved as `compressionModelId` in `~/.pi/acp.json` (a project's `.pi/acp.json` takes precedence).
81
+
82
+ - Write `provider/id`. A bare id shared by several providers is rejected as ambiguous.
83
+ - Models come from Pi's live registry, so providers registered by other extensions work too.
84
+ - A model being listed does not mean its provider can serve it. If the call fails, that range stays uncompressed and is retried next time; `~/.pi/acp.log` records `compression-model-failed`. Only when the model cannot be resolved at all, or the host cannot make nested calls, does the main model's short summary get used, with a warning.
85
+
86
+ ## Configuration
87
+
88
+ Config files are `~/.pi/acp.json` and `<project>/.pi/acp.json`. Upstream options in [CONFIGURATION.md](CONFIGURATION.md) still apply, except:
89
+
90
+ - `autoUpdate` is always off.
91
+ - The removed delegation keys (`delegate.*`, `displayUsage`, `delegatePrompt`) are ignored.
92
+ - `promptSections.*` is ignored because the fork injects its own prompt; you get a one-time warning.
93
+
94
+ Compression timing can be tuned per provider and model:
95
+
96
+ ```json
97
+ {
98
+ "compressionModelId": "<provider>/<cheap-model>",
99
+ "compress": {
100
+ "promptPack": "lean",
101
+ "providers": {
102
+ "<provider>": {
103
+ "models": {
104
+ "<main-model>": { "maxContextLimit": 0.4, "nudgeGrowthTokens": 70000, "minPressureBenefitTokens": 30000 }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Tools the model sees
113
+
114
+ - `compress`: fold finished message ranges into summary blocks.
115
+ - `acp_context`: `decompress`, `search_context`, `acp_status`, `acp_cache` and `help`, e.g. `{ "op": "search_context", "args": { "query": "authentication" } }`.
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ npm ci
121
+ npm run check # typecheck, tests, build and facade checks
122
+ npm run measure # tool + prompt footprint vs upstream (needs ../my-lean-pi-setup)
123
+ ```
124
+
125
+ `dist/` is committed because Pi installs git packages without building them; commit it together with source changes. Maintenance notes: [MAINTENANCE.md](MAINTENANCE.md).
126
+
127
+ ## License
128
+
129
+ MIT. Based on [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) by ranxianglei and contributors (MIT), including the dedicated summary model from upstream PR #241.
@@ -0,0 +1 @@
1
+ export declare function assertNotAborted(signal: AbortSignal | undefined): void;
@@ -0,0 +1,8 @@
1
+ import type { CompressionCore, CompressionState, CoreMessage } from "acp-kernel";
2
+ export declare function findOffBranchBlocks(state: CompressionState, messages: readonly CoreMessage[], persistedIds: ReadonlySet<string>): string[];
3
+ /** Record the off-branch blocks for this state object (in-memory only). */
4
+ export declare function markOffBranchBlocks(state: CompressionState, blockIds: readonly string[]): void;
5
+ export declare function offBranchBlocksOf(state: CompressionState): ReadonlySet<string> | undefined;
6
+ /** Wrap a kernel core so processTurn honours the marks above, and marks carry
7
+ * over to the states processTurn / applyCompression return. */
8
+ export declare function withBranchGuard(core: CompressionCore): CompressionCore;
@@ -0,0 +1,12 @@
1
+ import { Type } from "typebox";
2
+ import type { ExtensionContext, SessionEntry, ToolDefinition } from "@earendil-works/pi-coding-agent";
3
+ import type { AcpRuntime } from "./runtime.js";
4
+ import { type ToolPromptOverrides } from "./surface.js";
5
+ import { type CacheSample } from "acp-kernel";
6
+ declare const CacheParams: Type.TObject<{
7
+ detail: Type.TOptional<Type.TUnion<[Type.TLiteral<"summary">, Type.TLiteral<"full">]>>;
8
+ }>;
9
+ export declare function cacheSamples(entries: SessionEntry[]): CacheSample[];
10
+ export declare function cacheReportText(runtime: AcpRuntime, ctx: ExtensionContext, detail?: "summary" | "full"): Promise<string>;
11
+ export declare function makeCacheTool(runtime: AcpRuntime, overrides?: ToolPromptOverrides): ToolDefinition<typeof CacheParams>;
12
+ export {};
@@ -0,0 +1,18 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/contract.ts
8
+ var SIDECAR_SCHEMA_VERSION = 1;
9
+ function sidecarProducer() {
10
+ return `billion-context-pi@${true ? "0.1.83-lean.10" : "dev"}`;
11
+ }
12
+
13
+ export {
14
+ __export,
15
+ SIDECAR_SCHEMA_VERSION,
16
+ sidecarProducer
17
+ };
18
+ //# sourceMappingURL=chunk-HTPJ5GG7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contract.ts"],"sourcesContent":["/**\n * Machine-readable contract for the `<sessionFile>.acp.json` sidecar (#368).\n *\n * Downstream tools (e.g. pi-billion-memory) glob the sidecar directly instead\n * of running inside pi. To keep that stable across releases:\n *\n * - `schemaVersion` is written on every save. A MISSING version means v1\n * (pre-contract files). An unknown NEWER version means \"fields may have\n * changed\" — skip it, log once, never rewrite the file.\n * - `producer` identifies the writer (`billion-context-pi@x.y.z`).\n * - The file is replaced atomically (tmp + rename), so a reader either sees\n * the previous or the next complete file, never a torn write.\n * - Blocks are stored verbatim as the kernel's CompressionBlock; unknown\n * extra fields are additive and must be ignored by readers.\n */\nexport const SIDECAR_SCHEMA_VERSION = 1;\n\n/**\n * One compressed block, as persisted in the sidecar's `blocks[]`. Mirrors the\n * kernel's CompressionBlock (acp-kernel 0.0.81) — inlined rather than aliased\n * so this subpath carries no type dependency on acp-kernel (dev-only, bundled\n * at build time). Fields the kernel adds later are additive; tests assert the\n * kernel shape still covers this one.\n */\nexport interface BcpBlockV1 {\n blockId: string;\n runId: string;\n tier: 1 | 2 | 3;\n topic?: string;\n summary: string;\n directMessageIds: string[];\n effectiveMessageIds: string[];\n directBlockIds: string[];\n compressedTokens: number;\n createdAt: number;\n survivedCount: number;\n generation: \"young\" | \"old\";\n active: boolean;\n expanded?: boolean;\n durationMs?: number;\n compressCallId?: string;\n startRef?: string;\n endRef?: string;\n}\n\n/** Top-level shape of the sidecar (state fields beyond the contract allowed). */\nexport interface BcpSidecarV1 {\n schemaVersion: number;\n producer: string;\n blocks: BcpBlockV1[];\n [extra: string]: unknown;\n}\n\nexport function sidecarProducer(): string {\n return `billion-context-pi@${typeof CURRENT_VERSION !== \"undefined\" ? CURRENT_VERSION : \"dev\"}`;\n}\n\ndeclare const CURRENT_VERSION: string;\n"],"mappings":";;;;;;;AAeO,IAAM,yBAAyB;AAsC/B,SAAS,kBAA0B;AACxC,SAAO,sBAAsB,OAAyC,mBAAkB,KAAK;AAC/F;","names":[]}
@@ -0,0 +1,8 @@
1
+ import type { ExtensionAPI, RegisteredCommand } from "@earendil-works/pi-coding-agent";
2
+ import type { AcpRuntime } from "./runtime.js";
3
+ type CommandOptions = Omit<RegisteredCommand, "name" | "sourceInfo">;
4
+ export declare function makeCommands(runtime: AcpRuntime, pi?: ExtensionAPI): Array<{
5
+ name: string;
6
+ options: CommandOptions;
7
+ }>;
8
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ /**
3
+ * Host compatibility layer for pi vs omp (oh-my-pi) API differences.
4
+ *
5
+ * pi: systemPrompt is string, getSystemPrompt() returns string
6
+ * omp: systemPrompt is string[], getSystemPrompt() returns string[]
7
+ *
8
+ * These helpers normalize the differences so the rest of the codebase
9
+ * can work with a consistent string interface.
10
+ */
11
+ /** Normalize systemPrompt to a single string (join with newlines if array). */
12
+ export declare function normalizeSystemPrompt(input: string | string[] | undefined): string;
13
+ /**
14
+ * Format systemPrompt for before_agent_start event handler.
15
+ * Always returns string to satisfy pi's type definition, but handles
16
+ * both string (pi) and string[] (omp) input types at runtime.
17
+ */
18
+ export declare function formatSystemPromptForEvent(base: string | string[], append: string): string;
19
+ /**
20
+ * Get the system prompt as a single string, regardless of host type.
21
+ * Handles both pi (string) and omp (string[]) return types.
22
+ */
23
+ export declare function getSystemPromptText(ctx: ExtensionContext): string;
@@ -0,0 +1,65 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { Api, Context, Model } from "@earendil-works/pi-ai";
3
+ /** Sentinel compressionModelId value meaning "use the session's own model".
4
+ * The compression call reuses the session's prompt prefix (system prompt +
5
+ * tools + messages) so it can hit the provider's prompt cache. Distinct from a
6
+ * model ref ("provider/id" or a bare id). */
7
+ export declare const SESSION_MODEL_REF = "session";
8
+ /** A model known to Pi's model registry, addressed by provider + id. */
9
+ export interface CompressionModelInfo {
10
+ provider: string;
11
+ id: string;
12
+ name?: string;
13
+ }
14
+ /** `model` is set when the ref is unambiguous; `ambiguous` lists candidates
15
+ * when a bare id matches several providers. */
16
+ export interface ResolveResult {
17
+ model: Model<Api> | null;
18
+ ambiguous: CompressionModelInfo[];
19
+ }
20
+ type ModelRegistryLike = ExtensionContext["modelRegistry"];
21
+ /** Every chat model Pi knows, including providers registered by extensions
22
+ * (the session's live registry — not a private models.json-only runtime). */
23
+ export declare function listModels(registry: ModelRegistryLike): CompressionModelInfo[];
24
+ /** Resolve "provider/id" (explicit, split at the first slash) or a bare id. */
25
+ export declare function resolveModel(registry: ModelRegistryLike, ref: string): ResolveResult;
26
+ /** Run one summary call through the session's model registry (request-time
27
+ * auth, extension-registered providers). Throws on host without nested
28
+ * completion support, API error, abort, or empty output — the caller then
29
+ * keeps the main model's fallback summary. */
30
+ export declare function summarizeWithModel(ctx: ExtensionContext, model: Model<Api>, context: Context, maxTokens: number, signal?: AbortSignal): Promise<string>;
31
+ /** The leaked text (trimmed) when the opening of `summary` is planning, else null. */
32
+ export declare function detectPlanningLeak(summary: string): string | null;
33
+ /** The echoed text when `summary` contains copied transcript entries, else null. */
34
+ export declare function detectTranscriptEcho(summary: string): string | null;
35
+ export type SummaryDefect = "planning" | "transcript-echo";
36
+ /** summarizeWithModel, plus one retry when the output opens with planning
37
+ * text or copies transcript entries. A second defective output throws, so
38
+ * the range is not folded (the caller retries later) — never stored, never
39
+ * silently stripped. */
40
+ export declare function summarizeChecked(ctx: ExtensionContext, model: Model<Api>, context: Context, maxTokens: number, signal?: AbortSignal, onDefect?: (text: string, kind: SummaryDefect) => void): Promise<string>;
41
+ /** System prompt for a dedicated model: reuse the kernel's tier-1 rules so
42
+ * its output matches what the main model would produce. */
43
+ export declare function buildSummarizeSystemPrompt(prompts: {
44
+ compressPhilosophy: string;
45
+ howToCompressRules: string;
46
+ }, topic: string | undefined): string;
47
+ /** Truncate very large content, keeping head (goal/early context) and tail
48
+ * (latest state) around a marker. */
49
+ export declare function truncateContent(content: string, maxChars: number): string;
50
+ /** A chunk of range content; pinned chunks (earlier block summaries) are never truncated. */
51
+ export interface ContentPart {
52
+ text: string;
53
+ pinned?: boolean;
54
+ }
55
+ /** Fit extracted range content into the summary model's context window
56
+ * instead of a fixed 120K-char cap; truncate only what cannot fit.
57
+ * When truncation is needed, pinned parts (earlier block summaries) are
58
+ * kept whole and listed first, and only the raw messages are cut — the
59
+ * summaries carry the decisions the new summary must inherit. Pinned parts
60
+ * that cannot fit whole throw: the caller then keeps its own summary, written
61
+ * by the main model that saw them in full, instead of one that lost facts. */
62
+ export declare function fitToModel(parts: ContentPart[], model: {
63
+ contextWindow?: number;
64
+ }, maxTokens: number): string;
65
+ export {};
@@ -0,0 +1,76 @@
1
+ import { Type, type Static } from "typebox";
2
+ import { type ExtensionAPI, type ToolDefinition } from "@earendil-works/pi-coding-agent";
3
+ import type { SessionMessageEntry } from "@earendil-works/pi-coding-agent";
4
+ import type { AcpRuntime } from "./runtime.js";
5
+ import { type ToolPromptOverrides } from "./surface.js";
6
+ import { type CompressionBlock, type CompressionState } from "acp-kernel";
7
+ declare const RangeSpec: Type.TObject<{
8
+ startId: Type.TString;
9
+ endId: Type.TString;
10
+ summary: Type.TString;
11
+ topic: Type.TOptional<Type.TString>;
12
+ }>;
13
+ declare const CompressParams: Type.TObject<{
14
+ topic: Type.TOptional<Type.TString>;
15
+ content: Type.TUnion<[Type.TArray<Type.TObject<{
16
+ startId: Type.TString;
17
+ endId: Type.TString;
18
+ summary: Type.TString;
19
+ topic: Type.TOptional<Type.TString>;
20
+ }>>, Type.TString]>;
21
+ summaryMaxChars: Type.TOptional<Type.TNumber>;
22
+ }>;
23
+ type CompressArgs = Static<typeof CompressParams>;
24
+ export declare function makeCompressTool(runtime: AcpRuntime, overrides?: ToolPromptOverrides, pi?: ExtensionAPI): ToolDefinition<typeof CompressParams>;
25
+ type RangeEntry = Static<typeof RangeSpec>;
26
+ /** Persisted in the compress toolResult's `details` (never sent to the model).
27
+ * When a dedicated model writes summaries, the call's arguments hold only the
28
+ * caller's short fallback, and failed ranges are not folded at all — so the
29
+ * log alone cannot rebuild the blocks. `acpFolded` records exactly what was
30
+ * folded; state-rebuild prefers it over the arguments. */
31
+ export interface CompressResultDetails {
32
+ acpFolded?: {
33
+ version: 1;
34
+ ranges: Array<{
35
+ startId: string;
36
+ endId: string;
37
+ summary: string;
38
+ topic?: string;
39
+ }>;
40
+ };
41
+ }
42
+ export declare function normalizeRanges(args: CompressArgs): RangeEntry[] | string;
43
+ export declare function tailRepair(s: string): string | undefined;
44
+ export declare function arrayWrapRepair(s: string): string | undefined;
45
+ /** Panel block count, or -1 for non-panels. Accepts BOTH the legacy
46
+ * "… B blocks)" form (0-block runs; historical transcripts replayed by
47
+ * index/floor-stale) and the #376 "… blocks: b3=m00044–m00097*, …" form. */
48
+ export declare function compressPanelBlocks(text: string): number;
49
+ /** Success = completed run that created >= 1 block (partial range errors
50
+ * still count: progress was made). A 0-block panel must NOT be success —
51
+ * it would reset the retry counter while the emergency nudge re-fires,
52
+ * looping no-op compressions (issue #6). */
53
+ export declare function isCompressSuccessText(text: string): boolean;
54
+ /** No-op = completed run that compressed nothing (0-block panel: every
55
+ * range skipped). Counted as a FAILED attempt by noteCompressOutcomes so
56
+ * the retry cap applies. Non-panels ("No ranges provided.") stay neutral. */
57
+ export declare function isCompressNoopText(text: string): boolean;
58
+ export declare function blockSpanLabel(block: CompressionBlock, state: CompressionState): string;
59
+ export declare function summaryFingerprintLine(blockId: string, summary: string): string;
60
+ export declare function tierReadyHint(state: CompressionState, config: ReturnType<AcpRuntime["configFor"]>): string;
61
+ type AgentMessage = SessionMessageEntry["message"];
62
+ /**
63
+ * Lean: put each active block's full current summary back into the outbound
64
+ * compress-call arguments. The kernel stubs those summaries to 200 chars
65
+ * because it renders the full text as separate `acp_summary_` messages, but
66
+ * coreOutToAgentMessages drops those (pi has no slot for them), so without this
67
+ * the main model only ever sees its own stubbed fallback — never the summary
68
+ * the dedicated model wrote — and decompresses blocks just to read them.
69
+ * Arguments are parsed with normalizeRanges, so every shape the tool accepts
70
+ * (bare objects, startRef/endRef, single-range, string content) is covered.
71
+ * Ranges match blocks by start/end ref; a single-range call whose callId owns a
72
+ * single active block (legacy blocks without refs) maps directly. Ranges with
73
+ * no active block are dropped, as the kernel does for dead ranges.
74
+ */
75
+ export declare function inlineBlockSummaries(messages: AgentMessage[], blocks: readonly CompressionBlock[]): AgentMessage[];
76
+ export {};
@@ -0,0 +1,32 @@
1
+ export type HostConfigSurface = {
2
+ CONFIG_DIR_NAME?: unknown;
3
+ getAgentDir?: unknown;
4
+ };
5
+ /** Pi's name — the fallback, and what releases before fork-host resolution used on every host. */
6
+ export declare const PI_CONFIG_DIR_NAME = ".pi";
7
+ /**
8
+ * Config directory name, resolved from the host package the extension runs under.
9
+ *
10
+ * Every path in this adapter is `<home|cwd>/<name>/…`, with the agent dir at
11
+ * `~/<name>/agent` — Pi's layout, where CONFIG_DIR_NAME is ".pi". Resolution order:
12
+ * 1. the host's CONFIG_DIR_NAME, if it is a single directory name (Pi);
13
+ * 2. the parent of the host's getAgentDir(), if that is `~/<name>/agent` — Pi forks
14
+ * that do not re-export CONFIG_DIR_NAME, or export it with other semantics (Prime:
15
+ * CONFIG_DIR_NAME ".prime/agent", getAgentDir() ~/.prime/agent → ".prime");
16
+ * 3. ".pi".
17
+ * The namespace import is deliberate: a missing named export fails at link time
18
+ * under plain Node ESM→CJS interop and as `undefined` under loader aliasing (#364).
19
+ * It is the ONLY value import from the pi package. See docs/host-adapter.md §4.
20
+ */
21
+ export declare function resolveConfigDirName(host: HostConfigSurface, home: string): string;
22
+ export declare const CONFIG_DIR_NAME: string;
23
+ /**
24
+ * `<root>/<name>/<segments>` for a user-authored file (acp.json, prompt packs). When
25
+ * `name` is not ".pi", an existing `<root>/.pi/<segments>` is still used while the
26
+ * host's own path does not exist: earlier releases read ".pi" on every host, and Prime
27
+ * users were told to put acp.json there (#467).
28
+ */
29
+ export declare function userConfigPathIn(name: string, root: string, ...segments: string[]): string;
30
+ export declare function userConfigPath(root: string, ...segments: string[]): string;
31
+ /** Global then project acp.json; later entries override earlier ones. */
32
+ export declare function acpJsonFiles(cwd: string): string[];