|
| 1 | +# Contributing to ComputerAgent |
| 2 | + |
| 3 | +ComputerAgent is a TypeScript monorepo (pnpm + turbo) implementing the **Harness Protocol** as a set of pluggable ports. Most contributions add a new plug-in (engine, identity loader, substrate, session store) by implementing a small interface — the framework itself is intentionally closed to modification. |
| 4 | + |
| 5 | +## Setup |
| 6 | + |
| 7 | +Requires [Bun](https://bun.sh) ≥ 1.1, [Node](https://nodejs.org) ≥ 20, [pnpm](https://pnpm.io) ≥ 9. |
| 8 | + |
| 9 | +```bash |
| 10 | +git clone https://github.com/open-gitagent/ComputerAgent |
| 11 | +cd ComputerAgent |
| 12 | +pnpm install |
| 13 | +pnpm build |
| 14 | +pnpm test |
| 15 | +``` |
| 16 | + |
| 17 | +CI runs `pnpm -r build`, `pnpm -r typecheck`, and `pnpm -r test` on Node 20 and 22 for every PR. |
| 18 | + |
| 19 | +## Adding a plug-in |
| 20 | + |
| 21 | +The four pluggable ports each follow the same shape — implement the interface, register at server boot. |
| 22 | + |
| 23 | +| Port | Where it lives | Contract | |
| 24 | +|---|---|---| |
| 25 | +| `EngineDriver` | `packages/engine-*` | Wraps an agent loop. `startSession(ctx)` returns an `AsyncIterable<EngineEvent>`. | |
| 26 | +| `IdentityLoader` | `packages/identity-*` | Translates a "what is this agent" source into engine-native options. `load(args)` returns `IdentityLoadResult`. | |
| 27 | +| `Substrate` | `packages/runtime-*` | Boots a harness-server somewhere. `bootHarness({envs})` returns `{baseUrl, shutdown}`. | |
| 28 | +| `SessionStore` | `packages/session-store-*` | Cross-process conversation persistence. `append(key, entries)` + `load(key)`. Re-exported from the Claude Agent SDK so any backend works the same way for both engines. | |
| 29 | + |
| 30 | +### Validate against the conformance suite |
| 31 | + |
| 32 | +`@computeragent/testing` ships portable cases that any conforming implementation must pass: |
| 33 | + |
| 34 | +```ts |
| 35 | +import { conformanceCases, runConformanceSuite } from "@computeragent/testing"; |
| 36 | + |
| 37 | +const report = await runConformanceSuite(() => yourDriver(yourHarness)); |
| 38 | +console.log(`${report.passed} passed, ${report.failed.length} failed`); |
| 39 | +``` |
| 40 | + |
| 41 | +If the suite passes, your plug-in is a drop-in replacement. |
| 42 | + |
| 43 | +For `SessionStore` specifically, the LSP gate is in `packages/session-store-sqlite/src/integration.test.ts` — clone that file, swap in your store, run it. Three assertions: round-trip, cross-process resume, `UNKNOWN_STORE` error registry hygiene. |
| 44 | + |
| 45 | +## Code rules |
| 46 | + |
| 47 | +These are enforced by review (no automated lint yet): |
| 48 | + |
| 49 | +1. **No file over 250 LOC.** Hard cap. Split by responsibility. |
| 50 | +2. **No abstraction without a second consumer.** YAGNI is non-negotiable. |
| 51 | +3. **Pure functions for translations.** Side effects (fs, network) live at the edges. |
| 52 | +4. **Errors are values at boundaries.** Use the `BadRequest` / `NotFound` / `Conflict` helpers in `packages/harness-server/src/error-mapper.ts`. Don't throw raw. |
| 53 | +5. **Zod at the wire, types inside.** All inbound HTTP and outbound SSE bodies pass through zod. Internal code uses inferred types. |
| 54 | +6. **No `any`.** `unknown` and narrow. |
| 55 | +7. **No emojis in source, logs, or generated output.** Plain text everywhere. |
| 56 | +8. **Imports go down the dependency graph.** `engine-*` and `identity-*` import from `@computeragent/protocol`. `harness-server` imports only from `@computeragent/protocol`. No cross-imports between engine and identity packages. |
| 57 | +9. **Tests live next to source.** `foo.ts` + `foo.test.ts`. Vitest. |
| 58 | + |
| 59 | +## Failure semantics |
| 60 | + |
| 61 | +Documented contract — please don't change without an RFC: |
| 62 | + |
| 63 | +- **`AuditSink`** — fire-and-forget. Errors swallowed. The harness keeps going. |
| 64 | +- **`SessionStore`** — fail loud. Errors surface to the client via `ca_session_ended { reason: "error" }`. Data integrity matters. |
| 65 | +- **`EngineDriver`** — fail loud. Errors surface as above. |
| 66 | +- **`IdentityLoader`** — fail at session-create time as `400 BAD_REQUEST`. |
| 67 | +- **Sibling sessions** — never share blast radius. One bad session must not corrupt another. |
| 68 | + |
| 69 | +The `validateStoreEntries` server option lets deployments enforce the SessionStore contract at the framework boundary (drops malformed entries from `load()` before they reach the engine). Default OFF — framework is pass-through; engines validate. |
| 70 | + |
| 71 | +## Versioning |
| 72 | + |
| 73 | +We use [changesets](https://github.com/changesets/changesets) for cross-package versioning. |
| 74 | + |
| 75 | +After making a change that affects published packages: |
| 76 | + |
| 77 | +```bash |
| 78 | +pnpm changeset # describe what changed; pick semver bump per package |
| 79 | +git add .changeset/ |
| 80 | +git commit -m "..." |
| 81 | +``` |
| 82 | + |
| 83 | +When ready to release, the maintainer runs: |
| 84 | + |
| 85 | +```bash |
| 86 | +pnpm version-packages # consumes pending changesets, bumps versions, updates CHANGELOGs |
| 87 | +pnpm release # builds all packages and publishes those that bumped |
| 88 | +``` |
| 89 | + |
| 90 | +`@computeragent/examples` is private (the demo scripts) and excluded from versioning. |
| 91 | + |
| 92 | +## Live tests |
| 93 | + |
| 94 | +The `MONGO_URL` and `ANTHROPIC_API_KEY` env vars opt into live integration suites: |
| 95 | + |
| 96 | +```bash |
| 97 | +MONGO_URL="mongodb://..." pnpm --filter @computeragent/session-store-mongo test |
| 98 | +ANTHROPIC_API_KEY="sk-..." bun run examples/wedge16-mongo-resume-demo.ts |
| 99 | +``` |
| 100 | + |
| 101 | +CI runs the offline suite by default; the live ones skip cleanly when the env vars are absent. |
| 102 | + |
| 103 | +## Pull request checklist |
| 104 | + |
| 105 | +- [ ] `pnpm -r build` clean |
| 106 | +- [ ] `pnpm -r typecheck` clean |
| 107 | +- [ ] `pnpm -r test` clean |
| 108 | +- [ ] If you added a published-package change, `pnpm changeset` was run |
| 109 | +- [ ] If you added a new plug-in, a passing conformance run is included in the PR description |
| 110 | +- [ ] No file added over 250 LOC |
| 111 | + |
| 112 | +## License |
| 113 | + |
| 114 | +All contributions are under [MIT](./LICENSE). |
0 commit comments