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.

eventimmutable factprojectionMachine<Event, State, Value>initial · step · outputcomponentProjection<State, ComponentOutput>named · composableviewvalue for its parenttransitionsintents + effects

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 reconstruct projected state from the stored events. Recovery requires compatible component code and configuration; external work whose outcome was not recorded may run again.

interface
ts
interface Event {
  readonly type: string
  readonly [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 summarizes the events relevant to the projection's output. A projection can also receive immutable dependency data during initialization. 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
ts
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 machine over events. Its output contains a view for its parent, enabled transitions, and optional interactions. Authors define its state through initial, step, and output; the runtime manages the machine.

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.

Effects that call an LLM receive Effect’s LanguageModel service from the host. Shared model utilities collect streams, enforce supplied request timeouts, and encode provider errors. Each component owns its prompts and interpretation: inference derives tool requests and turn outcomes, while compaction accepts a summary and records its checkpoint. The model package has no dependency on the agent package.

interface
ts
import type {
  ComponentOutput, Event, TransitionContext
} from "tardie/deprecated/core"

// LeafComponent shows the required fields for a leaf component.
interface LeafComponent<State, View, Requirements = never> {
  name: string
  initial(): State
  step(state: Readonly<State>, event: Event,
    context: TransitionContext): State
  output(state: Readonly<State>):
    ComponentOutput<View, Requirements>
}

Component authors keep their state type through component. Tardigrade erases that private type only when it composes different component machines.

interface
ts
import { component } from "tardie/deprecated/core"

const counter = component({
  name: "counter",
  initial: () => 0,
  step: (count, event) =>
    event.type === "Counted" ? count + 1 : count,
  output: (count) => ({
    view: count,
    transitions: []
  })
})

Example: compaction

Before an inference request, compaction checks the active model's context window from model metadata. With a 128k window and the default triggerRatio: 0.8, 104k tokens cross the threshold and triggers a summary of the older span. The effect appends CompactionCompleted. Later model requests use its summary with a tail budget of 64k tokens, while the full event log remains available. Set triggerRatio and retainRatio on compaction() to change these thresholds.

event log104k tokenscompaction threshold crossedcomponent projection · compactioninitial · step · outputpure · advances one event at a timeeffect transitionsummarize old spanruntime executesevent · appended to logCompactionCompletedsummary and checkpoint recordednext model contextsummary + 64k-token tail

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
ts
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.

Components declare work through the context supplied to step(state, event, context). A local tag, the component name, and the owning event position identify each transition. The runtime attaches that reference to the final event in its result batch and uses recorded completions to avoid repeating recorded work. External services still need idempotency when a crash occurs between performing work and recording its outcome. The actor is settled when no transition remains enabled.

interface
ts
import { component, type TransitionContext, type Transition } from "tardie/deprecated/core"

type Pending = {
  context: TransitionContext
  work: Transition<never>
}

const acknowledge = component<Pending | undefined, undefined>({
  name: "acknowledge",
  initial: () => undefined,
  step: (pending, event, context) => {
    if (event.type === "Requested") {
      return {
        context,
        work: context.intent("ack", { type: "Acknowledged" })
      }
    }
    return pending?.context.matches("ack", event) ? undefined : pending
  },
  output: (pending) => ({
    view: undefined,
    transitions: pending === undefined ? [] : [pending.work]
  })
})

This component acknowledges the latest request. It retains the owning context until its completion arrives. context.effect(tag, { input, act }) declares external work; act(input, { signal }) returns an Effect containing the result events. Reuse a tag for retries of the same operation. A new operation needs a different tag or owning event.

Example: a tool-calling agent

A message enables inference. ModelReturned records the response and its usage; each requested tool also gets a ToolCalled event linked by responseId. A model tool call enables service work. Its result enables inference again. A final response completes the method call.

eventMessageReceivedeffect transitionmodel.generate(messages)eventToolCalledeffect transitionservice.call(...)eventToolReturnedeventTurnCompletedsettled

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.

interface
ts
import { actor } from "tardie/deprecated/core"
import { agentMessageMethod, infer, outputValidateOnce } from "tardie/deprecated/agent"

const assistant = actor({
  name: "assistant",
  methods: { message: agentMessageMethod },
  components: [infer([outputValidateOnce])]
})
actor / researcherCallermethodstyped callsevent log · audit-4201MessageReceived02ToolCalled03ToolReturned04TurnCompletedcomponent projectionsviews + transitionsExternal services

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. The method projection tracks history and exposes currentEpoch(id) and invocationState(invocation) queries.

interface
ts
import { actorMethod } from "tardie/deprecated/core"
import { input, output, event, projection } from "./message-contract"

const message = actorMethod({
  input,
  output,
  event,
  projection
})

The application supplies the input and output schemas, event builder, and projection. The projection has initial, step, and output; its output exposes the lifecycle queries. An optional cancellation declaration provides the cancellation event builder.

typed call · audit-42message({ text: "Audit the deployment." })method · messageevent(call)validates typed inputevent · appended to logMessageReceivedcall · audit-42actor runs transitionsand records outcomesevent · in the logTurnCompletedcall · audit-42method · messageinvocationState(invocation)typed result · audit-42completed({ output: string })