@strands-agents/sdk 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/src/__fixtures__/agent-helpers.d.ts +16 -1
- package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/agent-helpers.js +42 -0
- package/dist/src/__fixtures__/agent-helpers.js.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.d.ts +2 -1
- package/dist/src/__fixtures__/tool-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.js +20 -3
- package/dist/src/__fixtures__/tool-helpers.js.map +1 -1
- package/dist/src/__tests__/interrupt.test.d.ts +2 -0
- package/dist/src/__tests__/interrupt.test.d.ts.map +1 -0
- package/dist/src/__tests__/interrupt.test.js +264 -0
- package/dist/src/__tests__/interrupt.test.js.map +1 -0
- package/dist/src/__tests__/mcp.test.js +447 -7
- package/dist/src/__tests__/mcp.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.hook.test.js +551 -1
- package/dist/src/agent/__tests__/agent.hook.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.interrupt.test.d.ts +2 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.d.ts.map +1 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.js +779 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.js.map +1 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.d.ts +2 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.d.ts.map +1 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.js +161 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.js.map +1 -0
- package/dist/src/agent/__tests__/agent.test.js +174 -0
- package/dist/src/agent/__tests__/agent.test.js.map +1 -1
- package/dist/src/agent/__tests__/snapshot.test.js +148 -4
- package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
- package/dist/src/agent/agent-as-tool.d.ts.map +1 -1
- package/dist/src/agent/agent-as-tool.js +2 -3
- package/dist/src/agent/agent-as-tool.js.map +1 -1
- package/dist/src/agent/agent.d.ts +94 -4
- package/dist/src/agent/agent.d.ts.map +1 -1
- package/dist/src/agent/agent.js +625 -223
- package/dist/src/agent/agent.js.map +1 -1
- package/dist/src/agent/snapshot.d.ts +11 -19
- package/dist/src/agent/snapshot.d.ts.map +1 -1
- package/dist/src/agent/snapshot.js +23 -19
- package/dist/src/agent/snapshot.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js +230 -9
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js +19 -6
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +422 -41
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js +75 -1
- package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/conversation-manager.d.ts +67 -22
- package/dist/src/conversation-manager/conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/conversation-manager.js +65 -13
- package/dist/src/conversation-manager/conversation-manager.js.map +1 -1
- package/dist/src/conversation-manager/index.d.ts +1 -1
- package/dist/src/conversation-manager/index.d.ts.map +1 -1
- package/dist/src/conversation-manager/index.js +1 -1
- package/dist/src/conversation-manager/index.js.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +43 -10
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js +202 -45
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts +23 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.js +39 -17
- package/dist/src/conversation-manager/summarizing-conversation-manager.js.map +1 -1
- package/dist/src/hooks/__tests__/events.test.js +99 -12
- package/dist/src/hooks/__tests__/events.test.js.map +1 -1
- package/dist/src/hooks/__tests__/registry.test.js +166 -2
- package/dist/src/hooks/__tests__/registry.test.js.map +1 -1
- package/dist/src/hooks/events.d.ts +125 -32
- package/dist/src/hooks/events.d.ts.map +1 -1
- package/dist/src/hooks/events.js +111 -8
- package/dist/src/hooks/events.js.map +1 -1
- package/dist/src/hooks/index.d.ts +4 -3
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +2 -1
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/registry.d.ts +12 -12
- package/dist/src/hooks/registry.d.ts.map +1 -1
- package/dist/src/hooks/registry.js +55 -15
- package/dist/src/hooks/registry.js.map +1 -1
- package/dist/src/hooks/types.d.ts +23 -0
- package/dist/src/hooks/types.d.ts.map +1 -1
- package/dist/src/hooks/types.js +17 -1
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/index.d.ts +12 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/interrupt.d.ts +247 -0
- package/dist/src/interrupt.d.ts.map +1 -0
- package/dist/src/interrupt.js +316 -0
- package/dist/src/interrupt.js.map +1 -0
- package/dist/src/mcp.d.ts +61 -4
- package/dist/src/mcp.d.ts.map +1 -1
- package/dist/src/mcp.js +161 -25
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/models/__tests__/anthropic.test.js +78 -8
- package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
- package/dist/src/models/__tests__/bedrock.test.js +156 -18
- package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
- package/dist/src/models/__tests__/defaults.test.d.ts +2 -0
- package/dist/src/models/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/src/models/__tests__/defaults.test.js +36 -0
- package/dist/src/models/__tests__/defaults.test.js.map +1 -0
- package/dist/src/models/__tests__/google.test.js +72 -6
- package/dist/src/models/__tests__/google.test.js.map +1 -1
- package/dist/src/models/anthropic.d.ts +10 -0
- package/dist/src/models/anthropic.d.ts.map +1 -1
- package/dist/src/models/anthropic.js +14 -4
- package/dist/src/models/anthropic.js.map +1 -1
- package/dist/src/models/bedrock.d.ts +17 -3
- package/dist/src/models/bedrock.d.ts.map +1 -1
- package/dist/src/models/bedrock.js +80 -13
- package/dist/src/models/bedrock.js.map +1 -1
- package/dist/src/models/defaults.d.ts +10 -0
- package/dist/src/models/defaults.d.ts.map +1 -1
- package/dist/src/models/defaults.js +129 -0
- package/dist/src/models/defaults.js.map +1 -1
- package/dist/src/models/google/model.d.ts.map +1 -1
- package/dist/src/models/google/model.js +4 -2
- package/dist/src/models/google/model.js.map +1 -1
- package/dist/src/models/google/types.d.ts +10 -0
- package/dist/src/models/google/types.d.ts.map +1 -1
- package/dist/src/models/model.d.ts +15 -0
- package/dist/src/models/model.d.ts.map +1 -1
- package/dist/src/models/model.js +18 -0
- package/dist/src/models/model.js.map +1 -1
- package/dist/src/models/openai/__tests__/chat.test.js +55 -2
- package/dist/src/models/openai/__tests__/chat.test.js.map +1 -1
- package/dist/src/models/openai/__tests__/responses.test.js +19 -0
- package/dist/src/models/openai/__tests__/responses.test.js.map +1 -1
- package/dist/src/models/openai/errors.d.ts.map +1 -1
- package/dist/src/models/openai/errors.js +7 -4
- package/dist/src/models/openai/errors.js.map +1 -1
- package/dist/src/models/openai/model.d.ts.map +1 -1
- package/dist/src/models/openai/model.js +2 -2
- package/dist/src/models/openai/model.js.map +1 -1
- package/dist/src/multiagent/__tests__/graph.test.js +69 -0
- package/dist/src/multiagent/__tests__/graph.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/graph.tracer.test.js +14 -0
- package/dist/src/multiagent/__tests__/graph.tracer.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/interrupts.test.d.ts +2 -0
- package/dist/src/multiagent/__tests__/interrupts.test.d.ts.map +1 -0
- package/dist/src/multiagent/__tests__/interrupts.test.js +390 -0
- package/dist/src/multiagent/__tests__/interrupts.test.js.map +1 -0
- package/dist/src/multiagent/__tests__/nodes.test.js +13 -0
- package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/state.test.js +139 -1
- package/dist/src/multiagent/__tests__/state.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/swarm.test.js +77 -0
- package/dist/src/multiagent/__tests__/swarm.test.js.map +1 -1
- package/dist/src/multiagent/events.d.ts +15 -1
- package/dist/src/multiagent/events.d.ts.map +1 -1
- package/dist/src/multiagent/events.js +18 -0
- package/dist/src/multiagent/events.js.map +1 -1
- package/dist/src/multiagent/graph.d.ts +59 -3
- package/dist/src/multiagent/graph.d.ts.map +1 -1
- package/dist/src/multiagent/graph.js +201 -34
- package/dist/src/multiagent/graph.js.map +1 -1
- package/dist/src/multiagent/multiagent.d.ts +77 -3
- package/dist/src/multiagent/multiagent.d.ts.map +1 -1
- package/dist/src/multiagent/multiagent.js +115 -1
- package/dist/src/multiagent/multiagent.js.map +1 -1
- package/dist/src/multiagent/nodes.d.ts +18 -0
- package/dist/src/multiagent/nodes.d.ts.map +1 -1
- package/dist/src/multiagent/nodes.js +69 -22
- package/dist/src/multiagent/nodes.js.map +1 -1
- package/dist/src/multiagent/state.d.ts +39 -3
- package/dist/src/multiagent/state.d.ts.map +1 -1
- package/dist/src/multiagent/state.js +80 -1
- package/dist/src/multiagent/state.js.map +1 -1
- package/dist/src/multiagent/swarm.d.ts +30 -1
- package/dist/src/multiagent/swarm.d.ts.map +1 -1
- package/dist/src/multiagent/swarm.js +166 -33
- package/dist/src/multiagent/swarm.js.map +1 -1
- package/dist/src/registry/__tests__/tool-registry.test.js +37 -0
- package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
- package/dist/src/registry/tool-registry.d.ts +13 -7
- package/dist/src/registry/tool-registry.d.ts.map +1 -1
- package/dist/src/registry/tool-registry.js +35 -10
- package/dist/src/registry/tool-registry.js.map +1 -1
- package/dist/src/retry/__tests__/backoff-strategy.test.d.ts +2 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.d.ts.map +1 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.js +116 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.js.map +1 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.d.ts +2 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.d.ts.map +1 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.js +225 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.js.map +1 -0
- package/dist/src/retry/backoff-strategy.d.ts +108 -0
- package/dist/src/retry/backoff-strategy.d.ts.map +1 -0
- package/dist/src/retry/backoff-strategy.js +86 -0
- package/dist/src/retry/backoff-strategy.js.map +1 -0
- package/dist/src/retry/default-model-retry-strategy.d.ts +76 -0
- package/dist/src/retry/default-model-retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/default-model-retry-strategy.js +104 -0
- package/dist/src/retry/default-model-retry-strategy.js.map +1 -0
- package/dist/src/retry/index.d.ts +8 -0
- package/dist/src/retry/index.d.ts.map +1 -0
- package/dist/src/retry/index.js +7 -0
- package/dist/src/retry/index.js.map +1 -0
- package/dist/src/retry/model-retry-strategy.d.ts +80 -0
- package/dist/src/retry/model-retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/model-retry-strategy.js +85 -0
- package/dist/src/retry/model-retry-strategy.js.map +1 -0
- package/dist/src/retry/retry-strategy.d.ts +34 -0
- package/dist/src/retry/retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/retry-strategy.js +25 -0
- package/dist/src/retry/retry-strategy.js.map +1 -0
- package/dist/src/session/__tests__/session-manager.test.js +84 -3
- package/dist/src/session/__tests__/session-manager.test.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +11 -2
- package/dist/src/session/session-manager.d.ts.map +1 -1
- package/dist/src/session/session-manager.js +17 -6
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/telemetry/__tests__/meter.test.js +5 -27
- package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
- package/dist/src/telemetry/meter.d.ts +12 -4
- package/dist/src/telemetry/meter.d.ts.map +1 -1
- package/dist/src/telemetry/meter.js +13 -8
- package/dist/src/telemetry/meter.js.map +1 -1
- package/dist/src/tools/__tests__/tool.test.js +24 -1
- package/dist/src/tools/__tests__/tool.test.js.map +1 -1
- package/dist/src/tools/function-tool.d.ts.map +1 -1
- package/dist/src/tools/function-tool.js +6 -1
- package/dist/src/tools/function-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts.map +1 -1
- package/dist/src/tools/mcp-tool.js +3 -2
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/tool.d.ts +10 -1
- package/dist/src/tools/tool.d.ts.map +1 -1
- package/dist/src/tools/tool.js +12 -0
- package/dist/src/tools/tool.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/types/__tests__/agent.test.js +97 -0
- package/dist/src/types/__tests__/agent.test.js.map +1 -1
- package/dist/src/types/agent.d.ts +48 -8
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js +28 -3
- package/dist/src/types/agent.js.map +1 -1
- package/dist/src/types/interrupt.d.ts +103 -0
- package/dist/src/types/interrupt.d.ts.map +1 -0
- package/dist/src/types/interrupt.js +63 -0
- package/dist/src/types/interrupt.js.map +1 -0
- package/dist/src/types/messages.d.ts +2 -1
- package/dist/src/types/messages.d.ts.map +1 -1
- package/dist/src/types/messages.js.map +1 -1
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js +292 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js +148 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.js +78 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/index.d.ts +23 -0
- package/dist/src/vended-plugins/context-offloader/index.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/index.js +21 -0
- package/dist/src/vended-plugins/context-offloader/index.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/plugin.d.ts +48 -0
- package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/plugin.js +244 -0
- package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/storage.d.ts +114 -0
- package/dist/src/vended-plugins/context-offloader/storage.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/storage.js +204 -0
- package/dist/src/vended-plugins/context-offloader/storage.js.map +1 -0
- package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js +12 -0
- package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js.map +1 -1
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +3 -0
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
- package/dist/src/vended-tools/bash/bash.d.ts.map +1 -1
- package/dist/src/vended-tools/bash/bash.js +0 -3
- package/dist/src/vended-tools/bash/bash.js.map +1 -1
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js +3 -0
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -1
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js +3 -0
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js.map +1 -1
- package/dist/src/vended-tools/notebook/notebook.d.ts +1 -1
- package/package.json +9 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { SlidingWindowConversationManager } from '../sliding-window-conversation-manager.js';
|
|
3
|
-
import { ContextWindowOverflowError, Message, TextBlock, ToolUseBlock, ToolResultBlock, } from '../../index.js';
|
|
4
|
-
import { AfterInvocationEvent, AfterModelCallEvent } from '../../hooks/events.js';
|
|
3
|
+
import { ContextWindowOverflowError, DocumentBlock, ImageBlock, JsonBlock, Message, TextBlock, ToolUseBlock, ToolResultBlock, VideoBlock, } from '../../index.js';
|
|
4
|
+
import { AfterInvocationEvent, AfterModelCallEvent, BeforeModelCallEvent } from '../../hooks/events.js';
|
|
5
5
|
import { createMockAgent, invokeTrackedHook } from '../../__fixtures__/agent-helpers.js';
|
|
6
6
|
async function triggerSlidingWindow(manager, agent) {
|
|
7
7
|
const pluginAgent = createMockAgent();
|
|
@@ -12,7 +12,7 @@ async function triggerSlidingWindow(manager, agent) {
|
|
|
12
12
|
async function triggerContextOverflow(manager, agent, error) {
|
|
13
13
|
const pluginAgent = createMockAgent();
|
|
14
14
|
manager.initAgent(pluginAgent);
|
|
15
|
-
const event = new AfterModelCallEvent({ agent, model: {}, error, invocationState: {} });
|
|
15
|
+
const event = new AfterModelCallEvent({ agent, model: {}, attemptCount: 1, error, invocationState: {} });
|
|
16
16
|
await invokeTrackedHook(pluginAgent, event);
|
|
17
17
|
return event;
|
|
18
18
|
}
|
|
@@ -46,7 +46,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
46
46
|
new ToolResultBlock({
|
|
47
47
|
toolUseId: 'tool-1',
|
|
48
48
|
status: 'success',
|
|
49
|
-
content: [new TextBlock('
|
|
49
|
+
content: [new TextBlock('x'.repeat(500))],
|
|
50
50
|
}),
|
|
51
51
|
],
|
|
52
52
|
}),
|
|
@@ -123,8 +123,10 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
describe('reduceContext - tool result truncation', () => {
|
|
126
|
-
it('truncates tool results
|
|
126
|
+
it('partially truncates large tool results preserving first and last 200 chars', async () => {
|
|
127
127
|
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
128
|
+
const middle = 'MIDDLE_CONTENT_TO_REMOVE'.repeat(10); // 240 chars, safely above MIN_TRUNCATION_GAIN
|
|
129
|
+
const original = 'A'.repeat(200) + middle + 'B'.repeat(200);
|
|
128
130
|
const messages = [
|
|
129
131
|
new Message({
|
|
130
132
|
role: 'user',
|
|
@@ -132,19 +134,41 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
132
134
|
new ToolResultBlock({
|
|
133
135
|
toolUseId: 'tool-1',
|
|
134
136
|
status: 'success',
|
|
135
|
-
content: [new TextBlock(
|
|
137
|
+
content: [new TextBlock(original)],
|
|
136
138
|
}),
|
|
137
139
|
],
|
|
138
140
|
}),
|
|
139
141
|
];
|
|
140
142
|
const mockAgent = createMockAgent({ messages });
|
|
141
143
|
await triggerContextOverflow(manager, mockAgent, new ContextWindowOverflowError('Context overflow'));
|
|
142
|
-
const
|
|
143
|
-
expect(
|
|
144
|
-
|
|
144
|
+
const expectedText = `${'A'.repeat(200)}\n<truncated chars="${middle.length}"/>\n${'B'.repeat(200)}`;
|
|
145
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
146
|
+
toolUseId: 'tool-1',
|
|
147
|
+
status: 'success',
|
|
148
|
+
content: [new TextBlock(expectedText)],
|
|
149
|
+
}));
|
|
145
150
|
});
|
|
146
|
-
it('
|
|
151
|
+
it('leaves small tool results unchanged', () => {
|
|
147
152
|
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
153
|
+
const messages = [
|
|
154
|
+
new Message({
|
|
155
|
+
role: 'user',
|
|
156
|
+
content: [
|
|
157
|
+
new ToolResultBlock({
|
|
158
|
+
toolUseId: 'tool-1',
|
|
159
|
+
status: 'success',
|
|
160
|
+
content: [new TextBlock('Small result')],
|
|
161
|
+
}),
|
|
162
|
+
],
|
|
163
|
+
}),
|
|
164
|
+
];
|
|
165
|
+
const result = manager._truncateToolResults(messages, 0);
|
|
166
|
+
expect(result).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
it('finds oldest message with tool results', async () => {
|
|
169
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
170
|
+
const firstOriginal = 'F'.repeat(500);
|
|
171
|
+
const secondOriginal = 'S'.repeat(500);
|
|
148
172
|
const messages = [
|
|
149
173
|
new Message({ role: 'user', content: [new TextBlock('Message 1')] }),
|
|
150
174
|
new Message({
|
|
@@ -153,7 +177,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
153
177
|
new ToolResultBlock({
|
|
154
178
|
toolUseId: 'tool-1',
|
|
155
179
|
status: 'success',
|
|
156
|
-
content: [new TextBlock(
|
|
180
|
+
content: [new TextBlock(firstOriginal)],
|
|
157
181
|
}),
|
|
158
182
|
],
|
|
159
183
|
}),
|
|
@@ -164,21 +188,25 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
164
188
|
new ToolResultBlock({
|
|
165
189
|
toolUseId: 'tool-2',
|
|
166
190
|
status: 'success',
|
|
167
|
-
content: [new TextBlock(
|
|
191
|
+
content: [new TextBlock(secondOriginal)],
|
|
168
192
|
}),
|
|
169
193
|
],
|
|
170
194
|
}),
|
|
171
195
|
];
|
|
172
196
|
const mockAgent = createMockAgent({ messages });
|
|
173
197
|
await triggerContextOverflow(manager, mockAgent, new ContextWindowOverflowError('Context overflow'));
|
|
174
|
-
//
|
|
175
|
-
const
|
|
176
|
-
expect(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
expect(
|
|
198
|
+
// Oldest tool-result message is truncated; newer one is untouched.
|
|
199
|
+
const expectedTruncated = `${'F'.repeat(200)}\n<truncated chars="100"/>\n${'F'.repeat(200)}`;
|
|
200
|
+
expect(messages[1].content[0]).toEqual(new ToolResultBlock({
|
|
201
|
+
toolUseId: 'tool-1',
|
|
202
|
+
status: 'success',
|
|
203
|
+
content: [new TextBlock(expectedTruncated)],
|
|
204
|
+
}));
|
|
205
|
+
expect(messages[3].content[0]).toEqual(new ToolResultBlock({
|
|
206
|
+
toolUseId: 'tool-2',
|
|
207
|
+
status: 'success',
|
|
208
|
+
content: [new TextBlock(secondOriginal)],
|
|
209
|
+
}));
|
|
182
210
|
});
|
|
183
211
|
it('returns after successful truncation without trimming messages', async () => {
|
|
184
212
|
const manager = new SlidingWindowConversationManager({ windowSize: 2, shouldTruncateResults: true });
|
|
@@ -191,7 +219,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
191
219
|
new ToolResultBlock({
|
|
192
220
|
toolUseId: 'tool-1',
|
|
193
221
|
status: 'success',
|
|
194
|
-
content: [new TextBlock('
|
|
222
|
+
content: [new TextBlock('L'.repeat(500))],
|
|
195
223
|
}),
|
|
196
224
|
],
|
|
197
225
|
}),
|
|
@@ -217,7 +245,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
217
245
|
new ToolResultBlock({
|
|
218
246
|
toolUseId: 'tool-1',
|
|
219
247
|
status: 'success',
|
|
220
|
-
content: [new TextBlock('
|
|
248
|
+
content: [new TextBlock('L'.repeat(500))],
|
|
221
249
|
}),
|
|
222
250
|
],
|
|
223
251
|
}),
|
|
@@ -228,24 +256,30 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
228
256
|
expect(mockAgent.messages).toHaveLength(3);
|
|
229
257
|
expect(mockAgent.messages[0].role).toBe('user');
|
|
230
258
|
// Tool result should not be truncated
|
|
231
|
-
|
|
232
|
-
|
|
259
|
+
expect(mockAgent.messages[2].content[0]).toEqual(new ToolResultBlock({
|
|
260
|
+
toolUseId: 'tool-1',
|
|
261
|
+
status: 'success',
|
|
262
|
+
content: [new TextBlock('L'.repeat(500))],
|
|
263
|
+
}));
|
|
233
264
|
});
|
|
234
|
-
it('does not truncate already-truncated results', async () => {
|
|
265
|
+
it('does not re-truncate already-truncated results', async () => {
|
|
235
266
|
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
267
|
+
// Produced by an earlier run: 200 chars + marker + 200 chars = well under the 450-char
|
|
268
|
+
// threshold below which truncation is not worth running.
|
|
269
|
+
const alreadyTruncated = 'A'.repeat(200) + '\n<truncated chars="1000"/>\n' + 'B'.repeat(200);
|
|
236
270
|
const messages = [
|
|
237
271
|
new Message({
|
|
238
272
|
role: 'user',
|
|
239
273
|
content: [
|
|
240
274
|
new ToolResultBlock({
|
|
241
275
|
toolUseId: 'tool-1',
|
|
242
|
-
status: '
|
|
243
|
-
content: [new TextBlock(
|
|
276
|
+
status: 'success',
|
|
277
|
+
content: [new TextBlock(alreadyTruncated)],
|
|
244
278
|
}),
|
|
245
279
|
],
|
|
246
280
|
}),
|
|
247
281
|
];
|
|
248
|
-
// First call should return false (
|
|
282
|
+
// First call should return false (too short to gain anything from re-truncating)
|
|
249
283
|
const result = manager._truncateToolResults(messages, 0);
|
|
250
284
|
expect(result).toBe(false);
|
|
251
285
|
// reduceContext should fall through to message trimming
|
|
@@ -255,19 +289,316 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
255
289
|
content: [
|
|
256
290
|
new ToolResultBlock({
|
|
257
291
|
toolUseId: 'tool-1',
|
|
258
|
-
status: '
|
|
259
|
-
content: [new TextBlock(
|
|
292
|
+
status: 'success',
|
|
293
|
+
content: [new TextBlock(alreadyTruncated)],
|
|
260
294
|
}),
|
|
261
295
|
],
|
|
262
296
|
}),
|
|
263
297
|
new Message({ role: 'assistant', content: [new TextBlock('Response')] }),
|
|
264
298
|
new Message({ role: 'user', content: [new TextBlock('Message')] }),
|
|
265
299
|
];
|
|
266
|
-
const mockAgent = { messages: messages2 };
|
|
300
|
+
const mockAgent = createMockAgent({ messages: messages2 });
|
|
267
301
|
await triggerContextOverflow(manager, mockAgent, new ContextWindowOverflowError('Context overflow'));
|
|
268
302
|
// Should have trimmed messages since truncation was skipped
|
|
269
303
|
expect(mockAgent.messages.length).toBeLessThan(3);
|
|
270
304
|
});
|
|
305
|
+
it('replaces image blocks nested in tool results with descriptive placeholders', () => {
|
|
306
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
307
|
+
const bytes = new Uint8Array(1234);
|
|
308
|
+
const messages = [
|
|
309
|
+
new Message({
|
|
310
|
+
role: 'user',
|
|
311
|
+
content: [
|
|
312
|
+
new ToolResultBlock({
|
|
313
|
+
toolUseId: 'tool-1',
|
|
314
|
+
status: 'success',
|
|
315
|
+
content: [new ImageBlock({ format: 'png', source: { bytes } }), new TextBlock('tail')],
|
|
316
|
+
}),
|
|
317
|
+
],
|
|
318
|
+
}),
|
|
319
|
+
];
|
|
320
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
321
|
+
expect(changed).toBe(true);
|
|
322
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
323
|
+
toolUseId: 'tool-1',
|
|
324
|
+
status: 'success',
|
|
325
|
+
content: [new TextBlock('[image: png, source: bytes, 1234 bytes]'), new TextBlock('tail')],
|
|
326
|
+
}));
|
|
327
|
+
});
|
|
328
|
+
it('preserves the error field on truncated tool results', () => {
|
|
329
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
330
|
+
const originalError = new Error('tool blew up');
|
|
331
|
+
const messages = [
|
|
332
|
+
new Message({
|
|
333
|
+
role: 'user',
|
|
334
|
+
content: [
|
|
335
|
+
new ToolResultBlock({
|
|
336
|
+
toolUseId: 'tool-1',
|
|
337
|
+
status: 'error',
|
|
338
|
+
content: [new TextBlock('x'.repeat(500))],
|
|
339
|
+
error: originalError,
|
|
340
|
+
}),
|
|
341
|
+
],
|
|
342
|
+
}),
|
|
343
|
+
];
|
|
344
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
345
|
+
expect(changed).toBe(true);
|
|
346
|
+
const expectedText = `${'x'.repeat(200)}\n<truncated chars="100"/>\n${'x'.repeat(200)}`;
|
|
347
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
348
|
+
toolUseId: 'tool-1',
|
|
349
|
+
status: 'error',
|
|
350
|
+
content: [new TextBlock(expectedText)],
|
|
351
|
+
error: originalError,
|
|
352
|
+
}));
|
|
353
|
+
});
|
|
354
|
+
it('image placeholder reflects non-bytes source kinds honestly', () => {
|
|
355
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
356
|
+
const messages = [
|
|
357
|
+
new Message({
|
|
358
|
+
role: 'user',
|
|
359
|
+
content: [
|
|
360
|
+
new ToolResultBlock({
|
|
361
|
+
toolUseId: 'tool-1',
|
|
362
|
+
status: 'success',
|
|
363
|
+
content: [
|
|
364
|
+
new ImageBlock({ format: 'jpeg', source: { url: 'https://example.com/x.jpg' } }),
|
|
365
|
+
new ImageBlock({ format: 'png', source: { location: { type: 's3', uri: 's3://bucket/key' } } }),
|
|
366
|
+
],
|
|
367
|
+
}),
|
|
368
|
+
],
|
|
369
|
+
}),
|
|
370
|
+
];
|
|
371
|
+
manager._truncateToolResults(messages, 0);
|
|
372
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
373
|
+
toolUseId: 'tool-1',
|
|
374
|
+
status: 'success',
|
|
375
|
+
content: [new TextBlock('[image: jpeg, source: url]'), new TextBlock('[image: png, source: s3]')],
|
|
376
|
+
}));
|
|
377
|
+
});
|
|
378
|
+
it('replaces video bytes blocks with a descriptive placeholder', () => {
|
|
379
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
380
|
+
const messages = [
|
|
381
|
+
new Message({
|
|
382
|
+
role: 'user',
|
|
383
|
+
content: [
|
|
384
|
+
new ToolResultBlock({
|
|
385
|
+
toolUseId: 'tool-1',
|
|
386
|
+
status: 'success',
|
|
387
|
+
content: [new VideoBlock({ format: 'mp4', source: { bytes: new Uint8Array(4096) } })],
|
|
388
|
+
}),
|
|
389
|
+
],
|
|
390
|
+
}),
|
|
391
|
+
];
|
|
392
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
393
|
+
expect(changed).toBe(true);
|
|
394
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
395
|
+
toolUseId: 'tool-1',
|
|
396
|
+
status: 'success',
|
|
397
|
+
content: [new TextBlock('[video: mp4, source: bytes, 4096 bytes]')],
|
|
398
|
+
}));
|
|
399
|
+
});
|
|
400
|
+
it('replaces video s3 blocks with a descriptive placeholder', () => {
|
|
401
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
402
|
+
const messages = [
|
|
403
|
+
new Message({
|
|
404
|
+
role: 'user',
|
|
405
|
+
content: [
|
|
406
|
+
new ToolResultBlock({
|
|
407
|
+
toolUseId: 'tool-1',
|
|
408
|
+
status: 'success',
|
|
409
|
+
content: [
|
|
410
|
+
new VideoBlock({
|
|
411
|
+
format: 'mp4',
|
|
412
|
+
source: { location: { type: 's3', uri: 's3://bucket/key' } },
|
|
413
|
+
}),
|
|
414
|
+
],
|
|
415
|
+
}),
|
|
416
|
+
],
|
|
417
|
+
}),
|
|
418
|
+
];
|
|
419
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
420
|
+
expect(changed).toBe(true);
|
|
421
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
422
|
+
toolUseId: 'tool-1',
|
|
423
|
+
status: 'success',
|
|
424
|
+
content: [new TextBlock('[video: mp4, source: s3]')],
|
|
425
|
+
}));
|
|
426
|
+
});
|
|
427
|
+
it('replaces document bytes blocks with a descriptive placeholder', () => {
|
|
428
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
429
|
+
const messages = [
|
|
430
|
+
new Message({
|
|
431
|
+
role: 'user',
|
|
432
|
+
content: [
|
|
433
|
+
new ToolResultBlock({
|
|
434
|
+
toolUseId: 'tool-1',
|
|
435
|
+
status: 'success',
|
|
436
|
+
content: [
|
|
437
|
+
new DocumentBlock({
|
|
438
|
+
name: 'report',
|
|
439
|
+
format: 'pdf',
|
|
440
|
+
source: { bytes: new Uint8Array(8192) },
|
|
441
|
+
}),
|
|
442
|
+
],
|
|
443
|
+
}),
|
|
444
|
+
],
|
|
445
|
+
}),
|
|
446
|
+
];
|
|
447
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
448
|
+
expect(changed).toBe(true);
|
|
449
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
450
|
+
toolUseId: 'tool-1',
|
|
451
|
+
status: 'success',
|
|
452
|
+
content: [new TextBlock('[document: report, pdf, source: bytes, 8192 bytes]')],
|
|
453
|
+
}));
|
|
454
|
+
});
|
|
455
|
+
it('replaces document s3 blocks with a descriptive placeholder', () => {
|
|
456
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
457
|
+
const messages = [
|
|
458
|
+
new Message({
|
|
459
|
+
role: 'user',
|
|
460
|
+
content: [
|
|
461
|
+
new ToolResultBlock({
|
|
462
|
+
toolUseId: 'tool-1',
|
|
463
|
+
status: 'success',
|
|
464
|
+
content: [
|
|
465
|
+
new DocumentBlock({
|
|
466
|
+
name: 'spec',
|
|
467
|
+
format: 'pdf',
|
|
468
|
+
source: { location: { type: 's3', uri: 's3://b/k' } },
|
|
469
|
+
}),
|
|
470
|
+
],
|
|
471
|
+
}),
|
|
472
|
+
],
|
|
473
|
+
}),
|
|
474
|
+
];
|
|
475
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
476
|
+
expect(changed).toBe(true);
|
|
477
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
478
|
+
toolUseId: 'tool-1',
|
|
479
|
+
status: 'success',
|
|
480
|
+
content: [new TextBlock('[document: spec, pdf, source: s3]')],
|
|
481
|
+
}));
|
|
482
|
+
});
|
|
483
|
+
it('partially truncates large text inside a document text source', () => {
|
|
484
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
485
|
+
const middle = 'M'.repeat(240);
|
|
486
|
+
const originalText = 'A'.repeat(200) + middle + 'B'.repeat(200);
|
|
487
|
+
const messages = [
|
|
488
|
+
new Message({
|
|
489
|
+
role: 'user',
|
|
490
|
+
content: [
|
|
491
|
+
new ToolResultBlock({
|
|
492
|
+
toolUseId: 'tool-1',
|
|
493
|
+
status: 'success',
|
|
494
|
+
content: [new DocumentBlock({ name: 'report', format: 'txt', source: { text: originalText } })],
|
|
495
|
+
}),
|
|
496
|
+
],
|
|
497
|
+
}),
|
|
498
|
+
];
|
|
499
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
500
|
+
expect(changed).toBe(true);
|
|
501
|
+
const expectedText = `${'A'.repeat(200)}\n<truncated chars="${middle.length}"/>\n${'B'.repeat(200)}`;
|
|
502
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
503
|
+
toolUseId: 'tool-1',
|
|
504
|
+
status: 'success',
|
|
505
|
+
content: [new DocumentBlock({ name: 'report', format: 'txt', source: { text: expectedText } })],
|
|
506
|
+
}));
|
|
507
|
+
});
|
|
508
|
+
it('leaves small text inside a document text source unchanged', () => {
|
|
509
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
510
|
+
const messages = [
|
|
511
|
+
new Message({
|
|
512
|
+
role: 'user',
|
|
513
|
+
content: [
|
|
514
|
+
new ToolResultBlock({
|
|
515
|
+
toolUseId: 'tool-1',
|
|
516
|
+
status: 'success',
|
|
517
|
+
content: [new DocumentBlock({ name: 'short', format: 'txt', source: { text: 'hello' } })],
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
520
|
+
}),
|
|
521
|
+
];
|
|
522
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
523
|
+
expect(changed).toBe(false);
|
|
524
|
+
});
|
|
525
|
+
it('truncates long nested text blocks inside a document content source', () => {
|
|
526
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
527
|
+
const longText = 'A'.repeat(200) + 'M'.repeat(240) + 'B'.repeat(200);
|
|
528
|
+
const messages = [
|
|
529
|
+
new Message({
|
|
530
|
+
role: 'user',
|
|
531
|
+
content: [
|
|
532
|
+
new ToolResultBlock({
|
|
533
|
+
toolUseId: 'tool-1',
|
|
534
|
+
status: 'success',
|
|
535
|
+
content: [
|
|
536
|
+
new DocumentBlock({
|
|
537
|
+
name: 'pages',
|
|
538
|
+
format: 'txt',
|
|
539
|
+
source: { content: [new TextBlock(longText), new TextBlock('short')] },
|
|
540
|
+
}),
|
|
541
|
+
],
|
|
542
|
+
}),
|
|
543
|
+
],
|
|
544
|
+
}),
|
|
545
|
+
];
|
|
546
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
547
|
+
expect(changed).toBe(true);
|
|
548
|
+
const expectedText = `${'A'.repeat(200)}\n<truncated chars="240"/>\n${'B'.repeat(200)}`;
|
|
549
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
550
|
+
toolUseId: 'tool-1',
|
|
551
|
+
status: 'success',
|
|
552
|
+
content: [
|
|
553
|
+
new DocumentBlock({
|
|
554
|
+
name: 'pages',
|
|
555
|
+
format: 'txt',
|
|
556
|
+
source: { content: [new TextBlock(expectedText), new TextBlock('short')] },
|
|
557
|
+
}),
|
|
558
|
+
],
|
|
559
|
+
}));
|
|
560
|
+
});
|
|
561
|
+
it('replaces large json blocks with a size placeholder', () => {
|
|
562
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
563
|
+
const big = { payload: 'x'.repeat(1000) };
|
|
564
|
+
const size = JSON.stringify(big).length;
|
|
565
|
+
const messages = [
|
|
566
|
+
new Message({
|
|
567
|
+
role: 'user',
|
|
568
|
+
content: [
|
|
569
|
+
new ToolResultBlock({
|
|
570
|
+
toolUseId: 'tool-1',
|
|
571
|
+
status: 'success',
|
|
572
|
+
content: [new JsonBlock({ json: big })],
|
|
573
|
+
}),
|
|
574
|
+
],
|
|
575
|
+
}),
|
|
576
|
+
];
|
|
577
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
578
|
+
expect(changed).toBe(true);
|
|
579
|
+
expect(messages[0].content[0]).toEqual(new ToolResultBlock({
|
|
580
|
+
toolUseId: 'tool-1',
|
|
581
|
+
status: 'success',
|
|
582
|
+
content: [new TextBlock(`[json: ${size} chars]`)],
|
|
583
|
+
}));
|
|
584
|
+
});
|
|
585
|
+
it('leaves small json blocks unchanged', () => {
|
|
586
|
+
const manager = new SlidingWindowConversationManager({ shouldTruncateResults: true });
|
|
587
|
+
const messages = [
|
|
588
|
+
new Message({
|
|
589
|
+
role: 'user',
|
|
590
|
+
content: [
|
|
591
|
+
new ToolResultBlock({
|
|
592
|
+
toolUseId: 'tool-1',
|
|
593
|
+
status: 'success',
|
|
594
|
+
content: [new JsonBlock({ json: { ok: true } })],
|
|
595
|
+
}),
|
|
596
|
+
],
|
|
597
|
+
}),
|
|
598
|
+
];
|
|
599
|
+
const changed = manager._truncateToolResults(messages, 0);
|
|
600
|
+
expect(changed).toBe(false);
|
|
601
|
+
});
|
|
271
602
|
it('does not call truncateToolResults unless an error is passed in', async () => {
|
|
272
603
|
const manager = new SlidingWindowConversationManager({ windowSize: 2, shouldTruncateResults: true });
|
|
273
604
|
const messages = [
|
|
@@ -534,6 +865,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
534
865
|
const event = new AfterModelCallEvent({
|
|
535
866
|
agent: mockAgent,
|
|
536
867
|
model: {},
|
|
868
|
+
attemptCount: 1,
|
|
537
869
|
error: originalError,
|
|
538
870
|
invocationState: {},
|
|
539
871
|
});
|
|
@@ -544,7 +876,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
544
876
|
});
|
|
545
877
|
});
|
|
546
878
|
describe('helper methods', () => {
|
|
547
|
-
describe('
|
|
879
|
+
describe('findOldestMessageWithToolResults', () => {
|
|
548
880
|
it('returns correct index when tool results exist', () => {
|
|
549
881
|
const manager = new SlidingWindowConversationManager();
|
|
550
882
|
const messages = [
|
|
@@ -561,7 +893,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
561
893
|
}),
|
|
562
894
|
new Message({ role: 'assistant', content: [new TextBlock('Response')] }),
|
|
563
895
|
];
|
|
564
|
-
const index = manager.
|
|
896
|
+
const index = manager._findOldestMessageWithToolResults(messages);
|
|
565
897
|
expect(index).toBe(1);
|
|
566
898
|
});
|
|
567
899
|
it('returns undefined when no tool results exist', () => {
|
|
@@ -570,10 +902,10 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
570
902
|
new Message({ role: 'user', content: [new TextBlock('Message 1')] }),
|
|
571
903
|
new Message({ role: 'assistant', content: [new TextBlock('Response 1')] }),
|
|
572
904
|
];
|
|
573
|
-
const index = manager.
|
|
905
|
+
const index = manager._findOldestMessageWithToolResults(messages);
|
|
574
906
|
expect(index).toBeUndefined();
|
|
575
907
|
});
|
|
576
|
-
it('iterates
|
|
908
|
+
it('iterates forward from start', () => {
|
|
577
909
|
const manager = new SlidingWindowConversationManager();
|
|
578
910
|
const messages = [
|
|
579
911
|
new Message({
|
|
@@ -598,9 +930,9 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
598
930
|
],
|
|
599
931
|
}),
|
|
600
932
|
];
|
|
601
|
-
const index = manager.
|
|
602
|
-
// Should find the
|
|
603
|
-
expect(index).toBe(
|
|
933
|
+
const index = manager._findOldestMessageWithToolResults(messages);
|
|
934
|
+
// Should find the first one (index 0), not the last (index 2)
|
|
935
|
+
expect(index).toBe(0);
|
|
604
936
|
});
|
|
605
937
|
});
|
|
606
938
|
describe('truncateToolResults', () => {
|
|
@@ -613,7 +945,7 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
613
945
|
new ToolResultBlock({
|
|
614
946
|
toolUseId: 'id-1',
|
|
615
947
|
status: 'success',
|
|
616
|
-
content: [new TextBlock('
|
|
948
|
+
content: [new TextBlock('x'.repeat(500))],
|
|
617
949
|
}),
|
|
618
950
|
],
|
|
619
951
|
}),
|
|
@@ -623,14 +955,15 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
623
955
|
});
|
|
624
956
|
it('returns false when already truncated', () => {
|
|
625
957
|
const manager = new SlidingWindowConversationManager();
|
|
958
|
+
const alreadyTruncated = 'A'.repeat(200) + '\n<truncated chars="1000"/>\n' + 'B'.repeat(200);
|
|
626
959
|
const messages = [
|
|
627
960
|
new Message({
|
|
628
961
|
role: 'user',
|
|
629
962
|
content: [
|
|
630
963
|
new ToolResultBlock({
|
|
631
964
|
toolUseId: 'id-1',
|
|
632
|
-
status: '
|
|
633
|
-
content: [new TextBlock(
|
|
965
|
+
status: 'success',
|
|
966
|
+
content: [new TextBlock(alreadyTruncated)],
|
|
634
967
|
}),
|
|
635
968
|
],
|
|
636
969
|
}),
|
|
@@ -646,5 +979,53 @@ describe('SlidingWindowConversationManager', () => {
|
|
|
646
979
|
});
|
|
647
980
|
});
|
|
648
981
|
});
|
|
982
|
+
describe('reduceOnThreshold', () => {
|
|
983
|
+
it('trims oldest messages when compressionThreshold is exceeded', async () => {
|
|
984
|
+
const manager = new SlidingWindowConversationManager({
|
|
985
|
+
windowSize: 4,
|
|
986
|
+
proactiveCompression: { compressionThreshold: 0.7 },
|
|
987
|
+
});
|
|
988
|
+
const messages = [
|
|
989
|
+
new Message({ role: 'user', content: [new TextBlock('Message 1')] }),
|
|
990
|
+
new Message({ role: 'assistant', content: [new TextBlock('Response 1')] }),
|
|
991
|
+
new Message({ role: 'user', content: [new TextBlock('Message 2')] }),
|
|
992
|
+
new Message({ role: 'assistant', content: [new TextBlock('Response 2')] }),
|
|
993
|
+
new Message({ role: 'user', content: [new TextBlock('Message 3')] }),
|
|
994
|
+
new Message({ role: 'assistant', content: [new TextBlock('Response 3')] }),
|
|
995
|
+
];
|
|
996
|
+
const mockModel = { getConfig: () => ({ contextWindowLimit: 1000 }) };
|
|
997
|
+
const mockAgent = createMockAgent({ messages });
|
|
998
|
+
manager.initAgent(mockAgent);
|
|
999
|
+
const event = new BeforeModelCallEvent({
|
|
1000
|
+
agent: mockAgent,
|
|
1001
|
+
model: mockModel,
|
|
1002
|
+
invocationState: {},
|
|
1003
|
+
projectedInputTokens: 800,
|
|
1004
|
+
});
|
|
1005
|
+
await invokeTrackedHook(mockAgent, event);
|
|
1006
|
+
expect(mockAgent.messages.length).toBe(4);
|
|
1007
|
+
});
|
|
1008
|
+
it('does not trim when below compressionThreshold', async () => {
|
|
1009
|
+
const manager = new SlidingWindowConversationManager({
|
|
1010
|
+
windowSize: 4,
|
|
1011
|
+
proactiveCompression: { compressionThreshold: 0.7 },
|
|
1012
|
+
});
|
|
1013
|
+
const messages = [
|
|
1014
|
+
new Message({ role: 'user', content: [new TextBlock('Message 1')] }),
|
|
1015
|
+
new Message({ role: 'assistant', content: [new TextBlock('Response 1')] }),
|
|
1016
|
+
];
|
|
1017
|
+
const mockModel = { getConfig: () => ({ contextWindowLimit: 1000 }) };
|
|
1018
|
+
const mockAgent = createMockAgent({ messages });
|
|
1019
|
+
manager.initAgent(mockAgent);
|
|
1020
|
+
const event = new BeforeModelCallEvent({
|
|
1021
|
+
agent: mockAgent,
|
|
1022
|
+
model: mockModel,
|
|
1023
|
+
invocationState: {},
|
|
1024
|
+
projectedInputTokens: 500,
|
|
1025
|
+
});
|
|
1026
|
+
await invokeTrackedHook(mockAgent, event);
|
|
1027
|
+
expect(mockAgent.messages).toHaveLength(2);
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
649
1030
|
});
|
|
650
1031
|
//# sourceMappingURL=sliding-window-conversation-manager.test.js.map
|