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>
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
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;
|
|
}
|