Skip to main content

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:

  1. Intelligence Dashboard -- the tasks_completed_today metric counts successful runs from agent_run_log.
  2. Auditor Agent -- reads recent runs to validate agent liveness and detect silent failures.
  3. 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

FieldTypeRequiredDescription
agent_idstringYesAgent identifier (e.g., sage, nova, aurora). Must not be empty.
statusstringYesOne of: success, partial, failed.
actions_takenobjectNoStructured summary of what the agent did. Defaults to {}.

Validation Rules

  • agent_id is trimmed of whitespace and must not be empty after trimming.
  • status must be exactly one of success, partial, or failed. Any other value returns a 422 Unprocessable Entity error.
  • actions_taken is an arbitrary JSON object. By convention, it should include a summary key 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

ColumnDescription
idAuto-generated UUID primary key.
agent_idWhich agent produced this run (e.g., sage).
run_atServer-side UTC timestamp of when the run was recorded.
action_summaryExtracted from actions_taken["summary"]. Used for quick display.
actions_takenFull structured payload as JSONB. Contains everything the agent reported.
statussuccess, partial, or failed.
consecutive_silence_countTracks how many consecutive runs had no meaningful output. Used by the Auditor.
rlhf_scoresPopulated asynchronously by the RLHF scoring Celery task after insertion.
created_atRow 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 (completed or failed)
  • 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.


FilePurpose
src/backend/app/api/internal/agent_run_log.pyEndpoint implementation
src/backend/app/services/agent_run_log_service.pyAsync service layer for direct DB access
src/backend/app/tasks/rlhf_tasks.pyCelery task that scores runs after insertion
src/backend/app/api/v1/endpoints/platform_intelligence.pyDashboard that reads from agent_run_log
scripts/sync-production-schema.pyTable creation (search for sync_agent_run_log_table)