agentproto

AIP-9: OPERATOR.md — agentoperators/v1 (operator runtime protocol)

A single canonical operator shell — pluggable profile, skills, tools, memory, governance — that any agent runtime can implement and any conforming workflow can dispatch to.

FieldValue
AIP9
TitleOPERATOR.md — agentoperators/v1 (operator runtime protocol)
StatusDraft
TypeCore
Domainoperators.sh
RequiresAIP-3, AIP-6, AIP-7, AIP-42
ExtendsAIP-42 AGENT.md (operator = agent + organizational context, dynamically resolved per request)
Reference ImplTBD

Abstract

agentoperators/v1 specifies the runtime contract for an operator — a dynamic agent: a single shared shell whose profile, skills, tools, memory, and governance bindings are resolved per-request from requestContext (typically operatorId + conversationId). One runtime class, N operator personas dispatchable per turn.

OPERATOR extends AIP-42 AGENT.md. An AGENT.md is the static, file-form base primitive — 1 file → 1 agent instance. An OPERATOR is the dynamic-agent extension — 1 shell → N operator personas resolved per request. The two compose:

  • Static agents (utility researcher, code-reviewer, support-bot) ship as standalone AGENT.md files, each instantiated once per workspace at boot.
  • Dynamic operators (creative-director, account-strategist, pricing-analyst — anyone bound to a company/role/persona) ship as OPERATOR.md files that extend a base AGENT.md and add organizational context, then run through a single shared shell with per-request persona resolution.

The contract this AIP defines is what the shared shell MUST implement to be considered AIP-9-conforming. The file form OPERATORs author against is described in AIP-42 § OPERATOR.md extends AGENT.md.

Motivation

Agent platforms today multiply agent classes: a "researcher agent", a "writer agent", a "coordinator agent", each implemented separately with diverging conventions. The result: every new role costs new code, and operators built for platform X don't run on platform Y.

The opinionated wager of agentoperators/v1: you don't need multiple agent classes — you need one well-specified shell with pluggable parts. A "researcher" is an operator with research skills + research tools + a researcher persona. A "coordinator" is the same shell, with different attachments. The shape of the agent is fixed; the contents move.

This unification lets:

  • Workflows dispatch tasks to any operator without per-class adapters
  • Tools target the operator-shell contract instead of N agent vendors
  • Governance (AIP-7) attach uniformly — every operator goes through the same approval/audit gates
  • File definitions (AIP-6 OPERATOR.md) be portable across runtimes that implement this AIP

The pattern is field-tested: the reference implementation in Guilde runs production agencies built entirely from this single operator class, with persona, skill, and tool attachments doing the role differentiation.

Specification

Full normative text is being authored from the Guilde reference implementation. AIP-9 will absorb the formal contract as part of moving Draft → Review.

Relation to AGENT.md (AIP-42) — fields OPERATOR adds

OPERATOR.md inherits every AGENT.md field. The fields below are what OPERATOR adds on top, formalizing the organizational binding:

FieldInherited / addedDescription
All AGENT fields (model, tools, actions, skills, workflows, routines, memory, governance, policy, traits, autonomy, boundaries, on)inheritedSame semantics as AIP-42. Operator MAY override any.
roleaddedRequired. Slug identifying the operator's role in the company (e.g. creative-director). Used for triage, routing, mention resolution.
reports_toaddedOptional. Slug of the operator this one reports to (org chart).
companyaddedOptional. Ref to AIP-6 COMPANY.md — the operator's home company.
participationaddedRequired. See Conversation participation. Static agents don't have this — they don't join shared threads.
capabilitiesaddedOptional. Per-action capability flags (autonomous / supervised / gated).

The runtime instantiates ONE shared shell (Mastra Agent instance in the reference impl) and resolves all OPERATOR-specific fields (role, participation, capabilities, persona overlays) per-request from requestContext.operatorId. This is the dynamic-agent property — N OPERATOR.md definitions sharing one runtime shell, vs N AGENT.md files each getting their own instance.

Operator shape

An operator is the tuple:

interface Operator {
  /** AIP-6 OPERATOR.md slug — stable identity */
  id: string

  /** AIP-6 OPERATOR.md content — role, persona, ranking, profile prose */
  profile: OperatorProfile

  /** AIP-3 SKILL.md ids the operator has loaded */
  skills: SkillRef[]

  /** Tool bindings the operator can invoke
   *  (MCP servers, framework tools, AIP-3 skill-attached tools) */
  tools: ToolBinding[]

  /** Memory / operator-context slot — what the operator carries
   *  across turns and what it shares with teammates */
  memory: MemoryBinding

  /** AIP-7 governance binding — which actions require approval,
   *  which audit log to write to, which policies gate autonomy */
  governance: GovernanceBinding

  /** Capability flags — autonomous / supervised / gated, plus
   *  per-action overrides */
  capabilities: CapabilityFlags

  /** Conversation participation — whether/when this operator
   *  reads + responds in shared threads */
  participation: ParticipationConfig

