> FEATURES

Everything an agent team needs.

From semantic routing to the A2A protocol — every feature is built for production teams.

> Pi Runtime > Hybrid Routing > Portal & API > Teams of teams > NATS JetStream > Persona System > A2A Protocol > Chains & Schedules
> SYSTEM FOUNDATION

Built on the Pi runtime

AgentOffice is a Pi extension. The Pi runtime (pi-coding-agent) manages the conversation loop, tool execution, provider authentication, and streaming. AgentOffice adds the orchestration layer: semantic routing, cross-team transport (coms-net or NATS), a real-time portal, and the A2A protocol.

LAYER 3 — ECOSYSTEM
pi.dev packages
tools and capabilities
⬇
LAYER 2 — RUNTIME
Pi runtime
conversation loop + providers + streaming
⬇
LAYER 1 — ORCHESTRATION
AgentOffice
routing + hub + portal + A2A
★
extensions/agent-office.ts
// AgentOffice as a Pi extension
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { createHub }     from "./hub/index.js";
import { HybridScorer } from "./routing/hybrid.js";
import { startPortal }  from "./portal/server.js";

export default function agentOffice(api: ExtensionAPI) {
  const scorer = new HybridScorer(api.agents);
  const hub    = createHub(api, scorer);

  // Tool: send a message to another agent
  api.registerTool("send_message", async (args) => {
    const target = await scorer.route(args.message);
    return hub.dispatch(target.id, args.message);
  });

  // Command: team control panel
  api.registerCommand("office", async (args) => {
    return hub.status();
  });

  // Web portal on port 3000
  startPortal({ hub, port: 3000 });
}
> HYBRID ROUTING

HybridScorer — BM25F + Embeddings + RRF

The routing pipeline combines sparse and dense retrieval, fusing the rankings with Reciprocal Rank Fusion. Zero configuration required — with optional cross-encoder reranking for maximum precision.

STEP 01
BM25F

Per-field sparse retrieval — title, tags, and description weighted individually.

→
STEP 02
all-MiniLM-L6-v2

Dense embeddings for semantic similarity. Runs locally, with no external calls.

→
STEP 03
RRF k=60

Reciprocal Rank Fusion — merges the two rankings with no manually tuned parameters.

→
STEP 04
optional
cross-encoder

ms-marco-MiniLM reranking. Enabled via ROUTING_RERANK=true.

5ms
p50 latency without reranker
20ms
p99 latency with cross-encoder
0
required parameters
⚡
QdrantScorer for large fleets

For 1000+ agents: enable via QDRANT_URL. The system automatically migrates from the in-memory HybridScorer to the QdrantScorer, with no downtime and no practical limit on vectors.

Example SSE event emitted after every routing decision:
routing:decision — Server-Sent Event
// SSE stream — connect via GET /stream/:sessionId
event: routing:decision
data: {
  "query":  "need help deploying to k8s",
  "winner": {
    "id":    "devops-agent",
    "score": 0.94,
    "method":"hybrid"
  },
  "candidates": [
    { "id": "devops-agent",  "bm25": 0.89, "embed": 0.91, "rrf": 0.94 },
    { "id": "backend-agent", "bm25": 0.61, "embed": 0.58, "rrf": 0.62 }
  ],
  "latencyMs": 4,
  "reranked": false
}
> PORTAL AND API

Real-time interface + complete REST

The web portal runs on port 3000, the REST API on port 3777. Both operate in parallel — the portal consumes the same API you use in your own code.

Portal — port 3000

Real-time chat SSE streaming Routing visualization Agent status Message history File upload Tool calls trace Multi-session Dark terminal Agent picker Team view Metrics dashboard

REST API — port 3777

