PRODUCT — SEP 28, 2026

Dynamic workflows for stateful agents and decision models

Letta Code now supports dynamic workflows for both local and cloud agents. Agents can write scripts that orchestrate many subagents at once, running each on whichever model fits the job, including decision models like OpenAI's Decisions API and TypeSafe's Jev.

Letta Code agents now ship with built-in tools for writing and running dynamic workflows. We've also rebuilt memory initialization around them: during /init, agents write workflows that study available context, such as local codebases and past coding agent sessions.

What are dynamic workflows?

Dynamic workflows allow agents to create structured workflows for fanning out work to subagents. Traditional agentic workflows are designed by a developer ahead of time. A dynamic workflow is written by the agent itself, for the specific task in front of it.

Dynamic workflows are useful both for putting more agents to work on complex tasks (e.g. large refactors) and for processing many tasks or large amounts of context. For example, a single agent may struggle to review thousands of files, but it can write a dynamic workflow that spawns subagents to parallelize the work.

Multi-model dynamic workflows

Letta Code supports models from multiple providers, including bring-your-own-key (BYOK) access to OpenAI, OpenRouter, Together AI, and others. A subagent in a dynamic workflow can run on any connected model that supports structured outputs.

Letta's model support allows user to control costs more effectively. Dynamic workflows can spawn many subagents, so per-call cost adds up fast. Low-cost models with strong coding ability, such as DeepSeek V4.1 Flash and GPT-6 Luna, make large fan-outs practical. In the example below, a deepseek/deepseek-v4.1-flash subagent checks each file, then a second round of openai/gpt-6-luna subagents tries to disprove each finding, and only the findings that survive are kept.

const perFile = await pipeline(
  args.routeFiles, // complete list from a deterministic scan of src/routes/
  file => agent(`Find possible auth gaps in ${file}; check shared middleware too`,
                { model: 'deepseek/deepseek-v4.1-flash', json: true }),
  async findings => {
    const challenges = await parallel(findings.map(f =>
      () => agent(`Try to disprove this finding: ${JSON.stringify(f)}`,
                  { model: 'openai/gpt-6-luna', json: true })
    ))
    return findings.filter((_, i) => challenges[i]?.confirmed === true)
  },
)
return perFile.flat()

Using Decisions APIs with decide()

Workflows are built from a small set of primitives: agent(), parallel(), pipeline(), and decide(). decide() calls a decision model, such as OpenAI's new Decisions API or TypeSafe's Jev. Instead of generating text, these models answer questions you define (pick an option, rate on a scale, or answer yes/no) and return each answer with a confidence score.

That makes decide() useful anywhere a workflow needs a quick judgment: routing work to the right model, gating which items get an agent, or checking whether a subagent's output holds up. For example, a workflow can use decide() to triage traces and only spend an agent on the ones whose outcome conflicts with the user's request:

return pipeline(
  args.traces,

  // Stage 1: decide() asks a decision model a question about the trace.
  trace =>
    decide(trace.evidence, {
      outcome: {
        type: 'choice',
        instructions:
          "Compare the user's request with what the agent actually did.",
        criteria: {
          failed: "The agent's result conflicts with the user's request.",
          succeeded: "The agent's result satisfies the user's request.",
          unclear: 'The evidence does not show the relevant action or result.',
        },
      },
    }),

  // Stage 2: investigate only failures with agent()
  (decision, trace) => {
    const { choice, confidence } = decision.answers.outcome
    if (choice !== 'failed') return { id: trace.id, choice, confidence }
    return agent(
      `Trace ${trace.id} did not do what the user asked. Find the root cause.\n\n${trace.evidence}`,
      ...
    )
  },
)

Scaling compute on context

When creating a new agent with Letta Code, you can run /init to have your agent initialize its memory from local files as well as past coding agent sessions from Claude Code and Codex. During /init, the agent doesn't read all of that itself. Instead, it writes a dynamic workflow that divides the material among many subagents, so it can learn from far more context than a single agent could process.

Orchestrating dynamic workflows with memory

Subagents in a workflow are stateless. They start with only what the orchestrating agent gives them. The orchestrator can pass along relevant excerpts from its own memory, or pointers to its external memory so subagents can look things up themselves.

Next steps

To get started with multi-model dynamic workflows, install the latest version of Letta Code or try chat.letta.com to run dynamic workflows in the cloud.

npm install -g @letta-ai/letta-code