chore(orchestration): drop XML wrappers + close remaining audit gaps

Final pass after re-audit. Three sub-specs landed:

**XML simplification** — the legacy XML envelopes were prompt-engineering
ceremony, not parser primitives (the SDK uses native tool-calling). Drop
the verbose wrappers in favor of one-liner labeled headers. Side benefit:
fixes the unescaped-content XML-injection bug the audit caught (peer
content containing ``</content>`` no longer breaks the wrapper).

- ``_format_inter_agent_message``: ``<inter_agent_message><sender>...
  <content>...`` 9-line XML → ``[Message from {name} ({id}) | type=... |
  priority=...]\n{content}``.
- ``_render_completion_report``: ``<agent_completion_report><agent_info>
  ...<results>...`` XML → human-readable structured text with section
  headers and bulleted lists.
- ``inherited_context``: ``<inherited_context_from_parent>...`` →
  ``== Inherited context from parent (background only) ==``.

**MG1: TUI stop-agent uses graceful cancel.** ``tui.py`` was calling
``bus.cancel_descendants`` (hard, ``task.cancel()`` mid-stream) for the
stop-agent button. Switched to ``bus.cancel_descendants_graceful``, which
uses ``RunResultStreaming.cancel(mode="after_turn")`` to let each agent
finish its current turn (and save to session) before honoring the cancel.
The hard path remains in ``entry.py`` for KeyboardInterrupt where
graceful isn't possible.

**MG2: Document hook lock-free stats mutation.** Added a comment in
``hooks.on_llm_start`` explaining why ``warned_85`` / ``warned_final``
are mutated lock-free: SDK serializes ``on_llm_start`` per agent, so this
hook is the sole writer to those keys; ``record_usage`` only writes
disjoint keys (in/out/cached/calls).

**AG3: Auto-load ``coordination/root_agent`` skill for the root.**
Legacy auto-loaded the orchestration-guidance skill for root agents
only. Threaded ``is_root`` through ``render_system_prompt`` →
``_resolve_skills``; root agents now get the skill, children don't.

