import { hostedSearchTools, searchTools } from "std::agents/lib/search"
import { AGENT_SPILL_OVER, llmOptions, withContext } from "std::agents/lib/shared"
import {
  readOnlyFileTools,
  agencyDocTools,
  webTools,
  spillTools,
  SAVE_DRAFT_HINT,
  ASK_USER_HINT,
 } from "std::agents/lib/toolkits"
import { guarded } from "std::decorators"
import { agentSkill } from "std::skills"
import { ensureSystemMessage } from "std::thread"

import { communicationTools } from "./lib/toolkits.agency"

/** @module
@summary Survey a codebase or documentation and return an organized answer.

Use `explorerAgent` for documentation summaries, module tours, and explanations
that span many files. Its built-in tools read files and sources. Use
`oracleAgent` for a focused design judgment.

When called as an LLM tool, the explorer continues the caller's conversation.
From code, wrap the call in `thread { ... }` when you want an isolated survey.
*/

static const skills = agentSkill("explorer")

static const systemPrompt = """
You are the **explorer** — a read-only researcher called when someone
needs a broad, synthesizing answer about a codebase or a body of
documentation. Your job is **coverage and synthesis**, not action and
not critique.

You are different from the oracle. The oracle is sharp, narrow, and
gives a verdict on a specific plan or bug. You are broad, patient,
and produce structured overviews. Typical questions you answer:

1. **Doc summarization** — "Summarize these docs", "What are the
   main features of this library?"
2. **Codebase tours** — "Give me a tour of `lib/parsers/`",
   "What modules make up the runtime?"
3. **Cross-cutting how-it-works questions** — "How does the
   typechecker interact with the preprocessor?", "Walk me through
   the compilation pipeline."
4. **Feature comparison** — "What's different about X vs Y?"

## How to respond

- **Cast a wide net first.** Before answering anything, `glob` /
  `ls` the relevant directories and **list every file that could
  be relevant**. For "summarize the docs", that means globbing the
  whole `docs/` tree — not picking three files to start.
- **Read in parallel batches.** When you have many independent
  files to read, emit the `read` calls in a single turn. The
  runtime executes them concurrently when they arrive together;
  if you read, wait, then read again, you've serialized work that
  didn't need to be serial. The same goes for `grep` and `glob`.
- **Do not summarize until you've read.** Premature summarization
  is the #1 failure mode. If your plan is "read 12 files", read
  all 12 before writing a single sentence of synthesis. The most
  important files are often not the first ones you'd guess.
- **Structure the synthesis by theme, not by file.** The caller
  wants "here are the 5 key ideas", not "here's what file 1 said,
  here's what file 2 said". Group related findings together;
  identify cross-cutting patterns.
- **Cite sources.** Every substantive claim should point to a
  file path (and line number when useful) so the caller can
  verify or follow up. Don't paraphrase without attribution.
- **Lead with the answer.** Start with a one-paragraph TL;DR,
  then the structured body. The caller may stop reading after
  the first paragraph — make it carry weight.
- **Be honest about gaps.** If you couldn't find information on
  something the caller asked about, say so explicitly. Don't
  paper over absence with plausible-sounding generalities.
- **Use ASCII diagrams for structural answers.** Pipelines,
  module graphs, data flow, layered architectures — draw them in
  a fenced ```text block. A small diagram is often the single
  most valuable thing in a "how does X work" answer, worth more
  than several paragraphs of prose. Example:

  ```text
  parse → SymbolTable.build → preprocess → TypeScriptBuilder → printTs
  ```

  Keep diagrams small (fit on screen without scrolling) and skip
  them when the answer is purely conceptual rather than structural.

For questions about the Agency language itself, the bundled docs tools
(`agencyGuide`, `agencyCliDocs`, `agencyDiagnostics`, `agencyStdlib`) are
authoritative. `agencyStdlib` lists every std:: module with a one-line
summary, which is the fastest way to answer "what exists".

## What NOT to do

- Don't write or modify any code. You have no write, edit, or shell tools.
- Don't critique or recommend changes — that's the oracle's job.
  You describe what exists; you don't judge it.
- Don't ask the caller to narrow a broad ask. If they said
  "summarize the docs", interpret that generously — read widely,
  organize the result, deliver. A broad scope is not a missing
  fact: use `question` for something you cannot find by reading,
  never to turn a wide survey into a narrow one.
- Don't stop early because the answer "feels good enough" after
  reading three files. Coverage is the point. If you set out to
  read 12, read 12.
- Don't pad the synthesis with restatements of the caller's
  question or apologies for length. Get to the substance.

## Communicating with the user
- Make sure the user is following what you're doing. Use the `whatIAmDoing` tool frequently to tell the user what you're doing.${ASK_USER_HINT}${SAVE_DRAFT_HINT}

"""

// Read-only by design: the explorer surveys and describes, and a surveyor
// that can edit is no longer a surveyor.
export def buildTools(): any[] {
  """
  Return the explorer's file, source, and web lookup tools.
  """
  return [
    ...readOnlyFileTools(),
    ...agencyDocTools(),
    ...webTools(),
    ...spillTools(),
    ...searchTools(),
    ...communicationTools(),
    skills,
  ]
}

def survey(
  question: string,
  context: string,
  model: string,
  provider: string,
  extraTools: any[],
): string {
  ensureSystemMessage(systemPrompt)
  return llm(
    withContext(task: question, context: context),
    llmOptions(
    model: model,
    provider: provider,
    tools: [...buildTools(), ...extraTools, saveDraft],
    spillToolResultsOver: AGENT_SPILL_OVER,
    hostedTools: hostedSearchTools(model, provider),
    reasoningEffort: "high",
    thinking: true,
  ),
  )
}

/** Called from code, this runs on the caller's thread like any function:
  the system prompt, the reads, and the answer stay there. Wrap the call
  in `thread { ... }` for an isolated survey. */
@guarded(cost: maxCost, time: maxTime)
export handoff def explorerAgent(
  question: string,
  context: string = "",
  maxCost: number = $20.00,
  maxTime: number = 15m,
  model: string = "",
  provider: string = "",
  extraTools: any[] = [],
): Result<string> {
  """
  Survey a codebase or documentation and return an organized answer with sources.
  It never changes anything. It continues your conversation, and every file it
  reads stays in your history, so say how much ground to cover and scope the
  question tightly.

  @param question - The question and the scope to cover.
  @param maxCost - Cost budget in USD.
  @param maxTime - Time budget in milliseconds.
  @param model - Model override, or "" for the ambient model.
  @param provider - Provider for the model override, ignored when model is empty.
  @param extraTools - Tools to add to the built-in lookup tools.
  """

  // Return position: a draft the model saved reaches the guard, which
  // returns it as the backup answer if the budget runs out.
  return survey(
    question: question,
    context: context,
    model: model,
    provider: provider,
    extraTools: extraTools,
  )
}
