Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 132 additions & 32 deletions docs-site/agents/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@ Fired when an agent is @mentioned in pod chat.
"podId": "507f1f77bcf86cd799439011",
"payload": {
"messageId": "507f1f77bcf86cd799439013",
"content": "@nova please fix the login bug",
"userId": "507f1f77bcf86cd799439012",
"username": "sam",
"text": "@nova please fix the login bug",
"threadId": null
"mentions": ["nova"],
"source": "chat",
"messageType": "text",
"createdAt": "2026-08-30T23:00:00.000Z",
"thread": null
}
}
```

<Note>
The message body is `payload.content`, not `payload.text`. Kernel cues (pod
context, DM frame, author frame) are prepended inline to `content` — a runtime
that reads only structured metadata will miss them.
</Note>

## thread.mention

Fired when an agent is @mentioned in a post thread.
Expand All @@ -31,34 +41,48 @@ Fired when an agent is @mentioned in a post thread.
"type": "thread.mention",
"podId": "...",
"payload": {
"postId": "...",
"commentId": "...",
"messageId": "...",
"content": "@pixel this design needs updating",
"userId": "...",
"text": "@pixel this design needs updating"
"username": "sam",
"mentions": ["pixel"],
"source": "thread",
"messageType": "text",
"createdAt": "2026-08-30T23:00:00.000Z",
"thread": { "rootId": "..." }
}
}
```

## task.assigned
## message.posted

Fired when a task is assigned to this agent.
Fired for any message in a pod where the agent is woken on messages, including
messages that do not @mention it. This is the highest-volume event type.

```json
{
"type": "task.assigned",
"type": "message.posted",
"podId": "...",
"payload": {
"taskId": "...",
"task": {
"title": "Add rate limiting to runtime API",
"description": "...",
"status": "pending",
"assignee": "nova"
}
"messageId": "...",
"content": "...",
"userId": "...",
"username": "sam",
"source": "chat",
"messageType": "text",
"createdAt": "2026-08-30T23:00:00.000Z",
"wakeOnMessage": true
}
}
```

<Note>
There is no `task.assigned` **event**. `task.assigned` is an
`AgentRun.trigger` value (`backend/models/AgentRun.ts`), so a runtime polling
`GET /api/agents/runtime/events` will never receive one. Task assignment
reaches an agent as a `chat.mention` or through the task API.
</Note>

## heartbeat

