import type { ObservabilityEvent } from "../../observability/event-bus";
import type { RoutesManifest, RouteSpec } from "../../spec/schema";
import type { BundleManifest } from "../../bundler/types";
import type { DiagnoseReport, DiagnoseCheckResult } from "../../diagnose/types";

export type AgentDevToolsCategory =
  | "hydration"
  | "guard"
  | "contract"
  | "runtime"
  | "release"
  | "agent-tools"
  | "build-broken"
  | "boot-breaking";

export type AgentDevToolsMode = "observe" | "suggest" | "assist" | "approval_required";

export interface AgentDevToolsError {
  id?: string;
  type?: string;
  severity?: string;
  message: string;
  stack?: string;
  url?: string;
  source?: string;
  line?: number;
  column?: number;
  timestamp?: number;
}

export interface AgentStatsInput {
  totalAgents: number;
  totalEvents: number;
  agents: Record<string, {
    toolCalls: number;
    failures: number;
    topTools: Array<{ tool: string; count: number }>;
    avgDuration: number;
    firstSeen: number;
    lastSeen: number;
  }>;
}

export interface AgentDevToolsRequestLike {
  method?: string;
  path?: string;
  status?: number;
  duration?: number;
  timestamp?: number;
  cacheStatus?: string;
}

export interface BuildAgentContextPackInput {
  rootDir: string;
  manifest: RoutesManifest;
  guardEnabled: boolean;
  errors: AgentDevToolsError[];
  requests: AgentDevToolsRequestLike[];
  httpEvents: ObservabilityEvent[];
  mcpEvents: ObservabilityEvent[];
  guardEvents: ObservabilityEvent[];
  agentStats: AgentStatsInput;
  /**
   * Plan 18 P0-2 — additional signals so `pickSituation` can distinguish
   * build-broken / boot-breaking from runtime errors. All three are
   * optional: the handler passes them when collection succeeds and
   * leaves them undefined on environments where the source is missing
   * (fixture roots, tests, fresh project before first build).
   */
  bundleManifest?: BundleManifest | null;
  diagnoseReport?: DiagnoseReport | null;
  changedFiles?: string[];
}

export interface AgentToolRecommendation {
  task: string;
  skill: string;
  mcpTools: string[];
  cliFallback: string;
  useWhen: string;
}

export interface KnowledgeCard {
  id: string;
  title: string;
  category: AgentDevToolsCategory;
  body: string;
  references: string[];
}

export interface PromptSuggestion {
  title: string;
  copyText: string;
  variables: Array<{ name: string; value: string }>;
}

export interface NextSafeAction {
  mode: AgentDevToolsMode;
  title: string;
  reason: string;
  tool?: string;
  command?: string;
  validation: string[];
  risk: "low" | "medium" | "high";
}

export interface AgentContextPack {
  generatedAt: string;
  project: {
    rootDir: string;
    framework: "mandu";
  };
  summary: {
    routes: {
      total: number;
      pages: number;
      apis: number;
      metadata: number;
      islands: number;
      contracts: number;
    };
    guardEnabled: boolean;
    storedErrors: number;
    recentRequests: number;
    recentHttpErrors: number;
    recentMcpEvents: number;
    /**
     * Plan 18 P0-2 — bundle / diagnose / diff summary. Each block is
     * absent when the corresponding signal was not provided.
     */
    bundle?: {
      env: "development" | "production";
      buildTime: string;
      bundleCount: number;
      islandCount: number;
    };
    diagnose?: {
      healthy: boolean;
      errorCount: number;
      warningCount: number;
      total: number;
      failedRules: string[];
    };
    changedFiles?: {
      count: number;
      sample: string[];
    };
  };
  agentStatus: {
    totalAgents: number;
    observedToolCalls: number;
    failures: number;
    topTools: Array<{ tool: string; count: number }>;
    brain: {
      oauth: "unknown";
      statusTool: "mandu.brain.status";
      note: string;
    };
  };
  situation: {
    category: AgentDevToolsCategory;
    severity: "info" | "warn" | "error";
    title: string;
    details: string[];
  };
  toolRecommendations: AgentToolRecommendation[];
  knowledgeCards: KnowledgeCard[];
  prompt: PromptSuggestion;
  nextSafeAction: NextSafeAction;
}

