Skip to content

This is a reference doc. It covers all workspace/*.yaml schemas and environment variables for each plugin.


Environment variables ​

Agent Runtime Plugin ​

VariableRequiredDescription
LLM_GATEWAY_URLNoLiteLLM Proxy base URL for all in-process agent LLM calls (default: http://gateway:4000/v1)
OPENAI_API_KEYYes (for agents)Bearer token for the gateway — set to your gateway API key
DISABLE_AGENT_RUNTIMENoSet to any value to skip loading AgentRuntimePlugin
DISABLE_SKILL_BROKERNoSet to any value to skip loading SkillBrokerPlugin (use once all agents are migrated in-process)

Core / A2A Plugin ​

VariableRequiredDescription
AGENTS_YAMLNoPath to external A2A agent registry (default: /workspace/agents.yaml)
AVA_API_KEYIf using a remote A2A agentAPI key injected as the X-API-Key header for a remote A2A agent server.
AVA_APP_IDFor chain commentsGitHub App ID — chain responses post as @ava[bot]
AVA_APP_PRIVATE_KEYFor chain commentsGitHub App private key (PKCS#1 PEM)

GitHub Plugin ​

VariableRequiredDescription
GITHUB_TOKENYesPAT — enables the plugin; posts comment replies
GITHUB_WEBHOOK_SECRETRecommendedValidates X-Hub-Signature-256 on inbound payloads
GITHUB_WEBHOOK_PORTNoWebhook HTTP server port (default: 8082)
GITHUB_APP_IDFor bot commentsGitHub App ID — Quinn's responses post as protoquinn[bot]
GITHUB_APP_PRIVATE_KEYFor bot commentsGitHub App private key (PKCS#1 PEM)

Discord Plugin ​

VariableRequiredDescription
DISCORD_BOT_TOKENYesBot token — enables the plugin
DISCORD_GUILD_IDYesGuild ID for slash command registration
DISCORD_DIGEST_CHANNELNoFallback channel ID for cron pushes (overridden by discord.yaml)

Linear Plugin ​

VariableRequiredDescription
LINEAR_API_KEYFor outboundPersonal API key for GraphQL mutations (comment/create/update issues)
LINEAR_WEBHOOK_SECRETFor inboundHMAC-SHA256 signing secret from Linear webhook config
LINEAR_WEBHOOK_PORTNoDefaults to 8084

Google Plugin ​

VariableRequiredDescription
GOOGLE_CLIENT_IDYesOAuth2 client ID — enables the plugin
GOOGLE_CLIENT_SECRETYesOAuth2 client secret
GOOGLE_REFRESH_TOKENYesLong-lived refresh token

Quinn Vector Context Pipeline ​

VariableRequiredDescription
QDRANT_URLYes (for vector)http://qdrant:6333
QDRANT_VECTOR_SIZENoEmbedding dimensions (default: 768)
EMBED_MODELNoGateway embedding model (default: qwen3-embedding)
LLM_GATEWAY_URLNoGateway base URL (default: http://gateway:4000/v1)

Scheduler Plugin ​

VariableRequiredDescription
TZNoSystem timezone (e.g., America/New_York). Affects default cron timezone.

Gitignored workspace files ​

The following files contain deployment-specific config and are not committed to the repo. Each has a .example counterpart — copy it to bootstrap a new deployment:

FileCopy from
workspace/agents/<name>.yamlworkspace/agents/<name>.yaml.example — one file per in-process agent
workspace/agents.yamlworkspace/agents.yaml.example — external A2A agent registry
workspace/discord.yamlworkspace/discord.yaml.example
workspace/google.yamlworkspace/google.yaml.example
workspace/a2a.yamlworkspace/a2a.yaml.example — outbound A2A delivery targets for the scheduler
workspace/incidents.yamlworkspace/incidents.yaml.example

Behavior files under ceremonies/ are tracked and committed as-is.


workspace/agents/<name>.yaml ​

Per-agent definition for the in-process AgentRuntimePlugin. One file per agent under workspace/agents/. Files ending in .example are skipped. All files in this directory are gitignored.

yaml
name: quinn                         # unique agent key — used in skill routing
role: qa                            # orchestrator | qa | devops | content | research | general

# LLM model alias — resolved by the gateway (LiteLLM Proxy at LLM_GATEWAY_URL).
# `protolabs/reasoning` is the standard fleet default; concrete names like
# `claude-sonnet-4-6` / `claude-opus-4-7` also work.
model: protolabs/reasoning

systemPrompt: |
  You are Quinn, the QA Engineer for protoLabs AI.
  ...

# Workstacean bus tools this agent may call.
# Available: publish_event, get_projects, get_incidents, report_incident,
#            get_ceremonies, run_ceremony
tools:
  - publish_event
  - get_projects
  - report_incident

# Agents this agent may delegate to (orchestrator role only, DeepAgent pattern)
canDelegate:
  - researcher

# Max agentic turns per invocation (-1 = unlimited, default: 10)
maxTurns: 15

# Skills this agent handles — matched against agent.skill.request skillHint
skills:
  - name: bug_triage
    description: Triage a bug report — severity, root cause, next action
  - name: pr_review
    description: Review a pull request diff

role drives the agent profile and delegation rules:

  • orchestrator — DeepAgent delegation pattern; can delegate to canDelegate agents
  • qa, devops, content, research, general — ReAct subagent; no delegation. Use general for conversational agents like the in-process ava chat agent.

tools is a whitelist — the agent subprocess only sees the tools listed here, plus proto CLI built-ins (file, bash, search).

model is any alias the LiteLLM gateway recognises. See your gateway config for the full alias list.


workspace/fleet.yaml ​

Maps abstract roles to concrete agents — the one file a fork edits to re-skin the fleet without touching code. Loaded by lib/fleet/fleet-config.ts; the dispatch/review/remediation paths read these roles instead of hardcoding agent names. All keys default to the proto-labs fleet's values, so an unmodified deploy needs no fleet.yaml.

yaml
roles:
  helm: ava          # default target for untargeted A2A requests + the OpenAI-compat chat alias
  reviewer: quinn    # runs PR review
github:
  reviewerBotLogins: [protoquinn, "protoquinn[bot]"]   # reviewer's own GitHub identities (review-loop matching)

A fork also sets the GitHub App credentials via env: GITHUB_APP_ID + GITHUB_APP_PRIVATE_KEY (the agent's reviews/comments/issue-closes post as that App's bot).

workspace/agents.yaml ​

Source of truth for the external A2A agent registry. Used by SkillBrokerPlugin to dispatch skills to remote agents over JSON-RPC 2.0. Agents listed here run as separate services (Docker containers or remote hosts).

yaml
agents:
  # protoPen — security / pentest / RF-recon agent, running remotely.
  - name: protopen
    team: security
    url: http://steamdeck:7870/a2a
    apiKeyEnv: PROTOPEN_API_KEY  # env var holding the API key (not the key itself)
    skills:
      - security_triage
      - pentest
      - rf_recon

  - name: quinn
    team: dev
    url: http://quinn:7870/a2a
    skills:
      - qa_report
      - bug_triage
      - pr_review
      - security_triage

  - name: frank
    team: dev
    url: http://frank:7880/a2a
    skills:
      - infra_health
      - deploy
      - monitoring

chain is optional. When chain[skill] is set, the named agent is called with the first agent's response as context. One level deep only.

Note: workspace/agents/<name>.yaml (in-process) and workspace/agents.yaml (external A2A) coexist. In-process agents like the ava chat agent run inside workstacean via DeepAgentExecutor (LangGraph); external agents like Quinn and Jon are called via A2AExecutor. Both register into the same ExecutorRegistry, so skill dispatch is identical from the bus's perspective.


Project metadata (no workspace file) ​

There is no workspace/projects.yaml. The project registry is compiled from GitHub: every repo in the protoLabsAI org tagged with the protoagent-plugin topic (plus an explicit base set) is assembled by scripts/sync-project-registry.sh (in homelab-iac, cron every 15 min) into a static projects.json, served by the workstacean-projects nginx sidecar at /api/settings/global. workstacean's ProjectRegistry (src/plugins/project-registry.ts) polls that endpoint (URL from PROJECT_REGISTRY_URL) every 5 minutes and re-serves the list at GET /api/projects. Each entry exposes id, name, derived slug, filesystem path, derived github ({ owner, repo }), and defaultBranch.

Discord channel bindings are not part of project metadata — they live in workspace/channels.yaml via the ChannelRegistry, keyed by (projectSlug, kind) and resolved with ChannelRegistry.getProjectChannel(slug, kind).

To add a project, tag its repo with the protoagent-plugin GitHub topic; the 15-min sync cron compiles it into projects.json and workstacean picks it up on the next registry refresh (5-min interval, or immediately on restart).


workspace/discord.yaml ​

yaml
channels:
  digest: ""       # channel ID for cron-triggered posts and message.outbound.discord.push.*
  welcome: ""      # new member welcome messages (blank = disabled)
  modLog: ""       # reserved for future moderation logging

moderation:
  rateLimit:
    maxMessages: 5       # max messages per user per window
    windowSeconds: 10    # rolling window in seconds
  spamPatterns:          # regex strings, case-insensitive, matched messages silently deleted
    - "free\\s*nitro"
    - "discord\\.gift/"

commands:
  - name: mybot
    description: "Bot description shown in Discord"
    subcommands:
      - name: status
        description: "Subcommand description"
        content: "/status"              # text sent as payload.content
        skillHint: qa_report           # optional — routes to specific A2A skill
        options:
          - name: version
            description: "Option description"
            type: string               # string | integer | boolean
            required: false
            autocomplete: false        # true enables live Discord filtering; project options resolve via the in-process project registry

workspace/github.yaml ​

yaml
mentionHandle: "@protoquinn"  # case-insensitive handle to watch for

skillHints:
  issue_comment: bug_triage              # comment @mention on issue
  issues: bug_triage                     # new issue body @mention
  pull_request_review_comment: pr_review # review comment @mention
  pull_request: pr_review                # PR body @mention

workspace/google.yaml ​

yaml
drive:
  orgFolderId: ""             # root org Drive folder ID
  templateFolderId: ""        # per-project template folder ID (optional)

calendar:
  orgCalendarId: ""           # shared org calendar ID (from Google Calendar settings)
  pollIntervalMinutes: 60     # polling interval

gmail:
  watchLabels: []             # Gmail label names to monitor
  pollIntervalMinutes: 5
  routingRules:               # label → skillHint mappings
    - label: "bug-report"
      skillHint: bug_triage

workspace/crons/{id}.yaml ​

yaml
id: daily-digest              # unique, kebab-case — used as filename
type: cron                    # "cron" or "once" (auto-detected if omitted)
schedule: "0 14 * * *"        # cron expression (recurring) or ISO datetime (one-shot)
timezone: "America/New_York"  # IANA timezone (system default if omitted)
topic: "cron.daily-digest"    # bus topic to publish on fire
payload:
  content: "Generate the daily QA digest"
  sender: "cron"
  channel: "signal"           # reply channel or Discord channel ID
  skillHint: qa_report
enabled: true
lastFired: "2026-04-01T14:00:00.000Z"   # auto-updated by SchedulerPlugin

workspace/mcp-servers.d/{name}.yaml ​

One MCP server per file (ADR-0005): name, trust (builtin/trusted/community), transport (stdio→command/args/env, sse→url), optional grants, allowedTools/excludeTools, enabled. McpClientPlugin connects each enabled server and registers its tools as executors. Managed via the Console / POST /api/mcp-servers.

workspace/plugins/{name}.ts (retired) ​

The dynamic TS-plugin loader was removed in ADR-0005. First-party plugins live in lib/plugins/ (compiled in); runtime extension is out-of-process via A2A agents or MCP servers. See reference/plugins.md.

protoWorkstacean — a switchboard, not an agent.