Skip to content

TypeScript Setup

Install the core SDK, the Vitest integration and Vitest:

Terminal window
npm install -D "@attest-ai/core@>=1.1.0" "@attest-ai/vitest@>=1.1.0" "vitest@^4"

Use the agent() wrapper from @attest-ai/core: it supplies a TraceBuilder and returns an AgentResult. Your code or adapter records each LLM and tool call; the wrapper does not intercept arbitrary calls automatically. This is the quickstart-ts example from attest-examples:

quickstart-ts/agent.ts
/**
* Simple customer support agent for Attest quickstart example.
*
* TypeScript port of the Python quickstart — same agent, same trace structure.
*/
import { agent, type TraceBuilder } from "@attest-ai/core";
export const customerSupport = agent(
"customer-support",
(builder: TraceBuilder, args: Record<string, unknown>) => {
const orderId = args.order_id as string;
// Simulate LLM call to understand the customer's request
builder.addLlmCall("gpt-4", {
args: {
messages: [
{
role: "user",
content: `Customer wants to return order ${orderId}. Process the refund.`,
},
],
},
result: { response: "I will process the refund for this order." },
});
// First tool: look up the order details
builder.addToolCall("lookup_order", {
args: { order_id: orderId },
result: {
order_id: orderId,
status: "delivered",
amount: 89.99,
customer: "John Doe",
},
});
// Second tool: process the refund
builder.addToolCall("process_refund", {
args: { order_id: orderId, amount: 89.99 },
result: { refund_id: "RFD-001", status: "processed" },
});
// Set metadata
builder.setMetadata({
total_tokens: 150,
cost_usd: 0.005,
latency_ms: 1200,
model: "gpt-4",
});
return {
message: `Your refund of $89.99 has been processed successfully. Refund ID: RFD-001. The funds will appear in your account within 3-5 business days.`,
structured: {
refund_id: "RFD-001",
amount: 89.99,
status: "processed",
},
};
},
);

Build the assertion chain with attestExpect from @attest-ai/core and evaluate it with evaluate from @attest-ai/vitest:

quickstart-ts/agent.test.ts
/**
* Tests for the customer support agent.
*
* Demonstrates Attest assertions across layers 1-4:
* - Layer 1: Schema validation
* - Layer 2: Cost and performance constraints
* - Layer 3: Trace structure (tool ordering)
* - Layer 4: Content validation
*/
import { attestExpect } from "@attest-ai/core";
import { evaluate } from "@attest-ai/vitest";
import { describe, it, expect } from "vitest";
import { customerSupport } from "./agent";
describe("customer support agent", () => {
it("processes refunds correctly", async () => {
const result = customerSupport({ order_id: "ORD-12345" });
const chain = attestExpect(result)
// Layer 1: Schema validation
.outputMatchesSchema({
type: "object",
properties: {
refund_id: { type: "string" },
amount: { type: "number" },
status: { type: "string" },
},
required: ["refund_id", "status"],
})
// Layer 2: Cost and performance constraints
.costUnder(0.01)
.latencyUnder(5000)
.tokensUnder(500)
// Layer 3: Trace structure
.toolsCalledInOrder(["lookup_order", "process_refund"])
.requiredTools(["lookup_order", "process_refund"])
.forbiddenTools(["delete_order", "cancel_refund"])
// Layer 4: Content validation
.outputContains("refund")
.outputContains("processed")
.outputNotContains("error");
const evaluated = await evaluate(chain);
expect(evaluated.passed).toBe(true);
});
});

Before the first test, download and check the matching engine with npx attest doctor. On a cold machine the initial download can exceed Vitest’s default five-second test timeout. Then run the suite:

Terminal window
npx attest doctor
npx vitest run

evaluate(chain) throws on a hard failure and skips the test when an assertion could not be evaluated. Use inspect(chain) to get the results without enforcing them.

Build traces manually for custom agent architectures. The metadata below is invented sample usage, not a provider price or measured API cost:

import { AgentResult, TraceBuilder, attestExpect } from "@attest-ai/core";
import { evaluate } from "@attest-ai/vitest";
import { it } from "vitest";
it("answers arithmetic", async () => {
const builder = new TraceBuilder("my-agent");
builder.setInput({ query: "What is 2 + 2?" });
builder.addLlmCall("gpt-4.1", { args: { model: "gpt-4.1" }, result: { content: "4" } });
builder.setOutput({ message: "The answer is 4." });
builder.setMetadata({ total_tokens: 50, cost_usd: 0.001, latency_ms: 800 });
const result = new AgentResult(builder.build());
await evaluate(attestExpect(result).outputContains("4").costUnder(0.05));
});
VariablePurpose
OPENAI_API_KEYOpenAI API key for judge/embedding assertions
ATTEST_ENGINE_PATHOverride engine binary location
ATTEST_SIMULATIONSkip embedding and judge provider calls; application calls still need mocks
ATTEST_JUDGE_PROVIDERLLM judge provider: openai, anthropic, gemini, acp
ATTEST_JUDGE_MODELModel for judge assertions (e.g., gpt-4.1)