/**
 * Queues embedded-agent session compaction onto the correct command lane.
 */
import { OPENCLAW_EMBEDDED_CONTEXT_ENGINE_HOST } from "../../context-engine/host-compat.js";
import { ensureContextEnginesInitialized } from "../../context-engine/init.js";
import {
  resolveContextEngine,
  resolveContextEngineOwnerPluginId,
} from "../../context-engine/registry.js";
import { buildContextEngineRuntimeSettings } from "../../context-engine/runtime-settings.js";
import type {
  ContextEngine,
  ContextEngineRuntimeContext,
  ContextEngineRuntimeSettings,
  ContextEngineSessionTarget,
} from "../../context-engine/types.js";
import type { CapturedCompactionCheckpointSnapshot } from "../../gateway/session-compaction-checkpoints.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.types.js";
import { requireActivePluginRegistry } from "../../plugins/runtime.js";
import { withPluginRuntimeRegistryScope } from "../../plugins/runtime/gateway-request-scope.js";
import { enqueueCommandInLane } from "../../process/command-queue.js";
import { resolveUserPath } from "../../utils.js";
import { normalizeOptionalAgentRuntimeId } from "../agent-runtime-id.js";
import { resolveAgentDir, resolveDefaultAgentDir, resolveSessionAgentIds } from "../agent-scope.js";
import { isRecoverableNativeHarnessBindingFailure } from "../harness/compaction-recovery.js";
import { maybeCompactAgentHarnessSession } from "../harness/compaction.js";
import { ensureSelectedAgentHarnessPlugin } from "../harness/runtime-plugin.js";
import { isOpenAIProvider } from "../openai-routing.js";
import {
  acquireAgentRunPreparedModelRuntime,
  type PreparedModelRuntimeSnapshot,
} from "../prepared-model-runtime.js";
import { resolveAgentRunSessionTarget } from "../run-session-target.js";
import { materializePreparedRuntimeModel } from "../runtime-plan/materialize-model.js";
import { SessionManager } from "../sessions/index.js";
import { DEFERRED_CONTEXT_ENGINE_COMPACTION_REASON } from "./compact-reasons.js";
import type { CompactEmbeddedAgentSessionParams } from "./compact.types.js";
import { compactionCheckpointStore, persistCompactionCheckpoint } from "./compaction-checkpoint.js";
import { asCompactionHookRunner, runPostCompactionSideEffects } from "./compaction-hooks.js";
import {
  buildEmbeddedCompactionRuntimeContext,
  resolveCompactionContextTokenBudget,
} from "./compaction-runtime-context.js";
import {
  prepareCompactionHarnessAuth,
  resolveCompactionRuntimeSelection,
} from "./compaction-runtime-preparation.js";
import {
  compactContextEngineWithSafetyTimeout,
  resolveCompactionTimeoutMs,
} from "./compaction-safety-timeout.js";
import { resolveContextEngineCompactionSuccessor } from "./compaction-successor.js";
import { resolveContextEngineCapabilities } from "./context-engine-capabilities.js";
import { runContextEngineMaintenance } from "./context-engine-maintenance.js";
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
import { log } from "./logger.js";
import { resolveModelAsync } from "./model.js";
import type { EmbeddedAgentQueueHandle } from "./run-state.js";
import {
  clearActiveEmbeddedRun,
  isEmbeddedAgentRunHandleActive,
  resolveActiveEmbeddedRunHandleSessionId,
  resolveActiveEmbeddedRunHandleSessionIdBySessionFile,
  setActiveEmbeddedRun,
} from "./runs.js";
import type { EmbeddedAgentCompactResult } from "./types.js";

function shouldFallbackAfterHarnessCompaction(
  result: EmbeddedAgentCompactResult | undefined,
): boolean {
  return isRecoverableNativeHarnessBindingFailure(result);
}

function lockedCompactionRuntimeFailure(runtime?: string): EmbeddedAgentCompactResult {
  return {
    ok: false,
    compacted: false,
    reason: runtime
      ? `Model selection is locked to native agent harness "${runtime}", but native compaction is unavailable.`
      : "Model selection is locked but the persisted agent harness is unavailable.",
    failure: { reason: "model_selection_locked" },
  };
}

const DEFERRED_CONTEXT_ENGINE_COMPACTION_SCHEDULE_FAILURE_REASON =
  "failed to schedule background context-engine maintenance";
const MANUAL_COMPACTION_ACTIVE_RUN_REASON =
  "manual compaction unavailable while another embedded run is active";
