Embedding ocra
Run the review pipeline from your own program with review() from @open-cr-agent/core.
ocra is a library first. The ocra command is one caller of review() in @open-cr-agent/core; your program can be another: a bot, a CI step of your own, a service that reviews on a schedule. The same stages, trust rules, cost limits and report apply.
A complete call
import {
correctnessReviewerPlugin,
coverageGaps,
review,
securityReviewerPlugin,
sessionJsonlPlugin,
startPlugins,
toReportOutput,
} from "@open-cr-agent/core";
import { opencodeRuntimePlugin } from "@open-cr-agent/runtime-opencode";
import { localGitPlugin } from "@open-cr-agent/vcs-local";
// 1. Plugins register adapters, runtimes, reviewers, rules, tools and listeners.
const registry = await startPlugins(
[localGitPlugin, opencodeRuntimePlugin, correctnessReviewerPlugin, securityReviewerPlugin, sessionJsonlPlugin],
{
env: process.env,
settings: { "session-jsonl": { dir: ".ocra/sessions", id: "my-run" } },
warn: (message) => console.warn(message),
},
);
// 2. Where the change comes from, and what runs the agents.
const vcs = registry.createVcs("local", {
cwd: process.cwd(),
target: { mode: "range", from: "main", to: "HEAD" },
});
const runtime = registry.createRuntime("opencode", {
models: {
top: ["google/gemini-3.1-pro-preview"],
standard: ["google/gemini-3.5-flash", "google/gemini-flash-lite-latest"],
light: ["google/gemini-flash-lite-latest"],
},
env: process.env,
});
// 3. The review.
try {
const report = await review({
vcs,
runtime,
reviewers: registry.reviewers,
rules: registry.rules,
maxCostUsd: 2,
onEvent: (event) => registry.emit(event),
});
const { notReviewed, nothingReviewed } = coverageGaps(report);
console.log(JSON.stringify(toReportOutput(report)));
if (nothingReviewed || notReviewed > 0) process.exitCode = 3;
} finally {
await runtime.dispose?.();
}startPlugins runs the plugins' lifecycle (Plugins) and returns a frozen registry; createVcs and createRuntime build the adapter and the runtime the plugins registered, by name; registry.emit hands each event to the listeners, here the session log. The plugins and settings above are what the CLI uses; the .ocra/config.json it reads is a CLI convenience, not part of the library.
What review() takes
ReviewOptions, as the stability page says, is a contract while ocra is 0.x: a minor release may change it, and the changelog says how. These fields are the contract:
| Option | Meaning |
|---|---|
vcs | The VcsAdapter for the change: where the diff and files come from, and where publish sends the review |
runtime | The AgentRuntime that runs one isolated review task at a time |
reviewers | The reviewers to run (default: correctness alone); the registry's, or your own ReviewerDefinitions |
reviewerOverrides | Per reviewer: enabled, minTier |
rules | Path-scoped review rules (RepoRule[]), the registry's plus your own |
readTrusted | Where AGENTS.md, .ocra/rules.json and .ocra/memory.json are read from. Default: the revision under review; for a pull request, pass a reader of the trusted base so the change cannot rewrite them |
selection | Which files are reviewed: include, exclude, maxPatchChars |
concurrency, taskTimeoutMs, runTimeoutMs | Parallel tasks (4), the per-task timeout (10 minutes) and the run's (25 minutes) |
verify, judge | Fact-check findings against the diff, and merge, filter and recalibrate them across reviewers (both default on) |
maxCostUsd, maxTasks | The spend limit (--max-cost-usd) and the task cap |
fullReview | Review every file although the platform reports what changed since the previous review |
ultra | Recall over cost (Modes) |
sarif | Parsed SARIF logs (parseSarifLog) whose results on the change join the review (SARIF input) |
signal | An AbortSignal: the run stops, keeps what it found and writes a partial report |
onEvent | Receives each ReviewEvent as the run progresses |
runId | Names the run in the report, the events and what is posted; generated when absent. The CLI passes its session id |
provenance | { ocraVersion, configHash, sampling? }: with it, the report's provenance adds the hash of the system prompts the enabled reviewers and helper stages send and the sampling the runtime applied (sampling is what you asked the runtime for; stableHash() hashes a configuration of yours) |
Up to 0.2, ReviewOptions also carried bundling, grouper, relocate and abortGraceMs; they were tuning and test hooks, never a contract, and are no longer part of it.
What it returns
A ReviewReport: the change, risk tier, verdict and summary, coverage per file, bundles, tasks, findings, what verification refuted and the judge decided, the comparison with the previous review, usage and warnings. Two things to know when you read it:
- Publish
toReportOutput(report), not the report itself. It is the versioned JSON the CLI writes, described byreportOutputSchemaand the published JSON Schema; the internal report carries fields that may change. - An incomplete review is not a pass.
coverageGaps(report)says how many selected files were not reviewed and whether nothing was;report.unverifiedCriticalscounts critical findings nobody could check. The CLI's exit codes are computed from these; a program that gates on the review should do the same, and never readverdictalone as approval.
vcs.publish(report) posts the review the way --publish does, under the platform's trust rules; it returns the parts it could not post as warnings.
Events (onEvent) are how progress, the session log and the terminal output are produced. Their shapes are not a contract: a listener should tolerate new event types and fields.
Errors
review() rejects only when the run cannot start: no reviewers, an invalid .ocra/rules.json or .ocra/memory.json, or a change the VCS adapter cannot read. Once tasks run, a failed task, a failed helper call, a model that is out of quota and the spend limit are recorded in the report (task outcomes, coverage, warnings), not thrown; read them with coverageGaps(report) as above.
What ocra's packages throw is an OcraError (or one of its subclasses) with a code; isOcraError(error, code?) tells you so, and OCRA_ERROR_CODES lists the codes. Where an error has an underlying one (a JSON parse error, a failed read), it is the cause. Branch on the code, never on the message: messages are for people and may be reworded. A plugin's adapter or runtime may throw errors of its own, so a caller should handle errors that are not OcraError too.
| Code | Meaning |
|---|---|
CONFIG_INVALID | Configuration that cannot be used: options, .ocra/config.json and what it extends, .ocra/rules.json, .ocra/memory.json, a model name, no reviewer, no model for a tier |
CONFIG_CREDENTIALS_MISSING | A token or model key the run needs is not set |
INPUT_USAGE | A command line ocra cannot run (the CLI's usage errors) |
INPUT_INVALID | Input given to ocra that it cannot read, such as a SARIF log or a list of commit ids |
ACCESS_DENIED | The access policy refused a path: a secret, git internals, a symbolic link where ocra writes |
PLUGIN_INVALID | A plugin that cannot be loaded or registers something twice or after start, or a name no plugin registered |
VCS_GIT_FAILED | A git command failed |
VCS_API_FAILED | The GitHub or GitLab API failed or answered something unexpected; GitHubApiError and GitLabApiError carry the HTTP status |
VCS_REF_UNKNOWN | A commit or ref that is not in the repository and could not be fetched |
VCS_NOT_READY | The platform has not prepared the change yet, such as a merge request without a diff; try again shortly |
RUNTIME_START_FAILED | The agent runtime could not start (no OpenCode binary, a server that did not come up) |
RUNTIME_FAILED | A model call failed on every model of its chain (CompletionError, which carries the usage the attempts spent) |
RUNTIME_INVALID_OUTPUT | A model answered something that does not match the expected schema |
BUDGET_EXHAUSTED | The spend limit was reached (SpendLimitReached, the reason running tasks were stopped) |
INTERNAL | A broken invariant or a misused object, such as one OpenCode runtime used for two runs: a bug in ocra or in its caller |
The subclasses keep their names for instanceof: CompletionError, SpendLimitReached, AccessDeniedError, SarifError and PluginError from @open-cr-agent/core, GitError from @open-cr-agent/vcs-local, GitHubApiError and GitLabApiError from the platform adapters. ocra puts no token or model key in an error message, and both runtimes redact them from what a model provider answers before quoting it.
The codes are a contract under the 0.x rule: a minor release may add a code, and the changelog says when one is renamed or removed. The CLI prints the code of an error that ends a run with exit 2, as in ocra [CONFIG_INVALID]: .ocra/config.json is invalid: ….
Pull requests and merge requests
githubPlugin (@open-cr-agent/vcs-github) and gitlabPlugin (@open-cr-agent/vcs-gitlab) register the github and gitlab adapters. Their createVcs options (the repository, the number, the token's variable, whether to publish) are not documented as a library contract yet; packages/cli/src/review/target.ts in the repository shows how the CLI builds them, including the trusted-base reader it passes as readTrusted. Treat those shapes as internal until this page lists them.
Trust
The program that embeds ocra is the trust boundary. Plugins run code, so load only plugins you trust, and never ones named by the tree under review; pass model keys through env, where the runtime forwards only what the configured providers need (Security); for a pull request, read guidelines, rules and memory from the base revision through readTrusted. The review itself gives agents read-only tools and no shell, whoever calls it.
The public API
The main entry of each package is its public API, a contract under the 0.x rule:
@open-cr-agent/core:review()andReviewOptions; what it returns (ReviewReport,Findingand the types they are made of),coverageGaps(),toReportOutput()withReportOutput,reportOutputSchema,reportJsonSchema()andREPORT_VERSION;parseSarifLog(); the plugin interface (OcraPluginand its contexts,startPlugins(),PluginRegistry, the built-in reviewer plugins andsessionJsonlPlugin); the contracts a plugin implements (VcsAdapter,AgentRuntimeand the types they use,ToolDefinition,ReviewerDefinition,RepoRule); and the error model.@open-cr-agent/vcs-local:localGitPlugin,LocalTarget,LocalGitOptionsandGitError.vcs-githubandvcs-gitlab:githubPluginandgitlabPlugin, and their API errors.runtime-opencodeandruntime-direct:opencodeRuntimePluginanddirectRuntimePlugin.@open-cr-agent/vcs-platformis what ocra's platform adapters share; it is not a contract yet.
Every name and the shape of every type is in the package's API report, in the repository's etc/ directory; CI fails when an entry changes and its report does not.
@open-cr-agent/core/internal, and the /internal entry of some other packages, is what ocra's packages share with each other. It is not a contract: any release, a patch included, may change or remove what it exports. Build on the main entries only.