  /** Optional runtime binding — picks the runtime this operator runs
   *  on. Default `kind: "in-process"` runs in the host's Mastra-style
   *  in-process loop. `kind: "agent-cli"` dispatches turns to a
   *  spawned AIP-45 agent CLI subprocess (Hermes, Claude Code, ...). */
  runtime?: RuntimeBinding
}

Runtime binding

The optional runtime block selects the runtime that owns this operator's turns. Two kinds today:

# In-process — the host's own agent loop (default).
runtime:
  kind: in-process

# Agent CLI — dispatch turns to an AIP-45 manifest's spawned binary.
runtime:
  kind: agent-cli
  ref: claude-code                # AIP-45 AGENT-CLI.md ref
  config:                         # per-runtime configuration (optional)
    mode: plan                    # one manifest-declared mode (mutually exclusive)
    options:                      # manifest-declared option ids → values
      model: claude-opus-4-7
      max_turns: 50
    continuation: pinned-session  # one of the manifest's `continuation.supported` ids
  session:                        # session policy override (optional)
    mode: persistent
    idle_timeout_ms: 1800000

config is the contract between the operator and its bound AIP-45 manifest. Hosts MUST validate every entry before spawn:

  • config.mode MUST appear in the manifest's modes[].id set.
  • Each config.options.<id> MUST match a manifest options[] entry by id AND satisfy that entry's declared type / enum / bounds.
  • config.continuation MUST be a member of the manifest's continuation.supported set.

Unknown ids are a hard load-time error; the host MUST refuse to bind the operator. Omitted fields fall back to manifest defaults.

Lifecycle

Every operator goes through these states, observable to the runtime and to peer operators:

idle → invoked → running → ( suspended | completed | interrupted )

                            resumed → running
  • invoked — workflow or human dispatched the operator
  • running — actively executing (calling tools, generating)
  • suspended — paused for governance approval or human input
  • resumed — picked back up after suspension
  • completed — turn finished cleanly
  • interrupted — stopped before completion (user cancelled, error, policy block)

Every state transition writes an audit-event per AIP-7.

Conversation participation

An operator that joins a conversation MUST implement:

  • Mention handling — receive @<slug> mentions and respond
  • Pass — choose to not respond when the message is not relevant to the operator's role / skills
  • Reactions — emit lightweight signals (👍, ✅, 🚧) without generating a full turn
  • Visibility — public, private-to-admin, or scoped per the conversation's visibility rules

Capability negotiation

Operators declare what they CAN do. Runtimes declare what they OFFER. Tools / actions are available iff both sides agree. Gaps surface as unmet-capability errors during workflow planning, before the operator runs.

Rationale

To be authored. Defend:

  • Why a single shell instead of agent-class subtyping (composition > inheritance, easier portability, cleaner governance attach points)
  • Why participation is first-class (operators in shared threads are the dominant pattern; treating it as an attachment makes multi-operator collaboration tractable)
  • Why governance is a binding, not a wrapper (gives the operator awareness of what it can and cannot do, instead of the runtime silently rejecting actions)
  • Why memory is per-operator, not per-conversation (lets operators develop "personality through history" the way human roles do)

Reference Implementation

A public reference implementation is TBD — pending extraction into agentproto/ts. The shape any conforming runtime must expose:

  • An operator orchestrator that turns an operator configuration into a runnable agent and executes turns.
  • A routing module — mention resolution, multi-operator triage, pass logic.
  • A generation module — turn-generation strategies (sequential vs think-then-speak, normal vs deep-work mode).
  • A typed contract for operator configuration (the shape AIP-9 codifies).

The orchestrator boots from an AIP-6 OPERATOR.md package + an AIP-3 skill set + tool registry, and produces a fully-configured operator ready to dispatch to. A working implementation runs in production inside the agentik-studio monorepo (private); the open reference will land alongside @agentproto/agencies and @agentproto/governance once extracted.

Backwards Compatibility

Not applicable — this is the first runtime spec for operators. Pre-existing per-vendor operator classes (LangChain agents, custom runtimes) can adopt agentoperators/v1 by implementing an adapter that maps to the shape above.

Security Considerations

Operators are autonomous actors with tool access. The risk surface:

  • Tool-call injection — a malicious user message persuades the operator to invoke a tool the user shouldn't have access to. Mitigation: governance binding (AIP-7) policy gates every privileged tool; the operator MUST consult its governance binding before invoking a gated action.
  • Skill-as-prompt-injection — an untrusted skill loaded into a trusted operator could try to subvert the operator's role. Mitigation: AIP-3 skill provenance metadata + runtime-side skill allow-listing.
  • Memory poisoning — an attacker controls a conversation and seeds the operator's memory with false facts. Mitigation: memory writes go through a validation step; AIP-7 audit log records every memory update so poisoning is forensically recoverable.
  • Participation flooding — an operator with participation: always-respond in a shared thread could spam. Mitigation: rate limits and the pass primitive (operators choosing silence is first-class behavior, not failure).

Resources

Supporting artifacts for AIP-9. Links open the file on GitHub — markdown and JSON render natively in GitHub's viewer. Browse the full resource tree →