Patterns
Patterns are the agent’s loop shape. Built-ins self-register via register_pattern in packages/harness/src/felix/patterns/ (felix-run/felix).
Built-ins
Section titled “Built-ins”| Pattern | Kind | Behavior |
|---|---|---|
| react (default) | single | Sequential tool-calling loop bounded by recursion_limit |
| deep | single | react + plan tools (plan_create / plan_update_step / plan_get) + planning prompt suffix |
| router | multi | Classifier picks one sub_agents child; forwards thread_id |
| parallel | multi | Fan-out all children, then aggregator synthesis; parent owns the session |
| groupchat | multi | Round-robin speakers sharing a parent transcript (max_turns) |
| reflect | single | react + verifier model; below-threshold critique replays up to max_iterations |
| plan_execute | single | Planner decomposes → per-subtask react executor → synthesis; replans the remainder when a subtask ends early |
react (default)
Section titled “react (default)”- Open session;
SessionStrategyrenders the working set (full_replay/windowed:N/summarizing:N/semantic:N/compacting). - Persist new caller turns.
- Loop: optional JIT tool retrieval → token-budget checks →
model.chat/stream_chat→ sequential tool dispatch through governance-wrapped executors → persist events. - Stop on non-
tool_use, fatal tool, or recursion exhaustion.
Streaming uses an async generator: text deltas yield; the final structured result (tool calls + usage) is the generator return value — no second non-stream call.
plan_execute
Section titled “plan_execute”Three kinds of turn, and they do not share a model unless you let them.
- Plan — one call to
planner_model(orspec.model) for a numbered list, truncated tomax_subtasks. - Execute — each subtask runs on a react agent built from
executor_modelandexecutor_recursion_limit, with the earlier subtasks’ notes in its prompt. - Synthesise — one call on
spec.modelcomposing the answer from the notes. This is the answering turn, so it is the one that carriesspec.output_schema; the planning and executing turns stay free-form, because a plan shaped like the answer schema is not a plan.
That split is the reason to reach for the pattern: planning is one call whose quality shapes everything after it, execution is many narrow calls. “Plan with the expensive model, execute with the cheap one” is the lever.
Replanning
Section titled “Replanning”When a subtask ends on refusal or max_tokens — governance replaced the reply, or the model was cut off mid-answer — the note the synthesiser would record is not an answer. With replan_on_failure, the planner is asked again for the remaining work only, described rather than reissued verbatim, and the completed steps keep their notes. max_replans bounds it; max_subtasks still bounds the whole run, so a replan asks only for the budget the ceiling has left.
Deliberately not a failure: an empty or unhelpful answer. That step ran to completion and produced little, which is a planning problem — replanning on it would spend the budget looping on subtasks that are simply hard to say anything about.
Multi-agent thread discipline
Section titled “Multi-agent thread discipline”| Pattern | Session ownership |
|---|---|
| react / deep / reflect | Opens {tenant_id}:{suffix}; appends as events are produced |
| router | Forwards thread_id to the chosen child |
| parallel / groupchat / plan_execute | Parent owns the transcript; children run without writing the parent session (avoids concurrent append races) |
Shared contracts
Section titled “Shared contracts”- Deny strings, not throws — governance returns model-visible deny content so the loop can adapt.
- Sequential tool dispatch — deterministic audit order; limit counters don’t race.
- Orthogonal bounds —
recursion_limitcaps loop steps;spec.limitscaps aggregate work (tool calls, wall clock, peer hops, tokens). - Durable —
spec.execution.mode: durablewraps any pattern so invoke enqueues a fiber (see architecture).