@zhin.js/agent 0.0.20 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +5 -2
  2. package/lib/cron-engine.d.ts +16 -1
  3. package/lib/cron-engine.d.ts.map +1 -1
  4. package/lib/cron-engine.js +47 -13
  5. package/lib/cron-engine.js.map +1 -1
  6. package/lib/discover-skills.d.ts +3 -1
  7. package/lib/discover-skills.d.ts.map +1 -1
  8. package/lib/discover-skills.js +7 -9
  9. package/lib/discover-skills.js.map +1 -1
  10. package/lib/discover-tools.d.ts +1 -6
  11. package/lib/discover-tools.d.ts.map +1 -1
  12. package/lib/discover-tools.js +2 -6
  13. package/lib/discover-tools.js.map +1 -1
  14. package/lib/index.d.ts +2 -4
  15. package/lib/index.d.ts.map +1 -1
  16. package/lib/index.js +1 -2
  17. package/lib/index.js.map +1 -1
  18. package/lib/init/create-zhin-agent.d.ts.map +1 -1
  19. package/lib/init/create-zhin-agent.js +58 -21
  20. package/lib/init/create-zhin-agent.js.map +1 -1
  21. package/lib/init/register-ai-trigger.d.ts.map +1 -1
  22. package/lib/init/register-ai-trigger.js +10 -3
  23. package/lib/init/register-ai-trigger.js.map +1 -1
  24. package/lib/init/register-builtin-tools.d.ts.map +1 -1
  25. package/lib/init/register-builtin-tools.js +46 -14
  26. package/lib/init/register-builtin-tools.js.map +1 -1
  27. package/lib/init/register-db-models.d.ts.map +1 -1
  28. package/lib/init/register-db-models.js +1 -3
  29. package/lib/init/register-db-models.js.map +1 -1
  30. package/lib/init/register-db-upgrade.d.ts.map +1 -1
  31. package/lib/init/register-db-upgrade.js +1 -8
  32. package/lib/init/register-db-upgrade.js.map +1 -1
  33. package/lib/init/register-management-tools.d.ts.map +1 -1
  34. package/lib/init/register-management-tools.js +33 -20
  35. package/lib/init/register-management-tools.js.map +1 -1
  36. package/lib/service.d.ts +4 -0
  37. package/lib/service.d.ts.map +1 -1
  38. package/lib/service.js +3 -8
  39. package/lib/service.js.map +1 -1
  40. package/lib/zhin-agent/builtin-tools.d.ts +0 -2
  41. package/lib/zhin-agent/builtin-tools.d.ts.map +1 -1
  42. package/lib/zhin-agent/builtin-tools.js +0 -55
  43. package/lib/zhin-agent/builtin-tools.js.map +1 -1
  44. package/lib/zhin-agent/config.d.ts +4 -1
  45. package/lib/zhin-agent/config.d.ts.map +1 -1
  46. package/lib/zhin-agent/config.js +2 -1
  47. package/lib/zhin-agent/config.js.map +1 -1
  48. package/lib/zhin-agent/index.d.ts +11 -6
  49. package/lib/zhin-agent/index.d.ts.map +1 -1
  50. package/lib/zhin-agent/index.js +147 -81
  51. package/lib/zhin-agent/index.js.map +1 -1
  52. package/lib/zhin-agent/prompt.d.ts.map +1 -1
  53. package/lib/zhin-agent/prompt.js +31 -76
  54. package/lib/zhin-agent/prompt.js.map +1 -1
  55. package/lib/zhin-agent/tool-collector.d.ts.map +1 -1
  56. package/lib/zhin-agent/tool-collector.js +7 -7
  57. package/lib/zhin-agent/tool-collector.js.map +1 -1
  58. package/package.json +7 -4
  59. package/CHANGELOG.md +0 -170
  60. package/lib/follow-up.d.ts +0 -131
  61. package/lib/follow-up.d.ts.map +0 -1
  62. package/lib/follow-up.js +0 -265
  63. package/lib/follow-up.js.map +0 -1
  64. package/src/agent.ts +0 -6
  65. package/src/bootstrap.ts +0 -309
  66. package/src/builtin-tools.ts +0 -958
  67. package/src/compaction.ts +0 -28
  68. package/src/context-manager.ts +0 -15
  69. package/src/conversation-memory.ts +0 -5
  70. package/src/cron-engine.ts +0 -338
  71. package/src/discover-agents.ts +0 -138
  72. package/src/discover-skills.ts +0 -325
  73. package/src/discover-tools.ts +0 -302
  74. package/src/discovery-utils.ts +0 -96
  75. package/src/file-policy.ts +0 -333
  76. package/src/follow-up.ts +0 -357
  77. package/src/hooks.ts +0 -223
  78. package/src/index.ts +0 -183
  79. package/src/init/create-zhin-agent.ts +0 -136
  80. package/src/init/register-ai-service.ts +0 -53
  81. package/src/init/register-ai-trigger.ts +0 -253
  82. package/src/init/register-builtin-tools.ts +0 -308
  83. package/src/init/register-db-models.ts +0 -31
  84. package/src/init/register-db-upgrade.ts +0 -77
  85. package/src/init/register-management-tools.ts +0 -71
  86. package/src/init/register-message-recorder.ts +0 -31
  87. package/src/init/register-tool-service.ts +0 -9
  88. package/src/init/shared-refs.ts +0 -20
  89. package/src/init/types.ts +0 -18
  90. package/src/init.ts +0 -50
  91. package/src/output.ts +0 -15
  92. package/src/rate-limiter.ts +0 -5
  93. package/src/service.ts +0 -224
  94. package/src/session.ts +0 -13
  95. package/src/storage.ts +0 -9
  96. package/src/subagent.ts +0 -209
  97. package/src/tone-detector.ts +0 -5
  98. package/src/tools.ts +0 -214
  99. package/src/user-profile.ts +0 -182
  100. package/src/zhin-agent/builtin-tools.ts +0 -247
  101. package/src/zhin-agent/config.ts +0 -121
  102. package/src/zhin-agent/exec-policy.ts +0 -285
  103. package/src/zhin-agent/index.ts +0 -559
  104. package/src/zhin-agent/prompt.ts +0 -305
  105. package/src/zhin-agent/tool-collector.ts +0 -249
  106. package/tests/ai/follow-up.test.ts +0 -175
  107. package/tests/ai/integration.test.ts +0 -582
  108. package/tests/ai/multimodal.test.ts +0 -106
  109. package/tests/ai/setup.ts +0 -186
  110. package/tests/ai/subagent.test.ts +0 -270
  111. package/tests/ai/tools-builtin.test.ts +0 -310
  112. package/tests/ai/user-profile.test.ts +0 -73
  113. package/tests/ai/zhin-agent.test.ts +0 -306
  114. package/tests/exec-policy.test.ts +0 -355
  115. package/tests/file-policy.test.ts +0 -405
  116. package/tsconfig.json +0 -22
