"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DialogStore = exports.MainDialog = exports.SideDialog = exports.Dialog = exports.DialogID = exports.InvalidReminderIndexError = void 0;
exports.scheduleGlobalDialogMutexCleanupForRoot = scheduleGlobalDialogMutexCleanupForRoot;
exports.buildSideDialogAskerStack = buildSideDialogAskerStack;
exports.buildSideDialogAssignmentPromptMeta = buildSideDialogAssignmentPromptMeta;
const id_1 = require("@longrun-ai/kernel/utils/id");
const time_1 = require("@longrun-ai/kernel/utils/time");
const util_1 = require("util");
const evt_registry_1 = require("./evt-registry");
const log_1 = require("./log");
const async_fifo_mutex_1 = require("./runtime/async-fifo-mutex");
const driver_messages_1 = require("./runtime/driver-messages");
const inter_dialog_format_1 = require("./runtime/inter-dialog-format");
const work_language_1 = require("./runtime/work-language");
const shared_reminders_1 = require("./shared-reminders");
const tool_1 = require("./tool");
const id_2 = require("./utils/id");
async function queueRootDialogDrive(rootId, meta) {
    const { globalDialogRegistry } = await import('./dialog-global-registry.js');
    globalDialogRegistry.queueRootDrive(rootId, meta);
}
class InvalidReminderIndexError extends Error {
    index;
    total;
    constructor(index, total) {
        super(`Reminder target index ${index} is stale; current reminder count is ${total}`);
        this.name = 'InvalidReminderIndexError';
        this.index = index;
        this.total = total;
    }
}
exports.InvalidReminderIndexError = InvalidReminderIndexError;
class DialogID {
    selfId;
    rootId;
    constructor(selfId, rootId) {
        this.selfId = selfId;
        this.rootId = rootId || selfId;
        if (typeof selfId !== 'string')
            throw new Error(`Wrong type [${typeof selfId}] passed as dlg id: ${(0, util_1.inspect)(selfId)}`);
        if (rootId && typeof rootId !== 'string')
            throw new Error(`Wrong type [${typeof rootId}] passed as dlg id: ${(0, util_1.inspect)(rootId)}`);
    }
    key() {
        return this.valueOf();
    }
    valueOf() {
        if (!this.rootId || this.rootId === this.selfId) {
            return this.selfId;
        }
        return this.rootId + '#' + this.selfId;
    }
    /**
     * Pretty print representation for debugging and display
     */
    toString() {
        return this.valueOf();
    }
    /**
     * Check equality with another DialogID
     */
    equals(other) {
        if (other === null || other === undefined) {
            return false;
        }
        return this.selfId === other.selfId && this.rootId === other.rootId;
    }
    /**
     * Generate hash code for Map/Set usage
     */
    hashCode() {
        return this.valueOf();
    }
}
exports.DialogID = DialogID;
const globalDialogMutexes = new Map();
const pendingGlobalDialogMutexCleanupRootIds = new Set();
function getGlobalDialogMutex(dialogId) {
    const key = dialogId.key();
    const existing = globalDialogMutexes.get(key);
    if (existing)
        return existing;
    const created = new async_fifo_mutex_1.AsyncFifoMutex();
    globalDialogMutexes.set(key, created);
    return created;
}
function clearIdleGlobalDialogMutexesForRoot(rootId) {
    const sideDialogPrefix = `${rootId}#`;
    let hasRemainingMutex = false;
    for (const [key, mutex] of globalDialogMutexes.entries()) {
        if (key !== rootId && !key.startsWith(sideDialogPrefix)) {
            continue;
        }
        if (!mutex.isLocked()) {
            globalDialogMutexes.delete(key);
        }
        else {
            hasRemainingMutex = true;
        }
    }
    return hasRemainingMutex;
}
function scheduleGlobalDialogMutexCleanupForRoot(rootId) {
    pendingGlobalDialogMutexCleanupRootIds.add(rootId);
    if (!clearIdleGlobalDialogMutexesForRoot(rootId)) {
        pendingGlobalDialogMutexCleanupRootIds.delete(rootId);
    }
}
function cleanupPendingGlobalDialogMutexesForRoot(rootId) {
    if (!pendingGlobalDialogMutexCleanupRootIds.has(rootId)) {
        return;
    }
    if (!clearIdleGlobalDialogMutexesForRoot(rootId)) {
        pendingGlobalDialogMutexCleanupRootIds.delete(rootId);
    }
}
function compareVisibleReminderTargetOrder(a, b) {
    return (0, tool_1.compareReminderDisplayOrder)(a.reminder, b.reminder);
}
function buildSideDialogAskerStack(args) {
    const expectedReplyCallName = args.assignment.callName === 'tellask' ? 'replyTellask' : 'replyTellaskSessionless';
    return {
        askerStack: [
            {
                kind: 'asker_dialog_stack_frame',
                askerDialogId: args.askerDialogId,
                assignmentFromAsker: args.assignment,
                tellaskReplyObligation: {
                    expectedReplyCallName,
                    targetDialogId: args.assignment.askerDialogId,
                    targetCallId: args.assignment.callId,
                    tellaskContent: args.assignment.tellaskContent,
                },
            },
        ],
    };
}
function getSideDialogAskerStackTop(askerStack) {
    const top = askerStack.askerStack[askerStack.askerStack.length - 1];
    if (!top) {
        throw new Error('SideDialog askerStack invariant violation: empty reply obligation stack');
    }
    return top;
}
function getSideDialogAskerStackCurrentAssignment(askerStack) {
    for (let index = askerStack.askerStack.length - 1; index >= 0; index -= 1) {
        const frame = askerStack.askerStack[index];
        if (frame?.assignmentFromAsker !== undefined) {
            return frame.assignmentFromAsker;
        }
    }
    throw new Error('SideDialog askerStack invariant violation: missing assignment frame');
}
function buildSideDialogAssignmentPromptMeta(sideDialog) {
    const assignment = sideDialog.assignmentFromAsker;
    switch (assignment.callName) {
        case 'tellask':
            return {
                tellaskReplyDirective: {
                    expectedReplyCallName: 'replyTellask',
                    targetDialogId: assignment.askerDialogId,
                    targetCallId: assignment.callId,
                    tellaskContent: assignment.tellaskContent,
                },
                calleeDialogReplyTarget: {
                    callerDialogId: assignment.askerDialogId,
                    callType: 'B',
                    callId: assignment.callId,
                    callSiteCourse: assignment.callSiteCourse,
                    callSiteGenseq: assignment.callSiteGenseq,
                },
            };
        case 'tellaskSessionless':
        case 'freshBootsReasoning':
            return {
                tellaskReplyDirective: {
                    expectedReplyCallName: 'replyTellaskSessionless',
                    targetDialogId: assignment.askerDialogId,
                    targetCallId: assignment.callId,
                    tellaskContent: assignment.tellaskContent,
                },
                calleeDialogReplyTarget: {
                    callerDialogId: assignment.askerDialogId,
                    callType: 'C',
                    callId: assignment.callId,
                    callSiteCourse: assignment.callSiteCourse,
                    callSiteGenseq: assignment.callSiteGenseq,
                },
            };
    }
}
/**
 * Abstract base class for all dialog types.
 * Contains common properties and methods shared between MainDialog and SideDialog.
 */
