# Proactive Agent Research: Making Kitt Autonomous

**Date**: 2026-02-23  
**Problem**: Kitt only acts when a human messages it or a heartbeat fires. Sub-agent completions don't trigger follow-up. There's no event loop.

---

## 1. What's Possible TODAY with OpenClaw

### Heartbeats (Periodic Polling)
- **Mechanism**: Gateway runs periodic agent turns in the main session (default every 30m)
- **Config**: `agents.defaults.heartbeat.every` — can be set as low as needed
- **HEARTBEAT.md**: Agent reads this checklist each heartbeat cycle — can include "check if background tasks finished"
- **Active hours**: Configurable window to avoid overnight spam
- **Delivery**: Can target specific channels (Discord, WhatsApp, etc.)
- **Key insight**: Heartbeat runs in the **main session** with full context, so the agent can see sub-agent completion summaries

### Cron Jobs (Precise Scheduling)
- **Two modes**:
  - **Main session** (`sessionTarget: "main"`): Enqueues a system event, optionally wakes heartbeat immediately
  - **Isolated** (`sessionTarget: "isolated"`): Dedicated agent turn in `cron:<jobId>`, fresh session each run
- **Wake modes**: `now` (immediate heartbeat) vs `next-heartbeat` (wait for scheduled)
- **Delivery**: `announce` (posts summary to channel), `webhook` (POST to URL), `none`
- **One-shot**: `--at` for precise future timestamps, `--delete-after-run`
- **Model overrides**: Can use different/cheaper models per job
- **Storage**: Persisted in `~/.openclaw/cron/jobs.json`

### Webhooks (External Triggers)
- **`POST /hooks/wake`**: Enqueues a system event for main session, optionally triggers immediate heartbeat
- **`POST /hooks/agent`**: Runs isolated agent turn with delivery options
- **Auth**: Bearer token required
- **Custom mappings**: `hooks.mappings` for transforming arbitrary payloads
- **Agent routing**: Can target specific agents via `agentId`
- This is the **most powerful proactivity primitive** — anything that can POST HTTP can wake the agent

### Hooks (Event-Driven Scripts)
- **Event types**: `command`, `session`, `agent`, `gateway`, `message`
- **Plugin hooks** (run inside the agent loop):
  - `agent_end` — fires after agent run completes
  - `session_start` / `session_end` — session lifecycle
  - `after_tool_call` — intercept tool results
  - `message_received` / `message_sent` — message lifecycle
  - `gateway_start` / `gateway_stop`
- **Key insight**: `agent_end` hook could detect sub-agent completion and trigger a webhook to wake the main agent

### Sub-Agent Architecture
- Sub-agents auto-announce completion to their requester session (push-based)
- The main agent receives this announcement on its **next turn** (heartbeat or user message)
- No mechanism today to force the main agent to process the announcement immediately

---

## 2. What's Missing

### Critical Gaps

1. **No sub-agent completion → immediate wake**: When a sub-agent finishes, the result sits in the main session context until the next heartbeat or user message. There's no built-in "sub-agent done → wake main agent now" hook.

2. **No event loop / continuous execution**: OpenClaw is fundamentally request-response. Each agent turn is a discrete invocation. There's no persistent process that continuously monitors state.

3. **No internal event bus for agent-to-agent signaling**: Sub-agents can't programmatically trigger a webhook to wake the parent. The `message` tool sends to channels, not to the Gateway's webhook endpoint.

4. **No file-watch or state-change triggers**: Can't say "when this file changes, wake the agent." Would need external `inotifywait` + webhook.

5. **No pipeline/workflow orchestration**: No built-in DAG execution, no "run A, then B, then C" with automatic handoffs.

6. **Heartbeat minimum granularity**: While configurable, running heartbeats every 1-2 minutes is expensive (each is a full LLM call). For near-real-time reactivity, this is cost-prohibitive.

---

## 3. Workarounds (Faking Proactivity with Existing Tools)

### Strategy A: Tight Heartbeat + HEARTBEAT.md Checklist (Simplest)

**How**: Set heartbeat to 5-10 minutes. Add to HEARTBEAT.md:
```markdown
- Check for completed sub-agent results (subagents list)
- If any completed, process results and continue pipeline
- Check for new files in workspace/inbox/
```

**Pros**: Zero custom code, works today  
**Cons**: 5-10 min latency, costs ~6-12 LLM calls/hour, agent may miss items between checks

**Config**:
```json5
{
  agents: {
    defaults: {
      heartbeat: {
        every: "5m",
        target: "discord",
        to: "channel:1475554330139562130",
        activeHours: { start: "06:00", end: "02:00" },
      },
    },
  },
}
```

### Strategy B: Webhook Bridge Script (Best Balance)

**How**: Write a lightweight daemon that watches for sub-agent completion signals (file, process exit, etc.) and POSTs to `/hooks/wake`.

