@roj-ai/sdk 0.1.14 → 0.1.16
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/bootstrap.d.ts +1 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/core/agents/agent.d.ts +25 -1
- package/dist/core/agents/agent.d.ts.map +1 -1
- package/dist/core/agents/agent.js +117 -21
- package/dist/core/agents/agent.js.map +1 -1
- package/dist/core/agents/config.d.ts +7 -0
- package/dist/core/agents/config.d.ts.map +1 -1
- package/dist/core/agents/context.d.ts +10 -0
- package/dist/core/agents/context.d.ts.map +1 -1
- package/dist/core/agents/state.d.ts +11 -3
- package/dist/core/agents/state.d.ts.map +1 -1
- package/dist/core/agents/state.js.map +1 -1
- package/dist/core/file-store/file-store.d.ts +5 -1
- package/dist/core/file-store/file-store.d.ts.map +1 -1
- package/dist/core/file-store/file-store.js +31 -21
- package/dist/core/file-store/file-store.js.map +1 -1
- package/dist/core/image/vips-resizer.test.js +26 -14
- package/dist/core/image/vips-resizer.test.js.map +1 -1
- package/dist/core/llm/anthropic.d.ts.map +1 -1
- package/dist/core/llm/anthropic.js +11 -8
- package/dist/core/llm/anthropic.js.map +1 -1
- package/dist/core/llm/cache-breakpoints.d.ts +5 -1
- package/dist/core/llm/cache-breakpoints.d.ts.map +1 -1
- package/dist/core/llm/cache-breakpoints.js +10 -5
- package/dist/core/llm/cache-breakpoints.js.map +1 -1
- package/dist/core/sessions/session.d.ts.map +1 -1
- package/dist/core/sessions/session.js +3 -0
- package/dist/core/sessions/session.js.map +1 -1
- package/dist/core/sessions/session.test.js +5 -0
- package/dist/core/sessions/session.test.js.map +1 -1
- package/dist/core/sessions/state.d.ts.map +1 -1
- package/dist/core/sessions/state.js +5 -1
- package/dist/core/sessions/state.js.map +1 -1
- package/dist/core/tools/executor.test.js +1 -0
- package/dist/core/tools/executor.test.js.map +1 -1
- package/dist/plugins/agent-status/plugin.d.ts.map +1 -1
- package/dist/plugins/agent-status/plugin.js +18 -26
- package/dist/plugins/agent-status/plugin.js.map +1 -1
- package/dist/plugins/context-compact/compaction-live.test.d.ts +17 -0
- package/dist/plugins/context-compact/compaction-live.test.d.ts.map +1 -0
- package/dist/plugins/context-compact/compaction-live.test.js +177 -0
- package/dist/plugins/context-compact/compaction-live.test.js.map +1 -0
- package/dist/plugins/context-compact/context-compact.integration.test.js +123 -3
- package/dist/plugins/context-compact/context-compact.integration.test.js.map +1 -1
- package/dist/plugins/context-compact/context-compactor.d.ts +47 -17
- package/dist/plugins/context-compact/context-compactor.d.ts.map +1 -1
- package/dist/plugins/context-compact/context-compactor.js +60 -36
- package/dist/plugins/context-compact/context-compactor.js.map +1 -1
- package/dist/plugins/context-compact/context-compactor.test.js +69 -103
- package/dist/plugins/context-compact/context-compactor.test.js.map +1 -1
- package/dist/plugins/context-compact/plugin.d.ts +9 -2
- package/dist/plugins/context-compact/plugin.d.ts.map +1 -1
- package/dist/plugins/context-compact/plugin.js +8 -4
- package/dist/plugins/context-compact/plugin.js.map +1 -1
- package/dist/plugins/filesystem/filesystem.integration.test.js +36 -0
- package/dist/plugins/filesystem/filesystem.integration.test.js.map +1 -1
- package/dist/plugins/filesystem/plugin.d.ts.map +1 -1
- package/dist/plugins/filesystem/plugin.js +8 -6
- package/dist/plugins/filesystem/plugin.js.map +1 -1
- package/dist/plugins/mailbox/mailbox.integration.test.js +9 -16
- package/dist/plugins/mailbox/mailbox.integration.test.js.map +1 -1
- package/dist/plugins/resources/plugin.d.ts.map +1 -1
- package/dist/plugins/resources/plugin.js +4 -1
- package/dist/plugins/resources/plugin.js.map +1 -1
- package/dist/plugins/uploads/preprocessors/image-classifier.d.ts.map +1 -1
- package/dist/plugins/uploads/preprocessors/image-classifier.js +15 -2
- package/dist/plugins/uploads/preprocessors/image-classifier.js.map +1 -1
- package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.d.ts.map +1 -1
- package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js +72 -19
- package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js.map +1 -1
- package/dist/plugins/user-chat/plugin.d.ts +2 -0
- package/dist/plugins/user-chat/plugin.d.ts.map +1 -1
- package/dist/plugins/user-chat/plugin.js +47 -3
- package/dist/plugins/user-chat/plugin.js.map +1 -1
- package/dist/plugins/user-chat/schema.d.ts +10 -0
- package/dist/plugins/user-chat/schema.d.ts.map +1 -1
- package/dist/plugins/user-chat/schema.js +1 -0
- package/dist/plugins/user-chat/schema.js.map +1 -1
- package/dist/plugins/user-chat/user-chat.integration.test.js +86 -0
- package/dist/plugins/user-chat/user-chat.integration.test.js.map +1 -1
- package/package.json +2 -2
- package/src/core/agents/agent.ts +134 -20
- package/src/core/agents/config.ts +7 -0
- package/src/core/agents/context.ts +11 -0
- package/src/core/agents/state.ts +11 -4
- package/src/core/file-store/file-store.ts +38 -18
- package/src/core/image/vips-resizer.test.ts +26 -15
- package/src/core/llm/anthropic.ts +19 -12
- package/src/core/llm/cache-breakpoints.ts +15 -6
- package/src/core/sessions/session.test.ts +6 -0
- package/src/core/sessions/session.ts +4 -0
- package/src/core/sessions/state.ts +5 -1
- package/src/core/tools/executor.test.ts +1 -0
- package/src/plugins/agent-status/plugin.ts +18 -25
- package/src/plugins/context-compact/compaction-live.test.ts +221 -0
- package/src/plugins/context-compact/context-compact.integration.test.ts +135 -3
- package/src/plugins/context-compact/context-compactor.test.ts +71 -110
- package/src/plugins/context-compact/context-compactor.ts +88 -43
- package/src/plugins/context-compact/plugin.ts +19 -10
- package/src/plugins/filesystem/filesystem.integration.test.ts +44 -0
- package/src/plugins/filesystem/plugin.ts +8 -6
- package/src/plugins/mailbox/mailbox.integration.test.ts +12 -18
- package/src/plugins/resources/plugin.ts +4 -1
- package/src/plugins/uploads/preprocessors/image-classifier.ts +15 -2
- package/src/plugins/uploads/preprocessors/markitdown-preprocessor.ts +89 -20
- package/src/plugins/user-chat/plugin.ts +60 -3
- package/src/plugins/user-chat/schema.ts +10 -1
- package/src/plugins/user-chat/user-chat.integration.test.ts +99 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from 'bun:test'
|
|
2
2
|
import type { AgentId } from '~/core/agents/schema.js'
|
|
3
3
|
import { generateTestAgentId } from '~/core/agents/schema.js'
|
|
4
|
-
import type {
|
|
4
|
+
import type { InferenceResponse, LLMMessage } from '~/core/llm/provider.js'
|
|
5
5
|
import { ModelId } from '~/core/llm/schema.js'
|
|
6
6
|
import type { SessionId } from '~/core/sessions/schema.js'
|
|
7
7
|
import { generateSessionId } from '~/core/sessions/schema.js'
|
|
8
8
|
import { generateToolCallId } from '~/core/tools/schema.js'
|
|
9
9
|
import { Err, Ok } from '~/lib/utils/result.js'
|
|
10
|
-
import type { Result } from '~/lib/utils/result.js'
|
|
11
10
|
import { silentLogger } from '../../lib/logger/logger.js'
|
|
12
11
|
import { ContextCompactor, createContextCompactedEvent, formatMessageForSummary } from './context-compactor.js'
|
|
13
|
-
import type { CompactionConfig, CompactionResult } from './context-compactor.js'
|
|
12
|
+
import type { CompactionConfig, CompactionResult, RunInferenceFn } from './context-compactor.js'
|
|
14
13
|
|
|
15
14
|
// ============================================================================
|
|
16
15
|
// Test Constants
|
|
@@ -19,24 +18,25 @@ import type { CompactionConfig, CompactionResult } from './context-compactor.js'
|
|
|
19
18
|
const TEST_MODEL_ID: ModelId = ModelId('test/model')
|
|
20
19
|
|
|
21
20
|
// ============================================================================
|
|
22
|
-
// Mock
|
|
21
|
+
// Mock runInference callback
|
|
23
22
|
// ============================================================================
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
interface InferenceMockCall {
|
|
25
|
+
extraMessages: LLMMessage[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class InferenceMock {
|
|
27
29
|
private responses: InferenceResponse[] = []
|
|
28
30
|
private responseIndex = 0
|
|
29
|
-
calls:
|
|
31
|
+
calls: InferenceMockCall[] = []
|
|
30
32
|
|
|
31
33
|
setResponses(responses: InferenceResponse[]): void {
|
|
32
34
|
this.responses = responses
|
|
33
35
|
this.responseIndex = 0
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
async
|
|
37
|
-
|
|
38
|
-
): Promise<Result<InferenceResponse, LLMError>> {
|
|
39
|
-
this.calls.push(request)
|
|
38
|
+
readonly run: RunInferenceFn = async (extraMessages) => {
|
|
39
|
+
this.calls.push({ extraMessages })
|
|
40
40
|
if (this.responseIndex >= this.responses.length) {
|
|
41
41
|
return Err({ type: 'server_error', message: 'No more mock responses' })
|
|
42
42
|
}
|
|
@@ -49,12 +49,12 @@ class MockLLMProvider implements LLMProvider {
|
|
|
49
49
|
// ============================================================================
|
|
50
50
|
|
|
51
51
|
describe('ContextCompactor.needsCompaction', () => {
|
|
52
|
-
let
|
|
52
|
+
let inference: InferenceMock
|
|
53
53
|
let compactor: ContextCompactor
|
|
54
54
|
|
|
55
55
|
beforeEach(() => {
|
|
56
|
-
|
|
57
|
-
compactor = new ContextCompactor(
|
|
56
|
+
inference = new InferenceMock()
|
|
57
|
+
compactor = new ContextCompactor(silentLogger, {
|
|
58
58
|
model: TEST_MODEL_ID,
|
|
59
59
|
maxTokens: 100,
|
|
60
60
|
keepRecentMessages: 2,
|
|
@@ -82,14 +82,14 @@ describe('ContextCompactor.needsCompaction', () => {
|
|
|
82
82
|
// ============================================================================
|
|
83
83
|
|
|
84
84
|
describe('ContextCompactor.compact', () => {
|
|
85
|
-
let
|
|
85
|
+
let inference: InferenceMock
|
|
86
86
|
let compactor: ContextCompactor
|
|
87
87
|
let sessionId: SessionId
|
|
88
88
|
let agentId: AgentId
|
|
89
89
|
|
|
90
90
|
beforeEach(() => {
|
|
91
|
-
|
|
92
|
-
compactor = new ContextCompactor(
|
|
91
|
+
inference = new InferenceMock()
|
|
92
|
+
compactor = new ContextCompactor(silentLogger, {
|
|
93
93
|
model: TEST_MODEL_ID,
|
|
94
94
|
maxTokens: 100,
|
|
95
95
|
keepRecentMessages: 2,
|
|
@@ -105,7 +105,7 @@ describe('ContextCompactor.compact', () => {
|
|
|
105
105
|
{ role: 'assistant', content: 'message 2' },
|
|
106
106
|
]
|
|
107
107
|
|
|
108
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
108
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
109
109
|
expect(result.ok).toBe(true)
|
|
110
110
|
if (!result.ok) return
|
|
111
111
|
|
|
@@ -115,7 +115,7 @@ describe('ContextCompactor.compact', () => {
|
|
|
115
115
|
})
|
|
116
116
|
|
|
117
117
|
it('compacts old messages and keeps recent ones', async () => {
|
|
118
|
-
|
|
118
|
+
inference.setResponses([
|
|
119
119
|
{
|
|
120
120
|
content: 'Summary of the conversation',
|
|
121
121
|
toolCalls: [],
|
|
@@ -138,7 +138,7 @@ describe('ContextCompactor.compact', () => {
|
|
|
138
138
|
{ role: 'assistant', content: 'recent message 2' },
|
|
139
139
|
]
|
|
140
140
|
|
|
141
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
141
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
142
142
|
expect(result.ok).toBe(true)
|
|
143
143
|
if (!result.ok) return
|
|
144
144
|
|
|
@@ -146,8 +146,8 @@ describe('ContextCompactor.compact', () => {
|
|
|
146
146
|
expect(result.value.messagesRemoved).toBe(3)
|
|
147
147
|
expect(result.value.compactedMessages.length).toBe(3) // summary + 2 recent
|
|
148
148
|
|
|
149
|
-
// First message is summary
|
|
150
|
-
expect(result.value.compactedMessages[0].role).toBe('
|
|
149
|
+
// First message is the summary (user-role so it cleanly fits into chat history)
|
|
150
|
+
expect(result.value.compactedMessages[0].role).toBe('user')
|
|
151
151
|
expect(result.value.compactedMessages[0].content).toContain(
|
|
152
152
|
'[CONVERSATION SUMMARY]',
|
|
153
153
|
)
|
|
@@ -160,8 +160,8 @@ describe('ContextCompactor.compact', () => {
|
|
|
160
160
|
expect(result.value.compactedMessages[2].content).toBe('recent message 2')
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
it('calls
|
|
164
|
-
|
|
163
|
+
it('calls runInference with a single trailing summarize-instruction message', async () => {
|
|
164
|
+
inference.setResponses([
|
|
165
165
|
{
|
|
166
166
|
content: 'Summary',
|
|
167
167
|
toolCalls: [],
|
|
@@ -183,17 +183,16 @@ describe('ContextCompactor.compact', () => {
|
|
|
183
183
|
{ role: 'assistant', content: 'recent too' },
|
|
184
184
|
]
|
|
185
185
|
|
|
186
|
-
await compactor.compact(sessionId, agentId, messages)
|
|
186
|
+
await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
187
187
|
|
|
188
|
-
expect(
|
|
189
|
-
const
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
expect(
|
|
194
|
-
expect(
|
|
195
|
-
|
|
196
|
-
expect(request.messages[0].content).not.toContain('recent')
|
|
188
|
+
expect(inference.calls.length).toBe(1)
|
|
189
|
+
const call = inference.calls[0]
|
|
190
|
+
// Inline summarization sends ONE trailing user message — the host (agent)
|
|
191
|
+
// is responsible for the full prefix (preamble + history); the compactor
|
|
192
|
+
// only contributes the instruction.
|
|
193
|
+
expect(call.extraMessages.length).toBe(1)
|
|
194
|
+
expect(call.extraMessages[0].role).toBe('user')
|
|
195
|
+
expect(typeof call.extraMessages[0].content).toBe('string')
|
|
197
196
|
})
|
|
198
197
|
|
|
199
198
|
it('returns error when LLM fails', async () => {
|
|
@@ -207,7 +206,7 @@ describe('ContextCompactor.compact', () => {
|
|
|
207
206
|
{ role: 'assistant', content: 'recent 2' },
|
|
208
207
|
]
|
|
209
208
|
|
|
210
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
209
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
211
210
|
expect(result.ok).toBe(false)
|
|
212
211
|
if (result.ok) return
|
|
213
212
|
|
|
@@ -220,14 +219,14 @@ describe('ContextCompactor.compact', () => {
|
|
|
220
219
|
// ============================================================================
|
|
221
220
|
|
|
222
221
|
describe('ContextCompactor.compactIfNeeded', () => {
|
|
223
|
-
let
|
|
222
|
+
let inference: InferenceMock
|
|
224
223
|
let compactor: ContextCompactor
|
|
225
224
|
let sessionId: SessionId
|
|
226
225
|
let agentId: AgentId
|
|
227
226
|
|
|
228
227
|
beforeEach(() => {
|
|
229
|
-
|
|
230
|
-
compactor = new ContextCompactor(
|
|
228
|
+
inference = new InferenceMock()
|
|
229
|
+
compactor = new ContextCompactor(silentLogger, {
|
|
231
230
|
model: TEST_MODEL_ID,
|
|
232
231
|
maxTokens: 50,
|
|
233
232
|
keepRecentMessages: 1,
|
|
@@ -239,16 +238,16 @@ describe('ContextCompactor.compactIfNeeded', () => {
|
|
|
239
238
|
it('returns null when compaction not needed', async () => {
|
|
240
239
|
const messages: LLMMessage[] = [{ role: 'user', content: 'short' }]
|
|
241
240
|
|
|
242
|
-
const result = await compactor.compactIfNeeded(sessionId, agentId, messages)
|
|
241
|
+
const result = await compactor.compactIfNeeded(sessionId, agentId, messages, inference.run)
|
|
243
242
|
expect(result.ok).toBe(true)
|
|
244
243
|
if (!result.ok) return
|
|
245
244
|
|
|
246
245
|
expect(result.value).toBeNull()
|
|
247
|
-
expect(
|
|
246
|
+
expect(inference.calls.length).toBe(0)
|
|
248
247
|
})
|
|
249
248
|
|
|
250
249
|
it('compacts when needed', async () => {
|
|
251
|
-
|
|
250
|
+
inference.setResponses([
|
|
252
251
|
{
|
|
253
252
|
content: 'Summary',
|
|
254
253
|
toolCalls: [],
|
|
@@ -270,7 +269,7 @@ describe('ContextCompactor.compactIfNeeded', () => {
|
|
|
270
269
|
{ role: 'user', content: 'c'.repeat(100) },
|
|
271
270
|
]
|
|
272
271
|
|
|
273
|
-
const result = await compactor.compactIfNeeded(sessionId, agentId, messages)
|
|
272
|
+
const result = await compactor.compactIfNeeded(sessionId, agentId, messages, inference.run)
|
|
274
273
|
expect(result.ok).toBe(true)
|
|
275
274
|
if (!result.ok) return
|
|
276
275
|
|
|
@@ -338,13 +337,13 @@ describe('createContextCompactedEvent', () => {
|
|
|
338
337
|
|
|
339
338
|
describe('ContextCompactor with custom config', () => {
|
|
340
339
|
it('respects custom maxTokens', () => {
|
|
341
|
-
const
|
|
340
|
+
const inference = new InferenceMock()
|
|
342
341
|
const config: CompactionConfig = {
|
|
343
342
|
model: TEST_MODEL_ID,
|
|
344
343
|
maxTokens: 20,
|
|
345
344
|
keepRecentMessages: 1,
|
|
346
345
|
}
|
|
347
|
-
const compactor = new ContextCompactor(
|
|
346
|
+
const compactor = new ContextCompactor(silentLogger, config)
|
|
348
347
|
|
|
349
348
|
const smallMessages: LLMMessage[] = [{ role: 'user', content: 'hi' }]
|
|
350
349
|
expect(compactor.needsCompaction(smallMessages)).toBe(false)
|
|
@@ -356,8 +355,8 @@ describe('ContextCompactor with custom config', () => {
|
|
|
356
355
|
})
|
|
357
356
|
|
|
358
357
|
it('respects custom keepRecentMessages', async () => {
|
|
359
|
-
const
|
|
360
|
-
|
|
358
|
+
const inference = new InferenceMock()
|
|
359
|
+
inference.setResponses([
|
|
361
360
|
{
|
|
362
361
|
content: 'Summary',
|
|
363
362
|
toolCalls: [],
|
|
@@ -377,7 +376,7 @@ describe('ContextCompactor with custom config', () => {
|
|
|
377
376
|
maxTokens: 10,
|
|
378
377
|
keepRecentMessages: 3,
|
|
379
378
|
}
|
|
380
|
-
const compactor = new ContextCompactor(
|
|
379
|
+
const compactor = new ContextCompactor(silentLogger, config)
|
|
381
380
|
|
|
382
381
|
const messages: LLMMessage[] = [
|
|
383
382
|
{ role: 'user', content: 'old 1' },
|
|
@@ -391,6 +390,7 @@ describe('ContextCompactor with custom config', () => {
|
|
|
391
390
|
generateSessionId(),
|
|
392
391
|
generateTestAgentId(),
|
|
393
392
|
messages,
|
|
393
|
+
inference.run,
|
|
394
394
|
)
|
|
395
395
|
|
|
396
396
|
expect(result.ok).toBe(true)
|
|
@@ -402,8 +402,8 @@ describe('ContextCompactor with custom config', () => {
|
|
|
402
402
|
})
|
|
403
403
|
|
|
404
404
|
it('uses custom summaryPrompt', async () => {
|
|
405
|
-
const
|
|
406
|
-
|
|
405
|
+
const inference = new InferenceMock()
|
|
406
|
+
inference.setResponses([
|
|
407
407
|
{
|
|
408
408
|
content: 'Summary',
|
|
409
409
|
toolCalls: [],
|
|
@@ -425,17 +425,18 @@ describe('ContextCompactor with custom config', () => {
|
|
|
425
425
|
keepRecentMessages: 1,
|
|
426
426
|
summaryPrompt: customPrompt,
|
|
427
427
|
}
|
|
428
|
-
const compactor = new ContextCompactor(
|
|
428
|
+
const compactor = new ContextCompactor(silentLogger, config)
|
|
429
429
|
|
|
430
430
|
const messages: LLMMessage[] = [
|
|
431
431
|
{ role: 'user', content: 'old' },
|
|
432
432
|
{ role: 'user', content: 'recent' },
|
|
433
433
|
]
|
|
434
434
|
|
|
435
|
-
await compactor.compact(generateSessionId(), generateTestAgentId(), messages)
|
|
435
|
+
await compactor.compact(generateSessionId(), generateTestAgentId(), messages, inference.run)
|
|
436
436
|
|
|
437
|
-
expect(
|
|
438
|
-
|
|
437
|
+
expect(inference.calls.length).toBe(1)
|
|
438
|
+
// Custom prompt is sent as the content of the trailing user-role instruction.
|
|
439
|
+
expect(inference.calls[0].extraMessages[0].content).toBe(customPrompt)
|
|
439
440
|
})
|
|
440
441
|
})
|
|
441
442
|
|
|
@@ -557,14 +558,14 @@ describe('formatMessageForSummary', () => {
|
|
|
557
558
|
// ============================================================================
|
|
558
559
|
|
|
559
560
|
describe('ContextCompactor with tool calls', () => {
|
|
560
|
-
let
|
|
561
|
+
let inference: InferenceMock
|
|
561
562
|
let compactor: ContextCompactor
|
|
562
563
|
let sessionId: SessionId
|
|
563
564
|
let agentId: AgentId
|
|
564
565
|
|
|
565
566
|
beforeEach(() => {
|
|
566
|
-
|
|
567
|
-
compactor = new ContextCompactor(
|
|
567
|
+
inference = new InferenceMock()
|
|
568
|
+
compactor = new ContextCompactor(silentLogger, {
|
|
568
569
|
model: TEST_MODEL_ID,
|
|
569
570
|
maxTokens: 100,
|
|
570
571
|
keepRecentMessages: 1,
|
|
@@ -574,7 +575,7 @@ describe('ContextCompactor with tool calls', () => {
|
|
|
574
575
|
})
|
|
575
576
|
|
|
576
577
|
it('does not leave orphaned tool results at the start of kept messages', async () => {
|
|
577
|
-
|
|
578
|
+
inference.setResponses([
|
|
578
579
|
{
|
|
579
580
|
content: 'Summary',
|
|
580
581
|
toolCalls: [],
|
|
@@ -597,7 +598,7 @@ describe('ContextCompactor with tool calls', () => {
|
|
|
597
598
|
{ role: 'tool', content: 'export const foo = 1', toolCallId, toolName: 'read' },
|
|
598
599
|
]
|
|
599
600
|
|
|
600
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
601
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
601
602
|
expect(result.ok).toBe(true)
|
|
602
603
|
if (!result.ok) return
|
|
603
604
|
|
|
@@ -609,41 +610,6 @@ describe('ContextCompactor with tool calls', () => {
|
|
|
609
610
|
// All 5 original messages should be compacted (none kept except summary)
|
|
610
611
|
expect(result.value.messagesRemoved).toBe(5)
|
|
611
612
|
})
|
|
612
|
-
|
|
613
|
-
it('includes tool calls in summarization request', async () => {
|
|
614
|
-
mockLLM.setResponses([
|
|
615
|
-
{
|
|
616
|
-
content: 'Summary',
|
|
617
|
-
toolCalls: [],
|
|
618
|
-
finishReason: 'stop',
|
|
619
|
-
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
620
|
-
},
|
|
621
|
-
])
|
|
622
|
-
|
|
623
|
-
const toolCallId = generateToolCallId()
|
|
624
|
-
const messages: LLMMessage[] = [
|
|
625
|
-
{ role: 'user', content: 'Read the file' },
|
|
626
|
-
{
|
|
627
|
-
role: 'assistant',
|
|
628
|
-
content: '',
|
|
629
|
-
toolCalls: [{ id: toolCallId, name: 'read', input: { path: '/src/index.ts' } }],
|
|
630
|
-
},
|
|
631
|
-
{ role: 'tool', content: 'export const foo = 1', toolCallId, toolName: 'read' },
|
|
632
|
-
{ role: 'user', content: 'recent message' },
|
|
633
|
-
]
|
|
634
|
-
|
|
635
|
-
await compactor.compact(sessionId, agentId, messages)
|
|
636
|
-
|
|
637
|
-
expect(mockLLM.calls.length).toBe(1)
|
|
638
|
-
const request = mockLLM.calls[0]
|
|
639
|
-
const summaryContent = request.messages[0].content as string
|
|
640
|
-
|
|
641
|
-
// Verify tool call is included
|
|
642
|
-
expect(summaryContent).toContain('[Called tools: read(path)]')
|
|
643
|
-
// Verify tool result includes tool name
|
|
644
|
-
expect(summaryContent).toContain('Tool(read):')
|
|
645
|
-
expect(summaryContent).toContain('export const foo = 1')
|
|
646
|
-
})
|
|
647
613
|
})
|
|
648
614
|
|
|
649
615
|
// ============================================================================
|
|
@@ -651,12 +617,12 @@ describe('ContextCompactor with tool calls', () => {
|
|
|
651
617
|
// ============================================================================
|
|
652
618
|
|
|
653
619
|
describe('ContextCompactor with history offloading', () => {
|
|
654
|
-
let
|
|
620
|
+
let inference: InferenceMock
|
|
655
621
|
let sessionId: SessionId
|
|
656
622
|
let agentId: AgentId
|
|
657
623
|
|
|
658
624
|
beforeEach(() => {
|
|
659
|
-
|
|
625
|
+
inference = new InferenceMock()
|
|
660
626
|
sessionId = generateSessionId()
|
|
661
627
|
agentId = generateTestAgentId()
|
|
662
628
|
})
|
|
@@ -670,7 +636,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
670
636
|
},
|
|
671
637
|
}
|
|
672
638
|
|
|
673
|
-
|
|
639
|
+
inference.setResponses([
|
|
674
640
|
{
|
|
675
641
|
content: 'Summary',
|
|
676
642
|
toolCalls: [],
|
|
@@ -680,7 +646,6 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
680
646
|
])
|
|
681
647
|
|
|
682
648
|
const compactor = new ContextCompactor(
|
|
683
|
-
mockLLM,
|
|
684
649
|
silentLogger,
|
|
685
650
|
{
|
|
686
651
|
model: TEST_MODEL_ID,
|
|
@@ -697,7 +662,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
697
662
|
{ role: 'user', content: 'recent message' },
|
|
698
663
|
]
|
|
699
664
|
|
|
700
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
665
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
701
666
|
|
|
702
667
|
expect(result.ok).toBe(true)
|
|
703
668
|
if (!result.ok) return
|
|
@@ -723,7 +688,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
723
688
|
},
|
|
724
689
|
}
|
|
725
690
|
|
|
726
|
-
|
|
691
|
+
inference.setResponses([
|
|
727
692
|
{
|
|
728
693
|
content: 'Summary',
|
|
729
694
|
toolCalls: [],
|
|
@@ -733,7 +698,6 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
733
698
|
])
|
|
734
699
|
|
|
735
700
|
const compactor = new ContextCompactor(
|
|
736
|
-
mockLLM,
|
|
737
701
|
silentLogger,
|
|
738
702
|
{
|
|
739
703
|
model: TEST_MODEL_ID,
|
|
@@ -749,7 +713,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
749
713
|
{ role: 'user', content: 'recent' },
|
|
750
714
|
]
|
|
751
715
|
|
|
752
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
716
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
753
717
|
|
|
754
718
|
expect(result.ok).toBe(true)
|
|
755
719
|
if (!result.ok) return
|
|
@@ -759,7 +723,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
759
723
|
})
|
|
760
724
|
|
|
761
725
|
it('does not offload history when offloader is not provided', async () => {
|
|
762
|
-
|
|
726
|
+
inference.setResponses([
|
|
763
727
|
{
|
|
764
728
|
content: 'Summary',
|
|
765
729
|
toolCalls: [],
|
|
@@ -769,7 +733,6 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
769
733
|
])
|
|
770
734
|
|
|
771
735
|
const compactor = new ContextCompactor(
|
|
772
|
-
mockLLM,
|
|
773
736
|
silentLogger,
|
|
774
737
|
{
|
|
775
738
|
model: TEST_MODEL_ID,
|
|
@@ -785,7 +748,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
785
748
|
{ role: 'user', content: 'recent' },
|
|
786
749
|
]
|
|
787
750
|
|
|
788
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
751
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
789
752
|
|
|
790
753
|
expect(result.ok).toBe(true)
|
|
791
754
|
if (!result.ok) return
|
|
@@ -802,7 +765,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
802
765
|
},
|
|
803
766
|
}
|
|
804
767
|
|
|
805
|
-
|
|
768
|
+
inference.setResponses([
|
|
806
769
|
{
|
|
807
770
|
content: 'Summary',
|
|
808
771
|
toolCalls: [],
|
|
@@ -812,7 +775,6 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
812
775
|
])
|
|
813
776
|
|
|
814
777
|
const compactor = new ContextCompactor(
|
|
815
|
-
mockLLM,
|
|
816
778
|
silentLogger,
|
|
817
779
|
{
|
|
818
780
|
model: TEST_MODEL_ID,
|
|
@@ -829,7 +791,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
829
791
|
{ role: 'user', content: 'recent' },
|
|
830
792
|
]
|
|
831
793
|
|
|
832
|
-
await compactor.compact(sessionId, agentId, messages)
|
|
794
|
+
await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
833
795
|
|
|
834
796
|
expect(offloadedPaths.length).toBe(1)
|
|
835
797
|
expect(offloadedPaths[0].pathPrefix).toBe('/session/.custom-history/')
|
|
@@ -842,7 +804,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
842
804
|
},
|
|
843
805
|
}
|
|
844
806
|
|
|
845
|
-
|
|
807
|
+
inference.setResponses([
|
|
846
808
|
{
|
|
847
809
|
content: 'Summary despite offload failure',
|
|
848
810
|
toolCalls: [],
|
|
@@ -852,7 +814,6 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
852
814
|
])
|
|
853
815
|
|
|
854
816
|
const compactor = new ContextCompactor(
|
|
855
|
-
mockLLM,
|
|
856
817
|
silentLogger,
|
|
857
818
|
{
|
|
858
819
|
model: TEST_MODEL_ID,
|
|
@@ -868,7 +829,7 @@ describe('ContextCompactor with history offloading', () => {
|
|
|
868
829
|
{ role: 'user', content: 'recent' },
|
|
869
830
|
]
|
|
870
831
|
|
|
871
|
-
const result = await compactor.compact(sessionId, agentId, messages)
|
|
832
|
+
const result = await compactor.compact(sessionId, agentId, messages, inference.run)
|
|
872
833
|
|
|
873
834
|
// Compaction should succeed despite offload failure
|
|
874
835
|
expect(result.ok).toBe(true)
|