const COMPACTION_ABORTED_REASON = "compaction aborted";

function createCompactionAbortedResult(): EmbeddedAgentCompactResult {
  return {
    ok: false,
    compacted: false,
    reason: COMPACTION_ABORTED_REASON,
  };
}

function resolveManualCompactionActiveRunSessionId(
  params: CompactEmbeddedAgentSessionParams,
): string | undefined {
  return (
    (isEmbeddedAgentRunHandleActive(params.sessionId) ? params.sessionId : undefined) ??
    (params.sessionKey ? resolveActiveEmbeddedRunHandleSessionId(params.sessionKey) : undefined) ??
    resolveActiveEmbeddedRunHandleSessionIdBySessionFile(params.sessionFile)
  );
}

function shouldDeferOwningContextEngineBudgetCompaction(params: {
  compactParams: CompactEmbeddedAgentSessionParams;
  contextEngine: ContextEngine;
}): boolean {
  // Request-time budget compaction for context-engine-owned transcripts can
  // spend the whole reply preflight budget. Only defer engines that explicitly
  // advertise background turn maintenance, leaving native/current-session
  // harness compaction synchronous.
  return (
    params.compactParams.deferOwningContextEngineCompaction === true &&
    params.compactParams.trigger === "budget" &&
    params.contextEngine.info.ownsCompaction === true &&
    params.contextEngine.info.turnMaintenanceMode === "background" &&
    typeof params.contextEngine.maintain === "function"
  );
}

function buildContextEngineCompactionSessionTarget(
  params: CompactEmbeddedAgentSessionParams,
): ContextEngineSessionTarget {
  const agentId = params.sessionTarget?.agentId ?? params.agentId;
  const sessionKey = params.sessionTarget?.sessionKey ?? params.sessionKey;
  const storePath = params.sessionTarget?.storePath;
  return {
    ...(agentId ? { agentId } : {}),
    sessionId: params.sessionTarget?.sessionId ?? params.sessionId,
    ...(sessionKey ? { sessionKey } : {}),
    ...(storePath ? { storePath } : {}),
    ...(params.sessionTarget?.threadId !== undefined
      ? { threadId: params.sessionTarget.threadId }
      : {}),
  };
}

async function disposeContextEngine(contextEngine: ContextEngine): Promise<void> {
  try {
    await contextEngine.dispose?.();
  } catch (err) {
    log.warn("context engine dispose failed", {
      errorMessage: formatErrorMessage(err),
    });
  }
}

async function deferOwningContextEngineBudgetCompaction(params: {
  compactParams: CompactEmbeddedAgentSessionParams;
  contextEngine: ContextEngine;
  contextEngineRuntimeContext: ContextEngineRuntimeContext;
  contextEngineRuntimeSettings: ContextEngineRuntimeSettings;
}): Promise<EmbeddedAgentCompactResult> {
  let deferredScheduled = false;
  let deferredScheduleFailure: unknown;
  try {
    await runContextEngineMaintenance({
      contextEngine: params.contextEngine,
      sessionId: params.compactParams.sessionId,
      sessionKey: params.compactParams.sessionKey,
      sessionTarget: buildContextEngineCompactionSessionTarget(params.compactParams),
      sessionFile: params.compactParams.sessionFile,
      reason: "turn",
      runtimeContext: params.contextEngineRuntimeContext,
      runtimeSettings: params.contextEngineRuntimeSettings,
      config: params.compactParams.config,
      disposeDeferredContextEngineAfterMaintenance: true,
      onDeferredMaintenance: () => {
        deferredScheduled = true;
      },
      onDeferredMaintenanceFailure: (error) => {
        deferredScheduleFailure = error;
      },
    });
  } catch (err) {
    log.warn("failed to defer context-engine budget compaction", {
      errorMessage: formatErrorMessage(err),
    });
  }

  if (!deferredScheduled || deferredScheduleFailure) {
    log.warn(
      `[compaction] failed to schedule context-engine-owned budget compaction background maintenance ` +
        `(sessionKey=${params.compactParams.sessionKey ?? params.compactParams.sessionId}` +
        `${deferredScheduleFailure ? ` error=${formatErrorMessage(deferredScheduleFailure)}` : ""})`,
    );
    return {
      ok: false,
      compacted: false,
      reason: DEFERRED_CONTEXT_ENGINE_COMPACTION_SCHEDULE_FAILURE_REASON,
      failure: { reason: "deferred_compaction_not_scheduled" },
    };
  }

  log.info(
    `[compaction] deferred context-engine-owned budget compaction to background maintenance ` +
      `(sessionKey=${params.compactParams.sessionKey ?? params.compactParams.sessionId} ` +
      `scheduled=${String(deferredScheduled)})`,
  );
  return {
    ok: true,
    compacted: false,
    reason: DEFERRED_CONTEXT_ENGINE_COMPACTION_REASON,
  };
}