METHOD ENDPOINT DESCRIPTION
GET /agents Lists available agents with their capabilities
POST /message Sends a message, returns an SSE stream
GET /agents/:id/status Current agent state (idle / busy / error)
GET /stream/:id SSE stream for an active session
POST /team Creates an agent team with routing policies
GET /teams Lists teams and active members
DEL /session/:id Closes the session and releases resources
GET /routing/log History of routing decisions
POST /session Opens a new session with a specific agent
GET /sessions Lists open sessions by team
POST /agents/:id/reload Reloads the agent's context and skills
GET /metrics Routing, latency, and throughput metrics
PUT /agents/:id/skills Updates an agent's local skills at runtime
POST /chain Executes a multi-step chain across agents
GET /schedules Lists active schedules and upcoming runs
POST /schedules Creates a schedule with a cron expression
DEL /schedules/:id Removes a schedule
GET /.well-known/agent.json A2A protocol Agent Card
> RECURSIVE ARCHITECTURE

Teams of teams

Loading agent-office into a Pi creates a team. Any agent on that team can, in turn, load its own agent-office extension and become a team of its own — with its own agents, extensions, and configuration. No limit on depth, no limit on how many teams exist in the ecosystem.

Team — Project A
agent: sdr
agent: closer
agent: forecast → spins up agent-office
Team — “forecast” (sub-team)
agent: data
agent: report
↔
coms-net
or NATS
Team — Project B
agent: triage
agent: billing
agent: escalation
any team above
→
A2A — outside the Pi ecosystem
→
external agent (third-party framework)

Three mechanisms, three roles — not stacked layers, but alternatives for different needs:

MECHANISM ROLE WHEN TO USE
coms-net hub Centralized HTTP transport between teams/instances Default — simple, no broker infrastructure
NATS JetStream Decentralized pub/sub transport between teams/instances Alternative to coms-net — larger topologies, at-least-once delivery
A2A Protocol Interoperability with agents/frameworks outside the Pi ecosystem When the other end doesn't speak AgentOffice's internal protocol
ⓘ
coms-net and NATS are alternatives, not complements: when PI_NATS_URL is set, NATS takes over discovery and dispatch between teams; without it, the coms-net HTTP hub covers the same function as a fallback. Semantic routing (messageRouter) decides who responds regardless of which transport is active underneath.
> P2P TRANSPORT

NATS JetStream — at-least-once between instances

Each AgentOffice instance connects to the NATS cluster on startup. Peers are discovered immediately — no warm-up, no manual registration. Messages persist for 1 hour to guarantee delivery.

SUBJECT DIRECTION FUNCTION
tasks.route Pub New task sent for routing
tasks.result Sub Final result of the agent's execution
tasks.stream Sub Incremental result streaming (chunks)
tasks.cancel Pub Cancellation of a task in progress
agents.ping Pub/Sub Peer discovery — emitted immediately on connect
agents.capabilities Sub Capabilities announced by remote peers
RECOMMENDED Mode A — JWT Resolver

Per-agent .creds credentials — identity isolation and granular permissions across the cluster.

PI_NATS_URL=nats://cluster:4222
NATS_CREDS_DEVOPS=./creds/devops.creds
NATS_CREDS_BUILDER=./creds/builder.creds
FALLBACK Mode B — Shared Token

Single token for all agents — ideal for local development and testing environments.

PI_NATS_URL=nats://localhost:4222
PI_NATS_TOKEN=my-secret-token
HEARTBEAT
10s — inactive peers are automatically removed from the catalog.
PEER DISCOVERY
Immediate on connect — no waiting for the heartbeat cycle.
PERSISTENCE
Messages retained for 1 hour — automatic redelivery after reconnection.
DELIVERY
At-least-once guaranteed by JetStream — no silent loss.
> PERSONA SYSTEM

7 layers of context

Each agent is built from context layers with a defined priority. Lower-numbered layers take precedence — AGENTS.md defines who does what, SOUL.md persists the personality.

PRIO FILE FUNCTION
10 AGENTS.md Agent definitions and routing
20 SOUL.md Persistent personality traits
30 IDENTITY.md Agent role and specialization
40 USER.md User preferences and history
50 TOOLS.md Available tools and instructions
60 global skills Capabilities shared across agents
70 local skills Agent-exclusive capabilities
persona loader — boot sequence
$ ao start --agent devops
Loading AGENTS.md [prio 10] ✓
Loading SOUL.md [prio 20] ✓
Loading IDENTITY.md [prio 30] ✓
Loading USER.md [prio 40] ✓
Loading TOOLS.md [prio 50] ✓
Global skills: 3 [prio 60] ✓
Local skills: 2 [prio 70] ✓
devops-agent ready
extensions/skills/ — structure
# Skills available to agents
extensions/skills/
├── global/           ← available to everyone
│   ├── code-review.md
│   ├── git-workflow.md
│   └── security-check.md
└── agents/
    ├── devops/       ← devops agent only
    │   ├── deploy.md
    │   └── k8s-debug.md
    └── builder/      ← builder agent only
        ├── scaffold.md
        └── test-runner.md
