Skip to content

Repository files navigation

Bug reproducer

An OpenComputer Serverless Agent that reproduces bug reports.

Input: a bug report and the URL of a public Git repository. Output: a failing test written in the repository's own test convention, the test runner's output, the file and line responsible, and a likely cause.

Triage starts with "does this reproduce, and where". Answering that takes a checkout, a test runner, and a few iterations. A model with declared read-only tools can read the code and guess; this agent runs it.

What the example shows:

  • The managed harness is a complete coding agent in a VM per session: a shell, a filesystem, git, node, and network access. Any agent can select those tools. This one defines no tools of its own and uses no secrets.
  • The agent function runs before every model step and selects the tools for that step. One deployment behaves as a chat agent or a coding agent depending on the input.
  • Sessions are durable. The workspace, including a clone and the tests the agent wrote, survives the VM suspending and resuming.
  • A webhook payload reaches the same function as a typed message. Delivery is idempotent.

The agent

// Simplified excerpt of opencomputer/agents/bug-repro/agent.ts.
import { useInput, useModel, useTool } from "@opencomputer/agent";

export default function Agent() {
  // Omitted here: the file also reads the report from payload.report (webhooks).
  const report = useInput().text ?? "";

  useModel("google/gemini-3.8-flash");

  if (report.includes("github.com/")) {
    // A repository is named: attach the harness's shell and filesystem.
    // Omitted here: the file also selects read, write, glob, grep.
    useTool("shell");
    return reproductionPrompt(report);
  } else {
    // No repository: conversation. The model request carries no tools.
    return conversationPrompt(report);
  }
}

// Omitted here: the prompt text. See agent.ts.
function conversationPrompt(text: string) { return `…`; }
function reproductionPrompt(report: string) { return `…`; }
// Simplified excerpt of opencomputer/agents/bug-repro/opencode.json.
// Omitted here: the file also registers read, write, glob, grep.
{ "tools": { "shell": true }, "permission": { "shell": "allow" } }

Two levels. opencode.json registers the harness tools this agent's deployment may use at all. The function selects from that set for each model step. A tool that is not registered cannot be selected; a tool that is not selected is absent from the model request, not merely discouraged.

Each run of the function is recorded as an agent.rendered event, with the selected tools and the instructions hash, before the model is called. The four runs below are taken from the event log of one deployment.

1. A report gets the shell and produces a failing test

$ npx opencomputer session --verbose "$(cat fixture/reports/tax-off-by-a-cent.txt)"

agent.rendered  providerTurn 1  enabledTools [glob, grep, read, shell, write]
shell           git clone --depth 1 https://github.com/diggerhq/opencomputer-example-bug-repro repo
read            fixture/test/invoice.test.js
read            fixture/src/invoice.js
write           fixture/test/invoice_bug_cent_short.test.js
shell           node --test test/invoice_bug_cent_short.test.js
                not ok 1 - invoiceTotal rounds half-up on .5 tax cent (8.20 @ 7.5% = 8.82)
                8.81 !== 8.82
agent.rendered  providerTurn 8  (same instructions hash as turn 1)

Reproduced: yes
Failing test: fixture/test/invoice_bug_cent_short.test.js
  cd fixture && node --test test/invoice_bug_cent_short.test.js
Observed vs expected:
  8.81 !== 8.82
Where: fixture/src/invoice.js:13
  const tax = round2(subtotal * taxRate);
Likely cause: 8.20 * 0.075 is 0.6149999999999999 in IEEE 754, just below 0.615,
  so Math.round gives 0.61 instead of 0.62 and the total is 8.81.

One turn, eight model steps, 55 seconds. The function ran eight times, once before each step, and produced the same record each time because the input had not changed. The test file remains in the session's workspace.

2. A message without a repository gets no tools

$ npx opencomputer session --verbose "hi, what do you do?"

agent.rendered  providerTurn 1  enabledTools []

I specialize in reproducing bug reports against public Git repositories. To
get started, please share the repository URL and the bug report text you'd
like me to investigate.

Same deployment. The render selected no tools, so the model request contained none. The shell exists in the harness; it was not in the model request for this step.

3. A webhook payload selects the same tools; a repeated delivery returns the same session

npx opencomputer webhooks create bug-reports --agent bug-repro --environment development
# prints the URL and a bearer token once