function mergeSecondaryNativeHarnessCompactionDetails(params: {
  details: unknown;
  nativeResult: EmbeddedAgentCompactResult | undefined;
  detailsKey: "codexNativeCompaction" | "nativeHarnessCompaction";
}): unknown {
  if (!params.nativeResult) {
    return params.details;
  }
  const details =
    params.details && typeof params.details === "object" && !Array.isArray(params.details)
      ? (params.details as Record<string, unknown>)
      : params.details === undefined
        ? {}
        : { contextEngine: params.details };
  return {
    ...details,
    [params.detailsKey]: params.nativeResult,
  };
}

/**
 * Compacts a session with lane queueing (session lane + global lane).
 * Use this from outside a lane context. If already inside a lane, use
 * `compactEmbeddedAgentSessionDirect` to avoid deadlocks.
 */
export async function compactEmbeddedAgentSession(
  params: CompactEmbeddedAgentSessionParams,
): Promise<EmbeddedAgentCompactResult> {
  const runtimeTarget = await resolveAgentRunSessionTarget({
    ...params,
    missingSessionKey: "resolve-existing",
  });
  const resolvedParams = {
    ...params,
    agentId: runtimeTarget.agentId,
    sessionId: runtimeTarget.sessionId,
    sessionKey: runtimeTarget.sessionKey,
    sessionTarget: runtimeTarget,
    sessionFile: runtimeTarget.sessionKey,
  };
  if (resolvedParams.trigger !== "manual") {
    return await compactEmbeddedAgentSessionImpl(resolvedParams);
  }
  // Reply operations and embedded handles are separate lifecycle owners. A
  // /compact reply may coexist with this handle, but another embedded writer may not.
  if (resolveManualCompactionActiveRunSessionId(resolvedParams)) {
    return {
      ok: false,
      compacted: false,
      reason: MANUAL_COMPACTION_ACTIVE_RUN_REASON,
      failure: { reason: "active_run" },
    };
  }

  const controller = new AbortController();
  const abortSignal = resolvedParams.abortSignal
    ? AbortSignal.any([resolvedParams.abortSignal, controller.signal])
    : controller.signal;
  const handle: EmbeddedAgentQueueHandle = {
    kind: "embedded",
    queueMessage: async () => {},
    isStreaming: () => true,
    isAborted: () => abortSignal.aborted,
    isCompacting: () => true,
    abort: (reason) => controller.abort(reason ?? "user_abort"),
    cancel: (reason) => controller.abort(reason ?? "user_abort"),
  };
  setActiveEmbeddedRun(
    resolvedParams.sessionId,
    handle,
    resolvedParams.sessionKey,
    resolvedParams.sessionFile,
  );
  try {
    return await compactEmbeddedAgentSessionImpl({
      ...resolvedParams,
      abortSignal,
    });
  } finally {
    clearActiveEmbeddedRun(
      resolvedParams.sessionId,
      handle,
      resolvedParams.sessionKey,
      resolvedParams.sessionFile,
    );
  }
}