class Dialog {
    dlgStore;
    // relative path to a specific rtws (runtime workspace) file,
    // used as mission/plan/progress doc of a course of a dialog
    taskDocPath; // Taskdoc path is immutable for the dialog lifecycle
    id;
    agentId; // team member id
    reminders;
    msgs;
    // Persistence state
    _currentCourse = 1;
    _remindersVer = 0;
    _activeGenSeq;
    _activeGenCourse;
    _createdAt;
    _updatedAt;
    _uiLanguage;
    _lastUserLanguageCode;
    _lastContextHealth;
    _lastContextHealthGenseq;
    // Prompts queued for future drives (set by startNewCourse or deferred-resume flows).
    _queuedPrompts = [];
    _driveIntents = [];
    _activeRunControlSpec;
    _newCourseHook;
    _driveIntentMode = 'legacy';
    // Course prefix messages injected into LLM context on every course.
    // This is an in-process cache only (not persisted), intended for small, stable “felt-sense” context
    // like Agent Priming transcripts.
    _coursePrefixMsgs = [];
    _courseRuntimeNoticeMsgs = [];
    // Track whether the current course's initial events (user_text, generating_start)
    // have been fully processed. Used to ensure sideDialog_final_response_evt arrives
    // only after parent events are emitted.
    _generationStarted = false;
    // Track the generation sequence when _generationStarted was set
    // Used to ensure proper ordering when multiple generations occur
    _generationStartedGenseq = 0;
    // Pending sideDialog IDs (for active callee tracking)
    _activeCalleeDialogIds = [];
    // Phase 11: Suspension state for Type A sideDialog mechanism
    // Tracks whether this dialog is in normal state, suspended, or resuming from suspension
    _suspensionState = 'active';
    // Diligence Push (diligence auto-continue) budget counter (main-dialog state).
    // Persisted via latest.yaml so restarts and UI navigation can restore correct remaining budget.
    diligencePushRemainingBudget = 0;
    // Diligence Push disable switch (persisted via latest.yaml; default = false).
    disableDiligencePush = false;
    _mutex;
    // Current callId for tellask call correlation
    // - Set when tellask-special call results are finalized
    // - Retrieved during inline call-result emission (for receiveTellaskCallResult callId parameter)
    // - Enables frontend to attach result INLINE to the calling section
    // - NOT used for Side Dialog-response bubbles (which use calleeDialogId instead)
    _currentCallId = null;
    constructor(dlgStore, taskDocPath, id, agentId, initialState) {
        // Validate required parameters
        if (!taskDocPath || taskDocPath.trim() === '') {
            throw new Error('Taskdoc path is required for creating a dialog');
        }
        this.dlgStore = dlgStore;
        this.taskDocPath = taskDocPath.trim();
        if (id === undefined) {
            const generatedId = (0, id_2.generateDialogID)();
            id = new DialogID(generatedId);
        }
        this.id = id;
        this._mutex = getGlobalDialogMutex(this.id);
        this.agentId = agentId;
        this.reminders = initialState?.reminders || [];
        this.msgs = initialState?.messages || [];
        // Initialize persistence state
        const now = (0, time_1.formatUnifiedTimestamp)(new Date());
        this._createdAt = initialState?.createdAt || now;
        this._updatedAt = initialState?.updatedAt || now;
        this._currentCourse = initialState?.currentCourse || 1;
        this._uiLanguage = (0, work_language_1.getWorkLanguage)();
        this._lastUserLanguageCode = (0, work_language_1.getWorkLanguage)();
        this._lastContextHealth = initialState?.contextHealth;
        this._lastContextHealthGenseq = undefined;
        if (initialState?.pendingRuntimePrompt) {
            this.setPendingRuntimePrompt(initialState.pendingRuntimePrompt);
        }
        this.resetCourseLanguageNotice();
    }
    setLastContextHealth(snapshot) {
        this._lastContextHealth = snapshot;
        // Track the generation sequence that produced this snapshot, so reminder owners
        // can distinguish pre-generation updates (previous snapshot) from post-generation
        // updates (current snapshot) without relying on heuristics.
        this._lastContextHealthGenseq = this._activeGenSeq;
    }
    getLastContextHealth() {
        return this._lastContextHealth;
    }
    getLastContextHealthGenseq() {
        return this._lastContextHealthGenseq;
    }
    get remindersVer() {
        return this._remindersVer;
    }
    touchReminders() {
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
        this._remindersVer++;
    }
    get askerDialog() {
        return undefined;
    }
    getUiLanguage() {
        return this._uiLanguage;
    }
    setUiLanguage(language) {
        this._uiLanguage = language;
    }
    getLastUserLanguageCode() {
        return this._lastUserLanguageCode;
    }
    setLastUserLanguageCode(language) {
        this._lastUserLanguageCode = language;
    }
    buildCourseRuntimeNotice(content) {
        return {
            type: 'environment_msg',
            role: 'user',
            content,
        };
    }
    resetCourseLanguageNotice() {
        this._courseRuntimeNoticeMsgs = [
            this.buildCourseRuntimeNotice((0, driver_messages_1.formatCurrentUserLanguagePreference)((0, work_language_1.getWorkLanguage)(), this._lastUserLanguageCode)),
        ];
    }
    appendCourseLanguageChangedNotice(previousLanguage, nextLanguage) {
        if (previousLanguage === nextLanguage) {
            return;
        }
        this._courseRuntimeNoticeMsgs.push(this.buildCourseRuntimeNotice((0, driver_messages_1.formatUserLanguagePreferenceChangedNotice)((0, work_language_1.getWorkLanguage)(), previousLanguage, nextLanguage)));
    }
    /**
     * Get the current callId for tellask call correlation
     *
     * Call Types:
     * - tellask-special function call: callId is set when call results are finalized and used for inline result correlation
     * - SideDialog response bubbles: use calleeDialogId instead of callId
     *
     * @returns The current callId for call correlation, or null if no active call
     */
    getCurrentCallId() {
        return this._currentCallId;
    }
    /**
     * Set the current callId for tellask-special inline result correlation
     *
     * @param callId - The function-call correlation ID
     */
    setCurrentCallId(callId) {
        this._currentCallId = callId;
    }
    /**
     * Clear the current callId (called after tool response is sent)
     */
    clearCurrentCallId() {
        this._currentCallId = null;
    }
    /**
     * Acquire the dialog mutex. Returns a release callback.
     * FIFO queue ensures fairness when multiple callers wait.
     */
    async acquire() {
        const release = await this._mutex.acquire();
        return () => {
            release();
            cleanupPendingGlobalDialogMutexesForRoot(this.id.rootId);
        };
    }
    /**
     * Run an async function while holding the dialog mutex.
     * Prefer this over manually acquiring/releasing to avoid leaks on exceptions.
     */
    async withLock(fn) {
        const release = await this.acquire();
        try {
            return await fn();
        }
        finally {
            release();
        }
    }
    /**
     * Check if the dialog mutex is currently locked.
     */
    isLocked() {
        return this._mutex.isLocked();
    }
    /**
     * Check if dialog has pending Q4H questions.
     * Reads the dialog latest user-wait state; q4h.yaml remains the detailed question payload store.
     */
    async hasPendingQ4H() {
        try {
            const latest = await this.dlgStore.loadDialogLatest(this.id, this.status);
            return latest?.userWait?.kind === 'awaiting_user_answer';
        }
        catch (err) {
            log_1.log.error('Failed to load Q4H state for pending check', err, {
                dialogId: this.id.selfId,
            });
            throw err;
        }
    }
    /**
     * Check if dialog has active callee dispatches.
     */
    async hasActiveCalleeDispatches() {
        try {
            const activeCallees = await this.dlgStore.loadActiveCallees(this.id, this.status);
            return activeCallees.batches.some((batch) => batch.callees.some((callee) => callee.status === 'pending'));
        }
        catch (err) {
            log_1.log.warn('Failed to load active callees for dispatch check', undefined, {
                dialogId: this.id.selfId,
                error: err,
            });
            return true;
        }
    }
    /**
     * Check if dialog can be driven.
     *
     * Active callee dispatches are background callee work: they are observable, but they must not
     * block the caller dialog from continuing when some other concrete drive source exists.
     */
    async canDrive() {
        const suspension = await this.getSuspensionStatus();
        return suspension.canDrive;
    }
    /**
     * Get suspension status for logging/debugging.
     */
    async getSuspensionStatus() {
        const hasQ4H = await this.hasPendingQ4H();
        const hasBackgroundCalleeDialogs = await this.hasActiveCalleeDispatches();
        return {
            q4h: hasQ4H,
            backgroundCalleeDialogs: hasBackgroundCalleeDialogs,
            canDrive: !hasQ4H,
        };
    }
    get activeCalleeDialogIds() {
        return this._activeCalleeDialogIds;
    }
    addActiveCalleeDialogs(ids) {
        this._activeCalleeDialogIds.push(...ids);
    }
    clearActiveCalleeDialogs() {
        this._activeCalleeDialogIds = [];
    }
    /**
     * Load active callee dispatches from persistence into memory.
     */
    async loadActiveCalleeDispatchesFromPersistence() {
        try {
            const activeCalleeDispatches = await this.dlgStore.loadActiveCalleeDispatches(this.id, this.status);
            this.clearActiveCalleeDialogs();
            this.addActiveCalleeDialogs(activeCalleeDispatches.map((record) => record.calleeDialogId));
        }
        catch (err) {
            log_1.log.warn('Failed to load active callee dispatches from persistence', undefined, {
                dialogId: this.id.selfId,
                error: err,
            });
        }
    }
    /**
     * Post a dialog event using the standard event registry.
     */
    postEvent(event) {
        (0, evt_registry_1.postDialogEvent)(this, event);
    }
    // - return true if human response has been collected and incorporated into the msgs, thus
    // should continue generating next llm messages immediately;
    //
    // Reminder management methods
    addReminder(content, owner, meta, position, options) {
        const reminder = (0, tool_1.materializeReminder)({
            content,
            owner,
            meta,
            echoback: options?.echoback,
            scope: options?.scope ?? 'dialog',
            renderMode: options?.renderMode,
        });
        const insertIndex = position !== undefined ? position : this.reminders.length;
        if (insertIndex < 0 || insertIndex > this.reminders.length) {
            throw new Error(`Invalid reminder position ${insertIndex}. Valid range: 0-${this.reminders.length}`);
        }
        this.reminders.splice(insertIndex, 0, reminder);
        this.touchReminders();
        return reminder;
    }
    deleteReminder(index) {
        if (index < 0 || index >= this.reminders.length) {
            throw new InvalidReminderIndexError(index, this.reminders.length);
        }
        const deleted = this.reminders.splice(index, 1)[0];
        this.touchReminders();
        return deleted;
    }
    updateReminder(index, content, meta, options) {
        if (index < 0 || index >= this.reminders.length) {
            throw new InvalidReminderIndexError(index, this.reminders.length);
        }
        const oldReminder = this.reminders[index];
        const updatedReminder = (0, tool_1.materializeReminder)({
            id: oldReminder.id,
            content,
            owner: oldReminder.owner,
            meta: meta !== undefined ? meta : oldReminder.meta,
            echoback: options?.echoback ?? oldReminder.echoback,
            scope: oldReminder.scope,
            createdAt: oldReminder.createdAt,
            priority: oldReminder.priority,
            renderMode: options?.renderMode ?? oldReminder.renderMode,
        });
        this.reminders[index] = updatedReminder;
        this.touchReminders();
        return oldReminder;
    }
    clearReminders() {
        this.reminders.length = 0;
        this.touchReminders();
    }
    async listVisibleReminderTargets() {
        const targets = this.reminders.map((reminder, index) => ({
            source: 'dialog',
            index,
            reminder,
        }));
        const sharedTargets = [
            { kind: 'task', agentId: this.agentId, taskDocPath: this.taskDocPath },
            { kind: 'agent', agentId: this.agentId },
        ];
        for (const sharedTarget of sharedTargets) {
            const sharedReminders = await (0, shared_reminders_1.loadSharedReminders)(sharedTarget);
            for (let index = 0; index < sharedReminders.length; index += 1) {
                const reminder = sharedReminders[index];
                if (!reminder)
                    continue;
                targets.push({
                    source: 'runtime',
                    index,
                    reminder,
                    target: sharedTarget,
                });
            }
        }
        // Visible reminders are always merged into a single newest-first stream regardless of
        // storage scope, so UI rendering, reminder injection, and reminder-id targeting agree.
        targets.sort(compareVisibleReminderTargetOrder);
        return targets;
    }
    async listVisibleReminders() {
        return (await this.listVisibleReminderTargets()).map((target) => target.reminder);
    }
    async resolveReminderTargetById(reminderId) {
        const trimmed = reminderId.trim();
        if (trimmed === '')
            return null;
        const visibleTargets = await this.listVisibleReminderTargets();
        for (const target of visibleTargets) {
            if (target.reminder.id === trimmed) {
                return target;
            }
        }
        return null;
    }
    async processReminderCollection(reminders) {
        let changed = false;
        const indicesToRemove = [];
        for (let i = 0; i < reminders.length; i++) {
            const reminder = reminders[i];
            if (!reminder.owner || !reminder.owner.updateReminder) {
                continue;
            }
            try {
                const result = await reminder.owner.updateReminder(this, reminder);
                switch (result.treatment) {
                    case 'drop':
                        indicesToRemove.push(i);
                        changed = true;
                        break;
                    case 'update': {
                        const nextContent = result.updatedContent ?? reminder.content;
                        const nextMeta = result.updatedMeta !== undefined ? result.updatedMeta : reminder.meta;
                        const updatedReminder = (0, tool_1.materializeReminder)({
                            id: reminder.id,
                            content: nextContent,
                            owner: reminder.owner,
                            meta: nextMeta,
                            echoback: reminder.echoback,
                            scope: reminder.scope,
                            createdAt: reminder.createdAt,
                            priority: reminder.priority,
                            renderMode: reminder.renderMode,
                        });
                        const contentChanged = updatedReminder.content !== reminder.content;
                        const metaChanged = updatedReminder.meta !== reminder.meta;
                        if (contentChanged || metaChanged) {
                            reminders[i] = updatedReminder;
                            changed = true;
                        }
                        break;
                    }
                    case 'keep':
                        break;
                }
            }
            catch (error) {
                log_1.log.error(`Error updating reminder from tool ${reminder.owner}:`, error);
            }
        }
        for (let i = indicesToRemove.length - 1; i >= 0; i--) {
            reminders.splice(indicesToRemove[i], 1);
        }
        return changed;
    }
    buildVisibleReminderContents(taskSharedReminders, runtimeReminders) {
        const visibleReminders = [
            ...this.reminders.map((reminder) => (0, tool_1.cloneReminder)(reminder)),
            ...taskSharedReminders.map((reminder) => (0, tool_1.cloneReminder)(reminder)),
            ...runtimeReminders.map((reminder) => (0, tool_1.cloneReminder)(reminder)),
        ];
        visibleReminders.sort(tool_1.compareReminderDisplayOrder);
        return visibleReminders.map((r) => ({
            content: r.content,
            meta: r.meta,
            reminder_id: r.id,
            renderRevision: (0, tool_1.computeReminderRenderRevision)(r),
            echoback: (0, tool_1.reminderEchoBackEnabled)(r),
            scope: r.scope,
            renderMode: r.renderMode ?? 'markdown',
        }));
    }
    emitFullRemindersUpdate(reminders) {
        const fullRemindersEvt = {
            type: 'full_reminders_update',
            reminders,
        };
        (0, evt_registry_1.postDialogEvent)(this, fullRemindersEvt);
    }
    /**
     * Emits current visible reminders without reconciling tool-owned state or writing persistence.
     * This is for read-only dialog display, especially completed/archived dialogs opened from old tabs.
     */
    async emitReminderSnapshot() {
        const taskSharedTarget = {
            kind: 'task',
            agentId: this.agentId,
            taskDocPath: this.taskDocPath,
        };
        const runtimeTarget = { kind: 'agent', agentId: this.agentId };
        const taskSharedReminders = await (0, shared_reminders_1.loadSharedReminders)(taskSharedTarget);
        const runtimeReminders = await (0, shared_reminders_1.loadSharedReminders)(runtimeTarget);
        const reminders = this.buildVisibleReminderContents(taskSharedReminders, runtimeReminders);
        this.emitFullRemindersUpdate(reminders);
        return reminders;
    }
    /**
     * Process reminder updates before LLM generation.
     * Calls updateReminder on each tool that owns reminders to allow them to update/drop/keep their reminders.
     * Returns reminder contents with metadata for the frontend.
     */
    async processReminderUpdates() {
        const taskSharedTarget = {
            kind: 'task',
            agentId: this.agentId,
            taskDocPath: this.taskDocPath,
        };
        const runtimeTarget = { kind: 'agent', agentId: this.agentId };
        const taskSharedReminders = await (0, shared_reminders_1.loadSharedReminders)(taskSharedTarget);
        const runtimeReminders = await (0, shared_reminders_1.loadSharedReminders)(runtimeTarget);
        const localChanged = await this.processReminderCollection(this.reminders);
        const taskSharedChanged = await this.processReminderCollection(taskSharedReminders);
        const runtimeChanged = await this.processReminderCollection(runtimeReminders);
        if (localChanged || taskSharedChanged || runtimeChanged) {
            this.touchReminders();
        }
        // Centralized persistence - called when emitting event.
        // Must be awaited to preserve ordering (avoid out-of-date writes winning).
        try {
            await this.dlgStore.persistReminders(this, this.reminders);
        }
        catch (err) {
            log_1.log.warn('Failed to persist reminders', err, { dialogId: this.id.valueOf() });
        }
        try {
            await (0, shared_reminders_1.replaceSharedReminders)(taskSharedTarget, taskSharedReminders);
        }
        catch (err) {
            log_1.log.warn('Failed to persist task-scoped reminders', err, {
                dialogId: this.id.valueOf(),
                agentId: this.agentId,
                taskDocPath: this.taskDocPath,
            });
        }
        try {
            await (0, shared_reminders_1.replaceSharedReminders)(runtimeTarget, runtimeReminders);
        }
        catch (err) {
            log_1.log.warn('Failed to persist agent-scoped reminders', err, {
                dialogId: this.id.valueOf(),
                agentId: this.agentId,
            });
        }
        const reminders = this.buildVisibleReminderContents(taskSharedReminders, runtimeReminders);
        this.emitFullRemindersUpdate(reminders);
        return reminders;
    }
    setCoursePrefixMsgs(msgs) {
        this._coursePrefixMsgs = [...msgs];
    }
    getCoursePrefixMsgs() {
        return [...this._coursePrefixMsgs, ...this._courseRuntimeNoticeMsgs];
    }
    // only to be used by the driver
    async addChatMessages(...msgs) {
        this.msgs.push(...msgs);
    }
    /**
     * Get current course number
     */
    get currentCourse() {
        return this._currentCourse;
    }
    /**
     * Get current generation sequence number
     */
    get activeGenSeq() {
        if (!this._activeGenSeq) {
            throw new Error(`No active genseq, this is bug!!`);
        }
        return this._activeGenSeq;
    }
    get activeGenSeqOrUndefined() {
        return this._activeGenSeq;
    }
    get activeGenCourseOrUndefined() {
        return this._activeGenCourse;
    }
    get hasActiveGeneration() {
        return this._activeGenCourse !== undefined;
    }
    /**
     * Check if generation has started for the current course.
     * Used to ensure sideDialog_final_response_evt arrives after parent events.
     */
    get generationStarted() {
        return this._generationStarted;
    }
    /**
     * Mark generation as started (after user_text event has been emitted).
     * This ensures sideDialog_final_response_evt waits for this signal.
     * @param genseq The generation sequence number when this flag is set
     */
    markGenerationStarted(genseq) {
        this._generationStarted = true;
        this._generationStartedGenseq = genseq ?? this._activeGenSeq ?? 0;
    }
    /**
     * Get the genseq when generation was marked as started
     */
    get generationStartedGenseq() {
        return this._generationStartedGenseq;
    }
    /**
     * Phase 11: Get current suspension state
     * @returns 'active' | 'suspended' | 'resumed'
     */
    getSuspensionState() {
        return this._suspensionState;
    }
    /**
     * Phase 11: Set suspension state
     * @param state The new suspension state
     */
    setSuspensionState(state) {
        this._suspensionState = state;
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
    }
    get createdAt() {
        return this._createdAt;
    }
    get updatedAt() {
        return this._updatedAt;
    }
    replaceQueuedPromptState(existingMsgId, nextPrompt) {
        const queueIndex = this._queuedPrompts.findIndex((prompt) => prompt.msgId === existingMsgId);
        if (queueIndex < 0) {
            throw new Error(`Queued prompt replacement invariant violation: dialog=${this.id.valueOf()} existingMsgId=${existingMsgId}`);
        }
        this._queuedPrompts[queueIndex] = nextPrompt;
        for (let index = this._driveIntents.length - 1; index >= 0; index -= 1) {
            const intent = this._driveIntents[index];
            if (intent.kind !== 'prompt' || intent.prompt.msgId !== existingMsgId)
                continue;
            const promptCommon = {
                content: nextPrompt.prompt,
                ...(nextPrompt.contentItems === undefined ? {} : { contentItems: nextPrompt.contentItems }),
                msgId: nextPrompt.msgId,
                grammar: nextPrompt.grammar ?? 'markdown',
                userLanguageCode: nextPrompt.userLanguageCode ?? this._lastUserLanguageCode,
            };
            this._driveIntents[index] = {
                ...intent,
                prompt: (() => {
                    switch (nextPrompt.kind) {
                        case 'user_generation_boundary':
                        case 'deferred_q4h_answer': {
                            const prompt = {
                                ...promptCommon,
                                origin: 'user',
                                ...(nextPrompt.q4hAnswerCallId === undefined
                                    ? {}
                                    : { q4hAnswerCallId: nextPrompt.q4hAnswerCallId }),
                            };
                            return prompt;
                        }
                        case 'registered_assignment_update':
                        case 'new_course_runtime_sideDialog': {
                            const prompt = {
                                ...promptCommon,
                                origin: 'runtime',
                                ...(nextPrompt.skipTaskdoc === undefined
                                    ? {}
                                    : { skipTaskdoc: nextPrompt.skipTaskdoc }),
                                tellaskReplyDirective: nextPrompt.tellaskReplyDirective,
                                calleeDialogReplyTarget: nextPrompt.calleeDialogReplyTarget,
                            };
                            return prompt;
                        }
                        case 'new_course_runtime_reply': {
                            const prompt = {
                                ...promptCommon,
                                origin: 'runtime',
                                ...(nextPrompt.skipTaskdoc === undefined
                                    ? {}
                                    : { skipTaskdoc: nextPrompt.skipTaskdoc }),
                                tellaskReplyDirective: nextPrompt.tellaskReplyDirective,
                            };
                            return prompt;
                        }
                        case 'new_course_runtime_guide': {
                            const prompt = {
                                ...promptCommon,
                                origin: 'runtime',
                                ...(nextPrompt.skipTaskdoc === undefined
                                    ? {}
                                    : { skipTaskdoc: nextPrompt.skipTaskdoc }),
                            };
                            return prompt;
                        }
                        default: {
                            const _exhaustive = nextPrompt;
                            throw new Error(`Queued prompt replacement invariant violation: unsupported queued prompt`);
                        }
                    }
                })(),
                runControl: nextPrompt.runControl,
            };
            return nextPrompt;
        }
        throw new Error(`Queued prompt replacement invariant violation: missing drive intent dialog=${this.id.valueOf()} existingMsgId=${existingMsgId}`);
    }
    setPendingRuntimePrompt(prompt) {
        const prepared = typeof prompt === 'string'
            ? {
                content: prompt,
                msgId: (0, id_1.generateShortId)(),
                grammar: 'markdown',
                userLanguageCode: this._lastUserLanguageCode,
                origin: 'runtime',
            }
            : prompt;
        const trimmed = prepared.content.trim();
        if (!trimmed) {
            throw new Error('Prompt is required to start a new course');
        }
        const runtimeCommon = {
            content: trimmed,
            msgId: prepared.msgId.trim() || (0, id_1.generateShortId)(),
            grammar: 'markdown',
            userLanguageCode: prepared.userLanguageCode ?? this._lastUserLanguageCode,
            origin: 'runtime',
            ...(prepared.skipTaskdoc === undefined ? {} : { skipTaskdoc: prepared.skipTaskdoc }),
        };
        const normalized = prepared.calleeDialogReplyTarget !== undefined
            ? (() => {
                const prompt = {
                    ...runtimeCommon,
                    tellaskReplyDirective: prepared.tellaskReplyDirective,
                    calleeDialogReplyTarget: prepared.calleeDialogReplyTarget,
                };
                return prompt;
            })()
            : prepared.tellaskReplyDirective !== undefined
                ? (() => {
                    const prompt = {
                        ...runtimeCommon,
                        tellaskReplyDirective: prepared.tellaskReplyDirective,
                    };
                    return prompt;
                })()
                : (() => {
                    const prompt = runtimeCommon;
                    return prompt;
                })();
        this._queuedPrompts = [
            normalized.calleeDialogReplyTarget !== undefined
                ? {
                    kind: 'new_course_runtime_sideDialog',
                    prompt: normalized.content,
                    msgId: normalized.msgId,
                    grammar: normalized.grammar,
                    userLanguageCode: normalized.userLanguageCode,
                    origin: 'runtime',
                    tellaskReplyDirective: normalized.tellaskReplyDirective,
                    skipTaskdoc: normalized.skipTaskdoc,
                    calleeDialogReplyTarget: normalized.calleeDialogReplyTarget,
                }
                : normalized.tellaskReplyDirective !== undefined
                    ? {
                        kind: 'new_course_runtime_reply',
                        prompt: normalized.content,
                        msgId: normalized.msgId,
                        grammar: normalized.grammar,
                        userLanguageCode: normalized.userLanguageCode,
                        origin: 'runtime',
                        tellaskReplyDirective: normalized.tellaskReplyDirective,
                        skipTaskdoc: normalized.skipTaskdoc,
                    }
                    : {
                        kind: 'new_course_runtime_guide',
                        prompt: normalized.content,
                        msgId: normalized.msgId,
                        grammar: normalized.grammar,
                        userLanguageCode: normalized.userLanguageCode,
                        origin: 'runtime',
                        skipTaskdoc: normalized.skipTaskdoc,
                    },
        ];
        return normalized;
    }
    mergePromptQ4HAnswerCallId(existing, incoming) {
        const normalizedExisting = typeof existing === 'string' ? existing.trim() : '';
        const normalizedIncoming = typeof incoming === 'string' ? incoming.trim() : '';
        if (normalizedExisting === '') {
            return normalizedIncoming !== '' ? normalizedIncoming : undefined;
        }
        if (normalizedIncoming === '') {
            return normalizedExisting;
        }
        if (normalizedExisting !== normalizedIncoming) {
            throw new Error(`Q4H answer continuation invariant violation: conflicting callIds cannot be merged ` +
                `(dialog=${this.id.valueOf()} existing=${normalizedExisting} incoming=${normalizedIncoming})`);
        }
        return normalizedExisting;
    }
    enqueueQueuedPromptState(state) {
        this._queuedPrompts.push(state);
        const promptCommon = {
            content: state.prompt,
            ...(state.contentItems === undefined ? {} : { contentItems: state.contentItems }),
            msgId: state.msgId,
            grammar: state.grammar ?? 'markdown',
            userLanguageCode: state.userLanguageCode ?? this._lastUserLanguageCode,
        };
        this._driveIntents.push({
            kind: 'prompt',
            prompt: (() => {
                switch (state.kind) {
                    case 'user_generation_boundary':
                    case 'deferred_q4h_answer': {
                        const prompt = {
                            ...promptCommon,
                            origin: 'user',
                            ...(state.q4hAnswerCallId === undefined
                                ? {}
                                : { q4hAnswerCallId: state.q4hAnswerCallId }),
                        };
                        return prompt;
                    }
                    case 'registered_assignment_update':
                    case 'new_course_runtime_sideDialog': {
                        const prompt = {
                            ...promptCommon,
                            origin: 'runtime',
                            ...(state.skipTaskdoc === undefined ? {} : { skipTaskdoc: state.skipTaskdoc }),
                            tellaskReplyDirective: state.tellaskReplyDirective,
                            calleeDialogReplyTarget: state.calleeDialogReplyTarget,
                        };
                        return prompt;
                    }
                    case 'new_course_runtime_reply': {
                        const prompt = {
                            ...promptCommon,
                            origin: 'runtime',
                            ...(state.skipTaskdoc === undefined ? {} : { skipTaskdoc: state.skipTaskdoc }),
                            tellaskReplyDirective: state.tellaskReplyDirective,
                        };
                        return prompt;
                    }
                    case 'new_course_runtime_guide': {
                        const prompt = {
                            ...promptCommon,
                            origin: 'runtime',
                            ...(state.skipTaskdoc === undefined ? {} : { skipTaskdoc: state.skipTaskdoc }),
                        };
                        return prompt;
                    }
                    default: {
                        const _exhaustive = state;
                        throw new Error(`Queued prompt enqueue invariant violation: unsupported queued prompt`);
                    }
                }
            })(),
            runControl: state.runControl,
        });
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
    }
    peekLatestQueuedPrompt() {
        if (this._queuedPrompts.length === 0) {
            return undefined;
        }
        return this._queuedPrompts[this._queuedPrompts.length - 1];
    }
    queueUserPromptAtGenerationBoundary(options) {
        const existing = this.peekLatestQueuedPrompt();
        const trimmed = options.prompt.trim();
        if (!trimmed) {
            throw new Error('Prompt is required to queue generation-boundary user prompt');
        }
        if (existing?.kind !== 'user_generation_boundary') {
            const created = {
                kind: 'user_generation_boundary',
                prompt: trimmed,
                msgId: options.msgId,
                grammar: options.grammar,
                userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
                origin: 'user',
                contentItems: options.contentItems,
                q4hAnswerCallId: options.q4hAnswerCallId,
            };
            this.enqueueQueuedPromptState(created);
            return created;
        }
        const merged = {
            ...existing,
            prompt: `${existing.prompt}\n\n---\n\n${trimmed}`,
            contentItems: existing.contentItems || options.contentItems
                ? [...(existing.contentItems ?? []), ...(options.contentItems ?? [])]
                : undefined,
            grammar: options.grammar,
            userLanguageCode: options.userLanguageCode ?? existing.userLanguageCode ?? this._lastUserLanguageCode,
            q4hAnswerCallId: this.mergePromptQ4HAnswerCallId(existing.q4hAnswerCallId, options.q4hAnswerCallId),
        };
        this.replaceQueuedPromptState(existing.msgId, merged);
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
        return merged;
    }
    queueDeferredQ4HAnswerPrompt(options) {
        const trimmed = options.prompt.trim();
        if (!trimmed) {
            throw new Error('Continuation input is required to queue deferred Q4H answer');
        }
        // This queue item only carries the already-answered askHuman call correlation into the next
        // resumed drive. The answer itself has already been persisted as tellask result/carryover.
        const created = {
            kind: 'deferred_q4h_answer',
            prompt: trimmed,
            contentItems: options.contentItems,
            msgId: options.msgId,
            grammar: options.grammar,
            userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
            origin: 'user',
            q4hAnswerCallId: options.q4hAnswerCallId,
            runControl: undefined,
        };
        this.enqueueQueuedPromptState(created);
        return created;
    }
    queueRegisteredAssignmentUpdatePrompt(options) {
        const existing = this.peekLatestQueuedPrompt();
        const trimmed = options.prompt.trim();
        if (!trimmed) {
            throw new Error('Prompt is required to queue registered assignment update');
        }
        // This is runtime scheduling state, not a durable redo log. Persisted business facts already
        // record the latest assignment in the asker stack and pending records; if the process dies
        // before this prompt is consumed, recovery should surface those facts loudly for the LLM to
        // decide the next action. Do not add persistence here just to make the queued bubble replayable.
        if (existing?.kind !== 'registered_assignment_update') {
            const created = {
                kind: 'registered_assignment_update',
                prompt: trimmed,
                msgId: options.msgId,
                grammar: options.grammar,
                userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
                origin: 'runtime',
                tellaskReplyDirective: options.tellaskReplyDirective,
                skipTaskdoc: options.skipTaskdoc,
                calleeDialogReplyTarget: options.calleeDialogReplyTarget,
                runControl: undefined,
            };
            this.enqueueQueuedPromptState(created);
            return created;
        }
        const merged = {
            ...existing,
            prompt: trimmed,
            msgId: options.msgId,
            grammar: options.grammar,
            userLanguageCode: options.userLanguageCode ?? existing.userLanguageCode ?? this._lastUserLanguageCode,
            tellaskReplyDirective: options.tellaskReplyDirective,
            skipTaskdoc: options.skipTaskdoc ?? existing.skipTaskdoc,
            calleeDialogReplyTarget: options.calleeDialogReplyTarget,
            runControl: undefined,
        };
        this.replaceQueuedPromptState(existing.msgId, merged);
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
        return merged;
    }
    async persistPendingRuntimePrompt(prompt) {
        await this.dlgStore.persistPendingRuntimePrompt(this, prompt);
        if (this.id.selfId === this.id.rootId) {
            await queueRootDialogDrive(this.id.rootId, {
                source: 'dialog_runtime_prompt',
                reason: `queued_runtime_prompt:${prompt.msgId}`,
            });
        }
    }
    runtimePromptCommon(options) {
        const trimmed = options.prompt.trim();
        if (!trimmed) {
            throw new Error('Prompt is required to queue runtime prompt');
        }
        return {
            prompt: trimmed,
            msgId: options.msgId,
            grammar: options.grammar,
            userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
            origin: 'runtime',
            runControl: undefined,
        };
    }
    async queueRuntimeReplyPrompt(options) {
        const common = this.runtimePromptCommon(options);
        const created = {
            ...common,
            kind: 'new_course_runtime_reply',
            tellaskReplyDirective: options.tellaskReplyDirective,
            skipTaskdoc: options.skipTaskdoc,
        };
        this.enqueueQueuedPromptState(created);
        await this.persistPendingRuntimePrompt({
            content: created.prompt,
            msgId: created.msgId,
            grammar: created.grammar ?? 'markdown',
            userLanguageCode: created.userLanguageCode,
            origin: 'runtime',
            tellaskReplyDirective: created.tellaskReplyDirective,
            skipTaskdoc: created.skipTaskdoc,
        });
        return created;
    }
    async queueRuntimeSideDialogPrompt(options) {
        const common = this.runtimePromptCommon(options);
        const created = {
            ...common,
            kind: 'new_course_runtime_sideDialog',
            tellaskReplyDirective: options.tellaskReplyDirective,
            skipTaskdoc: options.skipTaskdoc,
            calleeDialogReplyTarget: options.calleeDialogReplyTarget,
        };
        this.enqueueQueuedPromptState(created);
        await this.persistPendingRuntimePrompt({
            content: created.prompt,
            msgId: created.msgId,
            grammar: created.grammar ?? 'markdown',
            userLanguageCode: created.userLanguageCode,
            origin: 'runtime',
            tellaskReplyDirective: created.tellaskReplyDirective,
            skipTaskdoc: created.skipTaskdoc,
            calleeDialogReplyTarget: created.calleeDialogReplyTarget,
        });
        return created;
    }
    hasQueuedPrompt() {
        return this._queuedPrompts.length > 0;
    }
    peekQueuedPrompt() {
        return this._queuedPrompts[0];
    }
    takeQueuedPrompt() {
        return this._queuedPrompts.shift();
    }
    removeQueuedPromptByMsgId(msgId) {
        const normalized = msgId.trim();
        if (normalized === '') {
            return false;
        }
        const before = this._queuedPrompts.length;
        this._queuedPrompts = this._queuedPrompts.filter((prompt) => prompt.msgId !== normalized);
        return this._queuedPrompts.length !== before;
    }
    removeQueuedPromptsMatching(predicate) {
        const before = this._queuedPrompts.length;
        this._queuedPrompts = this._queuedPrompts.filter((prompt) => !predicate(prompt));
        return before - this._queuedPrompts.length;
    }
    setActiveRunControlSpec(spec) {
        this._activeRunControlSpec = spec;
    }
    getActiveRunControlSpec() {
        return this._activeRunControlSpec;
    }
    setNewCourseHook(hook) {
        this._newCourseHook = hook;
    }
    enqueueDriveIntent(intent) {
        this._driveIntents.push(intent);
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
    }
    takeDriveIntent() {
        return this._driveIntents.shift();
    }
    hasDriveIntents() {
        return this._driveIntents.length > 0;
    }
    clearDriveIntents() {
        this._driveIntents.length = 0;
    }
    /**
     * Start a new course - clears conversational noise, Q4H, and increments course counter.
     * Queues a new-course prompt for the driver to consume on the next drive cycle.
     * This is the single entry point for mental clarity operations that start a new course.
     */
    async startNewCourse(newCoursePrompt, options) {
        const trimmedPrompt = newCoursePrompt.trim();
        if (!trimmedPrompt) {
            throw new Error('newCoursePrompt is required to start a new course');
        }
        const combinedPrompt = this instanceof SideDialog
            ? `${(0, inter_dialog_format_1.formatAssignmentFromAskerDialog)({
                fromAgentId: this.assignmentFromAsker.originMemberId,
                toAgentId: this.agentId,
                callName: this.assignmentFromAsker.callName,
                mentionList: this.assignmentFromAsker.mentionList,
                tellaskContent: this.assignmentFromAsker.tellaskContent,
                language: (0, work_language_1.getWorkLanguage)(),
                collectiveTargets: this.assignmentFromAsker.collectiveTargets ?? [this.agentId],
            })}\n---\n${trimmedPrompt}`
            : trimmedPrompt;
        const basePrompt = this instanceof SideDialog
            ? {
                content: combinedPrompt,
                msgId: (0, id_1.generateShortId)(),
                grammar: 'markdown',
                userLanguageCode: this._lastUserLanguageCode,
                origin: 'runtime',
                ...buildSideDialogAssignmentPromptMeta(this),
            }
            : {
                content: combinedPrompt,
                msgId: (0, id_1.generateShortId)(),
                grammar: 'markdown',
                userLanguageCode: this._lastUserLanguageCode,
                origin: 'runtime',
            };
        const runControlSpec = options?.runControl ?? this._activeRunControlSpec;
        let nextPrompt = basePrompt;
        if (this._newCourseHook && options?.skipRunControlHook !== true) {
            const hookResult = await this._newCourseHook({
                dialog: this,
                prompt: basePrompt,
                runControl: runControlSpec,
            });
            if (hookResult.kind === 'reject') {
                throw new Error(hookResult.errorText);
            }
            nextPrompt = hookResult.prompt;
        }
        // Clear all messages and Q4H questions for mental clarity
        this.msgs.length = 0;
        await this.dlgStore.clearQuestions4Human(this);
        // Delegate to DialogStore for course start persistence
        if (this.dlgStore) {
            await this.dlgStore.startNewCourse(this, nextPrompt);
        }
        if (this.id.selfId === this.id.rootId) {
            await queueRootDialogDrive(this.id.rootId, {
                source: 'dialog_start_new_course',
                reason: `queued_runtime_prompt:${nextPrompt.msgId}`,
            });
        }
        const storeCourse = this.dlgStore
            ? await this.dlgStore.loadCurrentCourse(this.id)
            : this._currentCourse + 1;
        this._currentCourse = storeCourse;
        this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
        this.resetCourseLanguageNotice();
        const normalized = this.setPendingRuntimePrompt(nextPrompt);
        if (options?.skipEnqueueIntent !== true) {
            this._driveIntents.length = 0;
            this._driveIntents.push({
                kind: 'new_course',
                prompt: normalized,
                reason: options?.reason,
                runControl: runControlSpec,
            });
        }
        return normalized;
    }
    // Proxy methods for DialogStore - route calls through dialog object instead of direct dlgStore access
    async receiveFuncResult(result) {
        return await this.dlgStore.receiveFuncResult(this, result);
    }
    async receiveTellaskResult(result) {
        return await this.dlgStore.receiveTellaskResult(this, result);
    }
    async receiveTellaskCarryover(result) {
        return await this.dlgStore.receiveTellaskCarryover(this, result);
    }
    async receiveTellaskCallResult(responderId, callName, mentionList, tellaskContent, result, status, callId, options) {
        const tellaskResult = callName === 'tellask'
            ? {
                type: 'tellask_result_msg',
                role: 'tool',
                callId,
                callName,
                status,
                content: result,
                ...(options?.callSiteCourse !== undefined
                    ? { callSiteCourse: options.callSiteCourse }
                    : {}),
                ...(options?.callSiteGenseq !== undefined
                    ? { callSiteGenseq: options.callSiteGenseq }
                    : {}),
                call: {
                    tellaskContent,
                    mentionList: mentionList ?? [],
                },
                responder: {
                    responderId,
                },
            }
            : callName === 'tellaskSessionless'
                ? {
                    type: 'tellask_result_msg',
                    role: 'tool',
                    callId,
                    callName,
                    status,
                    content: result,
                    ...(options?.callSiteCourse !== undefined
                        ? { callSiteCourse: options.callSiteCourse }
                        : {}),
                    ...(options?.callSiteGenseq !== undefined
                        ? { callSiteGenseq: options.callSiteGenseq }
                        : {}),
                    call: {
                        tellaskContent,
                        mentionList: mentionList ?? [],
                    },
                    responder: {
                        responderId,
                    },
                }
                : {
                    type: 'tellask_result_msg',
                    role: 'tool',
                    callId,
                    callName,
                    status,
                    content: result,
                    ...(options?.callSiteCourse !== undefined
                        ? { callSiteCourse: options.callSiteCourse }
                        : {}),
                    ...(options?.callSiteGenseq !== undefined
                        ? { callSiteGenseq: options.callSiteGenseq }
                        : {}),
                    call: {
                        tellaskContent,
                    },
                    responder: {
                        responderId,
                    },
                };
        await this.receiveTellaskResult(tellaskResult);
    }
    async receiveTellaskResponse(responderId, callName, mentionList, tellaskContent, status, sideDialogId, options) {
        const currentCourse = this.activeGenCourseOrUndefined ?? this.currentCourse;
        const resultRoute = {
            ...(sideDialogId ? { calleeDialogId: sideDialogId.selfId } : {}),
            ...(typeof options.calleeCourse === 'number' ? { calleeCourse: options.calleeCourse } : {}),
            ...(typeof options.calleeGenseq === 'number' ? { calleeGenseq: options.calleeGenseq } : {}),
        };
        const carryoverCallSiteCourse = options.callSiteCourse;
        const isCrossCourseResponse = carryoverCallSiteCourse !== undefined && carryoverCallSiteCourse !== currentCourse;
        if (isCrossCourseResponse) {
            // Cross-course completion is not represented as a tool-result pair in the latest course.
            // Instead we write a canonical current-course carryover message that restates the older
            // tellask plus its resolution so the latest-course LLM context can read it directly.
            if (typeof options.carryoverContent !== 'string') {
                throw new Error(`receiveTellaskResponse invariant violation: missing carryover content for cross-course response ` +
                    `(callId=${options.callId}, callName=${callName}, callSiteCourse=${options.callSiteCourse}, currentCourse=${currentCourse})`);
            }
            if (options.carryoverContent.trim() === '') {
                throw new Error(`receiveTellaskResponse invariant violation: empty carryover content ` +
                    `(callId=${options.callId}, callName=${callName})`);
            }
            if (options.response.trim() === '') {
                throw new Error(`receiveTellaskResponse invariant violation: empty carryover response body ` +
                    `(callId=${options.callId}, callName=${callName})`);
            }
            if (callName === 'tellaskBack') {
                throw new Error(`receiveTellaskResponse invariant violation: tellaskBack does not support carryover (callId=${options.callId})`);
            }
            const carryoverResult = {
                type: 'tellask_carryover_msg',
                role: 'user',
                genseq: this.activeGenSeqOrUndefined ?? 1,
                content: options.carryoverContent,
                ...(options.contentItems === undefined ? {} : { contentItems: options.contentItems }),
                callSiteCourse: carryoverCallSiteCourse,
                carryoverCourse: currentCourse,
                responderId,
                callName,
                tellaskContent,
                status,
                response: options.response,
                agentId: options.agentId,
                callId: options.callId,
                originMemberId: options.originMemberId,
                ...(callName === 'tellask'
                    ? {
                        mentionList: mentionList ?? [],
                        sessionSlug: options.sessionSlug,
                    }
                    : callName === 'tellaskSessionless'
                        ? {
                            mentionList: mentionList ?? [],
                        }
                        : {}),
                ...resultRoute,
            };
            await this.receiveTellaskCarryover(carryoverResult);
            return carryoverResult;
        }
        const tellaskResult = callName === 'tellask'
            ? {
                type: 'tellask_result_msg',
                role: 'tool',
                callId: options.callId,
                callName,
                status,
                content: options.response,
                ...(options.contentItems === undefined ? {} : { contentItems: options.contentItems }),
                ...(options.callSiteCourse !== undefined
                    ? { callSiteCourse: options.callSiteCourse }
                    : {}),
                ...(options.callSiteGenseq !== undefined
                    ? { callSiteGenseq: options.callSiteGenseq }
                    : {}),
                call: {
                    tellaskContent,
                    mentionList: mentionList ?? [],
                    ...(options.sessionSlug ? { sessionSlug: options.sessionSlug } : {}),
                },
                responder: {
                    responderId,
                    agentId: options.agentId,
                    originMemberId: options.originMemberId,
                },
                route: resultRoute,
            }
            : callName === 'tellaskSessionless'
                ? {
                    type: 'tellask_result_msg',
                    role: 'tool',
                    callId: options.callId,
                    callName,
                    status,
                    content: options.response,
                    ...(options.contentItems === undefined ? {} : { contentItems: options.contentItems }),
                    ...(options.callSiteCourse !== undefined
                        ? { callSiteCourse: options.callSiteCourse }
                        : {}),
                    ...(options.callSiteGenseq !== undefined
                        ? { callSiteGenseq: options.callSiteGenseq }
                        : {}),
                    call: {
                        tellaskContent,
                        mentionList: mentionList ?? [],
                    },
                    responder: {
                        responderId,
                        agentId: options.agentId,
                        originMemberId: options.originMemberId,
                    },
                    route: resultRoute,
                }
                : {
                    type: 'tellask_result_msg',
                    role: 'tool',
                    callId: options.callId,
                    callName,
                    status,
                    content: options.response,
                    ...(options.contentItems === undefined ? {} : { contentItems: options.contentItems }),
                    ...(options.callSiteCourse !== undefined
                        ? { callSiteCourse: options.callSiteCourse }
                        : {}),
                    ...(options.callSiteGenseq !== undefined
                        ? { callSiteGenseq: options.callSiteGenseq }
                        : {}),
                    call: {
                        tellaskContent,
                    },
                    responder: {
                        responderId,
                        agentId: options.agentId,
                        originMemberId: options.originMemberId,
                    },
                    route: resultRoute,
                };
        await this.receiveTellaskResult(tellaskResult);
        return tellaskResult;
    }
    async notifyGeneratingStart(msgId) {
        // Capture the generation's starting course so any events emitted during this generation
        // remain attributed to the correct course even if a tool mutates dialog.currentCourse
        // mid-generation (e.g., clear_mind).
        this._activeGenCourse = this.currentCourse;
        if (typeof this._activeGenSeq === 'number') {
            this._activeGenSeq++;
        }
        else {
            // Get next sequence number from store
            const genseq = await this.dlgStore.getNextSeq(this.id, this.currentCourse);
            this._activeGenSeq = genseq;
        }
        // Mark generation as started with the actual genseq
        // This ensures sideDialog_final_response_evt waits for both user_text and generating_start_evt
        this.markGenerationStarted();
        try {
            return await this.dlgStore.notifyGeneratingStart(this, msgId);
        }
        catch (err) {
            this._generationStarted = false;
            this._generationStartedGenseq = 0;
            this._activeGenCourse = undefined;
            throw err;
        }
    }
    async notifyGeneratingFinish(contextHealth, llmGenModel) {
        if (contextHealth) {
            this.setLastContextHealth(contextHealth);
        }
        try {
            await this.dlgStore.notifyGeneratingFinish(this, contextHealth, llmGenModel);
        }
        finally {
            // Reset generation tracking for the next course even when persistence fails loudly.
            this._generationStarted = false;
            this._generationStartedGenseq = 0;
            this._activeGenCourse = undefined;
        }
    }
    async streamError(error) {
        if (this.dlgStore) {
            await this.dlgStore.streamError(this, error);
        }
    }
    async thinkingStart() {
        await this.dlgStore.thinkingStart(this);
    }
    async thinkingChunk(chunk) {
        await this.dlgStore.thinkingChunk(this, chunk);
    }
    async markdownStart() {
        await this.dlgStore.markdownStart(this);
    }
    async markdownChunk(chunk) {
        await this.dlgStore.markdownChunk(this, chunk);
    }
    async thinkingFinish(reasoning, provider_data) {
        await this.dlgStore.thinkingFinish(this, reasoning, provider_data);
    }
    async markdownFinish() {
        await this.dlgStore.markdownFinish(this);
    }
    async sayingStart() {
        await this.dlgStore.sayingStart(this);
    }
    async sayingChunk(chunk) {
        await this.dlgStore.sayingChunk(this, chunk);
        // No frontend event needed - frontend gets content through streaming parser
    }
    async sayingFinish() {
        await this.dlgStore.sayingFinish(this);
    }
    async webSearchCall(payload) {
        await this.dlgStore.webSearchCall(this, payload);
    }
    async nativeToolCall(payload) {
        await this.dlgStore.nativeToolCall(this, payload);
    }
    async toolResultImageIngest(payload) {
        await this.dlgStore.toolResultImageIngest(this, payload);
    }
    async userImageIngest(payload) {
        await this.dlgStore.userImageIngest(this, payload);
    }
    // Tellask-special call lifecycle events
    async callingStart(payload) {
        // Store callId for inline call-result correlation
        this.setCurrentCallId(payload.callId);
        await this.dlgStore.callingStart(this, payload);
    }
    async updateQuestions4Human(questions) {
        return await this.dlgStore.updateQuestions4Human(this, questions);
    }
    async persistUserMessage(content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallId, tellaskReplyDirective, contentItems) {
        return await this.dlgStore.persistUserMessage(this, content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallId, tellaskReplyDirective, contentItems);
    }
    async receiveHumanReply(args) {
        if (args.content.trim() === '') {
            throw new Error(`receiveHumanReply invariant violation: empty human reply content ` +
                `(rootId=${this.id.rootId} selfId=${this.id.selfId})`);
        }
        const callId = args.q4hAnswerCallId.trim();
        if (callId === '') {
            throw new Error(`receiveHumanReply invariant violation: empty q4hAnswerCallId ` +
                `(rootId=${this.id.rootId} selfId=${this.id.selfId})`);
        }
        if (args.userLanguageCode === 'zh' || args.userLanguageCode === 'en') {
            this.setLastUserLanguageCode(args.userLanguageCode);
        }
    }
    async appendTellaskReplyResolution(payload) {
        await this.dlgStore.appendTellaskReplyResolution(this, payload);
    }
    async persistAgentMessage(content, genseq, type, provider_data, reasoning) {
        return await this.dlgStore.persistAgentMessage(this, content, genseq, type, provider_data, reasoning);
    }
    async persistUiOnlyMarkdown(content, genseq) {
        return await this.dlgStore.persistUiOnlyMarkdown(this, content, genseq);
    }
    async persistFunctionCall(id, name, rawArgumentsText, genseq, rawId) {
        return await this.dlgStore.persistFunctionCall(this, id, name, rawArgumentsText, genseq, rawId);
    }
    async persistTellaskCall(id, name, rawArgumentsText, genseq, options) {
        return await this.dlgStore.persistTellaskCall(this, id, name, rawArgumentsText, genseq, options);
    }
    /**
     * Post sideDialog completion response to this dialog.
     * Phase 14: No wait - emit immediately with virtual gen markers for Type C sideDialogs
     */
    async postSideDialogResponse(sideDialogId, response) {
        try {
            const metadata = await this.dlgStore.loadDialogMetadata(sideDialogId, 'running');
            if (!metadata) {
                throw new Error(`Missing sideDialog metadata for ${sideDialogId.valueOf()}`);
            }
            const assignmentFromAsker = await this.dlgStore.loadSideDialogAssignmentFromAsker(sideDialogId, 'running');
            if (!assignmentFromAsker) {
                throw new Error(`Missing sideDialog assignment for ${sideDialogId.valueOf()}`);
            }
            let responderId = sideDialogId.rootId;
            let responderAgentId;
            if (metadata.agentId.trim() !== '') {
                responderId = metadata.agentId;
                responderAgentId = metadata.agentId;
            }
            const callName = assignmentFromAsker.callName;
            let mentionList = assignmentFromAsker.mentionList;
            const tellaskContent = assignmentFromAsker.tellaskContent;
            const originMemberId = assignmentFromAsker.originMemberId;
            const callId = assignmentFromAsker.callId;
            const sessionSlug = typeof metadata.sessionSlug === 'string' && metadata.sessionSlug.trim() !== ''
                ? metadata.sessionSlug.trim()
                : undefined;
            if ((callName === 'tellask' || callName === 'tellaskSessionless') &&
                (!Array.isArray(mentionList) || mentionList.length < 1)) {
                mentionList = [`@${responderId}`];
            }
            // NO WAIT - emit immediately with virtual gen markers
            // Emit virtual generating_start_evt for sideDialog response bubble
            await this.notifyGeneratingStart();
            const rawResponse = response;
            // Emit TellaskResultEvent
            const evt = (() => {
                switch (callName) {
                    case 'tellask':
                        if (!sessionSlug) {
                            throw new Error(`postSideDialogResponse invariant violation: missing sessionSlug for tellask ` +
                                `(dialogId=${this.id.selfId}, sideDialogId=${sideDialogId.selfId}, callId=${callId})`);
                        }
                        return {
                            type: 'tellask_result_evt',
                            callName,
                            call: {
                                sessionSlug,
                                mentionList: mentionList ?? [],
                                tellaskContent,
                            },
                            status: 'completed',
                            course: this.currentCourse,
                            callId,
                            content: rawResponse,
                            responder: {
                                responderId,
                                agentId: responderAgentId ?? responderId,
                                originMemberId,
                            },
                            route: {
                                calleeDialogId: sideDialogId.selfId,
                            },
                        };
                    case 'tellaskSessionless':
                        return {
                            type: 'tellask_result_evt',
                            callName,
                            call: {
                                mentionList: mentionList ?? [],
                                tellaskContent,
                            },
                            status: 'completed',
                            course: this.currentCourse,
                            callId,
                            content: rawResponse,
                            responder: {
                                responderId,
                                agentId: responderAgentId ?? responderId,
                                originMemberId,
                            },
                            route: {
                                calleeDialogId: sideDialogId.selfId,
                            },
                        };
                    case 'freshBootsReasoning':
                        return {
                            type: 'tellask_result_evt',
                            callName,
                            call: {
                                tellaskContent,
                            },
                            status: 'completed',
                            course: this.currentCourse,
                            callId,
                            content: rawResponse,
                            responder: {
                                responderId,
                                agentId: responderAgentId ?? responderId,
                                originMemberId,
                            },
                            route: {
                                calleeDialogId: sideDialogId.selfId,
                            },
                        };
                }
            })();
            (0, evt_registry_1.postDialogEvent)(this, evt);
            // Emit virtual generating_finish_evt
            await this.notifyGeneratingFinish();
        }
        catch (err) {
            log_1.log.error('Failed to post tellask_result_evt event', err, {
                error: err,
                message: err instanceof Error ? err.message : String(err),
            });
            throw err;
        }
    }
}
exports.Dialog = Dialog;
/**
 * SideDialog - A sideDialog created by a MainDialog for autonomous tellask Side Dialog work.
 * Stores the main dialog for registry and lookup, and resolves its effective askerDialog dynamically.
 */
