feat(migration): phase 3 — multi-agent graph tools + Runner bridge

Six SDK function tools that drive the AgentMessageBus from Phase 0,
replacing the legacy _agent_graph / _agent_messages / _agent_instances
globals:

- view_agent_graph: render parent/child tree from bus.parent_of with a
  per-status summary (running / waiting / completed / crashed / stopped).
- agent_status: per-agent lifecycle + pending-message count snapshot.
- send_message_to_agent: queue into bus.inboxes; rejects sends to
  finalized targets so the model gets feedback rather than a silent
  drop (the bus's own send method drops to support the C13 cleanup,
  but the tool surfaces it as a structured error).
- wait_for_message: poll inbox once per second up to timeout. Polling
  rather than asyncio.Event because a missed wakeup on Event would be
  hard to debug; the bus already serializes through its own lock.
- create_agent: spawn a child via asyncio.create_task(Runner.run(...)).
  Pulls an agent_factory callable from ctx.context (the Phase 5 root
  assembly is the one that wires it in). Registers the child with the
  bus before the task starts, stores the task handle in bus.tasks so
  cancel_descendants can cascade (C9), builds the child's identity
  block + optional inherited parent context, and runs the child with
  StrixOrchestrationHooks.
- agent_finish: subagent-only termination. Flips agent_finish_called
  so the on_agent_end hook records "completed" instead of "crashed"
  (C8), and posts a structured <agent_completion_report> XML envelope
  to the parent's inbox.

run_config_factory.make_agent_context grows two fields: sandbox_client
(reused across child runs) and agent_factory (Phase 3 needs it; Phase 5
fills it in). PLC0415 fixed by hoisting the openai.types.shared.Reasoning
import to module-level.

Tests: 17 new tests in test_sdk_graph_tools.py — registration, all six
tools' happy and error paths, real AgentMessageBus integration so the
tools exercise production code paths, create_agent verified for spawn
shape (task created, bus registered, identity block in input) plus a
bus.cancel_descendants integration check.

Refs: PLAYBOOK.md §4.3, AUDIT_R2 §1.4 (cancel_descendants), AUDIT_R3 C8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-04-25 00:36:00 -07:00
parent 044e4e82ae
commit 1ac32df817
4 changed files with 1007 additions and 2 deletions
+11 -2
View File
@@ -27,6 +27,7 @@ from agents.retry import (
retry_policies,
)
from agents.sandbox import SandboxRunConfig
from openai.types.shared import Reasoning
from strix.llm.multi_provider_setup import build_multi_provider
from strix.orchestration.filter import inject_messages_filter
@@ -130,8 +131,6 @@ def make_run_config(
),
)
if reasoning_effort is not None:
from openai.types.shared import Reasoning
base_settings = base_settings.resolve(
ModelSettings(reasoning=Reasoning(effort=reasoning_effort)),
)
@@ -174,6 +173,8 @@ def make_agent_context(
is_whitebox: bool = False,
diff_scope: dict[str, Any] | None = None,
run_id: str | None = None,
sandbox_client: Any | None = None,
agent_factory: Any | None = None,
) -> dict[str, Any]:
"""Build the per-agent ``context`` dict passed to ``Runner.run(context=...)``.
@@ -184,10 +185,17 @@ def make_agent_context(
C21 (AUDIT_R3): includes ``is_whitebox``, ``diff_scope`` and ``run_id``
fields that the legacy code relied on but the original PLAYBOOK §2.10
skeleton omitted.
``agent_factory`` is a callable ``(name, skills) -> agents.Agent`` used by
the ``create_agent`` graph tool to spin up children. The actual factory
lives in the Phase 4/5 root-assembly module; Phase 3 only requires that
it be present in context. ``sandbox_client`` is the host-side Docker
subclass; ``create_agent`` reuses it across child runs.
"""
return {
"bus": bus,
"sandbox_session": sandbox_session,
"sandbox_client": sandbox_client,
"sandbox_token": sandbox_token,
"tool_server_host_port": tool_server_host_port,
"caido_host_port": caido_host_port,
@@ -203,4 +211,5 @@ def make_agent_context(
"is_whitebox": is_whitebox,
"diff_scope": diff_scope,
"run_id": run_id,
"agent_factory": agent_factory,
}