```bash
#!/bin/bash
# watch-subagents.sh — polls for completion marker files
HOOK_URL="http://127.0.0.1:18789/hooks/wake"
TOKEN="your-hooks-token"
WATCH_DIR="/root/.openclaw/workspace/.signals"

mkdir -p "$WATCH_DIR"
inotifywait -m -e create "$WATCH_DIR" | while read dir event file; do
  curl -s -X POST "$HOOK_URL" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"text\": \"Signal received: $file\", \"mode\": \"now\"}"
  rm "$WATCH_DIR/$file"
done
```

Then have sub-agents `touch /root/.openclaw/workspace/.signals/task-done-<id>` when they complete.

**Pros**: Near-instant reactivity, low cost (only wakes agent when needed)  
**Cons**: Requires sub-agents to write signal files (convention, not enforced), needs inotifywait

### Strategy C: Custom `agent_end` Plugin Hook (Most Robust)

**How**: Write an OpenClaw plugin hook that fires on `agent_end` for sub-agent sessions and POSTs to `/hooks/wake`.

```typescript
// hooks/subagent-wake/handler.ts
const handler = async (event) => {
  if (event.type !== 'agent' || event.action !== 'end') return;
  
  // Check if this is a sub-agent session
  const sessionKey = event.context?.sessionKey || '';
  if (!sessionKey.includes('subagent:')) return;
  
  // Wake the main agent
  const resp = await fetch('http://127.0.0.1:18789/hooks/wake', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.OPENCLAW_HOOKS_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      text: `Sub-agent completed: ${sessionKey}`,
      mode: 'now',
    }),
  });
};

export default handler;
```

**Pros**: Automatic, no sub-agent cooperation needed, fires on every sub-agent completion  
**Cons**: Need to verify `agent_end` fires for sub-agent sessions (may need plugin hook, not just internal hook)

### Strategy D: Cron-Based Pipeline Runner

**How**: Create a cron job that runs every 2-3 minutes, checks a pipeline state file, and continues work.

```bash
openclaw cron add \
  --name "Pipeline runner" \
  --cron "*/3 * * * *" \
  --session main \
  --system-event "Check pipeline state in workspace/pipeline-state.json. If any stages completed, process results and advance pipeline." \
  --wake now
```

**Pros**: Simple, persistent across restarts  
**Cons**: 3-min latency, burns tokens polling

---

## 4. What Other Frameworks Do

### AutoGPT
- **Continuous loop**: Runs in a `while True` loop — think → act → observe → repeat
- **No human gate**: Optionally runs N steps without approval
- **Problem**: Unbounded loops, runaway costs, hallucination cascades
- **Relevant lesson**: Continuous execution without guardrails is dangerous

### BabyAGI
- **Task queue**: Maintains a priority queue of tasks
- **Loop**: Create tasks → prioritize → execute top task → repeat
- **Key insight**: The task queue is the state machine. Each iteration pops a task, executes it, and may create new tasks
- **Relevant lesson**: A persistent task queue + periodic executor = pseudo-autonomy

### CrewAI
- **Agent roles**: Multiple specialized agents with defined roles
- **Process types**: Sequential (A→B→C) or hierarchical (manager delegates)
- **Delegation**: Agents can delegate to other agents mid-task
- **Relevant lesson**: Structured workflows with explicit handoff points

### LangGraph
- **State machine**: Defines agent workflows as directed graphs
- **Nodes**: Each node is an agent/tool execution
- **Edges**: Conditional routing based on state
- **Persistence**: Checkpoints allow resume after interruption
- **Relevant lesson**: The most mature approach — explicit state machines with persistence and conditional branching

### Key Takeaway from All Frameworks
The pattern that works is: **persistent state + event-driven transitions + bounded execution**. Not infinite loops, but rather: "when X happens, do Y, then checkpoint."

---

## 5. Concrete Recommendations

### Immediate (Do Today)

#### 1. Enable webhooks
```json5
// ~/.openclaw/openclaw.json
{
  hooks: {
    enabled: true,
    token: "${OPENCLAW_HOOKS_TOKEN}",
    path: "/hooks",
    defaultSessionKey: "hook:ingress",
    allowRequestSessionKey: true,
    allowedSessionKeyPrefixes: ["hook:"],
  },
}
```

#### 2. Tighten heartbeat to 10 minutes
```json5
{
  agents: {
    defaults: {
      heartbeat: {
        every: "10m",
        target: "discord",
        to: "channel:1475554330139562130",
      },
    },
  },
}
```

#### 3. Update HEARTBEAT.md with sub-agent monitoring
```markdown
## Sub-Agent Pipeline
- Run `subagents list` to check for completed sub-agents
- If any completed since last check, review their results
- If results require follow-up, take action immediately
- If a pipeline stage completed, advance to next stage
```

