@polpo-ai/core 0.4.1 → 0.4.2
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/dist/__tests__/context-compaction-e2e.test.d.ts +2 -0
- package/dist/__tests__/context-compaction-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/context-compaction-e2e.test.js +281 -0
- package/dist/__tests__/context-compaction-e2e.test.js.map +1 -0
- package/dist/__tests__/context-compactor.test.d.ts +2 -0
- package/dist/__tests__/context-compactor.test.d.ts.map +1 -0
- package/dist/__tests__/context-compactor.test.js +338 -0
- package/dist/__tests__/context-compactor.test.js.map +1 -0
- package/dist/context-compactor.d.ts +90 -0
- package/dist/context-compactor.d.ts.map +1 -0
- package/dist/context-compactor.js +293 -0
- package/dist/context-compactor.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compaction-e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/context-compaction-e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Compaction E2E test — verifies compaction triggers and works
|
|
3
|
+
* with realistic message sequences that exceed the context window.
|
|
4
|
+
*
|
|
5
|
+
* Uses a tiny contextWindow (2000 tokens) so we can trigger compaction
|
|
6
|
+
* with just a few messages instead of needing 200K tokens.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi } from "vitest";
|
|
9
|
+
import { compactIfNeeded, estimateMessagesTokens, } from "../context-compactor.js";
|
|
10
|
+
// ── Helpers ──────────────────────────────────────────────
|
|
11
|
+
/** Create a user message */
|
|
12
|
+
function userMsg(text) {
|
|
13
|
+
return { role: "user", content: text, timestamp: Date.now() };
|
|
14
|
+
}
|
|
15
|
+
/** Create an assistant text message */
|
|
16
|
+
function assistantTextMsg(text) {
|
|
17
|
+
return {
|
|
18
|
+
role: "assistant",
|
|
19
|
+
content: [{ type: "text", text }],
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Create an assistant tool call message */
|
|
24
|
+
function assistantToolCallMsg(toolName, args) {
|
|
25
|
+
return {
|
|
26
|
+
role: "assistant",
|
|
27
|
+
content: [{ type: "toolCall", id: `call_${Math.random().toString(36).slice(2)}`, name: toolName, arguments: args }],
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Create a tool result message with large output */
|
|
32
|
+
function toolResultMsg(toolCallId, toolName, output) {
|
|
33
|
+
return {
|
|
34
|
+
role: "toolResult",
|
|
35
|
+
toolCallId,
|
|
36
|
+
toolName,
|
|
37
|
+
content: [{ type: "text", text: output }],
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Generate a large string of N tokens (approx) */
|
|
42
|
+
function generateLargeText(targetTokens) {
|
|
43
|
+
// 4 chars per token
|
|
44
|
+
return "x".repeat(targetTokens * 4);
|
|
45
|
+
}
|
|
46
|
+
/** Build a conversation with many tool calls that fills up context */
|
|
47
|
+
function buildLongConversation(numToolCalls, outputTokensEach) {
|
|
48
|
+
const messages = [
|
|
49
|
+
userMsg("Fix the authentication bug in the login page"),
|
|
50
|
+
];
|
|
51
|
+
for (let i = 0; i < numToolCalls; i++) {
|
|
52
|
+
const tc = assistantToolCallMsg("read_file", { path: `/src/file_${i}.ts` });
|
|
53
|
+
const toolCallId = tc.content[0].id;
|
|
54
|
+
messages.push(tc);
|
|
55
|
+
messages.push(toolResultMsg(toolCallId, "read_file", generateLargeText(outputTokensEach)));
|
|
56
|
+
}
|
|
57
|
+
// Final assistant response
|
|
58
|
+
messages.push(assistantTextMsg("I've reviewed all the files. Here's my analysis..."));
|
|
59
|
+
return messages;
|
|
60
|
+
}
|
|
61
|
+
// ── Tests ────────────────────────────────────────────────
|
|
62
|
+
describe("Context Compaction E2E", () => {
|
|
63
|
+
// Tiny context window so we can trigger compaction easily
|
|
64
|
+
const smallConfig = {
|
|
65
|
+
contextWindow: 2000, // 2K tokens
|
|
66
|
+
maxOutputTokens: 200, // 200 output tokens
|
|
67
|
+
// usable = 1800, trigger at 85% = 1530
|
|
68
|
+
};
|
|
69
|
+
const summarizeFn = vi.fn(async (_messages, _prompt) => {
|
|
70
|
+
return "Summary: The agent was fixing an auth bug. Read 10 files. Found the issue in auth.ts line 42. Modified login handler.";
|
|
71
|
+
});
|
|
72
|
+
it("does NOT compact when context is small", async () => {
|
|
73
|
+
const messages = [
|
|
74
|
+
userMsg("Hello"),
|
|
75
|
+
assistantTextMsg("Hi! How can I help?"),
|
|
76
|
+
];
|
|
77
|
+
const result = await compactIfNeeded({
|
|
78
|
+
systemPrompt: "You are an agent.",
|
|
79
|
+
messages,
|
|
80
|
+
config: smallConfig,
|
|
81
|
+
summarize: summarizeFn,
|
|
82
|
+
mode: "task",
|
|
83
|
+
});
|
|
84
|
+
expect(result.compacted).toBe(false);
|
|
85
|
+
expect(result.pruned).toBe(false);
|
|
86
|
+
expect(result.messages).toEqual(messages);
|
|
87
|
+
expect(summarizeFn).not.toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
it("prunes tool outputs when context exceeds threshold", async () => {
|
|
90
|
+
// Use a larger context window where pruning alone is enough
|
|
91
|
+
// 8000 token window, 800 output → usable 7200, trigger at 6120
|
|
92
|
+
// 15 tool calls * 500 tokens = 7500 → over trigger
|
|
93
|
+
// Pruning protects last 40K (all of them since total < 40K)
|
|
94
|
+
// We need pruneProtect to be smaller to actually see pruning
|
|
95
|
+
const pruneConfig = {
|
|
96
|
+
contextWindow: 8000,
|
|
97
|
+
maxOutputTokens: 800,
|
|
98
|
+
pruneProtect: 1500, // only protect last 1500 tokens
|
|
99
|
+
pruneMinimum: 500, // prune if we can reclaim 500+
|
|
100
|
+
};
|
|
101
|
+
const messages = buildLongConversation(15, 500);
|
|
102
|
+
const tokensBefore = estimateMessagesTokens(messages);
|
|
103
|
+
expect(tokensBefore).toBeGreaterThan(6120);
|
|
104
|
+
const pruneSummarize = vi.fn(async () => "Should not be called");
|
|
105
|
+
const result = await compactIfNeeded({
|
|
106
|
+
systemPrompt: "You are an agent.",
|
|
107
|
+
messages,
|
|
108
|
+
config: pruneConfig,
|
|
109
|
+
summarize: pruneSummarize,
|
|
110
|
+
mode: "task",
|
|
111
|
+
});
|
|
112
|
+
expect(result.compacted).toBe(true);
|
|
113
|
+
expect(result.pruned).toBe(true);
|
|
114
|
+
expect(result.tokensBefore).toBeGreaterThan(result.tokensAfter);
|
|
115
|
+
// Verify some tool outputs were pruned (contain placeholder text)
|
|
116
|
+
const allText = JSON.stringify(result.messages);
|
|
117
|
+
expect(allText).toContain("Output pruned");
|
|
118
|
+
});
|
|
119
|
+
it("calls summarize when pruning alone isn't enough", async () => {
|
|
120
|
+
// Many tool calls with large outputs — pruning won't bring it under target
|
|
121
|
+
const messages = buildLongConversation(20, 300);
|
|
122
|
+
const mockSummarize = vi.fn(async () => {
|
|
123
|
+
return "Summary: Fixed auth bug. Modified auth.ts and login.ts.";
|
|
124
|
+
});
|
|
125
|
+
const result = await compactIfNeeded({
|
|
126
|
+
systemPrompt: "You are an agent.",
|
|
127
|
+
messages,
|
|
128
|
+
config: smallConfig,
|
|
129
|
+
summarize: mockSummarize,
|
|
130
|
+
mode: "task",
|
|
131
|
+
});
|
|
132
|
+
expect(result.compacted).toBe(true);
|
|
133
|
+
expect(mockSummarize).toHaveBeenCalled();
|
|
134
|
+
expect(result.summary).toBeDefined();
|
|
135
|
+
expect(result.summary).toContain("Fixed auth bug");
|
|
136
|
+
// First message should be the summary injection
|
|
137
|
+
const firstMsg = result.messages[0];
|
|
138
|
+
expect(firstMsg.role).toBe("user");
|
|
139
|
+
expect(firstMsg.content).toContain("Previous context summary");
|
|
140
|
+
expect(firstMsg.content).toContain("Fixed auth bug");
|
|
141
|
+
// Token count should be significantly reduced
|
|
142
|
+
expect(result.tokensAfter).toBeLessThan(result.tokensBefore);
|
|
143
|
+
});
|
|
144
|
+
it("preserves recent messages after summarization", async () => {
|
|
145
|
+
const messages = buildLongConversation(20, 300);
|
|
146
|
+
const lastAssistantMsg = messages[messages.length - 1];
|
|
147
|
+
const mockSummarize = vi.fn(async () => "Summary of old conversation.");
|
|
148
|
+
const result = await compactIfNeeded({
|
|
149
|
+
systemPrompt: "You are an agent.",
|
|
150
|
+
messages,
|
|
151
|
+
config: smallConfig,
|
|
152
|
+
summarize: mockSummarize,
|
|
153
|
+
mode: "task",
|
|
154
|
+
});
|
|
155
|
+
// The last message (assistant analysis) should still be in the result
|
|
156
|
+
const resultTexts = result.messages.map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content));
|
|
157
|
+
const lastOriginalText = typeof lastAssistantMsg.content === "string"
|
|
158
|
+
? lastAssistantMsg.content
|
|
159
|
+
: JSON.stringify(lastAssistantMsg.content);
|
|
160
|
+
expect(resultTexts.some((t) => t.includes("reviewed all the files"))).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it("uses task prompt for task mode", async () => {
|
|
163
|
+
const messages = buildLongConversation(20, 300);
|
|
164
|
+
let capturedPrompt = "";
|
|
165
|
+
const mockSummarize = vi.fn(async (_msgs, prompt) => {
|
|
166
|
+
capturedPrompt = prompt;
|
|
167
|
+
return "Summary";
|
|
168
|
+
});
|
|
169
|
+
await compactIfNeeded({
|
|
170
|
+
systemPrompt: "You are an agent.",
|
|
171
|
+
messages,
|
|
172
|
+
config: smallConfig,
|
|
173
|
+
summarize: mockSummarize,
|
|
174
|
+
mode: "task",
|
|
175
|
+
});
|
|
176
|
+
expect(capturedPrompt).toContain("Goal");
|
|
177
|
+
expect(capturedPrompt).toContain("Progress");
|
|
178
|
+
expect(capturedPrompt).toContain("Files");
|
|
179
|
+
expect(capturedPrompt).toContain("Next Steps");
|
|
180
|
+
});
|
|
181
|
+
it("uses chat prompt for chat mode", async () => {
|
|
182
|
+
const messages = buildLongConversation(20, 300);
|
|
183
|
+
let capturedPrompt = "";
|
|
184
|
+
const mockSummarize = vi.fn(async (_msgs, prompt) => {
|
|
185
|
+
capturedPrompt = prompt;
|
|
186
|
+
return "Summary";
|
|
187
|
+
});
|
|
188
|
+
await compactIfNeeded({
|
|
189
|
+
systemPrompt: "You are an agent.",
|
|
190
|
+
messages,
|
|
191
|
+
config: smallConfig,
|
|
192
|
+
summarize: mockSummarize,
|
|
193
|
+
mode: "chat",
|
|
194
|
+
});
|
|
195
|
+
expect(capturedPrompt).toContain("preferences");
|
|
196
|
+
expect(capturedPrompt).toContain("decisions");
|
|
197
|
+
});
|
|
198
|
+
it("passes older messages to summarize, not recent ones", async () => {
|
|
199
|
+
const messages = buildLongConversation(20, 300);
|
|
200
|
+
let summarizedCount = 0;
|
|
201
|
+
const mockSummarize = vi.fn(async (msgs) => {
|
|
202
|
+
summarizedCount = msgs.length;
|
|
203
|
+
return "Summary";
|
|
204
|
+
});
|
|
205
|
+
const result = await compactIfNeeded({
|
|
206
|
+
systemPrompt: "You are an agent.",
|
|
207
|
+
messages,
|
|
208
|
+
config: smallConfig,
|
|
209
|
+
summarize: mockSummarize,
|
|
210
|
+
mode: "task",
|
|
211
|
+
});
|
|
212
|
+
// Should have summarized some but not all messages
|
|
213
|
+
expect(summarizedCount).toBeGreaterThan(0);
|
|
214
|
+
expect(summarizedCount).toBeLessThan(messages.length);
|
|
215
|
+
// Result should have fewer messages than original
|
|
216
|
+
expect(result.messages.length).toBeLessThan(messages.length);
|
|
217
|
+
});
|
|
218
|
+
it("handles conversation with only text (no tool calls)", async () => {
|
|
219
|
+
// Build a text-heavy conversation that exceeds context
|
|
220
|
+
const messages = [];
|
|
221
|
+
for (let i = 0; i < 30; i++) {
|
|
222
|
+
messages.push(userMsg(generateLargeText(50)));
|
|
223
|
+
messages.push(assistantTextMsg(generateLargeText(50)));
|
|
224
|
+
}
|
|
225
|
+
const mockSummarize = vi.fn(async () => "Summary of long text conversation.");
|
|
226
|
+
const result = await compactIfNeeded({
|
|
227
|
+
systemPrompt: "You are an agent.",
|
|
228
|
+
messages,
|
|
229
|
+
config: smallConfig,
|
|
230
|
+
summarize: mockSummarize,
|
|
231
|
+
mode: "chat",
|
|
232
|
+
});
|
|
233
|
+
expect(result.compacted).toBe(true);
|
|
234
|
+
// With no tool outputs to prune, should go straight to summarization
|
|
235
|
+
expect(mockSummarize).toHaveBeenCalled();
|
|
236
|
+
});
|
|
237
|
+
it("respects disabled flag", async () => {
|
|
238
|
+
const messages = buildLongConversation(20, 300);
|
|
239
|
+
const result = await compactIfNeeded({
|
|
240
|
+
systemPrompt: "You are an agent.",
|
|
241
|
+
messages,
|
|
242
|
+
config: { ...smallConfig, disabled: true },
|
|
243
|
+
summarize: summarizeFn,
|
|
244
|
+
mode: "task",
|
|
245
|
+
});
|
|
246
|
+
expect(result.compacted).toBe(false);
|
|
247
|
+
expect(result.messages).toEqual(messages);
|
|
248
|
+
});
|
|
249
|
+
it("multiple compaction cycles work correctly", async () => {
|
|
250
|
+
// Simulate: first compaction, then more messages, then second compaction
|
|
251
|
+
const messages1 = buildLongConversation(15, 300);
|
|
252
|
+
const mockSummarize = vi.fn(async () => "Summary round 1.");
|
|
253
|
+
const result1 = await compactIfNeeded({
|
|
254
|
+
systemPrompt: "You are an agent.",
|
|
255
|
+
messages: messages1,
|
|
256
|
+
config: smallConfig,
|
|
257
|
+
summarize: mockSummarize,
|
|
258
|
+
mode: "task",
|
|
259
|
+
});
|
|
260
|
+
expect(result1.compacted).toBe(true);
|
|
261
|
+
// Add more messages on top of compacted result
|
|
262
|
+
const extendedMessages = [
|
|
263
|
+
...result1.messages,
|
|
264
|
+
...buildLongConversation(10, 300).slice(1), // skip the initial user msg
|
|
265
|
+
];
|
|
266
|
+
mockSummarize.mockResolvedValueOnce("Summary round 2 (includes round 1).");
|
|
267
|
+
const result2 = await compactIfNeeded({
|
|
268
|
+
systemPrompt: "You are an agent.",
|
|
269
|
+
messages: extendedMessages,
|
|
270
|
+
config: smallConfig,
|
|
271
|
+
summarize: mockSummarize,
|
|
272
|
+
mode: "task",
|
|
273
|
+
});
|
|
274
|
+
expect(result2.compacted).toBe(true);
|
|
275
|
+
// The summary should be from round 2
|
|
276
|
+
if (result2.summary) {
|
|
277
|
+
expect(result2.summary).toContain("round 2");
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=context-compaction-e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compaction-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/context-compaction-e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compactor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/context-compactor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { estimateTokens, estimateMessagesTokens, shouldCompact, pruneToolOutputs, compactIfNeeded, getCompactionPrompt, PRUNE_PROTECT, PRUNE_MINIMUM, TRIGGER_THRESHOLD, TARGET_AFTER, } from "../context-compactor.js";
|
|
3
|
+
// ── estimateTokens ──────────────────────────────────────────────────────
|
|
4
|
+
describe("estimateTokens", () => {
|
|
5
|
+
it("returns 0 for empty string", () => {
|
|
6
|
+
expect(estimateTokens("")).toBe(0);
|
|
7
|
+
});
|
|
8
|
+
it('estimates "hello" as ~1-2 tokens', () => {
|
|
9
|
+
// "hello" = 5 chars => 5/4 = 1.25 => Math.round => 1
|
|
10
|
+
expect(estimateTokens("hello")).toBe(1);
|
|
11
|
+
});
|
|
12
|
+
it("estimates longer text proportionally", () => {
|
|
13
|
+
const text = "a".repeat(400);
|
|
14
|
+
expect(estimateTokens(text)).toBe(100);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
// ── estimateMessagesTokens ──────────────────────────────────────────────
|
|
18
|
+
describe("estimateMessagesTokens", () => {
|
|
19
|
+
it("estimates string content messages", () => {
|
|
20
|
+
const messages = [
|
|
21
|
+
{ role: "user", content: "Hello, how are you?" },
|
|
22
|
+
{ role: "assistant", content: "I am fine." },
|
|
23
|
+
];
|
|
24
|
+
const tokens = estimateMessagesTokens(messages);
|
|
25
|
+
// Each message: 4 (overhead) + text tokens
|
|
26
|
+
// "Hello, how are you?" = 19 chars => ~5 tokens => total 9
|
|
27
|
+
// "I am fine." = 10 chars => ~3 tokens => total 7
|
|
28
|
+
// Sum = 16
|
|
29
|
+
expect(tokens).toBeGreaterThan(0);
|
|
30
|
+
expect(tokens).toBe(4 + estimateTokens("Hello, how are you?") +
|
|
31
|
+
4 + estimateTokens("I am fine."));
|
|
32
|
+
});
|
|
33
|
+
it("estimates tool call messages", () => {
|
|
34
|
+
const messages = [
|
|
35
|
+
{
|
|
36
|
+
role: "assistant",
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "toolCall",
|
|
40
|
+
id: "call_1",
|
|
41
|
+
name: "readFile",
|
|
42
|
+
arguments: { path: "/tmp/test.txt" },
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
const tokens = estimateMessagesTokens(messages);
|
|
48
|
+
expect(tokens).toBeGreaterThan(4); // more than just overhead
|
|
49
|
+
});
|
|
50
|
+
it("estimates tool result messages", () => {
|
|
51
|
+
const messages = [
|
|
52
|
+
{
|
|
53
|
+
role: "toolResult",
|
|
54
|
+
content: [
|
|
55
|
+
{ type: "text", text: "File contents here: hello world" },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const tokens = estimateMessagesTokens(messages);
|
|
60
|
+
expect(tokens).toBe(4 + estimateTokens("File contents here: hello world"));
|
|
61
|
+
});
|
|
62
|
+
it("handles empty array", () => {
|
|
63
|
+
expect(estimateMessagesTokens([])).toBe(0);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
// ── shouldCompact ───────────────────────────────────────────────────────
|
|
67
|
+
describe("shouldCompact", () => {
|
|
68
|
+
const config = {
|
|
69
|
+
contextWindow: 100_000,
|
|
70
|
+
maxOutputTokens: 4_000,
|
|
71
|
+
};
|
|
72
|
+
it("returns false when below threshold", () => {
|
|
73
|
+
// usable = 96000, threshold = 81600 (85%)
|
|
74
|
+
expect(shouldCompact(config, 50_000)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
it("returns true when above threshold", () => {
|
|
77
|
+
// usable = 96000, threshold = 81600 (85%)
|
|
78
|
+
expect(shouldCompact(config, 85_000)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
it("returns true at exact threshold", () => {
|
|
81
|
+
const usable = config.contextWindow - config.maxOutputTokens;
|
|
82
|
+
const threshold = usable * TRIGGER_THRESHOLD;
|
|
83
|
+
expect(shouldCompact(config, threshold)).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
it("returns false when disabled", () => {
|
|
86
|
+
expect(shouldCompact({ ...config, disabled: true }, 999_999)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
it("uses custom triggerThreshold", () => {
|
|
89
|
+
const custom = {
|
|
90
|
+
...config,
|
|
91
|
+
triggerThreshold: 0.5,
|
|
92
|
+
};
|
|
93
|
+
// usable = 96000, threshold = 48000 (50%)
|
|
94
|
+
expect(shouldCompact(custom, 50_000)).toBe(true);
|
|
95
|
+
expect(shouldCompact(custom, 40_000)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// ── pruneToolOutputs ────────────────────────────────────────────────────
|
|
99
|
+
describe("pruneToolOutputs", () => {
|
|
100
|
+
const config = {
|
|
101
|
+
contextWindow: 200_000,
|
|
102
|
+
maxOutputTokens: 4_000,
|
|
103
|
+
};
|
|
104
|
+
it("returns unchanged messages when no tool outputs", () => {
|
|
105
|
+
const messages = [
|
|
106
|
+
{ role: "user", content: "hello" },
|
|
107
|
+
{ role: "assistant", content: "hi" },
|
|
108
|
+
];
|
|
109
|
+
const result = pruneToolOutputs(messages, config);
|
|
110
|
+
expect(result).toEqual(messages);
|
|
111
|
+
});
|
|
112
|
+
it("protects recent tool outputs", () => {
|
|
113
|
+
// Create a single tool result that fits within PRUNE_PROTECT
|
|
114
|
+
const smallOutput = "x".repeat(100); // ~25 tokens, well within 40K
|
|
115
|
+
const messages = [
|
|
116
|
+
{
|
|
117
|
+
role: "toolResult",
|
|
118
|
+
content: [{ type: "text", text: smallOutput }],
|
|
119
|
+
toolName: "readFile",
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
const result = pruneToolOutputs(messages, config);
|
|
123
|
+
// Should be unchanged since it's within the protected window
|
|
124
|
+
expect(result[0].content[0].text).toBe(smallOutput);
|
|
125
|
+
});
|
|
126
|
+
it("prunes old outputs beyond the protection window", () => {
|
|
127
|
+
// Create outputs that exceed PRUNE_PROTECT + PRUNE_MINIMUM
|
|
128
|
+
const bigOutput = "x".repeat(PRUNE_PROTECT * 4 + PRUNE_MINIMUM * 4 + 100);
|
|
129
|
+
// Oldest message — should be pruned
|
|
130
|
+
const messages = [
|
|
131
|
+
{
|
|
132
|
+
role: "toolResult",
|
|
133
|
+
content: [{ type: "text", text: bigOutput }],
|
|
134
|
+
toolName: "oldTool",
|
|
135
|
+
},
|
|
136
|
+
// Recent message — protected
|
|
137
|
+
{
|
|
138
|
+
role: "toolResult",
|
|
139
|
+
content: [{ type: "text", text: "y".repeat(PRUNE_PROTECT * 4) }],
|
|
140
|
+
toolName: "recentTool",
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
const result = pruneToolOutputs(messages, config);
|
|
144
|
+
// Old output should be pruned
|
|
145
|
+
expect(result[0].content[0].text).toContain("[Output pruned");
|
|
146
|
+
expect(result[0].content[0].text).toContain("Tool: oldTool");
|
|
147
|
+
// Recent output should be preserved
|
|
148
|
+
expect(result[1].content[0].text).toBe("y".repeat(PRUNE_PROTECT * 4));
|
|
149
|
+
});
|
|
150
|
+
it("does not prune if total prunable is under minimum", () => {
|
|
151
|
+
// Two small outputs — neither exceeds minimum for pruning
|
|
152
|
+
const smallOutput = "x".repeat(100);
|
|
153
|
+
const messages = [
|
|
154
|
+
{
|
|
155
|
+
role: "toolResult",
|
|
156
|
+
content: [{ type: "text", text: smallOutput }],
|
|
157
|
+
toolName: "tool1",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
role: "toolResult",
|
|
161
|
+
content: [{ type: "text", text: smallOutput }],
|
|
162
|
+
toolName: "tool2",
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
const result = pruneToolOutputs(messages, config);
|
|
166
|
+
// Both should be unchanged
|
|
167
|
+
expect(result[0].content[0].text).toBe(smallOutput);
|
|
168
|
+
expect(result[1].content[0].text).toBe(smallOutput);
|
|
169
|
+
});
|
|
170
|
+
it("does not mutate original messages", () => {
|
|
171
|
+
const bigOutput = "x".repeat((PRUNE_PROTECT + PRUNE_MINIMUM) * 4 + 400);
|
|
172
|
+
const messages = [
|
|
173
|
+
{
|
|
174
|
+
role: "toolResult",
|
|
175
|
+
content: [{ type: "text", text: bigOutput }],
|
|
176
|
+
toolName: "tool1",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
role: "toolResult",
|
|
180
|
+
content: [{ type: "text", text: "y".repeat(PRUNE_PROTECT * 4) }],
|
|
181
|
+
toolName: "tool2",
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
const originalFirstText = messages[0].content[0].text;
|
|
185
|
+
pruneToolOutputs(messages, config);
|
|
186
|
+
// Original should be untouched
|
|
187
|
+
expect(messages[0].content[0].text).toBe(originalFirstText);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
// ── compactIfNeeded ─────────────────────────────────────────────────────
|
|
191
|
+
describe("compactIfNeeded", () => {
|
|
192
|
+
const smallConfig = {
|
|
193
|
+
contextWindow: 1000,
|
|
194
|
+
maxOutputTokens: 100,
|
|
195
|
+
// usable = 900, trigger at 765 (85%), target after = 450 (50%)
|
|
196
|
+
};
|
|
197
|
+
const mockSummarize = vi.fn(async () => "Summary of previous context.");
|
|
198
|
+
function makeInput(overrides = {}) {
|
|
199
|
+
return {
|
|
200
|
+
systemPrompt: "You are a helpful assistant.",
|
|
201
|
+
messages: [
|
|
202
|
+
{ role: "user", content: "hello" },
|
|
203
|
+
{ role: "assistant", content: "hi there" },
|
|
204
|
+
],
|
|
205
|
+
config: smallConfig,
|
|
206
|
+
summarize: mockSummarize,
|
|
207
|
+
mode: "task",
|
|
208
|
+
...overrides,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
it("returns unchanged when under threshold", async () => {
|
|
212
|
+
const input = makeInput({
|
|
213
|
+
config: {
|
|
214
|
+
contextWindow: 1_000_000,
|
|
215
|
+
maxOutputTokens: 4_000,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const result = await compactIfNeeded(input);
|
|
219
|
+
expect(result.compacted).toBe(false);
|
|
220
|
+
expect(result.pruned).toBe(false);
|
|
221
|
+
expect(result.messages).toBe(input.messages); // same reference
|
|
222
|
+
expect(result.tokensBefore).toBe(result.tokensAfter);
|
|
223
|
+
});
|
|
224
|
+
it("returns unchanged when disabled", async () => {
|
|
225
|
+
const input = makeInput({
|
|
226
|
+
config: { ...smallConfig, disabled: true },
|
|
227
|
+
});
|
|
228
|
+
// Even with a tiny context window, disabled means no compaction
|
|
229
|
+
const result = await compactIfNeeded(input);
|
|
230
|
+
expect(result.compacted).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
it("prunes sufficiently without calling summarize", async () => {
|
|
233
|
+
const summarize = vi.fn(async () => "summary");
|
|
234
|
+
// Build messages that push over threshold due to large tool outputs,
|
|
235
|
+
// where pruning alone is enough to get under target.
|
|
236
|
+
//
|
|
237
|
+
// The pruning logic walks backwards (most recent first) and protects
|
|
238
|
+
// recent tool outputs up to pruneProtect tokens. Once that budget is
|
|
239
|
+
// filled, everything older is prunable.
|
|
240
|
+
//
|
|
241
|
+
// Config: contextWindow=2000, maxOutputTokens=100 => usable=1900
|
|
242
|
+
// trigger = 1615 (85%), target = 950 (50%)
|
|
243
|
+
const config = {
|
|
244
|
+
contextWindow: 2000,
|
|
245
|
+
maxOutputTokens: 100,
|
|
246
|
+
pruneProtect: 200, // protect 200 tokens of recent tool output
|
|
247
|
+
pruneMinimum: 50, // prune if at least 50 tokens reclaimable
|
|
248
|
+
};
|
|
249
|
+
// System prompt "You are a helpful assistant." = 28 chars = 7 tokens
|
|
250
|
+
// Msg "do something" = 12 chars = 3 + 4 overhead = 7
|
|
251
|
+
// Old tool result "x"*6000 = 6000 chars = 1500 + 4 overhead = 1504 tokens
|
|
252
|
+
// Recent tool result "y"*1000 = 1000 chars = 250 + 4 overhead = 254 tokens
|
|
253
|
+
// (250 > pruneProtect=200, so this alone fills the protect budget)
|
|
254
|
+
// Msg "done" = 4 chars = 1 + 4 overhead = 5
|
|
255
|
+
// Total = 7 + 7 + 1504 + 254 + 5 = 1777, well over trigger of 1615
|
|
256
|
+
//
|
|
257
|
+
// After pruning: old tool result becomes short placeholder (~15 tokens + overhead)
|
|
258
|
+
// Total after = 7 + 7 + ~19 + 254 + 5 = ~292, under target of 950
|
|
259
|
+
const messages = [
|
|
260
|
+
{ role: "user", content: "do something" },
|
|
261
|
+
{
|
|
262
|
+
role: "toolResult",
|
|
263
|
+
content: [{ type: "text", text: "x".repeat(6000) }],
|
|
264
|
+
toolName: "bigTool",
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
role: "toolResult",
|
|
268
|
+
content: [{ type: "text", text: "y".repeat(1000) }],
|
|
269
|
+
toolName: "recentTool",
|
|
270
|
+
},
|
|
271
|
+
{ role: "assistant", content: "done" },
|
|
272
|
+
];
|
|
273
|
+
const input = makeInput({ messages, config, summarize });
|
|
274
|
+
const result = await compactIfNeeded(input);
|
|
275
|
+
expect(result.pruned).toBe(true);
|
|
276
|
+
expect(result.compacted).toBe(true);
|
|
277
|
+
expect(result.tokensAfter).toBeLessThan(result.tokensBefore);
|
|
278
|
+
// Summarize should NOT have been called since pruning was sufficient
|
|
279
|
+
expect(summarize).not.toHaveBeenCalled();
|
|
280
|
+
// The old tool result should contain the placeholder
|
|
281
|
+
const prunedToolResult = result.messages[1];
|
|
282
|
+
expect(prunedToolResult.content[0].text).toContain("[Output pruned");
|
|
283
|
+
});
|
|
284
|
+
it("calls summarize when pruning is insufficient", async () => {
|
|
285
|
+
const summarize = vi.fn(async () => "Brief summary.");
|
|
286
|
+
// Very small context window forces summarization
|
|
287
|
+
const config = {
|
|
288
|
+
contextWindow: 200,
|
|
289
|
+
maxOutputTokens: 10,
|
|
290
|
+
// usable = 190, trigger = 161.5, target = 95
|
|
291
|
+
pruneProtect: 0,
|
|
292
|
+
pruneMinimum: 0,
|
|
293
|
+
};
|
|
294
|
+
// Generate enough text messages to exceed threshold
|
|
295
|
+
// Each message ~4 overhead + text tokens
|
|
296
|
+
const messages = [];
|
|
297
|
+
for (let i = 0; i < 30; i++) {
|
|
298
|
+
messages.push({ role: "user", content: `Question number ${i}: ${"context ".repeat(10)}` });
|
|
299
|
+
messages.push({ role: "assistant", content: `Answer number ${i}: ${"response ".repeat(10)}` });
|
|
300
|
+
}
|
|
301
|
+
const input = makeInput({ messages, config, summarize });
|
|
302
|
+
const result = await compactIfNeeded(input);
|
|
303
|
+
expect(result.compacted).toBe(true);
|
|
304
|
+
expect(summarize).toHaveBeenCalled();
|
|
305
|
+
expect(result.summary).toBe("Brief summary.");
|
|
306
|
+
// First message should be the summary injection
|
|
307
|
+
expect(result.messages[0].role).toBe("user");
|
|
308
|
+
expect(result.messages[0].content).toContain("[Previous context summary]");
|
|
309
|
+
expect(result.messages[0].content).toContain("Brief summary.");
|
|
310
|
+
expect(result.messages[0].content).toContain("[End summary — continue from here]");
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
// ── getCompactionPrompt ─────────────────────────────────────────────────
|
|
314
|
+
describe("getCompactionPrompt", () => {
|
|
315
|
+
it("returns task prompt for task mode", () => {
|
|
316
|
+
const prompt = getCompactionPrompt("task");
|
|
317
|
+
expect(prompt).toContain("Summarize the conversation for handoff");
|
|
318
|
+
expect(prompt).toContain("### Goal");
|
|
319
|
+
expect(prompt).toContain("### Progress");
|
|
320
|
+
expect(prompt).toContain("### Files Modified");
|
|
321
|
+
expect(prompt).toContain("### Next Steps");
|
|
322
|
+
});
|
|
323
|
+
it("returns chat prompt for chat mode", () => {
|
|
324
|
+
const prompt = getCompactionPrompt("chat");
|
|
325
|
+
expect(prompt).toContain("Summarize the conversation to preserve context");
|
|
326
|
+
expect(prompt).toContain("user preferences");
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
// ── Constants exported correctly ────────────────────────────────────────
|
|
330
|
+
describe("constants", () => {
|
|
331
|
+
it("exports expected values", () => {
|
|
332
|
+
expect(PRUNE_PROTECT).toBe(40_000);
|
|
333
|
+
expect(PRUNE_MINIMUM).toBe(20_000);
|
|
334
|
+
expect(TRIGGER_THRESHOLD).toBe(0.85);
|
|
335
|
+
expect(TARGET_AFTER).toBe(0.50);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
//# sourceMappingURL=context-compactor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compactor.test.js","sourceRoot":"","sources":["../../src/__tests__/context-compactor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Compaction — prevents context window overflow.
|
|
3
|
+
*
|
|
4
|
+
* Two-phase approach (same as OpenCode):
|
|
5
|
+
* Phase 1: Prune old tool outputs (no LLM call, free)
|
|
6
|
+
* Phase 2: LLM summarization if pruning isn't enough
|
|
7
|
+
*/
|
|
8
|
+
/** Estimate token count: ~4 chars per token (industry standard heuristic) */
|
|
9
|
+
export declare function estimateTokens(text: string): number;
|
|
10
|
+
/** Protect last 40K tokens of tool output from pruning */
|
|
11
|
+
export declare const PRUNE_PROTECT = 40000;
|
|
12
|
+
/** Only prune if we can reclaim at least 20K tokens */
|
|
13
|
+
export declare const PRUNE_MINIMUM = 20000;
|
|
14
|
+
/** Trigger compaction at 85% of usable context */
|
|
15
|
+
export declare const TRIGGER_THRESHOLD = 0.85;
|
|
16
|
+
/** Target 50% of usable context after compaction */
|
|
17
|
+
export declare const TARGET_AFTER = 0.5;
|
|
18
|
+
export interface CompactionConfig {
|
|
19
|
+
/** Model's context window size in tokens */
|
|
20
|
+
contextWindow: number;
|
|
21
|
+
/** Max output tokens the model can generate */
|
|
22
|
+
maxOutputTokens: number;
|
|
23
|
+
/** Trigger compaction at this % of usable context (default: 0.85) */
|
|
24
|
+
triggerThreshold?: number;
|
|
25
|
+
/** Tokens of recent tool output to protect from pruning (default: 40000) */
|
|
26
|
+
pruneProtect?: number;
|
|
27
|
+
/** Minimum tokens to reclaim before pruning (default: 20000) */
|
|
28
|
+
pruneMinimum?: number;
|
|
29
|
+
/** Disable auto-compaction entirely */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type SummarizeFn = (messages: any[], prompt: string) => Promise<string>;
|
|
33
|
+
/** Event emitted when compaction occurs */
|
|
34
|
+
export interface CompactionEvent {
|
|
35
|
+
/** What phase triggered: "prune" or "summarize" */
|
|
36
|
+
phase: "prune" | "summarize";
|
|
37
|
+
/** Token count before compaction */
|
|
38
|
+
tokensBefore: number;
|
|
39
|
+
/** Token count after compaction */
|
|
40
|
+
tokensAfter: number;
|
|
41
|
+
/** Tokens reclaimed */
|
|
42
|
+
tokensReclaimed: number;
|
|
43
|
+
/** Number of messages before */
|
|
44
|
+
messagesBefore: number;
|
|
45
|
+
/** Number of messages after */
|
|
46
|
+
messagesAfter: number;
|
|
47
|
+
/** Number of tool outputs pruned (phase 1) */
|
|
48
|
+
toolOutputsPruned?: number;
|
|
49
|
+
/** Summary text (phase 2 only) */
|
|
50
|
+
summary?: string;
|
|
51
|
+
/** Mode: task or chat */
|
|
52
|
+
mode: "task" | "chat";
|
|
53
|
+
}
|
|
54
|
+
export type OnCompactionFn = (event: CompactionEvent) => void;
|
|
55
|
+
export interface CompactionInput {
|
|
56
|
+
systemPrompt: string;
|
|
57
|
+
messages: any[];
|
|
58
|
+
tools?: any[];
|
|
59
|
+
config: CompactionConfig;
|
|
60
|
+
summarize: SummarizeFn;
|
|
61
|
+
mode: "task" | "chat";
|
|
62
|
+
/** Called when compaction occurs — use for logging, events, UI updates */
|
|
63
|
+
onCompaction?: OnCompactionFn;
|
|
64
|
+
}
|
|
65
|
+
export interface CompactionResult {
|
|
66
|
+
messages: any[];
|
|
67
|
+
compacted: boolean;
|
|
68
|
+
pruned: boolean;
|
|
69
|
+
summary?: string;
|
|
70
|
+
tokensBefore: number;
|
|
71
|
+
tokensAfter: number;
|
|
72
|
+
}
|
|
73
|
+
/** Get the appropriate compaction prompt for a given mode */
|
|
74
|
+
export declare function getCompactionPrompt(mode: "task" | "chat"): string;
|
|
75
|
+
/** Estimate total tokens across an array of messages */
|
|
76
|
+
export declare function estimateMessagesTokens(messages: any[]): number;
|
|
77
|
+
/** Check whether compaction should be triggered based on current token usage */
|
|
78
|
+
export declare function shouldCompact(config: CompactionConfig, currentTokens: number): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Walk backwards through messages and replace old tool result content with
|
|
81
|
+
* placeholders. Protects the most recent `pruneProtect` tokens of tool output.
|
|
82
|
+
* Only prunes if total prunable tokens >= `pruneMinimum`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function pruneToolOutputs(messages: any[], config: CompactionConfig): any[];
|
|
85
|
+
/**
|
|
86
|
+
* Main entry point. Checks if compaction is needed, tries pruning first,
|
|
87
|
+
* then falls back to LLM summarization if necessary.
|
|
88
|
+
*/
|
|
89
|
+
export declare function compactIfNeeded(input: CompactionInput): Promise<CompactionResult>;
|
|
90
|
+
//# sourceMappingURL=context-compactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compactor.d.ts","sourceRoot":"","sources":["../src/context-compactor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAID,0DAA0D;AAC1D,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC,uDAAuD;AACvD,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC,kDAAkD;AAClD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,oDAAoD;AACpD,eAAO,MAAM,YAAY,MAAO,CAAC;AAIjC,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE/E,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,KAAK,EAAE,OAAO,GAAG,WAAW,CAAC;IAC7B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,WAAW,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,cAAc,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AA+BD,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEjE;AAID,wDAAwD;AACxD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM,CAM9D;AAkCD,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAKtF;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,GAAG,EAAE,EACf,MAAM,EAAE,gBAAgB,GACvB,GAAG,EAAE,CAqEP;AAID;;;GAGG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsHvF"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Compaction — prevents context window overflow.
|
|
3
|
+
*
|
|
4
|
+
* Two-phase approach (same as OpenCode):
|
|
5
|
+
* Phase 1: Prune old tool outputs (no LLM call, free)
|
|
6
|
+
* Phase 2: LLM summarization if pruning isn't enough
|
|
7
|
+
*/
|
|
8
|
+
// ── Token estimation ────────────────────────────────────────────────────
|
|
9
|
+
/** Estimate token count: ~4 chars per token (industry standard heuristic) */
|
|
10
|
+
export function estimateTokens(text) {
|
|
11
|
+
return Math.round(text.length / 4);
|
|
12
|
+
}
|
|
13
|
+
// ── Constants (same as OpenCode) ────────────────────────────────────────
|
|
14
|
+
/** Protect last 40K tokens of tool output from pruning */
|
|
15
|
+
export const PRUNE_PROTECT = 40_000;
|
|
16
|
+
/** Only prune if we can reclaim at least 20K tokens */
|
|
17
|
+
export const PRUNE_MINIMUM = 20_000;
|
|
18
|
+
/** Trigger compaction at 85% of usable context */
|
|
19
|
+
export const TRIGGER_THRESHOLD = 0.85;
|
|
20
|
+
/** Target 50% of usable context after compaction */
|
|
21
|
+
export const TARGET_AFTER = 0.50;
|
|
22
|
+
// ── Summarization prompts ───────────────────────────────────────────────
|
|
23
|
+
const TASK_COMPACTION_PROMPT = `Summarize the conversation for handoff to a continuation agent.
|
|
24
|
+
|
|
25
|
+
## Required sections:
|
|
26
|
+
### Goal
|
|
27
|
+
What is the task trying to accomplish?
|
|
28
|
+
|
|
29
|
+
### Progress
|
|
30
|
+
- Completed: what's done
|
|
31
|
+
- In progress: what was interrupted
|
|
32
|
+
- Remaining: what's left
|
|
33
|
+
|
|
34
|
+
### Key Decisions & Discoveries
|
|
35
|
+
Technical decisions, constraints found, approaches tried and failed
|
|
36
|
+
|
|
37
|
+
### Files Modified
|
|
38
|
+
List of files read, created, or edited with brief description
|
|
39
|
+
|
|
40
|
+
### Next Steps
|
|
41
|
+
Specific actions to take next
|
|
42
|
+
|
|
43
|
+
Be precise and concise. Preserve file paths, function names, and error messages exactly.`;
|
|
44
|
+
const CHAT_COMPACTION_PROMPT = `Summarize the conversation to preserve context for continuation.
|
|
45
|
+
Focus on: user preferences, decisions made, questions asked, key facts shared.
|
|
46
|
+
Preserve any code snippets, file paths, or technical details mentioned.
|
|
47
|
+
Be precise and concise.`;
|
|
48
|
+
/** Get the appropriate compaction prompt for a given mode */
|
|
49
|
+
export function getCompactionPrompt(mode) {
|
|
50
|
+
return mode === "task" ? TASK_COMPACTION_PROMPT : CHAT_COMPACTION_PROMPT;
|
|
51
|
+
}
|
|
52
|
+
// ── Token estimation for messages ───────────────────────────────────────
|
|
53
|
+
/** Estimate total tokens across an array of messages */
|
|
54
|
+
export function estimateMessagesTokens(messages) {
|
|
55
|
+
let total = 0;
|
|
56
|
+
for (const msg of messages) {
|
|
57
|
+
total += estimateMessageTokens(msg);
|
|
58
|
+
}
|
|
59
|
+
return total;
|
|
60
|
+
}
|
|
61
|
+
function estimateMessageTokens(msg) {
|
|
62
|
+
if (!msg)
|
|
63
|
+
return 0;
|
|
64
|
+
// Role overhead (~4 tokens for role + formatting)
|
|
65
|
+
let tokens = 4;
|
|
66
|
+
const content = msg.content;
|
|
67
|
+
if (typeof content === "string") {
|
|
68
|
+
tokens += estimateTokens(content);
|
|
69
|
+
}
|
|
70
|
+
else if (Array.isArray(content)) {
|
|
71
|
+
for (const block of content) {
|
|
72
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
73
|
+
tokens += estimateTokens(block.text);
|
|
74
|
+
}
|
|
75
|
+
else if (block.type === "toolCall") {
|
|
76
|
+
// Tool call: name + stringified arguments
|
|
77
|
+
if (block.name)
|
|
78
|
+
tokens += estimateTokens(block.name);
|
|
79
|
+
if (block.arguments !== undefined) {
|
|
80
|
+
const args = typeof block.arguments === "string"
|
|
81
|
+
? block.arguments
|
|
82
|
+
: JSON.stringify(block.arguments);
|
|
83
|
+
tokens += estimateTokens(args);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return tokens;
|
|
89
|
+
}
|
|
90
|
+
// ── Threshold check ─────────────────────────────────────────────────────
|
|
91
|
+
/** Check whether compaction should be triggered based on current token usage */
|
|
92
|
+
export function shouldCompact(config, currentTokens) {
|
|
93
|
+
if (config.disabled)
|
|
94
|
+
return false;
|
|
95
|
+
const usable = config.contextWindow - config.maxOutputTokens;
|
|
96
|
+
const threshold = config.triggerThreshold ?? TRIGGER_THRESHOLD;
|
|
97
|
+
return currentTokens >= usable * threshold;
|
|
98
|
+
}
|
|
99
|
+
// ── Phase 1: Prune tool outputs ─────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Walk backwards through messages and replace old tool result content with
|
|
102
|
+
* placeholders. Protects the most recent `pruneProtect` tokens of tool output.
|
|
103
|
+
* Only prunes if total prunable tokens >= `pruneMinimum`.
|
|
104
|
+
*/
|
|
105
|
+
export function pruneToolOutputs(messages, config) {
|
|
106
|
+
const protectTokens = config.pruneProtect ?? PRUNE_PROTECT;
|
|
107
|
+
const minimumTokens = config.pruneMinimum ?? PRUNE_MINIMUM;
|
|
108
|
+
const entries = [];
|
|
109
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
110
|
+
const msg = messages[i];
|
|
111
|
+
if (msg.role === "toolResult" && Array.isArray(msg.content)) {
|
|
112
|
+
for (let j = msg.content.length - 1; j >= 0; j--) {
|
|
113
|
+
const block = msg.content[j];
|
|
114
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
115
|
+
const tokens = estimateTokens(block.text);
|
|
116
|
+
// Try to find the tool name from the message-level or block-level properties
|
|
117
|
+
const toolName = msg.toolName || msg.name || block.toolName || "unknown";
|
|
118
|
+
entries.push({
|
|
119
|
+
messageIndex: i,
|
|
120
|
+
blockIndex: j,
|
|
121
|
+
tokens,
|
|
122
|
+
toolName,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// entries are in reverse order (most recent first) due to backward walk
|
|
129
|
+
// Calculate how many tokens to protect and how many are prunable
|
|
130
|
+
let protectedSoFar = 0;
|
|
131
|
+
let prunableTokens = 0;
|
|
132
|
+
const prunableEntries = [];
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (protectedSoFar < protectTokens) {
|
|
135
|
+
protectedSoFar += entry.tokens;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
prunableTokens += entry.tokens;
|
|
139
|
+
prunableEntries.push(entry);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Only prune if we can reclaim enough
|
|
143
|
+
if (prunableTokens < minimumTokens) {
|
|
144
|
+
return messages;
|
|
145
|
+
}
|
|
146
|
+
// Deep-clone messages to avoid mutation
|
|
147
|
+
const result = JSON.parse(JSON.stringify(messages));
|
|
148
|
+
// Apply pruning
|
|
149
|
+
for (const entry of prunableEntries) {
|
|
150
|
+
const msg = result[entry.messageIndex];
|
|
151
|
+
if (entry.blockIndex !== undefined && Array.isArray(msg.content)) {
|
|
152
|
+
msg.content[entry.blockIndex] = {
|
|
153
|
+
type: "text",
|
|
154
|
+
text: `[Output pruned — was ${entry.tokens} tokens. Tool: ${entry.toolName}]`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
// ── Phase 2: Full compaction ────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* Main entry point. Checks if compaction is needed, tries pruning first,
|
|
163
|
+
* then falls back to LLM summarization if necessary.
|
|
164
|
+
*/
|
|
165
|
+
export async function compactIfNeeded(input) {
|
|
166
|
+
const { systemPrompt, messages, tools, config, summarize, mode, onCompaction } = input;
|
|
167
|
+
if (config.disabled) {
|
|
168
|
+
const tokens = estimateTotalTokens(systemPrompt, messages, tools);
|
|
169
|
+
return {
|
|
170
|
+
messages,
|
|
171
|
+
compacted: false,
|
|
172
|
+
pruned: false,
|
|
173
|
+
tokensBefore: tokens,
|
|
174
|
+
tokensAfter: tokens,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const tokensBefore = estimateTotalTokens(systemPrompt, messages, tools);
|
|
178
|
+
const usable = config.contextWindow - config.maxOutputTokens;
|
|
179
|
+
const threshold = config.triggerThreshold ?? TRIGGER_THRESHOLD;
|
|
180
|
+
// Not over threshold — nothing to do
|
|
181
|
+
if (tokensBefore < usable * threshold) {
|
|
182
|
+
return {
|
|
183
|
+
messages,
|
|
184
|
+
compacted: false,
|
|
185
|
+
pruned: false,
|
|
186
|
+
tokensBefore,
|
|
187
|
+
tokensAfter: tokensBefore,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const target = usable * TARGET_AFTER;
|
|
191
|
+
// Phase 1: try pruning tool outputs
|
|
192
|
+
const pruned = pruneToolOutputs(messages, config);
|
|
193
|
+
const tokensAfterPrune = estimateTotalTokens(systemPrompt, pruned, tools);
|
|
194
|
+
if (tokensAfterPrune <= target) {
|
|
195
|
+
onCompaction?.({
|
|
196
|
+
phase: "prune",
|
|
197
|
+
tokensBefore,
|
|
198
|
+
tokensAfter: tokensAfterPrune,
|
|
199
|
+
tokensReclaimed: tokensBefore - tokensAfterPrune,
|
|
200
|
+
messagesBefore: messages.length,
|
|
201
|
+
messagesAfter: pruned.length,
|
|
202
|
+
toolOutputsPruned: countPrunedOutputs(pruned),
|
|
203
|
+
mode,
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
messages: pruned,
|
|
207
|
+
compacted: true,
|
|
208
|
+
pruned: true,
|
|
209
|
+
tokensBefore,
|
|
210
|
+
tokensAfter: tokensAfterPrune,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Phase 2: LLM summarization
|
|
214
|
+
// Keep ~60% of target budget as recent messages
|
|
215
|
+
const recentBudget = target * 0.6;
|
|
216
|
+
// Walk backwards to find split point
|
|
217
|
+
let recentTokens = 0;
|
|
218
|
+
let splitIndex = pruned.length;
|
|
219
|
+
for (let i = pruned.length - 1; i >= 0; i--) {
|
|
220
|
+
const msgTokens = estimateMessageTokens(pruned[i]);
|
|
221
|
+
if (recentTokens + msgTokens > recentBudget && i < pruned.length - 1) {
|
|
222
|
+
splitIndex = i + 1;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
recentTokens += msgTokens;
|
|
226
|
+
if (i === 0)
|
|
227
|
+
splitIndex = 0;
|
|
228
|
+
}
|
|
229
|
+
// Need at least some older messages to summarize
|
|
230
|
+
if (splitIndex <= 0) {
|
|
231
|
+
// Everything is "recent" — just return pruned
|
|
232
|
+
return {
|
|
233
|
+
messages: pruned,
|
|
234
|
+
compacted: true,
|
|
235
|
+
pruned: true,
|
|
236
|
+
tokensBefore,
|
|
237
|
+
tokensAfter: tokensAfterPrune,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const olderMessages = pruned.slice(0, splitIndex);
|
|
241
|
+
const recentMessages = pruned.slice(splitIndex);
|
|
242
|
+
const prompt = getCompactionPrompt(mode);
|
|
243
|
+
const summary = await summarize(olderMessages, prompt);
|
|
244
|
+
const summaryMessage = {
|
|
245
|
+
role: "user",
|
|
246
|
+
content: `[Previous context summary]\n${summary}\n[End summary — continue from here]`,
|
|
247
|
+
};
|
|
248
|
+
const compactedMessages = [summaryMessage, ...recentMessages];
|
|
249
|
+
const tokensAfter = estimateTotalTokens(systemPrompt, compactedMessages, tools);
|
|
250
|
+
onCompaction?.({
|
|
251
|
+
phase: "summarize",
|
|
252
|
+
tokensBefore,
|
|
253
|
+
tokensAfter,
|
|
254
|
+
tokensReclaimed: tokensBefore - tokensAfter,
|
|
255
|
+
messagesBefore: messages.length,
|
|
256
|
+
messagesAfter: compactedMessages.length,
|
|
257
|
+
toolOutputsPruned: countPrunedOutputs(pruned),
|
|
258
|
+
summary,
|
|
259
|
+
mode,
|
|
260
|
+
});
|
|
261
|
+
return {
|
|
262
|
+
messages: compactedMessages,
|
|
263
|
+
compacted: true,
|
|
264
|
+
pruned: true,
|
|
265
|
+
summary,
|
|
266
|
+
tokensBefore,
|
|
267
|
+
tokensAfter,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
// ── Internal helpers ────────────────────────────────────────────────────
|
|
271
|
+
function countPrunedOutputs(messages) {
|
|
272
|
+
let count = 0;
|
|
273
|
+
for (const msg of messages) {
|
|
274
|
+
if (msg.role === "toolResult" && Array.isArray(msg.content)) {
|
|
275
|
+
for (const block of msg.content) {
|
|
276
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.startsWith("[Output pruned")) {
|
|
277
|
+
count++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return count;
|
|
283
|
+
}
|
|
284
|
+
function estimateTotalTokens(systemPrompt, messages, tools) {
|
|
285
|
+
let total = estimateTokens(systemPrompt);
|
|
286
|
+
total += estimateMessagesTokens(messages);
|
|
287
|
+
if (tools && tools.length > 0) {
|
|
288
|
+
// Rough estimate: stringify tool definitions
|
|
289
|
+
total += estimateTokens(JSON.stringify(tools));
|
|
290
|
+
}
|
|
291
|
+
return total;
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=context-compactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compactor.js","sourceRoot":"","sources":["../src/context-compactor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -51,4 +51,6 @@ export { assessTask, runCheck, runMetric, type AssessmentDeps, type CheckProgres
|
|
|
51
51
|
export { DEFAULT_DIMENSIONS, buildRubricSection, computeWeightedScore, computeMedianScores } from "./assessment-scoring.js";
|
|
52
52
|
export { validateReviewPayload, ReviewPayloadSchema, ReviewScoreSchema, REVIEW_JSON_SCHEMA, type ValidatedReviewPayload } from "./assessment-schemas.js";
|
|
53
53
|
export { withRetry, isTransientError, type RetryOptions } from "./retry.js";
|
|
54
|
+
export { estimateTokens, estimateMessagesTokens, shouldCompact, pruneToolOutputs, compactIfNeeded, getCompactionPrompt, PRUNE_PROTECT, PRUNE_MINIMUM, TRIGGER_THRESHOLD, TARGET_AFTER, } from "./context-compactor.js";
|
|
55
|
+
export type { CompactionConfig, CompactionEvent, OnCompactionFn, SummarizeFn, CompactionInput, CompactionResult, } from "./context-compactor.js";
|
|
54
56
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -41,4 +41,6 @@ export { assessTask, runCheck, runMetric } from "./assessor.js";
|
|
|
41
41
|
export { DEFAULT_DIMENSIONS, buildRubricSection, computeWeightedScore, computeMedianScores } from "./assessment-scoring.js";
|
|
42
42
|
export { validateReviewPayload, ReviewPayloadSchema, ReviewScoreSchema, REVIEW_JSON_SCHEMA } from "./assessment-schemas.js";
|
|
43
43
|
export { withRetry, isTransientError } from "./retry.js";
|
|
44
|
+
// ── Context Compaction ──────────────────────────────────────────────────
|
|
45
|
+
export { estimateTokens, estimateMessagesTokens, shouldCompact, pruneToolOutputs, compactIfNeeded, getCompactionPrompt, PRUNE_PROTECT, PRUNE_MINIMUM, TRIGGER_THRESHOLD, TARGET_AFTER, } from "./context-compactor.js";
|
|
44
46
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAE3B,4EAA4E;AAC5E,cAAc,aAAa,CAAC;AAE5B,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEjG,4EAA4E;AAC5E,cAAc,cAAc,CAAC;AAE7B,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgB1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAiBrD,2EAA2E;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,0EAA0E;AAC1E,OAAO,EACL,cAAc,EAAE,eAAe,EAAE,yBAAyB,EAC1D,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,GAC1D,MAAM,oBAAoB,CAAC;AAG5B,0EAA0E;AAC1E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAYnE,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEzF,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,2EAA2E;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,2EAA2E;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAW9D,4EAA4E;AAC5E,OAAO,EAAE,sBAAsB,EAAwB,MAAM,8BAA8B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,KAAK,EAAoE,MAAM,yBAAyB,CAAC;AAC5N,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAK/E,2EAA2E;AAC3E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAA8E,MAAM,eAAe,CAAC;AAC5I,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5H,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,kBAAkB,EAA+B,MAAM,yBAAyB,CAAC;AACzJ,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAqB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAE3B,4EAA4E;AAC5E,cAAc,aAAa,CAAC;AAE5B,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEjG,4EAA4E;AAC5E,cAAc,cAAc,CAAC;AAE7B,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgB1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAiBrD,2EAA2E;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,0EAA0E;AAC1E,OAAO,EACL,cAAc,EAAE,eAAe,EAAE,yBAAyB,EAC1D,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,GAC1D,MAAM,oBAAoB,CAAC;AAG5B,0EAA0E;AAC1E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAYnE,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEzF,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,2EAA2E;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,2EAA2E;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAW9D,4EAA4E;AAC5E,OAAO,EAAE,sBAAsB,EAAwB,MAAM,8BAA8B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,KAAK,EAAoE,MAAM,yBAAyB,CAAC;AAC5N,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAK/E,2EAA2E;AAC3E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAA8E,MAAM,eAAe,CAAC;AAC5I,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5H,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,kBAAkB,EAA+B,MAAM,yBAAyB,CAAC;AACzJ,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAqB,MAAM,YAAY,CAAC;AAE5E,2EAA2E;AAC3E,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polpo-ai/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Pure business logic, types, schemas, and store interfaces for the Polpo AI agent orchestration platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -193,6 +193,10 @@
|
|
|
193
193
|
"./retry": {
|
|
194
194
|
"types": "./dist/retry.d.ts",
|
|
195
195
|
"import": "./dist/retry.js"
|
|
196
|
+
},
|
|
197
|
+
"./context-compactor": {
|
|
198
|
+
"types": "./dist/context-compactor.d.ts",
|
|
199
|
+
"import": "./dist/context-compactor.js"
|
|
196
200
|
}
|
|
197
201
|
},
|
|
198
202
|
"sideEffects": false,
|