refactor #22

Merged
hzhang merged 33 commits from refactor into main 2026-04-10 07:49:57 +00:00
4 changed files with 41 additions and 21 deletions
Showing only changes of commit d9bb5c2e21 - Show all commits

View File

@@ -119,12 +119,12 @@
- [ ] 确保 callback 完成后的 closed channel 不会继续触发 handoff - [ ] 确保 callback 完成后的 closed channel 不会继续触发 handoff
#### A7.4 `plugin/hooks/message-received.ts` #### A7.4 `plugin/hooks/message-received.ts`
- [ ] 梳理 moderator bot 消息当前是否已被过滤,避免 moderator 自己再次触发讨论链路 - [x] 梳理 moderator bot 消息当前是否已被过滤,避免 moderator 自己再次触发讨论链路
- [ ] 对 closed discussion channel 的新消息增加统一处理入口 - [x] 对 closed discussion channel 的新消息增加统一处理入口
- [ ] 若 closed discussion channel 收到新消息: - [x] 若 closed discussion channel 收到新消息:
- [ ] 不再唤醒任何 Agent 正常讨论 - [x] 不再唤醒任何 Agent 正常讨论
- [ ] 由 moderator 回复“channel 已关闭,仅做留档使用” - [x] 由 moderator 回复“channel 已关闭,仅做留档使用”
- [ ] 避免 moderator 的 closed 提示消息反复触发自身处理 - [x] 避免 moderator 的 closed 提示消息反复触发自身处理
#### A7.5 `plugin/core/session-state.ts`(如需) #### A7.5 `plugin/core/session-state.ts`(如需)
- [ ] 检查现有 session 相关缓存是否适合扩展 discussion 状态 - [ ] 检查现有 session 相关缓存是否适合扩展 discussion 状态
@@ -167,23 +167,23 @@
- [ ] 明确 channel 已关闭,仅做留档使用 - [ ] 明确 channel 已关闭,仅做留档使用
### A11. `discuss-callback` 详细校验任务 ### A11. `discuss-callback` 详细校验任务
- [ ] 校验当前 channel 必须是 discussion channel - [x] 校验当前 channel 必须是 discussion channel
- [ ] 校验当前 discussion 状态必须是 `active` - [x] 校验当前 discussion 状态必须是 `active`
- [ ] 校验调用者必须是 initiator - [x] 校验调用者必须是 initiator
- [ ] 校验 `summaryPath` 非空 - [x] 校验 `summaryPath` 非空
- [ ] 校验 `summaryPath` 文件存在 - [x] 校验 `summaryPath` 文件存在
- [ ] 校验 `summaryPath` 路径在 initiator workspace 内 - [x] 校验 `summaryPath` 路径在 initiator workspace 内
- [ ] 校验 callback 未重复执行 - [x] 校验 callback 未重复执行
- [ ] callback 成功后写入 `completedAt` - [x] callback 成功后写入 `completedAt`
- [ ] callback 成功后记录 `summaryPath` - [x] callback 成功后记录 `summaryPath`
- [ ] callback 成功后切换 discussion 状态为 `completed` / `closed` - [x] callback 成功后切换 discussion 状态为 `completed` / `closed`
### A12. 关闭后的行为封口 ### A12. 关闭后的行为封口
- [ ] closed discussion channel 中所有旧 session 继续使用 no-reply 覆盖 - [x] closed discussion channel 中所有旧 session 继续使用 no-reply 覆盖
- [ ] closed discussion channel 中任何新消息都不再进入真实讨论 - [x] closed discussion channel 中任何新消息都不再进入真实讨论
- [ ] closed discussion channel 的任何新消息统一走 moderator 固定回复 - [x] closed discussion channel 的任何新消息统一走 moderator 固定回复
- [ ] 防止 closed channel 中 moderator 自己的回复再次触发回环 - [x] 防止 closed channel 中 moderator 自己的回复再次触发回环
- [ ] 明确 archived-only 的最终行为与边界 - [x] 明确 archived-only 的最终行为与边界
### A13. 测试与文档收尾 ### A13. 测试与文档收尾
#### A13.1 工具层测试 #### A13.1 工具层测试

View File

