billion-context-pi-lean 0.1.69-lean.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +141 -81
- 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-PPYOZRUP.js +18 -0
- package/dist/chunk-PPYOZRUP.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-panel.d.ts +61 -0
- package/dist/config.d.ts +296 -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/idle-compact.d.ts +39 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +23169 -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/user-config.d.ts +67 -0
- package/lean/index.ts +376 -0
- package/package.json +85 -36
- package/schema/bcp-block-v1.json +42 -0
- package/README.zh-CN.md +0 -79
- package/index.ts +0 -266
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SessionEntry, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
type AgentMessage = SessionMessageEntry["message"];
|
|
3
|
+
export declare function liveOnlyTail(entries: SessionEntry[], live: AgentMessage[]): AgentMessage[] | null;
|
|
4
|
+
/** Cached variant of liveOnlyTail keyed by session id. Behaviourally identical
|
|
5
|
+
* (same tail or null); only the prefix proof is memoized. Doubt always falls
|
|
6
|
+
* back to the full walk. */
|
|
7
|
+
export declare function liveOnlyTailCached(sid: string, entries: SessionEntry[], live: AgentMessage[]): AgentMessage[] | null;
|
|
8
|
+
/** Drop a session's alignment cache (session_shutdown for memory hygiene). */
|
|
9
|
+
export declare function dropLiveOnlyTailCache(sid: string): void;
|
|
10
|
+
export {};
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function setDebugEnabled(enabled: boolean): void;
|
|
2
|
+
export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
3
|
+
export declare function closeLogStream(): void;
|
|
4
|
+
export declare function logError(scope: string, fields: Record<string, unknown>): void;
|
|
5
|
+
export declare function logWarn(scope: string, fields: Record<string, unknown>): void;
|
|
6
|
+
export declare function logInfo(scope: string, fields: Record<string, unknown>): void;
|
|
7
|
+
export declare function logThrow(scope: string, err: unknown, extra?: Record<string, unknown>): void;
|
|
8
|
+
export declare const debug: {
|
|
9
|
+
readonly enabled: boolean;
|
|
10
|
+
readonly logFile: string;
|
|
11
|
+
event(scope: string, fields: Record<string, unknown>): void;
|
|
12
|
+
};
|
|
13
|
+
export declare const logger: {
|
|
14
|
+
error: typeof logError;
|
|
15
|
+
warn: typeof logWarn;
|
|
16
|
+
info: typeof logInfo;
|
|
17
|
+
debug(scope: string, fields: Record<string, unknown>): void;
|
|
18
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { SessionEntry, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type Config, type CoreMessage } from "acp-kernel";
|
|
3
|
+
import type { TurnBoundaryEntry } from "./turn-boundary.js";
|
|
4
|
+
type AgentMessage = SessionMessageEntry["message"];
|
|
5
|
+
/** Host-injected status-panel custom messages (written by src/commands.ts) —
|
|
6
|
+
* UI-only, never projected into LLM context, so never user-like entries. */
|
|
7
|
+
export declare const ACP_STATUS_CUSTOM_TYPE = "acp-status";
|
|
8
|
+
/** /acp-export handoff docs (src/export.ts) — UI-only transcript output,
|
|
9
|
+
* persistent in the session but never projected into LLM context (#255). */
|
|
10
|
+
export declare const ACP_EXPORT_CUSTOM_TYPE = "acp-export";
|
|
11
|
+
/** /acp-rule outputs (src/commands.ts) — UI-only transcript output, same
|
|
12
|
+
* exclusion as acp-status/acp-export (#526): the rules themselves already
|
|
13
|
+
* reach the model through the feature's own channel, so the human-facing
|
|
14
|
+
* report must never be re-billed as model context. */
|
|
15
|
+
export declare const ACP_RULE_CUSTOM_TYPE = "acp-rule";
|
|
16
|
+
/** Nudge persistence record (issue #326): written via pi.appendEntry as a
|
|
17
|
+
* type:"custom" entry — never projected into the sent view (see above), so
|
|
18
|
+
* the compact one-liner stays out of model context while surviving restarts. */
|
|
19
|
+
export declare const ACP_NUDGE_CUSTOM_TYPE = "acp-nudge";
|
|
20
|
+
export interface AcpNudgeRecord {
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
/** True for host-injected custom_message entries that participate in LLM
|
|
24
|
+
* context: non-empty custom_message except the UI-only types in
|
|
25
|
+
* CONTEXT_EXCLUDED_CUSTOM_TYPES (acp-status panels, acp-export docs,
|
|
26
|
+
* acp-rule reports)
|
|
27
|
+
* (Pi-native projection semantics, session-manager.d.ts). The non-empty gate
|
|
28
|
+
* uses the exact same extractText check as the projection below, so an entry
|
|
29
|
+
* either enters context or it doesn't — and only entries that enter context
|
|
30
|
+
* can delimit a turn (#364 acceptance c: empty control signals start none).
|
|
31
|
+
* Shared by the turn-boundary predicate in src/turn-boundary.ts so the two
|
|
32
|
+
* views can never drift apart; defined here (not there) because it needs
|
|
33
|
+
* extractText — importing that back would create a cycle. */
|
|
34
|
+
export declare function isCustomMessageEntry(entry: TurnBoundaryEntry): entry is TurnBoundaryEntry & {
|
|
35
|
+
type: "custom_message";
|
|
36
|
+
};
|
|
37
|
+
export declare function entriesToCoreMessages(entries: SessionEntry[]): CoreMessage[];
|
|
38
|
+
/** Append-only incremental cache for entriesToCoreMessages (issue #561).
|
|
39
|
+
*
|
|
40
|
+
* Projection is a pure prefix-stable function: each entry contributes a fixed
|
|
41
|
+
* slice of CoreMessages, so projecting entries[0..n) then appending the
|
|
42
|
+
* projection of entries[n..m) equals projecting entries[0..m) — as long as the
|
|
43
|
+
* first n entries are UNCHANGED. Hosts rebuild the entries array (and the entry
|
|
44
|
+
* objects) from the append-only session jsonl every turn, so identity caching
|
|
45
|
+
* is useless; validate structurally instead:
|
|
46
|
+
* - count only ever GROWS (a drop = host rewind/truncate → full reproject),
|
|
47
|
+
* - the first and last cached boundary entries still carry the same ids
|
|
48
|
+
* (canaries against in-place rewrites the count check cannot see),
|
|
49
|
+
* - at most MAX_INCREMENTAL_ENTRIES new entries per turn.
|
|
50
|
+
* Any doubt → full entriesToCoreMessages, identical result, just slower. */
|
|
51
|
+
export declare class EntryProjectionCache {
|
|
52
|
+
private count;
|
|
53
|
+
private firstId;
|
|
54
|
+
private lastId;
|
|
55
|
+
private cores;
|
|
56
|
+
/** Project `entries`, reusing the previous turn's work when the array is a
|
|
57
|
+
* structural extension of the last one seen. Returns a defensive copy: the
|
|
58
|
+
* internal array is shared across turns, and callers hand it to kernel code
|
|
59
|
+
* that must never observe (or mutate) cross-turn state. */
|
|
60
|
+
project(entries: SessionEntry[]): CoreMessage[];
|
|
61
|
+
/** Drop the cache (state rebuilt, session switched). */
|
|
62
|
+
reset(): void;
|
|
63
|
+
}
|
|
64
|
+
export declare function extractText(content: unknown): string;
|
|
65
|
+
export declare function thinkingTokenCount(content: unknown): number;
|
|
66
|
+
export declare function countImageBlocks(content: unknown): number;
|
|
67
|
+
export declare function messageIdentity(message: unknown): string;
|
|
68
|
+
export declare function messageRef(message: unknown): string | undefined;
|
|
69
|
+
export declare const TRUNCATION_MARKER = "[truncated for context space]";
|
|
70
|
+
export declare function matchesStoredText(stored: string, visible: string): boolean;
|
|
71
|
+
export declare function coreOutToAgentMessages(coreOut: CoreMessage[], originalById: Map<string, AgentMessage>): AgentMessage[];
|
|
72
|
+
export interface LatestUserGuardResult {
|
|
73
|
+
messages: CoreMessage[];
|
|
74
|
+
/** Id of the latest user message whose kernel truncation was undone. */
|
|
75
|
+
restoredId?: string;
|
|
76
|
+
/** Estimated request size after the guard (tokenCount scale). */
|
|
77
|
+
remainingTokens: number;
|
|
78
|
+
}
|
|
79
|
+
/** Lean: the kernel protects the most recent user message from compression
|
|
80
|
+
* but its emergency truncation only protects the last N messages, so a big
|
|
81
|
+
* prompt followed by a few tool rounds lost its body without any summary.
|
|
82
|
+
* Undo that truncation and give the reclaimed budget to the other candidates
|
|
83
|
+
* (the kernel's own truncator, run with the user message hidden). */
|
|
84
|
+
export declare function protectLatestUserMessage(inMessages: CoreMessage[], outMessages: CoreMessage[], config: Config, tokenCount: number): LatestUserGuardResult;
|
|
85
|
+
export {};
|
package/dist/omp.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shown (and logged) when the extension detects an unsupported host and stands down.
|
|
3
|
+
*
|
|
4
|
+
* Unsupported = no Pi `buildContextEntries()` API AND no `PI_ACP_FORK_HOST` declaration
|
|
5
|
+
* (see ./host.ts). OMP (oh-my-pi) falls into this by default: its in-process live-entries
|
|
6
|
+
* integration diverges the nudge's example refs from the session's real refs, so compress
|
|
7
|
+
* calls fail with "does not exist in this session" ([#234]). The billion-context proxy runs
|
|
8
|
+
* compression server-side (it owns the ref coordinate space) and works on OMP.
|
|
9
|
+
*/
|
|
10
|
+
export declare const UNSUPPORTED_HOST_MESSAGE: string;
|
|
11
|
+
/**
|
|
12
|
+
* Shown (and logged) when a session runs on a host that declared itself a
|
|
13
|
+
* Pi-compatible fork via PI_ACP_FORK_HOST (see ./host.ts). Live-tail refs are
|
|
14
|
+
* content-addressed and stable across context fires (#459), which assumes the
|
|
15
|
+
* host's view of the session grows append-only; a host that rewrites or
|
|
16
|
+
* shrinks in-flight messages can still drift refs, so long sessions on such
|
|
17
|
+
* hosts are safer on the proxy (it owns the ref coordinate space server-side).
|
|
18
|
+
*/
|
|
19
|
+
export declare const FORK_HOST_WARNING_MESSAGE: string;
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
export declare const NO_BODY_4XX_MARKER: RegExp;
|
|
10
|
+
export declare function isNoBody4xxError(haystack: string | undefined | null): boolean;
|
|
11
|
+
/** Corroboration threshold for arming on an ambiguous no-body 4xx: the
|
|
12
|
+
* calibrated sent-view estimate must be at least this fraction of the
|
|
13
|
+
* effective limit (same basis as the turn log's `pct`). */
|
|
14
|
+
export declare const NO_BODY_ARM_RATIO = 0.5;
|
|
15
|
+
/** Decision returned by OverflowEpisode.onNoBody4xx(). */
|
|
16
|
+
export interface NoBodyDecision {
|
|
17
|
+
arm: boolean;
|
|
18
|
+
/** Consecutive no-body 4xx errors since the last successful assistant turn. */
|
|
19
|
+
consecutive: number;
|
|
20
|
+
/** Recorded sent-view estimate / effective limit, when both were seen. */
|
|
21
|
+
ratio: number | null;
|
|
22
|
+
}
|
|
23
|
+
/** Default cap on the output-headroom reservation, as a fraction of the
|
|
24
|
+
* context window (issue #207). Reserving the FULL registered max output
|
|
25
|
+
* capability halves the effective input budget on models whose maxTokens is a
|
|
26
|
+
* large share of the window (e.g. 131072 on a 262144 window → the 75% force-
|
|
27
|
+
* compress band fires at ~37% of the full window), while real per-turn
|
|
28
|
+
* replies rarely approach that. Capping at 25% keeps the guarantee where it
|
|
29
|
+
* matters — any single-turn reply up to the reserved amount still fits at the
|
|
30
|
+
* 95% emergency threshold — while bounding the budget loss. A reply longer
|
|
31
|
+
* than the reservation overflows once; the overflow self-heal (learned window
|
|
32
|
+
* + armed emergency) recovers it on the next turn. */
|
|
33
|
+
export declare const DEFAULT_OUTPUT_HEADROOM_MAX_PCT = 0.25;
|
|
34
|
+
/** Resolve the user's `outputHeadroomMaxPct` (ratio or "N%" string) to a
|
|
35
|
+
* numeric cap, falling back to DEFAULT_OUTPUT_HEADROOM_MAX_PCT when unset.
|
|
36
|
+
* Shared by every headroom call site (context transform, /acp, acp_status)
|
|
37
|
+
* so they all measure against the SAME capped limit (issue #207/#267). */
|
|
38
|
+
export declare function resolveOutputHeadroomCap(value: number | string | undefined): number;
|
|
39
|
+
/**
|
|
40
|
+
* Reserve the model's output budget from the context window, so the kernel's
|
|
41
|
+
* nudge/truncate bands sit below (window - reserved) and the context leaves
|
|
42
|
+
* room for the model's reply. This prevents the "context + output > window"
|
|
43
|
+
* overflow on a small window (agents routinely set a large max output).
|
|
44
|
+
*
|
|
45
|
+
* `capPct` bounds the reservation as a fraction of the window: reserved =
|
|
46
|
+
* min(maxOutput, capPct * window). The default (1) preserves the original
|
|
47
|
+
* full-capability reservation; the adapter passes DEFAULT_OUTPUT_HEADROOM_MAX_PCT
|
|
48
|
+
* (or the user's `outputHeadroomMaxPct`) so oversized registered capabilities
|
|
49
|
+
* no longer eat most of the input budget (issue #207). capPct semantics:
|
|
50
|
+
* - 0 → no reservation (window returned unchanged)
|
|
51
|
+
* - (0, 1) → reservation capped at capPct * window
|
|
52
|
+
* - >= 1 → legacy behavior (full maxOutput reserved)
|
|
53
|
+
* - non-finite → legacy behavior (treated as "not provided")
|
|
54
|
+
* Returns the window unchanged when maxOutput is not usable (non-positive,
|
|
55
|
+
* non-finite, or >= window — a maxOutput >= window request is degenerate and is
|
|
56
|
+
* left to the overflow self-heal).
|
|
57
|
+
*/
|
|
58
|
+
export declare function reserveOutputHeadroom(window: number, maxOutput: number, capPct?: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the OUTPUT budget should be reserved from the context window at
|
|
61
|
+
* all. Anthropic's Messages API enforces the input limit INDEPENDENTLY of
|
|
62
|
+
* max_tokens (the output budget is separate — input up to the window works
|
|
63
|
+
* with any max_tokens), so reserving the model's output capability would
|
|
64
|
+
* shift the nudge/truncate bands down by maxTokens on every session with no
|
|
65
|
+
* safety gain (e.g. a 200k model with a 64k output budget would start
|
|
66
|
+
* compressing around 136k). The OpenAI-family APIs count output against the
|
|
67
|
+
* window, so the reservation is only needed there. Unknown APIs reserve
|
|
68
|
+
* (conservative — a missed reservation at worst overflows once and the
|
|
69
|
+
* self-heal corrects it).
|
|
70
|
+
*/
|
|
71
|
+
export declare function shouldReserveOutputHeadroom(api: string | undefined): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Apply the output-headroom reservation to a resolved config's modelContextLimit
|
|
74
|
+
* (see reserveOutputHeadroom / shouldReserveOutputHeadroom). Returns a NEW config
|
|
75
|
+
* (never mutates the input) so the shared resolved config stays untouched. Used
|
|
76
|
+
* by BOTH the live context transform and the read-only panel surfaces (/acp,
|
|
77
|
+
* acp_status) so every percentage is measured against the SAME real request
|
|
78
|
+
* limit — otherwise the panel reports against the full window while the nudge
|
|
79
|
+
* bands run against (window − maxOutput) (issue #267).
|
|
80
|
+
*
|
|
81
|
+
* `capPct` bounds the reservation as a fraction of the window (see
|
|
82
|
+
* reserveOutputHeadroom); callers pass resolveOutputHeadroomCap(
|
|
83
|
+
* adapter.outputHeadroomMaxPct) so the panel and the nudge bands share the
|
|
84
|
+
* same capped limit (issue #207). The default (1) preserves the legacy
|
|
85
|
+
* full-capability reservation for callers that don't pass a cap.
|
|
86
|
+
*/
|
|
87
|
+
export declare function applyOutputHeadroom<T extends {
|
|
88
|
+
modelContextLimit: number;
|
|
89
|
+
}>(config: T, model: {
|
|
90
|
+
maxTokens?: number;
|
|
91
|
+
api?: string;
|
|
92
|
+
} | undefined, capPct?: number): T;
|
|
93
|
+
export declare class OverflowEpisode {
|
|
94
|
+
/** Real windows learned from overflow errors, keyed by model id. A learned
|
|
95
|
+
* window is model-specific: switching to a bigger model mid-session must
|
|
96
|
+
* not inherit the smaller model's learned limit (that would re-center the
|
|
97
|
+
* bands below the new model's real window → premature compression). */
|
|
98
|
+
private learned;
|
|
99
|
+
learnedWindowFor(modelId: string): number | null;
|
|
100
|
+
setLearnedWindow(modelId: string, window: number): void;
|
|
101
|
+
/** When true, the next context event forces usage >=95% (emergency). Kept
|
|
102
|
+
* session-scoped (not per-model): the context did not shrink, so the next
|
|
103
|
+
* turn needs the emergency regardless of which model answers it. */
|
|
104
|
+
armed: boolean;
|
|
105
|
+
/** Sent-view estimate + effective limit recorded by the context event —
|
|
106
|
+
* the numbers of the request that just ended. */
|
|
107
|
+
private sentTokens;
|
|
108
|
+
private sentLimit;
|
|
109
|
+
/** Consecutive no-body 4xx errors since the last successful assistant
|
|
110
|
+
* turn. 0 at session start (the episode is created per session). */
|
|
111
|
+
private noBody4xx;
|
|
112
|
+
noteSentView(tokens: number, limit: number): void;
|
|
113
|
+
/** A successful assistant turn unwedged the request loop: the consecutive
|
|
114
|
+
* no-body count restarts from zero. */
|
|
115
|
+
noteSuccess(): void;
|
|
116
|
+
/**
|
|
117
|
+
* Record one ambiguous no-body 4xx and decide whether it corroborates a
|
|
118
|
+
* probable context overflow. Guards against the false positive (the same
|
|
119
|
+
* text serves non-overflow 4xx): arm only when the current sent-view
|
|
120
|
+
* estimate is >= NO_BODY_ARM_RATIO of the effective limit, OR this is the
|
|
121
|
+
* >=2nd consecutive no-body since the last success. The consecutive guard
|
|
122
|
+
* recovers the dead-loop even at a low ratio (the incident ran at ~24%:
|
|
123
|
+
* the estimate under-reported vs the input+max_tokens cap) — a genuine
|
|
124
|
+
* overflow fails identically while the context is unchanged, so repeated
|
|
125
|
+
* no-body errors with no successful turn in between are size-dependent by
|
|
126
|
+
* construction. No window can be parsed from a bodyless error, so when
|
|
127
|
+
* this arms, the emergency uses the already-resolved effective limit (no
|
|
128
|
+
* window is learned).
|
|
129
|
+
*/
|
|
130
|
+
onNoBody4xx(): NoBodyDecision;
|
|
131
|
+
reset(): void;
|
|
132
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { builtinSource, createDirPackSource, createPackResolver, defaultPack, isValidPackName, leanPack, sanitizePackSurface } from "acp-kernel";
|
|
2
|
+
import type { Pack, PackResolver, PackSource, PackSurface, PromptPackFile, Prompts } from "acp-kernel";
|
|
3
|
+
import type { AdapterConfig } from "./config.js";
|
|
4
|
+
import { type PiPromptSections } from "./system-prompt.js";
|
|
5
|
+
import { type AcpToolName, type NudgeSectionsConfig, type ToolPromptsConfig } from "./surface.js";
|
|
6
|
+
export { builtinSource, createDirPackSource, createPackResolver, defaultPack, isValidPackName, leanPack, sanitizePackSurface };
|
|
7
|
+
export type { Pack, PackResolver, PackSource, PackSurface, PromptPackFile };
|
|
8
|
+
export declare function defaultPackSources(cwd: string): PackSource[];
|
|
9
|
+
export declare function packResolver(cwd: string): PackResolver;
|
|
10
|
+
export declare function discoverPack(name: string, cwd: string): Pack | null;
|
|
11
|
+
export declare function resolvePackName(adapter: AdapterConfig, provider?: string, modelId?: string): string;
|
|
12
|
+
export declare function resolveActivePack(adapter: AdapterConfig, cwd: string, provider?: string, modelId?: string, resolver?: PackResolver): Pack;
|
|
13
|
+
export interface SurfaceMeta {
|
|
14
|
+
pack: string;
|
|
15
|
+
packVersion?: string;
|
|
16
|
+
host: string;
|
|
17
|
+
}
|
|
18
|
+
/** Surface meta from an already-resolved pack (avoids double resolution
|
|
19
|
+
* when the caller already holds the active Pack). */
|
|
20
|
+
export declare function surfaceMetaOf(pack: Pack, requested: string): SurfaceMeta;
|
|
21
|
+
/** Host-declared surface facts for the kernel's status reports (acp-kernel
|
|
22
|
+
* StatusReportMeta). Which pack is active is host policy — the kernel only
|
|
23
|
+
* renders what the host declares about the active surface. */
|
|
24
|
+
export declare function resolveSurfaceMeta(adapter: AdapterConfig, cwd: string, provider?: string, modelId?: string, resolver?: PackResolver): SurfaceMeta;
|
|
25
|
+
export interface PiToolExtras {
|
|
26
|
+
promptSnippet?: string;
|
|
27
|
+
promptGuidelines?: string[];
|
|
28
|
+
}
|
|
29
|
+
export type PiToolExtrasConfig = Partial<Record<AcpToolName, PiToolExtras>>;
|
|
30
|
+
export interface PiAdapterSurface {
|
|
31
|
+
promptSections: Partial<PiPromptSections>;
|
|
32
|
+
toolExtras: PiToolExtrasConfig;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pi-specific part of a pack. `surface.adapters.pi` is opaque to the kernel,
|
|
36
|
+
* so the adapter sanitizes it here: tri-state prompt sections restricted to
|
|
37
|
+
* pi's section keys, per-tool extras with malformed fields dropped.
|
|
38
|
+
*/
|
|
39
|
+
export declare function piAdapterSurface(pack: Pack): PiAdapterSurface;
|
|
40
|
+
export interface InlineSurface {
|
|
41
|
+
prompts?: Partial<Prompts>;
|
|
42
|
+
promptSections?: Partial<PiPromptSections>;
|
|
43
|
+
nudgeSections?: NudgeSectionsConfig;
|
|
44
|
+
toolPrompts?: ToolPromptsConfig;
|
|
45
|
+
}
|
|
46
|
+
export interface MergedSurface {
|
|
47
|
+
prompts: Partial<Prompts>;
|
|
48
|
+
promptSections: Partial<PiPromptSections>;
|
|
49
|
+
nudgeSections: NudgeSectionsConfig;
|
|
50
|
+
toolPrompts: ToolPromptsConfig;
|
|
51
|
+
}
|
|
52
|
+
export declare function mergeSurface(pack: Pack | null, inline: InlineSurface): MergedSurface;
|
|
53
|
+
export declare function readToolSurfaceWithPacks(cwd: string): ToolPromptsConfig;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function isBiliProxyBaseUrl(baseUrl: string | undefined): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Shown (and logged) when the extension detects that the model's baseUrl
|
|
4
|
+
* routes through the billion-context wire proxy and stands down. The proxy
|
|
5
|
+
* runs compression server-side and owns the ref coordinate space, so running
|
|
6
|
+
* ACP in-process on top of it would double-compress every request.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PROXY_STAND_DOWN_MESSAGE: string;
|
|
9
|
+
/**
|
|
10
|
+
* Shown (and logged) when a billion-context host-native entry owns this
|
|
11
|
+
* process (issue #461): the native entry self-spawns an in-process proxy and
|
|
12
|
+
* rewrites model API traffic at the fetch layer, so running ACP in-process on
|
|
13
|
+
* top of it would double-compress every request. Its BILLION_CONTEXT_PROXY env
|
|
14
|
+
* var is written only after the proxy is up (past the synchronous extension
|
|
15
|
+
* load), and the fetch-layer rewrite keeps the configured baseUrl clean —
|
|
16
|
+
* BILLION_CONTEXT_NATIVE, set synchronously at module evaluation by the native
|
|
17
|
+
* entry (billion-context markNativeHost, #820/#824), is the only signal visible
|
|
18
|
+
* at our checkpoints. `host` is the marker value ("pi", "opencode", …) — kept
|
|
19
|
+
* in the text so a mismatched host is diagnosable from the warning alone.
|
|
20
|
+
*/
|
|
21
|
+
export declare function nativeStandDownMessage(host: string): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { SessionMessageEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
type AgentMessage = SessionMessageEntry["message"];
|
|
3
|
+
/** [#336] Config for dropping oversized reasoning (thinking) parts from
|
|
4
|
+
* historical `compress` tool calls — exact alignment with opencode-acp #377.
|
|
5
|
+
* `compress` calls are hard-exempt from compression (their tool results are
|
|
6
|
+
* the anchors that keep block summaries addressable), so their thinking
|
|
7
|
+
* rides along every request as an unreclaimable context floor. This pass
|
|
8
|
+
* removes `thinking` parts at request time (persisted history is never
|
|
9
|
+
* modified) from closed-round compress messages whose total reasoning length
|
|
10
|
+
* exceeds `threshold`. A round is closed on ROUND EVIDENCE, not on user
|
|
11
|
+
* messages: the compress tool result must have arrived and at least one
|
|
12
|
+
* message must exist after it. The in-flight round (result still missing or
|
|
13
|
+
* still the last message) is never touched [#348]. */
|
|
14
|
+
export interface CompressReasoningConfig {
|
|
15
|
+
/** Master switch. Default: true. `drop: false` disables the pass entirely
|
|
16
|
+
* (kill-switch; also the recipe for providers whose thinking items are
|
|
17
|
+
* opaque and must round-trip unmodified, e.g. set it per-provider under
|
|
18
|
+
* `compress.providers.<name>`). */
|
|
19
|
+
drop?: boolean;
|
|
20
|
+
/** Single-thinking size gate (chars): a closed-turn compress message's
|
|
21
|
+
* total reasoning length (summed across parts of that message) must
|
|
22
|
+
* STRICTLY EXCEED this to be dropped. Small thinkings are kept; lengths
|
|
23
|
+
* are NOT accumulated across messages. Default: 2048. `0` drops any
|
|
24
|
+
* non-empty reasoning (only zero-length reasoning survives). */
|
|
25
|
+
threshold?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEFAULT_COMPRESS_REASONING: Required<CompressReasoningConfig>;
|
|
28
|
+
export declare function resolveReasoningDrop(cfg?: CompressReasoningConfig): Required<CompressReasoningConfig>;
|
|
29
|
+
/** [#361] Strict-echo thinking upstreams (DeepSeek thinking mode) require
|
|
30
|
+
* `reasoning_content` to round-trip verbatim across tool-call turns; a rebuilt
|
|
31
|
+
* request whose closed-round assistant messages lost their reasoning is rejected
|
|
32
|
+
* with HTTP 400 ("reasoning_content ... must be passed back"). Detect statically
|
|
33
|
+
* from the model's configured origin/name so the request-time drop pass stands
|
|
34
|
+
* down. Mirrors the proxy-side billion-context#690 detector, adapted to pi's
|
|
35
|
+
* signal sources: the in-process adapter does not own the HTTP layer, so there
|
|
36
|
+
* is no learn-on-400 self-heal here — see CONFIGURATION.md for the manual
|
|
37
|
+
* `drop:false` escape hatch (GLM-thinking / QwQ / self-hosted mirrors). */
|
|
38
|
+
export declare function isStrictReasoningEcho(provider?: string, baseUrl?: string): boolean;
|
|
39
|
+
/** [#361] Force the drop pass off on a strict-echo upstream so its reasoning
|
|
40
|
+
* round-trips unmodified. Pure: returns cfg unchanged when not strict-echo or
|
|
41
|
+
* already disabled. Cost-free for non-thinking models on those hosts — they emit
|
|
42
|
+
* no `thinking` parts, so the pass would be a no-op regardless. */
|
|
43
|
+
export declare function applyStrictReasoningGate(cfg: Required<CompressReasoningConfig>, provider?: string, baseUrl?: string): Required<CompressReasoningConfig>;
|
|
44
|
+
export declare function countThinkingChars(messages: AgentMessage[]): number;
|
|
45
|
+
/** Request-time pass aligned with opencode-acp #377: remove `thinking` parts
|
|
46
|
+
* from a message only when ALL gates hold —
|
|
47
|
+
* 1. closed round [#348]: EVERY `compress` toolCall in the message has its
|
|
48
|
+
* tool-result message (role `toolResult`, matching `toolCallId`) at a
|
|
49
|
+
* later index, and at least one message exists after that result (the
|
|
50
|
+
* round has demonstrably moved on). A compress call without a result, or
|
|
51
|
+
* whose result is still the last message, is in flight and never touched
|
|
52
|
+
* — no user message is required, so long agentic sessions do close
|
|
53
|
+
* rounds; a synthetic nudge pushed later cannot retroactively close one;
|
|
54
|
+
* 2. selector: the message carries a `toolCall` part with name "compress"
|
|
55
|
+
* (only compress; other protected tools would need their own explicit
|
|
56
|
+
* config);
|
|
57
|
+
* 3. size: the message's total reasoning length strictly exceeds
|
|
58
|
+
* `threshold` chars (summed across parts, never across messages).
|
|
59
|
+
* Pure: never mutates the input; idempotent; fail-safe (any error returns
|
|
60
|
+
* the input unchanged). */
|
|
61
|
+
export declare function dropCompressReasoning(messages: AgentMessage[], cfg?: CompressReasoningConfig): AgentMessage[];
|
|
62
|
+
/** Lean: thinking that stays on the wire for compress-call messages.
|
|
63
|
+
* estimateTokens skips compress cores whole, and the projection hangs a
|
|
64
|
+
* message's thinking on its FIRST tool-call core, so count thinking only for
|
|
65
|
+
* messages whose first call is compress, after the same drop pass the
|
|
66
|
+
* request applies (closed, oversized rounds lose it). */
|
|
67
|
+
export declare function keptCompressThinkingTokens(messages: AgentMessage[], cfg?: CompressReasoningConfig): number;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { AcpRuntime } from "./runtime.js";
|
|
4
|
+
declare const RuleParams: Type.TObject<{
|
|
5
|
+
rule: Type.TOptional<Type.TString>;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function makeRuleTool(runtime: AcpRuntime): ToolDefinition<typeof RuleParams>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { ExtensionContext, SessionEntry, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type CompressionCore, type CompressionState, type Config, type Prompts } from "acp-kernel";
|
|
3
|
+
import { type AdapterConfig } from "./config.js";
|
|
4
|
+
import { type CompressReasoningConfig } from "./reasoning-drop.js";
|
|
5
|
+
import type { SentViewMeterRecord } from "./tokens.js";
|
|
6
|
+
import { entriesToCoreMessages } from "./messages.js";
|
|
7
|
+
import { SessionStateStore } from "./state.js";
|
|
8
|
+
import { ThrottleEpisode } from "./throttle-retry.js";
|
|
9
|
+
import { OverflowEpisode } from "./overflow-selfheal.js";
|
|
10
|
+
import { type TurnBoundaryPolicy } from "./turn-boundary.js";
|
|
11
|
+
type AgentMessage = SessionMessageEntry["message"];
|
|
12
|
+
export declare function readContextEntries(sm: ExtensionContext["sessionManager"]): SessionEntry[];
|
|
13
|
+
export declare function isPiHost(sm: ExtensionContext["sessionManager"]): boolean;
|
|
14
|
+
/** #453: key for the compress-retry circuit breaker, derived from PERSISTED
|
|
15
|
+
* entries only. Live-merged tail ids (content-addressed since #459) can still
|
|
16
|
+
* churn when the host's view of a not-yet-persisted message drifts, so a
|
|
17
|
+
* breaker keyed on the merged view could reset failCount mid-episode (#452
|
|
18
|
+
* log: cap → inject loop). Persisted entry ids are immutable; this key
|
|
19
|
+
* changes only when a genuine new user message reaches the session log.
|
|
20
|
+
* pi-native hosts see no change. */
|
|
21
|
+
export declare function retryBreakerKey(sm: ExtensionContext["sessionManager"], policy?: TurnBoundaryPolicy): string | undefined;
|
|
22
|
+
/** Minimal identity of a session for state operations that don't need a live
|
|
23
|
+
* ExtensionContext (hosts deriving inline child sessions build these from
|
|
24
|
+
* whatever session handles they hold). */
|
|
25
|
+
export interface SessionRef {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
sessionFile?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AcpRuntime {
|
|
30
|
+
core: CompressionCore;
|
|
31
|
+
/** Set when the host is unsupported (currently: OMP / oh-my-pi) or when the
|
|
32
|
+
* model's baseUrl routes through the billion-context wire proxy (#296).
|
|
33
|
+
* Once true, the extension stands down: the context transform, system-prompt
|
|
34
|
+
* injection, compaction-cancel and ACP tools all no-op so the host runs
|
|
35
|
+
* untouched. Set at session_start (first context event as fallback). */
|
|
36
|
+
refused: boolean;
|
|
37
|
+
/** User-facing reason shown when a refused ACP tool is invoked; null means
|
|
38
|
+
* the default OMP refusal text applies. Set together with `refused`. */
|
|
39
|
+
refusalMessage: string | null;
|
|
40
|
+
/** Per-session provider-throttle retry episode (attempt budget + kick
|
|
41
|
+
* pacing), keyed by session id so concurrent sessions in one extension
|
|
42
|
+
* instance cannot share an episode. Reset on session_start and on any
|
|
43
|
+
* real progress / user input. */
|
|
44
|
+
throttleFor: (sid: string) => ThrottleEpisode;
|
|
45
|
+
/** Drop a session's throttle episode entirely (session_shutdown): aborts a
|
|
46
|
+
* pending kick sleep and releases the map entry so a long-lived process
|
|
47
|
+
* that cycles through many sessions doesn't accumulate them. */
|
|
48
|
+
throttleDrop: (sid: string) => void;
|
|
49
|
+
/** Per-session tokenCount scale tracker (estimate vs provider). Returns true
|
|
50
|
+
* when the scale just flipped (stale↔not-stale) so the caller can reset the
|
|
51
|
+
* growth baseline — a cross-scale delta is a false artifact, not real growth
|
|
52
|
+
* (issue #267). The first observation for a session never reports a flip. */
|
|
53
|
+
noteTokenScale: (sid: string, stale: boolean) => boolean;
|
|
54
|
+
/** Drop a session's token-scale tracker (session_shutdown). */
|
|
55
|
+
dropTokenScale: (sid: string) => void;
|
|
56
|
+
store: SessionStateStore;
|
|
57
|
+
adapter: AdapterConfig;
|
|
58
|
+
setAdapter(adapter: AdapterConfig): void;
|
|
59
|
+
prompts: Prompts;
|
|
60
|
+
setPrompts(prompts: Prompts): void;
|
|
61
|
+
markNudgeShown(sid: string, turnKey: string, tokenCount?: number): void;
|
|
62
|
+
nudgeShownFor(sid: string, turnKey: string): boolean;
|
|
63
|
+
/** tokenCount at the last actual nudge injection for this turn, for growth-aware re-inject (issue #269). */
|
|
64
|
+
nudgeShownTokensFor(sid: string, turnKey: string): number | undefined;
|
|
65
|
+
/** Clears the token-count stamps recorded by markNudgeShown — used on a token-scale flip (issue #267) so the same-turn re-inject floor (#269 / PR #316) is not computed against an old-scale stamp. */
|
|
66
|
+
clearNudgeTokenStamps(sid: string): void;
|
|
67
|
+
/** Like the nudgeShown* pair but tracks the persisted display-only session
|
|
68
|
+
* entry (issue #326): at most one record per user turn even when the
|
|
69
|
+
* emergency nudge re-injects on every LLM call. */
|
|
70
|
+
markNudgeRecorded(sid: string, turnKey: string): void;
|
|
71
|
+
nudgeRecordedFor(sid: string, turnKey: string): boolean;
|
|
72
|
+
/** Process compress toolResults for the CURRENT user turn only (the caller
|
|
73
|
+
* scopes the list — see collectCompressOutcomes in src/index.ts); idempotent
|
|
74
|
+
* per toolCallId. turnKey MUST be the stable persisted-boundary key
|
|
75
|
+
* (retryBreakerKey) — a key derived from live-merged entries churns under
|
|
76
|
+
* fork hosts and resets the counter mid-episode (#453). Outcome classes:
|
|
77
|
+
* isError or noop (0-block panel) → failure (count++), success panel
|
|
78
|
+
* (>= 1 block) → reset, other non-error text → neutral (count unchanged).
|
|
79
|
+
* Returns the failure count and whether the cap was just reached. */
|
|
80
|
+
noteCompressOutcomes(sid: string, turnKey: string, outcomes: ReadonlyArray<{
|
|
81
|
+
toolCallId: string;
|
|
82
|
+
isError: boolean;
|
|
83
|
+
success: boolean;
|
|
84
|
+
noop?: boolean;
|
|
85
|
+
}>): {
|
|
86
|
+
count: number;
|
|
87
|
+
cappedNow: boolean;
|
|
88
|
+
};
|
|
89
|
+
/** True when this turn already burned MAX_COMPRESS_ATTEMPTS failed/no-op
|
|
90
|
+
* compress calls — used to stop re-injecting the (dedup-exempt) emergency
|
|
91
|
+
* nudge that would otherwise keep looping no-op compressions (issue #6). */
|
|
92
|
+
compressRetryCappedFor(sid: string, turnKey: string): boolean;
|
|
93
|
+
clearNudgeTracking(sid: string): void;
|
|
94
|
+
clearCompressRetryTracking(sid: string): void;
|
|
95
|
+
liveContextLimit(ctx: ExtensionContext): number;
|
|
96
|
+
configFor(ctx: ExtensionContext): Config;
|
|
97
|
+
/** [#336] Effective compress.reasoning drop settings for the active model
|
|
98
|
+
* (three-level merge + defaults). Feeds the request-time pass in the
|
|
99
|
+
* context transform. */
|
|
100
|
+
reasoningDropFor(ctx: ExtensionContext): Required<CompressReasoningConfig>;
|
|
101
|
+
/** Effective historical-image strip policy for the active model (issue #321).
|
|
102
|
+
* Host-side policy — deliberately NOT part of the kernel Config object. */
|
|
103
|
+
stripImagesFor(ctx: ExtensionContext): {
|
|
104
|
+
enabled: boolean;
|
|
105
|
+
keepRecent: number;
|
|
106
|
+
};
|
|
107
|
+
/** Re-read ~/.<dir>/acp.json + <cwd>/<dir>/acp.json and re-derive the adapter
|
|
108
|
+
* config when the contents change. Cheap no-op when unchanged. Called at
|
|
109
|
+
* session_start and on every context event so config edits apply live. */
|
|
110
|
+
reloadConfig(cwd: string): Promise<void>;
|
|
111
|
+
stateFor(ctx: ExtensionContext, liveMessages?: AgentMessage[]): Promise<{
|
|
112
|
+
state: CompressionState;
|
|
113
|
+
coreMessages: ReturnType<typeof entriesToCoreMessages>;
|
|
114
|
+
entries: SessionEntry[];
|
|
115
|
+
}>;
|
|
116
|
+
/** Record the EXACT sent view measured off the real processTurn output for
|
|
117
|
+
* this turn (issue #561): `viewTokens` counts turn.messages (the pruned,
|
|
118
|
+
* summary-injected projection that actually went on the wire), and `usable`
|
|
119
|
+
* marks whether that count is an honest view — a turn that ran with
|
|
120
|
+
* tokenCount at/above the truncate band may have truncated its output, so
|
|
121
|
+
* its post-truncation size under-reports and must not be trusted next turn. */
|
|
122
|
+
noteSentViewCount(sid: string, record: SentViewMeterRecord): void;
|
|
123
|
+
/** Last turn's measured sent view, or undefined on the first turn / after a
|
|
124
|
+
* signature change (blocks added or dropped, window re-centered). */
|
|
125
|
+
peekSentViewCount(sid: string): SentViewMeterRecord | undefined;
|
|
126
|
+
/** Drop a session's sent-view meter (session_shutdown). */
|
|
127
|
+
dropSentViewCount(sid: string): void;
|
|
128
|
+
/** Drop a session's incremental projection cache (session_shutdown): it
|
|
129
|
+
* holds the full projected CoreMessage[] for the session history. */
|
|
130
|
+
dropProjectionCache(sid: string): void;
|
|
131
|
+
save(state: CompressionState, ctx: ExtensionContext): Promise<void>;
|
|
132
|
+
/** #364 inline child sessions (same process, e.g. Prime RLM): derive the
|
|
133
|
+
* child's compression state from another session's. Inherits blocks /
|
|
134
|
+
* message refs / token snapshot so decompress + search_context keep working
|
|
135
|
+
* on inherited blocks; resets every rhythm ledger (nudge cadence, stats,
|
|
136
|
+
* absorb). One-time: writes a derivation marker into the child sidecar and
|
|
137
|
+
* refuses to run again; also refuses when the child already owns real
|
|
138
|
+
* (non-derived) blocks or when the parent has no blocks. Separate-process
|
|
139
|
+
* pi-native delegates must NOT call this — their parentSession header
|
|
140
|
+
* already inherits verbatim. Returns true when the child state was derived. */
|
|
141
|
+
deriveChildState(child: SessionRef, parent: SessionRef): Promise<boolean>;
|
|
142
|
+
acquireLock(sid: string): Promise<() => void>;
|
|
143
|
+
/** Per-session overflow self-heal state (learned window + armed emergency).
|
|
144
|
+
* Keyed by session id so concurrent sessions cannot share an episode. */
|
|
145
|
+
overflowFor(sid: string): OverflowEpisode;
|
|
146
|
+
/** Drop a session's overflow episode entirely (session_shutdown): releases
|
|
147
|
+
* the map entry so a long-lived process cycling through many sessions
|
|
148
|
+
* doesn't accumulate them. */
|
|
149
|
+
overflowDrop(sid: string): void;
|
|
150
|
+
/** Record a compress call whose every requested range is dead (refs stale or
|
|
151
|
+
* unknown — the kernel cannot create a block from it no matter what summary
|
|
152
|
+
* is written). Returns the failure count for this exact range fingerprint
|
|
153
|
+
* in this session (issue #250 loop breaker). */
|
|
154
|
+
noteDeadCompress(sid: string, fingerprint: string): number;
|
|
155
|
+
/** Drop a session's dead-range repeat tracking (a successful compress is
|
|
156
|
+
* progress — reset the #250 loop breaker for the next attempt round;
|
|
157
|
+
* session_shutdown for memory hygiene). Compress does NOT renumber refs. */
|
|
158
|
+
clearDeadCompress(sid: string): void;
|
|
159
|
+
/** Dedicated summary model ref (`session`, `provider/id`, bare id), or undefined. */
|
|
160
|
+
getCompressionModelRef(): string | undefined;
|
|
161
|
+
/** Persist the ref to the global acp.json (null clears) and apply it now. */
|
|
162
|
+
setCompressionModelRef(value: string | null): Promise<void>;
|
|
163
|
+
/** Exact transformed messages from the latest provider-bound context pass. */
|
|
164
|
+
setLastSentMessages(sid: string, messages: AgentMessage[]): void;
|
|
165
|
+
getLastSentMessages(sid: string): AgentMessage[] | null;
|
|
166
|
+
dropLastSentMessages(sid: string): void;
|
|
167
|
+
/** Record one turn's FRESH-anchor provider usage sample and report whether
|
|
168
|
+
* the recent window is stable enough to calibrate the internal estimate
|
|
169
|
+
* against it (issue #455): >=3 of the last 4 samples agree within a 25%
|
|
170
|
+
* spread. A jittering getContextUsage() must not become a moving cap, so
|
|
171
|
+
* unstable windows report false and the raw estimate stands. */
|
|
172
|
+
noteHostUsage(sid: string, tokens: number): boolean;
|
|
173
|
+
/** Drop a session's host-usage stability window (session_shutdown). */
|
|
174
|
+
dropHostUsageSamples(sid: string): void;
|
|
175
|
+
/** Track persistent >2x internal-vs-provider size disagreement (issue #455);
|
|
176
|
+
* returns true exactly once per episode — on the third consecutive
|
|
177
|
+
* divergent turn — so the caller warns once instead of every turn. A
|
|
178
|
+
* convergent turn ends the episode. */
|
|
179
|
+
noteSizeDivergence(sid: string, divergent: boolean): boolean;
|
|
180
|
+
/** Drop a session's size-divergence episode (session_shutdown). */
|
|
181
|
+
dropSizeDivergence(sid: string): void;
|
|
182
|
+
/** Track the kernel's terminal-escape signal (issue #464): returns true
|
|
183
|
+
* exactly once per episode — the first consecutive stuck fire — so the
|
|
184
|
+
* caller logs/notifies once instead of every turn. A non-escaping turn
|
|
185
|
+
* ends the episode. */
|
|
186
|
+
noteTerminalEscape(sid: string, active: boolean): boolean;
|
|
187
|
+
/** Drop a session's terminal-escape episode (session_shutdown). */
|
|
188
|
+
dropTerminalEscape(sid: string): void;
|
|
189
|
+
/** Track the kernel's truncation-skipped signal (issue #464): returns true
|
|
190
|
+
* exactly once per episode for low-frequency diagnostics. A turn that
|
|
191
|
+
* truncates (or skips nothing) ends the episode. */
|
|
192
|
+
noteTruncationSkipped(sid: string, active: boolean): boolean;
|
|
193
|
+
/** Drop a session's truncation-skipped episode (session_shutdown). */
|
|
194
|
+
dropTruncationSkipped(sid: string): void;
|
|
195
|
+
}
|
|
196
|
+
/** Max FAILED compress calls per user turn before the nudge circuit breaker engages. */
|
|
197
|
+
export declare const MAX_COMPRESS_ATTEMPTS = 3;
|
|
198
|
+
export declare function createRuntime(adapter: AdapterConfig): AcpRuntime;
|
|
199
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index — bridges pi's session log into acp-kernel's search.
|
|
3
|
+
*
|
|
4
|
+
* Builds SearchDoc[] from:
|
|
5
|
+
* 1. All compression blocks (active AND inactive) — via blockDocs()
|
|
6
|
+
* 2. Historical messages that compression folded into a block summary.
|
|
7
|
+
*
|
|
8
|
+
* Which messages are searchable? Those covered by SOME block's
|
|
9
|
+
* effectiveMessageIds — i.e. messages that were compressed into a summary and
|
|
10
|
+
* are no longer individually visible. Messages still live in context (not in
|
|
11
|
+
* any block) are skipped: the model can already see them.
|
|
12
|
+
*
|
|
13
|
+
* We deliberately do NOT use pi's buildContextEntries for the visible check:
|
|
14
|
+
* ACP prunes messages itself (no pi `compaction` entry is written), so pi
|
|
15
|
+
* reports ALL entries as in-context. The ACP state is the source of truth.
|
|
16
|
+
*/
|
|
17
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
18
|
+
import { type SearchDoc } from "acp-kernel";
|
|
19
|
+
import type { CompressionState } from "acp-kernel";
|
|
20
|
+
export declare function buildSearchDocs(ctx: ExtensionContext, state: CompressionState): SearchDoc[];
|