refactor #22
@@ -243,9 +243,9 @@
|
||||
- [ ] 评估是否需要增加 shuffle 默认配置项
|
||||
|
||||
#### B2.2 `plugin/rules.ts` / config 类型
|
||||
- [ ] 为 multi-message mode 相关配置补类型定义
|
||||
- [ ] 为 shuffle mode 相关 channel state / config 补类型定义
|
||||
- [ ] 确保运行时读取配置逻辑可访问新增字段
|
||||
- [x] 为 multi-message mode 相关配置补类型定义
|
||||
- [x] 为 shuffle mode 相关 channel state / config 补类型定义
|
||||
- [x] 确保运行时读取配置逻辑可访问新增字段
|
||||
|
||||
### B3. `plugin/core/` 新增 channel mode / shuffle state 模块
|
||||
- [x] 新增 channel mode state 模块(如 `plugin/core/channel-modes.ts`)
|
||||
@@ -305,14 +305,14 @@
|
||||
- [x] 命令帮助文本补充说明
|
||||
|
||||
### B8. `plugin/index.ts`
|
||||
- [ ] 注入 channel mode / shuffle state 模块依赖
|
||||
- [ ] 将新状态能力传给相关 hooks / turn-manager
|
||||
- [ ] 保持初始化关系清晰,避免 mode 逻辑散落
|
||||
- [x] 注入 channel mode / shuffle state 模块依赖
|
||||
- [x] 将新状态能力传给相关 hooks / turn-manager
|
||||
- [x] 保持初始化关系清晰,避免 mode 逻辑散落
|
||||
|
||||
### B9. moderator 消息模板
|
||||
- [ ] 定义 multi-message mode 下的 prompt marker 发送规则
|
||||
- [ ] 明确是否需要 start / end 的 moderator 确认消息
|
||||
- [ ] 定义退出 multi-message mode 后的 scheduling handoff 触发格式
|
||||
- [x] 定义 multi-message mode 下的 prompt marker 发送规则
|
||||
- [x] 明确是否需要 start / end 的 moderator 确认消息
|
||||
- [x] 定义退出 multi-message mode 后的 scheduling handoff 触发格式
|
||||
|
||||
### B10. 测试
|
||||
#### B10.1 Multi-Message Mode
|
||||
|
||||
@@ -30,6 +30,9 @@ Optional:
|
||||
- `channelPoliciesFile` (per-channel overrides in a standalone JSON file)
|
||||
- `schedulingIdentifier` (default `➡️`) — moderator handoff identifier
|
||||
- `enableDirigentPolicyTool` (default true)
|
||||
- `multiMessageStartMarker` (default `↗️`)
|
||||
- `multiMessageEndMarker` (default `↙️`)
|
||||
- `multiMessagePromptMarker` (default `⤵️`)
|
||||
|
||||
Unified optional tool:
|
||||
- `dirigent_tools`
|
||||
@@ -59,6 +62,15 @@ When the current speaker NO_REPLYs, the moderator bot sends: `<@NEXT_USER_ID>➡
|
||||
|
||||
This is a non-semantic scheduling message. The scheduling identifier (`➡️` by default) carries no meaning — it simply signals the next agent to check chat history and decide whether to speak.
|
||||
|
||||
## Multi-message mode / shuffle mode
|
||||
|
||||
- Human sends the configured start marker (default `↗️`) → channel enters multi-message mode.
|
||||
- While active, agents are forced to no-reply and the moderator sends only the configured prompt marker (default `⤵️`) after each additional human message.
|
||||
- Human sends the configured end marker (default `↙️`) → channel exits multi-message mode and normal scheduling resumes.
|
||||
- No separate moderator "entered/exited mode" confirmation message is sent; the markers themselves are the protocol.
|
||||
- The first moderator message after exit uses the normal scheduling handoff format: `<@NEXT_USER_ID>➡️`.
|
||||
- `/dirigent turn-shuffling`, `/dirigent turn-shuffling on`, and `/dirigent turn-shuffling off` control per-channel reshuffling between completed rounds.
|
||||
|
||||
## Slash command (Discord)
|
||||
|
||||
```
|
||||
@@ -66,6 +78,9 @@ This is a non-semantic scheduling message. The scheduling identifier (`➡️` b
|
||||
/dirigent turn-status
|
||||
/dirigent turn-advance
|
||||
/dirigent turn-reset
|
||||
/dirigent turn-shuffling
|
||||
/dirigent turn-shuffling on
|
||||
/dirigent turn-shuffling off
|
||||
```
|
||||
|
||||
Debug logging:
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
export type ChannelMode = "normal" | "multi-message";
|
||||
import type { ChannelRuntimeMode, ChannelRuntimeState } from "../rules.js";
|
||||
|
||||
export type ChannelModesState = {
|
||||
mode: ChannelMode;
|
||||
shuffling: boolean;
|
||||
lastShuffledAt?: number;
|
||||
};
|
||||
export type ChannelMode = ChannelRuntimeMode;
|
||||
export type ChannelModesState = ChannelRuntimeState;
|
||||
|
||||
const channelStates = new Map<string, ChannelModesState>();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import { resolvePolicy, type DirigentConfig } from "../rules.js";
|
||||
import { getTurnDebugInfo, onSpeakerDone, setWaitingForHuman } from "../turn-manager.js";
|
||||
import { isMultiMessageMode } from "../core/channel-modes.js";
|
||||
|
||||
type DebugConfig = {
|
||||
enableDebugLogs?: boolean;
|
||||
@@ -20,6 +19,7 @@ type BeforeMessageWriteDeps = {
|
||||
shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean;
|
||||
ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => Promise<void> | void;
|
||||
resolveDiscordUserId: (api: OpenClawPluginApi, accountId: string) => string | undefined;
|
||||
isMultiMessageMode: (channelId: string) => boolean;
|
||||
sendModeratorMessage: (
|
||||
botToken: string,
|
||||
channelId: string,
|
||||
@@ -45,6 +45,7 @@ export function registerBeforeMessageWriteHook(deps: BeforeMessageWriteDeps): vo
|
||||
shouldDebugLog,
|
||||
ensureTurnOrder,
|
||||
resolveDiscordUserId,
|
||||
isMultiMessageMode,
|
||||
sendModeratorMessage,
|
||||
discussionService,
|
||||
} = deps;
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import { evaluateDecision, type Decision, type DirigentConfig } from "../rules.js";
|
||||
import { checkTurn } from "../turn-manager.js";
|
||||
import { deriveDecisionInputFromPrompt } from "../decision-input.js";
|
||||
import { isMultiMessageMode } from "../core/channel-modes.js";
|
||||
|
||||
type DebugConfig = {
|
||||
enableDebugLogs?: boolean;
|
||||
@@ -30,6 +29,7 @@ type BeforeModelResolveDeps = {
|
||||
pruneDecisionMap: () => void;
|
||||
shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean;
|
||||
ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => Promise<void> | void;
|
||||
isMultiMessageMode: (channelId: string) => boolean;
|
||||
discussionService?: {
|
||||
isClosedDiscussion: (channelId: string) => boolean;
|
||||
};
|
||||
@@ -51,6 +51,7 @@ export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): vo
|
||||
pruneDecisionMap,
|
||||
shouldDebugLog,
|
||||
ensureTurnOrder,
|
||||
isMultiMessageMode,
|
||||
discussionService,
|
||||
} = deps;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import { onNewMessage, setMentionOverride, getTurnDebugInfo } from "../turn-manager.js";
|
||||
import { extractDiscordChannelId } from "../channel-resolver.js";
|
||||
import type { DirigentConfig } from "../rules.js";
|
||||
import { enterMultiMessageMode, exitMultiMessageMode, isMultiMessageMode } from "../core/channel-modes.js";
|
||||
|
||||
type DebugConfig = {
|
||||
enableDebugLogs?: boolean;
|
||||
@@ -19,6 +18,8 @@ type MessageReceivedDeps = {
|
||||
recordChannelAccount: (api: OpenClawPluginApi, channelId: string, accountId: string) => boolean;
|
||||
extractMentionedUserIds: (content: string) => string[];
|
||||
buildUserIdToAccountIdMap: (api: OpenClawPluginApi) => Map<string, string>;
|
||||
enterMultiMessageMode: (channelId: string) => void;
|
||||
exitMultiMessageMode: (channelId: string) => void;
|
||||
discussionService?: {
|
||||
maybeReplyClosedChannel: (channelId: string, senderId?: string) => Promise<boolean>;
|
||||
};
|
||||
@@ -35,6 +36,8 @@ export function registerMessageReceivedHook(deps: MessageReceivedDeps): void {
|
||||
recordChannelAccount,
|
||||
extractMentionedUserIds,
|
||||
buildUserIdToAccountIdMap,
|
||||
enterMultiMessageMode,
|
||||
exitMultiMessageMode,
|
||||
discussionService,
|
||||
} = deps;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { debugCtxSummary, pickDefined, shouldDebugLog } from "./core/utils.js";
|
||||
import { resolveDiscordUserId, sendModeratorMessage } from "./core/moderator-discord.js";
|
||||
import { startNoReplyApi, stopNoReplyApi } from "./core/no-reply-process.js";
|
||||
import { createDiscussionService } from "./core/discussion-service.js";
|
||||
import { enterMultiMessageMode, exitMultiMessageMode, isMultiMessageMode } from "./core/channel-modes.js";
|
||||
import {
|
||||
DECISION_TTL_MS,
|
||||
forceNoReplySessions,
|
||||
@@ -49,6 +50,9 @@ function normalizePluginConfig(api: OpenClawPluginApi): NormalizedDirigentConfig
|
||||
noReplyPort: 8787,
|
||||
schedulingIdentifier: "➡️",
|
||||
waitIdentifier: "👤",
|
||||
multiMessageStartMarker: "↗️",
|
||||
multiMessageEndMarker: "↙️",
|
||||
multiMessagePromptMarker: "⤵️",
|
||||
...(api.pluginConfig || {}),
|
||||
} as NormalizedDirigentConfig;
|
||||
}
|
||||
@@ -146,6 +150,8 @@ export default {
|
||||
recordChannelAccount,
|
||||
extractMentionedUserIds,
|
||||
buildUserIdToAccountIdMap,
|
||||
enterMultiMessageMode,
|
||||
exitMultiMessageMode,
|
||||
discussionService,
|
||||
});
|
||||
|
||||
@@ -164,6 +170,7 @@ export default {
|
||||
pruneDecisionMap,
|
||||
shouldDebugLog,
|
||||
ensureTurnOrder,
|
||||
isMultiMessageMode,
|
||||
discussionService,
|
||||
});
|
||||
|
||||
@@ -203,6 +210,7 @@ export default {
|
||||
shouldDebugLog,
|
||||
ensureTurnOrder,
|
||||
resolveDiscordUserId,
|
||||
isMultiMessageMode,
|
||||
sendModeratorMessage,
|
||||
discussionService,
|
||||
});
|
||||
|
||||
@@ -12,6 +12,12 @@ export type DirigentConfig = {
|
||||
schedulingIdentifier?: string;
|
||||
/** Wait identifier: agent ends with this when waiting for a human reply (default: 👤) */
|
||||
waitIdentifier?: string;
|
||||
/** Human-visible marker that enters multi-message mode for a channel (default: ↗️) */
|
||||
multiMessageStartMarker?: string;
|
||||
/** Human-visible marker that exits multi-message mode for a channel (default: ↙️) */
|
||||
multiMessageEndMarker?: string;
|
||||
/** Moderator marker sent after each human message while multi-message mode is active (default: ⤵️) */
|
||||
multiMessagePromptMarker?: string;
|
||||
noReplyProvider: string;
|
||||
noReplyModel: string;
|
||||
noReplyPort?: number;
|
||||
@@ -19,6 +25,14 @@ export type DirigentConfig = {
|
||||
moderatorBotToken?: string;
|
||||
};
|
||||
|
||||
export type ChannelRuntimeMode = "normal" | "multi-message";
|
||||
|
||||
export type ChannelRuntimeState = {
|
||||
mode: ChannelRuntimeMode;
|
||||
shuffling: boolean;
|
||||
lastShuffledAt?: number;
|
||||
};
|
||||
|
||||
export type ChannelPolicy = {
|
||||
listMode?: "human-list" | "agent-list";
|
||||
humanList?: string[];
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* - If sender IS in turn order → current = next after sender
|
||||
*/
|
||||
|
||||
import { isMultiMessageMode, exitMultiMessageMode } from "./core/channel-modes.js";
|
||||
import { getChannelShuffling, isMultiMessageMode, markLastShuffled } from "./core/channel-modes.js";
|
||||
|
||||
export type ChannelTurnState = {
|
||||
/** Ordered accountIds for this channel (auto-populated, shuffled) */
|
||||
@@ -379,6 +379,7 @@ export function onSpeakerDone(channelId: string, accountId: string, wasNoReply:
|
||||
const newOrder = reshuffleTurnOrder(channelId, state.turnOrder, prevSpeaker);
|
||||
if (newOrder !== state.turnOrder) {
|
||||
state.turnOrder = newOrder;
|
||||
markLastShuffled(channelId);
|
||||
console.log(`[dirigent][turn-debug] reshuffled turn order for channel=${channelId} newOrder=${JSON.stringify(newOrder)}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user