This page builds the same deterministic agent that ships as the minimal-tool-agent example: it reads one item from an immutable in-memory catalogue, produces a verified artifact, and exports an EvidenceRecord. No provider key, no network.
Compatibility: Pactmark 0.1.x. Every symbol below comes from @pactmark/agent, which re-exports the pieces of @pactmark/core you need for authoring.

1. Declare the schemas

Everything that crosses a boundary has a versioned schema identity. semanticRevision is what makes a meaning change visible even when the shape looks similar.
src/example.ts
Use .strict(). An open object silently accepts fields the rest of the system has never reasoned about, and those fields end up in digests, contexts and evidence.

2. Define the tool

A tool is not just a function. It carries a security declaration that policy will enforce and that contributes to its registration digest.
src/example.ts
maxCallsPerRun and timeoutMs are not hints. They are enforced ceilings — a looping model cannot spend its way past them, which is exactly the point.

3. Describe the model boundary

Even a deterministic local model must declare a security profile and a resource profile. Those two digests travel with the run, so a change of provider, region, retention or output cap is visible rather than silent.
src/example.ts
The driver itself is an async generator that yields either a tool_call or a final value. Here it is fully deterministic, which is what makes the example testable:
src/example.ts
The driver references the tool by its registration digest, not by name. If the tool’s schema, security metadata or implementation version changes, that digest changes and the old reference stops resolving. Drift becomes an error instead of a surprise.

4. Compile the agent

src/example.ts
Note default: "deny" — the type only accepts that value. There is no configuration in which a Pactmark policy defaults to allow.

5. Ask for the work

Figure 1. A WorkOrder is the request, not the agent. The same compiled agent can be accepted under many different work orders with different purposes, budgets and data classes.
src/example.ts

6. Start it and read the results

src/example.ts
Every read takes authority as its first argument. There is no ambient “current user” anywhere in the API, which is what makes cross-tenant access a compile-time conversation rather than a runtime accident.

What you just built

A compiled contract

Instructions, schemas, tools, model profiles, policy and verifiers, reduced to versioned identities with digests.

An append-only history

RunAccepted, ToolCallCompleted, RunCompleted — validated, ordered and replayable.

A content-addressed artifact

Output stored under the digest of its exact bytes, with verification bound to that digest.

An honest readiness report

production: false, because a memory store is not durability and the framework says so.

Next steps

Give the tool real teeth

Egress allowlists, effect strategies and what each ToolSecurity field actually enforces.

Add a human decision

Challenge, one-use proof, atomic consumption, and what approval can never waive.

Test the unhappy paths

Denial, cancellation, budget exhaustion, crash boundaries and registration drift.

Swap in a real provider

@pactmark/ai-sdk, credential references and why the adapter never sees a raw secret.