const TOOL_ROUTER: Record<AgentDevToolsCategory, AgentToolRecommendation> = {
  hydration: {
    task: "Hydration or client island work",
    skill: "mandu-hydration",
    mcpTools: ["mandu.island.list", "mandu.build.status", "mandu.hydration.set"],
    cliFallback: "bun run build",
    useWhen: "Hydration mismatch, island bundle, client slot, or browser runtime symptoms are present.",
  },
  guard: {
    task: "Architecture boundary work",
    skill: "mandu-guard-guide",
    mcpTools: ["mandu.guard.check", "mandu.guard.explain", "mandu.brain.checkImport"],
    cliFallback: "bun run typecheck",
    useWhen: "Imports, layer boundaries, file placement, or guard violations are relevant.",
  },
  contract: {
    task: "API contract and route schema work",
    skill: "mandu-create-api",
    mcpTools: ["mandu.contract.list", "mandu.contract.validate", "mandu.contract.create"],
    cliFallback: "bun test",
    useWhen: "API routes, request/response schema, generated handlers, or OpenAPI output are involved.",
  },
  runtime: {
    task: "Runtime diagnosis",
    skill: "mandu-debug",
    mcpTools: ["mandu.brain.doctor", "mandu.ai.brief", "mandu.test.smart"],
    cliFallback: "bun test",
    useWhen: "HTTP 4xx/5xx, request correlation, runtime exceptions, or failing tests are visible.",
  },
  release: {
    task: "Release confidence",
    skill: "mandu-release",
    mcpTools: ["mandu.build.status", "mandu.contract.validate", "mandu.test.precommit"],
    cliFallback: "bun run lint && bun run typecheck && bun test",
    useWhen: "The session is clean enough to validate and package changes.",
  },
  "agent-tools": {
    task: "Agent tool selection",
    skill: "mandu-agent-workflow",
    mcpTools: ["mandu.ai.brief", "mandu.brain.status", "mandu.watch.status"],
    cliFallback: "bun run lint",
    useWhen: "The next step is unclear, an agent skipped Mandu tools, or a supervised coding session is starting.",
  },
  "build-broken": {
    task: "Build artifact recovery",
    skill: "mandu-debug",
    mcpTools: ["mandu.build.status", "mandu.build", "mandu.diagnose"],
    cliFallback: "bun run build",
    useWhen: "Bundle manifest is missing, stale, or the diagnose pipeline reports a broken build artifact.",
  },
  "boot-breaking": {
    task: "Install or boot-time failure",
    skill: "mandu-debug",
    mcpTools: ["mandu.diagnose", "mandu.brain.doctor", "mandu.brain.checkImport"],
    cliFallback: "bun install",
    useWhen: "Diagnose surfaces nested_internal_core, package_export_gaps, or other install-time regressions (e.g. #260/#261).",
  },
};

function routeSummary(manifest: RoutesManifest): AgentContextPack["summary"]["routes"] {
  const routes = manifest.routes;
  return {
    total: routes.length,
    pages: routes.filter((route) => route.kind === "page").length,
    apis: routes.filter((route) => route.kind === "api").length,
    metadata: routes.filter((route) => route.kind === "metadata").length,
    islands: routes.filter((route) => !!route.clientModule).length,
    contracts: routes.filter((route) => !!route.contractModule).length,
  };
}

function readStatus(event: ObservabilityEvent | AgentDevToolsRequestLike): number {
  const fromData = "data" in event && typeof event.data?.status === "number"
    ? event.data.status
    : undefined;
  return fromData ?? ("status" in event && typeof event.status === "number" ? event.status : 0);
}

function readPath(event: ObservabilityEvent | AgentDevToolsRequestLike): string {
  if ("data" in event) {
    const path = event.data?.path ?? event.data?.url;
    if (typeof path === "string") return path;
  }
  return "path" in event && typeof event.path === "string" ? event.path : "";
}

function textContains(value: string | undefined, patterns: RegExp[]): boolean {
  if (!value) return false;
  return patterns.some((pattern) => pattern.test(value));
}

function isHydrationError(error: AgentDevToolsError): boolean {
  const patterns = [/hydration/i, /hydrate/i, /island/i, /client slot/i, /data-mandu/i];
  return textContains(error.message, patterns)
    || textContains(error.stack, patterns)
    || textContains(error.source, patterns)
    || textContains(error.type, patterns);
}

/**
 * Plan 18 P0-2 — install/boot-time regressions that make the next dev
 * step impossible. Surfaced ahead of every other category because the
 * symptom (`Cannot find module ...`, nested core shadowing) blocks the
 * tools the agent would otherwise use to investigate.
 */
const BOOT_BREAKING_RULES = new Set([
  "nested_internal_core",
  "package_export_gaps",
  "manifest_validation",
]);

