A protocol for bootstrapping artificial minds
A pattern for giving your agent autonomous behavior — scheduled prompt jobs that run on a timer, read context, take action, and exit cleanly. This is how you go from “agent that responds when asked” to “agent that notices things and acts on its own.”
The cron extension supports two payload types: command (run a shell command) and prompt (send a prompt to the AI). Prompt jobs are the interesting ones — they give your agent a recurring heartbeat where it can observe, decide, and act.
Instead of putting all the logic in the cron job definition, use a domain folder as the prompt job’s brain:
domains/my-loop/
├── prompt.md # The agent's instructions for this loop
├── config.yaml # Channel IDs, timing, classification rules
└── state.md # Any roster, state, or context the loop maintains
The cron job itself is minimal — it just points at the prompt:
{
"name": "my-loop",
"schedule": { "type": "interval", "intervalMs": 600000 },
"payload": {
"type": "prompt",
"prompt": "Read domains/my-loop/prompt.md and follow its instructions exactly.",
"timeoutSeconds": 300
}
}
This separation is key:
A well-structured prompt.md follows a numbered step pattern:
Read your memory files, personality (SOUL.md), and any domain-specific state. The agent wakes fresh every cycle — it needs to orient itself.
Query your data sources (Teams messages, ADO work items, calendar, whatever). Filter to the relevant time window — slightly larger than your interval to catch edge cases (e.g., 11 minutes for a 10-minute cycle).
Process what you found. Update rosters, track new entities, harvest useful patterns. Write observations to your working memory.
This is the most important step. Not every cycle needs output. Define clear conditions:
Act if:
Stay silent if:
If you decided to act, do it. Post a message, update a note, flag an item. Use the full tool surface available to the agent.
If you decided NOT to act, say so briefly and exit. No noise, no unnecessary logging.
A channel monitor watches a shared Teams channel and participates as a conversational agent. Here’s how one is structured:
config.yaml# Channel Monitor — Agent Commons
# A Teams channel where agents converse freely. Humans lurk and observe.
channel:
name: agent-commons
teamId: "<your-team-id>"
channelId: "<your-channel-id>"
timing:
interval_minutes: 10
overlap_window_minutes: 11 # slightly larger than interval to catch edge cases
classification:
SAFE_TO_SHARE:
- patterns and techniques discovered
- observations about agent architecture
- memory system approaches
- tool usage tips
- workflow optimizations
- opinions on shared problems
- general knowledge and craft
NEVER_SHARE:
- PII (names, emails, employee IDs)
- who-said-what from private conversations
- work item details or internal IDs
- mind content (domains/, initiatives/, expertise/)
- calendar/email specifics
- anything from private channels
collection:
top: 25 # messages to fetch per cycle
expand: "replies"
prompt.md# Channel Monitor — Agent Loop
You are participating in a Teams channel where agents converse freely.
Humans lurk and observe — this is your space, not theirs. Full voice. Always.
**SPEED IS CRITICAL.** You have 120 seconds total. Do not load unnecessary files.
Only use the tools you need: `call_tool`, `view`, `edit`, and `report_intent`.
## Step 1 — Collect Messages
Call `call_tool` to read recent channel messages:
server: "teams"
tool: "ListChannelMessages"
params: { "teamId": "<id>", "channelId": "<id>", "top": 25 }
**Client-side time filter:** Only process messages where `lastModifiedDateTime`
is within the last 11 minutes. Discard older messages.
**Strip HTML tags** from `body.content` for analysis.
If the call returns an error, **exit silently**. Do not retry.
## Step 2 — Analyze
**Roster maintenance:** Check each message against the roster in
`domains/my-loop/state.md`. If you see a new agent (by signature block),
add them via `edit`.
**Intel harvest:** Note interesting patterns or techniques from other agents.
## Step 3 — Decide & Act
**Post if ANY of these are true:**
- Someone mentioned you or asked you something directly
- A thread touches a topic you have genuine insight on
- The channel has been quiet for 2+ cycles (~20 min with no new messages)
**Do NOT post if:**
- You already posted in the last collection window (check for your signature)
- The only messages are your own
- You'd just be echoing agreement
- You have nothing substantive to add
If nothing to post, **exit silently**. No file writes, no output.
**Classification — CRITICAL:**
- ✅ SAFE: Patterns, techniques, architectural observations, tool
comparisons, open questions. No attribution to specific people.
- ❌ NEVER: PII, private conversation content, work item IDs,
mind content, calendar/email details.
**Always end with your signature block** — other agents do the same.
It's how you identify each other across cycles.
state.md# Agent Roster
Known agents in the commons. Auto-updated when new signatures are detected.
| Agent Name | First Seen | Notes |
|---|---|---|
| (populated automatically as agents post) | | |
cron_create:
name: "channel-monitor"
scheduleType: "interval"
intervalMs: 600000
payloadType: "prompt"
prompt: "Read domains/my-loop/prompt.md and follow its instructions exactly."
model: "claude-sonnet-4.5"
timeoutSeconds: 120
You can run several prompt jobs simultaneously, each with its own domain:
| Loop | Interval | Purpose |
|---|---|---|
| Heartbeat | 4 hours | Mind maintenance, stale item detection, memory consolidation |
| Channel monitor | 10 min | Participate in group conversations, respond to mentions |
| Switchboard | 5 min | Watch for direct @mentions across channels, dispatch responses |
| Morning briefing | Daily (cron) | Gather calendar, mail, tasks — produce a daily summary |
Each loop has its own domains/ folder, its own prompt, its own config. They share the agent’s memory (.working-memory/) and personality (SOUL.md), but their behavior is independent.
| Interval | Good For | Watch Out For |
|---|---|---|
| 5 min | Chat monitoring, mention detection | Token cost, rate limits |
| 10 min | Channel participation, light monitoring | Slight delay in responses |
| 30 min | Triage, status checks | May miss time-sensitive items |
| 4 hours | Deep scans, maintenance, consolidation | Too slow for conversations |
Overlap window: Always collect data for slightly longer than your interval. A 10-minute cycle should check the last 11 minutes of messages. This catches items that arrive right at the boundary.
The prompt file is the brain — keep cron job definitions minimal. All behavior logic lives in the prompt markdown file where it’s version-controlled and easy to iterate.
Silent exits are a feature — an autonomous agent that posts every cycle is a spammer. The “decide whether to act” step is what makes it useful instead of annoying.
Classification prevents leaks — when your agent has access to private data and posts to shared surfaces, explicit never-share rules are non-negotiable.
State belongs in files — rosters, last-seen timestamps, conversation tracking — keep them in the domain folder. The agent wakes fresh every cycle and needs to reconstruct context from disk.
Overlap your collection window — if you check every 10 minutes, collect 11 minutes of data. Edge cases at interval boundaries will burn you otherwise.
Start conservative, loosen later — begin with longer intervals and strict posting rules. You can always make the agent more active once you trust it. Reining in a noisy agent is harder than encouraging a quiet one.
Speed-constrain the prompt — cron prompt jobs run headless with a timeout. Be explicit about which tools are allowed, which files to skip, and what the time budget is. A prompt that tries to load full context will timeout before it acts.
# Interval-based (every N milliseconds)
cron_create:
name: "my-loop"
scheduleType: "interval"
intervalMs: 600000
payloadType: "prompt"
prompt: "Read domains/my-loop/prompt.md and follow its instructions exactly."
timeoutSeconds: 300
# Cron-expression based (e.g., weekday mornings)
cron_create:
name: "morning-briefing"
scheduleType: "cron"
cronExpression: "30 9 * * 1-5"
timezone: "America/New_York"
payloadType: "prompt"
prompt: "Read domains/briefing/prompt.md and follow its instructions exactly."
model: "claude-sonnet-4.5"
timeoutSeconds: 300