xml-pipeline/docs/handler-contract-v2.1.md
2026-01-08 12:30:58 -08:00

2.9 KiB

AgentServer v2.1 — Handler Contract

January 08, 2026

This document is the single canonical specification for all capability handlers in AgentServer v2.1.
All examples, documentation, and implementation must conform to this contract.

Handler Signature (Locked)

Every handler must be declared with the following exact signature:

async def handler(
    payload: PayloadDataclass,      # XSD-validated, deserialized @xmlify dataclass instance
    metadata: HandlerMetadata       # Minimal trustworthy context provided by the message pump
) -> bytes:
    ...
  • Handlers must be asynchronous (async def).
  • Synchronous functions are not permitted and will not be auto-wrapped.
  • The metadata parameter is mandatory.
  • The return value must be a bytes object containing one or more raw XML payload fragments.
  • Returning None or any non-bytes value is a programming error and will trigger a protective <huh> emission.

HandlerMetadata

@dataclass(frozen=True)
class HandlerMetadata:
    thread_id: str                  # Opaque thread UUID — safe for thread-scoped storage
    own_name: str | None = None     # Registered name of the executing listener.
                                    # Populated ONLY for listeners with `agent: true` in organism.yaml

Field Rationale

  • thread_id: Enables isolated per-thread state (e.g., conversation memory, calculator history) without exposing topology.
  • own_name: Allows LLM agents to produce self-referential reasoning text while remaining blind to routing mechanics.

No sender identity (from_id) is provided — preserving full topology privacy.

Security Model

The message pump captures all security-critical information (sender name, thread hierarchy, peers list enforcement) in trusted coroutine scope before invoking the handler.

Handlers are treated as untrusted code. They receive only the minimal safe context defined above and cannot:

  • Forge provenance
  • Escape thread boundaries
  • Probe or leak topology
  • Route arbitrarily

Example Handlers

Pure tool (no agent flag):

async def add_handler(payload: AddPayload, metadata: HandlerMetadata) -> bytes:
    result = payload.a + payload.b
    return f"<result>{result}</result>".encode("utf-8")

LLM agent (agent: true):

async def research_handler(payload: ResearchPayload, metadata: HandlerMetadata) -> bytes:
    own = metadata.own_name or "researcher"  # safe fallback
    return b"""
    <thought>I am the """ + own.encode() + b""" agent. Next step...</thought>
    <calculator.add.addpayload><a>7</a><b>35</b></calculator.add.addpayload>
    """

References in Other Documentation

  • All code examples in README.md, self-grammar-generation.md, and configuration.md must match this contract.
  • listener-class-v2.1.md now references this file as the authoritative source for signature and metadata.

This contract is now locked for v2.1