Channels
See Core Concepts → Channels & state for the full explanation of why state is modeled this way. This page is the terse signature reference.
Channel<V, U>
Section titled “Channel<V, U>”export interface Channel<V, U = V> { readonly "~value": V; readonly "~update": U; readonly reduce: (current: V, update: U) => V; readonly initial: () => V;}
export type StateOf<C> = { readonly [K in keyof C]: C[K] extends Channel<infer V, any> ? V : never };lastChannel(init)
Section titled “lastChannel(init)”export function lastChannel<T>(init: T): Channel<T, T>;Overwrite semantics — each update replaces the current value.
const review = lastChannel<ReviewResult | null>(null);listChannel()
Section titled “listChannel()”export function listChannel<T>(): Channel<readonly T[], T | readonly T[]>;Append semantics — accepts either a single item or an array, and appends. Starts at [].
const transcript = listChannel<Msg>();inputChannel()
Section titled “inputChannel()”export interface InputChannel<T> extends Channel<T, T> { readonly "~input": true;}export function inputChannel<T>(): InputChannel<T>;Same overwrite semantics as lastChannel, but with no static init value — it’s seeded by whatever input a team() run is actually called with. It’s branded with "~input": true so TeamInputOf<State> can pick these channels out of a team’s state record to derive the run’s input shape:
export type TeamInputOf<State> = { readonly [K in keyof State as State[K] extends InputChannel<any> ? K : never]: State[K] extends InputChannel<infer T> ? T : never;};const issue = inputChannel<Issue>();export const END: "~end" = "~end";export type END = typeof END;The sentinel a workflow() .branch/.edge target, or a team() .router, returns to terminate a run.