scripts/replay/core/engine.ts

import { loadInitiativeChannelIds, loadMessagesForReplay, loadRuntimeSettings, openReadOnlyDb, primeReplayHistory, resolveBotUserId } from "./db.ts"; import { createReplayLlmService } from "./llm.ts"; import type { ChannelMode, MessageRow, ReplayBaseArgs, ReplayEvent, ReplayEngineResult, ReplayScenarioDefinition, TurnSnapshot } from "./types.ts"; import { computeContextSince, stableNumber } from "./utils.ts";

export async function runReplayEngine< TArgs extends ReplayBaseArgs, TScenarioState, TDbState

( scenario: ReplayScenarioDefinition<TArgs, TScenarioState, TDbState>, args: TArgs ): Promise<ReplayEngineResult<TArgs, TScenarioState, TDbState>> { const contextSince = computeContextSince(args.since, args.historyLookbackHours); const db = openReadOnlyDb(args.dbPath);

let runtimeSettings: Record<string, unknown>; let messages: MessageRow[]; let replayMessages: MessageRow[]; let dbState: TDbState; try { runtimeSettings = loadRuntimeSettings(db); const loadedMessages = loadMessagesForReplay(db, { contextSince, since: args.since, until: args.until, channelId: args.channelId, maxTurns: args.maxTurns }); messages = loadedMessages.messages; replayMessages = loadedMessages.replayMessages; dbState = scenario.loadDbState({ db, args, contextSince, messages }); } finally { db.close(); }

const botUserId = resolveBotUserId(messages); const initiativeChannelIds = loadInitiativeChannelIds(runtimeSettings); const scenarioState = scenario.createScenarioState({ args, dbState, runtimeSettings, botUserId, initiativeChannelIds });

const llmService = createReplayLlmService(); const { historyByChannel, historyByMessageId } = primeReplayHistory( messages, args.since );

const timeline: ReplayEvent[] = []; const turnSnapshots: TurnSnapshot[] = []; const botName = String(runtimeSettings.botName || "clanky"); let syntheticBotCounter = 0; let processedTurns = 0;

for (const message of replayMessages) { const channelId = String(message.channel_id || ""); const createdAt = String(message.created_at || ""); const channelMode: ChannelMode = initiativeChannelIds.has(channelId) ? "initiative" : "non_initiative";

const history = historyByChannel.get(channelId) || [];
history.push(message);
historyByChannel.set(channelId, history);
historyByMessageId.set(String(message.message_id), message);
processedTurns += 1;

timeline.push({
  createdAt,
  channelId,
  role: "USER" as const,
  authorName: String(message.author_name || "user"),
  content: String(message.content || "")
});

const turnResult = await scenario.runTurn({
  args,
  scenarioState,
  runtimeSettings,
  mode: args.mode,
  message,
  channelMode,
  history,
  historyByMessageId,
  botUserId,
  llmService,
  turnIndex: processedTurns
});

const snapshot: TurnSnapshot = {
  index: processedTurns,
  messageId: String(message.message_id || ""),
  createdAt,
  channelId,
  channelMode,
  authorName: String(message.author_name || "user"),
  userContent: String(message.content || ""),
  addressed: Boolean(turnResult.addressed),
  attempted: Boolean(turnResult.attempted),
  decisionKind: turnResult.decision.kind,
  decisionReason: String(turnResult.decision.reason || ""),
  botContent: "",
  llmProvider: String(turnResult.decision.llmProvider || ""),
  llmModel: String(turnResult.decision.llmModel || ""),
  llmCostUsd: stableNumber(turnResult.decision.llmCostUsd, 0)
};
turnSnapshots.push(snapshot);

if (turnResult.decision.kind === "voice_intent_detected") {
  timeline.push({
    createdAt,
    channelId,
    role: "BOT_ACTION",
    authorName: botName,
    content: `[voice_intent:${turnResult.decision.voiceIntent || "detected"}]`
  });
  continue;
}

if (
  turnResult.decision.kind === "reply_skipped" ||
  turnResult.decision.kind === "no_action"
) {
  continue;
}

const botMessage: MessageRow = {
  message_id: `sim-bot-${++syntheticBotCounter}`,
  created_at: createdAt,
  guild_id: message.guild_id,
  channel_id: message.channel_id,
  author_id: botUserId,
  author_name: botName,
  is_bot: 1,
  content: String(turnResult.decision.content || ""),
  referenced_message_id: null
};
history.push(botMessage);
historyByChannel.set(channelId, history);
historyByMessageId.set(botMessage.message_id, botMessage);
snapshot.botContent = botMessage.content;
timeline.push({
  createdAt,
  channelId,
  role: "BOT",
  authorName: botName,
  content: botMessage.content
});

}

return { args, contextSince, runtimeSettings, botUserId, initiativeChannelIds, messages, replayMessages, processedTurns, timeline, turnSnapshots, llmService, scenarioState, dbState }; }