
Actaro
Actaro is a small TypeScript SDK that verifies whether an AI agent action actually changed real state. It does not trust a tool's success message: it executes the action, reads the state again through a separate verify function, and emits a JSON-serializable receipt containing the evidence.
It is designed for developers building agent tools and teams that need a reliable audit trail for side effects such as creating records, sending messages, provisioning resources, or processing payments.
Start here: 5-minute quickstart · API reference · Runnable examples
Why Actaro?
An agent tool returning success does not prove that the intended state change occurred. Actaro makes the verification step explicit and gives the calling agent and the team a structured result:
- Verified effects: read the resulting state independently from the action response.
- Safe retries: retry eventual consistency without blindly repeating completed work.
- Idempotency: deduplicate concurrent or repeated requests with the same key.
- Receipts: preserve a JSON-serializable record of the action, verification, and evidence.
- Agent feedback: expose
toolResultandcanClaimCompletionto the model.
Installation
npm install actaro-sdk zodActaro requires Node.js 20 or newer.
Choose the path that matches your use case:
- Developer: follow the quickstart and run
examples/memory-task.ts. - MCP integration: adapt a tool with
fromMcpTool()and add an independent verifier; seeexamples/local-mcp-style.ts. - Team or production rollout: review persistence, redaction, access control, and retention in the production guide.
5-minute quick start
import { actaro, defineAction } from "actaro-sdk";
import { z } from "zod";
const action = defineAction({
name: "create-task",
input: z.object({ title: z.string().min(1) }),
execute: ({ title }) => taskApi.create({ title }),
verify: async ({ title }) => {
const task = await taskApi.findByTitle(title);
return task
? { status: "verified", evidence: { id: task.id, state: task.state } }
: { status: "pending", reason: "Task not visible yet" };
},
});
const receipt = await actaro.run(action, { title: "Publish release notes" });If the task is not immediately visible, return pending from verify; Actaro retries it according to the configured verification policy. Only a verified receipt should be treated as completion by the agent.
defineAction also accepts description, metadata, and an idempotencyKey(input) function. Configure retry and timeout globally with createActaro({ verification: { retries, delayMs, timeoutMs } }), or override them for one call through the third argument to run. A pending verification is retried; verified and failed results stop immediately. Execution and verification exceptions become failed receipts. Invalid input throws Zod's validation error before execution.
Receipts and persistence
Every run produces an ActionReceipt with its UUID, action information, status, ISO dates, verification-attempt count, sanitized input, execution result, final verification, evidence, and reason. Receipts contain JSON values only.
The default client uses an in-memory store. Use a dedicated client for explicit persistence:
import { createActaro, fileStore } from "actaro-sdk";
const client = createActaro({ store: fileStore("./data/receipts.jsonl") });ReceiptStore has asynchronous save, get, list, and getByIdempotencyKey methods. memoryStore() and append-only JSONL fileStore(path) are included. Read persisted receipts with actaro list ./data/receipts.jsonl or actaro get ./data/receipts.jsonl <id>. Concurrent requests with matching idempotency keys are automatically deduplicated.
Security and redaction
Receipts may otherwise preserve sensitive input, output, metadata, or evidence. Configure recursive key redaction before storing them:
createActaro({ redaction: { fields: ["password", "apiKey", /token/i] } });Redaction is a safety aid, not a substitute for minimizing collected data, access controls, retention limits, and encryption. Verification functions should return the smallest useful proof. Hooks (executionStarted, executionFinished, verificationAttempt, and receiptCreated) should also avoid logging secrets because they receive live values.
MCP tools
fromMcpTool() adapts a tool call into an action. Its call function invokes the MCP-style tool, while its required verify function independently reads real state. A textual tool response is never evidence by itself. See examples/local-mcp-style.ts.
Agent Feedback
Format receipts directly for LLM tools using toAgentResult:
import { toAgentResult } from "actaro-sdk";
const receipt = await actaro.run(action, input);
const { toolResult, canClaimCompletion } = toAgentResult(receipt);
// Send toolResult back to the LLM (OpenAI, DeepSeek, Anthropic, etc.)
// canClaimCompletion is true only if the action was verifiedIntegrations and adoption
Actaro is framework-agnostic. The public API is available from the package root, and the core flow can be embedded in an existing agent tool, MCP server, or workflow activity:
- Define the action and its input schema.
- Execute the side effect.
- Verify the resulting state through a separate read.
- Return the receipt to the agent and persist it when the team needs an audit trail.
See the integration guide for MCP and LLM-tool patterns. If you are adding an adapter or have a production use case, open a GitHub discussion.
Actaro versus Temporal
Actaro is a verification SDK: it runs one operation, checks its effect, and records a receipt. Temporal is a durable workflow orchestration platform offering scheduling, recovery, distributed execution, and long-running workflow state. Actaro neither replaces nor embeds a workflow engine; it can be called from a Temporal activity when both durable orchestration and effect verification are needed.
Resources
Development
npm test
npm run lint
npm run buildThe examples/ directory also includes an in-memory task and eventual-consistency retry example. The public API is exported from the package root. See CONTRIBUTING.md, SECURITY.md, and CHANGELOG.md.