async function compactEmbeddedAgentSessionImpl(
  inputParams: CompactEmbeddedAgentSessionParams,
): Promise<EmbeddedAgentCompactResult> {
  if (inputParams.abortSignal?.aborted) {
    return createCompactionAbortedResult();
  }
  const runtimeTarget = await resolveAgentRunSessionTarget({
    ...inputParams,
    missingSessionKey: "resolve-existing",
  });
  const agentIds = resolveSessionAgentIds({
    sessionKey: runtimeTarget.sessionKey,
    config: inputParams.config,
    agentId: runtimeTarget.agentId,
  });
  const params = {
    ...inputParams,
    agentId: runtimeTarget.agentId,
    sessionId: runtimeTarget.sessionId,
    sessionKey: runtimeTarget.sessionKey,
    sessionTarget: runtimeTarget,
    sessionFile: runtimeTarget.sessionKey,
  };
  const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, agentIds.sessionAgentId);
  const resolvedWorkspaceDir = resolveUserPath(params.workspaceDir);
  const runtimeSelection = resolveCompactionRuntimeSelection({
    ...params,
    modelId: params.model,
    boundHarnessRuntime: params.agentHarnessId,
    preparedRuntimePlan: params.runtimePlan,
    selectedHarnessRuntime:
      params.modelSelectionLocked === true
        ? normalizeOptionalAgentRuntimeId(params.agentHarnessId)
        : undefined,
  });
  const lease = await acquireAgentRunPreparedModelRuntime({
    config: params.config ?? {},
    agentId: agentIds.sessionAgentId,
    agentDir,
    inheritedAuthDir: resolveDefaultAgentDir(params.config ?? {}),
    workspaceDir: resolvedWorkspaceDir,
    ...(params.allowGatewaySubagentBinding ? { allowGatewaySubagentBinding: true } : {}),
    runtimePluginSelections: [
      {
        provider: runtimeSelection.provider,
        modelId: runtimeSelection.modelId,
        ...(runtimeSelection.selectedHarnessRuntime
          ? { runtime: runtimeSelection.selectedHarnessRuntime }
          : {}),
        agentId: agentIds.sessionAgentId,
      },
    ],
  });
  const run = async () => {
    ensureContextEnginesInitialized();
    const contextEngine = await resolveContextEngine(params.config, {
      agentDir,
      workspaceDir: resolvedWorkspaceDir,
    });
    let disposeContextEngineOnExit = true;
    try {
      // Retain engine ownership until the queued path settles. Explicit cleanup
      // or accepted background maintenance may release it from this call.
      return await compactResolvedContextEngine(
        params,
        contextEngine,
        agentDir,
        resolvedWorkspaceDir,
        lease.snapshot,
        () => {
          disposeContextEngineOnExit = false;
        },
      );
    } finally {
      if (disposeContextEngineOnExit) {
        await disposeContextEngine(contextEngine);
      }
    }
  };
  try {
    return await withPluginRuntimeRegistryScope(lease.snapshot.pluginRegistry, run);
  } finally {
    lease.release();
  }
}

