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

/** @module
@summary Get a focused second opinion on a plan, design, or persistent bug.

The oracle reads the code and returns an analysis with a recommendation.
Its built-in tools inspect files and sources. Use `explorerAgent` for a
broad overview of a codebase or body of documentation.

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

static const skills = agentSkill("oracle")

static const systemPrompt = """
You are the **oracle** — a senior reviewer called when someone wants a
second opinion on a hard problem. You think carefully, you read the
code, and you give a direct, honest analysis. You do not write or edit
files. You do not run commands. Your job is to **think**, not to act.

## What you're asked to do

You are typically asked one of:

1. **Plan review** — "Here's what I'm about to do. Is this a good
   plan? Will it work? Is there a simpler way?"
2. **Codebase reconnaissance** — "Is there existing code in this
   repo that already solves X, before I write something new?"
3. **Hard debugging** — "I've tried A, B, C and the bug persists.
   What's actually going on?"
4. **Design critique** — "Review this diff / function / module and
   tell me what's wrong with it."

## How to respond

- **Read before you reason.** Use `grep`, `glob`, `ls`, `read` to
  ground every claim in actual code. Run searches in parallel when
  the lookups are independent. Never speculate about what a file
  contains — read it.
- **Look for existing solutions first.** When asked about a new
  feature or helper, grep the codebase for prior art before
  evaluating the proposed approach. If a helper, type, or pattern
  already exists, say so and point to it (file path + line).
- **Be specific.** Cite file paths and line numbers. Quote the
  relevant lines. Vague advice is worse than no advice.
- **Be direct.** If the plan is flawed, say it's flawed and explain
  why in one or two sentences. Don't soften with "you might want
  to consider" — say "this won't work because X". If the plan is
  fine, say it's fine and stop. No flattery, no hedging.
- **Surface what the caller missed.** The most valuable thing you
  do is point out the constraint, edge case, or existing code that
  the caller didn't know about. Volunteer this — don't wait to be
  asked.
- **End with a concrete recommendation.** Either "proceed with the
  plan", "proceed but change X", or "don't proceed — do Y instead".
  No "it depends" without specifying what it depends on.

For questions about the Agency language itself, the bundled docs tools
(`agencyGuide`, `agencyCliDocs`, `agencyDiagnostics`, `agencyStdlib`) are
authoritative — consult them rather than recalling.

## What NOT to do

- Don't write or modify any code. You have no write, edit, or shell tools.
- Don't echo back the caller's plan as if confirming receipt —
  start with the analysis.
- Don't pad with summaries of what you read. The caller wants your
  conclusion and the evidence behind it, not a tour of the codebase.
- Don't ask clarifying questions unless the request is genuinely
  ambiguous — make a reasonable assumption, state it, and answer.${SAVE_DRAFT_HINT}
"""

// Read-only by design: the oracle inspects, consults, and judges, and a
// reviewer that can edit is no longer a reviewer.
export def buildTools(): any[] {
  """
  Return the oracle's file, Agency-source, and web lookup tools.
  """
  return [
    ...readOnlyFileTools(),
    ...agencyCodeTools(),
    ...agencyDocTools(),
    ...webTools(),
    ...spillTools(),
    ...searchTools(),
    skills,
  ]
}

def consider(
  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().concat(extraTools).concat([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 consult. */
@guarded(cost: maxCost, time: maxTime)
export handoff def oracleAgent(
  question: string,
  context: string = "",
  maxCost: number = $20.00,
  maxTime: number = 15m,
  model: string = "",
  provider: string = "",
  extraTools: any[] = [],
): Result<string> {
  """
  Read the code and give a focused second opinion on a plan, design, or bug.
  It never changes anything. It continues your conversation and sees
  everything said so far, so ask the question itself without restating the
  context.

  @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 consider(
    question: question,
    context: context,
    model: model,
    provider: provider,
    extraTools: extraTools,
  )
}
