@dsh-overdrive/gateway 0.3.0 → 0.3.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/package.json +9 -4
- package/src/adapter.ts +0 -42
- package/src/adapters/cli.ts +0 -37
- package/src/adapters/dingtalk.ts +0 -206
- package/src/adapters/discord.ts +0 -127
- package/src/adapters/feishu.ts +0 -224
- package/src/adapters/slack.ts +0 -123
- package/src/adapters/telegram.ts +0 -142
- package/src/adapters/wechat.ts +0 -247
- package/src/adapters/wecom.ts +0 -218
- package/src/adapters/whatsapp.ts +0 -249
- package/src/asr.ts +0 -83
- package/src/commands.ts +0 -98
- package/src/config.ts +0 -104
- package/src/feed.ts +0 -190
- package/src/index.ts +0 -510
- package/src/memory.ts +0 -176
- package/src/mention.ts +0 -51
- package/src/pending-buttons.ts +0 -65
- package/src/session.ts +0 -23
- package/src/setup.ts +0 -252
- package/src/status.ts +0 -63
- package/src/text.ts +0 -32
- package/src/trajectory.ts +0 -45
- package/test/adapters.dingtalk.test.ts +0 -64
- package/test/adapters.discord.test.ts +0 -41
- package/test/adapters.feishu.test.ts +0 -66
- package/test/adapters.slack.test.ts +0 -45
- package/test/adapters.telegram.test.ts +0 -37
- package/test/adapters.wechat.test.ts +0 -78
- package/test/adapters.wecom.test.ts +0 -62
- package/test/adapters.whatsapp.test.ts +0 -138
- package/test/asr.test.ts +0 -77
- package/test/commands.test.ts +0 -53
- package/test/config.test.ts +0 -30
- package/test/feed.test.ts +0 -111
- package/test/memory.test.ts +0 -79
- package/test/mention.test.ts +0 -54
- package/test/multi.test.ts +0 -284
- package/test/outbound.test.ts +0 -29
- package/test/pending-buttons.test.ts +0 -100
- package/test/session.test.ts +0 -26
- package/test/status.test.ts +0 -41
- package/test/streaming.test.ts +0 -162
- package/test/text.test.ts +0 -20
- package/test/trajectory.test.ts +0 -58
- package/tsconfig.json +0 -5
package/test/session.test.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { Allowlist, buildSessionKey } from '../src/session.js';
|
|
3
|
-
|
|
4
|
-
describe('buildSessionKey', () => {
|
|
5
|
-
it('用 adapterId + chatId + userId 拼会话键', () => {
|
|
6
|
-
expect(buildSessionKey('whatsapp', { chatId: '60123', userId: '60123' })).toBe('whatsapp:60123:60123');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
describe('Allowlist', () => {
|
|
11
|
-
it('空列表 fail-closed:拒绝所有(生产默认)', () => {
|
|
12
|
-
const allow = new Allowlist([]);
|
|
13
|
-
expect(allow.allows('anything:any:any')).toBe(false);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('非空列表只放行白名单条目', () => {
|
|
17
|
-
const allow = new Allowlist(['whatsapp:60123:60123']);
|
|
18
|
-
expect(allow.allows('whatsapp:60123:60123')).toBe(true);
|
|
19
|
-
expect(allow.allows('whatsapp:99999:99999')).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('ALLOW_ALL 显式放行所有(开发逃生口)', () => {
|
|
23
|
-
const allow = new Allowlist([], true);
|
|
24
|
-
expect(allow.allows('anything:any:any')).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
});
|
package/test/status.test.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { createStatusServer } from '../src/status.js';
|
|
3
|
-
import type { Adapter } from '../src/adapter.js';
|
|
4
|
-
import { GatewayClient } from '@dsh-overdrive/sdk';
|
|
5
|
-
|
|
6
|
-
describe('createStatusServer', () => {
|
|
7
|
-
let server: ReturnType<typeof createStatusServer> | undefined;
|
|
8
|
-
let port = 0;
|
|
9
|
-
afterEach(async () => { await server?.close(); server = undefined; });
|
|
10
|
-
|
|
11
|
-
async function start(fakeAdapters: Adapter[]): Promise<string> {
|
|
12
|
-
const client = {
|
|
13
|
-
health: async () => ({ status: 'ok' as const, version: '0.1.0' }),
|
|
14
|
-
} as unknown as GatewayClient;
|
|
15
|
-
server = createStatusServer({ adapters: fakeAdapters, client, version: '0.1.0' });
|
|
16
|
-
port = await server.listen(0);
|
|
17
|
-
return `http://127.0.0.1:${port}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
it('/api/status 返回 dsh 健康与适配器状态', async () => {
|
|
21
|
-
const url = await start([
|
|
22
|
-
{ id: 'telegram', status: () => ({ connected: true }) },
|
|
23
|
-
{ id: 'whatsapp', status: () => ({ connected: false }) },
|
|
24
|
-
] as unknown as Adapter[]);
|
|
25
|
-
const res = await fetch(`${url}/api/status`);
|
|
26
|
-
expect(res.status).toBe(200);
|
|
27
|
-
const body = await res.json();
|
|
28
|
-
expect(body.dsh.status).toBe('ok');
|
|
29
|
-
expect(body.adapters).toEqual([
|
|
30
|
-
{ id: 'telegram', connected: true },
|
|
31
|
-
{ id: 'whatsapp', connected: false },
|
|
32
|
-
]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('GET / 返回 HTML 控制台页', async () => {
|
|
36
|
-
const url = await start([]);
|
|
37
|
-
const res = await fetch(`${url}/`);
|
|
38
|
-
expect(res.status).toBe(200);
|
|
39
|
-
expect((await res.text())).toContain('dsh-overdrive');
|
|
40
|
-
});
|
|
41
|
-
});
|
package/test/streaming.test.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { DeltaTracker, planOutbound, wireAdapter } from '../src/index.js';
|
|
3
|
-
import { HELP_TEXT } from '../src/commands.js';
|
|
4
|
-
import { GatewayClient, type ServerEvent } from '@dsh-overdrive/sdk';
|
|
5
|
-
import type { Adapter, NormalizedMessage, OutboundPayload } from '../src/adapter.js';
|
|
6
|
-
|
|
7
|
-
describe('DeltaTracker(message.delta → 打字指示,complete → 终稿)', () => {
|
|
8
|
-
it('首个 delta 触发 typing,重复 delta 不重复触发', () => {
|
|
9
|
-
const t = new DeltaTracker();
|
|
10
|
-
const typings: string[] = [];
|
|
11
|
-
const outputs: string[] = [];
|
|
12
|
-
t.onDelta('s1', () => typings.push('s1'));
|
|
13
|
-
t.onDelta('s1', () => typings.push('s1'));
|
|
14
|
-
expect(typings).toEqual(['s1']);
|
|
15
|
-
void outputs;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('complete 后同会话下一个 turn 的 delta 可再次触发 typing', () => {
|
|
19
|
-
const t = new DeltaTracker();
|
|
20
|
-
const typings: string[] = [];
|
|
21
|
-
t.onDelta('s1', () => typings.push('s1'));
|
|
22
|
-
t.onComplete('s1');
|
|
23
|
-
t.onDelta('s1', () => typings.push('s1'));
|
|
24
|
-
expect(typings).toEqual(['s1', 's1']);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('planOutbound(trajectory.summary 摘要卡片渲染)', () => {
|
|
29
|
-
it('trajectory.summary → formatTrajectorySummary 文本', () => {
|
|
30
|
-
const ev: ServerEvent = {
|
|
31
|
-
type: 'trajectory.summary', sessionId: 'cli:cli:local', ts: 1,
|
|
32
|
-
steps: [{ kind: 'thought', label: '分析' }, { kind: 'tool', label: 'bash' }],
|
|
33
|
-
};
|
|
34
|
-
const out = planOutbound(ev)!;
|
|
35
|
-
expect(out.payload.text).toContain('📋 轨迹(2 步)');
|
|
36
|
-
expect(out.payload.text).toContain('🧠 分析');
|
|
37
|
-
expect(out.payload.text).toContain('🛠️ bash');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
/** 可编程 FakeAdapter(带 sendTyping 探测)。 */
|
|
42
|
-
class FakeAdapter implements Adapter {
|
|
43
|
-
readonly id: string;
|
|
44
|
-
readonly sent: Array<{ chatId: string; payload: OutboundPayload }> = [];
|
|
45
|
-
readonly typings: string[] = [];
|
|
46
|
-
private messageCb?: (msg: NormalizedMessage) => Promise<void> | void;
|
|
47
|
-
private replyCb?: (buttonId: string) => Promise<void> | void;
|
|
48
|
-
constructor(id: string) { this.id = id; }
|
|
49
|
-
async connect(): Promise<void> {}
|
|
50
|
-
async send(chatId: string, payload: OutboundPayload): Promise<void> { this.sent.push({ chatId, payload }); }
|
|
51
|
-
async sendTyping(chatId: string): Promise<void> { this.typings.push(chatId); }
|
|
52
|
-
onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
|
|
53
|
-
onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
|
|
54
|
-
emit(msg: NormalizedMessage): Promise<void> | void { return this.messageCb?.(msg); }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** 假 DSH 客户端:可编程事件推流 + 记录调用。 */
|
|
58
|
-
function fakeClient() {
|
|
59
|
-
const createTasks: Array<{ sessionId: string; kind: string; prompt: string; schedule?: string }> = [];
|
|
60
|
-
const resets: string[] = [];
|
|
61
|
-
let eventCb: ((ev: ServerEvent) => void) | undefined;
|
|
62
|
-
const client = {
|
|
63
|
-
upsertSession: async (req: { platform: string; channel: string; user: string }) =>
|
|
64
|
-
({ sessionId: `${req.platform}:${req.channel}:${req.user}` }),
|
|
65
|
-
sendMessage: async () => ({ runId: 'r1' }),
|
|
66
|
-
resolveApproval: async () => ({ ok: true }),
|
|
67
|
-
createTask: async (req: { sessionId: string; kind: 'subagent' | 'cron'; prompt: string; schedule?: string }) => {
|
|
68
|
-
createTasks.push(req);
|
|
69
|
-
return { taskId: 't1' };
|
|
70
|
-
},
|
|
71
|
-
resetSession: async (sessionId: string) => { resets.push(sessionId); return { ok: true }; },
|
|
72
|
-
connect: async (cb: (ev: ServerEvent) => void) => { eventCb = cb; return () => undefined; },
|
|
73
|
-
};
|
|
74
|
-
const push = (ev: ServerEvent): void => eventCb?.(ev);
|
|
75
|
-
return { client, createTasks, resets, push } as unknown as {
|
|
76
|
-
client: GatewayClient; createTasks: typeof createTasks; resets: string[];
|
|
77
|
-
push: (ev: ServerEvent) => void;
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
describe('wireAdapter(命令分发 + delta 打字指示 + 轨迹聚合接线)', () => {
|
|
82
|
-
it('message.delta → sendTyping 一次;complete 后下一 turn 再触发', async () => {
|
|
83
|
-
const adapter = new FakeAdapter('cli');
|
|
84
|
-
const { client, push } = fakeClient();
|
|
85
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
86
|
-
|
|
87
|
-
push({ type: 'message.delta', sessionId: 'cli:cli:local', ts: 1, text: '…' });
|
|
88
|
-
push({ type: 'message.delta', sessionId: 'cli:cli:local', ts: 2, text: '…' });
|
|
89
|
-
expect(adapter.typings).toEqual(['cli']); // sendTyping 目标是 chatId(无消息时回退到 channel)
|
|
90
|
-
expect(adapter.sent).toHaveLength(0); // delta 不产出文本
|
|
91
|
-
|
|
92
|
-
push({ type: 'message.complete', sessionId: 'cli:cli:local', ts: 3, text: '结果' });
|
|
93
|
-
push({ type: 'message.delta', sessionId: 'cli:cli:local', ts: 4, text: '…' });
|
|
94
|
-
expect(adapter.typings).toEqual(['cli', 'cli']);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('trajectory.step 不实时输出,idle 时以 trajectory.summary 摘要输出', async () => {
|
|
98
|
-
const adapter = new FakeAdapter('cli');
|
|
99
|
-
const { client, push } = fakeClient();
|
|
100
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
101
|
-
|
|
102
|
-
push({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 1, status: 'busy' });
|
|
103
|
-
push({ type: 'trajectory.step', sessionId: 'cli:cli:local', ts: 2, step: { kind: 'thought', label: '分析' } });
|
|
104
|
-
push({ type: 'trajectory.step', sessionId: 'cli:cli:local', ts: 3, step: { kind: 'tool', label: 'bash' } });
|
|
105
|
-
expect(adapter.sent).toHaveLength(0); // 单步不推
|
|
106
|
-
|
|
107
|
-
push({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 4, status: 'idle' });
|
|
108
|
-
const summary = adapter.sent.find((s) => s.payload.text.includes('📋 轨迹'));
|
|
109
|
-
expect(summary?.payload.text).toContain('🧠 分析');
|
|
110
|
-
expect(summary?.payload.text).toContain('🛠️ bash');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('/help → HELP_TEXT 原样输出', async () => {
|
|
114
|
-
const adapter = new FakeAdapter('cli');
|
|
115
|
-
const { client } = fakeClient();
|
|
116
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
117
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/help' });
|
|
118
|
-
expect(adapter.sent[0].payload.text).toBe(HELP_TEXT);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('/task 派子任务并回执;/cron 注册定时任务并回执', async () => {
|
|
122
|
-
const adapter = new FakeAdapter('cli');
|
|
123
|
-
const { client, createTasks } = fakeClient();
|
|
124
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
125
|
-
|
|
126
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/task 调研竞品' });
|
|
127
|
-
expect(createTasks).toContainEqual({ sessionId: 'cli:cli:local', kind: 'subagent', prompt: '调研竞品' });
|
|
128
|
-
expect(adapter.sent.at(-1)!.payload.text).toContain('🤖 子任务已派出');
|
|
129
|
-
|
|
130
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/cron 0 8 * * * 每日汇报' });
|
|
131
|
-
expect(createTasks).toContainEqual({ sessionId: 'cli:cli:local', kind: 'cron', prompt: '每日汇报', schedule: '0 8 * * *' });
|
|
132
|
-
expect(adapter.sent.at(-1)!.payload.text).toContain('⏰ 定时任务已注册');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('/new 走 resetSession 端点并回执;/trace 显示最近摘要,无则提示', async () => {
|
|
136
|
-
const adapter = new FakeAdapter('cli');
|
|
137
|
-
const { client, resets, push } = fakeClient();
|
|
138
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
139
|
-
|
|
140
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/trace' });
|
|
141
|
-
expect(adapter.sent.at(-1)!.payload.text).toContain('暂无轨迹');
|
|
142
|
-
|
|
143
|
-
push({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 1, status: 'busy' });
|
|
144
|
-
push({ type: 'trajectory.step', sessionId: 'cli:cli:local', ts: 2, step: { kind: 'thought', label: '分析' } });
|
|
145
|
-
push({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 3, status: 'idle' });
|
|
146
|
-
|
|
147
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/trace' });
|
|
148
|
-
expect(adapter.sent.at(-1)!.payload.text).toContain('🧠 分析');
|
|
149
|
-
|
|
150
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/new' });
|
|
151
|
-
expect(resets).toEqual(['cli:cli:local']);
|
|
152
|
-
expect(adapter.sent.at(-1)!.payload.text).toContain('🆕 会话已重置');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('/agents 返回简化回执', async () => {
|
|
156
|
-
const adapter = new FakeAdapter('cli');
|
|
157
|
-
const { client } = fakeClient();
|
|
158
|
-
await wireAdapter(adapter, client, { allowlist: [], allowAll: true });
|
|
159
|
-
await adapter.emit({ chatId: 'cli', userId: 'local', text: '/agents' });
|
|
160
|
-
expect(adapter.sent[0].payload.text).toContain('/task 派发');
|
|
161
|
-
});
|
|
162
|
-
});
|
package/test/text.test.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { chunkLongText } from '../src/text.js';
|
|
3
|
-
|
|
4
|
-
describe('chunkLongText', () => {
|
|
5
|
-
it('短文本不分片', () => {
|
|
6
|
-
expect(chunkLongText('hello')).toEqual(['hello']);
|
|
7
|
-
});
|
|
8
|
-
it('超长文本按句号断行并带序号', () => {
|
|
9
|
-
const text = ('这是一段很长的内容。'.repeat(200));
|
|
10
|
-
const chunks = chunkLongText(text, 100);
|
|
11
|
-
expect(chunks.length).toBeGreaterThan(1);
|
|
12
|
-
expect(chunks[0]).toMatch(/(\d+\/\d+)$/);
|
|
13
|
-
expect(chunks.every((c) => c.length <= 130)).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
it('无标点长文本按硬上限截断', () => {
|
|
16
|
-
const chunks = chunkLongText('x'.repeat(500), 200);
|
|
17
|
-
expect(chunks.length).toBe(3);
|
|
18
|
-
expect(chunks[0].length).toBeLessThanOrEqual(210);
|
|
19
|
-
});
|
|
20
|
-
});
|
package/test/trajectory.test.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { TrajectoryAggregator, formatTrajectorySummary } from '../src/trajectory.js';
|
|
3
|
-
import type { ServerEvent, TrajectoryStep } from '@dsh-overdrive/sdk';
|
|
4
|
-
|
|
5
|
-
// 注意:ServerEvent 联合类型尚无 trajectory.summary(Task 2 协议修改才加入),
|
|
6
|
-
// 故本文件对 summary 事件统一用结构断言 + as 断言,不修改 sdk 协议。
|
|
7
|
-
describe('TrajectoryAggregator', () => {
|
|
8
|
-
it('聚合 turn 内轨迹,turn 结束产出摘要', () => {
|
|
9
|
-
const agg = new TrajectoryAggregator();
|
|
10
|
-
const events: ServerEvent[] = [];
|
|
11
|
-
agg.onEvent({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 1, status: 'busy' }, (ev) => events.push(ev));
|
|
12
|
-
agg.onEvent({ type: 'trajectory.step', sessionId: 'cli:cli:local', ts: 2, step: { kind: 'thought', label: '分析' } }, (ev) => events.push(ev));
|
|
13
|
-
agg.onEvent({ type: 'trajectory.step', sessionId: 'cli:cli:local', ts: 3, step: { kind: 'tool', label: 'bash' } }, (ev) => events.push(ev));
|
|
14
|
-
agg.onEvent({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 4, status: 'idle' }, (ev) => events.push(ev));
|
|
15
|
-
|
|
16
|
-
const summary = events.find((e) => (e as { type: string }).type === 'trajectory.summary') as
|
|
17
|
-
| { type: 'trajectory.summary'; steps: TrajectoryStep[] }
|
|
18
|
-
| undefined;
|
|
19
|
-
expect(summary).toBeDefined();
|
|
20
|
-
// 直接比较结构(Task 2 的协议变体含 steps,渲染由 formatTrajectorySummary 负责)
|
|
21
|
-
expect(summary!.steps).toEqual([
|
|
22
|
-
{ kind: 'thought', label: '分析' },
|
|
23
|
-
{ kind: 'tool', label: 'bash' },
|
|
24
|
-
]);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('busy 状态在摘要前透传,idle 后清空缓冲', () => {
|
|
28
|
-
const agg = new TrajectoryAggregator();
|
|
29
|
-
const events: ServerEvent[] = [];
|
|
30
|
-
agg.onEvent({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 1, status: 'busy' }, (ev) => events.push(ev));
|
|
31
|
-
expect(events).toHaveLength(1);
|
|
32
|
-
expect(events[0].type).toBe('agent.status');
|
|
33
|
-
|
|
34
|
-
agg.onEvent({ type: 'agent.status', sessionId: 'cli:cli:local', ts: 2, status: 'idle' }, (ev) => events.push(ev));
|
|
35
|
-
expect(events).toHaveLength(2);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('其他事件(message.complete 等)原样透传', () => {
|
|
39
|
-
const agg = new TrajectoryAggregator();
|
|
40
|
-
const events: ServerEvent[] = [];
|
|
41
|
-
agg.onEvent({ type: 'message.complete', sessionId: 's', ts: 1, text: '结果' }, (ev) => events.push(ev));
|
|
42
|
-
expect(events).toEqual([{ type: 'message.complete', sessionId: 's', ts: 1, text: '结果' }]);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('formatTrajectorySummary(摘要卡片渲染,Task 2 接线复用)', () => {
|
|
47
|
-
it('按 kind 渲染图标 + 标签,含步数标题', () => {
|
|
48
|
-
const text = formatTrajectorySummary([
|
|
49
|
-
{ kind: 'thought', label: '分析' },
|
|
50
|
-
{ kind: 'tool', label: 'bash' },
|
|
51
|
-
{ kind: 'subagent', label: '子任务' },
|
|
52
|
-
]);
|
|
53
|
-
expect(text).toContain('🧠 分析');
|
|
54
|
-
expect(text).toContain('🛠️ bash');
|
|
55
|
-
expect(text).toContain('🤖 子任务');
|
|
56
|
-
expect(text).toContain('📋 轨迹(3 步)');
|
|
57
|
-
});
|
|
58
|
-
});
|