Concepts
Understand the primitives that make up a Tardigrade actor.
Tardigrade builds an agent harness from a small set of primitives. Events record what happened. Projections turn those events into state. Components compose projections into views and enabled work. The runtime executes that work and records its outcome as more events.
Events
An event is an immutable fact. Every input, decision, and durable outcome is appended to a thread's event log. Existing events never change.
The event log is the source of truth. A process can stop, move to another host, or start again and recover the same behavior from the stored events.
interface Event {
type: string
[field: string]: unknown
}
Projections
A projection is a Moore-style machine whose inputs are events. It creates an initial state, steps that state forward for each event, and reads a value from the current state.
The state is a compact summary of the history that can still affect the projection's output. During replay, Tardigrade folds the complete log through the machine. During live execution, it retains the state and steps only the new event tail.
interface Machine<Input, State, Output> {
initial(): State
step(state: State, input: Input): State
output(state: State): Output
}
type Projection<State, Value> =
Machine<Event, State, Value>
Components
A component is a named projection. Its output contains a view for its parent and the transitions that are enabled in the current state.
Components are pure. Given the same state, they produce the same output without performing external work. Components can contain other components, which lets an actor combine inference, tools, budgets, compaction, permissions, and output policies.
interface ComponentOutput<View, Requirements> {
view: View
transitions: ReadonlyArray<Transition>
}
interface Component<View, Requirements> {
name: string
machine: Projection<unknown,
ComponentOutput<View, Requirements>>
}
Component authors keep their state type through incrementalComponent. Tardigrade erases that private type only when it composes different component machines.
incrementalComponent({
name: "counter",
initial: () => 0,
step: (count, event) =>
event.type === "Counted" ? count + 1 : count,
output: (count) => ({
view: count,
transitions: []
})
})
Example: compaction
The compaction component tracks the model context as events arrive. At 104k tokens, it crosses the 80% threshold of a 128k window and enables an effect that summarizes the older span. The effect appends CompactionCompleted. Later model requests use its summary with a 64k-token tail, while the full event log remains available.
Views
A view is the value a component exposes to its parent. A system component contributes instructions. A tool component contributes tool definitions. A transcript projection contributes model messages.
A parent combines child views through an associative view algebra. This gives each component a small interface while the complete component tree produces the context needed by inference or another parent.
interface ViewAlgebra<View> {
empty: View
combine(left: View, right: View): View
}
Transitions
A transition describes one unit of work enabled by projected state. An intent proposes events without contacting the outside world. An effect performs external work and returns events that record its outcome.
Every transition has a durable key. The runtime compares that key with recorded event keys, executes work that is still owed, commits its events, and advances the projections. The actor is settled when no transition remains enabled.
type Transition = Intent | ExternalEffect
interface Intent {
kind: "intent"
key: string
events(input, at): ReadonlyArray<Event>
}
interface ExternalEffect {
kind: "effect"
key: string
act(input, signal): Effect<ReadonlyArray<Event>>
}
Example: a tool-calling agent
A message enables inference. A model tool call enables service work. Its result enables inference again. A final response completes the method call.
Actors
An actor gives the composed machines an identity and a callable interface. Each actor instance can own many threads, and each thread has its own event log and projected state.
Methods accept work from callers. Components define how the actor responds. The runtime keeps executing enabled transitions until the thread settles.
actor({
name: string,
methods: ActorMethods,
components: Component[]
})
Methods
Methods define the actor's typed callable surface. Each method validates its input, turns a valid call into an event, and projects the call's pending, completed, failed, or cancelled state.
A caller supplies a call identifier. Repeating that call is absorbed by the log, and the caller can read the same durable result after a retry or process restart.
actorMethod({
input,
output,
event(call): Event,
state(log, invocation): MethodState
})