class SideDialog extends Dialog {
    mainDialog;
    sessionSlug;
    askerStack;
    _fbrConclusionToolsEnabled = false;
    constructor(dlgStore, mainDialog, taskDocPath, id, agentId, askerStack, sessionSlug, initialState) {
        super(dlgStore, taskDocPath, id, agentId, initialState);
        this.mainDialog = mainDialog;
        this.sessionSlug = sessionSlug;
        this.askerStack = askerStack;
        const top = getSideDialogAskerStackTop(askerStack);
        const assignmentFromAsker = getSideDialogAskerStackCurrentAssignment(askerStack);
        if (top.assignmentFromAsker !== undefined &&
            top.askerDialogId !== assignmentFromAsker.askerDialogId) {
            throw new Error(`SideDialog askerStack invariant violation: askerDialogId must match assignment askerDialogId ` +
                `(rootId=${mainDialog.id.rootId}, selfId=${this.id.selfId}, frameAskerDialogId=${top.askerDialogId}, assignmentAskerDialogId=${assignmentFromAsker.askerDialogId})`);
        }
        const resolvedAskerDialog = mainDialog.lookupDialog(top.askerDialogId);
        if (resolvedAskerDialog && resolvedAskerDialog.id.selfId === this.id.selfId) {
            throw new Error(`SideDialog askerDialog invariant violation: asker resolved to self ` +
                `(rootId=${mainDialog.id.rootId}, selfId=${this.id.selfId}, askerDialogId=${top.askerDialogId})`);
        }
        else if (!resolvedAskerDialog) {
            throw new Error(`SideDialog askerDialog invariant violation: asker missing from root registry ` +
                `(rootId=${mainDialog.id.rootId}, selfId=${this.id.selfId}, askerDialogId=${top.askerDialogId})`);
        }
        this.mainDialog.registerDialog(this);
    }
    get assignmentFromAsker() {
        return getSideDialogAskerStackCurrentAssignment(this.askerStack);
    }
    set assignmentFromAsker(assignment) {
        const nextFrame = buildSideDialogAskerStack({
            askerDialogId: assignment.askerDialogId,
            assignment,
        }).askerStack[0];
        if (!nextFrame) {
            throw new Error(`SideDialog assignment stack invariant violation: empty generated frame`);
        }
        this.askerStack = {
            askerStack: [...this.askerStack.askerStack, nextFrame],
        };
    }
    get askerDialog() {
        const askerDialogId = getSideDialogAskerStackTop(this.askerStack).askerDialogId;
        const resolved = this.mainDialog.lookupDialog(askerDialogId);
        if (!resolved || resolved.id.selfId === this.id.selfId) {
            throw new Error(`SideDialog askerDialog invariant violation: asker missing or self ` +
                `(rootId=${this.mainDialog.id.rootId}, selfId=${this.id.selfId}, askerDialogId=${askerDialogId})`);
        }
        return resolved;
    }
    get status() {
        return this.mainDialog.status;
    }
    setFbrConclusionToolsEnabled(enabled) {
        this._fbrConclusionToolsEnabled = enabled;
    }
    areFbrConclusionToolsEnabled() {
        return this._fbrConclusionToolsEnabled;
    }
    /**
     * Create a sideDialog under the same main dialog tree.
     * The new sideDialog's effective askerDialog is resolved via AssignmentFromAsker.askerDialogId.
     */
    async createSideDialog(targetAgentId, mentionList, tellaskContent, options) {
        return await this.dlgStore.createSideDialog(this, targetAgentId, mentionList, tellaskContent, options);
    }
}
exports.SideDialog = SideDialog;
/**
 * MainDialog - The main/main dialog that can create and manage sideDialogs.
 * Uses in-memory registries for O(1) dialog and Type B lookup.
 */
