Skip to content

Commit b8f4d07

Browse files
authored
✨ feat(core): unified tool-use loop with agent hypervisor (#78)
## Summary - Replace the multi-class orchestration pipeline (planner → executor → reflector → synthesizer) with a single async `ToolUseLoop` driven entirely by native `bind_tools` / `tool_use` - Introduce `AgentRegistry` hypervisor control plane — admission control via semaphore, lifecycle tracking, structured cancellation, and guaranteed cleanup - Add `AgentContext` (immutable, frozen) for depth-aware context propagation across the agent tree; root agent gets user-steering primitives (`message_queue`, `interrupt_step`), sub-agents do not - Add `SpawnAgentTool` — LLM-callable tool for creating child `ToolUseLoop` instances with scoped tool access (allowlist/denylist filtered before `bind_tools`, so denied tools never appear in the schema) - Add `AgentConfig` to `AppConfig` — `max_depth`, `max_concurrent`, `allowed_models`, `default_sub_model`, `default_denied_tools`, `sub_agent_max_steps` - Add real-time CLI agent tree display via `AgentDisplayManager` + Rich Live, driven by `on_agent_start`/`on_agent_stop` lifecycle hooks - Autoload `CLAUDE.md`/`AGENTS.md` from CWD as project instructions injected into system prompts for all agents (opt-out via `<!-- meeseeks:noload -->`) - Remove `ActionRunner`, `Reflection`, multi-step planner prompts (`plan-updater.txt`, `step-executor.txt`, `tool-selector.txt`, `response-synthesizer.txt`) - Migrate all `AGENTS.md` to `CLAUDE.md` across packages ## Breaking changes - `Orchestrator` API simplified — no longer exposes plan/execute/reflect cycle; runs `ToolUseLoop` directly - `ActionRunner` and `Reflection` modules deleted - `planning.py` reduced to `Planner.generate()` + `PromptBuilder.build()`; old multi-prompt pipeline removed - Test suites replaced: `test_orchestration.py`, `test_action_runner.py`, `test_reflection.py`, `test_planning_intent.py` removed; `test_tool_use_loop.py`, `test_agent_context.py`, `test_spawn_agent.py`, `test_cli_agent_display.py`, `test_project_instructions.py` added ## Tests - `pytest tests/ -v` — 287 passed - `ruff check` — all passed - `mypy` — all passed ## Notes - Net delta: **-706 lines** (3,571 added / 4,277 removed) - Sub-agents bypass `Orchestrator` and plan/mode logic entirely — they always execute - Depth ceiling (default 5) enforced at `AgentContext.child()`; `spawn_agent` removed from schema at max depth - Concurrency ceiling (default 20) enforced via `AgentRegistry` semaphore
1 parent 31de67a commit b8f4d07

62 files changed

Lines changed: 3706 additions & 4332 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devin/wiki.json

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@
2020
"parent": "Core Runtime & Orchestration (meeseeks_core)"
2121
},
2222
{
23-
"title": "Orchestration Engine",
24-
"purpose": "orchestrate_session, Orchestrator, ActionPlanRunner, and plan-act-observe-replan flow.",
23+
"title": "Unified Tool-Use Loop",
24+
"purpose": "ToolUseLoop as the single async execution engine: message construction, LLM invocation, tool dispatch, depth-aware prompts, and project instructions (CLAUDE.md/AGENTS.md) autoloading.",
25+
"parent": "Core Runtime & Orchestration (meeseeks_core)"
26+
},
27+
{
28+
"title": "Agent Hypervisor & Sub-Agents",
29+
"purpose": "AgentHypervisor (hypervisor.py) control plane, AgentContext propagation, SpawnAgentTool, tool scoping (filter-before-binding), admission control, lifecycle tracking, and structured cleanup.",
2530
"parent": "Core Runtime & Orchestration (meeseeks_core)"
2631
},
2732
{
@@ -35,8 +40,8 @@
3540
"parent": "Core Runtime & Orchestration (meeseeks_core)"
3641
},
3742
{
38-
"title": "Permissions, Hooks, Reflection",
39-
"purpose": "Approval callbacks, hook pipeline, and step reflection behavior.",
43+
"title": "Permissions & Hooks",
44+
"purpose": "PermissionPolicy, approval callbacks, HookManager pipeline, and agent lifecycle hooks (on_agent_start, on_agent_stop).",
4045
"parent": "Core Runtime & Orchestration (meeseeks_core)"
4146
},
4247
{
@@ -76,7 +81,7 @@
7681
},
7782
{
7883
"title": "CLI Project (meeseeks_cli)",
79-
"purpose": "CLI flow, dialogs, in-process runtime usage, and command handling.",
84+
"purpose": "CLI flow, dialogs, agent tree display (AgentDisplayManager + Rich Live), in-process runtime usage, and command handling.",
8085
"parent": "Overview & Repo Map"
8186
},
8287
{
@@ -91,7 +96,7 @@
9196
},
9297
{
9398
"title": "Configuration & Runtime Settings",
94-
"purpose": "configs/app.json, configs/mcp.json, env layering, and runtime defaults.",
99+
"purpose": "configs/app.json, configs/mcp.json, AgentConfig, env layering, and runtime defaults.",
95100
"parent": "Overview & Repo Map"
96101
},
97102
{
@@ -101,7 +106,7 @@
101106
},
102107
{
103108
"title": "Testing & Quality",
104-
"purpose": "Integration-first test patterns, boundaries, and coverage targets from tests/AGENTS.md.",
109+
"purpose": "Integration-first test patterns, boundaries, and coverage targets from tests/CLAUDE.md.",
105110
"parent": "Overview & Repo Map"
106111
},
107112
{
@@ -122,15 +127,19 @@
122127
},
123128
{
124129
"title": "Core Runtime & Orchestration (meeseeks_core)",
125-
"notes": "Mermaid: runtime facade flow (SessionRuntime -> Orchestrator -> ActionPlanRunner) and how clients call it."
130+
"notes": "Mermaid: runtime facade flow (SessionRuntime -> Orchestrator -> ToolUseLoop) and how clients call it."
126131
},
127132
{
128133
"title": "Session Runtime & Store",
129134
"notes": "Mermaid: sequence for async run + JSONL event log + API polling (explicitly API-only); include RunRegistry and SessionStore."
130135
},
131136
{
132-
"title": "Orchestration Engine",
133-
"notes": "Mermaid: state machine for plan -> act -> observe -> replan -> respond; label orchestrate_session/Orchestrator/ActionPlanRunner."
137+
"title": "Unified Tool-Use Loop",
138+
"notes": "Mermaid: ToolUseLoop step cycle (build messages -> LLM invoke -> tool dispatch -> gather results -> repeat or done). Show spawn_agent as a recursive call back into ToolUseLoop."
139+
},
140+
{
141+
"title": "Agent Hypervisor & Sub-Agents",
142+
"notes": "Mermaid: agent tree diagram showing AgentContext.root() -> child() propagation, AgentHypervisor (admission, registration, cleanup), and SpawnAgentTool filter-before-binding flow."
134143
},
135144
{
136145
"title": "LLM Abstraction",
@@ -141,8 +150,8 @@
141150
"notes": "Mermaid: data flow from EventRecord -> summary -> ContextBuilder snapshot."
142151
},
143152
{
144-
"title": "Permissions, Hooks, Reflection",
145-
"notes": "Mermaid: sequence diagram of tool call -> hooks -> permission -> tool result -> reflection."
153+
"title": "Permissions & Hooks",
154+
"notes": "Mermaid: sequence diagram of tool call -> hooks -> permission -> tool result; include on_agent_start/on_agent_stop lifecycle hooks."
146155
},
147156
{
148157
"title": "Core Data Models & Events",
@@ -174,7 +183,7 @@
174183
},
175184
{
176185
"title": "CLI Project (meeseeks_cli)",
177-
"notes": "Mermaid: sync flow CLI -> SessionRuntime -> Orchestrator -> response (no polling)."
186+
"notes": "Mermaid: sync flow CLI -> SessionRuntime -> Orchestrator -> ToolUseLoop -> response; include AgentDisplayManager + Rich Live rendering of agent tree via lifecycle hooks."
178187
},
179188
{
180189
"title": "Chat UI Project (meeseeks_chat)",
@@ -186,7 +195,7 @@
186195
},
187196
{
188197
"title": "Configuration & Runtime Settings",
189-
"notes": "Mermaid: config resolution flow (configs/app.json, configs/mcp.json, env overrides)."
198+
"notes": "Mermaid: config resolution flow (configs/app.json, configs/mcp.json, env overrides); include AgentConfig section (max_depth, max_concurrent, allowed_models, default_denied_tools)."
190199
},
191200
{
192201
"title": "CI/CD & Docs Pipeline",

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ repo-to-prompt.codemod.js
44
# MCP config (contains auth tokens)
55
.mcp.json
66

7+
# macOS resource forks / metadata
8+
._*
9+
.DS_Store
10+
711
# Byte-compiled / optimized / DLL files
812
__pycache__/
913
*.py[cod]

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This is a shim file for Claude agents.
2+
3+
Read and follow [@CLAUDE.md](./CLAUDE.md) in this directory as the source of truth for project instructions.

CLAUDE.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
# Agents Guide - Personal Assistant (Meeseeks)
22

33
## What this codebase is
4-
Meeseeks is a multi-agent LLM personal assistant that decomposes user requests into atomic actions, runs them through tools, and returns a synthesized response. It ships multiple interfaces (CLI, chat UI, REST API, Home Assistant) that share the same core engine.
4+
Meeseeks is a multi-agent LLM personal assistant with an async sub-agent hypervisor. The core engine uses a single async `ToolUseLoop` that the LLM drives via native `bind_tools` / `tool_use`. Sub-agents are spawned via a `spawn_agent` tool, tracked by an `AgentHypervisor`, and cleaned up via structured concurrency. It ships multiple interfaces (CLI, chat UI, REST API, Home Assistant) that share the same core engine.
55

66
## Core entry points
7-
- `packages/meeseeks_core/src/meeseeks_core/task_master.py`: action planning + task execution loop
7+
- `packages/meeseeks_core/src/meeseeks_core/tool_use_loop.py`: async tool-use conversation loop (`ToolUseLoop`) — the core execution engine
8+
- `packages/meeseeks_core/src/meeseeks_core/agent_context.py`: `AgentContext` (immutable per-agent state)
9+
- `packages/meeseeks_core/src/meeseeks_core/hypervisor.py`: `AgentHypervisor` (control plane), `AgentHandle` (per-agent runtime state)
10+
- `packages/meeseeks_core/src/meeseeks_core/spawn_agent.py`: `SpawnAgentTool` + `SPAWN_AGENT_SCHEMA` — sub-agent creation with tool scoping
11+
- `packages/meeseeks_core/src/meeseeks_core/orchestrator.py`: session lifecycle, sync→async bridge via `asyncio.run()`
12+
- `packages/meeseeks_core/src/meeseeks_core/task_master.py`: `generate_action_plan` + `orchestrate_session` entry points
813
- `packages/meeseeks_core/src/meeseeks_core/classes.py`: `ActionStep` (tool_id/operation/tool_input), `TaskQueue`, `AbstractTool` contracts
9-
- `packages/meeseeks_core/src/meeseeks_core/planning.py`: `Planner`, `ToolSelector`, `StepExecutor`, `PlanUpdater`
10-
- `packages/meeseeks_core/src/meeseeks_core/session_runtime.py`: session lifecycle, listing, archiving, and async runs
14+
- `packages/meeseeks_core/src/meeseeks_core/planning.py`: `Planner`, `PromptBuilder`
15+
- `packages/meeseeks_core/src/meeseeks_core/session_runtime.py`: session lifecycle, listing, user steering (`enqueue_message`, `interrupt_step`)
1116
- `packages/meeseeks_core/src/meeseeks_core/session_store.py`: transcript storage, tags, and archive state
17+
- `packages/meeseeks_core/src/meeseeks_core/config.py`: `AppConfig` including `AgentConfig` (max_depth, max_concurrent, allowed_models, etc.)
1218
- `packages/meeseeks_tools/src/meeseeks_tools/`: tool implementations and integration glue
1319
- `apps/meeseeks_chat/src/meeseeks_chat/chat_master.py`: Streamlit UI
1420
- `apps/meeseeks_api/src/meeseeks_api/backend.py`: Flask API
15-
- `apps/meeseeks_cli/src/meeseeks_cli/cli_master.py`: terminal CLI
21+
- `apps/meeseeks_cli/src/meeseeks_cli/cli_master.py`: terminal CLI with Rich Live agent display
1622
- `meeseeks_ha_conversation/`: Home Assistant integration
1723

1824
## How to get context fast
@@ -50,7 +56,7 @@ Observability platform for LLM traces. Meeseeks instruments all LLM calls with L
5056
#### Investigation workflow (most common path)
5157
1. **Start broad**: `get_error_count(age=1440)` to check if there are recent errors (last 24h).
5258
2. **List recent sessions**: `fetch_sessions(age=1440)` to find Meeseeks session IDs.
53-
3. **List traces for a session**: `fetch_traces(age=1440, session_id="...", name="meeseeks-task-master")` to find planning traces, or `name="meeseeks-response"` for synthesis traces.
59+
3. **List traces for a session**: `fetch_traces(age=1440, session_id="...", name="meeseeks-tool-use")` to find tool-use loop traces, or `name="meeseeks-task-master"` for planning traces.
5460
4. **Inspect a trace**: `fetch_trace(trace_id="...", include_observations=True)` to see all LLM calls within a trace, including prompts, completions, token counts, and latency.
5561
5. **Drill into a specific LLM call**: `fetch_observation(observation_id="...")` to inspect a single generation's input/output.
5662
6. **Check exceptions**: `get_exception_details(trace_id="...")` when a trace has errors.
@@ -59,7 +65,7 @@ Observability platform for LLM traces. Meeseeks instruments all LLM calls with L
5965
- **`get_error_count(age)`**: Quick health check — returns count of traces with exceptions in the last N minutes (max 10080 = 7 days).
6066
- **`fetch_sessions(age)`**: List Langfuse sessions. Meeseeks sessions map to Langfuse sessions via the session ID in `orchestrator.py`.
6167
- **`get_session_details(session_id, include_observations=True)`**: Deep-dive into a session with all its traces and observations.
62-
- **`fetch_traces(age, ...)`**: Find traces by name, user_id, session_id, tags, or metadata. Key trace names in Meeseeks: `meeseeks-task-master` (planning), `meeseeks-response` (synthesis), `meeseeks-reflection` (step reflection), `meeseeks-context` (context selection).
68+
- **`fetch_traces(age, ...)`**: Find traces by name, user_id, session_id, tags, or metadata. Key trace names in Meeseeks: `meeseeks-tool-use` (main tool-use loop), `meeseeks-task-master` (planning), `meeseeks-context` (context selection).
6369
- **`fetch_trace(trace_id, include_observations=True)`**: Full trace with all child observations. Use `output_mode="full_json_file"` for large traces.
6470
- **`fetch_observations(age, type="GENERATION")`**: Find all LLM generations in a time window. Filter by `name`, `user_id`, `trace_id`, or `parent_observation_id`.
6571
- **`fetch_observation(observation_id)`**: Single observation detail — includes full input/output, model name, token usage, latency.
@@ -93,7 +99,7 @@ Official library/framework documentation and code examples.
9399
- **Parallel queries**: When investigating, fire multiple MCP calls in parallel (e.g., DeepWiki for architecture + Langfuse for traces + SearXNG for docs).
94100
- **Cross-reference**: Use DeepWiki/Devin wiki for "how should it work" and Langfuse for "how did it actually work" during debugging.
95101
- **Session IDs bridge Meeseeks and Langfuse**: The `session_id` from `SessionStore` is the same ID used in Langfuse traces. Use it to jump between local transcript analysis and Langfuse observability.
96-
- **Trace names in Meeseeks**: Planning traces use `user_id="meeseeks-task-master"`, response synthesis uses `user_id="meeseeks-response"`, reflection uses `user_id="meeseeks-reflection"`, context selection uses `user_id="meeseeks-context"`.
102+
- **Trace names in Meeseeks**: Tool-use loop traces use `user_id="meeseeks-tool-use"`, planning uses `user_id="meeseeks-task-master"`, context selection uses `user_id="meeseeks-context"`. Sub-agent traces share the same session_id but have distinct agent_id tags in event payloads.
97103
- **Age parameter**: Langfuse tools use `age` in minutes (not timestamps). Common values: 60 (1h), 1440 (24h), 10080 (7 days max).
98104

99105
## Engineering principles (project-specific)
@@ -108,9 +114,13 @@ Official library/framework documentation and code examples.
108114
- Treat language models as black-box APIs with non-deterministic output; avoid anthropomorphic language and describe changes objectively (e.g., “updated prompts/instructions”).
109115
- Keep type hints precise; avoid loosening to `Any` unless no accurate alternative exists.
110116

111-
## Orchestration insights (transferable)
112-
- Separate tool execution from user-facing response: synthesize after tool results, don't dump raw tool output.
113-
- Keep the loop explicit: plan -> act -> observe -> decide; re-plan only when needed.
117+
## Orchestration architecture
118+
- **Single async loop**: `ToolUseLoop.run()` is the only execution engine. The LLM decides which tools to call via native `bind_tools`. No separate planner→executor→synthesizer pipeline.
119+
- **Sub-agent spawning**: The LLM can call `spawn_agent(task, model, allowed_tools, denied_tools)` to create child `ToolUseLoop` instances. Tool scoping follows Claude Code's "filter before binding" pattern.
120+
- **Agent hypervisor**: `AgentHypervisor` tracks all agents, enforces admission control (max_concurrent via Semaphore), and guarantees cleanup via structured concurrency (`asyncio.gather` + `finally` blocks).
121+
- **Depth control**: Max depth 5 (configurable). At max depth, `spawn_agent` is removed from the tool schema entirely. Depth-aware prompts guide spawn behavior.
122+
- **User steering**: Root agent has a `message_queue` (drained between steps as HumanMessage) and `interrupt_step` event. Sub-agents do not receive user messages.
123+
- **Planning is root-only**: Sub-agents always execute (act mode). They bypass `Orchestrator` and its plan/mode logic entirely.
114124
- Make tool inputs schema-aware; prefer structured `tool_input` for MCP tools.
115125
- Surface tool activity clearly (permissions, tool IDs, arguments) to reduce user confusion.
116126

README.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ https://github.com/user-attachments/assets/78754e8f-828a-4c54-9e97-29cbeacbc3bc
1919
2020
# Intro
2121

22-
Meeseeks is an AI task agent assistant built on a plan → tool selection → step execution loop. It breaks a request into steps, chooses tools per step, runs them, and synthesizes a final reply. It keeps a session transcript, compacts long histories, and stores summaries for continuity across longer conversations.
22+
Meeseeks is an AI task agent assistant built on a single async tool-use loop driven by native `bind_tools`. The LLM decides which tools to call, can spawn sub-agents for parallel work, and synthesizes a final reply. It keeps a session transcript, compacts long histories, and stores summaries for continuity across longer conversations.
2323

2424
<details>
2525
<summary><i>Legends (Expand to View) </i></summary>
@@ -61,10 +61,10 @@ We are upgrading the API backend to better support a task-orchestration frontend
6161
</table>
6262

6363
## Core workflow
64-
- (✅) **Plan → select tools → execute:** Builds a short plan, chooses the right tools per step, and executes them.
65-
- (✅) **Step-level reflection:** Validates tool outcomes and adjusts tool inputs when required.
66-
- (✅) **Plan updates:** Emits updated action plans after each step so UIs can refresh the to‑do list.
67-
- (✅) **Synthesized replies:** Produces a final answer after tool results are collected and summarized.
64+
- (✅) **Unified tool-use loop:** A single async `ToolUseLoop` where the LLM drives tool selection and execution via native `bind_tools`.
65+
- (✅) **Sub-agent spawning:** The LLM can delegate subtasks to parallel sub-agents via `spawn_agent`, managed by the `AgentHypervisor` control plane.
66+
- (✅) **Tool scoping & permissions:** Sub-agents receive scoped tool access (allowlist/denylist filtered before binding). Permission policies gate all tool execution.
67+
- (✅) **Synthesized replies:** Produces a final answer after tool results are collected.
6868

6969
## Memory and context management
7070
- (✅) **Session transcripts:** Writes tool activity and responses to disk for continuity.
@@ -136,13 +136,11 @@ flowchart LR
136136
subgraph Core["Core Orchestration\n(packages/meeseeks_core)"]
137137
TaskMaster["orchestrate_session\n(task_master.py)"]
138138
Orchestrator["Orchestrator\n(orchestrator.py)"]
139+
ToolUseLoop["ToolUseLoop\n(tool_use_loop.py)"]
140+
Hypervisor["AgentHypervisor\n(hypervisor.py)"]
141+
SpawnAgent["SpawnAgentTool\n(spawn_agent.py)"]
139142
Planner["Planner\n(planning.py)"]
140-
ToolSelector["ToolSelector\n(planning.py)"]
141-
StepExecutor["StepExecutor\n(planning.py)"]
142-
PlanUpdater["PlanUpdater\n(planning.py)"]
143-
ActionPlanRunner["ActionPlanRunner\n(action_runner.py)"]
144143
ContextBuilder["ContextBuilder\n(context.py)"]
145-
ActionStep["ActionStep\n(classes.py)"]
146144
TaskQueue["TaskQueue\n(classes.py)"]
147145
end
148146
@@ -182,24 +180,24 @@ flowchart LR
182180
SessionRuntime --> RunRegistry
183181
184182
TaskMaster --> Orchestrator
183+
Orchestrator --> ToolUseLoop
185184
Orchestrator --> Planner
186-
Orchestrator --> ToolSelector
187-
Orchestrator --> StepExecutor
188-
Orchestrator --> PlanUpdater
189-
Orchestrator --> ActionPlanRunner
185+
Orchestrator --> Hypervisor
190186
Orchestrator --> ContextBuilder
191-
Orchestrator --> ToolRegistry
192187
Orchestrator --> SessionStore
193188
194-
ActionPlanRunner --> TaskQueue
195-
ActionPlanRunner --> ActionStep
189+
ToolUseLoop --> SpawnAgent
190+
ToolUseLoop --> ToolRegistry
191+
ToolUseLoop --> TaskQueue
192+
SpawnAgent -->|"child loop"| ToolUseLoop
193+
SpawnAgent --> Hypervisor
196194
197195
ToolRegistry --> AbstractTool
198196
AbstractTool --> LocalTools
199197
AbstractTool --> MCPTools
200198
AbstractTool --> HATools
201199
202-
Orchestrator --> BuildChatModel
200+
ToolUseLoop --> BuildChatModel
203201
BuildChatModel --> ChatModel
204202
205203
SessionStore --> EventLog

0 commit comments

Comments
 (0)