function findBootBreakingCheck(report: DiagnoseReport | null | undefined): DiagnoseCheckResult | undefined {
  if (!report) return undefined;
  return report.checks.find(
    (check) => !check.ok && check.severity === "error" && BOOT_BREAKING_RULES.has(check.rule),
  );
}

function findBuildBrokenCheck(report: DiagnoseReport | null | undefined): DiagnoseCheckResult | undefined {
  if (!report) return undefined;
  return report.checks.find(
    (check) => !check.ok && check.severity === "error" && check.rule === "manifest_freshness",
  );
}

function isBundleManifestEmpty(manifest: BundleManifest | null | undefined): boolean {
  if (!manifest) return false; // null/undefined means "not collected", not "empty"
  const hasBundles = Object.keys(manifest.bundles ?? {}).length > 0;
  const hasIslands = Object.keys(manifest.islands ?? {}).length > 0;
  return !hasBundles && !hasIslands;
}

function pickSituation(input: BuildAgentContextPackInput): AgentContextPack["situation"] {
  // Boot-breaking has the highest priority: if the project can't load,
  // the agent's first move should be a recovery command, not a code edit.
  const bootBreaking = findBootBreakingCheck(input.diagnoseReport);
  if (bootBreaking) {
    return {
      category: "boot-breaking",
      severity: "error",
      title: "Install or boot regression detected",
      details: [
        `${bootBreaking.rule}: ${bootBreaking.message}`,
        bootBreaking.suggestion ?? "Run `mandu diagnose` for the copy-pastable fix.",
      ],
    };
  }

  // Build-broken comes next: missing or stale bundle manifest means
  // hydration / island / SSR diagnosis can't be trusted yet.
  const buildBroken = findBuildBrokenCheck(input.diagnoseReport);
  if (buildBroken) {
    return {
      category: "build-broken",
      severity: "error",
      title: "Build artifact is missing or stale",
      details: [
        buildBroken.message,
        buildBroken.suggestion ?? "Run `mandu build` and re-check.",
      ],
    };
  }

  // Bundle manifest absent on a hydrated project is the silent variant
  // of the above — diagnose may not have flagged it yet on a fresh
  // clone, but the supervisor should still hint at the missing build.
  const hydratedRoutes = input.manifest.routes.filter((route) => !!route.clientModule).length;
  if (
    input.bundleManifest !== undefined &&
    isBundleManifestEmpty(input.bundleManifest) &&
    hydratedRoutes > 0
  ) {
    return {
      category: "build-broken",
      severity: "warn",
      title: "Bundle manifest empty but hydration is declared",
      details: [
        `${hydratedRoutes} route(s) declare client hydration but no bundles are emitted.`,
        "Run `mandu build` before opening the page — DevTools will not see island bundles otherwise.",
      ],
    };
  }

  const hydrationError = input.errors.find(isHydrationError);
  if (hydrationError) {
    return {
      category: "hydration",
      severity: "error",
      title: "Hydration issue detected",
      details: [
        hydrationError.message,
        "Start from the island graph and build status before editing UI code.",
      ],
    };
  }

  const failingHttp = [...input.httpEvents, ...input.requests].find((event) => readStatus(event) >= 500);
  if (failingHttp) {
    return {
      category: "runtime",
      severity: "error",
      title: "Recent request failure detected",
      details: [
        `${readStatus(failingHttp)} ${readPath(failingHttp) || "unknown path"}`,
        "Trace the request correlation and reproduce with a targeted test or smoke request.",
      ],
    };
  }

  const guardFailure = input.guardEvents.find((event) => event.severity === "error" || event.severity === "warn");
  if (guardFailure) {
    return {
      category: "guard",
      severity: guardFailure.severity === "error" ? "error" : "warn",
      title: "Architecture guard signal detected",
      details: [
        guardFailure.message,
        "Use the guard toolchain before moving files or changing import paths.",
      ],
    };
  }

  const apiRoutes = input.manifest.routes.filter((route) => route.kind === "api");
  const apiRoutesWithoutContracts = apiRoutes.filter((route) => !route.contractModule);
  if (apiRoutes.length > 0 && apiRoutesWithoutContracts.length > 0) {
    return {
      category: "contract",
      severity: "warn",
      title: "API routes need contract attention",
      details: [
        `${apiRoutesWithoutContracts.length} of ${apiRoutes.length} API routes do not expose a contract module.`,
        "Prefer contract-aware changes so agents can validate request and response shape.",
      ],
    };
  }

  if (input.agentStats.totalEvents === 0) {
    return {
      category: "agent-tools",
      severity: "info",
      title: "No MCP tool usage observed yet",
      details: [
        "Start the session with an AI brief and brain status check.",
        "Record the selected skill, selected MCP tools, fallback reason, changed files, and validation in the agent report.",
      ],
    };
  }

  return {
    category: "release",
    severity: "info",
    title: "Session is ready for confidence checks",
    details: [
      "No high-priority runtime, guard, or hydration signal is currently visible.",
      "Run release confidence checks before shipping broader changes.",
    ],
  };
}

