LISAOS // DOCS
GATEWAY // ACTIVITY

Gateway Module — `activity/`

1. Purpose

The activity module is the gateway's observability event log — a mission-scoped feed of agent tool/skill usage (cache_query, memory_search, agent_memory_query, peer_memory_query, skill_usage, tool_call, skill_gate). Beyond logging, it carries a load-bearing side effect: every log_activity call with a non-null agent_name ticks the stall-detector heartbeat on that agent's in-flight dispatch rows (D1175/AK-345), aligning the MCP-side liveness signal with the dashboard activity feed. It also emits activity timeline events onto the shared timelineBus for SSE consumers, and computes skill-usage aggregates.

2. File Inventory

FileLinesResponsibility
repository.ts209activity_log prepared statements; heartbeat tick; timeline emission; skill-usage stats
routes.ts1193 endpoints; VALID_LOG_TYPES manual validation; event_type filters
Total328

No index.ts/types.ts — types are inline exports in repository.ts; router wired directly (createActivityRouter(activityRepo), mount index.ts:738).

3. Public API Surface

REST Endpoints (mount: index.ts:738/api/activity)

MethodPathAuthBodyResponseSide Effects
POST/api/activity/logBearer{event_type, agent_name, mission_id?, detail} (manual validation, not Zod){ok:true}INSERT activity_log; ticks agent_dispatch.last_heartbeat by agent; emits activity timeline event. 400 on invalid event_type/missing agent_name/missing detail
GET/api/activity/?limit=&event_type=Bearer{data: ActivityEvent[]}Recent across all missions; optional event_type filter (validated vs VALID_LOG_TYPES, 400 on invalid)
GET/api/activity/:missionId?limit=&agent=&event_type=Bearer{data: ActivityEvent[]}Per-mission; composable agent+event_type (in-memory compose per Fuda §10.2)

MCP Tools

log_activity (legacy permissive-flat, no raw twin/log). Documented in the mcp module: the REST endpoint uses manual destructure + VALID_LOG_TYPES set, not Zod, so it is tech-debt-flagged (harden Zod before converting to raw).

4. Internal API

createActivityRepository(db)ActivityRepository { log, findByMission, findByMissionAndAgent, findRecent, findRecentByEventType, findByMissionAndEventType, getSkillUsageStats }. The repo is a cross-module dependency — injected into agent-memory routes (to log agent_memory_query/peer_memory_query) and consumed elsewhere for skill-usage analytics (getSkillUsageStats groups skill_usage rows by detail+agent_name).

5. Background Services

None as a timer. But log() performs a synchronous best-effort heartbeat tick (D1175): UPDATE agent_dispatch SET last_heartbeat=now WHERE agent_name=@agent AND status IN ('dispatched','active','silent_but_live'). Wrapped in try/catch — failure must not break the primary INSERT contract. NULL agent_name skips the tick (explicit JS guard mirroring SQL three-valued logic). Timeline activity event emitted via setImmediate after commit, with dispatch_id: null (activity is mission-scoped, not dispatch-scoped).

6. Data Contracts

ActivityEventType = cache_query|memory_search|agent_memory_query|peer_memory_query|skill_usage|tool_call|skill_gate (D1450/#48 added skill_gate). ActivityEvent: id, event_type, agent_name, mission_id, target_agent, detail, created_at. LogActivityInput: event_type (required), agent_name?, mission_id?, target_agent?, detail?. No Zod — validation is a Set.has(event_type) check + presence checks in the route (VALID_LOG_TYPES, symmetric on POST and the GET event_type filter).

7. Dependencies

  • Gateway modules: dispatch/eventBus.js (timelineBus — emits activity events; also the reason activity ticks dispatch heartbeats via the shared DB).
  • External: better-sqlite3, express.
  • Environment variables: none. Table activity_log.

8. Test Coverage

LayerFileNotes
Routestest/activity-routes.test.tsVALID_LOG_TYPES 400 paths, event_type filter symmetry (D1450)
Heartbeattest/activity-heartbeat-tick.test.tsD1175 dispatch-heartbeat alignment, NULL agent skip, non-fatal failure

9. Known Limitations

  • No Zod validation — the log_activity MCP tool and REST endpoint validate via manual destructure + a Set, so the tool cannot be safely converted to a raw twin without hardening the endpoint first (see mcp module tech-debt flag). New non-string fields would be exposed to the client-cache staleness class.
  • The heartbeat tick updates all in-flight rows for an agent, not a specific dispatch (activity has no dispatch_id column). If an agent has two concurrent in-flight dispatches, a single log_activity refreshes both heartbeats — acceptable given the roster rarely double-dispatches one agent, but not surgically scoped.
  • getSkillUsageStats groups on the free-text detail column (skill name); any inconsistency in how callers spell a skill name fragments its usage count.

10. Change History

DateDispatchSummary
2026-07-042296Module spec authored (Phase 4 §5.2), smoke-clone-2, HEAD 5c9a304
1175AK-345 activity→dispatch heartbeat alignment tick
1450#48 skill_gate event_type + filterable GET queries

On this page