Autonomous Orchestrator¶
autonomous_orchestrator -- G6 Autonomous Multi-Agent Orchestration System.
Cluster: Experience & Autonomy | Type: component | MCP Tools: 27
Overview¶
24/7 autonomous multi-agent orchestration system that continuously processes a JSON todo list using a six-slot agent pool (design, execution, evaluation, and three proxy agents) with rate limiting, session logging, and human-review queuing. Wraps AgentPool, TaskManager, and RateLimiter into a single AIBlock with graceful shutdown via signal handling.
When to use:
- Running an unattended agent loop that picks up tasks from a structured todo file and executes them autonomously
- Distributing work across specialised agent roles (design → execution → evaluation) with token accounting
- Integrating continuous background processing into G6 with HITL review hand-off
Example:
from mvp.autonomous_orchestrator import AutonomousOrchestratorBlock, OrchestratorInput
block = AutonomousOrchestratorBlock()
result = block.infer(OrchestratorInput(
todo_path="components/mvp/workspace_manager/claude_todo_list.json",
dry_run=True,
stop_on_empty=True,
))
# result.ok → True; result.value → OrchestratorOutput with tasks_completed, summary
Works well with: recursive_architect, hat_orchestrator, autonomy_governor
OrchestratorInput safety-relevant fields:
op—Literal["describe", "status", "start_daemon"](defaultstart_daemon).describe/statusare read-only shims that never enter the dispatch loop; onlystart_daemonlaunches the 24/7 daemon. Registry-invoked bogus ops fail validation cleanly.production_mode—bool(defaultFalse). Withop="start_daemon"andproduction_mode=True, the direct JSON-backed path is refused withdegraded=True, degradation_reason="json_unsafe_in_production". The JSONTaskManagerhas no file-locking contract, so concurrent writers can corrupt the todo file. Use the SQLite-backedorchestrator_mcpstore in production.
Split-brain note: the direct daemon (JSON) and the orchestrator_mcp plane (SQLite) are separate sources of truth. The MCP plane is management/inspection only — it cannot start, stop, or inspect the live daemon. ao_get_pool_status reports active SQLite sessions (not live AgentPool slots), ao_configure_limits mutates shadow state (not a running RateLimiter), and ao_capabilities reports live_control_available: false. Cross-package enrichment fallback in ao_complete_task and bad-JSON / agentic degradation in ao_run_failure_guard are surfaced via degraded/degradation_reason.
Pilot-ready, not multi-worker production hardened
The autonomous orchestrator is suitable for local development, dry runs, and early pilot workflows where a single process owns the task queue. It is not yet hardened for multi-worker production operation.
The direct AutonomousOrchestratorBlock path uses JSON-backed todo and human-review files, which are not safe for concurrent writes from multiple orchestrator processes. Runtime dollar-cost tracking is also process-local, so production deployments should rely on a single orchestrator instance, explicit token limits, the kill-switch file, and human review until durable shared spend accounting is added.
For paid self-serve or unattended production use, run an end-to-end MCP install/run smoke test first and prefer the SQLite-backed orchestrator_mcp operations for task/session inspection. Do not treat successful task completion as a proof of business or regulated-domain correctness without human review.
Public API¶
AgentPool¶
Manages 6 concurrent agent slots for dispatching tasks.
Constructor:
| Parameter | Type | Default |
|---|---|---|
task_manager | TaskManager | required |
rate_limiter | RateLimiter | required |
config | OrchestratorInput | required |
governor | 'AutonomyGovernorBlock \| None' | None |
hitl | 'HITLDispatcher \| None' | None |
safety_policy | 'RuntimePolicy \| None' | None |
pricing_catalog | Any \| None | None |
Methods:
dispatch(stop_event: asyncio.Event) -> dict¶
Run the dispatch loop until stop_event is set or no tasks remain.
GuardSignals¶
Derived signals from the 20 failure-guard answers.
| Field | Type | Default |
|---|---|---|
stuck | bool | False |
waste | bool | False |
no_value | bool | False |
sanity_failed | bool | False |
degraded | bool | False |
degradation_reason | str | '' |
GuardVerdict¶
The outcome of a Ralph Wiggum loop iteration.
| Field | Type | Default |
|---|---|---|
signals | GuardSignals | field(default_factory=GuardSignals) |
next_action | str | 'continue' |
iteration | int | 0 |
passed | bool | True |
FailureGuard¶
Asks each of the 20 questions via a cheap LLM and logs answers.
Constructor:
| Parameter | Type | Default |
|---|---|---|
logger | SessionLogger \| None | None |
Methods:
ask_questions(context: str, task_title: str = '', dry_run: bool = False) -> list[tuple[int, str, str]]¶
Ask all 20 questions about the current step.
extract_signals_from_json(json_str: str) -> 'GuardSignals'¶
Parse structured JSON answer list into GuardSignals.
extract_signals(answers: list[tuple[int, str, str]]) -> GuardSignals¶
Derive 4 signals from key question answers.
RalphWiggumLoop¶
Orchestrates the full failure-guard + verdict pipeline per step.
Constructor:
| Parameter | Type | Default |
|---|---|---|
logger | SessionLogger \| None | None |
dry_run | bool | False |
Methods:
set_guard_signal_planner(planner: object) -> None¶
Inject an agentic failure-signal planner (test seam).
review_step(context: str, task_title: str = '', attempts: int = 0) -> GuardVerdict¶
Run the full 20-question review and determine next action.
AutonomousOrchestratorBlock(AIBlock[OrchestratorInput, OrchestratorOutput, None])¶
Top-level orchestrator that autonomously processes tasks 24/7.
| Field | Type | Default |
|---|---|---|
name | str | 'g6_autonomous_orchestrator' |
Methods:
infer(data: OrchestratorInput) -> Result[OrchestratorOutput]¶
Synchronous wrapper — calls
run_async()viaasyncio.run().
run_async(data: OrchestratorInput) -> OrchestratorOutput¶
Async entry point for the 24/7 orchestrator loop.
RateLimiter¶
Global token budget + per-agent cooldown enforcer with optional SQLite persistence.
Constructor:
| Parameter | Type | Default |
|---|---|---|
max_tokens_per_hour | int | MAX_TOKENS_PER_HOUR |
cooldown_min | float | COOLDOWN_MIN_SEC |
cooldown_max | float | COOLDOWN_MAX_SEC |
db_path | str \| None | _DEFAULT_DB |
max_cost_usd | float \| None | DEFAULT_MAX_COST_USD |
kill_switch_path | str \| None | None |
Methods:
kill_switch_tripped() -> bool¶
Return True when the configured kill-switch file exists on disk.
record_cost(cost_usd: float) -> None¶
Accumulate dollar spend; used by
acquireto refuse when
accumulated_cost_usd() -> float¶
Total USD cost recorded this process (0.0 when none).
is_budget_exhausted() -> bool¶
record_tokens(count: int, agent_id: str = '') -> None¶
tokens_last_hour() -> int¶
is_budget_exceeded() -> bool¶
mark_agent_call(agent_id: str) -> None¶
Record that agent_id just made a call.
wait_for_cooldown(agent_id: str) -> None¶
Sleep until the per-agent cooldown has expired.
wait_for_budget() -> None¶
Sleep until the global token budget is available again.
acquire(agent_id: str) -> str¶
Wait for cooldown/budget, then return the resolved model ID.
snapshot() -> dict¶
Return current state for diagnostics.
AgentRole(str, Enum)¶
Which slot an agent occupies in the pool.
AgencyLevel(int, Enum)¶
Progressive agency ladder — higher = more autonomous.
TaskItem(BaseModel)¶
A single work item in the todo queue.
| Field | Type | Default |
|---|---|---|
id | str | '' |
title | str | '' |
description | str | '' |
component | str | '' |
phase | Literal['basic', 'intermediate', 'advanced'] | 'basic' |
status | Literal['pending', 'in_progress', 'complete', 'failed'] | 'pending' |
parent_id | str \| None | None |
children_ids | list[str] | Field(default_factory=list) |
depends_on | list[str] | Field(default_factory=list) |
inputs | dict[str, Any] | Field(default_factory=dict) |
outputs | dict[str, Any] | Field(default_factory=dict) |
artifacts | list[str] | Field(default_factory=list) |
attempts | int | 0 |
interruptions | int | 0 |
created_at | str | '' |
updated_at | str | '' |
assigned_agent | str \| None | None |
session_log | str \| None | None |
Methods:
model_post_init(__context: Any) -> None¶
TodoList(BaseModel)¶
Serialisable container for claude_todo_list.json.
| Field | Type | Default |
|---|---|---|
tasks | list[TaskItem] | Field(default_factory=list) |
summary | str | '' |
last_updated | str | '' |
HumanReviewEntry(BaseModel)¶
One completed-task entry for human_review_todo.json.
| Field | Type | Default |
|---|---|---|
task_id | str | '' |
task_description | str | '' |
completed_at | str | '' |
explanation | str | '' |
what_was_achieved | str | '' |
session_log_path | str | '' |
review_status | Literal['pending', 'approved', 'rejected'] | 'pending' |
SessionEvent¶
A single JSONL-serialisable event within an agent session.
| Field | Type | Default |
|---|---|---|
event | str | required |
ts | str | '' |
session_id | str | '' |
task_id | str | '' |
task | str | '' |
agent_role | str | '' |
fn | str | '' |
result_summary | str | '' |
tokens | int | 0 |
q_id | int | 0 |
question | str | '' |
answer | str | '' |
iteration | int | 0 |
self_review | str | '' |
passed | bool | True |
goal_achieved | bool | False |
summary | str | '' |
tokens_total | int | 0 |
module | str | '' |
root_cause | str | '' |
detail | str | '' |
Methods:
to_dict() -> dict[str, Any]¶
Return dict with zero/empty/False fields stripped (except 'event').
AgentSession¶
Runtime state for one agent session (in-memory only).
| Field | Type | Default |
|---|---|---|
session_id | str | '' |
task_id | str | '' |
agent_role | AgentRole | AgentRole.PROXY_1 |
tokens_used | int | 0 |
events | list[SessionEvent] | field(default_factory=list) |
started_at | str | '' |
OrchestratorInput(BaseModel)¶
Input for AutonomousOrchestratorBlock.infer().
| Field | Type | Default |
|---|---|---|
op | Literal['describe', 'status', 'start_daemon'] | 'start_daemon' |
todo_path | str | 'components/mvp/workspace_manager/claude_todo_list.json' |
review_path | str | 'components/mvp/workspace_manager/human_review_todo.json' |
session_log_dir | str | 'logs/sessions' |
stop_on_empty | bool | False |
dry_run | bool | False |
podman_host | str | '' |
production_mode | bool | False |
OrchestratorOutput(BaseModel)¶
Output from AutonomousOrchestratorBlock.infer().
| Field | Type | Default |
|---|---|---|
tasks_completed | int | 0 |
tasks_failed | int | 0 |
tasks_remaining | int | 0 |
total_tokens | int | 0 |
summary | str | '' |
session_logs | list[str] | Field(default_factory=list) |
degraded | bool | False |
degradation_reason | str | '' |
completion_state | Literal['verified', 'qualified-draft', 'blocked-escalated'] | 'qualified-draft' |
warning_card | dict[str, Any] | Field(default_factory=dict) |
estimated_cost_usd | float | 0.0 |
budget_remaining | dict[str, Any] | Field(default_factory=dict) |
SessionLogger¶
Appends
SessionEventdicts as JSONL lines to a per-session file.
Constructor:
| Parameter | Type | Default |
|---|---|---|
session_id | str | required |
log_dir | str \| Path | 'logs/sessions' |
Methods:
path() -> Path¶
log(event: SessionEvent) -> None¶
Serialise event and append to the JSONL file.
log_raw(data: dict) -> None¶
Append an arbitrary dict (for ad-hoc events).
read_events() -> list[dict]¶
Read back all events from the JSONL file.
event_count() -> int¶
Return number of events logged so far (line count).
file_size() -> int¶
Return the current file size in bytes.
TaskManager¶
Reads/writes the todo JSON files and manages task lifecycle.
Constructor:
| Parameter | Type | Default |
|---|---|---|
todo_path | str \| Path | 'components/mvp/workspace_manager/claude_todo_list.json' |
review_path | str \| Path | 'components/mvp/workspace_manager/human_review_todo.json' |
Methods:
load_todo() -> TodoList¶
Load the todo list from disk (or return empty).
save_todo(todo: TodoList) -> None¶
Write the todo list to disk atomically (RS-16-F03).
load_reviews() -> list[HumanReviewEntry]¶
Load human review entries from disk.
save_reviews(reviews: list[HumanReviewEntry]) -> None¶
Write human review entries to disk atomically (RS-16-F03).
auto_populate() -> TodoList¶
Load existing todo list (no-op seeding — all components are implemented).
pick_next(todo: TodoList | None = None) -> TaskItem | None¶
Pick the next pending task.
claim_task(task_id: str, agent_id: str) -> TaskItem | None¶
Mark a task as in_progress and assign to agent_id.
complete_task(task_id: str, explanation: str = '', what_was_achieved: str = '', session_log_path: str = '', outputs: dict | None = None, artifacts: list[str] | None = None) -> None¶
Mark a task as complete and add a human review entry.
fail_task(task_id: str) -> None¶
Mark a task as failed and cascade-fail its pending dependents.
enqueue(task: TaskItem) -> None¶
Add a new task to the todo list.
increment_failure_count(component: str) -> int¶
Track and return cumulative failure count for a component.
reset_stale(stale_after_seconds: float = 900.0, now: datetime | None = None) -> int¶
Reset stale in_progress tasks back to pending (startup recovery).
checkpoint_task(task_id: str) -> bool¶
Return a single in-flight task to a resumable (pending) state.
create_subtasks(parent_id: str, max_children: int = 5) -> list[TaskItem]¶
Decompose a failed/stuck task into subtasks via GoalDecomposer.
stats(todo: TodoList | None = None) -> dict¶
Return task counts by status.
OrchestratorMCPBlock(AIBlock[MCPOrchestratorInput, MCPOrchestratorOutput, dict])¶
26-op MCP block for Autonomous Orchestrator.
| Field | Type | Default |
|---|---|---|
name | str | 'orchestrator_mcp' |
state | dict \| None | None |
db_path | str | '' |
Methods:
infer(data: MCPOrchestratorInput) -> Result[MCPOrchestratorOutput]¶
MCPOrchestratorInput(BaseModel)¶
| Field | Type | Default |
|---|---|---|
op | OrchestratorOp | required |
task_id | str \| None | None |
title | str \| None | None |
description | str \| None | None |
component | str \| None | None |
phase | str \| None | None |
status | str \| None | None |
agent_id | str \| None | None |
session_id | str \| None | None |
event_type | str \| None | None |
event_data_json | str \| None | None |
context | str \| None | None |
task_title | str \| None | None |
questions_json | str \| None | None |
answers_json | str \| None | None |
tokens | int \| None | None |
max_tokens_per_hour | int \| None | None |
cooldown_sec | int \| None | None |
session_name | str \| None | None |
limit | int \| None | None |
query | str \| None | None |
MCPOrchestratorOutput(BaseModel)¶
| Field | Type | Default |
|---|---|---|
ok | bool | required |
message | str | required |
data_json | str \| None | None |
degraded | bool | False |
degradation_reason | str | '' |
OrchestratorStore¶
5-table SQLite store for Autonomous Orchestrator MCP.
Constructor:
| Parameter | Type | Default |
|---|---|---|
db_path | str | ':memory:' |
Methods:
create_task(title: str, description: str = '', component: str = '', phase: str = 'basic') -> dict¶
get_task(task_id: str) -> dict | None¶
list_tasks(status: str | None = None, phase: str | None = None, component: str | None = None, limit: int = 50) -> list[dict]¶
update_task(task_id: str, **kwargs) -> bool¶
claim_task(task_id: str, agent_id: str) -> bool¶
complete_task(task_id: str) -> bool¶
fail_task(task_id: str) -> bool¶
reset_stale() -> int¶
Reset in_progress tasks back to pending. Returns count reset.
pick_next(phase: str | None = None) -> dict | None¶
Pick the next pending task with fewest attempts, optionally filtered by phase.
get_task_stats() -> dict¶
start_session(task_id: str = '', agent_role: str = '') -> dict¶
get_session(session_id: str) -> dict | None¶
list_sessions(task_id: str | None = None, status: str | None = None, limit: int = 50) -> list[dict]¶
end_session(session_id: str, tokens_used: int = 0) -> bool¶
get_session_stats() -> dict¶
log_event(session_id: str, event_type: str, data_json: str = '{}') -> dict¶
list_events(session_id: str, limit: int = 100) -> list[dict]¶
store_verdict(session_id: str = '', task_id: str = '', signals_json: str = '{}', next_action: str = 'continue', iteration: int = 0, passed: bool = True) -> dict¶
get_verdict(verdict_id: str) -> dict | None¶
list_verdicts(session_id: str | None = None, task_id: str | None = None, limit: int = 50) -> list[dict]¶
record_tokens(tokens: int, agent_id: str = '') -> dict¶
get_tokens_in_window(agent_id: str = '', window_seconds: int = 3600) -> int¶
Sum tokens recorded in the last window_seconds.
search(query: str, top_k: int = 10) -> list[dict]¶
Functions¶
list_patterns() -> dict[str, Any]¶
Return the autonomous_orchestrator applied-pattern + skill surface.
MCP Tools¶
| Operation | Source |
|---|---|
create_task | orchestrator_mcp |
get_task | orchestrator_mcp |
list_tasks | orchestrator_mcp |
update_task | orchestrator_mcp |
claim_task | orchestrator_mcp |
complete_task | orchestrator_mcp |
fail_task | orchestrator_mcp |
reset_stale | orchestrator_mcp |
start_session | orchestrator_mcp |
log_event | orchestrator_mcp |
get_session | orchestrator_mcp |
list_sessions | orchestrator_mcp |
get_session_stats | orchestrator_mcp |
run_failure_guard | orchestrator_mcp |
extract_signals | orchestrator_mcp |
get_verdict | orchestrator_mcp |
list_verdicts | orchestrator_mcp |
get_token_budget | orchestrator_mcp |
record_tokens | orchestrator_mcp |
configure_limits | orchestrator_mcp |
get_pool_status | orchestrator_mcp |
get_task_stats | orchestrator_mcp |
save_session | orchestrator_mcp |
load_session | orchestrator_mcp |
search | orchestrator_mcp |
info | orchestrator_mcp |
capabilities | orchestrator_mcp |