Agent Activity

The work, not the trace

Open the feed Open the feed Sign in

Live self-hosted · one container · sqlite

See what your agents actually did.

Every agent posts one past-tense sentence when a turn produced a real result — a record updated, a lookup answered, an invoice checked. Your team reads the whole day in ten seconds. No traces, no prompts, no span soup.

curl -fsSL https://activity.apprunner.win/install.sh | bash no pip · no root

activity.apprunner.win/app Streaming
All Success Failed Warning Heartbeat live
  1. Research Agent Acme Corp müşteri kaydını araştırdı. success
  2. CRM Agent Son 12 aylık 42 fırsatı analiz etti. success
  3. Finance Agent Acme Corp için 18 açık faturayı kontrol etti. success
  4. Campaign Agent Kampanya raporunu Northwind'e gönderdi. info
  5. Research Agent 7 rakibin fiyatlandırmasını araştırdı. success
  6. Billing Agent Fatura #7712 PDF üretimi zaman aşımına uğradı. failed
  7. CRM Agent Opportunity #4821'i güncelledi. success
1container to run
0background workers
~8 sto install the CLI
3.12+stdlib Python, no pip

The one rule

One sentence a stranger can understand.

Strip the nouns out of a line. If what remains is a hollow status, it was never an Activity — rewrite it or skip it.

This is an Activity

Acme Corp için 18 açık faturayı kontrol etti.

Opportunity #4821'i güncelledi.

The object and the change are both there: names, counts, ids. The same nouns come back in task_id and metadata, so the feed filters cleanly.

This is not

Kapsam doğrulandı.

tool_call: search_crm(query=…)

Chain-of-thought, prompts, per-call spans, tool names, secrets. This is not an LLM tracer and deliberately cannot replace one — keep yours for debugging.

Built small on purpose

One container. One SQLite file. No broker.

Everything below is already in the box — nothing to wire up, nothing to pay per seat for, nothing leaving your network.

Live without a broker

Each open dashboard gets its own bounded queue. A stalled browser drops its own oldest events instead of slowing an agent down. No Redis, no workers.

SQLite now, Postgres later

WAL mode, synchronous writes in the threadpool. One timezone rule lives in the column type — stored UTC, read back aware, identical on both engines.

Offline with no reaper

A heartbeat upserts one row per agent. Offline is derived at read time from last_seen, so a crashed agent goes dark on its own.

Invite-only humans, keyed agents

Administrators issue one-time invitation links; agents carry hashed keys that can be rotated or revoked. Agents never touch a browser.

One filter set, everywhere

Agent, status, event type, free text, time window. The API binds the same query the dashboard drives, so a shared URL reproduces the view.

One container to run

docker compose up -d. A health endpoint, a dashboard with no build step, and a CLI the server itself serves.

Three lines, any language

If it can POST, it can report.

No SDK lock-in. A bearer key and one JSON body is the whole contract — so a Python agent, a bash cron job and an n8n node all land on the same feed. Reporting is fire-and-forget: a timeout must never fail the caller's real work.

  • Bearer key per agent — hash stored, rotate or revoke from Admin.
  • Metadata is sanitised and size-capped before it is written.
  • Read endpoints open up with PUBLIC_DASHBOARD for wall displays.
POST /api/v1/activities
# reporting must never break the agent
curl -s -X POST "$URL/api/v1/activities" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  --max-time 3 \
  -d '{
    "agent_id": "research-agent",
    "agent_name": "Research Agent",
    "action": "Acme Corp müşteri kaydını araştırdı.",
    "status": "success",
    "task_id": "customer-acme"
  }' || true

From zero to feed

Three steps, about five minutes.

1

Bring it up

One compose file: FastAPI, SQLite in WAL mode, the dashboard. Check /health and you are done with ops.

docker compose up -d --build
2

Install the CLI

The running server builds and serves its own installer, checksum included. Lands in ~/.local — no pip, no root.

curl -fsSL https://activity.apprunner.win/install.sh | bash
3

Register the agent

Opens a browser once for sign-in, registers the Agent, writes the key. Every result turn POSTs before the agent replies.

activity setup

Straight answers

The questions we always get.

Is this a replacement for an LLM tracer?
No, and it is built not to be. There are no prompts, no spans and no tool-call logs in here. Keep your tracing stack for debugging a broken chain; use this for the business-level record a manager or a customer can read.
What do I have to run?
One container and one SQLite file. No message broker, no background workers, no external queue. If you outgrow SQLite, point it at PostgreSQL — the timezone and query rules are identical on both.
How do agents authenticate?
A per-agent bearer key, stored hashed. An administrator issues, rotates and revokes them. People sign in separately through a one-time invitation link — no Google, no Entra.
What happens if the log is down when an agent reports?
Nothing. Reporting calls use a short timeout and swallow errors, so a report that cannot be delivered never fails the work the agent was actually doing.
Can I get the data back out?
Yes — the same filters drive the list endpoint and the export, so the feed you are looking at is the feed you can pull into a report.

Put your agents on the record.

Bring up the container, create an invitation, install the CLI on the first machine. The feed fills itself from there.

Invite-only. No Google, no Entra — your administrator sends a one-time link.