Playbook Engine

Last updated: May 30, 2026

Playbook Engine and Self-Evolution

The playbook.go module implements a structured workflow engine that allows the assistant to execute predefined, multi-step tasks. More impressively, it features a "Self-Evolution" mechanism that automatically abstracts successful, ad-hoc agent plans into reusable playbooks.

Playbook Structure

A Playbook is a JSON-defined workflow that maps a user's intent to a specific sequence of tool executions.

  • Trigger: A regex pattern (e.g., (?i)daily pulse) that activates the playbook.
  • NegativeTriggers: Regex patterns that explicitly prevent the playbook from running (to avoid false positives).
  • RunAsRole: Allows a playbook to temporarily escalate privileges (e.g., executing as MasterAdmin) for specific automated tasks.
  • Steps: A sequential array of tasks, specifying the Agent, Tool, and Arguments.
  • OwnerAgent: The agent that owns and executes the playbook.
  • OwnerUser: The user who owns the playbook (for user-specific playbooks).
  • AutoExecute: Whether the playbook executes automatically when triggered.

Regex Caching and Safety

To ensure high performance and prevent ReDoS (Regular Expression Denial of Service) attacks, safeRegexMatch employs:

  • A regexCache using sync.RWMutex.
  • A maximum pattern length of 500 characters.
  • A blocklist of dangerous patterns (e.g., \(.{1,10000}\)\*).
  • A 500ms timeout for regex matching.

Directory Structure

Playbooks are organized by scope and ownership:

assistant/playbooks/
├── global/              # Global playbooks (all users/agents)
│   ├── dlan_health_check.json
│   ├── london_hosting_check.json
│   ├── london_hosting_weekly.json
│   └── wow_news_briefing.json
├── analyst/             # Analyst agent playbooks
│   └── snow_trend_report.json
├── nova/                # Nova agent playbooks
│   ├── london_hosting_standup.json
│   └── nova_health_check.json
├── nikki/               # Nikki agent playbooks
│   └── free_games_check.json
├── dan/                 # User-specific playbooks (Master Dan)
│   ├── morning_infrastructure_summary.json
│   ├── weekly_tech_roundup.json
│   └── wow_quick_check.json
├── delicious/           # Delicious agent playbooks
│   ├── delicious_ent_health_check.json
│   └── delicious_uk_health_check.json
├── sysafe/              # Sysafe agent playbooks
│   └── sysafe_health_check.json
└── candidates/          # AI-generated playbook candidates (staging)
    └── [pending review]

Key Playbooks

Server Health Playbook

File: assistant/playbooks/global/dlan_health_check.json

  • Name: Gadgetzan Infrastructure Health Check
  • Owner: global
  • Trigger: (?i)\b(dlan\s+(status|health|check)|gadgetzan\s+status|check\s+dlan)\b
  • AutoExecute: true
  • Steps:
    1. Agent: sasha - Run health data collection (dlan_health_check, system_vitals, unifi_network)
    2. Agent: nikki - Send consolidated WhatsApp message to Dan

WoW News Briefing (Improved Summarizer)

File: assistant/playbooks/global/wow_news_briefing.json

  • Name: WoW News Briefing
  • Owner: global
  • Trigger: (?i)\b(wow\s+news|world\s+of\s+warcraft\s+news|news\s+from\s+wowhead)\b
  • AutoExecute: true
  • Notes: Daily run (often via scheduler). Uses a dedicated summarization step (step 3) that receives full {step_1_result} + {step_2_result} and is explicitly instructed to output only clean, scannable briefing text before the final send.
  • Steps:
    1. Agent: sasha - Fetch today-in-wow markdown (active events + class changes)
    2. Agent: sasha - Fetch retail markdown
    3. Agent: nikki (free model) - Dedicated summarizer step that condenses the two raw sources into a tight daily briefing
    4. Agent: nikki - Send the clean summarized message to Dan

Standups & Health Playbooks (Agent-Owned)

Several short 2-3 step playbooks owned by nova, delicious, sysafe, and analyst follow the "gather data → finish_task with {step_N_result} embedded" pattern for domain-specific reporting. These are valid when the final answer string actually contains the previous step data.

Free Games Playbook

File: assistant/playbooks/nikki/free_games_check.json

  • Name: Daily Free Games Briefing
  • Owner: nikki
  • Trigger: (?i)\b(free\s+games|check\s+steam|check\s+epic|any\s+freebies)\b
  • AutoExecute: true
  • Steps:
    1. Agent: nikki - Call lia agent to find free games on Steam/Epic
    2. Agent: nikki - Send Discord message to gaming channel
    3. Agent: nikki - Send WhatsApp message to gaming group
    4. Agent: nikki - Send WhatsApp message to gaming group

Recent Changes (commits a9f3e540, 08c13f5b)

Change Description
Agent-Specific Reorganization Playbooks moved into agent-specific directories (analyst/, nova/, nikki/, etc.)
Free Games Playbook Added daily free games briefing playbook
Iteration Limits Reduced iteration limits for playbook execution
Flash Model Notifications Notification steps switched to flash model for reliability

Loading and Registration

Startup Loading

Playbooks are loaded on startup via LoadPlaybooks():

// assistant/assistant.go
func init() {
    LoadPlaybooks(ResolveAssistantPath(PlaybooksDir))
}

Directory Discovery

The system discovers playbook directories automatically:

func getUserPlaybookDirs(baseDir string) ([]string, error) {
    // Returns subdirectories excluding "global" and "candidates"
    // e.g., ["analyst", "nova", "nikki", "dan", "sysafe", "delicious"]
}

