Agentic functional programming: a multi-agent harness over orchestral with recursive spawn, addressable mailboxes, and FP-style combinators.
Project description
conjure
Agentic functional programming. A multi-agent harness built on
orchestral, organized around
three primitives — recursive agent spawning, addressable mailboxes,
and capability passing — with a library of FP-style combinators
(agent_map, agent_fold, agent_filter, agent_race, agent_ensemble,
agent_critic) built on top.
Most multi-agent frameworks bake in a topology — group chat, supervisor/worker, DAG — and you fight the framework when your problem has a different shape. Conjure inverts that: the primitives are the substrate, and topology is ordinary code. The composition vocabulary is borrowed from functional programming, where these problems have decades of prior art.
See DESIGN.md for the design philosophy and architecture.
The three primitives
- Recursive spawning. One root agent; every other agent is created by
another agent via
spawn(spec) → address. The spawn tree is the system's structure: callers own callees, lifecycle is parental, termination cascades. - Addressable mailboxes. Every live agent has a persistent, threaded, journaled inbox. Messages are async; parents are never blocked while children work. The user is just another address in the graph.
- Capability passing. An agent can only message addresses it was given at spawn time or explicitly handed later. Possession of an address is permission — addresses compose like function references.
The load-bearing deviation from subagent-style frameworks: a spawned agent does not vanish when it returns a value. It stays addressable — it can be re-prompted, queried, and introduced to siblings — until its parent (or the user) terminates it.
Install
pip install conjure-agents # core library + headless CLI (repl)
pip install "conjure-agents[ui]" # + the tmux/textual cockpit (conjure run)
pip install "conjure-agents[claude_agent]" # + claude-agent-sdk engine
The distribution is named conjure-agents; the import package and CLI are
plain conjure.
Requires Python ≥ 3.10 on POSIX (the daemon forks; Windows is unsupported).
Quickstart — CLI
Point conjure at a YAML config describing the root agent:
# agent.yaml
llms:
default:
provider: anthropic # or openai / google / groq / ollama
model: claude-haiku-4-5
root:
role_prompt: |
You are the root orchestrator. Spawn children when a specialist is
warranted; otherwise just do the work.
engine: orchestral
llm: default
tools: [primitive, combinator]
conjure config set ANTHROPIC_API_KEY sk-... # stored in ~/.config/conjure/.env
conjure check agent.yaml # validate config + keys, no LLM calls
conjure repl agent.yaml # single-pane REPL, no tmux needed
conjure run agent.yaml # tmux cockpit (requires the [ui] extra)
In tmux mode the session is daemonized: window 0 is your prompt to the
root agent, every spawned agent gets its own live window, and Ctrl+B M
opens a meta-view popup (spawn tree + inboxes + cost). Detach with
Ctrl+B d, reattach with conjure run --attach, stop with conjure quit.
Quickstart — library
The runtime is a plain Python object; engines are pluggable. This example
uses the deterministic ScriptedEngine (no API key needed) to show the
mechanics — swap in the orchestral or claude_agent engine factories
for LLM-backed agents (see examples/):
from conjure import AgentSpec, BehaviorRegistry, Runtime, agent_map
from conjure.tools.primitives import send_impl
def squarer(engine, prompt, envelopes):
for env in envelopes:
body = env.body
if isinstance(body, dict) and "item" in body:
send_impl(token=engine.token, to=body["reply_to"], body=body["item"] ** 2)
return "ok"
reg = BehaviorRegistry()
reg.register("idle", lambda *a, **kw: "idle")
reg.register("squarer", squarer)
rt = Runtime(engine_factory=reg.factory())
root = rt.root(AgentSpec(role_prompt="idle"))
squares = agent_map(
rt, root,
lambda _item: AgentSpec(role_prompt="squarer"),
[1, 2, 3, 4, 5],
timeout_s=5.0,
)
assert squares == [1, 4, 9, 16, 25] # workers spawned, gathered, terminated
rt.shutdown()
Combinators
All of these are ordinary code over the three primitives — and LLM-callable tools, so agents can invoke them on their own subtrees:
| Combinator | Shape |
|---|---|
agent_map |
Fan out one worker per item in parallel; gather in order |
agent_fold |
Thread an accumulator sequentially through workers |
agent_filter |
Classification swarm; keep items that pass |
agent_fixed_point |
Iterate an agent on its own output until it stabilizes |
agent_race |
Speculative execution: first reply wins, losers terminated |
agent_ensemble |
Best-of-N: gather all replies, synthesize via an aggregator |
agent_critic |
Generator/critic refinement loop with principled termination |
Engines
orchestral— wrapsorchestral.Agent; multi-provider out of the box (Anthropic, OpenAI, Google, Groq, Ollama).claude_agent(pip install "conjure[claude_agent]") — agents run asclaude-agent-sdksessions with Claude Code's full tool surface (Bash, Edit, Grep, …), per-agent sandbox directories, and permission routing.ScriptedEngine— deterministic behaviors for tests and offline development.
Persistence & replay
Every spawn, send, and terminate is journaled to an append-only JSONL log
per session. Runtime.replay(session_dir) reconstructs the spawn tree and
all inbox contents for post-hoc inspection — every interaction in a
session is auditable.
Limitations (v0.1)
Honest boundaries, documented rather than discovered:
- Single-process runtime; one OS thread per live agent.
- Termination does not interrupt an in-flight LLM call; it takes effect after the current step.
- Per-inbox FIFO ordering only — no global message order.
- Cost is tracked but not yet enforced as a ceiling.
agent_fixed_pointuses strict output equality, which stochastic LLMs rarely satisfy — preferagent_criticfor refinement loops.
See DESIGN.md §6 and §10 for where the FP framing breaks
down and which workloads the substrate fits poorly.
Development
git clone <repo-url> && cd conjure
pip install -e ".[test,ui]"
pytest -q
License
AGPL-3.0-only. You can use, modify, and redistribute conjure freely — but any distributed or network-served derivative must publish its source under the same terms. For commercial licensing outside the AGPL, contact the author.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file conjure_agents-0.1.1.tar.gz.
File metadata
- Download URL: conjure_agents-0.1.1.tar.gz
- Upload date:
- Size: 164.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb534136fbad5bf304ed5ea608d4e85915cf6276eaa80a40af166c61e77c7e2f
|
|
| MD5 |
baeed61882cf2c7629873090807a1e65
|
|
| BLAKE2b-256 |
9c6c45eba777f0e6196b4d9d4a7dfa3e6dac511dd9a1f78983518220e5b6bf96
|
File details
Details for the file conjure_agents-0.1.1-py3-none-any.whl.
File metadata
- Download URL: conjure_agents-0.1.1-py3-none-any.whl
- Upload date:
- Size: 180.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
109a0a3e7c84b69a32e5101941b9a74df47f4309fc9d29bc8e8451b504c3119a
|
|
| MD5 |
94e64d08df71ebabf507fd7cf7e3da6f
|
|
| BLAKE2b-256 |
b5a6449102197c149f497b74e99d302368a61d57aafb370918606cf2b9d730a7
|