Little documentation
Guides and APIReference

Events

HarnessEvent envelope and trace event vocabulary for sessions, models, tools, artifacts, and commits.

Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status

The event names below are the alpha trace vocabulary. The names are observable from local traces and onEvent callbacks, while metadata details may continue to evolve during alpha.

The trace vocabulary on this page is separate from the durable session-log contract described under HarnessEvent Envelope.

UNRELEASED: the durable validators normalizePersistedDurableHarnessEvent and canonicalHarnessEventRequestDigest, and the dedicated @little-stack/harness/events and @little-stack/harness/remote-session entry points, exist only in the current source. No npm version includes them yet. In the current source these two entries are the only home of the durable event contract (HarnessSessionLog, DurableHarnessEventInput, createHarnessAppendIdempotencyKey, emitHarnessOccurrence, …) and of the session-log helpers; the root entry no longer re-exports them.

Published 0.2.0-alpha.0 has neither the validators nor an ./events or ./remote-session package export. It exports the older durable and remote helpers from the root @little-stack/harness entry: emitHarnessOccurrence, remoteSessionLog, startSessionLogServer, createFileSessionLog and createInMemorySessionLog (renamed fileSessionLog and inMemorySessionLog in the current source). See Small subpaths.

// Published 0.2.0-alpha.0 (the `little-harness` package): older helpers from the root entry.
import { createFileSessionLog, remoteSessionLog } from "little-harness";

// UNRELEASED: the session logs and the durable event contract have their own entries.
import { fileSessionLog, remoteSessionLog } from "@little-stack/harness/remote-session";
import {
  canonicalHarnessEventRequestDigest,
  normalizePersistedDurableHarnessEvent,
  type HarnessSessionLog,
} from "@little-stack/harness/events";

HarnessEvent Envelope

HarnessEvent is the versioned trace/callback event object emitted by onEvent and written to Local Host trace.ndjson files. Every persisted Local Host trace event uses the same envelope:

type HarnessEvent<TEventType extends string = HarnessEventType> = {
  schemaVersion?: "lh.trace.v2";
  eventId?: string;
  sequence?: number;
  occurrenceId?: string;
  type: TEventType;
  sessionId: string;
  turnId?: string;
  stepId?: string;
  parentEventId?: string;
  timestamp: string;
  metadata?: Record<string, unknown>;
};

Most consumers should key off type, sessionId, turnId, and occurrenceId, then read type-specific details from metadata.

FieldMeaning
schemaVersionTrace schema version. Local traces currently use lh.trace.v2.
eventIdUnique id for this emitted trace/callback event.
sequenceMonotonic sequence number within one trace.ndjson file.
occurrenceIdCorrelation id for one model call, tool call, runtime command, or other logical occurrence.
typeDotted event name such as harness.session.started, harness.model.called, or harness.tool_call.failed.
sessionIdHarness session id.
turnIdTurn id for the current generateHarness() or streamHarness() call.
stepIdOptional model step id for multi-step model/tool loops.
parentEventIdOptional parent event id for nested event relationships.
timestampISO timestamp for when the event was emitted.
metadataEvent-specific details after trace redaction. Defaults to {} in validated local traces.

Example trace event:

{
  "schemaVersion": "lh.trace.v2",
  "eventId": "evt_abc123",
  "sequence": 3,
  "type": "harness.model.called",
  "sessionId": "chat_123",
  "turnId": "turn_abc123",
  "occurrenceId": "evt_model_call",
  "stepId": "step_1",
  "timestamp": "2026-06-10T12:00:00.000Z",
  "metadata": {
    "model": { "provider": "openai", "modelId": "gpt-5" }
  }
}

HarnessEvent is not the same object as a durable replay event. Durable replay stores payloads. The shape differs by version:

VersionDurableHarnessEventInputPersisted event adds
Published 0.2.0-alpha.0type, runId, optional occurrenceId, payload (a JSON object)eventId, sequence, recordedAt
UNRELEASED source (no npm version yet)The type below: a required appendIdempotencyKey, optional correlation ids, and either payload or payloadArtifactIdeventId, sequence, recordedAt, canonicalRequestDigest

The type below and the three paragraphs after it describe the UNRELEASED durable contract, including the Little Compute adapter, which is also unpublished. None of it is in 0.2.0-alpha.0; see Alpha Migrations.