curl -X POST 'https://app.opencomputer.dev/api/agent-webhooks/wh_...' \
  -H 'Authorization: Bearer ocwh_...' -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: report-2-delivery-1' \
  --data-binary @fixture/reports/coupon-preview.json
202  { "request": { "sessionId": "5b0420db-…" }, "duplicate": false }
202  { "request": { "sessionId": "5b0420db-…" }, "duplicate": true }    # the same request, sent again

agent.rendered  source webhook  enabledTools [glob, grep, read, shell, write]
shell           git clone …
write           fixture/test/coupon_preview.test.js
shell           not ok 1 - previewing a coupon does not change the original invoice total
                90 !== 100

Reproduced: yes
Where: fixture/src/invoice.js:21
Likely cause: withCoupon shallow-copies the invoice ({ ...invoice }); lines is
  the same array, and forEach mutates each line's unitPrice in place.

Webhooks are created outside the code and do not change with deployments. The report in the payload names the repository, so the function selects the same tools as in run 1. A second delivery with the same Idempotency-Key returns the original session instead of starting another one.

4. A suspended session resumes with its workspace

$ npx opencomputer session send 56d3d45e-… --verbose "Same repository: https://github.com/diggerhq/opencomputer-example-bug-repro path fixture. Follow-up: does lineTotal have the same rounding problem for unitPrice 2.675 and quantity 1? Reuse the existing clone if present."

Resuming 56d3d45e-…            # session status was suspended
Reusing the existing clone at /tmp/opencode/repo — no re-clone needed.
write           fixture/test/linetotal_bug_rounding.test.js
shell           ok 1 - lineTotal rounds half-up: unitPrice 2.675 × 1 = 2.68

Reproduced: no
Likely cause: 2.675 is stored just below 2.675, but 2.675 * 100 lands exactly on
  267.5 in IEEE 754; the two rounding errors cancel and Math.round rounds up.

The session suspended about a second after run 1 ended. The follow-up resumed the VM with the clone and the conversation intact. The agent tested the new case and reported that it does not reproduce, with the reason.

The function reads the input of the current turn only, not the conversation. A follow-up that omits the repository URL renders as in run 2 and gets no tools for that turn, whether or not a clone exists. Include the repository in each request, as the webhook payload does, or read it from session data with useSessionData(); there is no public API to write session data yet.

Run

Requires Node 22 and an OpenComputer account.

git clone https://github.com/diggerhq/opencomputer-example-bug-repro.git
cd opencomputer-example-bug-repro
npm install
npx opencomputer login
npx opencomputer deploy --watch --create-project bug-repro

deploy --watch builds the agent, creates the project, deploys to development, and redeploys on each save. There is no local agent server. Run the four scenarios from a second terminal. For run 4, take the session id from npx opencomputer session list.

To use another repository, put its URL in the report, in the session text or in payload.report.

Inspect a session

npx opencomputer sessions tail <session-id> --after 0 --no-follow --json

This prints the session's event log as NDJSON. agent.rendered carries enabledTools, the instructions hash, the deployment id, the provider turn, and the state version. tool.completed carries the shell output. session.status_changed records suspending and suspended about one second after a turn ends. The dashboard's session inspector shows the same records with the full instructions.

After a build, opencomputer/agents/bug-repro/.opencomputer/runtime/.opencomputer/reactive.json contains what the compiler extracted from the source, including the tool list that every render is checked against.

Files

opencomputer/project.ts                  lists the project's agents
opencomputer/agents/bug-repro/agent.ts   the function and its two prompts
opencomputer/agents/bug-repro/opencode.json   harness tools this agent may select
fixture/src/invoice.js                   a billing module with two bugs, on purpose
fixture/test/invoice.test.js             the existing suite; passes with both bugs present
fixture/reports/                         the two reports: one as text for the CLI, one as a webhook body
DX-NOTES.md                              observations from building this against the live platform

Limits

  • Public repositories only; the clone is unauthenticated. Credentials are declared as connections with the secret attached outside the VM; see the Pull Request Reviewer.
  • The repository's own scripts run inside the session VM. Treat the target repository as untrusted code. "Do not modify existing files" is an instruction to the model, not an enforced boundary. The VM holds no credentials.
  • Not used here: subagents, skills, MCP servers.

Docs: How it works · Reactive agents · Inputs · Webhooks · Sessions

MIT.

About

OpenComputer Serverless Agents example: an agent with a computer reproduces bug reports by cloning, writing a failing test, and running it

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages