LaufwerkLaufwerk
API reference

Types and schemas

How to define workflow input, activity results and structured agent output, with checked schema examples.

Laufwerk uses Effect Schema for runtime validation and persistence. A TypeScript interface alone describes what the compiler expects; a schema also checks a value received from JSON, a human, a service or an agent. Import Schema from effect.

Which schema goes where?

ArgumentAccepted schemaPurpose
Workflow.make({ payload })Struct fields or compatible struct schemaDecode workflow input
Workflow / Activity successEffect SchemaRecord the returned result
Workflow / Activity errorEffect SchemaRecord typed failures
Session outputSchema.Schema<A, I, never>Generate JSON guidance and decode agent output
AI SDK tool inputSchemaAI SDK flexible schema, such as ZodDescribe and validate tool arguments

The following are common Effect Schema building blocks, not an exhaustive reference for the entire Effect library.

ExpressionValue it describes
Schema.String, Schema.Number, Schema.BooleanPrimitive values
Schema.Literal("approve", "revise")Exactly one of these string literals
Schema.Struct({ title: Schema.String })Object with required title
Schema.Array(Schema.String)Array of strings
Schema.optional(Schema.String)Optional property in a struct
Schema.NullOr(Schema.String)String or explicit null
Schema.Union(A, B)A value matching either schema
Schema.VoidNo result value; default workflow/activity success
Schema.NeverNo typed failure value; default workflow/activity error

Optional and nullable are different. A required nullable field must still be present; an optional string can be omitted but does not accept null.

Example: a review result

review-schema.ts
import { Schema } from "effect";

export const Review = Schema.Struct({
  decision: Schema.Literal("approve", "revise"),
  summary: Schema.String,
  findings: Schema.Array(Schema.Struct({
    file: Schema.String,
    explanation: Schema.String,
  })),
  nextStep: Schema.optional(Schema.String),
});

export type ReviewResult = typeof Review.Type;
export const example: ReviewResult = {
  decision: "revise",
  summary: "An empty input produces an incorrect total.",
  findings: [{ file: "total.ts", explanation: "Return zero for an empty list." }],
};

Pass Review as a session's output; the successful result has the inferred ReviewResult type. Valid JSON matching this shape is still not proof that the review's claims are correct.

Encoded and decoded values

Schema.Schema<A, I, R> describes decoded value A, encoded representation I, and any required services R. typeof SomeSchema.Type extracts A; typeof SomeSchema.Encoded extracts I.

For ordinary structs these often match. A transform can make them different: Schema.NumberFromString accepts a numeric string and decodes it to a number.

schema.test.ts
import { expect, test } from "bun:test";
import { Schema } from "effect";

test("decode, optional and nullable fields have distinct contracts", () => {
  expect(Schema.decodeUnknownSync(Schema.NumberFromString)("42")).toBe(42);
  const Input = Schema.Struct({
    note: Schema.optional(Schema.String),
    reason: Schema.NullOr(Schema.String),
  });
  expect(Schema.decodeUnknownSync(Input)({ reason: null })).toEqual({ reason: null });
  expect(() => Schema.decodeUnknownSync(Input)({ reason: null, note: null })).toThrow();
  expect(() => Schema.decodeUnknownSync(Input)({})).toThrow();
});

Session output schemas must require no services (R = never) and support JSON Schema generation. Arbitrary runtime values such as a live process, function or execution provider cannot be sent back by the agent as JSON. Prefer a plain data schema for a result you intend to record.

Error types

The root SDK exports SessionError, WorkspaceError and HumanError. Each is an Error subclass with a matching name, a message, and an optional cause. Construct them using the standard new ErrorType(message, { cause }) shape.

An Effect's typed failure and a defect are different. SDK operations fail in the typed error channel; unexpected thrown errors can become defects. If your workflow declares error: Schema.String, map its typed errors with Effect.mapError(error => String(error)). This does not catch defects or make external side effects safe to retry.

On this page