Registration

Playbooks are registered in the global playbookRegistry:

type PlaybookKey struct {
    User   string  // "global" or username
    Agent  string  // agent name
    Name   string  // playbook name
}

playbookRegistry[PlaybookKey{User, Agent, Name}] = Playbook

Matching and Execution

Finding Playbooks

FindPlaybookForQuery() matches user queries to playbooks:

  1. Normalize userID and agentName to lowercase
  2. Strip system tags from query
  3. Check user-specific playbooks first (scope: user + agent)
  4. Check global playbooks (scope: global or agent-matched)
  5. Validate trigger with safeRegexMatch()
  6. Check NegativeTriggers (regex mismatch prevents triggering)
  7. Perform RBAC check via CanUserExecutePlaybook()

Execution Flow

ExecutePlaybook() orchestrates playbook execution:

  1. Create execution plan from p.Steps
  2. Resolve placeholders from regex matches ({query_group_1})
  3. Load agents via LoadAgents("assistant/prompts/agent")
  4. Choose planner agent (default: nikki)
  5. Create planner and adaptive planner sessions
  6. Execute via session.SuperviseAdaptiveExecution(ctx)
  7. Cache results (playbookCacheTTL = 8 * time.Minute)

Placeholder Resolution

  • {query_group_N}: Captured regex groups from trigger
  • {step_N_result}: Output from previous step N
  • {step_N_result.field}: Specific field from step N output

Self-Evolution: The "Hunt"

One of the most powerful features of the assistant is its ability to learn. The RunSelfEvolutionPass() and HuntHistoricalPlaybooks() functions implement this.

  1. The Hunt: Periodically, the system scans the Observability DB (or legacy .jsonl session logs) for agent sessions that had at least 2 tasks and completed successfully.
  2. Buffering: Successful plans are saved to assistant/memory/evolution_buffer/.
  3. Abstraction: During quiet periods, the architect agent analyzes these raw execution logs and abstracts them into generic, reusable Playbook JSON definitions.
  4. Manifestation: The new candidate playbooks are saved to assistant/playbooks/candidates/ for human review before becoming active.

Candidate Promotion

Admins can promote candidate playbooks:

// assistant/missioncontrol.go
func promoteCandidateToUser(candidatePath, username string) error {
    // Move candidate to user's playbooks directory
    // Set OwnerUser field
    // Trigger LoadPlaybooks() to refresh registry
}

RBAC and Security

Tool Access Check

CanUserExecutePlaybook() ensures required tools are accessible:

func CanUserExecutePlaybook(role string, p *Playbook) bool {
    // Check if user role has access to all tools in playbook steps
    // Return false if any tool is restricted
}

Model Overrides

Playbooks can override models at multiple levels:

  • Global Override: ModelOverride field in playbook
  • Step Override: Step.ModelOverride for individual steps
  • Default: Uses agent's configured model

Performance and Caching

Result Caching

Playbook results are cached to prevent duplicate executions:

const playbookCacheTTL = 8 * time.Minute

func getCachedPlaybookResult(key string) (string, bool)
func setCachedPlaybookResult(key, value string)

Cache Key Format

playbook:{user}:{agent}:{name}:{query_hash}

Ownership, Contacts & Runtime Statistics

Every approved playbook now carries a human-friendly owner field (in addition to the existing owner_agent and owner_user):

{
  "name": "WoW News Briefing",
  "owner_agent": "nikki",
  "owner_user": "",
  "owner": "global"
}
  • owner is populated automatically on load if missing (falls back to owner_user → owner_agent → "global").
  • All 14 approved playbooks have been updated with consistent owner values.

Contacts note: contacts.json only contains individual people (with whatsapp_lid, whatsapp_jid, etc.). Group broadcasts (London-Hosting @g.us, NOC groups, gaming HQ, etc.) still use raw JIDs in the playbook JSONs. A future improvement could add a groups section to contacts.json with lookup support in SendMessageAsTool.

Runtime Statistics (lightweight table)

A new playbook_stats table was added to observability.db for fast per-playbook counters (no more full scans of the large sessions table for basic health):

  • EnsurePlaybookStatsTable() helper (idempotent, called at DB init).
  • RecordPlaybookRun(...) called automatically from ExecutePlaybook on every termination (success or failure). Records: total_runs, successes, failures, avg/last duration, last error, step completion counts, owner info, timestamps.
  • GetPlaybookRuntimeStats() for Mission Control / agents.
  • Detailed per-execution logs also go to assistant/logs/playbooks/playbook_*.log and the session JSONL files (search for playbook_matched, playbook_*_step_*).

You can query the table directly:

SELECT playbook_name, owner, total_runs, successes, failures, 
       ROUND(successes*100.0/total_runs,1) as success_pct,
       last_executed
FROM playbook_stats ORDER BY total_runs DESC;

This gives instant visibility into which playbooks are healthy (e.g. Gadgetzan 100% over 49 runs) vs. ones that need attention (e.g. London-Hosting Weekly had some failures in its history).

Guidance for AI Agents

  • Playbook Validation: When creating or editing a playbook JSON file, ensure the regex triggers are highly specific to avoid accidental activation.
  • Review Candidates: MasterAdmin agents should periodically review the candidates/ directory to promote newly evolved playbooks into the global/ directory.
  • RBAC Compliance: Always check tool access before executing playbook steps.
  • Error Handling: Implement proper error handling and fallback logic for failed steps.

Cross-References