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.
// 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.
$ 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.
$ 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.
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.json202 { "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.
$ 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.
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-reprodeploy --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.
npx opencomputer sessions tail <session-id> --after 0 --no-follow --jsonThis 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.
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
- 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.