> For the complete documentation index, see [llms.txt](https://docs.fritesai.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fritesai.com/services/core.md).

# Core

`@frites/core` (`packages/core`) is the shared engine, configuration, and type layer for frites. It is the brain that fans a task out to multiple child agents, filters their results through an executable oracle, optionally synthesizes a single best implementation, and reconciles everything into one recommended candidate.

The package is deliberately **I/O-free**: it has no CLI, MCP, git, or process-spawning code of its own. Everything it touches the outside world with is expressed as a structural interface that `@frites/agents` and `@frites/isolation` satisfy at runtime. The only runtime dependency is [`zod`](https://www.npmjs.com/package/zod) for config validation. This keeps the engine fully unit-testable with fakes.

For the deeper internals (the engine event model, the stage-by-stage flow, and failure modes), see [Core engine](/architecture/core-engine.md).

## Exports

`packages/core/src/index.ts` re-exports the public surface:

| Module              | What it provides                                                                                                                       |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `types.js`          | `AgentSpec`, `Task`, `Candidate`, `OracleResult`, `OracleCommands`, `CommandResult`, `ReconcileDecision`, `SynthesisInfo`, `RunResult` |
| `events.js`         | `EngineEventHandler`, `noopEventHandler`, and the engine event union                                                                   |
| `config.js`         | `FritesConfigSchema`, `FritesConfig`, `resolveConfig`, `DEFAULT_CHILD_DIRECTIVE`, `withChildDirective`, pricing schemas                |
| `config-io.js`      | Config loading helpers                                                                                                                 |
| `pricing.js`        | `estimateCostUsd`, `pricingFor`, `UsageTokens`                                                                                         |
| `answer-council.js` | `runAnswerCouncil`, `decideFanOut`, `llmJudgeFanOut`, `parseFanOutVerdict`, `stripInjectedContext`                                     |
| `agent-loop.js`     | The agentic turn loop                                                                                                                  |
| `oracle.js`         | `detectOracle`, `runOracle`, `runCommand`                                                                                              |
| `judge.js`          | `heuristicJudge`, `diffSize`                                                                                                           |
| `synthesis.js`      | Synthesis eligibility, synthesizer selection, prompt construction, reconcile preference                                                |
| `engine.js`         | `runEngine` and its structural dependency interfaces                                                                                   |

## The engine (`engine.ts`)

`runEngine(task, deps, onEvent)` is the worktree-mode entry point. It takes a `Task`, a set of structural `EngineDeps`, and an event handler, and returns a `RunResult`.

### Structural dependencies

The engine never imports git or a CLI directly. Instead `EngineDeps` is satisfied by injected implementations:

* `worktrees: WorktreeManagerLike`: `resolveBase`, `create`, `captureDiff`, `cleanup`, and an optional `applyDiffToWorktree` (satisfied by [`@frites/isolation`](/services/isolation.md)).
* `runAgent: RunAgentFn`: runs one `AgentSpec` in a worktree and returns status, summary, cost, and normalized token usage (satisfied by [`@frites/agents`](/services/agents.md)).
* `runOracle: RunOracleFn`: runs build/lint/test against a worktree.
* `oracleCommands: OracleCommands`, `config: FritesConfig`, `newRunId: () => string`, and an optional external-cancellation `signal`.

### Flow

1. **Select agents.** `selectAgents` uses `task.agents` if present, else clones `config.defaultAgents` up to `n` (capped 1-5), suffixing duplicate ids.
2. **Resolve base.** `worktrees.resolveBase` pins the base ref and SHA every worktree branches from.
3. **Dispatch + execute (concurrent).** Each agent gets its own worktree (created and registered before the prompt runs, so the `finally` always reaps it), runs `runAgent`, and has its diff captured into a `Candidate`. A candidate's status becomes `empty` when it succeeded but touched no files.
4. **Oracle-filter (concurrent).** Each succeeded candidate is run through `runOracle`. With no executable oracle, candidates carry `hadOracle: false`.
5. **Synthesis (optional).** See below.
6. **Reconcile.** A pure `reconcile()` picks a winner over the original candidate pool, then `applySynthesisPreference` may override it with the synthesis candidate.

The whole run is wrapped in a `try/finally` that `Promise.allSettled`s `worktrees.cleanup` over every registered handle, so worktrees are reaped even on a throw.

### Reconciliation

`reconcile()` is pure and emits a `ReconcileDecision`:

| Decision    | Meaning                                                                                       |
| ----------- | --------------------------------------------------------------------------------------------- |
| `single`    | Only one agent; it passed the oracle.                                                         |
| `tests`     | The oracle filtered many candidates down to exactly one survivor.                             |
| `judge`     | Multiple survivors; tie-broken by `heuristicJudge`.                                           |
| `synthesis` | An oracle-passing synthesized candidate was preferred over the originals.                     |
| `near-miss` | No candidate passed (or none was usable); the closest is surfaced with a warning.             |
| `no-oracle` | No executable oracle existed; a best-effort pick by smallest diff, explicitly *not* verified. |

## The oracle (`oracle.ts`)

The oracle is frites's objective filter. `detectOracle` returns explicit `build`/`test`/`lint` commands when given, otherwise (when `autoDetect` is on) reads `package.json` scripts and prefixes them with the detected package manager (`pnpm`, `yarn`, `bun`, or `npm`, chosen from lockfiles). `runOracle` runs the commands in **build → lint → test** order, short-circuiting on the first failure, and passes only when at least one discriminating command ran and every one that ran exited 0. `runCommand` spawns via a shell, keeps a `4000`-char output tail, and supports an `AbortSignal` plus a wall-clock `timeoutMs` (which the engine wires to `config.perChildTimeoutMs`).

## The judge (`judge.ts`)

`heuristicJudge` is the v1 tie-breaker among oracle-passing survivors: it prefers the **smallest diff** (smallest blast radius) by `diffSize` (counted added/removed lines, excluding headers), then the fewest files touched. An LLM pairwise judge is a later phase.

## Config (`config.ts`)

`FritesConfigSchema` is the single zod source of truth for every tunable, and `resolveConfig(partial)` parses (and defaults) any partial input. It defines child defaults (`defaultN`, `defaultAgents`), idle/hard timeouts, budgets, oracle detection, the recursion fuse (`maxDepth`), fan-out policy and scope, progress/logging knobs, optional per-model `pricing`, and the full `synthesis*` family. `DEFAULT_CHILD_DIRECTIVE` is the thoroughness instruction woven into every substantive child prompt (`withChildDirective`). The complete key-by-key reference lives in [Configuration](/reference/configuration.md).

## The answer council (`answer-council.ts`)

The answer council is the transparent-proxy brain for **answer/reasoning** turns (Stance-A text synthesis): no worktrees or tools; heavy file-editing lives in the engine/MCP path.

* `decideFanOut` is the heuristic gate, honoring `config.fanOutPolicy` (`never`/`always`/`necessary`/`auto`). The `auto` and `necessary` paths inspect prompt length and a `HARD_SIGNAL` keyword regex (why, compare, design, debug, prove, optimize, …).
* `llmJudgeFanOut` upgrades that to a one-word LLM verdict, parsed **strictly** and **fail-closed** by `parseFanOutVerdict` (only a reply beginning with `fan-out` fans out; anything else resolves to a single agent). It falls back to the heuristic on any error.
* `stripInjectedContext` removes known harness wrapper tags (`system-reminder`, `ide_selection`) before classification so the judge sees the real ask.
* `runAnswerCouncil` runs N children with diverse framings (drawn from `defaultAgents`), each carrying the child directive and a Markdown-formatting directive, then asks one synthesizer to merge them into a single vetted answer, keeping agreements, adjudicating disagreements, and dropping unsupported claims, without revealing that multiple responses existed.

## Synthesis (`synthesis.ts`)

The synthesis stage integrates the strongest ideas from oracle-passing candidates into one implementation, verified by the **same** oracle, never a mechanical diff merge.

* `evaluateSynthesisEligibility` requires synthesis enabled, an executable oracle, and at least `synthesisMinCandidates` usable, oracle-passing candidates.
* `selectSynthesizer` picks `config.synthesisAgent`, else the first `claude-cli` child (so `synthesisBudgetUsd` actually bites via `--max-budget-usd`), else the first agent, mapping the `synthesis*` budget/timeout overrides onto the returned spec.
* `reservedSynthesisId` allocates a collision-free `synthesis-N` id.
* `buildSynthesisPrompt` constructs the strict integration prompt, embedding non-seed candidate diffs smallest-first up to `synthesisMaxDiffChars` and falling back to a file list + read-only worktree path past the cap.
* `applySynthesisPreference` prefers the synthesized candidate only when it is usable, passed the oracle, and its blast radius is within `synthesisMaxBlastFactor ×` the combined input size; otherwise it falls back to the best original passing candidate and records the reason.

The full design rationale, reconciliation policy, and non-goals live in [Synthesis and reconciliation](/concepts/synthesis-and-reconciliation.md).

## Exported types (`types.ts`)

The type layer is the contract every other package speaks:

* `AgentSpec`: id, `kind` (`claude-cli` | `codex-cli`), optional model, framing, budget, idle/hard timeout overrides, and codex `reasoningEffort`.
* `Task`: instructions, `repoPath`, optional `baseRef`, acceptance criteria, `n`, or an explicit `agents` list.
* `Candidate`: a child's worktree, diff, `filesTouched`, status (`succeeded`/`empty`/`errored`/`timed-out`), summary, cost, normalized token usage, and synthesis provenance.
* `OracleResult` / `CommandResult`: per-command output and the overall pass.
* `RunResult`: `runId`, `recommended`, all candidates/oracle results, the `decision` + `rationale`, a `costNote`, and (when enabled) `synthesis: SynthesisInfo`.

These types carry no I/O coupling, which is what lets the engine stay pure.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fritesai.com/services/core.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