package/tests/ai/setup.ts DELETED
@@ -1,186 +0,0 @@
1
- /**
2
- * Agent 模块测试环境设置
3
- * 提供 Mock 与测试辅助,类型与规范从 @zhin.js/core 引用
4
- * 通用 AI mocks 从 @zhin.js/ai 测试 setup 导入
5
- */
6
- import { vi } from 'vitest';
7
- import type { Message, MessageElement, Tool, ToolContext } from '@zhin.js/core';
8
- import type { AIConfig } from '@zhin.js/core';
9
-
10
- // Import and re-export generic AI mocks from ai package tests
11
- import {
12
- createMockLogger,
13
- createMockProvider,
14
- createChatMessage,
15
- delay,
16
- waitFor,
17
- collectAsyncGenerator,
18
- } from '../../../ai/tests/setup.js';
19
- export {
20
- createMockLogger,
21
- createMockProvider,
22
- createChatMessage,
23
- delay,
24
- waitFor,
25
- collectAsyncGenerator,
26
- };
27
-
28
- export const createMockPlugin = (name = 'test-plugin') => ({
29
- name,
30
- root: {
31
- inject: vi.fn(),
32
- contexts: new Map(),
33
- middleware: vi.fn(),
34
- },
35
- logger: createMockLogger(),
36
- onDispose: vi.fn(),
37
- collectAllTools: vi.fn(() => []),
38
- provide: vi.fn(),
39
- useContext: vi.fn(),
40
- addMiddleware: vi.fn(),
41
- });
42
-
43
- export interface MockMessageOptions {
44
- content?: string;
45
- elements?: MessageElement[];
46
- platform?: string;
47
- channelType?: 'group' | 'private' | 'guild';
48
- channelId?: string;
49
- senderId?: string;
50
- senderPermissions?: string[];
51
- senderRole?: string;
52
- botId?: string;
53
- }
54
-
55
- export const createMockMessage = (options: MockMessageOptions = {}): Partial<Message> => {
56
- const {
57
- content = '测试消息',
58
- elements,
59
- platform = 'test',
60
- channelType = 'group',
61
- channelId = 'channel-1',
62
- senderId = 'user-1',
63
- senderPermissions = [],
64
- senderRole,
65
- botId = 'bot-1',
66
- } = options;
67
-
68
- const $content: MessageElement[] = elements || [
69
- { type: 'text', data: { text: content } },
70
- ];
71
-
72
- return {
73
- $content,
74
- $bot: botId,
75
- $adapter: platform,
76
- $channel: {
77
- type: channelType,
78
- id: channelId,
79
- name: 'Test Channel',
80
- },
81
- $sender: {
82
- id: senderId,
83
- name: 'Test User',
84
- permissions: senderPermissions,
85
- role: senderRole,
86
- },
87
- $reply: vi.fn().mockResolvedValue(undefined),
88
- $quote: vi.fn().mockResolvedValue(undefined),
89
- };
90
- };
91
-
92
- export interface MockToolOptions {
93
- name: string;
94
- description?: string;
95
- parameters?: Record<string, { type: string; description?: string }>;
96
- required?: string[];
97
- platforms?: string[];
98
- scopes?: ('group' | 'private' | 'guild')[];
99
- permissionLevel?: 'user' | 'group_admin' | 'group_owner' | 'bot_admin' | 'owner';
100
- tags?: string[];
101
- keywords?: string[];
102
- executeResult?: any;
103
- executeError?: Error;
104
- }
105
-
106
- export const createMockTool = (options: MockToolOptions): Tool => {
107
- const {
108
- name,
109
- description = `${name} 工具`,
110
- parameters = {},
111
- required = [],
112
- platforms,
113
- scopes,
114
- permissionLevel,
115
- tags = [],
116
- keywords = [],
117
- executeResult = 'success',
118
- executeError,
119
- } = options;
120
-
121
- const properties: Record<string, any> = {};
122
- for (const [key, value] of Object.entries(parameters)) {
123
- properties[key] = {
124
- type: value.type,
125
- description: value.description,
126
- };
127
- }
128
-
129
- const tool: any = {
130
- name,
131
- description,
132
- parameters: {
133
- type: 'object',
134
- properties,
135
- required,
136
- },
137
- platforms,
138
- scopes,
139
- permissionLevel,
140
- tags,
141
- execute: executeError
142
- ? vi.fn().mockRejectedValue(executeError)
143
- : vi.fn().mockResolvedValue(executeResult),
144
- };
145
- if (keywords.length > 0) tool.keywords = keywords;
146
- return tool;
147
- };
148
-
149
- export const createToolContext = (options: Partial<ToolContext> = {}): ToolContext => ({
150
- platform: 'test',
151
- scope: 'group',
152
- permissionLevel: 'user',
153
- ...options,
154
- });
155
-
156
- export const createMockAIConfig = (overrides: Partial<AIConfig> = {}): AIConfig => ({
157
- defaultProvider: 'mock',
158
- sessions: {
159
- maxHistory: 10,
160
- expireMs: 300000,
161
- },
162
- context: {
163
- enabled: false,
164
- maxSize: 100,
165
- },
166
- trigger: {
167
- enabled: true,
168
- prefixes: ['#'],
169
- ignorePrefixes: ['/'],
170
- allowAtBot: true,
171
- allowPrivateChat: true,
172
- },
173
- ...overrides,
174
- });
175
-
176
- export const assertToolParameters = (
177
- tool: Tool,
178
- expectedProperties: string[],
179
- expectedRequired: string[] = []
180
- ) => {
181
- const props = Object.keys(tool.parameters.properties || {});
182
- expect(props).toEqual(expect.arrayContaining(expectedProperties));
183
- if (expectedRequired.length > 0) {
184
- expect(tool.parameters.required).toEqual(expect.arrayContaining(expectedRequired));
185
- }
186
- };
@@ -1,270 +0,0 @@
1
- /**
2
- * SubagentManager 测试
3
- */
4
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
5
- import { SubagentManager } from '@zhin.js/agent';
6
- import type { SubagentOrigin, SpawnOptions } from '@zhin.js/agent';
7
- import type { AgentTool, ChatCompletionResponse } from '@zhin.js/core';
8
-
9
- // Mock Logger
10
- vi.mock('@zhin.js/logger', () => ({
11
- Logger: class {
12
- debug = vi.fn();
13
- info = vi.fn();
14
- warn = vi.fn();
15
- error = vi.fn();
16
- },
17
- }));
18
-
19
- const baseOrigin: SubagentOrigin = {
20
- platform: 'test',
21
- botId: 'bot1',
22
- sceneId: 'scene1',
23
- senderId: 'user1',
24
- sceneType: 'private',
25
- };
26
-
27
- function createMockProvider(response: string = '任务完成') {
28
- return {
29
- name: 'mock',
30
- models: ['mock-model'],
31
- chat: vi.fn(async () => ({
32
- choices: [{ message: { role: 'assistant', content: response }, finish_reason: 'stop' }],
33
- usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
34
- } as ChatCompletionResponse)),
35
- listModels: vi.fn(async () => ['mock-model']),
36
- };
37
- }
38
-
39
- function createMockTools(): AgentTool[] {
40
- return [
41
- {
42
- name: 'read_file',
43
- description: '读取文件',
44
- parameters: { type: 'object', properties: { file_path: { type: 'string' } } },
45
- execute: vi.fn(async () => 'file content'),
46
- },
47
- {
48
- name: 'write_file',
49
- description: '写入文件',
50
- parameters: { type: 'object', properties: { file_path: { type: 'string' } } },
51
- execute: vi.fn(async () => 'ok'),
52
- },
53
- {
54
- name: 'web_search',
55
- description: '搜索',
56
- parameters: { type: 'object', properties: { query: { type: 'string' } } },
57
- execute: vi.fn(async () => 'search result'),
58
- },
59
- // 不应进入子 agent 的工具
60
- {
61
- name: 'spawn_task',
62
- description: '派生子任务',
63
- parameters: { type: 'object', properties: { task: { type: 'string' } } },
64
- execute: vi.fn(async () => 'should not be called'),
65
- },
66
- {
67
- name: 'activate_skill',
68
- description: '激活技能',
69
- parameters: { type: 'object', properties: { name: { type: 'string' } } },
70
- execute: vi.fn(async () => 'should not be called'),
71
- },
72
- {
73
- name: 'todo_write',
74
- description: '写计划',
75
- parameters: { type: 'object', properties: {} },
76
- execute: vi.fn(async () => 'should not be called'),
77
- },
78
- ];
79
- }
80
-
81
- describe('SubagentManager', () => {
82
- let manager: SubagentManager;
83
- let provider: ReturnType<typeof createMockProvider>;
84
- let mockTools: AgentTool[];
85
-
86
- beforeEach(() => {
87
- provider = createMockProvider();
88
- mockTools = createMockTools();
89
- manager = new SubagentManager({
90
- provider: provider as any,
91
- workspace: '/tmp/test-workspace',
92
- createTools: () => mockTools,
93
- maxIterations: 5,
94
- });
95
- });
96
-
97
- afterEach(() => {
98
- manager.dispose();
99
- });
100
-
101
- describe('spawn', () => {
102
- it('应返回确认文本并包含任务标签', async () => {
103
- const result = await manager.spawn({
104
- task: '分析项目结构',
105
- label: '结构分析',
106
- origin: baseOrigin,
107
- });
108
-
109
- expect(result).toContain('结构分析');
110
- expect(result).toContain('已启动');
111
- });
112
-
113
- it('无 label 时应自动截取 task 前30字符', async () => {
114
- const result = await manager.spawn({
115
- task: '这是一个非常长的任务描述用于测试自动截取功能',
116
- origin: baseOrigin,
117
- });
118
-
119
- expect(result).toContain('已启动');
120
- expect(result).toContain('这是一个非常长的任务描述用于测试自动截取功能');
121
- });
122
-
123
- it('应递增 runningTasks 计数', async () => {
124
- // 让 provider 永不返回,模拟长时间运行
125
- provider.chat.mockImplementation(() => new Promise(() => {}));
126
-
127
- expect(manager.getRunningCount()).toBe(0);
128
-
129
- await manager.spawn({ task: '任务1', origin: baseOrigin });
130
-
131
- // 异步启动后应有 1 个运行中的任务
132
- expect(manager.getRunningCount()).toBe(1);
133
- });
134
- });
135
-
136
- describe('工具过滤', () => {
137
- it('子 agent 应只获得白名单内的工具', async () => {
138
- const sender = vi.fn();
139
- manager.setSender(sender);
140
-
141
- await manager.spawn({ task: '测试', origin: baseOrigin });
142
-
143
- // 等待子 agent 完成
144
- await vi.waitFor(() => expect(sender).toHaveBeenCalled(), { timeout: 2000 });
145
-
146
- // 验证 provider.chat 被调用时传入的工具列表
147
- const chatCall = provider.chat.mock.calls[0][0] ?? provider.chat.mock.calls[0];
148
- // createAgent 调用 provider.chat({ model, messages, tools, ... })
149
- // 查找 tools 参数
150
- const callArgs = provider.chat.mock.calls[0];
151
- // 接口是 chat(request) 形式
152
- const request = callArgs[0] as any;
153
- if (request?.tools) {
154
- const toolNames = request.tools.map((t: any) => t.function?.name || t.name);
155
- expect(toolNames).toContain('read_file');
156
- expect(toolNames).toContain('write_file');
157
- expect(toolNames).toContain('web_search');
158
- expect(toolNames).not.toContain('spawn_task');
159
- expect(toolNames).not.toContain('activate_skill');
160
- expect(toolNames).not.toContain('todo_write');
161
- }
162
- });
163
- });
164
-
165
- describe('结果回告', () => {
166
- it('完成后应通过 sender 发送结果', async () => {
167
- const sender = vi.fn();
168
- manager.setSender(sender);
169
-
170
- await manager.spawn({ task: '读取 README', label: '读README', origin: baseOrigin });
171
-
172
- // 等待异步子 agent 完成
173
- await vi.waitFor(() => expect(sender).toHaveBeenCalled(), { timeout: 2000 });
174
-
175
- expect(sender).toHaveBeenCalledTimes(1);
176
- const [origin, content] = sender.mock.calls[0];
177
- expect(origin).toEqual(baseOrigin);
178
- expect(content).toContain('读README');
179
- expect(content).toContain('已完成');
180
- });
181
-
182
- it('provider 错误时 Agent 内部兜底,结果仍应送达', async () => {
183
- provider.chat.mockRejectedValue(new Error('API 调用失败'));
184
- const sender = vi.fn();
185
- manager.setSender(sender);
186
-
187
- await manager.spawn({ task: '会失败的任务', label: '失败测试', origin: baseOrigin });
188
-
189
- await vi.waitFor(() => expect(sender).toHaveBeenCalled(), { timeout: 2000 });
190
-
191
- const [_origin, content] = sender.mock.calls[0];
192
- // Agent.run() 内部兜底返回友好文本,SubagentManager 视为成功完成
193
- expect(content).toContain('失败测试');
194
- expect(content).toContain('API 调用失败');
195
- });
196
-
197
- it('无 sender 时不应崩溃', async () => {
198
- // 不设置 sender
199
- await manager.spawn({ task: '测试', origin: baseOrigin });
200
-
201
- // 等待子 agent 完成(不应抛错)
202
- await new Promise(r => setTimeout(r, 200));
203
- expect(manager.getRunningCount()).toBe(0);
204
- });
205
- });
206
-
207
- describe('完成后清理', () => {
208
- it('完成后应从 runningTasks 移除', async () => {
209
- const sender = vi.fn();
210
- manager.setSender(sender);
211
-
212
- await manager.spawn({ task: '快速任务', origin: baseOrigin });
213
-
214
- // 等待完成
215
- await vi.waitFor(() => expect(sender).toHaveBeenCalled(), { timeout: 2000 });
216
-
217
- expect(manager.getRunningCount()).toBe(0);
218
- });
219
-
220
- it('失败后也应从 runningTasks 移除', async () => {
221
- provider.chat.mockRejectedValue(new Error('boom'));
222
- const sender = vi.fn();
223
- manager.setSender(sender);
224
-
225
- await manager.spawn({ task: '会失败', origin: baseOrigin });
226
-
227
- await vi.waitFor(() => expect(sender).toHaveBeenCalled(), { timeout: 2000 });
228
-
229
- expect(manager.getRunningCount()).toBe(0);
230
- });
231
- });
232
-
233
- describe('dispose', () => {
234
- it('应清空 runningTasks', () => {
235
- manager.dispose();
236
- expect(manager.getRunningCount()).toBe(0);
237
- });
238
- });
239
- });
240
-
241
- describe('ZhinAgent spawn_task 集成', () => {
242
- it('spawn_task 工具应在关键词匹配时被注入', async () => {
243
- // 这部分在 zhin-agent.test.ts 中已有 process 的集成测试框架
244
- // 此处仅验证关键词正则匹配逻辑
245
- const patterns = [
246
- '帮我在后台分析一下代码',
247
- '异步搜索一下文件',
248
- '把这个交给子任务处理',
249
- 'spawn a background task',
250
- '用background方式执行',
251
- '并行处理这个任务',
252
- '独立处理这个问题',
253
- ];
254
-
255
- const regex = /后台|子任务|spawn|异步|background|并行|独立处理/i;
256
- for (const msg of patterns) {
257
- expect(regex.test(msg), `"${msg}" 应匹配`).toBe(true);
258
- }
259
-
260
- const negativePatterns = [
261
- '你好',
262
- '帮我查天气',
263
- '读取文件内容',
264
- '提醒我喝水',
265
- ];
266
- for (const msg of negativePatterns) {
267
- expect(regex.test(msg), `"${msg}" 不应匹配`).toBe(false);
268
- }
269
- });
270
- });