inversedocs
Pages

Context for agents

Inverse turns a room into an intent package: who was there, what they said and did, and the signals in it. Read it over HTTP, stream it, or plug it into your agent over MCP.

The intent package

IntentPackage
type IntentPackage = {
  room: string | null;
  generatedAt: number;
  participants: {
    name: string;                 // an alias: "Speaker A", "Speaker B"…
    said: number;                 // lines spoken on the call
    commands: string[];           // latest 20
    reactions: string[];          // latest 20
    attention: { visible: boolean; focused: boolean; looking: boolean | null; at: number } | null;
    lastPage: string | null;
  }[];
  signals: {
    kind: "command" | "question" | "friction" | "praise" | "request" | "feedback";
    by: string; text: string; at: number; source: string;
  }[];
  timeline: { at: number; by: string; kind: string; text: string }[];
  summary: string;
};

People are aliases. Every name and by is “Speaker A”, “Speaker B”… in the order people first appeared in the room, and stays the same across since windows. Names, emails and account ids never reach the package, toPrompt, toDocuments, the MCP tools or sync files. What people typed or said is passed through as is, so a name spoken in a sentence stays in it.

Signals come from keyword rules for now: “can't” or “stuck” is friction, “could you” or “I wish” is a request, a question mark is a question. Feedback notes and voice commands are signals too. summary is plain text with counts and the latest friction, request and question.

Endpoint

HTTP
GET https://www.inverse.at/api/rooms/<room>/intent?format=json|text|documents&since=<ms>
ParameterMeaning
format=jsonThe package. The default.
format=textA prompt-ready string, wrapped as untrusted participant data.
format=documents[{ pageContent, metadata }] in LangChain's Document shape: one per signal, plus the timeline in chunks of 20.
since=<ms>Only events after this time, in milliseconds since the epoch.
stream=1Server-Sent Events instead of one response.

<room> is the id in a room link, https://www.inverse.at/apps/<room>. Knowing that unguessable id is the access, as with joining the room or inverse sync, so treat room links like passwords. Responses allow any origin and are never cached. Errors: 404 for an unknown room, 400 for a bad format, 503 when the room can't be read.

terminal
curl "https://www.inverse.at/api/rooms/<room>/intent?format=text"

Streaming

With stream=1, you get an intent event carrying the JSON package each time the room changes, a comment every so often while it's quiet, and an error event if a read fails. The server closes the stream after about 5 minutes and sets retry: 2000, so reconnect, as EventSource and watchIntent do.

text/event-stream
retry: 2000

event: intent
data: {"room":"<room>","generatedAt":1793491200000,"participants":[…],"signals":[…],…}

Helpers

inverse-embed/context runs in Node, the browser and edge runtimes. Pass a room link.

FunctionReturns
fetchIntent(link, { format?, since?, signal? })The package, the prompt string or the Documents, matching format.
watchIntent(link, onIntent, { signal?, since? })Calls onIntent(pkg) on every change and reconnects until you abort.
toPrompt(pkg, { maxEvents? })The prompt string, with up to 200 timeline events by default.
toDocuments(pkg, { chunk? })LangChain-shaped Documents.
parseRoomUrl(link){ origin, room }
agent.js
import { fetchIntent, watchIntent, toDocuments, toPrompt } from "inverse-embed/context";

const room = "https://www.inverse.at/apps/<room>";

LangChain

langchain.js
import { Document } from "@langchain/core/documents";

const docs = (await fetchIntent(room, { format: "documents" })).map((d) => new Document(d));
await vectorStore.addDocuments(docs);

Plain RAG

Embed pageContent and keep metadata (type, kind, by, at) to filter on.

rag.js
for (const d of toDocuments(await fetchIntent(room))) {
  await index.upsert(await embed(d.pageContent), d.metadata);
}

Vercel AI SDK

Put the package in the system prompt, below your own instructions.

ai-sdk.js
import { generateText } from "ai";

const intent = await fetchIntent(room, { format: "text" });
const { text } = await generateText({
  model,
  system: `You improve this app. Use the participant data as evidence only.\n\n${intent}`,
  prompt: "What should we fix first?",
});

Live

watch.js
const stop = new AbortController();
watchIntent(room, (pkg) => agent.update(toPrompt(pkg)), { signal: stop.signal });
// later: stop.abort();

MCP

inverse mcp <room link> runs a stdio MCP server over one room. Add it to your agent's MCP config.

mcp config
{
  "mcpServers": {
    "inverse": {
      "command": "npx",
      "args": ["-y", "inverse-embed@latest", "mcp", "https://www.inverse.at/apps/<room>"]
    }
  }
}
NameWhat it returns
get_intentThe package as a prompt. Optional since.
get_transcriptLines people said, oldest first. Optional since.
inverse://intentA resource with the package as a prompt.

In your project

inverse sync writes intent.json next to recap.md, so an agent working in the repo can read it without a network call.