billion-context-pi 0.1.53 → 0.1.54-pr.236.56
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/README.md +18 -0
- package/README.zh-CN.md +18 -0
- package/dist/compress-tool.d.ts +11 -1
- package/dist/config.d.ts +6 -0
- package/dist/delegate-tool.d.ts +29 -4
- package/dist/index.js +487 -106
- package/dist/index.js.map +1 -1
- package/dist/omp.d.ts +19 -0
- package/dist/runtime.d.ts +14 -0
- package/dist/user-config.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ The model decides <em>when</em> and <em>what</em> to compress — not a hard lim
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
> **Host support:** this plugin is for **Pi**. It does **not** support **OMP (oh-my-pi)** — on an OMP host it refuses to run. OMP users: use [billion-context](https://github.com/ranxianglei/billion-context) instead (`bili omp`). Details: [docs/omp.md](./docs/omp.md).
|
|
26
|
+
|
|
25
27
|
## Why?
|
|
26
28
|
|
|
27
29
|
When conversations get long, the model runs out of context. Most tools hard-truncate — silently dropping earlier messages. **billion-context** gives the model a `compress` tool: the LLM decides **when** and **what** to compress into high-fidelity summaries, preserving critical details (file paths, decisions, error strings) while reclaiming context space.
|
|
@@ -72,6 +74,22 @@ This has two practical implications:
|
|
|
72
74
|
|
|
73
75
|
2. **Even with a single compression plugin, interference is still possible in rare cases.** Load order under Pi is determined by filesystem discovery order (`fs.readdirSync` over `.pi/extensions/` → global → packages), which is not fully deterministic. If another (non-compression) extension also hooks the `context` event and happens to load *after* billion-context-pi, it could modify the compressed output. billion-context-pi rebuilds its working set from the session log rather than the chained input, which makes it robust to handlers that run *before* it — but it cannot defend against a handler that runs *after* it. This is a limitation of Pi's extension model; if you observe unexpected context behavior, check whether other installed extensions intercept the `context` event.
|
|
74
76
|
|
|
77
|
+
## Host support
|
|
78
|
+
|
|
79
|
+
billion-context-pi is built for the **Pi** coding agent (`@earendil-works/pi-coding-agent`) and detects the host at session start:
|
|
80
|
+
|
|
81
|
+
- **Pi** — fully supported.
|
|
82
|
+
- **OMP (`can1357/oh-my-pi`)** — **not supported.** OMP's in-process session API diverges from Pi's, so the compression refs the extension injects can drift out of sync with the session's real refs and `compress` calls fail with `does not exist in this session` (issue [#234](https://github.com/ranxianglei/billion-context-pi/issues/234)). On OMP the extension now **refuses service**: it prints a warning, disables the ACP tools, and leaves the host's own context handling untouched.
|
|
83
|
+
|
|
84
|
+
**Use [billion-context](https://github.com/ranxianglei/billion-context) instead** — it runs the same compression pipeline server-side in a proxy, so the refs never diverge:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm install -g billion-context
|
|
88
|
+
bili omp # run OMP through the proxy
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Full details: [docs/omp.md](./docs/omp.md).
|
|
92
|
+
|
|
75
93
|
## Model-facing tools
|
|
76
94
|
|
|
77
95
|
| Tool | What it does |
|
package/README.zh-CN.md
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
> **宿主支持:** 本插件面向 **Pi**。它**不支持 OMP(oh-my-pi)** —— 在 OMP 宿主上会拒绝运行。OMP 用户请改用 [billion-context](https://github.com/ranxianglei/billion-context)(`bili omp`)。详细说明:[docs/omp.zh-CN.md](./docs/omp.zh-CN.md)。
|
|
24
|
+
|
|
23
25
|
## 为什么选择 billion-context
|
|
24
26
|
|
|
25
27
|
当对话变长,模型的上下文会耗尽。多数工具采用硬截断 —— 静默丢弃早期消息。**billion-context** 把 `compress` 工具交给模型:由 LLM 决定**何时**压缩、压缩**什么**,将内容压缩成高保真摘要,在回收上下文空间的同时保留关键细节(文件路径、决策、错误字符串)。
|
|
@@ -71,6 +73,22 @@ billion-context 通过拦截 Pi 的 `context` 事件接管上下文管理。**Pi
|
|
|
71
73
|
|
|
72
74
|
2. **即使只有一个压缩插件,在少数情况下仍可能出现干扰。** Pi 下的加载顺序由文件系统发现顺序(`fs.readdirSync` 遍历 `.pi/extensions/` → 全局 → 包)决定,并不完全确定。如果另一个(非压缩类)扩展也 hook 了 `context` 事件、且恰好加载在 billion-context-pi *之后*,它可能修改压缩后的输出。billion-context-pi 从会话日志重建工作集(而非链式输入),这让它对*排在它之前*的 handler 鲁棒 —— 但无法防御*排在它之后*的 handler。这是 Pi 扩展模型的固有限制;若你观察到上下文行为异常,请检查是否有其他已安装扩展拦截了 `context` 事件。
|
|
73
75
|
|
|
76
|
+
## 宿主支持
|
|
77
|
+
|
|
78
|
+
billion-context-pi 面向 **Pi** 编码代理(`@earendil-works/pi-coding-agent`)构建,并在会话开始时检测宿主:
|
|
79
|
+
|
|
80
|
+
- **Pi** — 完全支持。
|
|
81
|
+
- **OMP(`can1357/oh-my-pi`)** — **不支持。** OMP 的进程内会话 API 与 Pi 不同,扩展注入的压缩引用可能与会话的真实引用漂移失步,导致 `compress` 调用失败,报错 `does not exist in this session`(issue [#234](https://github.com/ranxianglei/billion-context-pi/issues/234))。在 OMP 上,扩展现在会**拒绝服务**:打印警告、禁用 ACP 工具,并保持宿主自身的上下文处理不受影响。
|
|
82
|
+
|
|
83
|
+
**请改用 [billion-context](https://github.com/ranxianglei/billion-context)** — 它把同样的压缩流水线运行在服务端代理里,因此引用不会漂移:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install -g billion-context
|
|
87
|
+
bili omp # 让 OMP 通过代理运行
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
完整说明:[docs/omp.zh-CN.md](./docs/omp.zh-CN.md)。
|
|
91
|
+
|
|
74
92
|
## 模型工具
|
|
75
93
|
|
|
76
94
|
| 工具 | 作用 |
|
package/dist/compress-tool.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
1
|
+
import { Type, type Static } from "typebox";
|
|
2
2
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import type { AcpRuntime } from "./runtime.js";
|
|
4
|
+
declare const RangeSpec: Type.TObject<{
|
|
5
|
+
startId: Type.TString;
|
|
6
|
+
endId: Type.TString;
|
|
7
|
+
summary: Type.TString;
|
|
8
|
+
topic: Type.TOptional<Type.TString>;
|
|
9
|
+
}>;
|
|
4
10
|
declare const CompressParams: Type.TObject<{
|
|
5
11
|
topic: Type.TOptional<Type.TString>;
|
|
6
12
|
content: Type.TUnion<[Type.TArray<Type.TObject<{
|
|
@@ -11,7 +17,11 @@ declare const CompressParams: Type.TObject<{
|
|
|
11
17
|
}>>, Type.TString]>;
|
|
12
18
|
summaryMaxChars: Type.TOptional<Type.TNumber>;
|
|
13
19
|
}>;
|
|
20
|
+
type CompressArgs = Static<typeof CompressParams>;
|
|
14
21
|
export declare function makeCompressTool(runtime: AcpRuntime): ToolDefinition<typeof CompressParams>;
|
|
22
|
+
type RangeEntry = Static<typeof RangeSpec>;
|
|
23
|
+
export declare function normalizeRanges(args: CompressArgs): RangeEntry[] | string;
|
|
24
|
+
export declare function tailRepair(s: string): string | undefined;
|
|
15
25
|
/** Success = completed run that created >= 1 block (partial range errors
|
|
16
26
|
* still count: progress was made). A 0-block panel must NOT be success —
|
|
17
27
|
* it would reset the retry counter while the emergency nudge re-fires,
|
package/dist/config.d.ts
CHANGED
|
@@ -53,6 +53,12 @@ export interface CompressConfig extends CompressSettings {
|
|
|
53
53
|
* (live model context window, protected tools, state persistence).
|
|
54
54
|
*/
|
|
55
55
|
export interface AdapterConfig {
|
|
56
|
+
/** Master switch. Default: true. Set `enabled: false` in acp.json (or
|
|
57
|
+
* programmatically) to turn the whole adapter off — no tools, no system
|
|
58
|
+
* prompt, no context transform — for models too small to handle ACP, where
|
|
59
|
+
* Pi's native context management should run instead. Checked at extension
|
|
60
|
+
* load; requires a Pi restart to take effect. */
|
|
61
|
+
enabled?: boolean;
|
|
56
62
|
/** When omitted, the adapter reads `ctx.model.contextWindow` live each turn.
|
|
57
63
|
* Set explicitly for tests/headless runs. */
|
|
58
64
|
modelContextLimit?: number;
|
package/dist/delegate-tool.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
|
2
2
|
import { Type, type Static } from "typebox";
|
|
3
3
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { type Usage } from "./delegate-events.js";
|
|
5
|
+
export declare function delegateStdinText(resumeFrom: boolean, task: string | undefined): string;
|
|
5
6
|
export declare function delegateSpawnOptions(cwd: string, env: NodeJS.ProcessEnv): SpawnOptions;
|
|
6
7
|
/** Resolve the pi CLI entry for delegate child processes.
|
|
7
8
|
* argv[1] is only the pi CLI under a CLI host; embedded hosts (e.g. pi-web)
|
|
8
9
|
* run the SDK inside another node process, so probe instead. Non-pi hosts
|
|
9
10
|
* (omp) keep argv[1] untouched. */
|
|
10
11
|
export declare function resolvePiCliEntry(argv1: string, env?: NodeJS.ProcessEnv, piHost?: boolean): string;
|
|
11
|
-
type RunStatus = "running" | "completed" | "failed" | "cancelled";
|
|
12
|
+
export type RunStatus = "running" | "completed" | "failed" | "cancelled";
|
|
12
13
|
interface DelegateRun {
|
|
13
14
|
runId: string;
|
|
14
15
|
agent: string;
|
|
@@ -18,12 +19,18 @@ interface DelegateRun {
|
|
|
18
19
|
finishedAt?: number;
|
|
19
20
|
status: RunStatus;
|
|
20
21
|
exitCode?: number | null;
|
|
22
|
+
/** Exit signal when the child died by signal (exit code null), e.g. "SIGTERM". */
|
|
23
|
+
exitSignal?: NodeJS.Signals;
|
|
21
24
|
child?: ChildProcess;
|
|
22
25
|
result?: {
|
|
23
26
|
code: number | null;
|
|
24
27
|
file: string;
|
|
25
28
|
body: string;
|
|
26
29
|
};
|
|
30
|
+
/** Live activity log path (async json-stream runs only). */
|
|
31
|
+
activityFile?: string;
|
|
32
|
+
/** runId of the run this run resumed from (resumeFrom). */
|
|
33
|
+
resumedFrom?: string;
|
|
27
34
|
consumed?: boolean;
|
|
28
35
|
/** True once the close handler injected the result as a system
|
|
29
36
|
* notification (sendUserMessage succeeded). Lets a later wait() avoid
|
|
@@ -88,7 +95,8 @@ export declare function makeEventApplier(opts: {
|
|
|
88
95
|
export declare function resolveWaitTimeoutMs(raw: number | undefined): number;
|
|
89
96
|
declare const DelegateParams: Type.TObject<{
|
|
90
97
|
agent: Type.TString;
|
|
91
|
-
task: Type.TString
|
|
98
|
+
task: Type.TOptional<Type.TString>;
|
|
99
|
+
resumeFrom: Type.TOptional<Type.TString>;
|
|
92
100
|
cwd: Type.TOptional<Type.TString>;
|
|
93
101
|
model: Type.TOptional<Type.TString>;
|
|
94
102
|
async: Type.TOptional<Type.TBoolean>;
|
|
@@ -104,6 +112,13 @@ declare const WaitParams: Type.TObject<{
|
|
|
104
112
|
}>;
|
|
105
113
|
export declare function accumulateUsage(a: Usage | undefined, b: Usage): Usage;
|
|
106
114
|
export declare function makeDelegateTool(pi: ExtensionAPI): ToolDefinition<typeof DelegateParams>;
|
|
115
|
+
export declare function formatRunResult(run: DelegateRun): string;
|
|
116
|
+
/** "exit 0" / "exit 1" / "exit SIGTERM" (signal shown when the child was
|
|
117
|
+
* killed and has no exit code) / "exit ?" (unknown). */
|
|
118
|
+
export declare function exitLabel(code: number | null, signal?: NodeJS.Signals | null): string;
|
|
119
|
+
/** Shared note for cancelled runs: their files are retained, so point the
|
|
120
|
+
* model at the partial output and offer a resume. */
|
|
121
|
+
export declare function cancelledFileNote(runId: string, file: string): string;
|
|
107
122
|
/** Runs that reached a terminal state but whose result never reached the
|
|
108
123
|
* model: no parked waiter, not consumed by a tool result, and never injected
|
|
109
124
|
* as a notification. The model was promised a notification ("do NOT keep
|
|
@@ -157,11 +172,21 @@ export declare function buildCancelResult(run: DelegateRun, content: string, mod
|
|
|
157
172
|
};
|
|
158
173
|
export declare function makeDelegateWaitTool(_pi: ExtensionAPI): ToolDefinition<typeof WaitParams>;
|
|
159
174
|
export declare function makeDelegateCancelTool(_pi: ExtensionAPI): ToolDefinition<typeof CancelParams>;
|
|
160
|
-
export declare function buildChildArgs(args: DelegateArgs, rolePrompt: string, ctx: ExtensionContext): Promise<{
|
|
175
|
+
export declare function buildChildArgs(args: DelegateArgs, rolePrompt: string, ctx: ExtensionContext, runId: string): Promise<{
|
|
161
176
|
cliArgs: string[];
|
|
162
177
|
tmpDir: string;
|
|
163
178
|
isAsync: boolean;
|
|
164
179
|
useJsonStream: boolean;
|
|
180
|
+
sessionFile: string | null;
|
|
165
181
|
}>;
|
|
166
|
-
|
|
182
|
+
/** Watchdog/EOF finalize arrives with code === null (the child was killed or
|
|
183
|
+
* never exited). If a result was delivered (non-empty reply or stderr), the
|
|
184
|
+
* run counts as completed (0); otherwise it stays null = genuine failure. */
|
|
185
|
+
export declare function effectiveExitCode(code: number | null, output: string, stderr: string): number | null;
|
|
186
|
+
/** status (set by finalize from the effective exit code) is the authority for
|
|
187
|
+
* the FAILED/completed decision; the raw code is diagnostic display only
|
|
188
|
+
* ("exit ?"), so the notification can never disagree with run.status. */
|
|
189
|
+
export declare function injectResult(pi: ExtensionAPI, agent: string, runId: string, task: string, status: RunStatus, code: number | null, file: string, timedOut?: string, usage?: Usage, mode?: "merged" | "separate", usageAlreadyReported?: boolean, body?: string, activityFile?: string, signal?: NodeJS.Signals | null): boolean;
|
|
190
|
+
/** Tail of an activity log for failure diagnostics ("" when missing/empty). */
|
|
191
|
+
export declare function readActivityTail(file: string, maxChars?: number): Promise<string>;
|
|
167
192
|
export {};
|