class MainDialog extends Dialog {
    _status = 'running';
    // Tracks all dialogs in this dialog tree for O(1) lookup
    _localRegistry = new Map();
    // Tracks Type-B registered sideDialogs by agentId!sessionSlug
    _sideDialogRegistry = new Map();
    constructor(dlgStore, taskDocPath, id, agentId, initialState) {
        super(dlgStore, taskDocPath, id, agentId, initialState);
        this.registerDialog(this);
    }
    get status() {
        return this._status;
    }
    setPersistenceStatus(status) {
        this._status = status;
    }
    /**
     * Register a dialog (self or sideDialog) in the local registry.
     */
    registerDialog(dialog) {
        this._localRegistry.set(dialog.id.selfId, dialog);
    }
    /**
     * Lookup a dialog by selfId in the local registry.
     */
    lookupDialog(selfId) {
        return this._localRegistry.get(selfId);
    }
    /**
     * Get all registered dialogs in this dialog tree.
     */
    getAllDialogs() {
        return Array.from(this._localRegistry.values());
    }
    /**
     * Remove a dialog from the local registry.
     */
    unregisterDialog(selfId) {
        this._localRegistry.delete(selfId);
    }
    /**
     * Generate a registry key from agentId and sessionSlug.
     */
    static makeSideDialogKey(agentId, sessionSlug) {
        return `${agentId}!${sessionSlug}`;
    }
    /**
     * Register a Type-B sideDialog for resumption.
     */
    registerSideDialog(sideDialog) {
        if (!sideDialog.sessionSlug) {
            return;
        }
        const key = MainDialog.makeSideDialogKey(sideDialog.agentId, sideDialog.sessionSlug);
        this._sideDialogRegistry.set(key, sideDialog);
        this.registerDialog(sideDialog);
    }
    /**
     * Lookup a Type-B sideDialog by agentId and sessionSlug.
     */
    lookupSideDialog(agentId, sessionSlug) {
        const key = MainDialog.makeSideDialogKey(agentId, sessionSlug);
        return this._sideDialogRegistry.get(key);
    }
    /**
     * Remove a Type-B sideDialog from registry.
     */
    unregisterSideDialog(agentId, sessionSlug) {
        const key = MainDialog.makeSideDialogKey(agentId, sessionSlug);
        const sideDialog = this._sideDialogRegistry.get(key);
        if (sideDialog) {
            this._localRegistry.delete(sideDialog.id.selfId);
            return this._sideDialogRegistry.delete(key);
        }
        return false;
    }
    /**
     * Get all registered sideDialogs.
     */
    getRegisteredSideDialogs() {
        return Array.from(this._sideDialogRegistry.values());
    }
    /**
     * Create a new sideDialog for autonomous tellask Side Dialog work.
     */
    async createSideDialog(targetAgentId, mentionList, tellaskContent, options) {
        return await this.dlgStore.createSideDialog(this, targetAgentId, mentionList, tellaskContent, options);
    }
    /**
     * Save sideDialog registry to disk (registry.yaml).
     */
    async saveSideDialogRegistry() {
        const entries = Array.from(this._sideDialogRegistry.entries()).map(([key, sideDialog]) => ({
            key,
            sideDialogId: sideDialog.id,
            agentId: sideDialog.agentId,
            sessionSlug: sideDialog.sessionSlug,
        }));
        await this.dlgStore.saveSideDialogRegistry(this, this.id, entries, this.status);
    }
    /**
     * Load sideDialog registry from disk (registry.yaml).
     */
    async loadSideDialogRegistry() {
        await this.dlgStore.loadSideDialogRegistry(this, this.status);
    }
}
exports.MainDialog = MainDialog;
/**
 * The UI showing a dialog in realtime
 */