function buildKnowledgeCards(
  input: BuildAgentContextPackInput,
  situation: AgentContextPack["situation"],
): KnowledgeCard[] {
  const summary = routeSummary(input.manifest);
  const cards: KnowledgeCard[] = [
    {
      id: "mcp-first",
      title: "MCP first, CLI second",
      category: "agent-tools",
      body: "Agents should select the Mandu skill and MCP tool for the task domain before falling back to Bun or shell commands.",
      references: ["docs/guides/07_agent_workflow.md", "AGENTS.md"],
    },
    {
      id: "brain-status",
      title: "Brain status is explicit",
      category: "agent-tools",
      body: "Kitchen cannot infer cloud OAuth state from the browser. Ask the MCP layer for mandu.brain.status when LLM-assisted doctor or heal output matters.",
      references: ["packages/mcp/src/tools/brain.ts"],
    },
  ];

  if (summary.islands > 0 || situation.category === "hydration") {
    cards.push({
      id: "hydration-map",
      title: "Inspect islands before editing UI",
      category: "hydration",
      body: "Hydration changes should start from route island inventory and current build status, then narrow to the affected client slot.",
      references: ["packages/mcp/src/tools/hydration.ts", "docs/guides/07_agent_workflow.md"],
    });
  }

  if (input.guardEnabled || situation.category === "guard") {
    cards.push({
      id: "guard-boundaries",
      title: "Guard preserves architecture",
      category: "guard",
      body: "Boundary fixes should explain the violated rule and verify imports with guard tools before typecheck.",
      references: ["packages/mcp/src/tools/guard.ts", "packages/core/src/guard"],
    });
  }

  if (summary.apis > 0 || situation.category === "contract") {
    cards.push({
      id: "contract-confidence",
      title: "Contracts make API edits agent-safe",
      category: "contract",
      body: "API edits should keep route contracts synchronized and use contract validation before broad tests.",
      references: ["packages/mcp/src/tools/contract.ts", "packages/core/src/kitchen/api/contract-api.ts"],
    });
  }

  if (situation.category === "build-broken") {
    cards.push({
      id: "build-first",
      title: "Bundle manifest is the source of truth",
      category: "build-broken",
      body: "Hydration / island / DevTools all read .mandu/manifest.json. When it is missing or stale, every downstream signal is unreliable — rebuild first, diagnose second.",
      references: ["packages/core/src/bundler/types.ts", "packages/core/src/diagnose/checks.ts"],
    });
  }

  if (situation.category === "boot-breaking") {
    cards.push({
      id: "boot-recovery",
      title: "Install regressions need install-time recovery",
      category: "boot-breaking",
      body: "nested_internal_core and package_export_gaps shadow module resolution. Code edits won't help until node_modules is reconciled — let mandu diagnose print the exact rm/install command.",
      references: ["packages/core/src/diagnose/checks.ts", "docs/issues (#260, #261)"],
    });
  }

  return cards.slice(0, 6);
}