async function compactResolvedContextEngine(
  params: CompactEmbeddedAgentSessionParams,
  contextEngine: ContextEngine,
  agentDir: string,
  resolvedWorkspaceDir: string,
  preparedModelRuntime: PreparedModelRuntimeSnapshot,
  releaseContextEngineOwnership: () => void,
): Promise<EmbeddedAgentCompactResult> {
  const runtimeTarget = await resolveAgentRunSessionTarget({
    ...params,
    missingSessionKey: "resolve-existing",
  });
  const lockedHarnessRuntime =
    params.modelSelectionLocked === true
      ? normalizeOptionalAgentRuntimeId(params.agentHarnessId)
      : undefined;
  if (
    params.modelSelectionLocked === true &&
    (!lockedHarnessRuntime || lockedHarnessRuntime === "auto")
  ) {
    return lockedCompactionRuntimeFailure();
  }
  // A model lock makes the persisted harness authoritative. Config may select
  // a runtime only for unlocked sessions that have no concrete pin.
  const {
    runtimePolicySessionKey,
    runtimePolicyAgentId,
    selectedHarnessRuntime,
    target: resolvedCompactionTarget,
    runtimeModelAuth: { plan: reusableRuntimeAuthPlan, modelAuth: initialModelAuth },
    provider: ceProvider,
    runtimeProvider: ceRuntimeProvider,
    contextConfigProvider: ceContextConfigProvider,
    modelId: ceModelId,
  } = resolveCompactionRuntimeSelection({
    ...params,
    modelId: params.model,
    boundHarnessRuntime: params.agentHarnessId,
    preparedRuntimePlan: params.runtimePlan,
    selectedHarnessRuntime: params.modelSelectionLocked === true ? lockedHarnessRuntime : undefined,
  });
  const lockedNativeHarness =
    params.modelSelectionLocked === true && selectedHarnessRuntime !== "openclaw";
  const attemptNativeHarnessCompaction = shouldAttemptNativeHarnessCompaction({
    provider: ceProvider,
    nativeHarnessCompaction: resolvedCompactionTarget.nativeHarnessCompaction,
    selectedHarnessRuntime,
  });
  let effectiveRuntimeModel: ProviderRuntimeModel | undefined;
  let preparedHarnessRuntime = selectedHarnessRuntime;
  let preparedParams = params;
  try {
    const preparedStores = preparedModelRuntime.createStores();
    // Ensure the policy-selected harness plugin so selection can pick implicit codex.
    await ensureSelectedAgentHarnessPlugin({
      config: params.config,
      provider: ceProvider,
      modelId: ceModelId,
      agentId: runtimePolicyAgentId,
      sessionKey: runtimePolicySessionKey,
      agentHarnessId: params.agentHarnessId,
      agentHarnessRuntimeOverride: selectedHarnessRuntime,
      workspaceDir: resolvedWorkspaceDir,
      pluginRegistry: requireActivePluginRegistry(),
    });
    const {
      model: ceModel,
      authStorage,
      modelRegistry,
    } = await resolveModelAsync(ceRuntimeProvider, ceModelId, agentDir, params.config, {
      ...initialModelAuth,
      ...preparedStores,
      preparedModelRuntime,
    });
    const ceRuntimeModel = ceModel as ProviderRuntimeModel | undefined;
    // Overrides stay unset when no bound/planned/explicit harness resolved so auth-aware
    // selection can pick the credential-owning harness (codex for ChatGPT OAuth).
    const {
      runtimeAuthPreparation,
      selectedPreparedHarness,
      providerUsesProfileScopedModelMetadata,
    } = await prepareCompactionHarnessAuth({
      ...params,
      provider: ceProvider,
      metadataProvider: ceRuntimeProvider,
      modelId: ceModelId,
      model: ceRuntimeModel,
      reusableRuntimeAuthPlan,
      agentDir,
      workspaceDir: resolvedWorkspaceDir,
      authProfileId: resolvedCompactionTarget.authProfileId,
      runtimePolicyAgentId,
      runtimePolicySessionKey,
      agentHarnessRuntimeOverride: selectedHarnessRuntime,
      convergenceErrorPrefix: "Prepared queued compaction",
    });
    preparedHarnessRuntime = selectedPreparedHarness.id;
    const runtimeAuthPlan = runtimeAuthPreparation.plan;
    effectiveRuntimeModel = await materializePreparedRuntimeModel<ProviderRuntimeModel>({
      plan: runtimeAuthPlan,
      provider: ceProvider,
      modelId: ceModelId,
      config: params.config,
      model: ceRuntimeModel,
      forceResolve:
        providerUsesProfileScopedModelMetadata && Boolean(runtimeAuthPlan.selectedAuthMode),
      resolveModel: async ({ config, authProfileId, authProfileMode }) => {
        const resolved = await resolveModelAsync(ceRuntimeProvider, ceModelId, agentDir, config, {
          authStorage,
          modelRegistry,
          skipAgentDiscovery: true,
          allowBundledStaticCatalogFallback: true,
          preferBundledStaticCatalogTransport: true,
          workspaceDir: resolvedWorkspaceDir,
          authProfileId,
          authProfileMode,
        });
        return { ...resolved, model: resolved.model as ProviderRuntimeModel | undefined };
      },
    });
    preparedParams = {
      ...params,
      provider: ceProvider,
      model: ceModelId,
      agentHarnessId: preparedHarnessRuntime,
      ...(reusableRuntimeAuthPlan
        ? {
            authProfileId: runtimeAuthPlan.forwardedAuthProfileId,
            authProfileIdSource: runtimeAuthPlan.forwardedAuthProfileSource,
            runtimeAuthPlan,
          }
        : {
            // Native compaction resolves this full attempt set itself. Legacy
            // compaction must re-plan too; forwarding one generated plan would
            // collapse cross-route and direct fallback before either dispatch.
            authProfileId: resolvedCompactionTarget.authProfileId,
            authProfileIdSource: resolvedCompactionTarget.authProfileId
              ? params.authProfileIdSource
              : undefined,
            runtimeAuthPlan: undefined,
            runtimePlan: undefined,
          }),
    };
  } catch (err) {
    await disposeContextEngine(contextEngine);
    releaseContextEngineOwnership();
    throw err;
  }
  const contextTokenBudget = resolveCompactionContextTokenBudget({
    config: params.config,
    provider: ceContextConfigProvider,
    modelId: ceModelId,
    model: effectiveRuntimeModel,
    requestedTokenBudget: params.contextTokenBudget,
  });
  const contextEngineRuntimeContext = buildCompactionContextEngineRuntimeContext({
    params: preparedParams,
    agentDir,
    harnessRuntime: preparedHarnessRuntime,
    contextTokenBudget,
    contextEnginePluginId: resolveContextEngineOwnerPluginId(contextEngine),
  });
  const contextEngineRuntimeSettings = buildContextEngineRuntimeSettings({
    contextEngineHost: OPENCLAW_EMBEDDED_CONTEXT_ENGINE_HOST,
    provider: ceProvider,
    requestedModel: params.model,
    resolvedModel: ceModelId,
    selectedContextEngineId: contextEngine.info.id,
    contextEngineSelectionSource: contextEngine.info.id === "legacy" ? "default" : "configured",
    promptTokenBudget: contextTokenBudget,
  });
  const contextEngineOwnsCompaction = contextEngine.info.ownsCompaction === true;
  const harnessResult =
    attemptNativeHarnessCompaction && (!contextEngineOwnsCompaction || lockedNativeHarness)
      ? await maybeCompactAgentHarnessSession({
          ...preparedParams,
          runtimeModel: effectiveRuntimeModel,
          contextEngine,
          contextTokenBudget,
          contextEngineRuntimeContext,
        })
      : undefined;
  if (lockedNativeHarness) {
    return harnessResult ?? lockedCompactionRuntimeFailure(selectedHarnessRuntime);
  }
  if (harnessResult) {
    if (!shouldFallbackAfterHarnessCompaction(harnessResult)) {
      return harnessResult;
    }
    log.warn(
      `native harness compaction could not use its session binding; falling back to context engine: ${harnessResult.reason ?? "unknown"}`,
    );
  }
  if (
    shouldDeferOwningContextEngineBudgetCompaction({
      compactParams: preparedParams,
      contextEngine,
    })
  ) {
    const deferredResult = await deferOwningContextEngineBudgetCompaction({
      compactParams: preparedParams,
      contextEngine,
      contextEngineRuntimeContext,
      contextEngineRuntimeSettings,
    });
    if (deferredResult.ok) {
      releaseContextEngineOwnership();
    }
    return deferredResult;
  }
  const sessionLane = resolveSessionLane(params.sessionKey?.trim() || params.sessionId);
  const globalLane = resolveGlobalLane(params.lane);
  const enqueueGlobal =
    params.enqueue ?? ((task, opts) => enqueueCommandInLane(globalLane, task, opts));
  return await enqueueCommandInLane(sessionLane, () =>
    enqueueGlobal(async () => {
      let checkpointSnapshot: CapturedCompactionCheckpointSnapshot | null | undefined;
      let checkpointSnapshotRetained = false;
      try {
        if (params.abortSignal?.aborted) {
          return createCompactionAbortedResult();
        }
        // When the context engine owns compaction, its compact() implementation
        // bypasses compactEmbeddedAgentSessionDirect (which fires the hooks internally).
        // Fire before_compaction / after_compaction hooks here so plugin subscribers
        // are notified regardless of which engine is active.
        const engineOwnsCompaction = contextEngine.info.ownsCompaction === true;
        checkpointSnapshot = engineOwnsCompaction
          ? await compactionCheckpointStore.captureSnapshot({
              sessionFile: params.sessionFile,
              sessionManager: SessionManager.open(runtimeTarget),
              sessionTarget: runtimeTarget,
            })
          : null;
        const hookRunner = engineOwnsCompaction
          ? asCompactionHookRunner(getGlobalHookRunner())
          : null;
        const hookSessionKey = runtimeTarget.sessionKey;
        const { sessionAgentId } = resolveSessionAgentIds({
          sessionKey: params.sessionKey,
          config: params.config,
          agentId: params.agentId,
        });
        const resolvedMessageProvider = params.messageChannel ?? params.messageProvider;
        const hookCtx = {
          sessionId: params.sessionId,
          agentId: sessionAgentId,
          sessionKey: hookSessionKey,
          workspaceDir: resolvedWorkspaceDir,
          messageProvider: resolvedMessageProvider,
        };
        const runtimeContext = contextEngineRuntimeContext;
        // Engine-owned compaction doesn't load the transcript at this level, so
        // message counts are unavailable. We pass sessionFile so hook subscribers
        // can read the transcript themselves if they need exact counts.
        if (hookRunner?.hasHooks?.("before_compaction") && hookRunner.runBeforeCompaction) {
          try {
            await hookRunner.runBeforeCompaction(
              {
                messageCount: -1,
                sessionFile: params.sessionFile,
              },
              hookCtx,
            );
          } catch (err) {
            log.warn("before_compaction hook failed", {
              errorMessage: formatErrorMessage(err),
            });
          }
        }
        // Bound the plugin-owned compaction with the same finite safety
        // timeout that protects native runtime compaction, and thread the
        // caller's abort signal through, so a slow/hung plugin compact()
        // cannot hang the queued /compact lane indefinitely. A timeout/abort
        // (or any thrown error) is surfaced as a clean { ok: false } result —
        // matching how the run-loop overflow/timeout lanes handle it — instead
        // of throwing a raw rejection at callers that only inspect result.ok.
        let result: Awaited<ReturnType<typeof contextEngine.compact>>;
        try {
          const compactionSessionTarget = buildContextEngineCompactionSessionTarget(params);
          result = await compactContextEngineWithSafetyTimeout(
            contextEngine,
            {
              sessionId: params.sessionId,
              sessionKey: hookSessionKey,
              ...(compactionSessionTarget.agentId
                ? { agentId: compactionSessionTarget.agentId }
                : {}),
              sessionTarget: compactionSessionTarget,
              tokenBudget: contextTokenBudget,
              currentTokenCount: params.currentTokenCount,
              compactionTarget: params.trigger === "manual" ? "threshold" : "budget",
              customInstructions: params.customInstructions,
              force:
                params.force === true ||
                params.forcePreflight === true ||
                params.preflightRequired === true ||
                params.trigger === "manual",
              runtimeContext: {
                ...runtimeContext,
                forceReason:
                  params.forcePreflight === true || params.preflightRequired === true
                    ? "preflight_required"
                    : params.trigger === "manual"
                      ? "manual"
                      : undefined,
                preflightCompactionTrigger: params.preflightCompactionTrigger,
              },
              runtimeSettings: contextEngineRuntimeSettings,
            },
            resolveCompactionTimeoutMs(params.config),
            params.abortSignal,
          );
        } catch (compactErr) {
          log.warn("context-engine compaction failed", {
            errorMessage: formatErrorMessage(compactErr),
          });
          result = {
            ok: false,
            compacted: false,
            reason: formatErrorMessage(compactErr),
          };
        }
        const successor = await resolveContextEngineCompactionSuccessor({
          config: params.config,
          currentSessionFile: params.sessionFile,
          currentTarget: runtimeTarget,
          result,
        });
        const postCompactionSessionId = successor.sessionId;
        const postCompactionSessionFile = successor.sessionFile;
        const postCompactionSessionTarget = successor.sessionTarget;
        if (result.ok && result.compacted) {
          checkpointSnapshotRetained = await persistCompactionCheckpoint({
            config: params.config,
            sessionKey: params.sessionKey,
            sessionId: postCompactionSessionId,
            trigger: params.trigger,
            snapshot: checkpointSnapshot,
            summary: result.result?.summary,
            firstKeptEntryId: result.result?.firstKeptEntryId,
            tokensBefore: result.result?.tokensBefore,
            tokensAfter: result.result?.tokensAfter,
            sessionFile: postCompactionSessionFile,
            sessionTarget: postCompactionSessionTarget,
          });
          await runContextEngineMaintenance({
            contextEngine,
            sessionId: postCompactionSessionId,
            sessionKey: params.sessionKey,
            sessionTarget: buildContextEngineCompactionSessionTarget({
              ...params,
              sessionFile: postCompactionSessionFile,
              sessionId: postCompactionSessionId,
              sessionTarget: postCompactionSessionTarget,
            }),
            sessionFile: postCompactionSessionFile,
            reason: "compaction",
            runtimeContext,
            runtimeSettings: contextEngineRuntimeSettings,
            config: params.config,
          });
        }
        if (engineOwnsCompaction && result.ok && result.compacted) {
          await runPostCompactionSideEffects({
            config: params.config,
            sessionKey: params.sessionKey,
            sessionId: postCompactionSessionId,
            agentId: sessionAgentId,
            sessionFile: postCompactionSessionFile,
          });
        }
        if (
          result.ok &&
          result.compacted &&
          hookRunner?.hasHooks?.("after_compaction") &&
          hookRunner.runAfterCompaction
        ) {
          try {
            const afterHookCtx = {
              ...hookCtx,
              sessionId: postCompactionSessionId,
            };
            await hookRunner.runAfterCompaction(
              {
                messageCount: -1,
                compactedCount: -1,
                tokenCount: result.result?.tokensAfter,
                sessionFile: postCompactionSessionFile,
                ...(postCompactionSessionId !== params.sessionId
                  ? { previousSessionId: params.sessionId }
                  : {}),
              },
              afterHookCtx,
            );
          } catch (err) {
            log.warn("after_compaction hook failed", {
              errorMessage: formatErrorMessage(err),
            });
          }
        }
        let secondaryNativeHarnessCompaction: EmbeddedAgentCompactResult | undefined;
        if (
          engineOwnsCompaction &&
          result.ok &&
          result.compacted &&
          attemptNativeHarnessCompaction
        ) {
          try {
            // The native bridge owns its terminal-event watchdog. Keep this lane held until
            // that bridge settles; an outer timeout would release transcript ownership while
            // the harness could still be compacting the same session.
            secondaryNativeHarnessCompaction = await maybeCompactAgentHarnessSession(
              {
                ...preparedParams,
                sessionId: postCompactionSessionId,
                sessionFile: postCompactionSessionFile,
                runtimeModel: effectiveRuntimeModel,
                contextEngine,
                contextTokenBudget,
                contextEngineRuntimeContext,
              },
              { nativeCompactionRequest: "after_context_engine" },
            );
            if (secondaryNativeHarnessCompaction && !secondaryNativeHarnessCompaction.ok) {
              log.warn(
                "secondary native harness compaction failed after context-engine compaction",
                {
                  reason: secondaryNativeHarnessCompaction.reason,
                },
              );
            }
          } catch (err) {
            secondaryNativeHarnessCompaction = {
              ok: false,
              compacted: false,
              reason: formatErrorMessage(err),
            };
            log.warn("secondary native harness compaction threw after context-engine compaction", {
              errorMessage: formatErrorMessage(err),
            });
          }
        }
        const secondaryNativeDetailsKey =
          normalizeOptionalAgentRuntimeId(preparedHarnessRuntime) === "codex"
            ? "codexNativeCompaction"
            : "nativeHarnessCompaction";
        return {
          ok: result.ok,
          compacted: result.compacted,
          reason: result.reason,
          result: result.result
            ? {
                summary: result.result.summary ?? "",
                firstKeptEntryId: result.result.firstKeptEntryId ?? "",
                tokensBefore: result.result.tokensBefore,
                tokensAfter: result.result.tokensAfter,
                details: mergeSecondaryNativeHarnessCompactionDetails({
                  details: result.result.details,
                  nativeResult: secondaryNativeHarnessCompaction,
                  detailsKey: secondaryNativeDetailsKey,
                }),
                ...(postCompactionSessionId !== params.sessionId
                  ? { sessionId: postCompactionSessionId }
                  : {}),
                ...(postCompactionSessionFile !== params.sessionFile
                  ? { sessionFile: postCompactionSessionFile }
                  : {}),
              }
            : undefined,
        };
      } finally {
        if (!checkpointSnapshotRetained) {
          await compactionCheckpointStore.cleanupSnapshot(checkpointSnapshot);
        }
      }
    }),
  );
}