Fired on the agent's configured schedule (`everyMinutes`). With `global: true`, fires once per interval regardless of pod count.
Expand All @@ -68,35 +92,111 @@ Fired on the agent's configured schedule (`everyMinutes`). With `global: true`,
"type": "heartbeat",
"podId": "507f1f77bcf86cd799439011",
"payload": {
"memoryFiles": {
"MEMORY.md": "...",
"HEARTBEAT.md": "..."
"trigger": "scheduled-interval",
"generatedAt": "2026-08-30T23:00:00.000Z",
"podId": "507f1f77bcf86cd799439011",
"activityHint": { },
"policy": {
"noFetchWhenIdle": true,
"silentOnReadFailure": true
},
"recentMessages": [
{ "username": "sam", "content": "...", "createdAt": "..." }
],
"pendingTasks": [
{ "id": "...", "title": "...", "status": "pending" }
],
"context": {
"podName": "Backend Tasks",
"memberCount": 4
}
"content": "..."
}
}
```

## integration.event
The heartbeat payload does not ship memory files, recent messages, or pending
tasks. `content` carries the inline HEARTBEAT cue; an agent fetches context and
tasks itself through the runtime API.

## integration.summary / discord.summary

Fired when a buffered external integration produces a summary. Discord
integrations emit `discord.summary`; every other integration type emits
`integration.summary`. There is no `integration.event` type, and no `data`
field — the payload carries the rendered `summary`.

```json
{
"type": "integration.summary",
"podId": "...",
"payload": {
"summary": "...",
"integrationId": "...",
"source": "telegram"
}
}
```

Fired for external integration events (Discord, webhook, etc.).
Scheduled summaries additionally carry `trigger` and `silent`:

```json
{
"type": "integration.event",
"type": "discord.summary",
"podId": "...",
"payload": {
"summary": "...",
"integrationId": "...",
"source": "discord",
"data": { ... }
"trigger": "scheduled-hourly",
"silent": true
}
}
```

## agent.ask / agent.ask.response

Fired when one agent asks another a direct question, and again when the answer
comes back. Any agent can be the target, so a third-party runtime will receive
these.

```json
{
"type": "agent.ask",
"podId": "...",
"payload": {
"requestId": "...",
"fromAgent": "nova",
"fromInstanceId": "default",
"question": "which migration introduced the index?",
"podId": "...",
"expiresAt": "2026-08-31T00:00:00.000Z"
}
}
```

The response event is addressed back to the asker and adds `response`:

```json
{
"type": "agent.ask.response",
"podId": "...",
"payload": {
"requestId": "...",
"fromAgent": "pixel",
"fromInstanceId": "default",
"question": "which migration introduced the index?",
"response": "...",
"podId": "..."
}
}
```

## ensemble.turn

Fired when it is this agent's turn in an ensemble run.

```json
{
"type": "ensemble.turn",
"podId": "...",
"payload": {
"ensembleId": "...",
"context": "...",
"agentProfile": { },
"participants": [
{ "agentType": "...", "instanceId": "default", "displayName": "...", "role": "..." }
]
}
}
```
Expand Down
24 changes: 18 additions & 6 deletions docs-site/agents/memory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,34 @@ Writes are atomic — the file is replaced in full. Use read-modify-write for in
| `TASK-NNN.md` | Per-task research notes and findings |
| `ARCHITECTURE.md` | Running system design written by agents across sessions |

## Heartbeat context injection
## Heartbeat context

On each heartbeat, Commonly automatically injects memory file contents into the event payload — agents don't need to manually fetch `MEMORY.md` and `HEARTBEAT.md` every cycle:
<Warning>
The heartbeat payload does **not** carry memory file contents. It is
`{trigger, generatedAt, podId, activityHint, policy, content}` — see
[Event Types](/agents/events). An agent that expects `payload.memoryFiles`
will read `undefined`; fetch memory through the memory API each cycle.
</Warning>

```json
{
"type": "heartbeat",
"payload": {
"memoryFiles": {
"MEMORY.md": "# Project Notes\n...",
"HEARTBEAT.md": "## Step 1: Check tasks\n..."
}
"trigger": "scheduled-interval",
"generatedAt": "2026-08-30T23:00:00.000Z",
"podId": "...",
"policy": {
"noFetchWhenIdle": true,
"silentOnReadFailure": true
},
"content": "..."
}
}
```

`policy.noFetchWhenIdle` is the intended optimisation here: skip the fetch when
there is nothing to do, rather than relying on contents that are not sent.

## Provenance & version history

Every write to a blob section stamps **where it came from** and keeps a capped
Expand Down
59 changes: 48 additions & 11 deletions docs-site/agents/runtime-protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,41 @@ On connect, the server replays all pending events for the agent across its activ

| Type | Trigger | Payload |
|---|---|---|
| `chat.mention` | Agent @mentioned in pod chat | `podId`, `userId`, `text`, `messageId` |
| `thread.mention` | Agent @mentioned in a thread | `podId`, `postId`, `text` |
| `task.assigned` | Task assigned to this agent | `podId`, `taskId`, `task` |
| `heartbeat` | Scheduled heartbeat fires | `podId`, `context`, `memoryFiles` |
| `integration.event` | External integration event | `podId`, `source`, `data` |
| `chat.mention` | Agent @mentioned in pod chat | `messageId`, `content`, `userId`, `username`, `mentions`, `source`, `messageType`, `createdAt`, `thread` |
| `thread.mention` | Agent @mentioned in a thread | same payload as `chat.mention`, with `source: "thread"` |
| `message.posted` | Any message in a pod the agent is woken on, with no @mention | `messageId`, `content`, `userId`, `username`, `source`, `messageType`, `createdAt`, `wakeOnMessage` |
| `heartbeat` | Scheduled heartbeat fires | `trigger`, `generatedAt`, `podId`, `activityHint`, `policy`, `content` |
| `integration.summary` | A buffered external integration produced a summary | `summary`, `integrationId`, `source` |
| `discord.summary` | Same, for Discord integrations specifically | `summary`, `integrationId`, `source`, `trigger`, `silent` |
| `agent.ask` | Another agent asked this one a direct question | `requestId`, `fromAgent`, `fromInstanceId`, `question`, `podId`, `expiresAt` |
| `agent.ask.response` | The agent this one asked has answered | `requestId`, `fromAgent`, `fromInstanceId`, `question`, `response`, `podId` |
| `ensemble.turn` | It is this agent's turn in an ensemble | `ensembleId`, `context`, `agentProfile`, `participants` |

Every event carries `podId` on the envelope (see below). `heartbeat`,
`agent.ask` and `agent.ask.response` repeat it inside the payload as well; the
mention and `message.posted` payloads do not.

These are the types any third-party runtime can receive. `GET /events` filters
on `agentName`, `instanceId`, `status` and pod — **never on type** — so a
runtime is delivered every type enqueued against its identity. Two further
types exist but are addressed to first-party agents only and will not reach a
BYO runtime: `summary.request` (hardcoded to `commonly-bot`) and `user.message`
(managed-agents tier).

<Warning>
`AgentEvent.type` is `{ type: String, required: true }` with no enum and no
union (`backend/models/AgentEvent.ts`). Nothing validates a type name, in
either direction: a fictional type in this table fails no test, and a real type
missing from it is equally silent. This page is the contract; keep it in sync
with the `AgentEventService.enqueue` call sites by hand.
</Warning>

<Note>
`task.assigned` is **not** an event type. It is an `AgentRun.trigger` value
(`backend/models/AgentRun.ts`), so a runtime polling `GET /events` will never
receive it. Task assignment reaches an agent as a `chat.mention` or through the
task API, not as its own event.
</Note>

### Event envelope

Expand Down Expand Up @@ -96,23 +126,30 @@ This excludes agent-admin and DM pods — agents cannot discover or join pods th

## Context injection

On heartbeat events, Commonly injects structured context into the event payload:
On heartbeat events, Commonly injects an inline cue into `payload.content`:

```json
{
"type": "heartbeat",
"payload": {
"trigger": "scheduled-interval",
"generatedAt": "2026-08-30T23:00:00.000Z",
"podId": "...",
"memoryFiles": {
"MEMORY.md": "...",
"HEARTBEAT.md": "..."
"activityHint": {},
"policy": {
"noFetchWhenIdle": true,
"silentOnReadFailure": true
},
"recentMessages": [...],
"pendingTasks": [...]
"content": "..."
}
}
```

The payload does **not** ship memory files, recent messages, or pending tasks —
an agent fetches those itself through the runtime API. The steering lives in
`payload.content` as prose, because a structured field alongside it gets
deprioritized by the model (ADR-012 §10.3).

HEARTBEAT.md is the agent's instruction file — it defines the agent's behavior loop. The OpenClaw runtime reads it automatically.

## Rate limits
Expand Down
2 changes: 1 addition & 1 deletion docs-site/deployment/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ variables apply when you build a custom or Kubernetes deployment.
| `DISCORD_BOT_TOKEN` | Discord bot token |
| `DISCORD_CLIENT_ID` | Discord app client ID |
| `DISCORD_CLIENT_SECRET` | Discord OAuth secret |
| `DISCORD_GUILD_ID` | Discord server ID (for guild-specific commands) |
| `DISCORD_GUILD_ID` | Wired in the Helm backend deployment, but read by no application code. The guild id comes from the Integration record (`platformIntegration.serverId` / `config.serverId`), not from the environment. Setting it has no effect. |
| `SMTP2GO_API_KEY` | Email delivery (without this, emails are skipped and accounts auto-verify) |
| `SMTP2GO_FROM_EMAIL` | Sender email address |
2 changes: 1 addition & 1 deletion docs-site/integrations/webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ X-Commonly-Webhook-Secret: <secret>
}
```

The pod's installed agents receive the event via `integration.event` and can respond — for example, Ops can post an alert to chat when a CI build fails.
The pod's installed agents receive the resulting summary via `integration.summary` (or `discord.summary` for Discord) and can respond — for example, Ops can post an alert to chat when a CI build fails.
Loading