function buildPrompt(
  situation: AgentContextPack["situation"],
  recommendation: AgentToolRecommendation,
  input: BuildAgentContextPackInput,
): PromptSuggestion {
  const summary = routeSummary(input.manifest);
  const routeHint = pickRouteHint(input.manifest.routes, situation.category);

  const lines: string[] = [
    "You are working inside a Mandu agent-native project.",
    `Task domain: ${situation.category}`,
    `Selected skill: ${recommendation.skill}`,
    `MCP tools to try first: ${recommendation.mcpTools.join(", ")}`,
    `Fallback command only if MCP is unavailable: ${recommendation.cliFallback}`,
    `Current signal: ${situation.title}`,
    `Details: ${situation.details.join(" | ")}`,
    `Route hint: ${routeHint}`,
    `Project shape: ${summary.pages} pages, ${summary.apis} APIs, ${summary.islands} islands, ${summary.contracts} contracts.`,
  ];

  // Plan 18 P0-2 — surface build / diagnose / diff signals into the
  // prompt so the agent does not need a second tool call to learn the
  // environment shape.
  const bundle = summarizeBundleManifest(input.bundleManifest);
  if (bundle) {
    lines.push(
      `Build state: env=${bundle.env}, bundles=${bundle.bundleCount}, islands=${bundle.islandCount}, builtAt=${bundle.buildTime}.`,
    );
  }
  const diag = summarizeDiagnose(input.diagnoseReport);
  if (diag) {
    lines.push(
      diag.healthy
        ? `Diagnose: healthy (${diag.total} checks).`
        : `Diagnose: ${diag.errorCount} error / ${diag.warningCount} warning — failed rules: ${diag.failedRules.join(", ") || "(none listed)"}.`,
    );
  }
  const changed = summarizeChangedFiles(input.changedFiles);
  if (changed && changed.count > 0) {
    lines.push(
      `Changed files (${changed.count}, first ${changed.sample.length}): ${changed.sample.join(", ")}.`,
    );
  }

  lines.push(
    "Before editing: inspect affected files and state the exact tool/skill choice.",
    "After editing: report changed files, validation commands, and any fallback reason.",
  );

  return {
    title: `Prompt for ${situation.category} work`,
    copyText: lines.join("\n"),
    variables: [
      { name: "task_domain", value: situation.category },
      { name: "selected_skill", value: recommendation.skill },
      { name: "route_hint", value: routeHint },
    ],
  };
}

function pickRouteHint(routes: RouteSpec[], category: AgentDevToolsCategory): string {
  if (category === "hydration") {
    return routes.find((route) => route.clientModule)?.pattern ?? "No client island route detected.";
  }
  if (category === "contract") {
    return routes.find((route) => route.kind === "api" && !route.contractModule)?.pattern
      ?? routes.find((route) => route.kind === "api")?.pattern
      ?? "No API route detected.";
  }
  return routes[0]?.pattern ?? "No route detected.";
}

function buildNextSafeAction(
  situation: AgentContextPack["situation"],
  recommendation: AgentToolRecommendation,
): NextSafeAction {
  switch (situation.category) {
    case "hydration":
      return {
        mode: "observe",
        title: "Inspect island inventory",
        reason: "Hydration fixes should begin by locating the exact island and build artifact.",
        tool: "mandu.island.list",
        command: "bun run build",
        validation: ["mandu.build.status", "targeted browser smoke if UI changed"],
        risk: "low",
      };
    case "guard":
      return {
        mode: "suggest",
        title: "Run guard check before moving code",
        reason: "Architecture fixes need rule-level evidence before imports are rewritten.",
        tool: "mandu.guard.check",
        command: "bun run typecheck",
        validation: ["mandu.guard.explain for any violation", "bun run typecheck"],
        risk: "low",
      };
    case "contract":
      return {
        mode: "assist",
        title: "Validate route contracts",
        reason: "API edits are safer when request and response shape are checked first.",
        tool: "mandu.contract.validate",
        command: "bun test",
        validation: ["mandu.contract.list", "mandu.contract.validate", "targeted API tests"],
        risk: "medium",
      };
    case "runtime":
      return {
        mode: "suggest",
        title: "Trace failing request",
        reason: "Runtime failures need the exact request path and correlated events before patching.",
        tool: "mandu.brain.doctor",
        command: "bun test",
        validation: ["targeted repro", "related unit or integration test"],
        risk: "medium",
      };
    case "release":
      return {
        mode: "assist",
        title: "Run confidence gate",
        reason: "No blocking signal is visible, so the next useful step is validation.",
        tool: recommendation.mcpTools[0],
        command: recommendation.cliFallback,
        validation: ["lint", "typecheck", "tests"],
        risk: "low",
      };
    case "agent-tools":
      return {
        mode: "observe",
        title: "Check brain and AI brief",
        reason: "The session has not shown MCP usage yet, so establish context before editing.",
        tool: "mandu.ai.brief",
        command: "bun run lint",
        validation: ["mandu.brain.status", "mandu.ai.brief"],
        risk: "low",
      };
    case "build-broken":
      return {
        mode: "assist",
        title: "Rebuild before any other change",
        reason: "Bundle manifest is missing or stale — DevTools, hydration, and island inventory all read it.",
        tool: "mandu.build",
        command: "bun run build",
        validation: ["mandu.build.status", "mandu.diagnose"],
        risk: "low",
      };
    case "boot-breaking":
      return {
        mode: "observe",
        title: "Run mandu diagnose for the recovery command",
        reason: "An install regression is shadowing module resolution; let diagnose print the exact rm/install fix.",
        tool: "mandu.diagnose",
        command: "bun install",
        validation: ["mandu.diagnose", "bunx @mandujs/mcp --help"],
        risk: "low",
      };
  }
}

