Skip to Content
DocumentationFeaturesState Management

State Management

Workflow state is stored as plain instance properties on the workflow class. Properties are automatically checkpointed and restored across transitions.

Defining State

No decorator needed — just declare properties on your workflow class:

export class MyWorkflow extends BaseWorkflow { counter: number = 0; llmResult?: ClaudeGenerateTextResult; items: string[] = []; confirmedConcept?: string | null; }

Writing State

Assign values directly in transition methods:

@Transition({ from: 'ready', to: 'processed' }) async process() { const result = await this.claudeGenerateText.call({ ... }); this.llmResult = result.data; this.counter++; this.items.push('new item'); }

Reading State

Access properties directly in any transition or guard method:

@Transition({ from: 'processed', to: 'done' }) async display() { await this.createChatMessage.call({ role: 'assistant', content: `Processed ${this.counter} items. Result: ${this.llmResult?.content}`, }); } hasToolCalls() { return this.llmResult?.stop_reason === 'tool_use'; }

Persistence Across Pauses

State survives when a workflow pauses at a wait: true transition and resumes later:

@Initial({ to: 'waiting' }) async setup() { this.counter = 42; // Set before pause } @Transition({ from: 'waiting', to: 'done', wait: true }) async onResume() { // this.counter is still 42 }

Accessing Workflow Args

Input arguments are available via this.ctx.args:

@Workflow({ schema: z.object({ value: z.number().default(150) }), }) export class MyWorkflow extends BaseWorkflow { @Initial({ to: 'ready' }) async setup() { const args = this.ctx.args as { value: number }; console.log(args.value); // 150 } }

Or receive them directly as a parameter in the @Initial method:

@Initial({ to: 'ready' }) async setup(args: { value: number }) { console.log(args.value); // 150 }

Tool State

Tool properties are also automatically persisted across invocations:

@Tool({ uiConfig: { description: 'Counter tool.' } }) export class CounterTool extends BaseTool { count: number = 0; async call(): Promise<ToolResult<number>> { this.count++; return { data: this.count }; } }

The counter persists even if the workflow pauses and resumes between calls:

// Before pause: count = 1, 2, 3 // After resume: count = 4, 5, 6

Helper Methods

Use regular private methods for reusable logic — no special decorator needed:

export class MyWorkflow extends BaseWorkflow { message?: string; @Final({ from: 'data_created' }) async showResults() { await this.createChatMessage.call({ role: 'assistant', content: this.formatMessage(this.message!), }); } private formatMessage(text: string): string { return text.toUpperCase(); } }

Registry References

Last updated on