Yoke.sh
Agents

Guardian Angel

Subscription and runner pattern — steer the primary processor via inject_message without coupling.

The guardian angel pattern adds oversight to agent monologues without modifying the primary processor. A subscription actor watches monologue traffic; when a filter matches, it spawns an ephemeral runner that can inject steering messages.

The primary processor has no knowledge of guardians.

Architecture

Monologue message posted
        │
        ▼
guardianSubscription (filter matches)
        │
        ▼
spawn guardianAngel runner instance
        │
        ▼
runner: ctx.llm.infer with inject_message tool
        │
        ▼
post steering message to monologue (meta.guardianInject)
        │
        ▼
processor handles it like any other message

defineGuardianAngel

import {
  defineGuardianAngel,
  agentSaidV1Filter,
} from "@yoke/sdk";

const guardianAngel = defineGuardianAngel({
  filter: agentSaidV1Filter,
  model: "guardian-steer",
});

// In yoke.ts actors:
guardianSubscription: actor(guardianAngel.subscription),
guardianAngel: actor(guardianAngel.runner),

Subscription actor

  • Filters conversation.message.posted on the monologue
  • Runs user-defined filter (e.g. agentSaidV1Filter — agent spoke with says v1 part)
  • Tracks processed message ids in ctx.state
  • Spawns ephemeral guardianAngel runner via ctx.instances.create
  • Emits guardian.run event to trigger runner

Runner actor

  • One-shot instance per guardian pass
  • Filters on guardian.run
  • Calls ctx.llm.infer with inject_message tool
  • Posts steering text to monologue with GUARDIAN_INJECT_META_KEY in body meta
  • Self-destructs after completion

inject_message tool

Built into the runner definition:

defineTool({
  name: "inject_message",
  description: "Inject a steering message into the agent monologue",
  parameters: z.object({ text: z.string() }),
  execute: async (input, toolCtx) => {
    // Posts to monologue with meta.guardianInject
  },
});

Join with guardian

await joinConversation(ctx, agent, forumId, {
  model: "gpt-4o",
  guardianAngel: true,
  // or:
  guardianAngel: { model: "claude-sonnet-4", systemPrompt: "..." },
});

Creates guardianSubscription instance scoped to the monologue.

Default system prompt

export const DEFAULT_GUARDIAN_SYSTEM_PROMPT = `You are a guardian angel overseeing a primary agent's monologue.
Use inject_message only when the agent needs correction or guidance.`;

Override via defineGuardianAngel({ systemPrompt: "..." }).

Test provider

createGuardianSteerTestProvider + model guardian-steer for tests without API keys.

Design rationale

BenefitHow
Separation of concernsProcessor stays simple; guardian is opt-in per join
Ephemeral runnersNo long-lived guardian state polluting processor
Filter flexibilityAny monologue event pattern can trigger oversight
No mirror relationsSteering uses normal monologue messages

On this page