jeopi-agent-core 16.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +1016 -0
  2. package/README.md +473 -0
  3. package/dist/types/agent-loop.d.ts +66 -0
  4. package/dist/types/agent.d.ts +427 -0
  5. package/dist/types/append-only-context.d.ts +133 -0
  6. package/dist/types/compaction/branch-summarization.d.ts +101 -0
  7. package/dist/types/compaction/compaction-v2-streaming.d.ts +82 -0
  8. package/dist/types/compaction/compaction.d.ts +283 -0
  9. package/dist/types/compaction/entries.d.ts +110 -0
  10. package/dist/types/compaction/errors.d.ts +26 -0
  11. package/dist/types/compaction/index.d.ts +12 -0
  12. package/dist/types/compaction/messages.d.ts +77 -0
  13. package/dist/types/compaction/openai.d.ts +77 -0
  14. package/dist/types/compaction/pruning.d.ts +105 -0
  15. package/dist/types/compaction/shake.d.ts +92 -0
  16. package/dist/types/compaction/tool-protection.d.ts +17 -0
  17. package/dist/types/compaction/utils.d.ts +58 -0
  18. package/dist/types/compaction.d.ts +1 -0
  19. package/dist/types/index.d.ts +12 -0
  20. package/dist/types/proxy.d.ts +85 -0
  21. package/dist/types/replay-policy.d.ts +5 -0
  22. package/dist/types/run-collector.d.ts +196 -0
  23. package/dist/types/telemetry.d.ts +590 -0
  24. package/dist/types/thinking.d.ts +17 -0
  25. package/dist/types/tokenizer.d.ts +1 -0
  26. package/dist/types/types.d.ts +640 -0
  27. package/dist/types/utils/yield.d.ts +71 -0
  28. package/package.json +78 -0
  29. package/src/agent-loop.ts +2188 -0
  30. package/src/agent.ts +1457 -0
  31. package/src/append-only-context.ts +348 -0
  32. package/src/compaction/branch-summarization.ts +370 -0
  33. package/src/compaction/compaction-v2-streaming.ts +719 -0
  34. package/src/compaction/compaction.ts +1553 -0
  35. package/src/compaction/entries.ts +142 -0
  36. package/src/compaction/errors.ts +31 -0
  37. package/src/compaction/index.ts +13 -0
  38. package/src/compaction/messages.ts +237 -0
  39. package/src/compaction/openai.ts +581 -0
  40. package/src/compaction/prompts/auto-handoff-threshold-focus.md +1 -0
  41. package/src/compaction/prompts/branch-summary-context.md +5 -0
  42. package/src/compaction/prompts/branch-summary-preamble.md +2 -0
  43. package/src/compaction/prompts/branch-summary.md +30 -0
  44. package/src/compaction/prompts/compaction-short-summary.md +9 -0
  45. package/src/compaction/prompts/compaction-summary-context.md +5 -0
  46. package/src/compaction/prompts/compaction-summary.md +38 -0
  47. package/src/compaction/prompts/compaction-turn-prefix.md +17 -0
  48. package/src/compaction/prompts/compaction-update-summary.md +45 -0
  49. package/src/compaction/prompts/file-operations.md +5 -0
  50. package/src/compaction/prompts/handoff-document.md +49 -0
  51. package/src/compaction/prompts/snapcompact-archive-context.md +3 -0
  52. package/src/compaction/prompts/summarization-system.md +3 -0
  53. package/src/compaction/pruning.ts +424 -0
  54. package/src/compaction/shake.ts +429 -0
  55. package/src/compaction/tool-protection.ts +55 -0
  56. package/src/compaction/utils.ts +323 -0
  57. package/src/compaction.ts +1 -0
  58. package/src/index.ts +24 -0
  59. package/src/proxy.ts +376 -0
  60. package/src/replay-policy.ts +13 -0
  61. package/src/run-collector.ts +631 -0
  62. package/src/telemetry.ts +2034 -0
  63. package/src/thinking.ts +19 -0
  64. package/src/tokenizer.ts +17 -0
  65. package/src/types.ts +718 -0
  66. package/src/utils/yield.ts +183 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Remote Compaction V2: streaming Responses compaction.