class DialogStore {
    /**
     * almost certainly, the subclass should override this method to be (sub)dialog structure aware
     *
     * impl here serves for demo purpose only
     *
     * @param askerDialog
     * @param targetAgentId
     * @param mentionList
     * @param tellaskContent
     * @returns
     */
    async createSideDialog(askerDialog, targetAgentId, mentionList, tellaskContent, options) {
        const generatedId = (0, id_2.generateDialogID)();
        const mainDialog = askerDialog instanceof MainDialog
            ? askerDialog
            : askerDialog instanceof SideDialog
                ? askerDialog.mainDialog
                : (() => {
                    throw new Error(`createSideDialog invariant violation: unsupported asker type (${askerDialog.constructor.name})`);
                })();
        const sideDialogId = new DialogID(generatedId, mainDialog.id.rootId);
        const assignment = {
            callName: options.callName,
            mentionList,
            tellaskContent,
            originMemberId: options.originMemberId,
            askerDialogId: options.askerDialogId,
            callId: options.callId,
            callSiteCourse: options.callSiteCourse,
            callSiteGenseq: options.callSiteGenseq,
            collectiveTargets: options.collectiveTargets,
            effectiveFbrEffort: options.effectiveFbrEffort,
        };
        return new SideDialog(this, mainDialog, askerDialog.taskDocPath, sideDialogId, targetAgentId, buildSideDialogAskerStack({
            askerDialogId: options.askerDialogId,
            assignment,
        }), options.sessionSlug);
    }
    /**
     * Receive and handle LLM generation streams (Markdown stream + tellask call stream)
     */
    /**
     * Notify start of LLM generation lifecycle (generating_start_evt)
     */
    async notifyGeneratingStart(_dialog, _msgId) {
        return [];
    }
    /**
     * Notify end of LLM generation lifecycle (generating_finish_evt)
     */
    async notifyGeneratingFinish(_dialog, _contextHealth, _llmGenModel) { }
    // Explicit phase notifications (driver-driven)
    thinkingStart(_dialog) { }
    thinkingChunk(_dialog, _chunk) { }
    thinkingFinish(_dialog, _reasoning, _provider_data) { }
    markdownStart(_dialog) { }
    markdownChunk(_dialog, _chunk) { }
    markdownFinish(_dialog) { }
    // Saying streaming methods (different from markdown)
    sayingStart(_dialog) { }
    sayingChunk(_dialog, _chunk) { }
    sayingFinish(_dialog) { }
    async receiveFuncResult(_dialog, _funcResult) { }
    async receiveTellaskResult(_dialog, _funcResult) { }
    async receiveTellaskCarryover(_dialog, _result) { }
    async updateQuestions4Human(_dialog, _questions) { }
    /**
     * Load Questions for Human state from storage
     */
    async loadQuestions4Human(_dialogId, _status) {
        return [];
    }
    async loadDialogLatest(_dialogId, _status) {
        return null;
    }
    async loadDialogMetadata(_dialogId, _status) {
        return null;
    }
    async loadSideDialogAssignmentFromAsker(_dialogId, _status) {
        return null;
    }
    async loadActiveCalleeDispatches(_dialogId, _status) {
        return [];
    }
    async loadActiveCallees(_dialogId, _status) {
        return { batches: [] };
    }
    async saveSideDialogRegistry(_dialog, _mainDialogId, _entries, _status) { }
    async loadSideDialogRegistry(_mainDialog, _status) { }
    /**
     * Clear Questions for Human state in storage
     */
    async clearQuestions4Human(_dialog) { }
    // Tellask-special call lifecycle methods
    async callingStart(_dialog, _payload) { }
    async webSearchCall(_dialog, _payload) { }
    async nativeToolCall(_dialog, _payload) { }
    async toolResultImageIngest(_dialog, _payload) { }
    async userImageIngest(_dialog, _payload) { }
    /**
     * Load current course number from persisted metadata
     * This method should be implemented by subclasses to read from storage
     */
    async loadCurrentCourse(_dialogId) {
        // Default implementation returns 1
        return 1;
    }
    /**
     * Get next sequence number for generation
     * This method should be implemented by subclasses for sequence allocation
     */
    async getNextSeq(_dialogId, _course) {
        // Default implementation returns 1
        return 1;
    }
    /**
     * Persist reminders to storage (event emission handled by processReminderUpdates)
     */
    async persistReminders(_dialog, _reminders) { }
    /**
     * Persist a user message to storage
     */
    async persistUserMessage(_dialog, _content, _msgId, _grammar, _origin, _userLanguageCode, _q4hAnswerCallId, _tellaskReplyDirective, _contentItems) { }
    async appendTellaskReplyResolution(_dialog, _payload) { }
    /**
     * Persist an assistant message to storage
     */
    async persistAgentMessage(_dialog, _content, _genseq, _type, _provider_data, _reasoning) { }
    /**
     * Persist an assistant markdown message that must be visible in UI + persistence,
     * but must never be injected into LLM context.
     */
    async persistUiOnlyMarkdown(_dialog, _content, _genseq) { }
    /**
     * Persist a function call to storage
     */
    async persistFunctionCall(_dialog, _id, _name, _rawArgumentsText, _genseq, _rawId) { }
    async persistTellaskCall(_dialog, _id, _name, _rawArgumentsText, _genseq, _options) { }
    /**
     * Start a new course in storage
     */
    async startNewCourse(_dialog, _newCoursePrompt) { }
    async persistPendingRuntimePrompt(_dialog, _prompt) { }
    /**
     * Handle stream error
     */
    async streamError(_dialog, _error) { }
}
exports.DialogStore = DialogStore;