// UNRELEASED durable event input.
type DurableHarnessEventInput<TEventType extends string = HarnessEventType> = {
  type: TEventType;
  runId: string;
  appendIdempotencyKey: string;
  occurrenceId?: string;
  operationId?: string;
  taskId?: string;
  attemptId?: string;
  correlationId?: string;
  causationId?: string;
} & (
  | { payload: Record<string, unknown>; payloadArtifactId?: never }
  | { payload?: never; payloadArtifactId: string }
);

appendIdempotencyKey is mandatory and is chosen by the logical emitter before transport. Retrying the same (runId, appendIdempotencyKey) and canonical request returns the original eventId and sequence; reusing the key with different content fails. occurrenceId is a separate, optional causal group: called/responded or started/succeeded lifecycle events normally share it while using different append keys. Use createHarnessAppendIdempotencyKey() to derive an opaque key from a stable session, turn, call, or step identity.

Persisted events add eventId, a per-run sequence, canonical UTC recordedAt, and canonicalRequestDigest. Inline payloads are normalized to wire JSON before that digest is computed. Large payloads should be stored as a content-addressed Artifact and referenced with payloadArtifactId. When Little Harness runs under Little Compute, its Artifact-spooling Session-log adapter does this automatically before the event append and can verify/reconstruct the payload after the trusted runner moves.

In a self-hosted Little Compute installation, that adapter uses an exact little_compute_runner database credential. It can select and append Session events, advance the per-run sequence head, and insert an Artifact idempotency record, but it cannot update/delete event history or delete a run head. API, scheduler, reconciler, GC, provider-gateway, and time-sampler credentials have no Session-log access.

The rest of this page returns to the trace vocabulary. Large strings and JSON values use a bounded content record:

type TraceContentRef = {
  captured: boolean;
  preview?: string;
  truncated?: boolean;
  contentRef?: string;
  bytes?: number;
  sha256?: string;
  mediaType?: string;
  redacted?: boolean;
  redactionReason?: string;
};

When content is too large to inline, contentRef points to /artifacts/trace/.... The trace still records byte counts and hashes so a developer can verify what changed.

Session Events

harness.session.started
harness.session.completed
harness.session.failed

Session events identify the runtime profile, root trace id, session status, and any app-defined extraBody fields allowed by the trace policy.

Model Events

harness.model.called
harness.model.responded
harness.model.failed

Model events track each AI SDK model-loop step.

harness.model.called is emitted before a provider call. Metadata should include the step number, provider and model id when available, prompt hash, bounded message previews, tool names, tool schema hashes, and output mode.

harness.model.responded is emitted after the step completes. Metadata should include finish reason, usage, warnings, provider metadata, bounded text output, provider-exposed reasoning when available, tool calls, sources, and generated file metadata.

harness.model.failed includes the model, step number, duration, retry metadata when available, and a structured error envelope. Provider-hidden reasoning is not traced.

Tool Events

harness.tool_call.started
harness.tool_call.succeeded
harness.tool_call.failed

Large tool outputs should point to artifacts. Automatically spooled tool results also emit normal harness.file.created, harness.file.written_by_tool, and harness.artifact.created events for the /artifacts/tool-results/... file.

Tool events include a caller value:

CallerMeaning
modelThe provider returned a tool call in an agent-loop step.
runtimejust-bash JavaScript called a configured tool through tools.<name>(args).
codeWorkflow code invoked a configured tool through a code-run proxy.

harness.tool_call.started records the tool name, tool call id, step id, and bounded input metadata when the trace policy allows tool input capture.

harness.tool_call.succeeded records duration, bounded output metadata, artifact refs, and spooled output refs. If the model receives a compact file-reference response, the trace should include the full artifact path and hash.

harness.tool_call.failed records duration and an error envelope. Tool implementation source code and closure internals are never traced.

Runtime Events

harness.runtime.command.started
harness.runtime.command.succeeded
harness.runtime.command.failed
harness.runtime.command.denied
harness.runtime.tier.escalated
harness.runtime.tier.unavailable
harness.runtime.usage
harness.runtime.error
harness.runtime.dispose.failed

Runtime events are for the execution substrate, such as the built-in bash tool. harness.runtime.command.* events record commands, exit codes, duration, bounded stdout and stderr previews, and file changes discovered by the runtime. Every sandbox emits them identically.

harness.runtime.tier.escalated, harness.runtime.tier.unavailable, and harness.runtime.command.denied appear only under tieredSandbox. They record why a turn switched execution tier, why a needed tier could not be used, or why a policy refused a command — with the classification reasons — and never reach the model's prompt. See Execution Tiers.

