feat: rewrite plugin as v2 with globalThis-based turn management
Complete rewrite of the Dirigent plugin turn management system to work correctly with OpenClaw's VM-context-per-session architecture: - All turn state stored on globalThis (persists across VM context hot-reloads) - Hooks registered unconditionally on every api instance; event-level dedup (runId Set for agent_end, WeakSet for before_model_resolve) prevents double-processing - Gateway lifecycle events (gateway_start/stop) guarded once via globalThis flag - Shared initializingChannels lock prevents concurrent channel init across VM contexts in message_received and before_model_resolve - New ChannelStore and IdentityRegistry replace old policy/session-state modules - Added agent_end hook with tail-match polling for Discord delivery confirmation - Added web control page, padded-cell auto-scan, discussion tool support - Removed obsolete v1 modules: channel-resolver, channel-modes, discussion-service, session-state, turn-bootstrap, policy/store, rules, decision-input Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
59
plugin/core/padded-cell.ts
Normal file
59
plugin/core/padded-cell.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { IdentityRegistry } from "./identity-registry.js";
|
||||
|
||||
type EgoData = {
|
||||
columns?: string[];
|
||||
agentScope?: Record<string, Record<string, string>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Scan padded-cell's ego.json and upsert agent Discord IDs into the identity registry.
|
||||
* Only runs if ego.json contains the "discord-id" column — otherwise treated as absent.
|
||||
*
|
||||
* @returns number of entries upserted, or -1 if padded-cell is not detected.
|
||||
*/
|
||||
export function scanPaddedCell(
|
||||
registry: IdentityRegistry,
|
||||
openclawDir: string,
|
||||
logger: { info: (m: string) => void; warn: (m: string) => void },
|
||||
): number {
|
||||
const egoPath = path.join(openclawDir, "ego.json");
|
||||
|
||||
if (!fs.existsSync(egoPath)) {
|
||||
logger.info("dirigent: padded-cell ego.json not found — skipping auto-registration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let ego: EgoData;
|
||||
try {
|
||||
ego = JSON.parse(fs.readFileSync(egoPath, "utf8"));
|
||||
} catch (e) {
|
||||
logger.warn(`dirigent: failed to parse ego.json: ${String(e)}`);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!Array.isArray(ego.columns) || !ego.columns.includes("discord-id")) {
|
||||
logger.info('dirigent: ego.json does not have "discord-id" column — padded-cell not configured for Discord, skipping');
|
||||
return -1;
|
||||
}
|
||||
|
||||
const agentScope = ego.agentScope ?? {};
|
||||
let count = 0;
|
||||
|
||||
for (const [agentId, fields] of Object.entries(agentScope)) {
|
||||
const discordUserId = fields["discord-id"];
|
||||
if (!discordUserId || typeof discordUserId !== "string") continue;
|
||||
|
||||
const existing = registry.findByAgentId(agentId);
|
||||
registry.upsert({
|
||||
agentId,
|
||||
discordUserId,
|
||||
agentName: existing?.agentName ?? agentId,
|
||||
});
|
||||
count++;
|
||||
}
|
||||
|
||||
logger.info(`dirigent: padded-cell scan complete — upserted ${count} identity entries`);
|
||||
return count;
|
||||
}
|
||||
Reference in New Issue
Block a user