@asimov-collective/calvin
v0.3.1
Published
Client package for the shared, durable client-feedback annotation system built on [`agentation`](https://www.npmjs.com/package/agentation) and the [`calvin-worker`](https://github.com/asimov-collective/calvin-worker) Cloudflare Worker.
Readme
@asimov-collective/calvin
Client package for the shared, durable client-feedback annotation system built on
agentation and the
calvin-worker
Cloudflare Worker.
Named for Susan Calvin, Asimov's robopsychologist — the one who reads what's actually going on and turns it into something actionable.
One Worker deployment can serve every client project — each project is isolated by a unique room id, so this package and the Worker are shared infrastructure, not per-project code.
Install
Published to the public npm registry under the @asimov-collective org — no auth needed
to install.
pnpm add @asimov-collective/calvin agentationEnvironment variables
| Variable | Where | Purpose |
|---|---|---|
| NEXT_PUBLIC_AGENTATION_HOST | client + server | Deployed calvin-worker host, e.g. calvin.<subdomain>.workers.dev (no https://) |
| NEXT_PUBLIC_AGENTATION_ROOM_ID | client + server | Unique id for this project, e.g. <client-slug>-site — isolates its annotations from every other project on the same Worker |
| NEXT_PUBLIC_ENABLE_AGENTATION | client + server | "true" to enable the toolbar + pins (keep "false" in production) |
| AGENTATION_WEBHOOK_SECRET | server only | Required if the target calvin-worker has a secret set (any real deployment should — see that repo's README). Sent as Authorization: Bearer <secret> on every server-side request to the Worker, including reads. Never expose this via a NEXT_PUBLIC_* var — the browser never talks to the Worker directly, it only calls this app's own /api/agentation/* routes, which attach the secret server-side. |
Setup (Next.js App Router)
1. API routes — proxy to the Worker and mirror to .agentation/ locally:
// src/app/api/agentation/webhook/route.ts
export { webhookPOST as POST } from "@asimov-collective/calvin/next-routes";
export const runtime = "nodejs";// src/app/api/agentation/annotations/route.ts
export { annotationsGET as GET } from "@asimov-collective/calvin/next-routes";
export const runtime = "nodejs";2. Mount the UI — once, e.g. in the root layout:
import { AgentationRoot } from "@asimov-collective/calvin/react";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<AgentationRoot />
</body>
</html>
);
}AgentationRoot is a no-op unless NEXT_PUBLIC_ENABLE_AGENTATION=true, and it's
lazy/client-only, so it costs nothing when disabled.
AgentationRoot mounts agentation's own toolbar — the only annotation UI. There's no
separate always-visible pin overlay: annotations are only visible while the toolbar is
active, and there's no resolved/dismissed state — deleting an annotation (agentation's
native action) is how it gets marked done. On mount, AgentationRoot seeds the
toolbar's local state from the server, so annotations created in one session show up
(numbered consistently, and editable/deletable) in any other session too.
3. Pull feedback locally for coding agents:
npx agentation-pullReads NEXT_PUBLIC_AGENTATION_HOST / NEXT_PUBLIC_AGENTATION_ROOM_ID from the
environment or .env.local, and writes:
.agentation/feedback.jsonl— one JSON annotation per line.agentation/PENDING.md— open items in markdown, meant to be handed to an agent
What's exported
@asimov-collective/calvin—protocol.tstypes +normalizeStoredAnnotation,client.tsfetch/post helpers,store.tslocal mirror writers@asimov-collective/calvin/react—AgentationRoot,AgentationToolbar@asimov-collective/calvin/next-routes—webhookPOST,annotationsGET
Multiple projects, one Worker
Deploy calvin-worker once. Give each consumer project its own
NEXT_PUBLIC_AGENTATION_ROOM_ID; they all point at the same
NEXT_PUBLIC_AGENTATION_HOST. Each room gets its own isolated Durable Object + SQLite
storage — there is no cross-project data access.
Every project using a shared Worker instance also needs the same AGENTATION_WEBHOOK_SECRET
that Worker was deployed with (see calvin-worker's README) — the room id isolates data,
but the secret is what keeps the endpoint itself from being wide open to the internet.