### Short-Term (This Week)

#### 4. Build the webhook bridge script
Deploy Strategy B (inotifywait + webhook POST) as a systemd service. Have sub-agents write signal files on completion.

#### 5. Implement pipeline state file
```json
// workspace/pipeline-state.json
{
  "pipelines": {
    "content-creation-001": {
      "status": "in-progress",
      "stages": [
        { "name": "research", "status": "complete", "subagentId": "abc-123" },
        { "name": "draft", "status": "pending" },
        { "name": "review", "status": "pending" }
      ],
      "createdAt": "2026-02-23T20:00:00Z"
    }
  }
}
```
The heartbeat checks this file and advances pipelines.

### Medium-Term (Next 2 Weeks)

#### 6. Write a plugin hook for `agent_end`
Create a proper OpenClaw plugin that catches sub-agent session endings and fires `/hooks/wake`. This eliminates polling entirely for sub-agent handoffs.

#### 7. Create a pipeline orchestrator skill
A SKILL.md that defines how Kitt manages multi-stage workflows with checkpointing.

---

## 6. Technical Implementation Plan

### Phase 1: Immediate Config Changes (30 min)

```bash
# 1. Set hooks token
export OPENCLAW_HOOKS_TOKEN=$(openssl rand -hex 16)

# 2. Update openclaw.json with webhook + heartbeat config
openclaw configure

# 3. Restart gateway
openclaw gateway restart

# 4. Test webhook
curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "Test wake", "mode": "now"}'
```

### Phase 2: Signal Bridge Service (2 hours)

**File**: `/root/.openclaw/workspace/scripts/signal-bridge.sh`
```bash
#!/bin/bash
set -euo pipefail
HOOK_URL="http://127.0.0.1:18789/hooks/wake"
TOKEN="${OPENCLAW_HOOKS_TOKEN}"
WATCH_DIR="/root/.openclaw/workspace/.signals"
mkdir -p "$WATCH_DIR"

echo "[signal-bridge] Watching $WATCH_DIR..."
inotifywait -m -e create -e moved_to "$WATCH_DIR" --format '%f' | while read file; do
  echo "[signal-bridge] Signal: $file"
  content=$(cat "$WATCH_DIR/$file" 2>/dev/null || echo "$file")
  curl -sf -X POST "$HOOK_URL" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"text\": \"[Pipeline Signal] $content\", \"mode\": \"now\"}" || true
  rm -f "$WATCH_DIR/$file"
done
```

**Systemd unit**: `/etc/systemd/system/openclaw-signal-bridge.service`
```ini
[Unit]
Description=OpenClaw Signal Bridge
After=network.target

[Service]
ExecStart=/root/.openclaw/workspace/scripts/signal-bridge.sh
Restart=always
Environment=OPENCLAW_HOOKS_TOKEN=<token>

[Install]
WantedBy=multi-user.target
```

### Phase 3: Sub-Agent Convention (1 hour)

Add to AGENTS.md or sub-agent instructions:
```markdown
## On Completion
When your task is complete, write a signal file:
```bash
echo "Task complete: <summary>" > /root/.openclaw/workspace/.signals/$(date +%s)-done
```
This triggers the main agent to pick up your results.
```

### Phase 4: Plugin Hook for agent_end (4 hours)

Create `/root/.openclaw/workspace/hooks/subagent-wake/`:
- `HOOK.md` with metadata targeting `agent:end` events
- `handler.ts` that filters for sub-agent sessions and POSTs to `/hooks/wake`

This is the cleanest long-term solution but requires understanding plugin hook registration.

### Phase 5: Pipeline State Machine (1 day)

Build a pipeline orchestrator that:
1. Reads `pipeline-state.json` 
2. Identifies completed stages
3. Spawns sub-agents for next stages
4. Updates state
5. Reports progress

This could be a SKILL.md that Kitt follows, or a standalone script.

---

## Summary: Effort vs Impact

| Solution | Effort | Latency | Cost | Robustness |
|----------|--------|---------|------|------------|
| Tighter heartbeat (10m) | 5 min | 10 min | Medium | Low |
| HEARTBEAT.md checklist | 10 min | 10 min | Medium | Medium |
| Webhook bridge + signals | 2 hours | <5 sec | Low | High |
| Plugin hook (agent_end) | 4 hours | <2 sec | Lowest | Highest |
| Pipeline state machine | 1 day | Depends | Low | Highest |

**Recommended approach**: Start with tighter heartbeat + HEARTBEAT.md (today), then build the webhook bridge (this week), then the plugin hook (next week). The pipeline state machine is optional but valuable for complex multi-stage work.

The webhook system (`/hooks/wake` and `/hooks/agent`) is the key enabler. Everything else is about getting signals into that webhook efficiently.