Skipped (per user direction): whitebox-wiki integration (CG2-4) — the
auto-injection / auto-update of the shared repo wiki was a pre-migration
feature; user opted not to restore it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-04-25 17:48:55 -07:00
parent f4834cd6f7
commit 25decb0685
6 changed files with 66 additions and 46 deletions
+1
View File
@@ -151,6 +151,7 @@ def build_strix_agent(
skills=skills, skills=skills,
scan_mode=scan_mode, scan_mode=scan_mode,
is_whitebox=is_whitebox, is_whitebox=is_whitebox,
is_root=is_root,
interactive=interactive, interactive=interactive,
system_prompt_context=system_prompt_context, system_prompt_context=system_prompt_context,
) )
+10 -1
View File
@@ -27,6 +27,7 @@ def _resolve_skills(
requested: list[str] | None, requested: list[str] | None,
scan_mode: str = "deep", scan_mode: str = "deep",
is_whitebox: bool = False, is_whitebox: bool = False,
is_root: bool = False,
) -> list[str]: ) -> list[str]:
"""Build the deduped, ordered skills list for the prompt render. """Build the deduped, ordered skills list for the prompt render.
@@ -36,11 +37,15 @@ def _resolve_skills(
2. ``scan_modes/<mode>`` (always). 2. ``scan_modes/<mode>`` (always).
3. ``tooling/agent_browser`` (always — every agent has shell + the 3. ``tooling/agent_browser`` (always — every agent has shell + the
agent-browser CLI). agent-browser CLI).
4. Whitebox-specific skills if applicable. 4. ``coordination/root_agent`` for the root agent only — orchestration
guidance for delegating to specialist subagents.
5. Whitebox-specific skills if applicable.
""" """
ordered: list[str] = list(requested or []) ordered: list[str] = list(requested or [])
ordered.append(f"scan_modes/{scan_mode}") ordered.append(f"scan_modes/{scan_mode}")
ordered.append("tooling/agent_browser") ordered.append("tooling/agent_browser")
if is_root:
ordered.append("coordination/root_agent")
if is_whitebox: if is_whitebox:
ordered.append("coordination/source_aware_whitebox") ordered.append("coordination/source_aware_whitebox")
ordered.append("custom/source_aware_sast") ordered.append("custom/source_aware_sast")
@@ -59,6 +64,7 @@ def render_system_prompt(
skills: list[str] | None = None, skills: list[str] | None = None,
scan_mode: str = "deep", scan_mode: str = "deep",
is_whitebox: bool = False, is_whitebox: bool = False,
is_root: bool = False,
interactive: bool = False, interactive: bool = False,
system_prompt_context: dict[str, Any] | None = None, system_prompt_context: dict[str, Any] | None = None,
) -> str: ) -> str:
@@ -70,6 +76,8 @@ def render_system_prompt(
skill. skill.
is_whitebox: When True, the source-aware whitebox skill stack is_whitebox: When True, the source-aware whitebox skill stack
is loaded too. is loaded too.
is_root: When True, ``coordination/root_agent`` orchestration
guidance is auto-loaded.
interactive: When True, the prompt renders the interactive-mode interactive: When True, the prompt renders the interactive-mode
communication rules block. communication rules block.
system_prompt_context: Free-form dict that the template's system_prompt_context: Free-form dict that the template's
@@ -96,6 +104,7 @@ def render_system_prompt(
requested=skills, requested=skills,
scan_mode=scan_mode, scan_mode=scan_mode,
is_whitebox=is_whitebox, is_whitebox=is_whitebox,
is_root=is_root,
) )
skill_content = load_skills(skills_to_load) skill_content = load_skills(skills_to_load)
env.globals["get_skill"] = lambda name: skill_content.get(name, "") env.globals["get_skill"] = lambda name: skill_content.get(name, "")
+6 -1
View File
@@ -1843,11 +1843,16 @@ class StrixTUIApp(App): # type: ignore[misc]
return agent_name, False return agent_name, False
def action_confirm_stop_agent(self, agent_id: str) -> None: def action_confirm_stop_agent(self, agent_id: str) -> None:
# Graceful stop: each agent's current turn finishes (and is saved to
# session) before the run loop honors the cancel. The interactive
# outer loop sees ``stopping`` and exits with status="stopped".
# The hard ``cancel_descendants`` path remains for KeyboardInterrupt
# in entry.py where graceful isn't possible.
if self._scan_loop is None or self._scan_loop.is_closed(): if self._scan_loop is None or self._scan_loop.is_closed():
logging.warning("No active scan loop; cannot stop agent %s", agent_id) logging.warning("No active scan loop; cannot stop agent %s", agent_id)
return return
asyncio.run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.bus.cancel_descendants(agent_id), self.bus.cancel_descendants_graceful(agent_id),
self._scan_loop, self._scan_loop,
) )
+14 -25
View File
@@ -8,7 +8,6 @@ retries — so a single drain per turn doesn't lose messages on retry.
from __future__ import annotations from __future__ import annotations
import logging import logging
from datetime import UTC, datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from agents.run_config import CallModelData, ModelInputData from agents.run_config import CallModelData, ModelInputData
@@ -26,11 +25,8 @@ logger = logging.getLogger(__name__)
async def inject_messages_filter(data: CallModelData) -> ModelInputData: async def inject_messages_filter(data: CallModelData) -> ModelInputData:
"""Drain bus inbox and append messages as user-role items before the LLM call. """Drain bus inbox and append messages as user-role items before the LLM call.
Peer-agent messages are wrapped in the legacy ``<inter_agent_message>`` Peer-agent messages get a one-line labeled header followed by the
XML envelope (sender / metadata / content / delivery_info) so the body. Direct user messages (``from="user"``) are passed plain.
receiving model gets the same prompt-shape as pre-migration including
the explicit "DO NOT echo back" instruction. Direct user messages
(``from="user"``) are passed plain.
Any exception inside the filter malformed message dict, bug in Any exception inside the filter malformed message dict, bug in
``bus.drain``, etc. is caught and the original ``data.model_data`` ``bus.drain``, etc. is caught and the original ``data.model_data``
@@ -72,31 +68,24 @@ async def inject_messages_filter(data: CallModelData) -> ModelInputData:
def _format_inter_agent_message(bus: AgentMessageBus, msg: dict[str, Any]) -> str: def _format_inter_agent_message(bus: AgentMessageBus, msg: dict[str, Any]) -> str:
"""Render a peer-agent message in the legacy XML envelope. """Render a peer-agent message as a labeled header + body.
The wrapper carries an explicit "do not echo back" instruction that Format:
the legacy harness used to keep models from quoting the entire [Message from {name} ({id}) | type={type} | priority={priority}]
received message dict in their own next turn. {content}
Plain text by design no XML wrapping, no escaping concerns. The
label line tells the receiver who sent this and why so it doesn't
confuse a peer message with its own work; the rest of the body is
delivered as-is.
""" """
sender_id = str(msg.get("from", "unknown")) sender_id = str(msg.get("from", "unknown"))
sender_name = bus.names.get(sender_id, sender_id) sender_name = bus.names.get(sender_id, sender_id)
msg_type = msg.get("type", "information") msg_type = msg.get("type", "information")
priority = msg.get("priority", "normal") priority = msg.get("priority", "normal")
timestamp = msg.get("timestamp") or datetime.now(UTC).isoformat()
content = str(msg.get("content", "")) content = str(msg.get("content", ""))
return ( return (
"<inter_agent_message>\n" f"[Message from {sender_name} ({sender_id}) "
" <delivery_notice><important>You have received a message from another " f"| type={msg_type} | priority={priority}]\n"
"agent. Acknowledge and respond to the sender if needed; DO NOT echo " f"{content}"
"back this entire message block.</important></delivery_notice>\n"
f" <sender><agent_name>{sender_name}</agent_name>"
f"<agent_id>{sender_id}</agent_id></sender>\n"
f" <message_metadata><type>{msg_type}</type>"
f"<priority>{priority}</priority>"
f"<timestamp>{timestamp}</timestamp></message_metadata>\n"
f" <content>{content}</content>\n"
" <delivery_info><note>This message was delivered during your task "
"execution. Please acknowledge and respond if needed.</note>"
"</delivery_info>\n"
"</inter_agent_message>"
) )
+7
View File
@@ -56,6 +56,13 @@ class StrixOrchestrationHooks(RunHooks[Any]):
# warnings fire exactly once per agent lifetime — surviving # warnings fire exactly once per agent lifetime — surviving
# ``run_with_continuation`` cycles, mirroring legacy # ``run_with_continuation`` cycles, mirroring legacy
# ``state.max_iterations_warning_sent``. # ``state.max_iterations_warning_sent``.
#
# The flags are mutated lock-free below. Safe because the SDK
# serializes ``on_llm_start`` per agent (one in-flight LLM call
# per ``Runner.run`` instance), so this hook is the sole writer
# to ``warned_85`` / ``warned_final`` for this agent_id.
# ``record_usage`` (which acquires the lock) only writes
# ``in`` / ``out`` / ``cached`` / ``calls`` — disjoint keys.
if cur >= int(max_turns * 0.85) and not stats.get("warned_85"): if cur >= int(max_turns * 0.85) and not stats.get("warned_85"):
stats["warned_85"] = True stats["warned_85"] = True
input_items.append( input_items.append(
+28 -19
View File
@@ -19,6 +19,7 @@ import asyncio
import json import json
import logging import logging
import uuid import uuid
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any, Literal from typing import TYPE_CHECKING, Any, Literal
from agents import RunContextWrapper, function_tool from agents import RunContextWrapper, function_tool
@@ -52,26 +53,34 @@ def _render_completion_report(
findings: list[str], findings: list[str],
recommendations: list[str], recommendations: list[str],
) -> str: ) -> str:
"""Render an ``<agent_completion_report>`` XML payload (legacy parity).""" """Render a child's completion report as plain structured text.
from datetime import UTC, datetime
from html import escape
Goes into the parent's bus inbox; the inject filter prepends a
``[Message from ...]`` header on top, so this body just carries the
contents. No XML no escaping concerns, no parser ambiguity.
"""
status = "SUCCESS" if success else "FAILED" status = "SUCCESS" if success else "FAILED"
completion_time = datetime.now(UTC).isoformat() completion_time = datetime.now(UTC).isoformat()
findings_xml = "".join(f"<finding>{escape(f)}</finding>" for f in findings)
recs_xml = "".join(f"<recommendation>{escape(r)}</recommendation>" for r in recommendations) lines: list[str] = [
return ( f"== Completion report from {agent_name} ({agent_id}) ==",
"<agent_completion_report>\n" f"Status: {status}",
f" <agent_info><agent_name>{escape(agent_name)}</agent_name>" f"Time: {completion_time}",
f"<agent_id>{escape(agent_id)}</agent_id>" ]
f"<task>{escape(task)}</task>" if task:
f"<status>{status}</status>" lines.append(f"Task: {task}")
f"<completion_time>{completion_time}</completion_time></agent_info>\n" lines.append("")
f" <results><summary>{escape(result_summary)}</summary>" lines.append("Summary:")
f"<findings>{findings_xml}</findings>" lines.append(result_summary or "(none)")
f"<recommendations>{recs_xml}</recommendations></results>\n" if findings:
"</agent_completion_report>" lines.append("")
) lines.append("Findings:")
lines.extend(f"- {f}" for f in findings)
if recommendations:
lines.append("")
lines.append("Recommendations:")
lines.extend(f"- {r}" for r in recommendations)
return "\n".join(lines)
@function_tool(timeout=30) @function_tool(timeout=30)
@@ -456,9 +465,9 @@ async def create_agent(
{ {
"role": "user", "role": "user",
"content": ( "content": (
"<inherited_context_from_parent>\n" "== Inherited context from parent (background only) ==\n"
f"{rendered}\n" f"{rendered}\n"
"</inherited_context_from_parent>\n" "== End of inherited context ==\n"
"Use the above as background only; do not continue the " "Use the above as background only; do not continue the "
"parent's work. Your task follows." "parent's work. Your task follows."
), ),