Agent Run Log
Internal endpoint that records structured execution logs for every OpenClaw agent run. Each agent calls this endpoint at the end of its cron cycle, producing a permanent audit trail that feeds the Intelligence Dashboard and the recursive RLHF quality loop.
Refs #3345
Purpose
Every OpenClaw agent (Sage, Aurora, Nova, Atlas, Lyra, Luma, Helios, Scout, etc.) runs on a cron schedule. When a run finishes, the agent reports what it did, whether it succeeded, and a structured summary of actions taken. This data serves three consumers:
- Intelligence Dashboard -- the
tasks_completed_todaymetric counts successful runs fromagent_run_log. - Auditor Agent -- reads recent runs to validate agent liveness and detect silent failures.
- RLHF Quality Loop -- each logged run automatically triggers an RLHF quality score via Celery, enabling recursive improvement measurement over time.
Endpoint
POST /api/v1/internal/agent-run-log
Records a single agent run completion.
Status Code: 204 No Content on success
Authentication: None. This is an internal-only endpoint behind Kong's internal route. It is not exposed to public consumers.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Agent identifier (e.g., sage, nova, aurora). Must not be empty. |
status | string | Yes | One of: success, partial, failed. |
actions_taken | object | No | Structured summary of what the agent did. Defaults to {}. |
Validation Rules
agent_idis trimmed of whitespace and must not be empty after trimming.statusmust be exactly one ofsuccess,partial, orfailed. Any other value returns a422 Unprocessable Entityerror.actions_takenis an arbitrary JSON object. By convention, it should include asummarykey with a human-readable string describing the run.
Database Table
CREATE TABLE agent_run_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
agent_id VARCHAR(255) NOT NULL,
run_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
action_summary TEXT,
actions_taken JSONB DEFAULT '{}',
status VARCHAR(32) NOT NULL DEFAULT 'success',
consecutive_silence_count INT NOT NULL DEFAULT 0,
rlhf_scores JSONB DEFAULT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_agent_run_log_agent_id ON agent_run_log(agent_id);
CREATE INDEX idx_agent_run_log_created_at ON agent_run_log(created_at DESC);
CREATE INDEX idx_agent_run_log_status ON agent_run_log(status);
CREATE INDEX idx_agent_run_log_agent_created ON agent_run_log(agent_id, created_at DESC);
Column Details
| Column | Description |
|---|---|
id | Auto-generated UUID primary key. |
agent_id | Which agent produced this run (e.g., sage). |
run_at | Server-side UTC timestamp of when the run was recorded. |
action_summary | Extracted from actions_taken["summary"]. Used for quick display. |
actions_taken | Full structured payload as JSONB. Contains everything the agent reported. |
status | success, partial, or failed. |
consecutive_silence_count | Tracks how many consecutive runs had no meaningful output. Used by the Auditor. |
rlhf_scores | Populated asynchronously by the RLHF scoring Celery task after insertion. |
created_at | Row creation timestamp. |
Insert Behavior
Unlike the peer swarm heartbeat (which upserts), the agent run log always inserts a new row. Every run produces a distinct record, building a complete execution history per agent.
After the row is inserted and committed, the endpoint fires an asynchronous Celery task (score_agent_run) to compute RLHF quality scores for the run. This is fire-and-forget: if the RLHF task fails, the run log row is still committed and the failure is logged as a warning.
Intelligence Dashboard Integration
tasks_completed_today
The GET /api/v1/public/platform/intelligence endpoint counts today's successful runs:
SELECT COUNT(*) FROM agent_run_log
WHERE status = 'success'
AND created_at >= :today_start
This count is combined with peer swarm improvement_loops and Agent Cloud task counts to produce the final tasks_completed_today metric.
Agent Event Cards
The dashboard generates per-agent event cards from the run log via _get_peer_swarm_events(). Each agent's most recent run becomes a card showing:
- The agent name and role (e.g., "Sage -- Backend")
- What the agent did (from
action_summary) - Run status (
completedorfailed) - How recently it ran
Auditor Agent
The Auditor agent calls recent_runs() from agent_run_log_service.py to read structured state across all agents, detecting patterns like:
- Agents that have stopped reporting (liveness check)
- Agents with consecutive failures
- Agents with empty
actions_taken(silent runs)
RLHF Quality Loop
After every run log insertion, the endpoint triggers:
from app.tasks.rlhf_tasks import score_agent_run
score_agent_run.delay(str(row_id))
This Celery task scores the run's quality and writes results back to the rlhf_scores JSONB column on the same row. The RLHF data feeds the rlhf_quality_score_7d metric on the Intelligence Dashboard, enabling the team to track whether agent output quality is improving over time.
Refs #3347
Example Integration
Python agent script
import requests
BACKEND_URL = "https://ainative-browser-builder.up.railway.app"
def report_run(agent_id: str, status: str, actions: dict) -> None:
"""Report agent run completion to the backend."""
resp = requests.post(
f"{BACKEND_URL}/api/v1/internal/agent-run-log",
json={
"agent_id": agent_id,
"status": status,
"actions_taken": actions,
},
timeout=10,
)
resp.raise_for_status()
# Example: Sage agent completing a backend task
report_run(
agent_id="sage",
status="success",
actions_taken={
"summary": "Fixed database connection pool leak in session.py",
"files_changed": ["src/backend/app/db/session.py"],
"tests_run": 14,
"tests_passed": 14,
"pr_created": "#3456",
},
)
# Example: Aurora agent with a partial result
report_run(
agent_id="aurora",
status="partial",
actions_taken={
"summary": "Ran QA suite; 3 of 5 test suites passed",
"suites_total": 5,
"suites_passed": 3,
"failures": ["test_billing_edge_cases", "test_oauth_refresh"],
},
)
# Example: Agent that failed
report_run(
agent_id="nova",
status="failed",
actions_taken={
"summary": "Security scan aborted: dependency resolution timeout",
"error": "pip install timed out after 120s",
},
)
curl
curl -X POST https://ainative-browser-builder.up.railway.app/api/v1/internal/agent-run-log \
-H "Content-Type: application/json" \
-d '{
"agent_id": "sage",
"status": "success",
"actions_taken": {
"summary": "Deployed schema migration for billing_events table",
"tables_modified": ["billing_events"],
"rows_affected": 0
}
}'
Response: 204 No Content
Service Layer
The agent_run_log_service.py module provides an async interface for writing and reading run logs directly via SQLAlchemy (bypassing the REST endpoint). This is used by agents running inside the same process:
from app.services.agent_run_log_service import write_run_log
await write_run_log(
agent_id="atlas",
actions_taken={"summary": "Scaled worker replicas to 3"},
status="success",
)
Both the REST endpoint and the service layer write to the same agent_run_log table with identical column mappings.
Related Files
| File | Purpose |
|---|---|
src/backend/app/api/internal/agent_run_log.py | Endpoint implementation |
src/backend/app/services/agent_run_log_service.py | Async service layer for direct DB access |
src/backend/app/tasks/rlhf_tasks.py | Celery task that scores runs after insertion |
src/backend/app/api/v1/endpoints/platform_intelligence.py | Dashboard that reads from agent_run_log |
scripts/sync-production-schema.py | Table creation (search for sync_agent_run_log_table) |