billion-context-pi-lean 0.1.57-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.
- package/LICENSE +1 -0
- package/README.md +129 -80
- package/dist/abort.d.ts +1 -0
- package/dist/branch-guard.d.ts +8 -0
- package/dist/cache-tool.d.ts +12 -0
- package/dist/chunk-HTPJ5GG7.js +18 -0
- package/dist/chunk-HTPJ5GG7.js.map +1 -0
- package/dist/commands.d.ts +8 -0
- package/dist/compat.d.ts +23 -0
- package/dist/compress-model.d.ts +65 -0
- package/dist/compress-tool.d.ts +76 -0
- package/dist/config-dir.d.ts +32 -0
- package/dist/config.d.ts +294 -0
- package/dist/contract.d.ts +51 -0
- package/dist/contract.js +9 -0
- package/dist/contract.js.map +1 -0
- package/dist/decompress-tool.d.ts +19 -0
- package/dist/degeneration.d.ts +98 -0
- package/dist/export.d.ts +20 -0
- package/dist/floor-stale.d.ts +31 -0
- package/dist/host.d.ts +15 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +21267 -0
- package/dist/index.js.map +1 -0
- package/dist/live-only-tail.d.ts +10 -0
- package/dist/log.d.ts +18 -0
- package/dist/messages.d.ts +85 -0
- package/dist/omp.d.ts +19 -0
- package/dist/overflow-selfheal.d.ts +132 -0
- package/dist/prompt-pack.d.ts +53 -0
- package/dist/proxy-detect.d.ts +21 -0
- package/dist/reasoning-drop.d.ts +68 -0
- package/dist/rule-tool.d.ts +8 -0
- package/dist/runtime.d.ts +199 -0
- package/dist/search-index.d.ts +20 -0
- package/dist/search-tool.d.ts +10 -0
- package/dist/sequence-match.d.ts +7 -0
- package/dist/session-log.d.ts +23 -0
- package/dist/state-rebuild.d.ts +30 -0
- package/dist/state.d.ts +44 -0
- package/dist/status-tool.d.ts +13 -0
- package/dist/strip-images.d.ts +18 -0
- package/dist/summary-sanitize.d.ts +8 -0
- package/dist/surface.d.ts +17 -0
- package/dist/system-passthrough.d.ts +4 -0
- package/dist/system-prompt.d.ts +20 -0
- package/dist/tag-tokens.d.ts +3 -0
- package/dist/throttle-retry.d.ts +52 -0
- package/dist/tokens.d.ts +61 -0
- package/dist/tool-guardrails.d.ts +33 -0
- package/dist/tool-pair-sanitizer.d.ts +8 -0
- package/dist/turn-boundary.d.ts +53 -0
- package/dist/update.d.ts +43 -0
- package/dist/user-config.d.ts +61 -0
- package/lean/index.ts +376 -0
- package/package.json +84 -30
- package/schema/bcp-block-v1.json +42 -0
- package/README.zh-CN.md +0 -78
- package/index.ts +0 -266
- package/tests/facade.test.mjs +0 -307
- package/tsconfig.json +0 -14
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,80 +1,129 @@
|
|
|
1
|
-
# billion-context-pi-lean
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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.
|
package/dist/abort.d.ts
ADDED
|
@@ -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 {};
|
package/dist/compat.d.ts
ADDED
|
@@ -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[];
|