function shouldAttemptNativeHarnessCompaction(params: {
  provider: string;
  nativeHarnessCompaction?: boolean;
  selectedHarnessRuntime?: string | null;
}): boolean {
  const selectedRuntime = normalizeOptionalAgentRuntimeId(params.selectedHarnessRuntime);
  if (!selectedRuntime || selectedRuntime === "auto" || selectedRuntime === "openclaw") {
    return false;
  }
  return isOpenAIProvider(params.provider) ? params.nativeHarnessCompaction === true : true;
}

function buildCompactionContextEngineRuntimeContext(params: {
  params: CompactEmbeddedAgentSessionParams;
  agentDir: string;
  harnessRuntime?: string;
  contextEnginePluginId?: string;
  contextTokenBudget?: number;
}): ContextEngineRuntimeContext {
  const { sessionAgentId } = resolveSessionAgentIds({
    sessionKey: params.params.sessionKey,
    config: params.params.config,
    agentId: params.params.agentId,
  });
  const { sessionFile: _sessionFile, ...runtimeParams } = params.params;
  return {
    ...runtimeParams,
    sessionTarget: buildContextEngineCompactionSessionTarget(params.params),
    ...buildEmbeddedCompactionRuntimeContext({
      ...params.params,
      agentDir: params.agentDir,
      modelId: params.params.model,
      harnessRuntime: params.harnessRuntime,
    }),
    ...resolveContextEngineCapabilities({
      config: params.params.config,
      sessionKey: params.params.sessionKey,
      agentId: sessionAgentId,
      authProfileId: params.params.authProfileId,
      contextEnginePluginId: params.contextEnginePluginId,
      purpose: "context-engine.compaction",
    }),
    tokenBudget: params.contextTokenBudget,
    currentTokenCount: params.params.currentTokenCount,
  };
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
