ocra

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:

OptionMeaning
vcsThe VcsAdapter for the change: where the diff and files come from, and where publish sends the review
runtimeThe AgentRuntime that runs one isolated review task at a time
reviewersThe reviewers to run (default: correctness alone); the registry's, or your own ReviewerDefinitions
reviewerOverridesPer reviewer: enabled, minTier
rulesPath-scoped review rules (RepoRule[]), the registry's plus your own
readTrustedWhere 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
selectionWhich files are reviewed: include, exclude, maxPatchChars
concurrency, taskTimeoutMs, runTimeoutMsParallel tasks (4), the per-task timeout (10 minutes) and the run's (25 minutes)
verify, judgeFact-check findings against the diff, and merge, filter and recalibrate them across reviewers (both default on)
maxCostUsd, maxTasksThe spend limit (--max-cost-usd) and the task cap
fullReviewReview every file although the platform reports what changed since the previous review
ultraRecall over cost (Modes)
sarifParsed SARIF logs (parseSarifLog) whose results on the change join the review (SARIF input)
signalAn AbortSignal: the run stops, keeps what it found and writes a partial report
onEventReceives each ReviewEvent as the run progresses
runIdNames 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 by reportOutputSchema and 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.unverifiedCriticals counts 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 read verdict alone 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.

CodeMeaning
CONFIG_INVALIDConfiguration 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_MISSINGA token or model key the run needs is not set
INPUT_USAGEA command line ocra cannot run (the CLI's usage errors)
INPUT_INVALIDInput given to ocra that it cannot read, such as a SARIF log or a list of commit ids
ACCESS_DENIEDThe access policy refused a path: a secret, git internals, a symbolic link where ocra writes
PLUGIN_INVALIDA plugin that cannot be loaded or registers something twice or after start, or a name no plugin registered
VCS_GIT_FAILEDA git command failed
VCS_API_FAILEDThe GitHub or GitLab API failed or answered something unexpected; GitHubApiError and GitLabApiError carry the HTTP status
VCS_REF_UNKNOWNA commit or ref that is not in the repository and could not be fetched
VCS_NOT_READYThe platform has not prepared the change yet, such as a merge request without a diff; try again shortly
RUNTIME_START_FAILEDThe agent runtime could not start (no OpenCode binary, a server that did not come up)
RUNTIME_FAILEDA model call failed on every model of its chain (CompletionError, which carries the usage the attempts spent)
RUNTIME_INVALID_OUTPUTA model answered something that does not match the expected schema
BUDGET_EXHAUSTEDThe spend limit was reached (SpendLimitReached, the reason running tasks were stopped)
INTERNALA 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() and ReviewOptions; what it returns (ReviewReport, Finding and the types they are made of), coverageGaps(), toReportOutput() with ReportOutput, reportOutputSchema, reportJsonSchema() and REPORT_VERSION; parseSarifLog(); the plugin interface (OcraPlugin and its contexts, startPlugins(), PluginRegistry, the built-in reviewer plugins and sessionJsonlPlugin); the contracts a plugin implements (VcsAdapter, AgentRuntime and the types they use, ToolDefinition, ReviewerDefinition, RepoRule); and the error model.
  • @open-cr-agent/vcs-local: localGitPlugin, LocalTarget, LocalGitOptions and GitError. vcs-github and vcs-gitlab: githubPlugin and gitlabPlugin, and their API errors. runtime-opencode and runtime-direct: opencodeRuntimePlugin and directRuntimePlugin.
  • @open-cr-agent/vcs-platform is 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.

Edit on GitHub

On this page