Klein Kit

Task document

How state travels between agents that can't read each other's sessions

No provider can read another's session. Klein Kit carries continuity across handoffs as a structured task document — kept current from native events and injected at each handoff.

interface TaskState {
  goal: string;
  status: "pending" | "in_progress" | "blocked" | "completed" | "failed";
  plan: PlanStep[];
  todos: TodoItem[];
  decisions: string[];
  openQuestions: string[];
  filesTouched: FileChange[];
  history: HandoffRecord[];   // per-agent history with resumable session ids
}

interface HandoffRecord {
  provider: ProviderId;
  nativeSessionId?: string;   // resume any past leg of the work
  role: string;
  summary?: string;
  startedAt: number;
  endedAt?: number;
}

Where it comes from

Every session maintains one automatically at session.taskState, folded from events:

  • turn.started moves pending → in_progress
  • plan.updated / todo.updated replace the plan/todos
  • file.changed upserts by path (a delete wins; a create survives a later modify)
  • turn.failed / session.error mark it blocked

Workflows create one from the workflow goal and thread it through every step; each handoff event carries the current state.

Using it directly

import { createTaskState, applyEventToTaskState, renderTaskState } from "@klein-kit/core";

const state = createTaskState("Migrate the config loader to zod");

for await (const event of session) {
  applyEventToTaskState(state, event);
}

// Deterministic markdown — inject into the next agent's prompt:
const doc = renderTaskState(state);

renderTaskState produces sections for the task, status, work so far, plan, todos, decisions, files touched, and open questions — with [x] / [~] / [ ] status markers. It's deterministic, so it diffs cleanly and caches well in prompts.

On this page