Product / How it works

The flow is the application. The conversation is the UI.

A PicoFlow flow models your business process as explicit stages in TypeScript. Within each stage the model conducts the conversation freely; around it, the runtime enforces durable state, typed tools, validation, and transitions — bounded autonomy, built for customer-facing production software.

  • Bounded autonomy
  • One flow per session
  • Self-hosted session storage

A deliberate turn loop

A flow is a durable cursor through a customer conversation.

Every request reconstructs the flow from its saved session, runs the current step, and stores the outcome. There is one current step for the next user turn—not a hidden prompt chain to untangle later.

One PicoFlow turn
  1. Request The client sends a message and optional session ID.
  2. Active Step Build its prompt, memory, model settings, and available tools.
  3. Tool + code Validate data and return stay(), go(), or direct().
  4. Session document Save state, memory, logs, token counts, and the next active step.
Flow
A per-request application object that registers the stages and resolves the session.
Step
The local boundary for one customer interaction: prompt, tools, state, validation, and routing.
Session
The durable record that lets the next HTTP request continue safely from the saved cursor.

Built for application control

The parts that make a conversation operable.

01

Explicit steps

Use a step for a meaningful interaction: collect an address, compare options, request approval, or produce a structured result. A step can keep its own prompt, memory namespace, model override, tools, and persisted JSON state.

Understand the step lifecycle

02

One durable session

Each session holds exactly one flow envelope: its current step, context, memory, step state, token accounting, and structured logs. Stores support SQLite, MongoDB, and Cosmos DB, as well as memory for local work.

Inspect the session document

03

Tools with code-level validation

Expose a Zod-described tool to the model, then validate its arguments in a handler before state changes or side effects occur. Tool handlers return semantic transitions, so routing stays explicit in your code.

Author a tool handler

04

Runtime safety signals

The engine validates model selections before a turn begins, checks a resumed session belongs to the requested flow, serializes local turns per session, and uses revision checks to surface concurrent writes rather than silently overwrite them.

Read about session conflicts

A small, inspectable boundary

Let the model propose. Let your step decide.

A model can call a tool to capture an email address. The handler owns validation, persistence, and the next stage. If the input is recoverable, the flow stays in the current step and gives the model precise feedback.

  • getPrompt() describes the conversation for this stage.
  • defineTool() supplies a structured contract to the model.
  • saveState() persists accepted business data.
  • stay() and go() make the transition explicit.
Read the complete first-flow guide
collect-email-step.ts TypeScript

import {
  Flow, Step, TerminateSessionStep,
  Tool, go, stay,
} from "@picoflow/core";
import { z } from "zod";

export class CollectEmailStep extends Step {
  getPrompt() {
    return "Ask for the customer's account email.";
  }

  defineTool() {
    return [{
      name: "capture_email",
      description: "Validate and store an account email",
      schema: z.object({ email: z.string() }),
    }];
  }

  @Tool
  async capture_email(args: Record<string, any>) {
    const email = String(args.email ?? "").trim().toLowerCase();

    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
      return stay("That address is invalid. Ask again.");
    }

    this.saveState({ email });
    return go(TerminateSessionStep).withPrompt(
      `Confirm that support will reply to ${email}.`,
    );
  }
}

export class SupportFlow extends Flow {
  protected defineSteps() {
    return [
      new CollectEmailStep(this).useMemory("support"),
      new TerminateSessionStep(this).useMemory("end"),
    ];
  }
}
      

The runtime carries the hard parts

Your flow code stays close to the business decision.

PicoFlow creates a new Flow instance for each request, restores the saved session when one exists, resolves the current step, runs the model and tool loop, then writes the resulting document with its current revision. That gives every turn a clear lifecycle and a durable trail for operations.

Before the turn Validate flow and provider configuration, then load or create the session.
During the turn Track tool calls, transitions, warnings, errors, and token use alongside the conversation.
After the turn Persist the current stage and durable context with compare-and-swap protection.

Start from a working model

Build the conversation as an application, not an accident.

Start with a small conversational flow, then add durable state, typed tools, and the storage your deployment needs. The documentation follows the same Flow → Step → Session model shown here.