Teams (Team Workspaces)¶
A Team Workspace is a directory whose root contains naqsha.toml. This single file defines the entire multi-agent topology: orchestrator and workers, their model adapters, tool allowlists, budget caps, memory settings, and reflection policy.
The naqsha.toml format¶
[workspace] — workspace metadata¶
[workspace]
name = "research-team" # human-readable label
orchestrator = "orch" # agent_id of the orchestrator
auto_approve = false # true → all write-tier tools auto-approved
[memory] — shared memory engine¶
[reflection] — autonomous improvement¶
[reflection]
enabled = true # enable Reflection Loop
auto_merge = false # ALWAYS false by default; opt-in only
reliability_gate = true # run pytest before auto-merge
gate_paths = ["tests/smoke/"]
[agents.<id>] — agent definition¶
[agents.orch]
role = "orchestrator" # "orchestrator" or "worker"
model_adapter = "openai_compat" # fake | openai_compat | anthropic | gemini | ollama
tools = ["clock", "list_memory_tables"] # strict allowlist
max_retries = 3 # Circuit Breaker threshold
max_steps = 20 # Budget: max steps
max_tokens = 4096 # Budget: max tokens
Adapter sub-tables¶
Each model adapter has its own configuration sub-table:
Complete example¶
[workspace]
name = "research-team"
orchestrator = "orch"
auto_approve = false
[memory]
db_path = ".naqsha/memory.db"
[reflection]
enabled = true
auto_merge = false
reliability_gate = true
gate_paths = ["tests/"]
[agents.orch]
role = "orchestrator"
model_adapter = "openai_compat"
tools = ["clock", "list_memory_tables"]
max_steps = 30
max_tokens = 8192
[agents.orch.openai_compat]
model = "gpt-4o"
api_base = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
[agents.researcher]
role = "worker"
model_adapter = "openai_compat"
tools = ["clock", "read_file", "list_files", "list_memory_tables", "memory_schema"]
max_retries = 3
max_steps = 15
max_tokens = 4096
[agents.researcher.openai_compat]
model = "gpt-4o-mini"
api_base = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
Topology API¶
from naqsha.orchestration.topology import (
parse_team_topology_file,
parse_team_topology,
)
# Load from disk
topo = parse_team_topology_file(Path("naqsha.toml"))
# Load from an in-memory dict (tests and code generators)
topo = parse_team_topology({"workspace": {"name": "x", ...}, ...}, base_dir=Path("."))
parse_team_topology validates:
- At least one agent defined with
role = "orchestrator" - All
model_adaptervalues are known types - All tool names in
toolslists are non-empty Role-Based Tool Policynames are consistent
It also auto-injects delegate_to_<worker> tools into the orchestrator's allowlist for each worker defined in the topology.
Runtime builders¶
from naqsha.orchestration.team_runtime import (
build_team_orchestrator_runtime,
build_worker_runtime,
run_profile_for_topology_agent,
)
from naqsha import RuntimeEventBus
bus = RuntimeEventBus()
# Orchestrator runtime (includes delegation tools, shared memory)
rt = build_team_orchestrator_runtime(topo, workspace_path=Path("."), event_bus=bus)
result = rt.run("Analyse topic X")
# Worker runtime (used internally by delegation tools)
worker_rt = build_worker_runtime(topo, "researcher", workspace_path=Path("."), span_context=...)
Tool-Based Delegation in detail¶
When build_team_orchestrator_runtime is called, NAQSHA:
- Creates a
delegate_to_<worker>tool for each worker in the topology. - Registers those tools in the orchestrator's
ToolRegistry. - When the orchestrator calls
delegate_to_researcher(task="..."): - A new
CoreRuntimeis spawned with the worker's profile. - A child
SpanContextis created (sametrace_id; newspan_idwithparent_span_id= orchestrator span). - The worker runs fully isolated — its
AgentContextis created fresh; the orchestrator's context is never passed. - The worker's trace events share the same JSONL file, keyed by
agent_id = "researcher". - The worker's answer is returned to the orchestrator as a
ToolObservation. - If the worker's Circuit Breaker trips, the observation carries
metadata["kind"] = "TaskFailedError"andmetadata["circuit_breaker"] = True.
Role-Based Tool Policy¶
Each agent's tools array is its complete allowlist. Calling a tool not in the list:
- The call is denied before execution.
- A
ToolErroredevent is emitted on the Event Bus. - A denial observation is written to the trace.
- The model receives the denial as a structured error observation.
There is no runtime way to escalate permissions. The allowlist is set at topology parse time.
Worker isolation invariants¶
These invariants are non-negotiable and enforced by the runtime:
- The orchestrator's
AgentContextis never passed to a worker. private_<agent_id>_*tables are inaccessible to other agents at the SQL level.- Workers cannot read or modify the orchestrator's private memory.
- Delegation runs a fully isolated
CoreRuntime; there is no shared mutable state.
Hierarchical trace¶
All agents in a team write to the same JSONL trace file, distinguished by:
| Field | Orchestrator | Worker |
|---|---|---|
trace_id |
shared run_id |
shared run_id |
agent_id |
"orch" |
"researcher" |
span_id |
"span_orch_..." |
"span_researcher_..." |
parent_span_id |
null |
"span_orch_..." |
The Workbench TUI's Span Tree and Flame Graph panels reconstruct the hierarchy from these fields.
Further reading¶
- API:
naqsha.orchestration - ADR: 0013 — Tool-Based Delegation Model
- ADR: 0012 — Multi-Agent Teams and Memory Scopes
- Getting started: Fake team example