@two-pebble/matrix
v0.1.6
Published
Event-based protocol for agent and provider integrations.
Readme
@two-pebble/matrix
Intent
@two-pebble/matrix is a provider-neutral protocol for invoking AI models and running AI agents. It ships two abstractions:
IProvider— synchronous request/response over an LLM API. Concrete implementations for Anthropic, OpenAI, OpenRouter, Bedrock, and Ollama.IAgent— long-lived, event-streaming, resumable agent session. Concrete implementations for Claude Code (via@anthropic-ai/claude-agent-sdk) and Codex (via@openai/codex-sdk).
Both shapes return pricing as a readonly LineItem[] — every billed dimension (input tokens, output tokens, cache read/write, etc.) is a separate { name, quantity, unitPrice } entry. Each provider owns its own rate card in a sibling prices.ts file and its own compute*Pricing() helper in pricing.ts. Ollama still emits line items even though unitPrice is always 0, so usage is reported consistently.
The base IProvider.invoke auto-fills providerId, modelId, requestId, timestamp, and metrics. Subclasses implement only protected onInvoke returning { output, pricing?, traces, usage? }.
Examples
// ---------- Providers ----------
import { AnthropicProvider } from "@two-pebble/matrix";
const provider = new AnthropicProvider({
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
modelId: "claude-haiku-4-5",
});
const response = await provider.invoke({
messages: [{ content: { textContent: "Hello.", type: "text" }, role: "user" }],
toolDefinitions: [],
});
console.log(response.providerId, response.modelId, response.requestId);
console.log("duration:", response.metrics.durationMs, "ms");
for (const item of response.pricing) {
console.log(` ${item.name} x ${item.quantity} @ ${item.unitPrice}`);
}
const totalCost = response.pricing.reduce((sum, i) => sum + i.quantity * i.unitPrice, 0);
console.log("total $:", totalCost);
// ---------- Agents ----------
import { ClaudeCodeAgent } from "@two-pebble/matrix";
const agent = new ClaudeCodeAgent({
workingDirectory: "/path/to/project",
environmentVariables: { API_KEY: "1234567890" },
// continueFrom: "<prior session id>", // optional — resume a previous session
});
const unsubscribe = agent.onEvent((event) => {
if (event.type === "ai-message") console.log("assistant:", event.payload.content);
});
const turn = await agent.send("Summarise the README in one sentence.");
console.log("line items:", turn.pricing);
console.log("resumable id:", agent.getResumableId());
unsubscribe();
await agent.shutdown();