function flattenTopTools(agentStats: AgentStatsInput): Array<{ tool: string; count: number }> {
  const counts = new Map<string, number>();
  for (const agent of Object.values(agentStats.agents)) {
    for (const item of agent.topTools) {
      counts.set(item.tool, (counts.get(item.tool) ?? 0) + item.count);
    }
  }
  return Array.from(counts.entries())
    .map(([tool, count]) => ({ tool, count }))
    .sort((a, b) => b.count - a.count)
    .slice(0, 5);
}

function uniqueRecommendations(primary: AgentDevToolsCategory): AgentToolRecommendation[] {
  const order: AgentDevToolsCategory[] = [
    primary,
    "boot-breaking",
    "build-broken",
    "agent-tools",
    "guard",
    "contract",
    "hydration",
    "runtime",
    "release",
  ];
  const seen = new Set<AgentDevToolsCategory>();
  const result: AgentToolRecommendation[] = [];
  for (const category of order) {
    if (seen.has(category)) continue;
    seen.add(category);
    result.push(TOOL_ROUTER[category]);
  }
  return result.slice(0, 4);
}

function summarizeBundleManifest(manifest: BundleManifest | null | undefined): AgentContextPack["summary"]["bundle"] {
  if (!manifest) return undefined;
  return {
    env: manifest.env,
    buildTime: manifest.buildTime,
    bundleCount: Object.keys(manifest.bundles ?? {}).length,
    islandCount: Object.keys(manifest.islands ?? {}).length,
  };
}

function summarizeDiagnose(report: DiagnoseReport | null | undefined): AgentContextPack["summary"]["diagnose"] {
  if (!report) return undefined;
  const failedRules = report.checks.filter((c) => !c.ok).map((c) => c.rule);
  return {
    healthy: report.healthy,
    errorCount: report.errorCount,
    warningCount: report.warningCount,
    total: report.summary.total,
    failedRules,
  };
}

function summarizeChangedFiles(files: string[] | undefined): AgentContextPack["summary"]["changedFiles"] {
  if (!files) return undefined;
  return {
    count: files.length,
    sample: files.slice(0, 8),
  };
}

export function buildAgentContextPack(input: BuildAgentContextPackInput): AgentContextPack {
  const routes = routeSummary(input.manifest);
  const recentHttpErrors = [...input.httpEvents, ...input.requests]
    .filter((event) => readStatus(event) >= 400).length;
  const situation = pickSituation(input);
  const recommendations = uniqueRecommendations(situation.category);
  const primaryRecommendation = recommendations[0] ?? TOOL_ROUTER["agent-tools"];
  const failures = Object.values(input.agentStats.agents)
    .reduce((sum, agent) => sum + agent.failures, 0);

  return {
    generatedAt: new Date().toISOString(),
    project: {
      rootDir: input.rootDir,
      framework: "mandu",
    },
    summary: {
      routes,
      guardEnabled: input.guardEnabled,
      storedErrors: input.errors.length,
      recentRequests: input.requests.length + input.httpEvents.length,
      recentHttpErrors,
      recentMcpEvents: input.mcpEvents.length,
      bundle: summarizeBundleManifest(input.bundleManifest),
      diagnose: summarizeDiagnose(input.diagnoseReport),
      changedFiles: summarizeChangedFiles(input.changedFiles),
    },
    agentStatus: {
      totalAgents: input.agentStats.totalAgents,
      observedToolCalls: input.agentStats.totalEvents,
      failures,
      topTools: flattenTopTools(input.agentStats),
      brain: {
        oauth: "unknown",
        statusTool: "mandu.brain.status",
        note: "OAuth state is owned by the MCP brain layer; call mandu.brain.status for current provider/tier.",
      },
    },
    situation,
    toolRecommendations: recommendations,
    knowledgeCards: buildKnowledgeCards(input, situation),
    prompt: buildPrompt(situation, primaryRecommendation, input),
    nextSafeAction: buildNextSafeAction(situation, primaryRecommendation),
  };
}

export function handleAgentContextRequest(input: BuildAgentContextPackInput): Response {
  return Response.json(buildAgentContextPack(input));
}
