import { randomUUID } from "node:crypto";
import { ensureOutDir } from "../artifacts/artifacts.js";
import { buildFileInstructions, outboxPath, stageFile, } from "../artifacts/attachments.js";
import { CODEX_AGENT_CAPABILITIES, agentLabel, agentReasoningLabel, agentReasoningOptions, isAgentId, } from "../agents/shared/agent.js";
import { respondToExternalApproval } from "../agents/shared/agent-approval.js";
import { listAgentAdapterDescriptors } from "../agents/shared/agent-adapter.js";
import { AgentUpdateManager } from "../agents/shared/agent-updates.js";
import { createAgentSessionService, enabledAgents } from "../agents/shared/agent-factory.js";
import { AuditLogStore } from "../access/audit-log.js";
import { BotPreferencesStore } from "../state/bot-preferences.js";
import { SessionNameStore } from "../state/session-names.js";
import { ChannelCommandService } from "../channels/shared/channel-command-service.js";
import { ChannelTurnService } from "../channels/shared/channel-turn-service.js";
import { activeSessionSourceForContextKey, ChannelMirrorRegistry } from "../channels/shared/channel-mirror-registry.js";
import { listThreads as listCodexThreads } from "../agents/codex/codex-state.js";
import { clearLogFile, getAgentUpdateLogPath, getConnectorHealth, getConnectorLogPath, getUpdateLogPath, getVersionChecks, readConnectorState, readFormattedLogTail, spawnConnectorRestart, spawnSelfUpdate } from "../support/operations.js";
import { PromptStore, toPromptEnvelope } from "../state/prompt-store.js";
import { UnifiedJobStore } from "../state/job-store.js";
import { WorkflowStore } from "../state/workflow-store.js";
import { QueuePlanStore } from "../state/queue-plan-store.js";
import { MetricsHistoryStore } from "../state/metrics-history-store.js";
import { RelayWorkflowService } from "./relay-workflow-service.js";
import { PluginService } from "../plugins/plugin-service.js";
import { PluginCollectorScheduler } from "../plugins/plugin-collector-scheduler.js";
import { runPeerWorkflowPromptStep } from "./relay-peer-workflow.js";
import {} from "./metrics.js";
import { RelayArtifactService } from "./relay-artifact-service.js";
import { RelayAuthService } from "./relay-auth-service.js";
import { RelayExternalActivityMonitor } from "./relay-external-activity-monitor.js";
import { RelayQueueService } from "./relay-queue-service.js";
import { RuntimeSnapshotCache } from "./runtime-cache.js";
import { activeSessionPriority, activityToUnifiedJob, agentUpdateStatusToUnified, dedupeJobs, hostLoginCommand, hostLogoutCommand, isSessionBusyWithExternalSnapshot, isPromptTerminalActivity, normalizeMimeType, promptActivityToUnifiedJob, shouldRefreshActiveSessions, taskToUnifiedJob, uploadFileDtos, } from "./relay-runtime-helpers.js";
import { RelayDashboardService } from "./relay-dashboard-service.js";
import { startAdaptiveExternalMonitor } from "./relay-external-monitor-scheduler.js";
import {} from "../observability/observability-registry.js";
import { startRuntimeMetricsHistory } from "./relay-metrics-history.js";
import { createRuntimePluginService } from "./relay-plugin-service.js";
import { capabilitiesOf } from "../channels/shared/bot-rendering.js";
import { renderSessionInfoPlain, renderSessionUsageRows } from "../channels/shared/session-format.js";
import { SessionLockStore } from "../access/session-locks.js";
import { SessionRegistry } from "../state/session-registry.js";
import { createSessionWorktreeStore, SessionWorktreeService } from "../worktrees/worktree-service.js";
import { createSupportBundle } from "../support/support-bundle.js";
import { transcribeAudio } from "../artifacts/voice.js";
import { WebActivityStore, WebChatStore, } from "../web/web-state.js";
import { evaluateWorkspacePolicy, filterAllowedWorkspaces } from "../core/workspace-policy.js";
import { relayRuntimeSubscribe, relayRuntimeSnapshot, relayRuntimeStatus, relayRuntimeBootstrapStatus, relayRuntimeVersion, relayRuntimeDiagnostics, relayRuntimeAdapterHealth, relayRuntimePermissions, relayRuntimeMetrics, relayRuntimeObservability, relayRuntimeAudit, relayRuntimeAuditPage, relayRuntimeSupportBundle, relayRuntimeLogs, relayRuntimeClearLogs, relayRuntimeRestartConnector, relayRuntimeDispose } from "./relay-runtime-dashboard.js";
import { relayRuntimeTrace } from "./relay-runtime-trace.js";
import { relayRuntimeChatAttachment } from "./relay-runtime-chat-attachment.js";
import { relayRuntimeUpdateConnector, relayRuntimeAgentUpdateJobs, relayRuntimeStartAgentUpdate, relayRuntimeAgentUpdateLog, relayRuntimeDeleteAgentUpdateLog, relayRuntimeSendAgentUpdateInput, relayRuntimeCancelAgentUpdate, relayRuntimeTasks, relayRuntimeJobs, relayRuntimeJobLog, relayRuntimeJobAction, relayRuntimeRecordAgentUpdateLifecycle } from "./relay-runtime-updates-jobs.js";
import { relayRuntimeActiveSessions, relayRuntimeGetSession, relayRuntimeListKnownContextMetadata, relayRuntimeDiscoverRunningConnectorSessions, relayRuntimeDiscoverActiveCodexSessions, relayRuntimeExternalActiveSession, relayRuntimeSessionStubForMetadata, relayRuntimeCapabilitiesForAgent, relayRuntimeActiveSessionKey, relayRuntimePreferredActiveSession, relayRuntimeRecordActivity, relayRuntimeAppendActivity, relayRuntimeEnrichActivityInput, relayRuntimeEnrichActivityEvent, relayRuntimeEnrichActivityFields, relayRuntimeAppendAudit, relayRuntimeUpdateCurrentProgress, relayRuntimeAddCurrentTool, relayRuntimeBroadcastQueue, relayRuntimeBroadcastStatus, relayRuntimeBroadcast, relayRuntimeScheduleActiveSessionsBroadcast, relayRuntimePublicInfo } from "./relay-runtime-active-sessions.js";
import { relayRuntimeLocks, relayRuntimeLockWebSession, relayRuntimeUnlockWebSession, relayRuntimeControlOptions, relayRuntimeAuthStatus, relayRuntimeLogin, relayRuntimeLogout, relayRuntimeChatHistory, relayRuntimeChatHistoryPage, relayRuntimeWebMirrorPreference, relayRuntimeSessionDetail, relayRuntimeSetSessionName, relayRuntimeClearChatHistory, relayRuntimeActivity, relayRuntimeActivityPage, relayRuntimeRetry, relayRuntimeSync, relayRuntimeListModels, relayRuntimeSetAgent, relayRuntimeNewSession, relayRuntimeSwitchSession, relayRuntimeAttachSession, relayRuntimeSetModel, relayRuntimeSetReasoningEffort, relayRuntimeSetFastMode, relayRuntimeSetLaunchProfile, relayRuntimeHandback, relayRuntimeAbort, relayRuntimeGetControlSession, relayRuntimeCliPathOptions } from "./relay-runtime-sessions.js";
import { relayRuntimeFilteredSessions, relayRuntimeListSessions, relayRuntimeListSessionsPage } from "./relay-runtime-session-list.js";
import { relayRuntimeCleanupSessionWorktrees, relayRuntimeCommitSessionWorktree, relayRuntimeCompareSessionWorktrees, relayRuntimeExportSessionWorktreeIntegrationPatch, relayRuntimeFinalizeSessionWorktreeIntegration, relayRuntimeForkCurrentSessionToWorktree, relayRuntimeIntegrateSessionWorktrees, relayRuntimePreviewSessionWorktreeIntegration, relayRuntimeRemoveSessionWorktree, relayRuntimeSessionWorktreeDiff, relayRuntimeSessionWorktrees, relayRuntimeUpdateSessionWorktreeFromBase } from "./relay-runtime-worktrees.js";
import { relayRuntimeSendPrompt, relayRuntimeSendUploadPrompt, relayRuntimeSendEnvelope, relayRuntimeQueue, relayRuntimeQueuePaused, relayRuntimeQueueAction, relayRuntimeArtifacts, relayRuntimeArtifact, relayRuntimeDeleteArtifact, relayRuntimeCreateArtifactZip, relayRuntimeArtifactPreview, relayRuntimeArtifactDiff, relayRuntimeArtifactUsage, relayRuntimeArtifactCleanupPreview, relayRuntimeArtifactCleanupRun, relayRuntimeEnsureActiveThread, relayRuntimeEnsureIdle, relayRuntimeRunPrompt, relayRuntimeDrainQueue, relayRuntimeUpdateSession } from "./relay-runtime-prompt-queue-artifacts.js";
import { relayRuntimeApproveQueuePlan, relayRuntimeCreateQueuePlan, relayRuntimeDeleteQueuePlan, relayRuntimeEnqueueQueuePlan, relayRuntimeMoveQueuePlan, relayRuntimeQueuePlannerSnapshot, relayRuntimeUpdateQueuePlan } from "./relay-runtime-queue-planner.js";
export const WEB_CONTEXT_KEY = "web:dashboard";
const ACTIVE_CODEX_DISCOVERY_LIMIT = 200;
const ACTIVE_ACTIVITY_TTL_MS = 6 * 60 * 60 * 1000;
const MAX_WEB_SESSION_PAGE_SIZE = 50;
const MAX_CHAT_HISTORY = 250;
export class RelayRuntime {
    config;
    contextKey;
    registry;
    promptStore;
    chatStore;
    activityStore;
    auditStore;
    preferencesStore;
    sessionNameStore;
    lockStore;
    agentUpdates;
    queueService;
    queueDrainOwnerId = `${process.pid}:${randomUUID().slice(0, 8)}`;
    jobStore;
    workflowStore;
    queuePlanStore;
    metricsHistoryStore;
    workflowService;
    pluginService;
    pluginCollectorScheduler;
    artifactService;
    worktreeService;
    mirrorRegistry;
    externalActivityMonitor;
    cache = new RuntimeSnapshotCache();
    dashboardService;
    turnService;
    authService;
    subscribers = new Set();
    agentUpdateActors = new Map();
    agentUpdateStates = new Map();
    externalMonitor;
    activeSessionsBroadcastTimer = null;
    metricsHistoryTimer = null;
    metricsHistoryPoller = null;
    activeSessionsLastBroadcastAt = 0;
    draining = false;
    currentTurnId = null;
    accumulatedText = "";
    currentTurnStartedAt = 0;
    currentProgress = null;
    backgroundServicesEnabled;
    constructor(config, options = {}) {
        this.config = config;
        this.backgroundServicesEnabled = options.backgroundServices ?? true;
        this.contextKey = options.contextKey ?? WEB_CONTEXT_KEY;
        this.worktreeService = new SessionWorktreeService(config, createSessionWorktreeStore(config));
        this.registry = new SessionRegistry(config, {
            fileName: options.registryFileName ?? "web-contexts.json",
            sqliteKey: options.registrySqliteKey ?? "web-contexts",
            worktreeService: this.worktreeService,
        });
        this.promptStore = new PromptStore(config.workspace, config.stateBackend);
        this.chatStore = new WebChatStore(config.workspace, config.stateBackend, MAX_CHAT_HISTORY);
        this.activityStore = new WebActivityStore(config.workspace, config.stateBackend, config.auditMaxEvents);
        this.auditStore = new AuditLogStore(config.workspace, config.stateBackend, config.auditMaxEvents);
        this.preferencesStore = new BotPreferencesStore(config.workspace, config.stateBackend);
        this.sessionNameStore = new SessionNameStore(config.workspace, config.stateBackend);
        this.lockStore = new SessionLockStore(config.workspace, config.stateBackend);
        this.queueService = new RelayQueueService(this.promptStore, this.contextKey);
        this.jobStore = new UnifiedJobStore(config.workspace, config.stateBackend, config.unifiedJobMaxItems);
        this.workflowStore = new WorkflowStore(config.workspace, config.stateBackend);
        this.queuePlanStore = new QueuePlanStore(config.workspace, config.stateBackend);
        this.metricsHistoryStore = new MetricsHistoryStore(config.workspace, config.stateBackend);
        this.pluginService = createRuntimePluginService(this, options.home ?? process.env.NORDRELAY_HOME ?? (process.env.HOME ? `${process.env.HOME}/.nordrelay` : ".nordrelay"));
        this.pluginCollectorScheduler = new PluginCollectorScheduler(this.pluginService);
        this.artifactService = new RelayArtifactService(config);
        this.authService = new RelayAuthService(config);
        this.mirrorRegistry = new ChannelMirrorRegistry(config, this.promptStore);
        this.agentUpdates = new AgentUpdateManager({
            onUpdate: (job) => {
                this.broadcast({ type: "agent_update", job });
                this.recordAgentUpdateLifecycle(job);
            },
        });
        this.externalActivityMonitor = new RelayExternalActivityMonitor({
            config,
            getSession: () => this.getSession(true),
            publicInfo: (session) => this.publicInfo(session),
            queueLength: () => this.queueService.length(),
            mirrorMode: () => this.preferencesStore.get(this.contextKey).mirrorMode ?? this.config.webMirrorMode,
            mirrorMinUpdateMs: () => this.config.webMirrorMinUpdateMs,
            chatStore: this.chatStore,
            chatHistory: () => this.chatHistory(),
            activity: (threadId) => this.activityStore.list({ threadId, limit: 50 }),
            persistWorkspaceArtifactsForTurn: (workspace, turnId, startedAt, provenance) => this.artifactService.persistWorkspaceArtifactsForTurn(workspace, turnId, startedAt, provenance),
            drainQueue: () => this.drainQueue(),
            appendActivity: (input) => this.appendActivity(input),
            broadcast: (event) => this.broadcast(event),
            broadcastStatus: (message, level) => this.broadcastStatus(message, level),
            scheduleActiveSessionsBroadcast: () => this.scheduleActiveSessionsBroadcast(),
        });
        this.dashboardService = new RelayDashboardService({
            config,
            cache: this.cache,
            snapshot: () => this.snapshot(),
            getSession: () => this.getSession(true),
            queuePaused: () => this.queueService.isPaused(),
            externalMirror: () => this.externalActivityMonitor.snapshot(),
            authStatus: (agentId) => this.authStatus(agentId),
            backgroundRefreshEnabled: this.backgroundServicesEnabled,
            isActive: () => this.subscribers.size > 0,
            cliPathOptions: () => this.cliPathOptions(),
        });
        this.dashboardService.startBackgroundRefresh();
        if (this.backgroundServicesEnabled) {
            startRuntimeMetricsHistory(this);
            this.pluginCollectorScheduler.start();
        }
        if (this.backgroundServicesEnabled && config.codexExternalBusyCheckMs > 0) {
            this.externalMonitor = startAdaptiveExternalMonitor({
                baseMs: config.codexExternalBusyCheckMs,
                run: () => this.externalActivityMonitor.monitorSafe(),
            });
        }
        this.turnService = new ChannelTurnService({
            source: "web",
            contextKey: this.contextKey,
            chatStore: this.chatStore,
            artifactService: this.artifactService,
            checkAuth: (info) => this.authService.check(info),
            ensureActiveThread: (session) => this.ensureActiveThread(session),
            updateSession: (session) => this.updateSession(session),
            appendActivity: (input) => this.appendActivity(input),
            appendAudit: (input) => this.appendAudit(input),
            broadcast: (event) => this.broadcast(event),
            chatHistory: () => this.chatHistory(),
            setLastPrompt: (envelope) => this.queueService.setLastPrompt(envelope),
            getCurrentProgress: () => this.currentProgress,
            setCurrentProgress: (progress) => {
                this.currentProgress = progress;
            },
            setCurrentTurn: (id, startedAt, accumulatedText) => {
                this.currentTurnId = id;
                if (startedAt !== undefined)
                    this.currentTurnStartedAt = startedAt;
                if (accumulatedText !== undefined)
                    this.accumulatedText = accumulatedText;
            },
            getCurrentTurnStartedAt: () => this.currentTurnStartedAt,
            getAccumulatedText: () => this.accumulatedText,
            setAccumulatedText: (text) => {
                this.accumulatedText = text;
            },
        });
        this.workflowService = new RelayWorkflowService({
            store: this.workflowStore,
            schedulerEnabled: this.backgroundServicesEnabled,
            getSession: (deferThreadStart) => this.getSession(deferThreadStart),
            newSession: (input, actor) => this.newSession(input, actor),
            setAgent: (agentId, actor) => this.setAgent(agentId, actor),
            attachSession: (threadId, actor) => this.attachSession(threadId, actor),
            runPrompt: (session, envelope) => this.runPrompt(session, envelope),
            runPeerPromptStep: (peerId, step, prompt, correlationId, actor) => runPeerWorkflowPromptStep({
                peerId,
                step,
                prompt,
                correlationId,
                actor,
                sourceContextKey: this.contextKey,
            }),
            pluginService: this.pluginService,
            isSessionBusy: (session) => isSessionBusyWithExternalSnapshot(session, this.config, (threadId) => this.activityStore.list({ threadId, limit: 50 })),
            abort: (actor) => this.abort(actor),
            appendActivity: (input) => this.appendActivity(input),
            appendAudit: (input) => this.appendAudit(input),
            upsertJob: (job) => { this.jobStore.upsert(job); },
            broadcastStatus: (message, level) => this.broadcastStatus(message, level),
        });
    }
    subscribe(callback) {
        return relayRuntimeSubscribe(this, callback);
    }
    async snapshot() {
        return relayRuntimeSnapshot(this);
    }
    async status() {
        return relayRuntimeStatus(this);
    }
    async bootstrapStatus() {
        return relayRuntimeBootstrapStatus(this);
    }
    async version(options = {}) {
        return relayRuntimeVersion(this, options);
    }
    updateConnector(actor) {
        return relayRuntimeUpdateConnector(this, actor);
    }
    agentUpdateJobs() {
        return relayRuntimeAgentUpdateJobs(this);
    }
    startAgentUpdate(agentId, operation = "update", actor) {
        return relayRuntimeStartAgentUpdate(this, agentId, operation, actor);
    }
    agentUpdateLog(id) {
        return relayRuntimeAgentUpdateLog(this, id);
    }
    deleteAgentUpdateLog(id, actor) {
        return relayRuntimeDeleteAgentUpdateLog(this, id, actor);
    }
    sendAgentUpdateInput(id, input, actor) {
        return relayRuntimeSendAgentUpdateInput(this, id, input, actor);
    }
    cancelAgentUpdate(id, actor) {
        return relayRuntimeCancelAgentUpdate(this, id, actor);
    }
    async diagnostics() {
        return relayRuntimeDiagnostics(this);
    }
    async refreshVoiceDiagnostics() {
        return this.dashboardService.refreshVoiceDiagnostics();
    }
    async adapterHealth() {
        return relayRuntimeAdapterHealth(this);
    }
    permissions() {
        return relayRuntimePermissions(this);
    }
    tasks() {
        return relayRuntimeTasks(this);
    }
    async jobs(options = {}) {
        return relayRuntimeJobs(this, options);
    }
    async jobLog(id) {
        return relayRuntimeJobLog(this, id);
    }
    async jobAction(id, action, actor) {
        return relayRuntimeJobAction(this, id, action, actor);
    }
    async activeSessions() {
        return relayRuntimeActiveSessions(this);
    }
    async metrics() {
        return relayRuntimeMetrics(this);
    }
    observability() {
        return relayRuntimeObservability();
    }
    metricsHistory(limit = 240) {
        return this.metricsHistoryStore.list(limit);
    }
    audit(options = 50) {
        return relayRuntimeAudit(this, options);
    }
    auditPage(options = {}) { return relayRuntimeAuditPage(this, options); }
    async trace(correlationId) { return relayRuntimeTrace(this, correlationId); }
    async supportBundle(actor) { return relayRuntimeSupportBundle(this, actor); }
    locks() { return relayRuntimeLocks(this); }
    lockWebSession(ownerName = "Web dashboard", actor) { return relayRuntimeLockWebSession(this, ownerName, actor); }
    unlockWebSession(actor) { return relayRuntimeUnlockWebSession(this, actor); }
    async controlOptions(agentId) { return relayRuntimeControlOptions(this, agentId); }
    async authStatus(agentId) { return relayRuntimeAuthStatus(this, agentId); }
    async login(agentId, actor) { return relayRuntimeLogin(this, agentId, actor); }
    async logout(agentId, actor) { return relayRuntimeLogout(this, agentId, actor); }
    async chatHistory(limit = 200) { return relayRuntimeChatHistory(this, limit); }
    async chatHistoryPage(options = {}) { return relayRuntimeChatHistoryPage(this, options); }
    async chatAttachment(messageId, attachmentId) { return relayRuntimeChatAttachment(this, messageId, attachmentId); }
    async webMirrorPreference(argument = "", actor) {
        return relayRuntimeWebMirrorPreference(this, argument, actor);
    }
    async sessionDetail(threadId, agentId) { return relayRuntimeSessionDetail(this, threadId, agentId); }
    async setSessionName(threadId, name, agentId, actor) { return relayRuntimeSetSessionName(this, threadId, name, agentId, actor); }
    async clearChatHistory(actor) { return relayRuntimeClearChatHistory(this, actor); }
    activity(options = {}) {
        return relayRuntimeActivity(this, options);
    }
    activityPage(options = {}) {
        return relayRuntimeActivityPage(this, options);
    }
    async retry(actor) { return relayRuntimeRetry(this, actor); }
    async sync(actor) { return relayRuntimeSync(this, actor); }
    async listSessions(limit = 80, query = "", agentId) { return relayRuntimeListSessions(this, limit, query, agentId); }
    async listSessionsPage(page = 1, pageSize = MAX_WEB_SESSION_PAGE_SIZE, query = "", agentId) { return relayRuntimeListSessionsPage(this, page, pageSize, query, agentId); }
    filteredSessions(session, query, limit) { return relayRuntimeFilteredSessions(this, session, query, limit); }
    async listModels() { return relayRuntimeListModels(this); }
    async setAgent(agentId, actor) { return relayRuntimeSetAgent(this, agentId, actor); }
    async newSession(options = {}, actor) {
        return relayRuntimeNewSession(this, options, actor);
    }
    async switchSession(threadId, actor) {
        return relayRuntimeSwitchSession(this, threadId, actor);
    }
    async attachSession(threadId, actor) {
        return relayRuntimeAttachSession(this, threadId, actor);
    }
    async sessionWorktrees() { return relayRuntimeSessionWorktrees(this); }
    async sessionWorktreeDiff(id) { return relayRuntimeSessionWorktreeDiff(this, id); }
    async compareSessionWorktrees(ids) { return relayRuntimeCompareSessionWorktrees(this, ids); }
    async previewSessionWorktreeIntegration(ids) { return relayRuntimePreviewSessionWorktreeIntegration(this, ids); }
    async exportSessionWorktreeIntegrationPatch(ids) { return relayRuntimeExportSessionWorktreeIntegrationPatch(this, ids); }
    async updateSessionWorktreeFromBase(id, actor) { return relayRuntimeUpdateSessionWorktreeFromBase(this, id, actor); }
    async cleanupSessionWorktrees(actor) { return relayRuntimeCleanupSessionWorktrees(this, actor); }
    async commitSessionWorktree(id, message, actor) { return relayRuntimeCommitSessionWorktree(this, id, message, actor); }
    async integrateSessionWorktrees(ids, options = {}, actor) { return relayRuntimeIntegrateSessionWorktrees(this, ids, options, actor); }
    async finalizeSessionWorktreeIntegration(id, options = {}, actor) { return relayRuntimeFinalizeSessionWorktreeIntegration(this, id, options, actor); }
    async forkCurrentSessionToWorktree(options = {}, actor) { return relayRuntimeForkCurrentSessionToWorktree(this, options, actor); }
    async removeSessionWorktree(id, force = false, actor) { return relayRuntimeRemoveSessionWorktree(this, id, force, actor); }
    async setModel(model, actor) { return relayRuntimeSetModel(this, model, actor); }
    async setReasoningEffort(effort, actor) { return relayRuntimeSetReasoningEffort(this, effort, actor); }
    async setFastMode(enabled, actor) { return relayRuntimeSetFastMode(this, enabled, actor); }
    async setLaunchProfile(profileId, actor, options = {}) {
        return relayRuntimeSetLaunchProfile(this, profileId, actor, options);
    }
    async handback(actor) { return relayRuntimeHandback(this, actor); }
    async abort(actor) { return relayRuntimeAbort(this, actor); }
    async respondExternalApproval(approvalId, choice, actor) {
        const session = await this.getSession(true);
        const result = await respondToExternalApproval(session, this.config, approvalId, choice);
        const info = this.publicInfo(session);
        if (result.ok) {
            const updated = this.chatStore.resolveAction({
                threadId: info.threadId ?? undefined,
                actionPrefix: "approval:",
                actionId: approvalId,
                label: externalApprovalChoiceDisplay(choice),
            });
            if (updated > 0) {
                this.broadcast({ type: "chat_history", messages: await this.chatHistory() });
            }
        }
        this.appendActivity({
            source: "web",
            status: result.ok ? "info" : "failed",
            type: "cli_action_required_response",
            threadId: info.threadId,
            workspace: info.workspace,
            agentId: info.agentId,
            actor,
            detail: result.message,
        });
        this.appendAudit({
            action: "command",
            status: result.ok ? "ok" : "failed",
            contextKey: this.contextKey,
            actor,
            agentId: info.agentId,
            threadId: info.threadId,
            workspace: info.workspace,
            description: `External approval ${choice}: ${result.message}`,
        });
        this.broadcast({ type: "status", level: result.ok ? "info" : "warn", message: result.message, at: new Date().toISOString() });
        this.scheduleActiveSessionsBroadcast();
        return result;
    }
    async sendPrompt(text, actor, correlationId) {
        return relayRuntimeSendPrompt(this, text, actor, correlationId);
    }
    async sendUploadPrompt(options, actor) {
        return relayRuntimeSendUploadPrompt(this, options, actor);
    }
    async sendEnvelope(envelope, actor) {
        return relayRuntimeSendEnvelope(this, envelope, actor);
    }
    queue() { return relayRuntimeQueue(this); }
    queuePaused() { return relayRuntimeQueuePaused(this); }
    queueAction(action, id, actor) { return relayRuntimeQueueAction(this, action, id, actor); }
    queuePlanner() { return relayRuntimeQueuePlannerSnapshot(this); }
    async createQueuePlan(input, actor) { return relayRuntimeCreateQueuePlan(this, input, actor); }
    updateQueuePlan(id, input, actor) { return relayRuntimeUpdateQueuePlan(this, id, input, actor); }
    async moveQueuePlan(id, status, actor) { return relayRuntimeMoveQueuePlan(this, id, status, actor); }
    approveQueuePlan(id, actor) { return relayRuntimeApproveQueuePlan(this, id, actor); }
    async enqueueQueuePlan(id, actor) { return relayRuntimeEnqueueQueuePlan(this, id, actor); }
    deleteQueuePlan(id, actor) { return relayRuntimeDeleteQueuePlan(this, id, actor); }
    async artifacts(limit = 20) { return relayRuntimeArtifacts(this, limit); }
    async artifact(turnId) { return relayRuntimeArtifact(this, turnId); }
    async deleteArtifact(turnId, actor) { return relayRuntimeDeleteArtifact(this, turnId, actor); }
    async createArtifactZip(turnId, actor) { return relayRuntimeCreateArtifactZip(this, turnId, actor); }
    async artifactPreview(turnId, relativePath) { return relayRuntimeArtifactPreview(this, turnId, relativePath); }
    async artifactDiff(turnId, relativePath) { return relayRuntimeArtifactDiff(this, turnId, relativePath); }
    async artifactUsage() { return relayRuntimeArtifactUsage(this); }
    async artifactCleanupPreview() { return relayRuntimeArtifactCleanupPreview(this); }
    async artifactCleanupRun(actor) { return relayRuntimeArtifactCleanupRun(this, actor); }
    async logs(target = "connector", options = 100) { return relayRuntimeLogs(this, target, options); }
    clearLogs(target = "connector", actor) { return relayRuntimeClearLogs(this, target, actor); }
    restartConnector(actor) { return relayRuntimeRestartConnector(this, actor); }
    dispose() { return relayRuntimeDispose(this); }
    async getSession(deferThreadStart) { return relayRuntimeGetSession(this, deferThreadStart); }
    listKnownContextMetadata() { return relayRuntimeListKnownContextMetadata(this); }
    discoverRunningConnectorSessions() { return relayRuntimeDiscoverRunningConnectorSessions(this); }
    discoverActiveCodexSessions(knownContexts, preferences) { return relayRuntimeDiscoverActiveCodexSessions(this, knownContexts, preferences); }
    externalActiveSession(meta, knownContexts, preferences) {
        return relayRuntimeExternalActiveSession(this, meta, knownContexts, preferences);
    }
    sessionStubForMetadata(meta, agentId, capabilities) {
        return relayRuntimeSessionStubForMetadata(this, meta, agentId, capabilities);
    }
    capabilitiesForAgent(agentId) {
        return relayRuntimeCapabilitiesForAgent(this, agentId);
    }
    activeSessionKey(session) {
        return relayRuntimeActiveSessionKey(this, session);
    }
    preferredActiveSession(existing, candidate) {
        return relayRuntimePreferredActiveSession(this, existing, candidate);
    }
    async getControlSession(agentId) {
        return relayRuntimeGetControlSession(this, agentId);
    }
    cliPathOptions() {
        return relayRuntimeCliPathOptions(this);
    }
    async ensureActiveThread(session) {
        return relayRuntimeEnsureActiveThread(this, session);
    }
    ensureIdle(session) {
        return relayRuntimeEnsureIdle(this, session);
    }
    async runPrompt(session, envelope) {
        return relayRuntimeRunPrompt(this, session, envelope);
    }
    async drainQueue() {
        return relayRuntimeDrainQueue(this);
    }
    updateSession(session) {
        return relayRuntimeUpdateSession(this, session);
    }
    recordActivity(input) {
        return relayRuntimeRecordActivity(this, input);
    }
    recordAgentUpdateLifecycle(job) {
        return relayRuntimeRecordAgentUpdateLifecycle(this, job);
    }
    appendActivity(input) {
        return relayRuntimeAppendActivity(this, input);
    }
    enrichActivityInput(input) {
        return relayRuntimeEnrichActivityInput(this, input);
    }
    enrichActivityEvent(event, info) {
        return relayRuntimeEnrichActivityEvent(this, event, info);
    }
    enrichActivityFields(event, info) {
        return relayRuntimeEnrichActivityFields(this, event, info);
    }
    appendAudit(input) {
        return relayRuntimeAppendAudit(this, input);
    }
    updateCurrentProgress(patch = {}) {
        return relayRuntimeUpdateCurrentProgress(this, patch);
    }
    addCurrentTool(toolName) {
        return relayRuntimeAddCurrentTool(this, toolName);
    }
    broadcastQueue() {
        return relayRuntimeBroadcastQueue(this);
    }
    broadcastStatus(message, level = "info") {
        return relayRuntimeBroadcastStatus(this, message, level);
    }
    broadcast(event) {
        return relayRuntimeBroadcast(this, event);
    }
    scheduleActiveSessionsBroadcast() {
        return relayRuntimeScheduleActiveSessionsBroadcast(this);
    }
    publicInfo(session, options) { return relayRuntimePublicInfo(this, session, options); }
}
function externalApprovalChoiceDisplay(choice) {
    if (choice === "persist") {
        return "Selected: Proceed and remember";
    }
    if (choice === "no") {
        return "Selected: Deny";
    }
    return "Selected: Proceed";
}