3
+ *
4
+ * Mirrors Codex `core/src/compact_remote_v2.rs`: append a `compaction_trigger`
5
+ * input item to the normal Responses stream, require exactly one streamed
6
+ * compaction output item, then install retained real user messages plus that
7
+ * compaction item as replacement history.
8
+ */
9
+ import type { FetchImpl, Model } from "jeopi-ai";
10
+ /** Retained-message budget Codex uses after streamed V2 compaction. */
11
+ export declare const V2_RETAINED_MESSAGE_TOKEN_BUDGET = 64000;
12
+ /** Max retries for V2 streaming compaction on transient stream errors. */
13
+ export declare const V2_COMPACTION_MAX_RETRIES = 2;
14
+ /** Timeout for V2 streaming compaction (3 minutes, same as V1). */
15
+ export declare const V2_COMPACTION_TIMEOUT_MS = 180000;
16
+ /** Token usage reported by the streamed V2 Responses completion. */
17
+ export interface CompactionV2Usage {
18
+ inputTokens: number;
19
+ outputTokens: number;
20
+ totalTokens: number;
21
+ cachedInputTokens?: number;
22
+ reasoningOutputTokens?: number;
23
+ }
24
+ /** Request body fields needed for Responses-stream V2 compaction. */
25
+ export interface CompactionV2Request {
26
+ model: string;
27
+ input: unknown[];
28
+ instructions: string;
29
+ retainedMessageBudget: number;
30
+ tools?: unknown[];
31
+ /** Responses reasoning param (effort + summary), matching a normal turn; omitted for non-reasoning models. */
32
+ reasoning?: {
33
+ effort: string;
34
+ summary: string;
35
+ };
36
+ sessionId?: string;
37
+ promptCacheKey?: string;
38
+ }
39
+ /** Response collected from the V2 stream and converted into replacement history. */
40
+ export interface CompactionV2Response {
41
+ compactionItem: Record<string, unknown>;
42
+ replacementHistory: Array<Record<string, unknown>>;
43
+ usedTokens: number;
44
+ usage?: CompactionV2Usage;
45
+ retainedImageCount: number;
46
+ }
47
+ /** Resolve the streaming Responses endpoint for a V2-capable model. */
48
+ export declare function getCompactionV2Endpoint(model: Model): string | undefined;
49
+ /** Check whether a model can use streaming V2 compaction. */
50
+ export declare function shouldUseCompactionV2Streaming(model: Model): model is Model<"openai-responses" | "azure-openai-responses" | "openai-codex-responses">;
51
+ /** Clamp the retained-message budget to Codex's known-safe 64K ceiling. */
52
+ export declare function resolveCompactionV2RetainedMessageBudget(value: number | undefined): number;
53
+ /** Build a V2 streaming compaction request from Responses-native history. */
54
+ export declare function buildCompactionV2Request(model: Model, input: unknown[], instructions: string, options?: {
55
+ tools?: unknown[];
56
+ reasoning?: {
57
+ effort: string;
58
+ summary: string;
59
+ };
60
+ sessionId?: string;
61
+ promptCacheKey?: string;
62
+ retainedMessageBudget?: number;
63
+ }): CompactionV2Request;
64
+ /** Request V2 compaction over the normal OpenAI Responses streaming endpoint. */
65
+ export declare function requestCompactionV2Streaming(model: Model, apiKey: string, request: CompactionV2Request, signal?: AbortSignal, options?: {
66
+ fetch?: FetchImpl;
67
+ timeoutMs?: number;
68
+ retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
69
+ }): Promise<CompactionV2Response>;
70
+ /** Build Codex-style V2 replacement history from prompt input plus compaction output. */
71
+ export declare function buildCompactionV2ReplacementHistory(input: unknown[], compactionItem: Record<string, unknown>, retainedMessageBudget?: number): {
72
+ replacementHistory: Array<Record<string, unknown>>;
73
+ retainedImageCount: number;
74
+ };
75
+ /** Store V2 replacement history in the OpenAI remote-compaction preserve slot. */
76
+ export declare function storeCompactionV2PreserveData(response: CompactionV2Response, model: Model): Record<string, unknown>;
77
+ /** Retrieve preserved OpenAI replacement history that V2 can extend. */
78
+ export declare function getCompactionV2PreserveData(preserveData: Record<string, unknown> | undefined): {
79
+ provider: string;
80
+ replacementHistory: Array<Record<string, unknown>>;
81
+ usedTokens: number;
82
+ } | undefined;
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Context compaction for long sessions.
3
+ *
4
+ * Pure functions for compaction logic. The session manager handles I/O,
5
+ * and after compaction the session is reloaded.
6
+ */
7
+ import { type Api, type ApiKey, type AssistantMessage, type Context, type FetchImpl, type MessageAttribution, type Model, type SimpleStreamOptions, type Tool, type Usage } from "jeopi-ai";
8
+ import { type AgentTelemetry } from "../telemetry";
9
+ import { ThinkingLevel } from "../thinking";
10
+ import type { AgentMessage } from "../types";
11
+ import type { SessionEntry } from "./entries";
12
+ import { type ConvertToLlm } from "./messages";
13
+ import { type FileOperations } from "./utils";
14
+ /** Details stored in CompactionEntry.details for file tracking */
15
+ export interface CompactionDetails {
16
+ readFiles: string[];
17
+ modifiedFiles: string[];
18
+ }
19
+ /** Result from compact() - SessionManager adds uuid/parentUuid when saving */
20
+ export interface CompactionResult<T = unknown> {
21
+ summary: string;
22
+ /** Short PR-style summary for display purposes. */
23
+ shortSummary?: string;
24
+ firstKeptEntryId: string;
25
+ tokensBefore: number;
26
+ /** Hook-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
27
+ details?: T;
28
+ /** Hook-provided data to persist alongside compaction entry. */
29
+ preserveData?: Record<string, unknown>;
30
+ }
31
+ export interface CompactionSettings {
32
+ enabled: boolean;
33
+ strategy?: "context-full" | "handoff" | "shake" | "snapcompact" | "off";
34
+ thresholdPercent?: number;
35
+ thresholdTokens?: number;
36
+ midTurnEnabled?: boolean;
37
+ /**
38
+ * Tokens reserved below the context window for the next prompt + response.
39
+ *
40
+ * Leave unset to use {@link DEFAULT_RESERVE_TOKENS}; the unset state is the
41
+ * provenance signal that lets small-window recovery replace the default with
42
+ * a proportional reserve (see {@link resolveBudgetReserveTokens}). An
43
+ * explicit value — even one equal to the default — is always honored.
44
+ */
45
+ reserveTokens?: number;
46
+ keepRecentTokens: number;
47
+ autoContinue?: boolean;
48
+ remoteEnabled?: boolean;
49
+ remoteEndpoint?: string;
50
+ remoteStreamingV2Enabled?: boolean;
51
+ v2RetainedMessageBudget?: number;
52
+ }
53
+ /** Reserve applied when {@link CompactionSettings.reserveTokens} is unset. */
54
+ export declare const DEFAULT_RESERVE_TOKENS = 16384;
55
+ export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
56
+ /**
57
+ * Calculate total context tokens from usage.
58
+ * Uses the native totalTokens field when available, falls back to computing from components.
59
+ */
60
+ export declare function calculateContextTokens(usage: Usage): number;
61
+ export declare function calculatePromptTokens(usage: Usage): number;
62
+ /**
63
+ * Find the last non-aborted assistant message usage from session entries.
64
+ */
65
+ export declare function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined;
66
+ /**
67
+ * Effective reserve: at least 15% of context window or the configured floor
68
+ * (defaulting to {@link DEFAULT_RESERVE_TOKENS} when unset), whichever is larger.
69
+ */
70
+ export declare function effectiveReserveTokens(contextWindow: number, settings: CompactionSettings): number;
71
+ /**
72
+ * Reserve used when deciding whether a prompt still fits inside the model window.
73
+ *
74
+ * The default absolute reserve predates small bundled windows and can leave no
75
+ * practical budget there; recover a DEFAULTED reserve that is impossible for
76
+ * the window with the 15% proportional reserve (clamped to >= 1 so the derived
77
+ * threshold stays strictly below the window even for tiny test windows).
78
+ * Explicit valid reserves — including one that happens to equal the default —
79
+ * still win, because they intentionally shrink the usable prompt budget;
80
+ * provenance is carried by `settings.reserveTokens` being unset, never by
81
+ * comparing values against the default.
82
+ */
83
+ export declare function resolveBudgetReserveTokens(contextWindow: number, settings: CompactionSettings): number;
84
+ /**
85
+ * Check if compaction should trigger based on context usage.
86
+ */
87
+ export declare function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean;
88
+ /**
89
+ * Context tokens to feed the compaction decision, floored by a local estimate of
90
+ * the stored conversation.
91
+ *
92
+ * The provider-reported usage is normally ground truth, but a
93
+ * `before_provider_request` payload transform — a compression extension (e.g.
94
+ * Headroom), an obfuscator, or inline snapcompact — can shrink the request below
95
+ * the real stored conversation. The provider then reports deflated prompt
96
+ * tokens, so anchoring compaction purely on that usage lets the real history
97
+ * grow unbounded until it overflows and native compaction can no longer run.
98
+ * Flooring by the agent's own estimate of the stored conversation keeps the
99
+ * compaction trigger honest regardless of on-wire compression. (Display/cost
100
+ * accounting still uses the exact provider usage; only the compaction decision
101
+ * takes the floor.)
102
+ */
103
+ export declare function compactionContextTokens(providerContextTokens: number, storedConversationEstimate: number): number;
104
+ export declare function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number;
105
+ /**
106
+ * Estimate token count for a message using cl100k_base via the native
107
+ * tokenizer. This is not Claude's first-party tokenizer (Anthropic doesn't
108
+ * publish one) but is within ~5–10% across English/code text.
109
+ *
110
+ * `excludeEncryptedReasoning` drops opaque provider reasoning payloads
111
+ * (`thinkingSignature`, `redactedThinking`) from the estimate. Those are billed
112
+ * by the provider on replay, so the default counts them — but their *local*
113
+ * byte size can diverge wildly from what the provider charges, so the
114
+ * compaction floor (which only needs the reliably-countable, on-wire-compressible
115
+ * content) excludes them to avoid false triggers on thinking-heavy turns.
116
+ */
117
+ export declare function estimateTokens(message: AgentMessage, options?: {
118
+ excludeEncryptedReasoning?: boolean;
119
+ }): number;
120
+ /**
121
+ * Find the user message (or bashExecution) that starts the turn containing the given entry index.
122
+ * Returns -1 if no turn start found before the index.
123
+ * BashExecutionMessage is treated like a user message for turn boundaries.
124
+ */
125
+ export declare function findTurnStartIndex(entries: SessionEntry[], entryIndex: number, startIndex: number): number;
126
+ export interface CutPointResult {
127
+ /** Index of first entry to keep */
128
+ firstKeptEntryIndex: number;
129
+ /** Index of user message that starts the turn being split, or -1 if not splitting */
130
+ turnStartIndex: number;
131
+ /** Whether this cut splits a turn (cut point is not a user message) */
132
+ isSplitTurn: boolean;
133
+ }
134
+ /**
135
+ * Find the cut point in session entries that keeps approximately `keepRecentTokens`.
136
+ *
137
+ * Algorithm: Walk backwards from newest, accumulating estimated message sizes.
138
+ * Stop when we've accumulated >= keepRecentTokens. Cut at that point.
139
+ *
140
+ * Can cut at user OR assistant messages (never tool results). When cutting at an
141
+ * assistant message with tool calls, its tool results come after and will be kept.
142
+ *
143
+ * Returns CutPointResult with:
144
+ * - firstKeptEntryIndex: the entry index to start keeping from
145
+ * - turnStartIndex: if cutting mid-turn, the user message that started that turn
146
+ * - isSplitTurn: whether we're cutting in the middle of a turn
147
+ *
148
+ * Only considers entries between `startIndex` and `endIndex` (exclusive).
149
+ */
150
+ export declare function findCutPoint(entries: SessionEntry[], startIndex: number, endIndex: number, keepRecentTokens: number): CutPointResult;
151
+ export declare const AUTO_HANDOFF_THRESHOLD_FOCUS: string;
152
+ /**
153
+ * Generate a summary of the conversation using the LLM.
154
+ * If previousSummary is provided, uses the update prompt to merge.
155
+ */
156
+ export interface SummaryOptions {
157
+ promptOverride?: string;
158
+ extraContext?: string[];
159
+ remoteEndpoint?: string;
160
+ remoteInstructions?: string;
161
+ initiatorOverride?: MessageAttribution;
162
+ metadata?: Record<string, unknown>;
163
+ convertToLlm?: ConvertToLlm;
164
+ /**
165
+ * Optional telemetry handle. When provided, every LLM call emitted during
166
+ * compaction is wrapped in an OTEL chat span tagged with
167
+ * `pi.gen_ai.oneshot.kind` (`compaction_summary`, `compaction_short_summary`,
168
+ * or `compaction_turn_prefix`). `undefined` keeps the call paths zero-cost.
169
+ */
170
+ telemetry?: AgentTelemetry;
171
+ /**
172
+ * Active session thinking level. Threaded from `agent-session.ts` so
173
+ * compaction honors the user's `/model` thinking selection instead of
174
+ * silently overriding it with `Effort.High` (the historical default).
175
+ * `undefined` / `ThinkingLevel.Inherit` falls back to that historical
176
+ * default; `ThinkingLevel.Off` omits reasoning entirely. See
177
+ * `resolveCompactionEffort` for the conversion contract.
178
+ */
179
+ thinkingLevel?: ThinkingLevel;
180
+ /** Session routing key for remote compaction transports with sticky provider sessions. */
181
+ sessionId?: string;
182
+ /** Prompt-cache key for remote compaction transports that support provider prefix caching. */
183
+ promptCacheKey?: string;
184
+ /** Provider-visible tools for remote compaction transports that replay native tool history. */
185
+ tools?: Tool[];
186
+ /** Optional fetch implementation threaded into remote compaction calls. */
187
+ fetch?: FetchImpl;
188
+ /**
189
+ * Optional completion transport override for host-level request wrappers
190
+ * (e.g. the coding-agent provider-concurrency limiter). When provided,
191
+ * every local summarization oneshot (`generateSummary`,
192
+ * `generateTurnPrefixSummary`, `generateShortSummary`) routes through it
193
+ * instead of the default `completeSimple`, so cap policies enforced on
194
+ * the live agent turn also bracket compaction HTTP requests.
195
+ */
196
+ completeImpl?: <TApi extends Api>(model: Model<TApi>, ctx: Context, options: SimpleStreamOptions) => Promise<AssistantMessage>;
197
+ }
198
+ export declare function generateSummary(currentMessages: AgentMessage[], model: Model, reserveTokens: number, apiKey: ApiKey, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, options?: SummaryOptions): Promise<string>;
199
+ export interface HandoffOptions {
200
+ /** Live agent system prompt — passed verbatim so providers hit the cached prefix. */
201
+ systemPrompt: string[];
202
+ /** Live agent tool list — same purpose. Forced to `toolChoice: "none"`. */
203
+ tools?: Tool[];
204
+ customInstructions?: string;
205
+ convertToLlm?: ConvertToLlm;
206
+ initiatorOverride?: MessageAttribution;
207
+ metadata?: Record<string, unknown>;
208
+ /**
209
+ * Optional telemetry handle. When provided, the handoff LLM call is
210
+ * wrapped in an OTEL chat span tagged with `pi.gen_ai.oneshot.kind = "handoff"`.
211
+ */
212
+ telemetry?: AgentTelemetry;
213
+ /**
214
+ * Active session thinking level. Threaded from `agent-session.ts` so
215
+ * handoff generation honors the user's `/model` thinking selection
216
+ * instead of silently overriding it with `Effort.High`. See
217
+ * `resolveCompactionEffort` for the conversion contract.
218
+ */
219
+ thinkingLevel?: ThinkingLevel;
220
+ }
221
+ export declare function renderHandoffPrompt(customInstructions?: string): string;
222
+ export interface HandoffFromContextOptions {
223
+ /**
224
+ * Stream options mirrored from the live agent turn: `apiKey`, `signal`, the
225
+ * `sessionId`/`promptCacheKey` cache-routing pair, `serviceTier`, and the
226
+ * session's payload/response hooks. Sending the same routing + payload shape
227
+ * the main loop uses is what lets the handoff oneshot READ the provider
228
+ * prompt cache the live turn populated instead of cold-missing the whole
229
+ * prefix. `reasoning` and `toolChoice` are set internally and override
230
+ * anything provided here.
231
+ */
232
+ streamOptions: SimpleStreamOptions;
233
+ /** Optional completion transport override for host-level request wrappers. */
234
+ completeImpl?: <TApi extends Api>(model: Model<TApi>, ctx: Context, options: SimpleStreamOptions) => Promise<AssistantMessage>;
235
+ /** See {@link HandoffOptions.telemetry}. */
236
+ telemetry?: AgentTelemetry;
237
+ /** See {@link HandoffOptions.thinkingLevel}. */
238
+ thinkingLevel?: ThinkingLevel;
239
+ }
240
+ /**
241
+ * Run the handoff oneshot against a fully-built provider {@link Context}.
242
+ *
243
+ * The caller assembles `context` exactly like a live agent turn — same system
244
+ * prompt, normalized tools, transformed + obfuscated message history, with the
245
+ * trailing handoff-prompt message already appended — and supplies
246
+ * `streamOptions` that mirror the live turn's cache routing. That keeps the
247
+ * cache-preserving context construction in the host (which owns the transform
248
+ * pipeline) while this function centralizes the handoff request contract:
249
+ * `toolChoice: "none"`, clamped reasoning effort, oneshot telemetry, text-only
250
+ * extraction, and provider-error mapping.
251
+ */
252
+ export declare function generateHandoffFromContext(context: Context, model: Model, options: HandoffFromContextOptions): Promise<string>;
253
+ export declare function generateHandoff(messages: AgentMessage[], model: Model, apiKey: ApiKey, options: HandoffOptions, signal?: AbortSignal): Promise<string>;
254
+ export interface CompactionPreparation {
255
+ /** UUID of first entry to keep */
256
+ firstKeptEntryId: string;
257
+ /** Messages that will be summarized and discarded */
258
+ messagesToSummarize: AgentMessage[];
259
+ /** Messages that will be turned into turn prefix summary (if splitting) */
260
+ turnPrefixMessages: AgentMessage[];
261
+ /** Messages kept in full after compaction (recent history) */
262
+ recentMessages: AgentMessage[];
263
+ /** Whether this is a split turn (cut point in middle of turn) */
264
+ isSplitTurn: boolean;
265
+ tokensBefore: number;
266
+ /** Summary from previous compaction, for iterative update */
267
+ previousSummary?: string;
268
+ /** Preserved opaque compaction payload from the previous compaction, if any. */
269
+ previousPreserveData?: Record<string, unknown>;
270
+ /** File operations extracted from messagesToSummarize */
271
+ fileOps: FileOperations;
272
+ /** Compaction settions from settings.jsonl */
273
+ settings: CompactionSettings;
274
+ }
275
+ export declare function prepareCompaction(pathEntries: SessionEntry[], settings: CompactionSettings, compactionModels?: readonly Model[]): CompactionPreparation | undefined;
276
+ /**
277
+ * Generate summaries for compaction using prepared data.
278
+ * Returns CompactionResult - SessionManager adds id/parentId when saving.
279
+ *
280
+ * @param preparation - Pre-calculated preparation from prepareCompaction()
281
+ * @param customInstructions - Optional custom focus for the summary
282
+ */
283
+ export declare function compact(preparation: CompactionPreparation, model: Model, apiKey: ApiKey, customInstructions?: string, signal?: AbortSignal, options?: SummaryOptions): Promise<CompactionResult>;
@@ -0,0 +1,110 @@
1
+ import type { ImageContent, MessageAttribution, ServiceTierByFamily, TextContent } from "jeopi-ai";
2
+ import type { AgentMessage } from "../types";
3
+ export interface SessionEntryBase {
4
+ type: string;
5
+ id: string;
6
+ parentId: string | null;
7
+ timestamp: string;
8
+ }
9
+ export interface SessionMessageEntry extends SessionEntryBase {
10
+ type: "message";
11
+ message: AgentMessage;
12
+ }
13
+ export interface ThinkingLevelChangeEntry extends SessionEntryBase {
14
+ type: "thinking_level_change";
15
+ thinkingLevel?: string | null;
16
+ }
17
+ export interface ModelChangeEntry extends SessionEntryBase {
18
+ type: "model_change";
19
+ /** Model in "provider/modelId" format */
20
+ model: string;
21
+ /** Role: "default", "smol", "slow", etc. Undefined treated as "default" */
22
+ role?: string;
23
+ }
24
+ export interface ServiceTierChangeEntry extends SessionEntryBase {
25
+ type: "service_tier_change";
26
+ serviceTier: ServiceTierByFamily | null;
27
+ }
28
+ export interface CompactionEntry<T = unknown> extends SessionEntryBase {
29
+ type: "compaction";
30
+ summary: string;
31
+ shortSummary?: string;
32
+ firstKeptEntryId: string;
33
+ tokensBefore: number;
34
+ /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
35
+ details?: T;
36
+ /** Hook-provided data to persist across compaction */
37
+ preserveData?: Record<string, unknown>;
38
+ /** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
39
+ fromExtension?: boolean;
40
+ }
41
+ export interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {
42
+ type: "branch_summary";
43
+ fromId: string;
44
+ summary: string;
45
+ /** Extension-specific data (not sent to LLM) */
46
+ details?: T;
47
+ /** True if generated by an extension, false if pi-generated */
48
+ fromExtension?: boolean;
49
+ }
50
+ export interface CustomMessageEntry<T = unknown> extends SessionEntryBase {
51
+ type: "custom_message";
52
+ customType: string;
53
+ content: string | (TextContent | ImageContent)[];
54
+ details?: T;
55
+ display: boolean;
56
+ /** Who initiated this message for billing/attribution semantics. */
57
+ attribution?: MessageAttribution;
58
+ }
59
+ export interface CustomEntry<T = unknown> extends SessionEntryBase {
60
+ type: "custom";
61
+ customType: string;
62
+ data?: T;
63
+ }
64
+ export interface LabelEntry extends SessionEntryBase {
65
+ type: "label";
66
+ targetId: string;
67
+ label: string | undefined;
68
+ }
69
+ export interface TitleChangeEntry extends SessionEntryBase {
70
+ type: "title_change";
71
+ title: string;
72
+ previousTitle?: string;
73
+ source: "auto" | "user";
74
+ trigger?: string;
75
+ }
76
+ export interface TtsrInjectionEntry extends SessionEntryBase {
77
+ type: "ttsr_injection";
78
+ /** Names of rules that were injected */
79
+ injectedRules: string[];
80
+ }
81
+ export interface MCPToolSelectionEntry extends SessionEntryBase {
82
+ type: "mcp_tool_selection";
83
+ /** MCP tool names selected for visibility in discovery mode. */
84
+ selectedToolNames: string[];
85
+ }
86
+ export interface SessionInitEntry extends SessionEntryBase {
87
+ type: "session_init";
88
+ /** Full system prompt sent to the model */
89
+ systemPrompt: string;
90
+ /** Initial task/user message */
91
+ task: string;
92
+ /** Tools available to the agent */
93
+ tools: string[];
94
+ /** Output schema if structured output was requested */
95
+ outputSchema?: unknown;
96
+ }
97
+ export interface ModeChangeEntry extends SessionEntryBase {
98
+ type: "mode_change";
99
+ /** Current mode name, or "none" when exiting a mode */
100
+ mode: string;
101
+ /** Optional mode-specific data (e.g. plan file path) */
102
+ data?: Record<string, unknown>;
103
+ }
104
+ export interface CustomCompactionSessionEntries {
105
+ }
106
+ export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TitleChangeEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
107
+ export interface ReadonlySessionManager {
108
+ getBranch(leafId?: string | null): SessionEntry[];
109
+ getEntry(id: string): SessionEntry | undefined;
110
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Compaction error types.
3
+ *
4
+ * `CompactionCancelledError` is the canonical signal raised when a compaction
5
+ * is explicitly aborted — operator Esc, extension hook returning `cancel`,
6
+ * programmatic `session.abortCompaction()` call, or any other deliberate
7
+ * abort source. Downstream callers (e.g. `executeCompaction`) discriminate
8
+ * cancellation from other failures via `instanceof CompactionCancelledError`
9
+ * rather than introspecting error messages or `name` fields — the typed
10
+ * sentinel makes classification source-agnostic and refactor-stable.
11
+ */
12
+ export declare class CompactionCancelledError extends Error {
13
+ readonly name: "CompactionCancelledError";
14
+ constructor(message?: string);
15
+ }
16
+ /**
17
+ * Outcome of a compaction attempt, surfaced by `CommandController.executeCompaction`
18
+ * so callers (e.g. the plan-mode approval flow) can distinguish a deliberate abort
19
+ * from an unrelated failure.
20
+ *
21
+ * "ok" — compaction completed; transcript was summarized.
22
+ * "cancelled" — `CompactionCancelledError` was raised. Operator Esc, extension
23
+ * hook, programmatic abort — all source-agnostic.
24
+ * "failed" — any other rejection from `session.compact()`.
25
+ */
26
+ export type CompactionOutcome = "ok" | "cancelled" | "failed";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compaction and summarization utilities.
3
+ */
4
+ export * from "./branch-summarization";
5
+ export * from "./compaction";
6
+ export * from "./entries";
7
+ export * from "./errors";
8
+ export * from "./messages";
9
+ export * from "./openai";
10
+ export * from "./pruning";
11
+ export * from "./shake";
12
+ export * from "./utils";
@@ -0,0 +1,77 @@
1
+ import type { ImageContent, Message, MessageAttribution, ProviderPayload, TextContent } from "jeopi-ai";
2
+ import type { AgentMessage } from "../types";
3
+ export interface CustomMessage<T = unknown> {
4
+ role: "custom";
5
+ customType: string;
6
+ content: string | (TextContent | ImageContent)[];
7
+ display: boolean;
8
+ details?: T;
9
+ /** Who initiated this message for billing/attribution semantics. */
10
+ attribution?: MessageAttribution;
11
+ timestamp: number;
12
+ }
13
+ /** Legacy hook message type (pre-extensions). Kept for session migration. */
14
+ export interface HookMessage<T = unknown> {
15
+ role: "hookMessage";
16
+ customType: string;
17
+ content: string | (TextContent | ImageContent)[];
18
+ display: boolean;
19
+ details?: T;
20
+ /** Who initiated this message for billing/attribution semantics. */
21
+ attribution?: MessageAttribution;
22
+ timestamp: number;
23
+ }
24
+ export interface BranchSummaryMessage {
25
+ role: "branchSummary";
26
+ summary: string;
27
+ fromId: string;
28
+ timestamp: number;
29
+ }
30
+ export interface CompactionSummaryMessage {
31
+ role: "compactionSummary";
32
+ summary: string;
33
+ shortSummary?: string;
34
+ tokensBefore: number;
35
+ providerPayload?: ProviderPayload;
36
+ /** Runtime-only ordered archive blocks for snapcompact: old text region,
37
+ * imaged middle, then new text region. When present, `summary` is already
38
+ * the final lead-in text (no legacy wrapper applied). */
39
+ blocks?: (TextContent | ImageContent)[];
40
+ /** Snapcompact image blocks, kept for display counts / legacy consumers. */
41
+ images?: ImageContent[];
42
+ timestamp: number;
43
+ }
44
+ export type CoreCompactionMessage = CustomMessage | HookMessage | BranchSummaryMessage | CompactionSummaryMessage;
45
+ declare module "../types" {
46
+ interface CustomAgentMessages {
47
+ custom: CustomMessage;
48
+ hookMessage: HookMessage;
49
+ branchSummary: BranchSummaryMessage;
50
+ compactionSummary: CompactionSummaryMessage;
51
+ }
52
+ }
53
+ export type ConvertToLlm = (messages: AgentMessage[]) => Message[];
54
+ export declare function renderBranchSummaryContext(summary: string): string;
55
+ export declare function renderCompactionSummaryContext(summary: string): string;
56
+ export declare function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage;
57
+ export declare function createCompactionSummaryMessage(summary: string, tokensBefore: number, timestamp: string, shortSummary?: string, providerPayload?: ProviderPayload, images?: ImageContent[], blocks?: (TextContent | ImageContent)[]): CompactionSummaryMessage;
58
+ export declare function createCustomMessage(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details: unknown | undefined, timestamp: string, attribution?: MessageAttribution): CustomMessage;
59
+ /**
60
+ * Transform a single core-domain agent message to its LLM form; `undefined`
61
+ * drops it from the provider request.
62
+ *
63
+ * Single source of truth for the core roles (user/developer/assistant/
64
+ * toolResult) and the compaction messages owned by this package. Embedders
65
+ * with their own app messages (e.g. the coding agent) handle their custom
66
+ * roles and delegate every core role here — duplicating these cases is how
67
+ * snapcompact frames once silently fell off the provider request.
68
+ */
69
+ export declare function convertMessageToLlm(message: AgentMessage): Message | undefined;
70
+ /**
71
+ * Default compaction-domain transformer.
72
+ *
73
+ * Embedders with their own app messages should pass a richer transformer through
74
+ * `SummaryOptions.convertToLlm`; this default intentionally preserves only the
75
+ * core LLM roles and the compaction messages owned by this package.
76
+ */
77
+ export declare function defaultConvertToLlm(messages: AgentMessage[]): Message[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Remote compaction utilities.
3
+ *
4
+ * Provider-side conversation summarization endpoints. Three flavors:
5
+ *
6
+ * - **OpenAI remote compaction V2** (Responses streaming): appends a
7
+ * `compaction_trigger` input item to the normal stream and stores the returned
8
+ * `compaction` item with retained real user messages in `preserveData`.
9
+ * - **OpenAI remote compaction V1** (`/responses/compact`): preserves encrypted
10
+ * reasoning across compactions by submitting the full responses-API native
11
+ * history and storing the returned `compaction` / `compaction_summary`
12
+ * item in `preserveData` so future turns can replay the encrypted state.
13
+ * - **Generic remote compaction**: a thin POST helper for self-hosted
14
+ * summarization endpoints that accept `{ systemPrompt, prompt }` and reply
15
+ * with `{ summary, shortSummary? }`.
16
+ */
17
+ import type { FetchImpl, Message, Model } from "jeopi-ai/types";
18
+ export * from "./compaction-v2-streaming";
19
+ export declare const OPENAI_REMOTE_COMPACTION_PRESERVE_KEY = "openaiRemoteCompaction";
20
+ /**
21
+ * Hard ceiling on remote compaction HTTP requests. Unlike every provider
22
+ * stream (guarded by first-event/idle watchdogs in pi-ai), these are raw
23
+ * fetches awaiting one non-streamed JSON body — a connection silently dropped
24
+ * by a middlebox would otherwise hang the whole compaction pipeline forever
25
+ * (frozen "Auto context-full maintenance…" spinner, manual /compact queueing
26
+ * behind it). On timeout the caller falls back to local summarization.
27
+ */
28
+ export declare const REMOTE_COMPACTION_TIMEOUT_MS = 180000;
29
+ export type OpenAiRemoteCompactionItem = {
30
+ type: "compaction" | "compaction_summary";
31
+ encrypted_content?: string;
32
+ summary?: string;
33
+ };
34
+ export interface OpenAiRemoteCompactionPreserveData {
35
+ provider?: string;
36
+ replacementHistory: Array<Record<string, unknown>>;
37
+ compactionItem: OpenAiRemoteCompactionItem;
38
+ }
39
+ export interface OpenAiRemoteCompactionRequest {
40
+ model: string;
41
+ input: Array<Record<string, unknown>>;
42
+ instructions: string;
43
+ }
44
+ export interface OpenAiRemoteCompactionResponse extends OpenAiRemoteCompactionPreserveData {
45
+ }
46
+ export interface RemoteCompactionRequest {
47
+ systemPrompt: string;
48
+ prompt: string;
49
+ }
50
+ export interface RemoteCompactionResponse {
51
+ summary: string;
52
+ shortSummary?: string;
53
+ }
54
+ export declare function shouldUseOpenAiRemoteCompaction(model: Model): boolean;
55
+ export declare function getPreservedOpenAiRemoteCompactionData(preserveData: Record<string, unknown> | undefined): OpenAiRemoteCompactionPreserveData | undefined;
56
+ export declare function withOpenAiRemoteCompactionPreserveData(preserveData: Record<string, unknown> | undefined, remoteCompaction: OpenAiRemoteCompactionPreserveData | undefined): Record<string, unknown> | undefined;
57
+ /**
58
+ * Build the OpenAI Responses-API native history array from LLM messages.
59
+ *
60
+ * Caller is responsible for converting any custom message types to
61
+ * `Message[]` first (e.g. via the agent's `convertToLlm`); this function
62
+ * operates purely on the LLM-domain shape.
63
+ *
64
+ * @param messages - LLM messages to encode.
65
+ * @param model - Target model (used for provider gating + tool-call id rules).
66
+ * @param previousReplacementHistory - History from a prior compaction whose
67
+ * encrypted reasoning we want to preserve.
68
+ */
69
+ export declare function buildOpenAiNativeHistory(messages: Message[], model: Model, previousReplacementHistory?: Array<Record<string, unknown>>): Array<Record<string, unknown>>;
70
+ export declare function requestOpenAiRemoteCompaction(model: Model, apiKey: string, compactInput: Array<Record<string, unknown>>, instructions: string, signal?: AbortSignal, opts?: {
71
+ fetch?: FetchImpl;
72
+ timeoutMs?: number;
73
+ }): Promise<OpenAiRemoteCompactionResponse>;
74
+ export declare function requestRemoteCompaction(endpoint: string, request: RemoteCompactionRequest, signal?: AbortSignal, opts?: {
75
+ fetch?: FetchImpl;
76
+ timeoutMs?: number;
77
+ }): Promise<RemoteCompactionResponse>;