refactor(plugin): extract shared session state and decision pruning
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import type { Decision, DirigentConfig } from "./rules.js";
|
||||
import type { DirigentConfig } from "./rules.js";
|
||||
import { startModeratorPresence, stopModeratorPresence } from "./moderator-presence.js";
|
||||
import { registerMessageReceivedHook } from "./hooks/message-received.js";
|
||||
import { registerBeforeModelResolveHook } from "./hooks/before-model-resolve.js";
|
||||
@@ -18,27 +18,22 @@ import { ensureTurnOrder, recordChannelAccount } from "./core/turn-bootstrap.js"
|
||||
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";
|
||||
|
||||
type DecisionRecord = {
|
||||
decision: Decision;
|
||||
createdAt: number;
|
||||
needsRestore?: boolean;
|
||||
};
|
||||
import {
|
||||
DECISION_TTL_MS,
|
||||
pruneDecisionMap,
|
||||
sessionAccountId,
|
||||
sessionAllowed,
|
||||
sessionChannelId,
|
||||
sessionDecision,
|
||||
sessionInjected,
|
||||
sessionTurnHandled,
|
||||
} from "./core/session-state.js";
|
||||
|
||||
type DebugConfig = {
|
||||
enableDebugLogs?: boolean;
|
||||
debugLogChannelIds?: string[];
|
||||
};
|
||||
|
||||
const sessionDecision = new Map<string, DecisionRecord>();
|
||||
const sessionAllowed = new Map<string, boolean>(); // Track if session was allowed to speak (true) or forced no-reply (false)
|
||||
const sessionInjected = new Set<string>(); // Track which sessions have already injected the end marker
|
||||
const sessionChannelId = new Map<string, string>(); // Track sessionKey -> channelId mapping
|
||||
const sessionAccountId = new Map<string, string>(); // Track sessionKey -> accountId mapping
|
||||
const sessionTurnHandled = new Set<string>(); // Track sessions where turn was already advanced in before_message_write
|
||||
const MAX_SESSION_DECISIONS = 2000;
|
||||
const DECISION_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
function buildEndMarkerInstruction(endSymbols: string[], isGroupChat: boolean, schedulingIdentifier: string, waitIdentifier: string): string {
|
||||
const symbols = endSymbols.length > 0 ? endSymbols.join("") : "🔚";
|
||||
let instruction = `Your response MUST end with ${symbols}. Exception: gateway keywords (e.g. NO_REPLY, HEARTBEAT_OK) must NOT include ${symbols}.`;
|
||||
@@ -53,20 +48,6 @@ function buildSchedulingIdentifierInstruction(schedulingIdentifier: string): str
|
||||
return `\n\nScheduling identifier: "${schedulingIdentifier}". This identifier itself is meaningless — it carries no semantic content. When you receive a message containing <@YOUR_USER_ID> followed by the scheduling identifier, check recent chat history and decide whether you have something to say. If not, reply NO_REPLY.`;
|
||||
}
|
||||
|
||||
function pruneDecisionMap(now = Date.now()) {
|
||||
for (const [k, v] of sessionDecision.entries()) {
|
||||
if (now - v.createdAt > DECISION_TTL_MS) sessionDecision.delete(k);
|
||||
}
|
||||
|
||||
if (sessionDecision.size <= MAX_SESSION_DECISIONS) return;
|
||||
const keys = sessionDecision.keys();
|
||||
while (sessionDecision.size > MAX_SESSION_DECISIONS) {
|
||||
const k = keys.next();
|
||||
if (k.done) break;
|
||||
sessionDecision.delete(k.value);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
id: "dirigent",
|
||||
name: "Dirigent",
|
||||
|
||||
Reference in New Issue
Block a user