@strands-agents/sdk 1.0.0 → 1.1.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/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 +259 -0
- package/dist/src/__tests__/interrupt.test.js.map +1 -0
- package/dist/src/__tests__/mcp.test.js +226 -0
- 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 +730 -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 +118 -0
- package/dist/src/agent/__tests__/agent.test.js.map +1 -1
- package/dist/src/agent/__tests__/snapshot.test.js +50 -4
- package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
- package/dist/src/agent/agent.d.ts +35 -4
- package/dist/src/agent/agent.d.ts.map +1 -1
- package/dist/src/agent/agent.js +548 -222
- package/dist/src/agent/agent.js.map +1 -1
- package/dist/src/agent/snapshot.d.ts +2 -2
- package/dist/src/agent/snapshot.d.ts.map +1 -1
- package/dist/src/agent/snapshot.js +14 -2
- 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 +51 -2
- 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 +17 -3
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js +10 -4
- 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 +102 -30
- package/dist/src/hooks/events.d.ts.map +1 -1
- package/dist/src/hooks/events.js +87 -6
- package/dist/src/hooks/events.js.map +1 -1
- package/dist/src/hooks/index.d.ts +3 -2
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +1 -0
- 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 +9 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interrupt.d.ts +220 -0
- package/dist/src/interrupt.d.ts.map +1 -0
- package/dist/src/interrupt.js +274 -0
- package/dist/src/interrupt.js.map +1 -0
- package/dist/src/mcp.d.ts +23 -2
- package/dist/src/mcp.d.ts.map +1 -1
- package/dist/src/mcp.js +77 -18
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/models/__tests__/anthropic.test.js +55 -0
- package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
- package/dist/src/models/__tests__/bedrock.test.js +115 -0
- 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 +58 -0
- package/dist/src/models/__tests__/google.test.js.map +1 -1
- package/dist/src/models/anthropic.d.ts +8 -0
- package/dist/src/models/anthropic.d.ts.map +1 -1
- package/dist/src/models/anthropic.js +4 -2
- package/dist/src/models/anthropic.js.map +1 -1
- package/dist/src/models/bedrock.d.ts +15 -0
- package/dist/src/models/bedrock.d.ts.map +1 -1
- package/dist/src/models/bedrock.js +58 -4
- 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 +8 -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 +45 -0
- package/dist/src/models/openai/__tests__/chat.test.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__/nodes.test.js +13 -0
- package/dist/src/multiagent/__tests__/nodes.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/graph.d.ts +22 -2
- package/dist/src/multiagent/graph.d.ts.map +1 -1
- package/dist/src/multiagent/graph.js +42 -3
- package/dist/src/multiagent/graph.js.map +1 -1
- package/dist/src/multiagent/multiagent.d.ts +5 -3
- package/dist/src/multiagent/multiagent.d.ts.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 +14 -1
- package/dist/src/multiagent/nodes.js.map +1 -1
- package/dist/src/multiagent/swarm.d.ts +15 -1
- package/dist/src/multiagent/swarm.d.ts.map +1 -1
- package/dist/src/multiagent/swarm.js +46 -3
- package/dist/src/multiagent/swarm.js.map +1 -1
- package/dist/src/registry/__tests__/tool-registry.test.js +11 -0
- package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
- package/dist/src/registry/tool-registry.d.ts +4 -0
- package/dist/src/registry/tool-registry.d.ts.map +1 -1
- package/dist/src/registry/tool-registry.js +6 -0
- 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 +39 -0
- package/dist/src/session/__tests__/session-manager.test.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +6 -0
- package/dist/src/session/session-manager.d.ts.map +1 -1
- package/dist/src/session/session-manager.js +8 -0
- package/dist/src/session/session-manager.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/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/agent.d.ts +22 -3
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js +8 -0
- 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/package.json +9 -5
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { ContextOffloader } from '../plugin.js';
|
|
3
|
+
import { InMemoryStorage } from '../storage.js';
|
|
4
|
+
import { AfterToolCallEvent } from '../../../hooks/events.js';
|
|
5
|
+
import { TextBlock, JsonBlock, ToolResultBlock } from '../../../types/messages.js';
|
|
6
|
+
import { ImageBlock, VideoBlock, DocumentBlock } from '../../../types/media.js';
|
|
7
|
+
import { createMockAgent, invokeTrackedHook } from '../../../__fixtures__/agent-helpers.js';
|
|
8
|
+
import { MockMessageModel } from '../../../__fixtures__/mock-message-model.js';
|
|
9
|
+
const mockModel = new MockMessageModel();
|
|
10
|
+
function makeMockAgent() {
|
|
11
|
+
return createMockAgent({ extra: { model: mockModel } });
|
|
12
|
+
}
|
|
13
|
+
function makeEvent(content, overrides) {
|
|
14
|
+
const agent = makeMockAgent();
|
|
15
|
+
const result = new ToolResultBlock({
|
|
16
|
+
toolUseId: 'tool-123',
|
|
17
|
+
status: overrides?.status ?? 'success',
|
|
18
|
+
content,
|
|
19
|
+
});
|
|
20
|
+
return new AfterToolCallEvent({
|
|
21
|
+
agent,
|
|
22
|
+
toolUse: { name: overrides?.toolName ?? 'some_tool', toolUseId: 'tool-123', input: {} },
|
|
23
|
+
tool: undefined,
|
|
24
|
+
result,
|
|
25
|
+
invocationState: {},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
describe('ContextOffloader', () => {
|
|
29
|
+
describe('constructor validation', () => {
|
|
30
|
+
it('throws if maxResultTokens is not positive', () => {
|
|
31
|
+
expect(() => new ContextOffloader({ storage: new InMemoryStorage(), maxResultTokens: 0 })).toThrow('maxResultTokens must be positive');
|
|
32
|
+
});
|
|
33
|
+
it('throws if previewTokens is negative', () => {
|
|
34
|
+
expect(() => new ContextOffloader({ storage: new InMemoryStorage(), previewTokens: -1 })).toThrow('previewTokens must be non-negative');
|
|
35
|
+
});
|
|
36
|
+
it('throws if previewTokens >= maxResultTokens', () => {
|
|
37
|
+
expect(() => new ContextOffloader({ storage: new InMemoryStorage(), maxResultTokens: 100, previewTokens: 100 })).toThrow('previewTokens must be less than maxResultTokens');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('plugin interface', () => {
|
|
41
|
+
it('has correct name', () => {
|
|
42
|
+
const plugin = new ContextOffloader({ storage: new InMemoryStorage() });
|
|
43
|
+
expect(plugin.name).toBe('strands:context-offloader');
|
|
44
|
+
});
|
|
45
|
+
it('registers AfterToolCallEvent hook', () => {
|
|
46
|
+
const plugin = new ContextOffloader({ storage: new InMemoryStorage() });
|
|
47
|
+
const agent = createMockAgent();
|
|
48
|
+
plugin.initAgent(agent);
|
|
49
|
+
expect(agent.trackedHooks).toHaveLength(1);
|
|
50
|
+
expect(agent.trackedHooks[0].eventType).toBe(AfterToolCallEvent);
|
|
51
|
+
});
|
|
52
|
+
it('returns retrieval tool by default', () => {
|
|
53
|
+
const plugin = new ContextOffloader({ storage: new InMemoryStorage() });
|
|
54
|
+
const tools = plugin.getTools();
|
|
55
|
+
expect(tools).toHaveLength(1);
|
|
56
|
+
expect(tools[0].name).toBe('retrieve_offloaded_content');
|
|
57
|
+
});
|
|
58
|
+
it('returns empty tools when includeRetrievalTool is false', () => {
|
|
59
|
+
const plugin = new ContextOffloader({ storage: new InMemoryStorage(), includeRetrievalTool: false });
|
|
60
|
+
expect(plugin.getTools()).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('hook behavior', () => {
|
|
64
|
+
it('does not offload results below threshold', async () => {
|
|
65
|
+
const storage = new InMemoryStorage();
|
|
66
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 2500 });
|
|
67
|
+
const agent = createMockAgent();
|
|
68
|
+
plugin.initAgent(agent);
|
|
69
|
+
const event = makeEvent([new TextBlock('short text')]);
|
|
70
|
+
await invokeTrackedHook(agent, event);
|
|
71
|
+
expect(event.result.content).toHaveLength(1);
|
|
72
|
+
expect(event.result.content[0]).toBeInstanceOf(TextBlock);
|
|
73
|
+
expect(event.result.content[0].text).toBe('short text');
|
|
74
|
+
});
|
|
75
|
+
it('does not offload error results', async () => {
|
|
76
|
+
const storage = new InMemoryStorage();
|
|
77
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 10, previewTokens: 5 });
|
|
78
|
+
const agent = createMockAgent();
|
|
79
|
+
plugin.initAgent(agent);
|
|
80
|
+
const event = makeEvent([new TextBlock('x'.repeat(1000))], { status: 'error' });
|
|
81
|
+
await invokeTrackedHook(agent, event);
|
|
82
|
+
expect(event.result.content[0].text).toBe('x'.repeat(1000));
|
|
83
|
+
});
|
|
84
|
+
it('does not offload retrieval tool results', async () => {
|
|
85
|
+
const storage = new InMemoryStorage();
|
|
86
|
+
const plugin = new ContextOffloader({
|
|
87
|
+
storage,
|
|
88
|
+
maxResultTokens: 10,
|
|
89
|
+
previewTokens: 5,
|
|
90
|
+
includeRetrievalTool: true,
|
|
91
|
+
});
|
|
92
|
+
const agent = createMockAgent();
|
|
93
|
+
plugin.initAgent(agent);
|
|
94
|
+
const event = makeEvent([new TextBlock('x'.repeat(1000))], { toolName: 'retrieve_offloaded_content' });
|
|
95
|
+
await invokeTrackedHook(agent, event);
|
|
96
|
+
expect(event.result.content[0].text).toBe('x'.repeat(1000));
|
|
97
|
+
});
|
|
98
|
+
it('offloads large text results', async () => {
|
|
99
|
+
const storage = new InMemoryStorage();
|
|
100
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 100, previewTokens: 10 });
|
|
101
|
+
const agent = createMockAgent();
|
|
102
|
+
plugin.initAgent(agent);
|
|
103
|
+
const largeText = 'a'.repeat(2000);
|
|
104
|
+
const event = makeEvent([new TextBlock(largeText)]);
|
|
105
|
+
await invokeTrackedHook(agent, event);
|
|
106
|
+
expect(event.result.content).toHaveLength(1);
|
|
107
|
+
const preview = event.result.content[0].text;
|
|
108
|
+
expect(preview).toContain('[Offloaded:');
|
|
109
|
+
expect(preview).toContain('Tool result was offloaded');
|
|
110
|
+
expect(preview).toContain('[Stored references:]');
|
|
111
|
+
expect(preview).not.toContain(largeText);
|
|
112
|
+
});
|
|
113
|
+
it('offloads large JSON results', async () => {
|
|
114
|
+
const storage = new InMemoryStorage();
|
|
115
|
+
// JSON uses chars/2 heuristic, so 1000 chars of JSON ≈ 500 tokens
|
|
116
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 10, previewTokens: 5 });
|
|
117
|
+
const agent = createMockAgent();
|
|
118
|
+
plugin.initAgent(agent);
|
|
119
|
+
const largeJson = { data: 'x'.repeat(1000) };
|
|
120
|
+
const event = makeEvent([new JsonBlock({ json: largeJson })]);
|
|
121
|
+
await invokeTrackedHook(agent, event);
|
|
122
|
+
const preview = event.result.content[0].text;
|
|
123
|
+
expect(preview).toContain('[Offloaded:');
|
|
124
|
+
expect(preview).toContain('json,');
|
|
125
|
+
});
|
|
126
|
+
it('offloads image blocks with placeholder', async () => {
|
|
127
|
+
const storage = new InMemoryStorage();
|
|
128
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 10, previewTokens: 5 });
|
|
129
|
+
const agent = createMockAgent();
|
|
130
|
+
plugin.initAgent(agent);
|
|
131
|
+
const imgBytes = new Uint8Array(10000);
|
|
132
|
+
const event = makeEvent([
|
|
133
|
+
new TextBlock('x'.repeat(1000)),
|
|
134
|
+
new ImageBlock({ format: 'png', source: { bytes: imgBytes } }),
|
|
135
|
+
]);
|
|
136
|
+
await invokeTrackedHook(agent, event);
|
|
137
|
+
const imageBlock = event.result.content.find((b) => b instanceof TextBlock && b.text.includes('[image:'));
|
|
138
|
+
expect(imageBlock).toBeDefined();
|
|
139
|
+
expect(imageBlock.text).toContain('[image: png,');
|
|
140
|
+
expect(imageBlock.text).toContain('ref:');
|
|
141
|
+
});
|
|
142
|
+
it('offloads document blocks with placeholder', async () => {
|
|
143
|
+
const storage = new InMemoryStorage();
|
|
144
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 10, previewTokens: 5 });
|
|
145
|
+
const agent = createMockAgent();
|
|
146
|
+
plugin.initAgent(agent);
|
|
147
|
+
const docBytes = new Uint8Array(10000);
|
|
148
|
+
const event = makeEvent([
|
|
149
|
+
new TextBlock('x'.repeat(1000)),
|
|
150
|
+
new DocumentBlock({ format: 'pdf', name: 'report.pdf', source: { bytes: docBytes } }),
|
|
151
|
+
]);
|
|
152
|
+
await invokeTrackedHook(agent, event);
|
|
153
|
+
const docBlock = event.result.content.find((b) => b instanceof TextBlock && b.text.includes('[document:'));
|
|
154
|
+
expect(docBlock).toBeDefined();
|
|
155
|
+
expect(docBlock.text).toContain('[document: pdf, report.pdf,');
|
|
156
|
+
expect(docBlock.text).toContain('ref:');
|
|
157
|
+
});
|
|
158
|
+
it('preserves original result on storage failure', async () => {
|
|
159
|
+
const failingStorage = new InMemoryStorage();
|
|
160
|
+
vi.spyOn(failingStorage, 'store').mockImplementation(() => {
|
|
161
|
+
throw new Error('storage down');
|
|
162
|
+
});
|
|
163
|
+
const plugin = new ContextOffloader({ storage: failingStorage, maxResultTokens: 10, previewTokens: 5 });
|
|
164
|
+
const agent = createMockAgent();
|
|
165
|
+
plugin.initAgent(agent);
|
|
166
|
+
const event = makeEvent([new TextBlock('x'.repeat(1000))]);
|
|
167
|
+
const originalResult = event.result;
|
|
168
|
+
await invokeTrackedHook(agent, event);
|
|
169
|
+
expect(event.result).toBe(originalResult);
|
|
170
|
+
});
|
|
171
|
+
it('includes retrieval tool guidance when enabled', async () => {
|
|
172
|
+
const storage = new InMemoryStorage();
|
|
173
|
+
const plugin = new ContextOffloader({
|
|
174
|
+
storage,
|
|
175
|
+
maxResultTokens: 10,
|
|
176
|
+
previewTokens: 5,
|
|
177
|
+
includeRetrievalTool: true,
|
|
178
|
+
});
|
|
179
|
+
const agent = createMockAgent();
|
|
180
|
+
plugin.initAgent(agent);
|
|
181
|
+
const event = makeEvent([new TextBlock('x'.repeat(1000))]);
|
|
182
|
+
await invokeTrackedHook(agent, event);
|
|
183
|
+
const preview = event.result.content[0].text;
|
|
184
|
+
expect(preview).toContain('retrieve_offloaded_content');
|
|
185
|
+
});
|
|
186
|
+
it('respects custom previewTokens', async () => {
|
|
187
|
+
const storage = new InMemoryStorage();
|
|
188
|
+
const plugin = new ContextOffloader({ storage, maxResultTokens: 10, previewTokens: 2 });
|
|
189
|
+
const agent = createMockAgent();
|
|
190
|
+
plugin.initAgent(agent);
|
|
191
|
+
const event = makeEvent([new TextBlock('a'.repeat(1000))]);
|
|
192
|
+
await invokeTrackedHook(agent, event);
|
|
193
|
+
const preview = event.result.content[0].text;
|
|
194
|
+
const previewSection = preview.split('[Stored references:]')[0];
|
|
195
|
+
// previewTokens=2 → 2*4=8 chars of 'a' in preview
|
|
196
|
+
expect(previewSection).toContain('a'.repeat(8));
|
|
197
|
+
expect(previewSection).not.toContain('a'.repeat(100));
|
|
198
|
+
});
|
|
199
|
+
it('stores and retrieves content round-trip', async () => {
|
|
200
|
+
const storage = new InMemoryStorage();
|
|
201
|
+
const plugin = new ContextOffloader({
|
|
202
|
+
storage,
|
|
203
|
+
maxResultTokens: 10,
|
|
204
|
+
previewTokens: 5,
|
|
205
|
+
includeRetrievalTool: true,
|
|
206
|
+
});
|
|
207
|
+
const agent = createMockAgent();
|
|
208
|
+
plugin.initAgent(agent);
|
|
209
|
+
const event = makeEvent([new TextBlock('hello world '.repeat(100))]);
|
|
210
|
+
await invokeTrackedHook(agent, event);
|
|
211
|
+
const preview = event.result.content[0].text;
|
|
212
|
+
const refMatch = preview.match(/mem_\d+_tool-123_0/);
|
|
213
|
+
expect(refMatch).not.toBeNull();
|
|
214
|
+
const retrieved = await storage.retrieve(refMatch[0]);
|
|
215
|
+
expect(new TextDecoder().decode(retrieved.content)).toBe('hello world '.repeat(100));
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
describe('retrieval tool', () => {
|
|
219
|
+
it('retrieves text content as string', async () => {
|
|
220
|
+
const storage = new InMemoryStorage();
|
|
221
|
+
const ref = await storage.store('k1', new TextEncoder().encode('hello'), 'text/plain');
|
|
222
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
223
|
+
const tools = plugin.getTools();
|
|
224
|
+
const retrievalTool = tools[0];
|
|
225
|
+
const result = await retrievalTool.invoke({
|
|
226
|
+
reference: ref,
|
|
227
|
+
});
|
|
228
|
+
expect(result).toBe('hello');
|
|
229
|
+
});
|
|
230
|
+
it('retrieves JSON content as parsed object', async () => {
|
|
231
|
+
const storage = new InMemoryStorage();
|
|
232
|
+
const ref = await storage.store('k1', new TextEncoder().encode('{"foo":"bar"}'), 'application/json');
|
|
233
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
234
|
+
const tools = plugin.getTools();
|
|
235
|
+
const retrievalTool = tools[0];
|
|
236
|
+
const result = await retrievalTool.invoke({
|
|
237
|
+
reference: ref,
|
|
238
|
+
});
|
|
239
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
240
|
+
});
|
|
241
|
+
it('retrieves image content as ImageBlock', async () => {
|
|
242
|
+
const storage = new InMemoryStorage();
|
|
243
|
+
const imgBytes = new Uint8Array([137, 80, 78, 71]);
|
|
244
|
+
const ref = await storage.store('k1', imgBytes, 'image/png');
|
|
245
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
246
|
+
const tools = plugin.getTools();
|
|
247
|
+
const retrievalTool = tools[0];
|
|
248
|
+
const result = await retrievalTool.invoke({
|
|
249
|
+
reference: ref,
|
|
250
|
+
});
|
|
251
|
+
expect(result).toBeInstanceOf(ImageBlock);
|
|
252
|
+
expect(result.format).toBe('png');
|
|
253
|
+
});
|
|
254
|
+
it('retrieves video content as VideoBlock', async () => {
|
|
255
|
+
const storage = new InMemoryStorage();
|
|
256
|
+
const vidBytes = new Uint8Array([0x00, 0x00, 0x00, 0x1c]);
|
|
257
|
+
const ref = await storage.store('k1', vidBytes, 'video/mp4');
|
|
258
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
259
|
+
const tools = plugin.getTools();
|
|
260
|
+
const retrievalTool = tools[0];
|
|
261
|
+
const result = await retrievalTool.invoke({
|
|
262
|
+
reference: ref,
|
|
263
|
+
});
|
|
264
|
+
expect(result).toBeInstanceOf(VideoBlock);
|
|
265
|
+
expect(result.format).toBe('mp4');
|
|
266
|
+
});
|
|
267
|
+
it('retrieves document content as DocumentBlock', async () => {
|
|
268
|
+
const storage = new InMemoryStorage();
|
|
269
|
+
const docBytes = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
|
|
270
|
+
const ref = await storage.store('k1', docBytes, 'application/pdf');
|
|
271
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
272
|
+
const tools = plugin.getTools();
|
|
273
|
+
const retrievalTool = tools[0];
|
|
274
|
+
const result = await retrievalTool.invoke({
|
|
275
|
+
reference: ref,
|
|
276
|
+
});
|
|
277
|
+
expect(result).toBeInstanceOf(DocumentBlock);
|
|
278
|
+
expect(result.format).toBe('pdf');
|
|
279
|
+
});
|
|
280
|
+
it('returns error string for missing reference', async () => {
|
|
281
|
+
const storage = new InMemoryStorage();
|
|
282
|
+
const plugin = new ContextOffloader({ storage, includeRetrievalTool: true });
|
|
283
|
+
const tools = plugin.getTools();
|
|
284
|
+
const retrievalTool = tools[0];
|
|
285
|
+
const result = await retrievalTool.invoke({
|
|
286
|
+
reference: 'nonexistent',
|
|
287
|
+
});
|
|
288
|
+
expect(result).toContain('Error: reference not found');
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
//# sourceMappingURL=plugin.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.test.js","sourceRoot":"","sources":["../../../../../src/vended-plugins/context-offloader/__tests__/plugin.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.test.d.ts","sourceRoot":"","sources":["../../../../../src/vended-plugins/context-offloader/__tests__/storage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { InMemoryStorage, S3Storage } from '../storage.js';
|
|
3
|
+
describe('InMemoryStorage', () => {
|
|
4
|
+
it('stores and retrieves text content', async () => {
|
|
5
|
+
const storage = new InMemoryStorage();
|
|
6
|
+
const content = new TextEncoder().encode('hello world');
|
|
7
|
+
const ref = await storage.store('key1', content, 'text/plain');
|
|
8
|
+
const result = await storage.retrieve(ref);
|
|
9
|
+
expect(new TextDecoder().decode(result.content)).toBe('hello world');
|
|
10
|
+
expect(result.contentType).toBe('text/plain');
|
|
11
|
+
});
|
|
12
|
+
it('stores and retrieves binary content', async () => {
|
|
13
|
+
const storage = new InMemoryStorage();
|
|
14
|
+
const content = new Uint8Array([1, 2, 3, 4, 5]);
|
|
15
|
+
const ref = await storage.store('key1', content, 'image/png');
|
|
16
|
+
const result = await storage.retrieve(ref);
|
|
17
|
+
expect(result.content).toEqual(content);
|
|
18
|
+
expect(result.contentType).toBe('image/png');
|
|
19
|
+
});
|
|
20
|
+
it('generates unique references', async () => {
|
|
21
|
+
const storage = new InMemoryStorage();
|
|
22
|
+
const content = new TextEncoder().encode('test');
|
|
23
|
+
const ref1 = await storage.store('key1', content);
|
|
24
|
+
const ref2 = await storage.store('key2', content);
|
|
25
|
+
expect(ref1).not.toBe(ref2);
|
|
26
|
+
});
|
|
27
|
+
it('uses mem_ prefix in references', async () => {
|
|
28
|
+
const storage = new InMemoryStorage();
|
|
29
|
+
const ref = await storage.store('mykey', new TextEncoder().encode('test'));
|
|
30
|
+
expect(ref).toMatch(/^mem_\d+_mykey$/);
|
|
31
|
+
});
|
|
32
|
+
it('throws on missing reference', async () => {
|
|
33
|
+
const storage = new InMemoryStorage();
|
|
34
|
+
await expect(storage.retrieve('nonexistent')).rejects.toThrow('Reference not found: nonexistent');
|
|
35
|
+
});
|
|
36
|
+
it('clears all stored content', async () => {
|
|
37
|
+
const storage = new InMemoryStorage();
|
|
38
|
+
const ref = await storage.store('key1', new TextEncoder().encode('test'));
|
|
39
|
+
storage.clear();
|
|
40
|
+
await expect(storage.retrieve(ref)).rejects.toThrow('Reference not found');
|
|
41
|
+
});
|
|
42
|
+
it('defaults content type to text/plain', async () => {
|
|
43
|
+
const storage = new InMemoryStorage();
|
|
44
|
+
const ref = await storage.store('key1', new TextEncoder().encode('test'));
|
|
45
|
+
const result = await storage.retrieve(ref);
|
|
46
|
+
expect(result.contentType).toBe('text/plain');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('S3Storage', () => {
|
|
50
|
+
let mockSend;
|
|
51
|
+
let mockS3Client;
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
mockSend = vi.fn();
|
|
54
|
+
mockS3Client = { send: mockSend };
|
|
55
|
+
});
|
|
56
|
+
describe('store', () => {
|
|
57
|
+
it('returns s3:// URI as reference', async () => {
|
|
58
|
+
mockSend.mockResolvedValue({});
|
|
59
|
+
const storage = new S3Storage('my-bucket', { s3Client: mockS3Client });
|
|
60
|
+
const ref = await storage.store('key1', new TextEncoder().encode('test'), 'text/plain');
|
|
61
|
+
expect(ref).toMatch(/^s3:\/\/my-bucket\//);
|
|
62
|
+
expect(ref).toContain('key1');
|
|
63
|
+
});
|
|
64
|
+
it('includes prefix in s3 key', async () => {
|
|
65
|
+
mockSend.mockResolvedValue({});
|
|
66
|
+
const storage = new S3Storage('my-bucket', { prefix: 'artifacts', s3Client: mockS3Client });
|
|
67
|
+
const ref = await storage.store('key1', new TextEncoder().encode('test'));
|
|
68
|
+
expect(ref).toMatch(/^s3:\/\/my-bucket\/artifacts\//);
|
|
69
|
+
});
|
|
70
|
+
it('normalizes trailing slashes on prefix', async () => {
|
|
71
|
+
mockSend.mockResolvedValue({});
|
|
72
|
+
const storage = new S3Storage('b', { prefix: 'p///', s3Client: mockS3Client });
|
|
73
|
+
const ref = await storage.store('k', new TextEncoder().encode('x'));
|
|
74
|
+
expect(ref).toMatch(/^s3:\/\/b\/p\//);
|
|
75
|
+
// Check no double slashes in the path portion (after s3://)
|
|
76
|
+
const pathPortion = ref.replace('s3://', '');
|
|
77
|
+
expect(pathPortion).not.toContain('//');
|
|
78
|
+
});
|
|
79
|
+
it('sends correct PutObject params', async () => {
|
|
80
|
+
mockSend.mockResolvedValue({});
|
|
81
|
+
const storage = new S3Storage('my-bucket', { s3Client: mockS3Client });
|
|
82
|
+
const content = new TextEncoder().encode('hello');
|
|
83
|
+
await storage.store('key1', content, 'application/json');
|
|
84
|
+
expect(mockSend).toHaveBeenCalledOnce();
|
|
85
|
+
const command = mockSend.mock.calls[0][0];
|
|
86
|
+
expect(command.input.Bucket).toBe('my-bucket');
|
|
87
|
+
expect(command.input.Body).toBe(content);
|
|
88
|
+
expect(command.input.ContentType).toBe('application/json');
|
|
89
|
+
});
|
|
90
|
+
it('sanitizes keys', async () => {
|
|
91
|
+
mockSend.mockResolvedValue({});
|
|
92
|
+
const storage = new S3Storage('b', { s3Client: mockS3Client });
|
|
93
|
+
const ref = await storage.store('../../etc/passwd', new TextEncoder().encode('x'));
|
|
94
|
+
expect(ref).not.toContain('..');
|
|
95
|
+
expect(ref).not.toContain('etc/passwd');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('retrieve', () => {
|
|
99
|
+
it('retrieves content by s3:// URI', async () => {
|
|
100
|
+
mockSend.mockResolvedValueOnce({}).mockResolvedValueOnce({
|
|
101
|
+
Body: { transformToByteArray: () => Promise.resolve(new Uint8Array([1, 2, 3])) },
|
|
102
|
+
ContentType: 'image/png',
|
|
103
|
+
});
|
|
104
|
+
const storage = new S3Storage('my-bucket', { s3Client: mockS3Client });
|
|
105
|
+
const ref = await storage.store('key1', new Uint8Array([1, 2, 3]), 'image/png');
|
|
106
|
+
const result = await storage.retrieve(ref);
|
|
107
|
+
expect(result.content).toEqual(new Uint8Array([1, 2, 3]));
|
|
108
|
+
expect(result.contentType).toBe('image/png');
|
|
109
|
+
});
|
|
110
|
+
it('retrieves content by raw key', async () => {
|
|
111
|
+
mockSend.mockResolvedValue({
|
|
112
|
+
Body: { transformToByteArray: () => Promise.resolve(new TextEncoder().encode('hello')) },
|
|
113
|
+
ContentType: 'text/plain',
|
|
114
|
+
});
|
|
115
|
+
const storage = new S3Storage('b', { s3Client: mockS3Client });
|
|
116
|
+
const result = await storage.retrieve('some/raw/key');
|
|
117
|
+
expect(new TextDecoder().decode(result.content)).toBe('hello');
|
|
118
|
+
const command = mockSend.mock.calls[0][0];
|
|
119
|
+
expect(command.input.Key).toBe('some/raw/key');
|
|
120
|
+
});
|
|
121
|
+
it('throws on bucket mismatch', async () => {
|
|
122
|
+
const storage = new S3Storage('my-bucket', { s3Client: mockS3Client });
|
|
123
|
+
await expect(storage.retrieve('s3://wrong-bucket/key')).rejects.toThrow('bucket mismatch');
|
|
124
|
+
});
|
|
125
|
+
it('throws on NoSuchKey error', async () => {
|
|
126
|
+
const noSuchKey = new Error('not found');
|
|
127
|
+
noSuchKey.name = 'NoSuchKey';
|
|
128
|
+
mockSend.mockRejectedValue(noSuchKey);
|
|
129
|
+
const storage = new S3Storage('b', { s3Client: mockS3Client });
|
|
130
|
+
await expect(storage.retrieve('missing-key')).rejects.toThrow('Reference not found');
|
|
131
|
+
});
|
|
132
|
+
it('defaults contentType to application/octet-stream when missing', async () => {
|
|
133
|
+
mockSend.mockResolvedValue({
|
|
134
|
+
Body: { transformToByteArray: () => Promise.resolve(new Uint8Array([0])) },
|
|
135
|
+
});
|
|
136
|
+
const storage = new S3Storage('b', { s3Client: mockS3Client });
|
|
137
|
+
const result = await storage.retrieve('key');
|
|
138
|
+
expect(result.contentType).toBe('application/octet-stream');
|
|
139
|
+
});
|
|
140
|
+
it('rethrows non-NoSuchKey errors', async () => {
|
|
141
|
+
const networkError = new Error('network timeout');
|
|
142
|
+
mockSend.mockRejectedValue(networkError);
|
|
143
|
+
const storage = new S3Storage('b', { s3Client: mockS3Client });
|
|
144
|
+
await expect(storage.retrieve('key')).rejects.toThrow('network timeout');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=storage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.test.js","sourceRoot":"","sources":["../../../../../src/vended-plugins/context-offloader/__tests__/storage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.test.node.d.ts","sourceRoot":"","sources":["../../../../../src/vended-plugins/context-offloader/__tests__/storage.test.node.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { FileStorage } from '../storage.js';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
describe('FileStorage', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'context-offloader-test-'));
|
|
10
|
+
});
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('stores and retrieves text content', async () => {
|
|
15
|
+
const storage = new FileStorage(tmpDir);
|
|
16
|
+
const content = new TextEncoder().encode('hello world');
|
|
17
|
+
const ref = await storage.store('key1', content, 'text/plain');
|
|
18
|
+
const result = await storage.retrieve(ref);
|
|
19
|
+
expect(new TextDecoder().decode(result.content)).toBe('hello world');
|
|
20
|
+
expect(result.contentType).toBe('text/plain');
|
|
21
|
+
});
|
|
22
|
+
it('stores and retrieves binary content', async () => {
|
|
23
|
+
const storage = new FileStorage(tmpDir);
|
|
24
|
+
const content = new Uint8Array([1, 2, 3, 4, 5]);
|
|
25
|
+
const ref = await storage.store('key1', content, 'image/png');
|
|
26
|
+
const result = await storage.retrieve(ref);
|
|
27
|
+
expect(result.content).toEqual(content);
|
|
28
|
+
expect(result.contentType).toBe('image/png');
|
|
29
|
+
});
|
|
30
|
+
it('returns file path as reference preserving configured directory', async () => {
|
|
31
|
+
const storage = new FileStorage(tmpDir);
|
|
32
|
+
const content = new TextEncoder().encode('test');
|
|
33
|
+
const ref = await storage.store('k1', content, 'text/plain');
|
|
34
|
+
expect(ref.startsWith(tmpDir)).toBe(true);
|
|
35
|
+
expect(ref).toMatch(/\.txt$/);
|
|
36
|
+
});
|
|
37
|
+
it('uses correct file extensions', async () => {
|
|
38
|
+
const storage = new FileStorage(tmpDir);
|
|
39
|
+
const content = new TextEncoder().encode('test');
|
|
40
|
+
const txtRef = await storage.store('k1', content, 'text/plain');
|
|
41
|
+
expect(txtRef).toMatch(/\.txt$/);
|
|
42
|
+
const jsonRef = await storage.store('k2', content, 'application/json');
|
|
43
|
+
expect(jsonRef).toMatch(/\.json$/);
|
|
44
|
+
const pngRef = await storage.store('k3', content, 'image/png');
|
|
45
|
+
expect(pngRef).toMatch(/\.png$/);
|
|
46
|
+
});
|
|
47
|
+
it('throws on missing reference', async () => {
|
|
48
|
+
const storage = new FileStorage(tmpDir);
|
|
49
|
+
await expect(storage.retrieve(path.join(tmpDir, 'nonexistent.txt'))).rejects.toThrow('Reference not found');
|
|
50
|
+
});
|
|
51
|
+
it('sanitizes keys for safe filenames', async () => {
|
|
52
|
+
const storage = new FileStorage(tmpDir);
|
|
53
|
+
const content = new TextEncoder().encode('test');
|
|
54
|
+
const ref = await storage.store('../../../etc/passwd', content, 'text/plain');
|
|
55
|
+
expect(ref).not.toContain('..');
|
|
56
|
+
});
|
|
57
|
+
it('prevents path traversal on retrieve', async () => {
|
|
58
|
+
const storage = new FileStorage(tmpDir);
|
|
59
|
+
await expect(storage.retrieve('../../etc/passwd')).rejects.toThrow('Reference not found');
|
|
60
|
+
});
|
|
61
|
+
it('creates artifact directory if it does not exist', async () => {
|
|
62
|
+
const nestedDir = path.join(tmpDir, 'nested', 'dir');
|
|
63
|
+
const storage = new FileStorage(nestedDir);
|
|
64
|
+
const content = new TextEncoder().encode('test');
|
|
65
|
+
await storage.store('key1', content, 'text/plain');
|
|
66
|
+
const stat = await fs.stat(nestedDir);
|
|
67
|
+
expect(stat.isDirectory()).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it('persists metadata across instances', async () => {
|
|
70
|
+
const storage1 = new FileStorage(tmpDir);
|
|
71
|
+
const content = new TextEncoder().encode('test');
|
|
72
|
+
const ref = await storage1.store('key1', content, 'application/json');
|
|
73
|
+
const storage2 = new FileStorage(tmpDir);
|
|
74
|
+
const result = await storage2.retrieve(ref);
|
|
75
|
+
expect(result.contentType).toBe('application/json');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=storage.test.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.test.node.js","sourceRoot":"","sources":["../../../../../src/vended-plugins/context-offloader/__tests__/storage.test.node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAE7B,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,MAAc,CAAA;IAElB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACvD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QACvD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QAE9D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC1C,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACpE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC/C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;QAE7D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QAE5D,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAEhD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAEhC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAA;QACtE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAElC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;QAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAC7G,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QAC7E,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;QACpD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAA;QAC1C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QAElD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACrC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACxC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAA;QAErE,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;QACxC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC3C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context offloading plugin for Strands Agents.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the ContextOffloader plugin and Storage backends for
|
|
5
|
+
* automatically offloading oversized tool results to external storage, replacing
|
|
6
|
+
* them with truncated previews and actionable storage references.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
11
|
+
* import { ContextOffloader, InMemoryStorage } from '@strands-agents/sdk/vended-plugins/context-offloader'
|
|
12
|
+
*
|
|
13
|
+
* const agent = new Agent({
|
|
14
|
+
* model,
|
|
15
|
+
* plugins: [new ContextOffloader({ storage: new InMemoryStorage() })],
|
|
16
|
+
* })
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export { ContextOffloader } from './plugin.js';
|
|
20
|
+
export type { ContextOffloaderConfig } from './plugin.js';
|
|
21
|
+
export type { Storage } from './storage.js';
|
|
22
|
+
export { InMemoryStorage, FileStorage, S3Storage } from './storage.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/vended-plugins/context-offloader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACzD,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"}
|