@@ -7,6 +7,7 @@ import { sendModeratorMessage } from "./moderator-discord.js";
type DiscussionServiceDeps = { type DiscussionServiceDeps = {
api: OpenClawPluginApi; api: OpenClawPluginApi;
moderatorBotToken?: string; moderatorBotToken?: string;
moderatorUserId?: string;
workspaceRoot?: string; workspaceRoot?: string;
forceNoReplyForSession: (sessionKey: string) => void; forceNoReplyForSession: (sessionKey: string) => void;
}; };
@@ -171,6 +172,7 @@ export function createDiscussionService(deps: DiscussionServiceDeps) {
async function maybeReplyClosedChannel(channelId: string, senderId?: string): Promise<boolean> { async function maybeReplyClosedChannel(channelId: string, senderId?: string): Promise<boolean> {
const metadata = getDiscussion(channelId); const metadata = getDiscussion(channelId);
if (!metadata || metadata.status !== "closed") return false; if (!metadata || metadata.status !== "closed") return false;
if (deps.moderatorUserId && senderId && senderId === deps.moderatorUserId) return true;
if (!deps.moderatorBotToken) return true; if (!deps.moderatorBotToken) return true;
await sendModeratorMessage(deps.moderatorBotToken, channelId, buildClosedMessage(), deps.api.logger); await sendModeratorMessage(deps.moderatorBotToken, channelId, buildClosedMessage(), deps.api.logger);
return true; return true;
@@ -179,6 +181,9 @@ export function createDiscussionService(deps: DiscussionServiceDeps) {
return { return {
initDiscussion, initDiscussion,
getDiscussion, getDiscussion,
isClosedDiscussion(channelId: string): boolean {
return isDiscussionClosed(channelId);
},
maybeSendIdleReminder, maybeSendIdleReminder,
maybeReplyClosedChannel, maybeReplyClosedChannel,
handleCallback, handleCallback,

View File

@@ -29,6 +29,9 @@ type BeforeModelResolveDeps = {
pruneDecisionMap: () => void; pruneDecisionMap: () => void;
shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean; shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean;
ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => Promise<void> | void; ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => Promise<void> | void;
discussionService?: {
isClosedDiscussion: (channelId: string) => boolean;
};
}; };
export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): void { export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): void {
@@ -47,6 +50,7 @@ export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): vo
pruneDecisionMap, pruneDecisionMap,
shouldDebugLog, shouldDebugLog,
ensureTurnOrder, ensureTurnOrder,
discussionService,
} = deps; } = deps;
api.on("before_model_resolve", async (event, ctx) => { api.on("before_model_resolve", async (event, ctx) => {
@@ -86,6 +90,15 @@ export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): vo
if (derived.channelId) { if (derived.channelId) {
sessionChannelId.set(key, derived.channelId); sessionChannelId.set(key, derived.channelId);
if (discussionService?.isClosedDiscussion(derived.channelId)) {
sessionAllowed.set(key, false);
api.logger.info(`dirigent: before_model_resolve forcing no-reply for closed discussion channel=${derived.channelId} session=${key}`);
return {
model: ctx.model,
provider: ctx.provider,
noReply: true,
};
}
} }
const resolvedAccountId = resolveAccountId(api, ctx.agentId || ""); const resolvedAccountId = resolveAccountId(api, ctx.agentId || "");
if (resolvedAccountId) { if (resolvedAccountId) {

View File

@@ -118,6 +118,7 @@ export default {
const discussionService = createDiscussionService({ const discussionService = createDiscussionService({
api, api,
moderatorBotToken: baseConfig.moderatorBotToken, moderatorBotToken: baseConfig.moderatorBotToken,
moderatorUserId: getModeratorUserId(baseConfig),
workspaceRoot: process.cwd(), workspaceRoot: process.cwd(),
forceNoReplyForSession: (sessionKey: string) => { forceNoReplyForSession: (sessionKey: string) => {
if (sessionKey) forceNoReplySessions.add(sessionKey); if (sessionKey) forceNoReplySessions.add(sessionKey);
@@ -163,6 +164,7 @@ export default {
pruneDecisionMap, pruneDecisionMap,
shouldDebugLog, shouldDebugLog,
ensureTurnOrder, ensureTurnOrder,
discussionService,
}); });
registerBeforePromptBuildHook({ registerBeforePromptBuildHook({