refactor #22
@@ -133,16 +133,16 @@
|
||||
- [x] 确保 session 生命周期结束后相关缓存可清理
|
||||
|
||||
### A8. `plugin/core/identity.ts` / `plugin/core/channel-members.ts` / `plugin/core/turn-bootstrap.ts`
|
||||
- [. ] 梳理 initiator identity 的可获取路径
|
||||
- [. ] 确认 callback 时如何稳定识别 initiator account/session
|
||||
- [. ] 确认 discussion channel 创建后 turn order 是否需立即 bootstrap
|
||||
- [. ] 确认 discussion participant 的成员集合获取方式是否可直接复用现有逻辑
|
||||
- [x] 梳理 initiator identity 的可获取路径
|
||||
- [x] 确认 callback 时如何稳定识别 initiator account/session
|
||||
- [x] 确认 discussion channel 创建后 turn order 是否需立即 bootstrap
|
||||
- [ ] 确认 discussion participant 的成员集合获取方式是否可直接复用现有逻辑
|
||||
|
||||
### A9. `plugin/index.ts`
|
||||
- [. ] 注入新增 discussion metadata/service 模块依赖
|
||||
- [. ] 将 discussion service 传入工具注册逻辑
|
||||
- [. ] 将 discussion 相关辅助能力传入需要的 hooks
|
||||
- [. ] 保持插件初始化结构清晰,避免在 `index.ts` 中堆业务细节
|
||||
- [x] 注入新增 discussion metadata/service 模块依赖
|
||||
- [x] 将 discussion service 传入工具注册逻辑
|
||||
- [x] 将 discussion 相关辅助能力传入需要的 hooks
|
||||
- [x] 保持插件初始化结构清晰,避免在 `index.ts` 中堆业务细节
|
||||
|
||||
### A13.2 metadata / service 测试
|
||||
- [x] 测试 discussion metadata 创建成功
|
||||
@@ -225,10 +225,10 @@
|
||||
|
||||
### A13. 测试与文档收尾
|
||||
#### A13.1 工具层测试
|
||||
- [. ] 测试普通 `discord_channel_create` 不带新参数时行为不变
|
||||
- [. ] 测试 `discord_channel_create` 带 `callbackChannelId` 但缺 `discussGuide` 时失败
|
||||
- [. ] 测试 discussion 模式 channel 创建成功
|
||||
- [. ] 测试 `discuss-callback` 注册成功并可调用
|
||||
- [x] 测试普通 `discord_channel_create` 不带新参数时行为不变
|
||||
- [x] 测试 `discord_channel_create` 带 `callbackChannelId` 但缺 `discussGuide` 时失败
|
||||
- [x] 测试 discussion 模式 channel 创建成功
|
||||
- [x] 测试 `discuss-callback` 注册成功并可调用
|
||||
|
||||
#### A13.2 metadata / service 测试
|
||||
- [x] 测试 discussion metadata 创建成功
|
||||
|
||||
208
test/register-tools.test.ts
Normal file
208
test/register-tools.test.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { registerDirigentTools } from '../plugin/tools/register-tools.ts';
|
||||
|
||||
type RegisteredTool = {
|
||||
name: string;
|
||||
handler: (params: Record<string, unknown>, ctx?: Record<string, unknown>) => Promise<any>;
|
||||
};
|
||||
|
||||
function makeApi() {
|
||||
const tools = new Map<string, RegisteredTool>();
|
||||
return {
|
||||
config: {
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: {
|
||||
bot: { token: 'discord-bot-token' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
info: (_msg: string) => {},
|
||||
warn: (_msg: string) => {},
|
||||
},
|
||||
registerTool(def: RegisteredTool) {
|
||||
tools.set(def.name, def);
|
||||
},
|
||||
tools,
|
||||
};
|
||||
}
|
||||
|
||||
function pickDefined(obj: Record<string, unknown>) {
|
||||
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
|
||||
}
|
||||
|
||||
test('plain private channel create works unchanged without discussion params', async () => {
|
||||
const api = makeApi();
|
||||
let initDiscussionCalls = 0;
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (_url: string | URL | Request, _init?: RequestInit) => {
|
||||
return new Response(JSON.stringify({ id: 'created-channel-1', name: 'plain-room' }), { status: 200 });
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
registerDirigentTools({
|
||||
api: api as any,
|
||||
baseConfig: {},
|
||||
pickDefined,
|
||||
discussionService: {
|
||||
async initDiscussion() {
|
||||
initDiscussionCalls += 1;
|
||||
return {};
|
||||
},
|
||||
async handleCallback() {
|
||||
return { ok: true };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tool = api.tools.get('dirigent_discord_control');
|
||||
assert.ok(tool);
|
||||
|
||||
const result = await tool!.handler({
|
||||
action: 'channel-private-create',
|
||||
guildId: 'guild-1',
|
||||
name: 'plain-room',
|
||||
allowedUserIds: ['user-1'],
|
||||
}, {
|
||||
agentId: 'agent-a',
|
||||
sessionKey: 'session-a',
|
||||
});
|
||||
|
||||
assert.equal(result.isError, undefined);
|
||||
assert.match(result.content[0].text, /"discussionMode": false/);
|
||||
assert.equal(initDiscussionCalls, 0);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('private channel create rejects callbackChannelId without discussGuide', async () => {
|
||||
const api = makeApi();
|
||||
|
||||
registerDirigentTools({
|
||||
api: api as any,
|
||||
baseConfig: {},
|
||||
pickDefined,
|
||||
discussionService: {
|
||||
async initDiscussion() {
|
||||
return {};
|
||||
},
|
||||
async handleCallback() {
|
||||
return { ok: true };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tool = api.tools.get('dirigent_discord_control');
|
||||
assert.ok(tool);
|
||||
|
||||
const result = await tool!.handler({
|
||||
action: 'channel-private-create',
|
||||
guildId: 'guild-1',
|
||||
name: 'discussion-room',
|
||||
callbackChannelId: 'origin-1',
|
||||
}, {
|
||||
agentId: 'agent-a',
|
||||
sessionKey: 'session-a',
|
||||
});
|
||||
|
||||
assert.equal(result.isError, true);
|
||||
assert.equal(result.content[0].text, 'discussGuide is required when callbackChannelId is provided');
|
||||
});
|
||||
|
||||
test('discussion-mode channel create initializes discussion metadata', async () => {
|
||||
const api = makeApi();
|
||||
const initCalls: Array<Record<string, unknown>> = [];
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (_url: string | URL | Request, _init?: RequestInit) => {
|
||||
return new Response(JSON.stringify({ id: 'discussion-channel-1', name: 'discussion-room' }), { status: 200 });
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
registerDirigentTools({
|
||||
api: api as any,
|
||||
baseConfig: {},
|
||||
pickDefined,
|
||||
discussionService: {
|
||||
async initDiscussion(params) {
|
||||
initCalls.push(params as Record<string, unknown>);
|
||||
return {};
|
||||
},
|
||||
async handleCallback() {
|
||||
return { ok: true };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tool = api.tools.get('dirigent_discord_control');
|
||||
assert.ok(tool);
|
||||
|
||||
const result = await tool!.handler({
|
||||
action: 'channel-private-create',
|
||||
guildId: 'guild-1',
|
||||
name: 'discussion-room',
|
||||
callbackChannelId: 'origin-1',
|
||||
discussGuide: 'Decide the callback contract.',
|
||||
}, {
|
||||
agentId: 'agent-a',
|
||||
sessionKey: 'session-a',
|
||||
});
|
||||
|
||||
assert.equal(result.isError, undefined);
|
||||
assert.match(result.content[0].text, /"discussionMode": true/);
|
||||
assert.equal(initCalls.length, 1);
|
||||
assert.deepEqual(initCalls[0], {
|
||||
discussionChannelId: 'discussion-channel-1',
|
||||
originChannelId: 'origin-1',
|
||||
initiatorAgentId: 'agent-a',
|
||||
initiatorSessionId: 'session-a',
|
||||
discussGuide: 'Decide the callback contract.',
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('discuss-callback registers and forwards channel/session/agent context', async () => {
|
||||
const api = makeApi();
|
||||
const callbackCalls: Array<Record<string, unknown>> = [];
|
||||
|
||||
registerDirigentTools({
|
||||
api: api as any,
|
||||
baseConfig: {},
|
||||
pickDefined,
|
||||
discussionService: {
|
||||
async initDiscussion() {
|
||||
return {};
|
||||
},
|
||||
async handleCallback(params) {
|
||||
callbackCalls.push(params as Record<string, unknown>);
|
||||
return { ok: true, summaryPath: '/workspace/summary.md' };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tool = api.tools.get('discuss-callback');
|
||||
assert.ok(tool);
|
||||
|
||||
const result = await tool!.handler({ summaryPath: 'plans/summary.md' }, {
|
||||
channelId: 'discussion-1',
|
||||
agentId: 'agent-a',
|
||||
sessionKey: 'session-a',
|
||||
});
|
||||
|
||||
assert.equal(result.isError, undefined);
|
||||
assert.deepEqual(callbackCalls, [{
|
||||
channelId: 'discussion-1',
|
||||
summaryPath: 'plans/summary.md',
|
||||
callerAgentId: 'agent-a',
|
||||
callerSessionKey: 'session-a',
|
||||
}]);
|
||||
assert.match(result.content[0].text, /"summaryPath": "\/workspace\/summary.md"/);
|
||||
});
|
||||
Reference in New Issue
Block a user