opc-agent 4.1.0 → 4.1.1
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/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- package/CHANGELOG.md +48 -48
- package/CONTRIBUTING.md +36 -36
- package/README.zh-CN.md +497 -497
- package/dist/channels/wechat.js +6 -6
- package/dist/deploy/index.js +56 -56
- package/dist/studio/server.js +30 -1
- package/dist/studio-ui/index.html +230 -10
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/fix-sidebar.mjs +188 -188
- package/install.ps1 +154 -154
- package/install.sh +164 -164
- package/package.json +1 -1
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/serve-studio.js +13 -13
- package/serve-test.js +25 -25
- package/src/channels/dingtalk.ts +46 -46
- package/src/channels/email.ts +351 -351
- package/src/channels/feishu.ts +349 -349
- package/src/channels/googlechat.ts +42 -42
- package/src/channels/imessage.ts +31 -31
- package/src/channels/irc.ts +82 -82
- package/src/channels/line.ts +32 -32
- package/src/channels/matrix.ts +33 -33
- package/src/channels/mattermost.ts +57 -57
- package/src/channels/msteams.ts +32 -32
- package/src/channels/nostr.ts +32 -32
- package/src/channels/qq.ts +33 -33
- package/src/channels/signal.ts +32 -32
- package/src/channels/sms.ts +33 -33
- package/src/channels/telegram.ts +616 -616
- package/src/channels/twitch.ts +65 -65
- package/src/channels/voice-call.ts +100 -100
- package/src/channels/websocket.ts +399 -399
- package/src/channels/wechat.ts +329 -329
- package/src/channels/whatsapp.ts +32 -32
- package/src/cli/chat.ts +99 -99
- package/src/cli/setup.ts +314 -314
- package/src/core/agent.ts +476 -476
- package/src/core/api-server.ts +277 -277
- package/src/core/audio.ts +98 -98
- package/src/core/collaboration.ts +275 -275
- package/src/core/context-discovery.ts +85 -85
- package/src/core/context-refs.ts +140 -140
- package/src/core/gateway.ts +106 -106
- package/src/core/heartbeat.ts +51 -51
- package/src/core/hooks.ts +105 -105
- package/src/core/ide-bridge.ts +133 -133
- package/src/core/node-network.ts +86 -86
- package/src/core/profiles.ts +122 -122
- package/src/core/scheduler.ts +187 -187
- package/src/core/session-manager.ts +137 -137
- package/src/core/subagent.ts +98 -98
- package/src/core/vision.ts +180 -180
- package/src/core/workflow-graph.ts +365 -365
- package/src/daemon.ts +96 -96
- package/src/deploy/index.ts +255 -255
- package/src/doctor.ts +156 -156
- package/src/eval/index.ts +211 -211
- package/src/eval/suites/basic.json +16 -16
- package/src/eval/suites/memory.json +12 -12
- package/src/eval/suites/safety.json +14 -14
- package/src/hub/brain-seed.ts +54 -54
- package/src/hub/client.ts +60 -60
- package/src/mcp/servers/calculator-mcp.ts +65 -65
- package/src/mcp/servers/crypto-mcp.ts +73 -73
- package/src/mcp/servers/database-mcp.ts +72 -72
- package/src/mcp/servers/datetime-mcp.ts +69 -69
- package/src/mcp/servers/filesystem.ts +66 -66
- package/src/mcp/servers/github-mcp.ts +58 -58
- package/src/mcp/servers/index.ts +63 -63
- package/src/mcp/servers/json-mcp.ts +102 -102
- package/src/mcp/servers/memory-mcp.ts +56 -56
- package/src/mcp/servers/regex-mcp.ts +53 -53
- package/src/mcp/servers/web-mcp.ts +49 -49
- package/src/memory/context-compressor.ts +189 -189
- package/src/memory/seed-loader.ts +212 -212
- package/src/memory/user-profiler.ts +215 -215
- package/src/plugins/content-filter.ts +23 -23
- package/src/plugins/logger.ts +18 -18
- package/src/plugins/rate-limiter.ts +38 -38
- package/src/protocols/a2a/client.ts +132 -132
- package/src/protocols/a2a/index.ts +8 -8
- package/src/protocols/a2a/server.ts +333 -333
- package/src/protocols/a2a/types.ts +88 -88
- package/src/protocols/a2a/utils.ts +50 -50
- package/src/protocols/agui/client.ts +83 -83
- package/src/protocols/agui/index.ts +4 -4
- package/src/protocols/agui/server.ts +218 -218
- package/src/protocols/agui/types.ts +153 -153
- package/src/protocols/index.ts +2 -2
- package/src/protocols/mcp/agent-tools.ts +134 -134
- package/src/protocols/mcp/index.ts +8 -8
- package/src/protocols/mcp/server.ts +262 -262
- package/src/protocols/mcp/types.ts +69 -69
- package/src/providers/index.ts +632 -632
- package/src/publish/index.ts +376 -376
- package/src/scheduler/cron-engine.ts +191 -191
- package/src/scheduler/index.ts +2 -2
- package/src/schema/oad.ts +217 -217
- package/src/security/approval.ts +131 -131
- package/src/security/approvals.ts +143 -143
- package/src/security/elevated.ts +105 -105
- package/src/security/guardrails.ts +248 -248
- package/src/security/index.ts +9 -9
- package/src/security/keys.ts +87 -87
- package/src/security/secrets.ts +129 -129
- package/src/skills/builtin/index.ts +408 -408
- package/src/skills/marketplace.ts +113 -113
- package/src/skills/types.ts +42 -42
- package/src/studio/server.ts +31 -1
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +230 -10
- package/src/telemetry/index.ts +324 -324
- package/src/tools/builtin/browser.ts +299 -299
- package/src/tools/builtin/datetime.ts +41 -41
- package/src/tools/builtin/file.ts +107 -107
- package/src/tools/builtin/home-assistant.ts +116 -116
- package/src/tools/builtin/rl-tools.ts +243 -243
- package/src/tools/builtin/shell.ts +43 -43
- package/src/tools/builtin/vision.ts +64 -64
- package/src/tools/builtin/web-search.ts +126 -126
- package/src/tools/builtin/web.ts +35 -35
- package/src/tools/document-processor.ts +213 -213
- package/src/tools/image-generator.ts +150 -150
- package/src/tools/integrations/calendar.ts +73 -73
- package/src/tools/integrations/code-exec.ts +39 -39
- package/src/tools/integrations/csv-analyzer.ts +92 -92
- package/src/tools/integrations/database.ts +44 -44
- package/src/tools/integrations/email-send.ts +76 -76
- package/src/tools/integrations/git-tool.ts +42 -42
- package/src/tools/integrations/github-tool.ts +76 -76
- package/src/tools/integrations/image-gen.ts +56 -56
- package/src/tools/integrations/index.ts +92 -92
- package/src/tools/integrations/jira.ts +83 -83
- package/src/tools/integrations/notion.ts +71 -71
- package/src/tools/integrations/npm-tool.ts +48 -48
- package/src/tools/integrations/pdf-reader.ts +58 -58
- package/src/tools/integrations/slack.ts +65 -65
- package/src/tools/integrations/summarizer.ts +49 -49
- package/src/tools/integrations/translator.ts +48 -48
- package/src/tools/integrations/trello.ts +60 -60
- package/src/tools/integrations/vector-search.ts +42 -42
- package/src/tools/integrations/web-scraper.ts +47 -47
- package/src/tools/integrations/web-search.ts +58 -58
- package/src/tools/integrations/webhook.ts +38 -38
- package/src/tools/mcp-client.ts +131 -131
- package/src/tools/web-scraper.ts +179 -179
- package/src/tools/web-search.ts +180 -180
- package/src/ui/components.ts +127 -127
- package/srv-out.txt +1 -1
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -9
- package/test-agent/README.md +50 -50
- package/test-agent/agent.yaml +23 -23
- package/test-agent/docker-compose.yml +11 -11
- package/test-agent/oad.yaml +31 -31
- package/test-agent/package-lock.json +1492 -1492
- package/test-agent/package.json +17 -17
- package/test-agent/src/index.ts +24 -24
- package/test-agent/src/skills/echo.ts +15 -15
- package/test-agent/tsconfig.json +24 -24
- package/test-full.js +43 -43
- package/test-sidebar.js +22 -22
- package/test-studio3.js +75 -75
- package/test-studio4.js +41 -41
- package/tests/a2a-protocol.test.ts +285 -285
- package/tests/agui-protocol.test.ts +246 -246
- package/tests/api-server.test.ts +148 -148
- package/tests/approvals.test.ts +89 -89
- package/tests/audio.test.ts +40 -40
- package/tests/brain-seed-extended.test.ts +490 -490
- package/tests/brain-seed.test.ts +239 -239
- package/tests/browser.test.ts +179 -179
- package/tests/channels/discord.test.ts +79 -79
- package/tests/channels/email.test.ts +148 -148
- package/tests/channels/feishu.test.ts +123 -123
- package/tests/channels/telegram.test.ts +129 -129
- package/tests/channels/websocket.test.ts +53 -53
- package/tests/channels/wechat.test.ts +170 -170
- package/tests/channels-extra.test.ts +45 -45
- package/tests/chat-cli.test.ts +160 -160
- package/tests/cli.test.ts +46 -46
- package/tests/context-compressor.test.ts +172 -172
- package/tests/context-refs.test.ts +121 -121
- package/tests/cron-engine.test.ts +101 -101
- package/tests/daemon.test.ts +135 -135
- package/tests/deepbrain-wire.test.ts +234 -234
- package/tests/deploy-and-dag.test.ts +196 -196
- package/tests/doctor.test.ts +38 -38
- package/tests/document-processor.test.ts +69 -69
- package/tests/e2e-nocode.test.ts +442 -442
- package/tests/elevated.test.ts +69 -69
- package/tests/eval.test.ts +173 -173
- package/tests/gateway.test.ts +63 -63
- package/tests/guardrails.test.ts +177 -177
- package/tests/home-assistant.test.ts +40 -40
- package/tests/hooks.test.ts +79 -79
- package/tests/ide-bridge.test.ts +38 -38
- package/tests/image-generator.test.ts +84 -84
- package/tests/init-role.test.ts +124 -124
- package/tests/integrations.test.ts +249 -249
- package/tests/mcp-client.test.ts +92 -92
- package/tests/mcp-server.test.ts +178 -178
- package/tests/mcp-servers.test.ts +260 -260
- package/tests/node-network.test.ts +74 -74
- package/tests/plugin-a2a-enhanced.test.ts +230 -230
- package/tests/profiles.test.ts +61 -61
- package/tests/publish.test.ts +231 -231
- package/tests/rl-tools.test.ts +93 -93
- package/tests/sandbox-manager.test.ts +46 -46
- package/tests/scheduler.test.ts +200 -200
- package/tests/secrets.test.ts +107 -107
- package/tests/security-enhanced.test.ts +233 -233
- package/tests/settings-api.test.ts +148 -148
- package/tests/setup.test.ts +73 -73
- package/tests/subagent.test.ts +193 -193
- package/tests/telegram-discord.test.ts +60 -60
- package/tests/telemetry.test.ts +186 -186
- package/tests/user-profiler.test.ts +169 -169
- package/tests/v090-features.test.ts +254 -254
- package/tests/vision.test.ts +61 -61
- package/tests/voice-call.test.ts +47 -47
- package/tests/voice-enhanced.test.ts +169 -169
- package/tests/voice-interaction.test.ts +38 -38
- package/tests/web-search.test.ts +155 -155
- package/tests/workflow-graph.test.ts +279 -279
- package/tutorial/customer-service-agent/README.md +612 -612
- package/tutorial/customer-service-agent/SOUL.md +26 -26
- package/tutorial/customer-service-agent/agent.yaml +63 -63
- package/tutorial/customer-service-agent/package.json +19 -19
- package/tutorial/customer-service-agent/src/index.ts +69 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
- package/tutorial/customer-service-agent/tsconfig.json +14 -14
|
@@ -1,234 +1,234 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { BaseAgent } from '../src/core/agent';
|
|
3
|
-
import type { Message } from '../src/core/types';
|
|
4
|
-
|
|
5
|
-
// Mock LLM provider
|
|
6
|
-
const mockProvider = {
|
|
7
|
-
chat: vi.fn().mockResolvedValue('Hello from LLM'),
|
|
8
|
-
chatStream: vi.fn(),
|
|
9
|
-
listModels: vi.fn(),
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
vi.mock('../src/providers', () => ({
|
|
13
|
-
createProvider: () => mockProvider,
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
function createAgent(opts?: any) {
|
|
17
|
-
return new BaseAgent({
|
|
18
|
-
name: 'test-agent',
|
|
19
|
-
systemPrompt: 'You are a test agent.',
|
|
20
|
-
...opts,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function createMessage(content: string): Message {
|
|
25
|
-
return {
|
|
26
|
-
id: `msg_${Date.now()}`,
|
|
27
|
-
role: 'user',
|
|
28
|
-
content,
|
|
29
|
-
timestamp: Date.now(),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe('BaseAgent long-term memory', () => {
|
|
34
|
-
let agent: BaseAgent;
|
|
35
|
-
let mockBrain: any;
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
agent = createAgent();
|
|
40
|
-
mockBrain = {
|
|
41
|
-
recall: vi.fn().mockResolvedValue([{ content: 'remembered fact' }]),
|
|
42
|
-
learn: vi.fn().mockResolvedValue(undefined),
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('setLongTermMemory sets the brain', () => {
|
|
47
|
-
agent.setLongTermMemory(mockBrain);
|
|
48
|
-
expect(agent.getLongTermMemory()).toBe(mockBrain);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('setLongTermMemory with config sets autoLearn/autoRecall', () => {
|
|
52
|
-
agent.setLongTermMemory(mockBrain, { autoLearn: false, autoRecall: true });
|
|
53
|
-
const config = agent.getLongTermMemoryConfig();
|
|
54
|
-
expect(config.autoLearn).toBe(false);
|
|
55
|
-
expect(config.autoRecall).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('handleMessage calls recall before LLM when autoRecall is true', async () => {
|
|
59
|
-
agent.setLongTermMemory(mockBrain);
|
|
60
|
-
const callOrder: string[] = [];
|
|
61
|
-
mockBrain.recall.mockImplementation(async () => {
|
|
62
|
-
callOrder.push('recall');
|
|
63
|
-
return [{ content: 'memory' }];
|
|
64
|
-
});
|
|
65
|
-
mockProvider.chat.mockImplementation(async () => {
|
|
66
|
-
callOrder.push('llm');
|
|
67
|
-
return 'response';
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
await agent.init();
|
|
71
|
-
await agent.handleMessage(createMessage('hello'));
|
|
72
|
-
|
|
73
|
-
expect(callOrder[0]).toBe('recall');
|
|
74
|
-
expect(callOrder[1]).toBe('llm');
|
|
75
|
-
expect(mockBrain.recall).toHaveBeenCalledWith('hello');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('handleMessage calls learn after LLM when autoLearn is true', async () => {
|
|
79
|
-
agent.setLongTermMemory(mockBrain);
|
|
80
|
-
const callOrder: string[] = [];
|
|
81
|
-
mockProvider.chat.mockImplementation(async () => {
|
|
82
|
-
callOrder.push('llm');
|
|
83
|
-
return 'response';
|
|
84
|
-
});
|
|
85
|
-
mockBrain.learn.mockImplementation(async () => {
|
|
86
|
-
callOrder.push('learn');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
await agent.init();
|
|
90
|
-
await agent.handleMessage(createMessage('hello'));
|
|
91
|
-
|
|
92
|
-
expect(callOrder.indexOf('llm')).toBeLessThan(callOrder.indexOf('learn'));
|
|
93
|
-
expect(mockBrain.learn).toHaveBeenCalled();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('recall failure does not crash handleMessage', async () => {
|
|
97
|
-
mockBrain.recall.mockRejectedValue(new Error('DB down'));
|
|
98
|
-
agent.setLongTermMemory(mockBrain);
|
|
99
|
-
|
|
100
|
-
await agent.init();
|
|
101
|
-
const response = await agent.handleMessage(createMessage('hello'));
|
|
102
|
-
expect(response.content).toBeDefined();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('learn failure does not crash handleMessage', async () => {
|
|
106
|
-
mockBrain.learn.mockRejectedValue(new Error('DB down'));
|
|
107
|
-
agent.setLongTermMemory(mockBrain);
|
|
108
|
-
|
|
109
|
-
await agent.init();
|
|
110
|
-
const response = await agent.handleMessage(createMessage('hello'));
|
|
111
|
-
expect(response.content).toBeDefined();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('does not recall when autoRecall is false', async () => {
|
|
115
|
-
agent.setLongTermMemory(mockBrain, { autoRecall: false, autoLearn: true });
|
|
116
|
-
|
|
117
|
-
await agent.init();
|
|
118
|
-
await agent.handleMessage(createMessage('hello'));
|
|
119
|
-
|
|
120
|
-
expect(mockBrain.recall).not.toHaveBeenCalled();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('does not learn when autoLearn is false', async () => {
|
|
124
|
-
agent.setLongTermMemory(mockBrain, { autoRecall: true, autoLearn: false });
|
|
125
|
-
|
|
126
|
-
await agent.init();
|
|
127
|
-
await agent.handleMessage(createMessage('hello'));
|
|
128
|
-
|
|
129
|
-
expect(mockBrain.learn).not.toHaveBeenCalled();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('injects memory context into LLM prompt', async () => {
|
|
133
|
-
mockBrain.recall.mockResolvedValue([{ content: 'user likes cats' }]);
|
|
134
|
-
agent.setLongTermMemory(mockBrain);
|
|
135
|
-
|
|
136
|
-
await agent.init();
|
|
137
|
-
await agent.handleMessage(createMessage('hello'));
|
|
138
|
-
|
|
139
|
-
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
140
|
-
expect(systemPromptArg).toContain('[Relevant memories]');
|
|
141
|
-
expect(systemPromptArg).toContain('user likes cats');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('handles empty recall array gracefully', async () => {
|
|
145
|
-
mockBrain.recall.mockResolvedValue([]);
|
|
146
|
-
agent.setLongTermMemory(mockBrain);
|
|
147
|
-
|
|
148
|
-
await agent.init();
|
|
149
|
-
await agent.handleMessage(createMessage('hello'));
|
|
150
|
-
|
|
151
|
-
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
152
|
-
expect(systemPromptArg).not.toContain('[Relevant memories]');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('handles string recall results', async () => {
|
|
156
|
-
mockBrain.recall.mockResolvedValue('a single memory string');
|
|
157
|
-
agent.setLongTermMemory(mockBrain);
|
|
158
|
-
|
|
159
|
-
await agent.init();
|
|
160
|
-
await agent.handleMessage(createMessage('hello'));
|
|
161
|
-
|
|
162
|
-
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
163
|
-
expect(systemPromptArg).toContain('a single memory string');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('learn includes user and assistant content', async () => {
|
|
167
|
-
mockProvider.chat.mockResolvedValue('I am fine');
|
|
168
|
-
agent.setLongTermMemory(mockBrain);
|
|
169
|
-
|
|
170
|
-
await agent.init();
|
|
171
|
-
await agent.handleMessage(createMessage('how are you'));
|
|
172
|
-
|
|
173
|
-
const learnArg = mockBrain.learn.mock.calls[0][0];
|
|
174
|
-
expect(learnArg).toContain('User: how are you');
|
|
175
|
-
expect(learnArg).toContain('Assistant: I am fine');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('no long-term memory means no recall/learn calls', async () => {
|
|
179
|
-
// No setLongTermMemory called
|
|
180
|
-
await agent.init();
|
|
181
|
-
await agent.handleMessage(createMessage('hello'));
|
|
182
|
-
|
|
183
|
-
// Should work fine without any brain
|
|
184
|
-
expect(mockBrain.recall).not.toHaveBeenCalled();
|
|
185
|
-
expect(mockBrain.learn).not.toHaveBeenCalled();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('getLongTermMemory returns undefined when not set', () => {
|
|
189
|
-
expect(agent.getLongTermMemory()).toBeUndefined();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('default config has autoLearn and autoRecall true', () => {
|
|
193
|
-
const config = agent.getLongTermMemoryConfig();
|
|
194
|
-
expect(config.autoLearn).toBe(true);
|
|
195
|
-
expect(config.autoRecall).toBe(true);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('recall with compiled_truth format', async () => {
|
|
199
|
-
mockBrain.recall.mockResolvedValue([{ compiled_truth: 'agent is helpful' }]);
|
|
200
|
-
agent.setLongTermMemory(mockBrain);
|
|
201
|
-
|
|
202
|
-
await agent.init();
|
|
203
|
-
await agent.handleMessage(createMessage('hello'));
|
|
204
|
-
|
|
205
|
-
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
206
|
-
expect(systemPromptArg).toContain('agent is helpful');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('OAD LongTerm memory schema', () => {
|
|
211
|
-
it('accepts deepbrain config with new fields', async () => {
|
|
212
|
-
const { LongTermMemorySchema } = await import('../src/schema/oad');
|
|
213
|
-
const result = LongTermMemorySchema.parse({
|
|
214
|
-
provider: 'deepbrain',
|
|
215
|
-
collection: 'test',
|
|
216
|
-
config: {
|
|
217
|
-
database: './data/brain.db',
|
|
218
|
-
embeddingProvider: 'ollama',
|
|
219
|
-
autoLearn: true,
|
|
220
|
-
autoRecall: true,
|
|
221
|
-
evolveInterval: 3600000,
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
expect(result.provider).toBe('deepbrain');
|
|
225
|
-
expect(result.config?.database).toBe('./data/brain.db');
|
|
226
|
-
expect(result.config?.evolveInterval).toBe(3600000);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('accepts minimal config', async () => {
|
|
230
|
-
const { LongTermMemorySchema } = await import('../src/schema/oad');
|
|
231
|
-
const result = LongTermMemorySchema.parse({ provider: 'deepbrain' });
|
|
232
|
-
expect(result.provider).toBe('deepbrain');
|
|
233
|
-
});
|
|
234
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { BaseAgent } from '../src/core/agent';
|
|
3
|
+
import type { Message } from '../src/core/types';
|
|
4
|
+
|
|
5
|
+
// Mock LLM provider
|
|
6
|
+
const mockProvider = {
|
|
7
|
+
chat: vi.fn().mockResolvedValue('Hello from LLM'),
|
|
8
|
+
chatStream: vi.fn(),
|
|
9
|
+
listModels: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
vi.mock('../src/providers', () => ({
|
|
13
|
+
createProvider: () => mockProvider,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
function createAgent(opts?: any) {
|
|
17
|
+
return new BaseAgent({
|
|
18
|
+
name: 'test-agent',
|
|
19
|
+
systemPrompt: 'You are a test agent.',
|
|
20
|
+
...opts,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createMessage(content: string): Message {
|
|
25
|
+
return {
|
|
26
|
+
id: `msg_${Date.now()}`,
|
|
27
|
+
role: 'user',
|
|
28
|
+
content,
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('BaseAgent long-term memory', () => {
|
|
34
|
+
let agent: BaseAgent;
|
|
35
|
+
let mockBrain: any;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.clearAllMocks();
|
|
39
|
+
agent = createAgent();
|
|
40
|
+
mockBrain = {
|
|
41
|
+
recall: vi.fn().mockResolvedValue([{ content: 'remembered fact' }]),
|
|
42
|
+
learn: vi.fn().mockResolvedValue(undefined),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('setLongTermMemory sets the brain', () => {
|
|
47
|
+
agent.setLongTermMemory(mockBrain);
|
|
48
|
+
expect(agent.getLongTermMemory()).toBe(mockBrain);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('setLongTermMemory with config sets autoLearn/autoRecall', () => {
|
|
52
|
+
agent.setLongTermMemory(mockBrain, { autoLearn: false, autoRecall: true });
|
|
53
|
+
const config = agent.getLongTermMemoryConfig();
|
|
54
|
+
expect(config.autoLearn).toBe(false);
|
|
55
|
+
expect(config.autoRecall).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('handleMessage calls recall before LLM when autoRecall is true', async () => {
|
|
59
|
+
agent.setLongTermMemory(mockBrain);
|
|
60
|
+
const callOrder: string[] = [];
|
|
61
|
+
mockBrain.recall.mockImplementation(async () => {
|
|
62
|
+
callOrder.push('recall');
|
|
63
|
+
return [{ content: 'memory' }];
|
|
64
|
+
});
|
|
65
|
+
mockProvider.chat.mockImplementation(async () => {
|
|
66
|
+
callOrder.push('llm');
|
|
67
|
+
return 'response';
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await agent.init();
|
|
71
|
+
await agent.handleMessage(createMessage('hello'));
|
|
72
|
+
|
|
73
|
+
expect(callOrder[0]).toBe('recall');
|
|
74
|
+
expect(callOrder[1]).toBe('llm');
|
|
75
|
+
expect(mockBrain.recall).toHaveBeenCalledWith('hello');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('handleMessage calls learn after LLM when autoLearn is true', async () => {
|
|
79
|
+
agent.setLongTermMemory(mockBrain);
|
|
80
|
+
const callOrder: string[] = [];
|
|
81
|
+
mockProvider.chat.mockImplementation(async () => {
|
|
82
|
+
callOrder.push('llm');
|
|
83
|
+
return 'response';
|
|
84
|
+
});
|
|
85
|
+
mockBrain.learn.mockImplementation(async () => {
|
|
86
|
+
callOrder.push('learn');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await agent.init();
|
|
90
|
+
await agent.handleMessage(createMessage('hello'));
|
|
91
|
+
|
|
92
|
+
expect(callOrder.indexOf('llm')).toBeLessThan(callOrder.indexOf('learn'));
|
|
93
|
+
expect(mockBrain.learn).toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('recall failure does not crash handleMessage', async () => {
|
|
97
|
+
mockBrain.recall.mockRejectedValue(new Error('DB down'));
|
|
98
|
+
agent.setLongTermMemory(mockBrain);
|
|
99
|
+
|
|
100
|
+
await agent.init();
|
|
101
|
+
const response = await agent.handleMessage(createMessage('hello'));
|
|
102
|
+
expect(response.content).toBeDefined();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('learn failure does not crash handleMessage', async () => {
|
|
106
|
+
mockBrain.learn.mockRejectedValue(new Error('DB down'));
|
|
107
|
+
agent.setLongTermMemory(mockBrain);
|
|
108
|
+
|
|
109
|
+
await agent.init();
|
|
110
|
+
const response = await agent.handleMessage(createMessage('hello'));
|
|
111
|
+
expect(response.content).toBeDefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('does not recall when autoRecall is false', async () => {
|
|
115
|
+
agent.setLongTermMemory(mockBrain, { autoRecall: false, autoLearn: true });
|
|
116
|
+
|
|
117
|
+
await agent.init();
|
|
118
|
+
await agent.handleMessage(createMessage('hello'));
|
|
119
|
+
|
|
120
|
+
expect(mockBrain.recall).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('does not learn when autoLearn is false', async () => {
|
|
124
|
+
agent.setLongTermMemory(mockBrain, { autoRecall: true, autoLearn: false });
|
|
125
|
+
|
|
126
|
+
await agent.init();
|
|
127
|
+
await agent.handleMessage(createMessage('hello'));
|
|
128
|
+
|
|
129
|
+
expect(mockBrain.learn).not.toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('injects memory context into LLM prompt', async () => {
|
|
133
|
+
mockBrain.recall.mockResolvedValue([{ content: 'user likes cats' }]);
|
|
134
|
+
agent.setLongTermMemory(mockBrain);
|
|
135
|
+
|
|
136
|
+
await agent.init();
|
|
137
|
+
await agent.handleMessage(createMessage('hello'));
|
|
138
|
+
|
|
139
|
+
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
140
|
+
expect(systemPromptArg).toContain('[Relevant memories]');
|
|
141
|
+
expect(systemPromptArg).toContain('user likes cats');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('handles empty recall array gracefully', async () => {
|
|
145
|
+
mockBrain.recall.mockResolvedValue([]);
|
|
146
|
+
agent.setLongTermMemory(mockBrain);
|
|
147
|
+
|
|
148
|
+
await agent.init();
|
|
149
|
+
await agent.handleMessage(createMessage('hello'));
|
|
150
|
+
|
|
151
|
+
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
152
|
+
expect(systemPromptArg).not.toContain('[Relevant memories]');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('handles string recall results', async () => {
|
|
156
|
+
mockBrain.recall.mockResolvedValue('a single memory string');
|
|
157
|
+
agent.setLongTermMemory(mockBrain);
|
|
158
|
+
|
|
159
|
+
await agent.init();
|
|
160
|
+
await agent.handleMessage(createMessage('hello'));
|
|
161
|
+
|
|
162
|
+
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
163
|
+
expect(systemPromptArg).toContain('a single memory string');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('learn includes user and assistant content', async () => {
|
|
167
|
+
mockProvider.chat.mockResolvedValue('I am fine');
|
|
168
|
+
agent.setLongTermMemory(mockBrain);
|
|
169
|
+
|
|
170
|
+
await agent.init();
|
|
171
|
+
await agent.handleMessage(createMessage('how are you'));
|
|
172
|
+
|
|
173
|
+
const learnArg = mockBrain.learn.mock.calls[0][0];
|
|
174
|
+
expect(learnArg).toContain('User: how are you');
|
|
175
|
+
expect(learnArg).toContain('Assistant: I am fine');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('no long-term memory means no recall/learn calls', async () => {
|
|
179
|
+
// No setLongTermMemory called
|
|
180
|
+
await agent.init();
|
|
181
|
+
await agent.handleMessage(createMessage('hello'));
|
|
182
|
+
|
|
183
|
+
// Should work fine without any brain
|
|
184
|
+
expect(mockBrain.recall).not.toHaveBeenCalled();
|
|
185
|
+
expect(mockBrain.learn).not.toHaveBeenCalled();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('getLongTermMemory returns undefined when not set', () => {
|
|
189
|
+
expect(agent.getLongTermMemory()).toBeUndefined();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('default config has autoLearn and autoRecall true', () => {
|
|
193
|
+
const config = agent.getLongTermMemoryConfig();
|
|
194
|
+
expect(config.autoLearn).toBe(true);
|
|
195
|
+
expect(config.autoRecall).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('recall with compiled_truth format', async () => {
|
|
199
|
+
mockBrain.recall.mockResolvedValue([{ compiled_truth: 'agent is helpful' }]);
|
|
200
|
+
agent.setLongTermMemory(mockBrain);
|
|
201
|
+
|
|
202
|
+
await agent.init();
|
|
203
|
+
await agent.handleMessage(createMessage('hello'));
|
|
204
|
+
|
|
205
|
+
const systemPromptArg = mockProvider.chat.mock.calls[0][1];
|
|
206
|
+
expect(systemPromptArg).toContain('agent is helpful');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('OAD LongTerm memory schema', () => {
|
|
211
|
+
it('accepts deepbrain config with new fields', async () => {
|
|
212
|
+
const { LongTermMemorySchema } = await import('../src/schema/oad');
|
|
213
|
+
const result = LongTermMemorySchema.parse({
|
|
214
|
+
provider: 'deepbrain',
|
|
215
|
+
collection: 'test',
|
|
216
|
+
config: {
|
|
217
|
+
database: './data/brain.db',
|
|
218
|
+
embeddingProvider: 'ollama',
|
|
219
|
+
autoLearn: true,
|
|
220
|
+
autoRecall: true,
|
|
221
|
+
evolveInterval: 3600000,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
expect(result.provider).toBe('deepbrain');
|
|
225
|
+
expect(result.config?.database).toBe('./data/brain.db');
|
|
226
|
+
expect(result.config?.evolveInterval).toBe(3600000);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('accepts minimal config', async () => {
|
|
230
|
+
const { LongTermMemorySchema } = await import('../src/schema/oad');
|
|
231
|
+
const result = LongTermMemorySchema.parse({ provider: 'deepbrain' });
|
|
232
|
+
expect(result.provider).toBe('deepbrain');
|
|
233
|
+
});
|
|
234
|
+
});
|