billion-context-pi 0.1.41 → 0.1.45
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 +12 -0
- package/README.zh-CN.md +12 -0
- package/dist/compress-tool.d.ts +11 -2
- package/dist/config.d.ts +6 -0
- package/dist/index.js +980 -246
- package/dist/index.js.map +1 -1
- package/dist/overflow-selfheal.d.ts +45 -0
- package/dist/runtime.d.ts +43 -0
- package/dist/setup-subagent-tools.d.ts +46 -3
- package/dist/throttle-retry.d.ts +52 -0
- package/dist/update.d.ts +26 -0
- package/dist/user-config.d.ts +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const OVERFLOW_MARKER: RegExp;
|
|
2
|
+
export interface OverflowInfo {
|
|
3
|
+
isOverflow: boolean;
|
|
4
|
+
/** The real context window, when the provider stated it in the error. */
|
|
5
|
+
window?: number;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function inspectOverflowMessage(haystack: string | undefined | null): OverflowInfo;
|
|
9
|
+
/**
|
|
10
|
+
* Reserve the model's output budget from the context window, so the kernel's
|
|
11
|
+
* nudge/truncate bands sit below (window - maxOutput) and the context always
|
|
12
|
+
* leaves room for the model's reply. This prevents the "context + output >
|
|
13
|
+
* window" overflow on a small window (agents routinely set a large max output).
|
|
14
|
+
* Returns the window unchanged when maxOutput is not usable (non-positive,
|
|
15
|
+
* non-finite, or >= window — a maxOutput >= window request is degenerate and is
|
|
16
|
+
* left to the overflow self-heal).
|
|
17
|
+
*/
|
|
18
|
+
export declare function reserveOutputHeadroom(window: number, maxOutput: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the OUTPUT budget should be reserved from the context window at
|
|
21
|
+
* all. Anthropic's Messages API enforces the input limit INDEPENDENTLY of
|
|
22
|
+
* max_tokens (the output budget is separate — input up to the window works
|
|
23
|
+
* with any max_tokens), so reserving the model's output capability would
|
|
24
|
+
* shift the nudge/truncate bands down by maxTokens on every session with no
|
|
25
|
+
* safety gain (e.g. a 200k model with a 64k output budget would start
|
|
26
|
+
* compressing around 136k). The OpenAI-family APIs count output against the
|
|
27
|
+
* window, so the reservation is only needed there. Unknown APIs reserve
|
|
28
|
+
* (conservative — a missed reservation at worst overflows once and the
|
|
29
|
+
* self-heal corrects it).
|
|
30
|
+
*/
|
|
31
|
+
export declare function shouldReserveOutputHeadroom(api: string | undefined): boolean;
|
|
32
|
+
export declare class OverflowEpisode {
|
|
33
|
+
/** Real windows learned from overflow errors, keyed by model id. A learned
|
|
34
|
+
* window is model-specific: switching to a bigger model mid-session must
|
|
35
|
+
* not inherit the smaller model's learned limit (that would re-center the
|
|
36
|
+
* bands below the new model's real window → premature compression). */
|
|
37
|
+
private learned;
|
|
38
|
+
learnedWindowFor(modelId: string): number | null;
|
|
39
|
+
setLearnedWindow(modelId: string, window: number): void;
|
|
40
|
+
/** When true, the next context event forces usage >=95% (emergency). Kept
|
|
41
|
+
* session-scoped (not per-model): the context did not shrink, so the next
|
|
42
|
+
* turn needs the emergency regardless of which model answers it. */
|
|
43
|
+
armed: boolean;
|
|
44
|
+
reset(): void;
|
|
45
|
+
}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -4,11 +4,22 @@ import { type AdapterConfig } from "./config.js";
|
|
|
4
4
|
import { DensityEstimator } from "./density.js";
|
|
5
5
|
import { entriesToCoreMessages } from "./messages.js";
|
|
6
6
|
import { SessionStateStore } from "./state.js";
|
|
7
|
+
import { ThrottleEpisode } from "./throttle-retry.js";
|
|
8
|
+
import { OverflowEpisode } from "./overflow-selfheal.js";
|
|
7
9
|
type AgentMessage = SessionMessageEntry["message"];
|
|
8
10
|
export declare function readContextEntries(sm: ExtensionContext["sessionManager"]): SessionEntry[];
|
|
9
11
|
export declare function isPiHost(sm: ExtensionContext["sessionManager"]): boolean;
|
|
10
12
|
export interface AcpRuntime {
|
|
11
13
|
core: CompressionCore;
|
|
14
|
+
/** Per-session provider-throttle retry episode (attempt budget + kick
|
|
15
|
+
* pacing), keyed by session id so concurrent sessions in one extension
|
|
16
|
+
* instance cannot share an episode. Reset on session_start and on any
|
|
17
|
+
* real progress / user input. */
|
|
18
|
+
throttleFor: (sid: string) => ThrottleEpisode;
|
|
19
|
+
/** Drop a session's throttle episode entirely (session_shutdown): aborts a
|
|
20
|
+
* pending kick sleep and releases the map entry so a long-lived process
|
|
21
|
+
* that cycles through many sessions doesn't accumulate them. */
|
|
22
|
+
throttleDrop: (sid: string) => void;
|
|
12
23
|
store: SessionStateStore;
|
|
13
24
|
density: DensityEstimator;
|
|
14
25
|
/** 设置 countTokens 闭包使用的 modelId(每轮 context 事件调用)。 */
|
|
@@ -22,11 +33,34 @@ export interface AcpRuntime {
|
|
|
22
33
|
/** Drop per-session tracking state (session_start). */
|
|
23
34
|
clearSessionTracking(sid: string): void;
|
|
24
35
|
adapter: AdapterConfig;
|
|
36
|
+
setAdapter(adapter: AdapterConfig): void;
|
|
25
37
|
prompts: Prompts;
|
|
26
38
|
setPrompts(prompts: Prompts): void;
|
|
27
39
|
markNudgeShown(turnKey: string): void;
|
|
28
40
|
nudgeShownFor(turnKey: string): boolean;
|
|
41
|
+
/** Process compress toolResults for the CURRENT user turn only (the caller
|
|
42
|
+
* scopes the list — see collectCompressOutcomes in src/index.ts); idempotent
|
|
43
|
+
* per toolCallId. Outcome classes: isError or noop (0-block panel) →
|
|
44
|
+
* failure (count++), success panel (>= 1 block) → reset, other non-error
|
|
45
|
+
* text → neutral (count unchanged). Returns the failure count, the
|
|
46
|
+
* toolCallId of the newest failure that still needs a retry prompt (null
|
|
47
|
+
* when none, capped, or count 0), and whether the cap was just reached. */
|
|
48
|
+
noteCompressOutcomes(turnKey: string, outcomes: ReadonlyArray<{
|
|
49
|
+
toolCallId: string;
|
|
50
|
+
isError: boolean;
|
|
51
|
+
success: boolean;
|
|
52
|
+
noop?: boolean;
|
|
53
|
+
}>): {
|
|
54
|
+
count: number;
|
|
55
|
+
retryFor: string | null;
|
|
56
|
+
cappedNow: boolean;
|
|
57
|
+
};
|
|
58
|
+
/** True when this turn already burned MAX_COMPRESS_ATTEMPTS failed/no-op
|
|
59
|
+
* compress calls — used to stop re-injecting the (dedup-exempt) emergency
|
|
60
|
+
* nudge that would otherwise keep looping no-op compressions (issue #6). */
|
|
61
|
+
compressRetryCappedFor(turnKey: string): boolean;
|
|
29
62
|
clearNudgeTracking(): void;
|
|
63
|
+
clearCompressRetryTracking(): void;
|
|
30
64
|
liveContextLimit(ctx: ExtensionContext): number;
|
|
31
65
|
configFor(ctx: ExtensionContext): Config;
|
|
32
66
|
/** Re-read ~/.<dir>/acp.json + <cwd>/<dir>/acp.json and re-derive the adapter
|
|
@@ -40,6 +74,15 @@ export interface AcpRuntime {
|
|
|
40
74
|
}>;
|
|
41
75
|
save(state: CompressionState, ctx: ExtensionContext): Promise<void>;
|
|
42
76
|
acquireLock(sid: string): Promise<() => void>;
|
|
77
|
+
/** Per-session overflow self-heal state (learned window + armed emergency).
|
|
78
|
+
* Keyed by session id so concurrent sessions cannot share an episode. */
|
|
79
|
+
overflowFor(sid: string): OverflowEpisode;
|
|
80
|
+
/** Drop a session's overflow episode entirely (session_shutdown): releases
|
|
81
|
+
* the map entry so a long-lived process cycling through many sessions
|
|
82
|
+
* doesn't accumulate them. */
|
|
83
|
+
overflowDrop(sid: string): void;
|
|
43
84
|
}
|
|
85
|
+
/** Max FAILED compress calls that get a retry prompt per user turn. */
|
|
86
|
+
export declare const MAX_COMPRESS_ATTEMPTS = 3;
|
|
44
87
|
export declare function createRuntime(adapter: AdapterConfig): AcpRuntime;
|
|
45
88
|
export {};
|
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
/** The four ACP tools to ensure on every pi-subagents builtin agent. */
|
|
2
|
+
export declare const ACP_TOOLS: readonly ["compress", "decompress", "search_context", "acp_status"];
|
|
2
3
|
export interface SetupResult {
|
|
3
4
|
path: string;
|
|
4
5
|
action: "skipped" | "updated" | "failed";
|
|
5
6
|
reason?: string;
|
|
6
7
|
}
|
|
7
|
-
export
|
|
8
|
-
|
|
8
|
+
export interface SetupOptions {
|
|
9
|
+
/** Injectable agent directory (defaults to env PI_CODING_AGENT_DIR or ~/.pi/agent). */
|
|
10
|
+
agentDir?: string;
|
|
11
|
+
/** Injectable cwd for project-scope detection (defaults to process.cwd()). */
|
|
12
|
+
cwd?: string;
|
|
13
|
+
/** Explicit install directory (skips detection; for git installs or forks). */
|
|
14
|
+
installDir?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the pi agent config directory (e.g. ~/.pi/agent), honoring the
|
|
18
|
+
* PI_CODING_AGENT_DIR environment variable (mirroring pi's own resolution).
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveAgentDir(): string;
|
|
21
|
+
interface ParsedBuiltin {
|
|
22
|
+
name: string;
|
|
23
|
+
/** Frontmatter `tools` list; undefined when the agent is unrestricted. */
|
|
24
|
+
tools?: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Detect an installed pi-subagents package and return its directory, or null
|
|
28
|
+
* when no installation is found.
|
|
29
|
+
*
|
|
30
|
+
* Checked in priority order:
|
|
31
|
+
* 1. user-scope npm install: <agentDir>/npm/node_modules/pi-subagents
|
|
32
|
+
* 2. project-scope npm install: <cwd>/.pi/npm/node_modules/pi-subagents
|
|
33
|
+
* 3. user-scope extension dir: <agentDir>/extensions/<name>/package.json where name === "pi-subagents"
|
|
34
|
+
* 4. project-scope extension dir: <cwd>/.pi/extensions/<name>/package.json
|
|
35
|
+
*
|
|
36
|
+
* Git installs and the legacy global npm location are intentionally not
|
|
37
|
+
* checked: a miss there is a safe no-op (ACP tools stay un-injected).
|
|
38
|
+
*/
|
|
39
|
+
export declare function findPiSubagentsInstall(agentDir: string, cwd: string): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Discover the builtin agents shipped by the detected pi-subagents package
|
|
42
|
+
* from its agents/*.md frontmatter (name + tools). Returns an empty list
|
|
43
|
+
* when the package ships no agents directory.
|
|
44
|
+
*/
|
|
45
|
+
export declare function discoverBuiltinAgents(installDir: string): ParsedBuiltin[];
|
|
46
|
+
/**
|
|
47
|
+
* Ensure ACP context tools are present in pi-subagents' agent overrides.
|
|
48
|
+
* No-op (skipped) unless a pi-subagents installation is detected.
|
|
49
|
+
*/
|
|
50
|
+
export declare function ensureSubagentAcpTools(settingsPath?: string, options?: SetupOptions): SetupResult;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export declare const THROTTLE_RETRY_ERROR_MESSAGE = "429 rate limit: Too many tokens, please wait before trying again.";
|
|
2
|
+
export declare const THROTTLE_KICK_SENTINEL = "[ACP:provider-throttle]";
|
|
3
|
+
export declare const THROTTLE_KICK_TEXT = "[ACP:provider-throttle] The previous assistant response was interrupted by a provider rate limit (transient, not a real failure). Resume the task exactly where it left off \u2014 do not re-run completed steps and do not discuss the interruption unless asked.";
|
|
4
|
+
export interface ThrottleErrorProbe {
|
|
5
|
+
role: string;
|
|
6
|
+
stopReason?: string;
|
|
7
|
+
errorMessage?: string;
|
|
8
|
+
content: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function isThrottleError(msg: ThrottleErrorProbe): boolean;
|
|
11
|
+
export declare function isKickMessage(msg: {
|
|
12
|
+
role: string;
|
|
13
|
+
content: unknown;
|
|
14
|
+
}): boolean;
|
|
15
|
+
export interface ThrottleRetryConfig {
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
maxRetries?: number;
|
|
18
|
+
baseDelayMs?: number;
|
|
19
|
+
maxDelayMs?: number;
|
|
20
|
+
backoffMode?: "exponential" | "fixed";
|
|
21
|
+
}
|
|
22
|
+
export interface ResolvedThrottleRetry {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
maxRetries: number;
|
|
25
|
+
baseDelayMs: number;
|
|
26
|
+
maxDelayMs: number;
|
|
27
|
+
backoffMode: "exponential" | "fixed";
|
|
28
|
+
}
|
|
29
|
+
export declare const DEFAULT_THROTTLE_RETRY: ResolvedThrottleRetry;
|
|
30
|
+
export declare function resolveThrottleRetry(cfg: boolean | ThrottleRetryConfig | undefined): ResolvedThrottleRetry;
|
|
31
|
+
export declare function throttleDelayMs(kickNumber: number, r: ResolvedThrottleRetry): number;
|
|
32
|
+
export interface ThrottleEpisodeState {
|
|
33
|
+
attempts: number;
|
|
34
|
+
kicks: number;
|
|
35
|
+
candidate: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare const INITIAL_THROTTLE_STATE: ThrottleEpisodeState;
|
|
38
|
+
export declare class ThrottleEpisode {
|
|
39
|
+
state: ThrottleEpisodeState;
|
|
40
|
+
private cancel;
|
|
41
|
+
reset(): void;
|
|
42
|
+
onProgress(): void;
|
|
43
|
+
onUserMessage(kick: boolean): void;
|
|
44
|
+
onThrottleError(maxRetries: number): "rewrite" | "exhausted";
|
|
45
|
+
onNonThrottleError(): void;
|
|
46
|
+
readyToKick(maxRetries: number): boolean;
|
|
47
|
+
onKickStarted(): void;
|
|
48
|
+
onKickCancelled(): void;
|
|
49
|
+
sleepController(): AbortController;
|
|
50
|
+
cancelSleep(): void;
|
|
51
|
+
}
|
|
52
|
+
export declare function abortableSleep(ms: number, signal: AbortSignal): Promise<"ok" | "aborted">;
|
package/dist/update.d.ts
CHANGED
|
@@ -1,2 +1,28 @@
|
|
|
1
|
+
export type NpmRunner = (args: string[], opts: {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
timeout: number;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
code: number;
|
|
6
|
+
stdout: string;
|
|
7
|
+
stderr: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const runNpm: NpmRunner;
|
|
10
|
+
export declare function setRunNpmForTest(impl: NpmRunner): void;
|
|
11
|
+
export type NodeRunner = (args: string[], opts: {
|
|
12
|
+
timeout: number;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
code: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const runNode: NodeRunner;
|
|
19
|
+
export declare function setRunNodeForTest(impl: NodeRunner): void;
|
|
20
|
+
export declare function isNewer(latest: string, current: string): boolean;
|
|
1
21
|
export declare function findNpmRoot(extDir: string): string | undefined;
|
|
22
|
+
export type InstallOutcome = "ok" | "failed" | "rolled-back";
|
|
23
|
+
export declare function verifyInstall(npmDir: string, latest: string): Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
reason?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function autoInstallLatest(latest: string, extDirOverride?: string): Promise<InstallOutcome>;
|
|
2
28
|
export declare function checkForUpdate(autoUpdate: boolean, notify?: (msg: string) => void): Promise<void>;
|
package/dist/user-config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Prompts } from "acp-kernel";
|
|
2
2
|
import type { AdapterConfig, CompressConfig, DelegateConfig } from "./config.js";
|
|
3
|
+
import type { ThrottleRetryConfig } from "./throttle-retry.js";
|
|
3
4
|
/** User-facing config keys (subset of AdapterConfig). Loaded from
|
|
4
5
|
* ~/.<CONFIG_DIR_NAME>/acp.json (global) and <cwd>/.<CONFIG_DIR_NAME>/acp.json
|
|
5
6
|
* (project-local overrides project-global). Project wins over global. */
|
|
@@ -11,6 +12,7 @@ export interface UserAcpConfig {
|
|
|
11
12
|
toolOutputMaxBytes?: number;
|
|
12
13
|
delegate?: boolean | DelegateConfig;
|
|
13
14
|
compress?: CompressConfig;
|
|
15
|
+
throttleRetry?: boolean | ThrottleRetryConfig;
|
|
14
16
|
displayUsage?: "merged" | "separate";
|
|
15
17
|
prompts?: Partial<Prompts>;
|
|
16
18
|
acknowledgePromptsRisk?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "billion-context-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.45",
|
|
4
4
|
"description": "One billion, not one million. Model-driven context management for the Pi coding agent.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
62
62
|
"@types/node": "^26.1.2",
|
|
63
|
-
"acp-kernel": "0.0.
|
|
63
|
+
"acp-kernel": "0.0.32",
|
|
64
64
|
"billion-context-kit": "0.2.0",
|
|
65
65
|
"tsup": "^8.5.1",
|
|
66
66
|
"tsx": "^4.23.1",
|