@prometheus-ai/agent-core 0.5.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/CHANGELOG.md +7 -0
- package/README.md +473 -0
- package/dist/types/agent-loop.d.ts +55 -0
- package/dist/types/agent.d.ts +331 -0
- package/dist/types/append-only-context.d.ts +113 -0
- package/dist/types/compaction/branch-summarization.d.ts +94 -0
- package/dist/types/compaction/compaction.d.ts +183 -0
- package/dist/types/compaction/entries.d.ts +103 -0
- package/dist/types/compaction/errors.d.ts +26 -0
- package/dist/types/compaction/index.d.ts +12 -0
- package/dist/types/compaction/messages.d.ts +61 -0
- package/dist/types/compaction/openai.d.ts +58 -0
- package/dist/types/compaction/pruning.d.ts +19 -0
- package/dist/types/compaction/shake.d.ts +82 -0
- package/dist/types/compaction/tool-protection.d.ts +17 -0
- package/dist/types/compaction/utils.d.ts +32 -0
- package/dist/types/compaction.d.ts +1 -0
- package/dist/types/harmony-leak.d.ts +118 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/proxy.d.ts +84 -0
- package/dist/types/run-collector.d.ts +196 -0
- package/dist/types/telemetry.d.ts +588 -0
- package/dist/types/thinking.d.ts +17 -0
- package/dist/types/types.d.ts +443 -0
- package/dist/types/utils/yield.d.ts +52 -0
- package/package.json +75 -0
- package/src/agent-loop.ts +1418 -0
- package/src/agent.ts +1236 -0
- package/src/append-only-context.ts +297 -0
- package/src/compaction/branch-summarization.ts +339 -0
- package/src/compaction/compaction.ts +1155 -0
- package/src/compaction/entries.ts +133 -0
- package/src/compaction/errors.ts +31 -0
- package/src/compaction/index.ts +13 -0
- package/src/compaction/messages.ts +212 -0
- package/src/compaction/openai.ts +552 -0
- package/src/compaction/prompts/auto-handoff-threshold-focus.md +1 -0
- package/src/compaction/prompts/branch-summary-context.md +5 -0
- package/src/compaction/prompts/branch-summary-preamble.md +2 -0
- package/src/compaction/prompts/branch-summary.md +30 -0
- package/src/compaction/prompts/compaction-short-summary.md +9 -0
- package/src/compaction/prompts/compaction-summary-context.md +5 -0
- package/src/compaction/prompts/compaction-summary.md +38 -0
- package/src/compaction/prompts/compaction-turn-prefix.md +17 -0
- package/src/compaction/prompts/compaction-update-summary.md +45 -0
- package/src/compaction/prompts/file-operations.md +10 -0
- package/src/compaction/prompts/handoff-document.md +49 -0
- package/src/compaction/prompts/summarization-system.md +3 -0
- package/src/compaction/pruning.ts +99 -0
- package/src/compaction/shake.ts +406 -0
- package/src/compaction/tool-protection.ts +55 -0
- package/src/compaction/utils.ts +185 -0
- package/src/compaction.ts +1 -0
- package/src/harmony-leak.ts +456 -0
- package/src/index.ts +21 -0
- package/src/proxy.ts +326 -0
- package/src/run-collector.ts +631 -0
- package/src/telemetry.ts +2020 -0
- package/src/thinking.ts +19 -0
- package/src/types.ts +505 -0
- package/src/utils/yield.ts +146 -0
|
@@ -0,0 +1,183 @@
|
|
|
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 MessageAttribution, type Model, type Usage } from "@prometheus-ai/ai";
|
|
8
|
+
import { type AgentTelemetry } from "../telemetry";
|
|
9
|
+
import { ThinkingLevel } from "../thinking";
|
|
10
|
+
import type { AgentMessage, AgentTool } 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" | "off";
|
|
34
|
+
thresholdPercent?: number;
|
|
35
|
+
thresholdTokens?: number;
|
|
36
|
+
reserveTokens: number;
|
|
37
|
+
keepRecentTokens: number;
|
|
38
|
+
autoContinue?: boolean;
|
|
39
|
+
remoteEnabled?: boolean;
|
|
40
|
+
remoteEndpoint?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
|
|
43
|
+
/**
|
|
44
|
+
* Calculate total context tokens from usage.
|
|
45
|
+
* Uses the native totalTokens field when available, falls back to computing from components.
|
|
46
|
+
*/
|
|
47
|
+
export declare function calculateContextTokens(usage: Usage): number;
|
|
48
|
+
export declare function calculatePromptTokens(usage: Usage): number;
|
|
49
|
+
/**
|
|
50
|
+
* Find the last non-aborted assistant message usage from session entries.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Effective reserve: at least 15% of context window or the configured floor, whichever is larger.
|
|
55
|
+
*/
|
|
56
|
+
export declare function effectiveReserveTokens(contextWindow: number, settings: CompactionSettings): number;
|
|
57
|
+
/**
|
|
58
|
+
* Check if compaction should trigger based on context usage.
|
|
59
|
+
*/
|
|
60
|
+
export declare function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean;
|
|
61
|
+
export declare function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number;
|
|
62
|
+
/**
|
|
63
|
+
* Estimate token count for a message using cl100k_base via the native
|
|
64
|
+
* tokenizer. This is not Claude's first-party tokenizer (Anthropic doesn't
|
|
65
|
+
* publish one) but is within ~5–10% across English/code text.
|
|
66
|
+
*/
|
|
67
|
+
export declare function estimateTokens(message: AgentMessage): number;
|
|
68
|
+
/**
|
|
69
|
+
* Find the user message (or bashExecution) that starts the turn containing the given entry index.
|
|
70
|
+
* Returns -1 if no turn start found before the index.
|
|
71
|
+
* BashExecutionMessage is treated like a user message for turn boundaries.
|
|
72
|
+
*/
|
|
73
|
+
export declare function findTurnStartIndex(entries: SessionEntry[], entryIndex: number, startIndex: number): number;
|
|
74
|
+
export interface CutPointResult {
|
|
75
|
+
/** Index of first entry to keep */
|
|
76
|
+
firstKeptEntryIndex: number;
|
|
77
|
+
/** Index of user message that starts the turn being split, or -1 if not splitting */
|
|
78
|
+
turnStartIndex: number;
|
|
79
|
+
/** Whether this cut splits a turn (cut point is not a user message) */
|
|
80
|
+
isSplitTurn: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Find the cut point in session entries that keeps approximately `keepRecentTokens`.
|
|
84
|
+
*
|
|
85
|
+
* Algorithm: Walk backwards from newest, accumulating estimated message sizes.
|
|
86
|
+
* Stop when we've accumulated >= keepRecentTokens. Cut at that point.
|
|
87
|
+
*
|
|
88
|
+
* Can cut at user OR assistant messages (never tool results). When cutting at an
|
|
89
|
+
* assistant message with tool calls, its tool results come after and will be kept.
|
|
90
|
+
*
|
|
91
|
+
* Returns CutPointResult with:
|
|
92
|
+
* - firstKeptEntryIndex: the entry index to start keeping from
|
|
93
|
+
* - turnStartIndex: if cutting mid-turn, the user message that started that turn
|
|
94
|
+
* - isSplitTurn: whether we're cutting in the middle of a turn
|
|
95
|
+
*
|
|
96
|
+
* Only considers entries between `startIndex` and `endIndex` (exclusive).
|
|
97
|
+
*/
|
|
98
|
+
export declare function findCutPoint(entries: SessionEntry[], startIndex: number, endIndex: number, keepRecentTokens: number): CutPointResult;
|
|
99
|
+
export declare const AUTO_HANDOFF_THRESHOLD_FOCUS: string;
|
|
100
|
+
/**
|
|
101
|
+
* Generate a summary of the conversation using the LLM.
|
|
102
|
+
* If previousSummary is provided, uses the update prompt to merge.
|
|
103
|
+
*/
|
|
104
|
+
export interface SummaryOptions {
|
|
105
|
+
promptOverride?: string;
|
|
106
|
+
extraContext?: string[];
|
|
107
|
+
remoteEndpoint?: string;
|
|
108
|
+
remoteInstructions?: string;
|
|
109
|
+
initiatorOverride?: MessageAttribution;
|
|
110
|
+
metadata?: Record<string, unknown>;
|
|
111
|
+
convertToLlm?: ConvertToLlm;
|
|
112
|
+
/**
|
|
113
|
+
* Optional telemetry handle. When provided, every LLM call emitted during
|
|
114
|
+
* compaction is wrapped in an OTEL chat span tagged with
|
|
115
|
+
* `prometheus.gen_ai.oneshot.kind` (`compaction_summary`, `compaction_short_summary`,
|
|
116
|
+
* or `compaction_turn_prefix`). `undefined` keeps the call paths zero-cost.
|
|
117
|
+
*/
|
|
118
|
+
telemetry?: AgentTelemetry;
|
|
119
|
+
/**
|
|
120
|
+
* Active session thinking level. Threaded from `agent-session.ts` so
|
|
121
|
+
* compaction honors the user's `/model` thinking selection instead of
|
|
122
|
+
* silently overriding it with `Effort.High` (the historical default).
|
|
123
|
+
* `undefined` / `ThinkingLevel.Inherit` falls back to that historical
|
|
124
|
+
* default; `ThinkingLevel.Off` omits reasoning entirely. See
|
|
125
|
+
* `resolveCompactionEffort` for the conversion contract.
|
|
126
|
+
*/
|
|
127
|
+
thinkingLevel?: ThinkingLevel;
|
|
128
|
+
}
|
|
129
|
+
export declare function generateSummary(currentMessages: AgentMessage[], model: Model, reserveTokens: number, apiKey: string, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, options?: SummaryOptions): Promise<string>;
|
|
130
|
+
export interface HandoffOptions {
|
|
131
|
+
/** Live agent system prompt — passed verbatim so providers hit the cached prefix. */
|
|
132
|
+
systemPrompt: string[];
|
|
133
|
+
/** Live agent tool list — same purpose. Forced to `toolChoice: "none"`. */
|
|
134
|
+
tools?: AgentTool<any>[];
|
|
135
|
+
customInstructions?: string;
|
|
136
|
+
convertToLlm?: ConvertToLlm;
|
|
137
|
+
initiatorOverride?: MessageAttribution;
|
|
138
|
+
metadata?: Record<string, unknown>;
|
|
139
|
+
/**
|
|
140
|
+
* Optional telemetry handle. When provided, the handoff LLM call is
|
|
141
|
+
* wrapped in an OTEL chat span tagged with `prometheus.gen_ai.oneshot.kind = "handoff"`.
|
|
142
|
+
*/
|
|
143
|
+
telemetry?: AgentTelemetry;
|
|
144
|
+
/**
|
|
145
|
+
* Active session thinking level. Threaded from `agent-session.ts` so
|
|
146
|
+
* handoff generation honors the user's `/model` thinking selection
|
|
147
|
+
* instead of silently overriding it with `Effort.High`. See
|
|
148
|
+
* `resolveCompactionEffort` for the conversion contract.
|
|
149
|
+
*/
|
|
150
|
+
thinkingLevel?: ThinkingLevel;
|
|
151
|
+
}
|
|
152
|
+
export declare function renderHandoffPrompt(customInstructions?: string): string;
|
|
153
|
+
export declare function generateHandoff(messages: AgentMessage[], model: Model, apiKey: string, options: HandoffOptions, signal?: AbortSignal): Promise<string>;
|
|
154
|
+
export interface CompactionPreparation {
|
|
155
|
+
/** UUID of first entry to keep */
|
|
156
|
+
firstKeptEntryId: string;
|
|
157
|
+
/** Messages that will be summarized and discarded */
|
|
158
|
+
messagesToSummarize: AgentMessage[];
|
|
159
|
+
/** Messages that will be turned into turn prefix summary (if splitting) */
|
|
160
|
+
turnPrefixMessages: AgentMessage[];
|
|
161
|
+
/** Messages kept in full after compaction (recent history) */
|
|
162
|
+
recentMessages: AgentMessage[];
|
|
163
|
+
/** Whether this is a split turn (cut point in middle of turn) */
|
|
164
|
+
isSplitTurn: boolean;
|
|
165
|
+
tokensBefore: number;
|
|
166
|
+
/** Summary from previous compaction, for iterative update */
|
|
167
|
+
previousSummary?: string;
|
|
168
|
+
/** Preserved opaque compaction payload from the previous compaction, if any. */
|
|
169
|
+
previousPreserveData?: Record<string, unknown>;
|
|
170
|
+
/** File operations extracted from messagesToSummarize */
|
|
171
|
+
fileOps: FileOperations;
|
|
172
|
+
/** Compaction settions from settings.jsonl */
|
|
173
|
+
settings: CompactionSettings;
|
|
174
|
+
}
|
|
175
|
+
export declare function prepareCompaction(pathEntries: SessionEntry[], settings: CompactionSettings): CompactionPreparation | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* Generate summaries for compaction using prepared data.
|
|
178
|
+
* Returns CompactionResult - SessionManager adds id/parentId when saving.
|
|
179
|
+
*
|
|
180
|
+
* @param preparation - Pre-calculated preparation from prepareCompaction()
|
|
181
|
+
* @param customInstructions - Optional custom focus for the summary
|
|
182
|
+
*/
|
|
183
|
+
export declare function compact(preparation: CompactionPreparation, model: Model, apiKey: string, customInstructions?: string, signal?: AbortSignal, options?: SummaryOptions): Promise<CompactionResult>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ImageContent, MessageAttribution, ServiceTier, TextContent } from "@prometheus-ai/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: ServiceTier | 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 TtsrInjectionEntry extends SessionEntryBase {
|
|
70
|
+
type: "ttsr_injection";
|
|
71
|
+
/** Names of rules that were injected */
|
|
72
|
+
injectedRules: string[];
|
|
73
|
+
}
|
|
74
|
+
export interface MCPToolSelectionEntry extends SessionEntryBase {
|
|
75
|
+
type: "mcp_tool_selection";
|
|
76
|
+
/** MCP tool names selected for visibility in discovery mode. */
|
|
77
|
+
selectedToolNames: string[];
|
|
78
|
+
}
|
|
79
|
+
export interface SessionInitEntry extends SessionEntryBase {
|
|
80
|
+
type: "session_init";
|
|
81
|
+
/** Full system prompt sent to the model */
|
|
82
|
+
systemPrompt: string;
|
|
83
|
+
/** Initial task/user message */
|
|
84
|
+
task: string;
|
|
85
|
+
/** Tools available to the agent */
|
|
86
|
+
tools: string[];
|
|
87
|
+
/** Output schema if structured output was requested */
|
|
88
|
+
outputSchema?: unknown;
|
|
89
|
+
}
|
|
90
|
+
export interface ModeChangeEntry extends SessionEntryBase {
|
|
91
|
+
type: "mode_change";
|
|
92
|
+
/** Current mode name, or "none" when exiting a mode */
|
|
93
|
+
mode: string;
|
|
94
|
+
/** Optional mode-specific data (e.g. plan file path) */
|
|
95
|
+
data?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
export interface CustomCompactionSessionEntries {
|
|
98
|
+
}
|
|
99
|
+
export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
|
|
100
|
+
export interface ReadonlySessionManager {
|
|
101
|
+
getBranch(leafId?: string | null): SessionEntry[];
|
|
102
|
+
getEntry(id: string): SessionEntry | undefined;
|
|
103
|
+
}
|
|
@@ -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,61 @@
|
|
|
1
|
+
import type { ImageContent, Message, MessageAttribution, ProviderPayload, TextContent } from "@prometheus-ai/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
|
+
timestamp: number;
|
|
37
|
+
}
|
|
38
|
+
export type CoreCompactionMessage = CustomMessage | HookMessage | BranchSummaryMessage | CompactionSummaryMessage;
|
|
39
|
+
declare module "../types" {
|
|
40
|
+
interface CustomAgentMessages {
|
|
41
|
+
custom: CustomMessage;
|
|
42
|
+
hookMessage: HookMessage;
|
|
43
|
+
branchSummary: BranchSummaryMessage;
|
|
44
|
+
compactionSummary: CompactionSummaryMessage;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export type ConvertToLlm = (messages: AgentMessage[]) => Message[];
|
|
48
|
+
export declare function renderBranchSummaryContext(summary: string): string;
|
|
49
|
+
export declare function renderCompactionSummaryContext(summary: string): string;
|
|
50
|
+
export declare function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage;
|
|
51
|
+
export declare function createCompactionSummaryMessage(summary: string, tokensBefore: number, timestamp: string, shortSummary?: string, providerPayload?: ProviderPayload): CompactionSummaryMessage;
|
|
52
|
+
export declare function createCustomMessage(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details: unknown | undefined, timestamp: string, attribution?: MessageAttribution): CustomMessage;
|
|
53
|
+
/**
|
|
54
|
+
* Default compaction-domain transformer.
|
|
55
|
+
*
|
|
56
|
+
* Embedders with their own app messages should pass a richer transformer through
|
|
57
|
+
* `SummaryOptions.convertToLlm`; this default intentionally preserves only the
|
|
58
|
+
* core LLM roles and the compaction messages owned by this package.
|
|
59
|
+
*/
|
|
60
|
+
export declare function defaultConvertToLlm(messages: AgentMessage[]): Message[];
|
|
61
|
+
export declare const convertToLlm: typeof defaultConvertToLlm;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote compaction utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provider-side conversation summarization endpoints. Two flavors:
|
|
5
|
+
*
|
|
6
|
+
* - **OpenAI remote compaction** (`/responses/compact`): preserves encrypted
|
|
7
|
+
* reasoning across compactions by submitting the full responses-API native
|
|
8
|
+
* history and storing the returned `compaction` / `compaction_summary`
|
|
9
|
+
* item in `preserveData` so future turns can replay the encrypted state.
|
|
10
|
+
* - **Generic remote compaction**: a thin POST helper for self-hosted
|
|
11
|
+
* summarization endpoints that accept `{ systemPrompt, prompt }` and reply
|
|
12
|
+
* with `{ summary, shortSummary? }`.
|
|
13
|
+
*/
|
|
14
|
+
import type { Message, Model } from "@prometheus-ai/ai/types";
|
|
15
|
+
export declare const OPENAI_REMOTE_COMPACTION_PRESERVE_KEY = "openaiRemoteCompaction";
|
|
16
|
+
export type OpenAiRemoteCompactionItem = {
|
|
17
|
+
type: "compaction" | "compaction_summary";
|
|
18
|
+
encrypted_content?: string;
|
|
19
|
+
summary?: string;
|
|
20
|
+
};
|
|
21
|
+
export interface OpenAiRemoteCompactionPreserveData {
|
|
22
|
+
provider?: string;
|
|
23
|
+
replacementHistory: Array<Record<string, unknown>>;
|
|
24
|
+
compactionItem: OpenAiRemoteCompactionItem;
|
|
25
|
+
}
|
|
26
|
+
export interface OpenAiRemoteCompactionRequest {
|
|
27
|
+
model: string;
|
|
28
|
+
input: Array<Record<string, unknown>>;
|
|
29
|
+
instructions: string;
|
|
30
|
+
}
|
|
31
|
+
export interface OpenAiRemoteCompactionResponse extends OpenAiRemoteCompactionPreserveData {
|
|
32
|
+
}
|
|
33
|
+
export interface RemoteCompactionRequest {
|
|
34
|
+
systemPrompt: string;
|
|
35
|
+
prompt: string;
|
|
36
|
+
}
|
|
37
|
+
export interface RemoteCompactionResponse {
|
|
38
|
+
summary: string;
|
|
39
|
+
shortSummary?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function shouldUseOpenAiRemoteCompaction(model: Model): boolean;
|
|
42
|
+
export declare function getPreservedOpenAiRemoteCompactionData(preserveData: Record<string, unknown> | undefined): OpenAiRemoteCompactionPreserveData | undefined;
|
|
43
|
+
export declare function withOpenAiRemoteCompactionPreserveData(preserveData: Record<string, unknown> | undefined, remoteCompaction: OpenAiRemoteCompactionPreserveData | undefined): Record<string, unknown> | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Build the OpenAI Responses-API native history array from LLM messages.
|
|
46
|
+
*
|
|
47
|
+
* Caller is responsible for converting any custom message types to
|
|
48
|
+
* `Message[]` first (e.g. via the agent's `convertToLlm`); this function
|
|
49
|
+
* operates purely on the LLM-domain shape.
|
|
50
|
+
*
|
|
51
|
+
* @param messages - LLM messages to encode.
|
|
52
|
+
* @param model - Target model (used for provider gating + tool-call id rules).
|
|
53
|
+
* @param previousReplacementHistory - History from a prior compaction whose
|
|
54
|
+
* encrypted reasoning we want to preserve.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildOpenAiNativeHistory(messages: Message[], model: Model, previousReplacementHistory?: Array<Record<string, unknown>>): Array<Record<string, unknown>>;
|
|
57
|
+
export declare function requestOpenAiRemoteCompaction(model: Model, apiKey: string, compactInput: Array<Record<string, unknown>>, instructions: string, signal?: AbortSignal): Promise<OpenAiRemoteCompactionResponse>;
|
|
58
|
+
export declare function requestRemoteCompaction(endpoint: string, request: RemoteCompactionRequest, signal?: AbortSignal): Promise<RemoteCompactionResponse>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool output pruning utilities for compaction.
|
|
3
|
+
*/
|
|
4
|
+
import type { SessionEntry } from "./entries";
|
|
5
|
+
import { type ProtectedToolMatcher } from "./tool-protection";
|
|
6
|
+
export interface PruneConfig {
|
|
7
|
+
/** Keep the most recent tool output tokens intact. */
|
|
8
|
+
protectTokens: number;
|
|
9
|
+
/** Only prune if total savings meets this threshold. */
|
|
10
|
+
minimumSavings: number;
|
|
11
|
+
/** Tool-result protection matchers. String entries protect every result from that tool; predicates may inspect the paired tool call. */
|
|
12
|
+
protectedTools: ProtectedToolMatcher[];
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_PRUNE_CONFIG: PruneConfig;
|
|
15
|
+
export interface PruneResult {
|
|
16
|
+
prunedCount: number;
|
|
17
|
+
tokensSaved: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function pruneToolOutputs(entries: SessionEntry[], config?: PruneConfig): PruneResult;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-reducing surgical compaction ("shake").
|
|
3
|
+
*
|
|
4
|
+
* `shake` drops heavy content out of the live context mechanically: whole
|
|
5
|
+
* tool-call results and large fenced/XML blocks are replaced with short
|
|
6
|
+
* placeholders. This module is the pure layer — region detection and in-place
|
|
7
|
+
* mutation only. Artifact offload, persistence, and provider-session teardown
|
|
8
|
+
* are orchestrated by the caller (`AgentSession.shake`).
|
|
9
|
+
*
|
|
10
|
+
* Layering mirrors `pruning.ts`: no I/O here.
|
|
11
|
+
*/
|
|
12
|
+
import type { CustomMessageEntry, SessionEntry, SessionMessageEntry } from "./entries";
|
|
13
|
+
import { type ProtectedToolMatcher } from "./tool-protection";
|
|
14
|
+
export interface ShakeConfig {
|
|
15
|
+
/** Keep the most recent context tokens (across all entries) intact. */
|
|
16
|
+
protectTokens: number;
|
|
17
|
+
/** Only shake when total estimated savings meets this threshold. */
|
|
18
|
+
minSavings: number;
|
|
19
|
+
/** Tool-result protection matchers. String entries protect every result from that tool; predicates may inspect the paired tool call. */
|
|
20
|
+
protectedTools: ProtectedToolMatcher[];
|
|
21
|
+
/** Minimum token size for a fenced/XML block to be eligible. */
|
|
22
|
+
fenceMinTokens: number;
|
|
23
|
+
}
|
|
24
|
+
/** Auto-shake config: protects the live tail, conservative thresholds. */
|
|
25
|
+
export declare const DEFAULT_SHAKE_CONFIG: ShakeConfig;
|
|
26
|
+
/** Manual `/shake`: aggressive — drops every eligible region across history. */
|
|
27
|
+
export declare const AGGRESSIVE_SHAKE_CONFIG: ShakeConfig;
|
|
28
|
+
/** A located eligible region. */
|
|
29
|
+
export interface ToolResultShakeRegion {
|
|
30
|
+
kind: "toolResult";
|
|
31
|
+
entry: SessionMessageEntry;
|
|
32
|
+
tokens: number;
|
|
33
|
+
originalText: string;
|
|
34
|
+
/** Human label for the offload doc (tool name). */
|
|
35
|
+
label: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BlockShakeRegion {
|
|
38
|
+
kind: "block";
|
|
39
|
+
entry: SessionMessageEntry | CustomMessageEntry;
|
|
40
|
+
/** Index into the content array, or -1 for string-form content. */
|
|
41
|
+
blockIndex: number;
|
|
42
|
+
/** Character offsets into the target text (start inclusive, end exclusive). */
|
|
43
|
+
start: number;
|
|
44
|
+
end: number;
|
|
45
|
+
tokens: number;
|
|
46
|
+
originalText: string;
|
|
47
|
+
/** Human label for the offload doc (role / customType). */
|
|
48
|
+
label: string;
|
|
49
|
+
}
|
|
50
|
+
export type ShakeRegion = ToolResultShakeRegion | BlockShakeRegion;
|
|
51
|
+
/**
|
|
52
|
+
* Pure detection: locate every eligible shake region on a branch.
|
|
53
|
+
*
|
|
54
|
+
* Walks the protect-recent window (most recent `protectTokens` of context is
|
|
55
|
+
* kept intact), collects whole tool-result messages (honoring `protectedTools`
|
|
56
|
+
* and skipping already-pruned results) and large fenced/XML blocks inside
|
|
57
|
+
* user/developer/assistant/custom messages. Returns regions in document order.
|
|
58
|
+
*
|
|
59
|
+
* `toolCall` blocks are never touched (tool-call/result pairing is preserved)
|
|
60
|
+
* and regions never span a message boundary. When the combined estimated
|
|
61
|
+
* savings is below `minSavings`, returns `[]` (no-op).
|
|
62
|
+
*/
|
|
63
|
+
export declare function collectShakeRegions(entries: SessionEntry[], config: ShakeConfig): ShakeRegion[];
|
|
64
|
+
/**
|
|
65
|
+
* Pure mutation: replace a single region's content in place.
|
|
66
|
+
*
|
|
67
|
+
* Tool-result: replaces the message content with the placeholder text and
|
|
68
|
+
* stamps `prunedAt`. Block: splices `replacement` over `[start, end)` of the
|
|
69
|
+
* target text block. When several block regions share one text block they MUST
|
|
70
|
+
* be applied highest-start-first so earlier offsets stay valid — use
|
|
71
|
+
* {@link applyShakeRegions}, which orders them correctly.
|
|
72
|
+
*/
|
|
73
|
+
export declare function applyShakeRegion(region: ShakeRegion, replacement: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Apply many regions at once. Block regions are applied highest-start-first so
|
|
76
|
+
* that splicing one region never shifts the offsets of another in the same text
|
|
77
|
+
* block; tool-result regions are independent.
|
|
78
|
+
*/
|
|
79
|
+
export declare function applyShakeRegions(items: Array<{
|
|
80
|
+
region: ShakeRegion;
|
|
81
|
+
replacement: string;
|
|
82
|
+
}>): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@prometheus-ai/ai";
|
|
2
|
+
import type { AgentToolCall } from "../types";
|
|
3
|
+
import type { SessionEntry } from "./entries";
|
|
4
|
+
export interface ProtectedToolContext {
|
|
5
|
+
readonly toolResult: ToolResultMessage;
|
|
6
|
+
readonly toolCall: AgentToolCall | undefined;
|
|
7
|
+
}
|
|
8
|
+
export type ProtectedToolMatcher = string | ((context: ProtectedToolContext) => boolean);
|
|
9
|
+
export declare function collectToolCallsById(entries: readonly SessionEntry[]): Map<string, AgentToolCall>;
|
|
10
|
+
/**
|
|
11
|
+
* Extract the `path` argument from a paired `read` tool call, when the result
|
|
12
|
+
* is a `read` result carrying a string path. Returns `undefined` otherwise.
|
|
13
|
+
* Shared primitive for read-targeted protection matchers (skills, plans, …).
|
|
14
|
+
*/
|
|
15
|
+
export declare function getReadToolPath({ toolResult, toolCall }: ProtectedToolContext): string | undefined;
|
|
16
|
+
export declare function isSkillReadToolResult(context: ProtectedToolContext): boolean;
|
|
17
|
+
export declare function isProtectedToolResult(toolResult: ToolResultMessage, toolCall: AgentToolCall | undefined, matchers: readonly ProtectedToolMatcher[]): boolean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for compaction and branch summarization.
|
|
3
|
+
*/
|
|
4
|
+
import type { Message } from "@prometheus-ai/ai";
|
|
5
|
+
import type { AgentMessage } from "../types";
|
|
6
|
+
export interface FileOperations {
|
|
7
|
+
read: Set<string>;
|
|
8
|
+
written: Set<string>;
|
|
9
|
+
edited: Set<string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createFileOps(): FileOperations;
|
|
12
|
+
/**
|
|
13
|
+
* Extract file operations from tool calls in an assistant message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractFileOpsFromMessage(message: AgentMessage, fileOps: FileOperations): void;
|
|
16
|
+
/**
|
|
17
|
+
* Compute final file lists from file operations.
|
|
18
|
+
* Returns readFiles (files only read, not modified) and modifiedFiles.
|
|
19
|
+
*/
|
|
20
|
+
export declare function computeFileLists(fileOps: FileOperations): {
|
|
21
|
+
readFiles: string[];
|
|
22
|
+
modifiedFiles: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function formatFileOperations(readFiles: string[], modifiedFiles: string[]): string;
|
|
25
|
+
export declare function upsertFileOperations(summary: string, readFiles: string[], modifiedFiles: string[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* Serialize LLM messages to text for summarization.
|
|
28
|
+
* This prevents the model from treating it as a conversation to continue.
|
|
29
|
+
* Call convertToLlm() first to handle custom message types.
|
|
30
|
+
*/
|
|
31
|
+
export declare function serializeConversation(messages: Message[]): string;
|
|
32
|
+
export declare const SUMMARIZATION_SYSTEM_PROMPT: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./compaction/index";
|