Skip to main content

Core API

@flow-state-dev/core — Isomorphic builders, type contracts, and item taxonomy.

Block Builders

handler(config)

Create a synchronous logic block.

import { handler } from "@flow-state-dev/core";

const myHandler = handler({
name: "my-handler",
inputSchema: z.object({ value: z.string() }),
outputSchema: z.object({ result: z.string() }),
sessionStateSchema: z.object({ count: z.number().default(0) }),
targetStateSchemas: {
research: z.object({ progress: z.number() }),
},
execute: async (input, ctx) => {
await ctx.session.incState({ count: 1 });
// ctx.targets.research is StateHandle<{ progress: number }> | undefined
await ctx.targets.research?.patchState({ progress: 50 });
return { result: input.value.toUpperCase() };
},
});

generator(config)

Create an LLM-calling block with tool loop support.

import { generator } from "@flow-state-dev/core";

const myGenerator = generator({
name: "my-gen",
model: "gpt-5-mini",
prompt: "You are a helpful assistant.",
inputSchema: z.object({ message: z.string() }),
outputSchema: z.object({ response: z.string() }),
targetStateSchemas: {
research: z.object({ progress: z.number() }),
},
user: (input, ctx) => {
const progress = ctx.targets.research?.state.progress ?? 0;
return `progress:${progress}${input.message}`;
},
tools: [myTool],
context: [myContextFn],
history: (_input, ctx) => ctx.session.items.llm(),
repair: { mode: "auto", maxAttempts: 3 },
});

sequencer(config)

Create a pipeline composition block.

import { sequencer } from "@flow-state-dev/core";

const pipeline = sequencer({
name: "my-pipeline",
inputSchema: z.object({ message: z.string() }),
container: { component: "pipeline-view", label: "Processing" },
});

Methods: then, thenIf, map, parallel, forEach, doUntil, doWhile, loopBack, work, waitForWork, tap, tapIf, rescue, branch

router(config)

Create a runtime block-selection block.

import { router } from "@flow-state-dev/core";

const myRouter = router({
name: "mode-router",
inputSchema: z.object({ mode: z.string() }),
targetStateSchemas: {
coordinator: z.object({ step: z.number() }),
},
routes: [chatBlock, agentBlock],
execute: async (input, ctx) => {
const step = ctx.targets.coordinator?.state.step ?? 0;
return step > 0 ? agentBlock : chatBlock;
},
});

Flow

defineFlow(definition)

Create a flow type.

import { defineFlow } from "@flow-state-dev/core";

const myFlow = defineFlow({
kind: "my-app",
requireUser: true,
actions: { /* ... */ },
session: { stateSchema, resources, clientData },
user: { stateSchema, resources, clientData },
request: { onStarted, onCompleted, onErrored, onFinished, onStepErrored },
});

export default myFlow({ id: "default" });

Resources

defineResource(config)

Create a portable resource definition. Can be used in flow scope configs and in block-level resource declarations (sessionResources, userResources, projectResources):

import { defineResource } from "@flow-state-dev/core";

const planResource = defineResource({
stateSchema: z.object({ steps: z.array(z.string()).default([]) }),
writable: true,
});

// Use in flow scope config
session: { resources: { plan: planResource } }

// Or declare on blocks — collected and merged into the flow automatically
const myHandler = handler({
name: "plan-manager",
sessionResources: { plan: planResource },
execute: async (input, ctx) => { /* ... */ },
});

Context Functions

contextFn(schemas, fn)

Create a typed context function for generators. Provides typed access to scope state via schema inference:

import { contextFn } from "@flow-state-dev/core";
import { section, list } from "@flow-state-dev/core/prompt";

const researchContext = contextFn(
{ session: sessionStateSchema },
({ session }) => {
if (session.coveredTopics.length === 0) return "";
return section("Research Progress", list(session.coveredTopics));
}
);

// Use in any generator
const agent = generator({
context: [researchContext],
// ...
});

Three overloads: (session), (session, user), (session, user, project).

Prompt Formatters

@flow-state-dev/core/prompt — Composable text formatters for building clean LLM context.

FormatterSignatureDescription
section(title, ...content)(string, ...string[]) => stringTitled section with ## header
list(items, options?)(string[], { ordered?, prefix? }) => stringBullet or numbered list
keyValues(data)(Record<string, unknown>) => stringKey-value pairs
entries(record, formatter)(Record, fn) => stringMapped record entries
codeBlock(code, language?)(string, string?) => stringFenced code block
join(...parts)(...(string | falsy)[]) => stringJoin with newlines, filtering falsy
when(condition, content)(boolean, string) => string | ""Conditional inclusion

Client Data

clientData on scope configs

Expose derived state to the frontend. Define compute functions on session, user, or project scope configs:

defineFlow({
session: {
stateSchema,
clientData: {
topicList: (ctx) => ctx.state.coveredTopics,
progress: (ctx) => ({
total: ctx.state.totalSteps,
completed: ctx.state.completedSteps,
}),
},
},
});

Compute functions receive { state, resources } from their scope. All entries are client-visible. Values must be JSON-serializable.

Type Helpers

import { StateOf, ContextOf, ResourceContext, BlockInput, BlockOutput } from "@flow-state-dev/core";

type PlanState = StateOf<typeof planResource>;
type SessionCtx = ContextOf<typeof sessionSchema, "session">;
type Input = BlockInput<typeof myBlock>;
type Output = BlockOutput<typeof myBlock>;

Subpath Exports

  • @flow-state-dev/core/types — Block, flow, resource, scope, streaming, and model type definitions
  • @flow-state-dev/core/items — Item unions, content types, and stream event helpers
  • @flow-state-dev/core/prompt — Composable prompt formatters (section, list, keyValues, entries, codeBlock, join, when)