harness.runtime.usage is emitted once per turn that had a shell sandbox, when the sandbox is disposed and before the turn's terminal session event, so a LittleDB trace of the turn includes it. It records which execution tier the turn reached and what the sandbox used, for cost accounting; it never reaches the model. Its metadata: tier ("tier-0" for the emulated shell over the session folder, "next-tier" for a real execution environment), commands (tier0, nextTier and denied counts), escalations, and sandbox, the HarnessSandboxUsage the sandbox reported: provider, instanceType, vcpu, memoryGiB, diskGB, activeMs (milliseconds commands were running), awakeMs (what the vendor billed as provisioned, when known) and costUsd (the provider's estimate at list price, when it knows the rates). The built-in just-bash sandboxes report provider: "just-bash" and their active time; a custom sandbox reports its own through HarnessSandbox.usage(), and one that reports nothing emits no usage event. See Execution Tiers.

harness.runtime.dispose.failed records a sandbox whose teardown threw, with the error envelope under metadata.error. It never fails the turn, but little-harness doctor counts it as a failure.

File Events

harness.file.created
harness.file.updated
harness.file.deleted
harness.file.staged_from_message
harness.file.staged_from_host
harness.file.written_by_tool
harness.artifact.created
harness.filesystem.mounted

The goal is not to log every byte of every file. The trace should explain what changed and where the data is stored.

harness.filesystem.mounted records roots that were made available to the turn:

{
  "root": "skills",
  "harnessDir": ".agents/skills",
  "source": "skill",
  "writable": false,
  "fileCount": 3,
  "files": [
    {
      "path": ".agents/skills/customer-research/SKILL.md",
      "kind": "file",
      "bytes": 1842
    }
  ]
}

File change events should include path, root, source, before/after byte and hash metadata, and a diff record when the changed content is text:

type TraceFileDiff =
  | {
      available: true;
      format: "unified";
      preview?: string;
      truncated: boolean;
      contentRef?: string;
      bytes?: number;
      sha256?: string;
    }
  | {
      available: false;
      reason:
        | "disabled"
        | "non_text"
        | "too_large"
        | "redacted"
        | "content_unavailable";
    };

Deleted file events should record metadata and path, not deleted file contents. When a deleted FileRef includes byte/hash fields, they must match the before version; created and updated references must match after.

UNRELEASED: createHarnessFileMutationEvents() exists only in the current source. Published 0.2.0-alpha.0 has no such symbol, and no npm version includes it yet.

Trusted local and remote adapters share createHarnessFileMutationEvents(). Its caller must allocate one stable occurrenceId at the file-mutation boundary, before transport; every file/tool/artifact projection of that one mutation shares the ID. Repeated byte-identical writes are separate mutations and therefore receive different occurrence IDs instead of collapsing during durable append replay. The serializer rejects noncanonical paths and version contradictions, accessors, symbols, non-JSON values, JSON deeper than 64 levels or larger than 10,000 nodes/256 KiB, strings larger than 64 KiB, keys larger than 1 KiB, paths larger than 4 KiB, and source/media/artifact labels larger than 1 KiB. These are event metadata limits, independent of Artifact and File Snapshot content limits.

Persistent Dir Events

harness.persistent_dir.loaded
harness.persistent_dir.commit.started
harness.persistent_dir.commit.succeeded
harness.persistent_dir.commit.failed

Commit failures should be host-facing. They should not be automatically inserted into the agent's normal context.

Persistent Dir metadata should include harnessDir, commit policy, file counts, change counts, storage adapter metadata allowed by policy, duration, and error details for failed commits.

Outcome Events

outcome.reported

outcome.reported is a side-channel event: a verdict appended to a session's trace after a run by reportHarnessOutcome, createHarnessOutcomeReporter, or a Chat SDK reaction. It is a valid trace event but deliberately not part of the durable session log — an outcome is never an input to the run it describes.

Metadata carries status ("success" | "failure" | "partial") and source, plus optional retracted, score, detail, reporter (always a pseudonym), reportKey, stepPath, promptHash, and caller-supplied extras. See Outcome Capture.

Subagent Events

Subagent trace events are deferred for the alpha release. If they are added in a later version, they should link back to the parent session and use the same content-free metadata rules as the rest of the trace vocabulary.

What Is Not Traced

Little Harness traces should not capture:

  • API keys, auth headers, cookies, bearer tokens, or provider credentials
  • raw mounted file contents
  • raw binary payloads such as PDFs, images, archives, audio, or video
  • hidden model reasoning not exposed by the provider
  • deleted file contents after deletion
  • Persistent Dir backing-store credentials
  • tool implementation source code or closure internals
  • anything matched by the configured redaction policy

On this page