> INTEROPERABILITY

Agent-to-Agent Protocol — native Google A2A

Any A2A-compatible external agent can discover and interact with AgentOffice — and vice versa. The handshake starts via the public Agent Card at /.well-known/agent.json.

A2A — communication topology
┌─────────────────────┐ ┌──────────────────────────┐
│ External Agent │ │ AgentOffice │
│ │ │ │
│ GET /.well-known/ │ ───────► │ agent.json (Agent Card) │
│ agent.json │ ◄─────── │ capabilities, endpoints │
│ │ │ │
│ POST /a2a │ ─────────────────────────────────►│
│ tasks/send │ ◄─────────────────────────────────│
│ │ │ │
│ SSE subscribe │ ◄── stream tasks/sendSubscribe ───│
└─────────────────────┘ └──────────────────────────┘
POST tasks/send

Sends a task and waits for the full response. Blocking — ideal for short tasks or when the result is needed immediately.

POST tasks/sendSubscribe

Sends and receives the result as an SSE stream. Ideal for long-running tasks — the agent emits chunks as it processes.

GET tasks/get

Checks the status of an async task by ID. Use alongside tasks/send in non-blocking mode.

DEL tasks/cancel

Cancels a running task. The agent receives the cancellation signal and releases resources — consistent with the NATS tasks.cancel signal.

GET /.well-known/agent.json — Agent Card
{
  "name":        "AgentOffice Hub",
  "description": "Multi-agent orchestration platform built on Pi runtime",
  "url":         "https://ao.platformteam.dev",
  "version":     "1.0.0",
  "capabilities": {
    "streaming":  true,
    "pushNotifications": false,
    "stateTransitionHistory": true
  },
  "skills": [
    {
      "id":          "route-message",
      "name":        "Route Message",
      "description": "Route a message to the best-matched agent via HybridScorer",
      "tags":        ["routing", "multi-agent", "nlp"]
    }
  ]
}
> AUTOMATION

Chains and Schedules

Chains link steps across different agents with result interpolation. Schedules trigger chains or standalone messages at times defined by a cron expression.

CHAINS — multi-step across agents

Each step sends a message to an agent and captures the result. A step's result is available as {{stepN.result}} in subsequent steps.

chains/code-review.yaml
name: code-review-pipeline
steps:
  - id: fetch-diff
    agent: devops
    message: "Generate the diff for PR #{{input.pr_number}}"

  - id: review-code
    agent: reviewer
    message: |
      Review the diff below and flag issues:
      {{fetch-diff.result}}

  - id: write-comment
    agent: github-bot
    message: "Post on the PR: {{review-code.result}}"
ℹ
Typed interpolation

Besides result, you can access status, agentId, and latencyMs for each step.

SCHEDULES — scheduled execution

Schedules use standard cron expressions and support time zones. They can trigger full chains or standalone messages to specific agents.

schedules/daily-report.yaml
name: daily-standup-report
cron:     "0 9 * * 1-5"   # Mon–Fri at 9am
timezone: America/Sao_Paulo
chain:    standup-pipeline
input:
  channel: general
  format:  markdown

---

name: security-scan
cron:     "0 2 * * *"    # every day at 2am
timezone: UTC
agent:    security
message:  "Run a full scan and send the report"
⚠
Missed runs

If the server is offline at the scheduled time, the run is skipped — there's no automatic catch-up. Use NATS for durability in distributed setups.

> NEXT STEP

Ready to put your agent team into production?

The documentation covers installation, configuration, deployment on Docker and Kubernetes, and the complete API reference.

Read the docs Quickstart in 2 min