You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: persist session events to DB and add replay endpoint
Stream-json events are now batch-persisted to a session_events table
(JSONB) during SSE streaming. Completed sessions can be replayed via
GET /sessions/{id}/events. The streaming pipeline is refactored into
layered generators: stream_events() -> stream_and_persist() for
clean separation of concerns.
- Add SessionEvent model with (session_id, event_id) unique constraint
- Alembic migration for session_events table
- Refactor stream_output() into stream_events() + stream_and_persist()
- Batch INSERT with ON CONFLICT DO NOTHING for idempotent reconnects
- Add GET /events replay endpoint with SessionEventsListResponse schema
- Frontend: load stored events for terminal sessions (existingSessionId prop)
- Fix FakeDockerClient log_lines to include trailing newlines
- 14 new tests (stream_events, stream_and_persist, get_session_events)
-**Skills as agents**: each skill is a self-contained folder with workflow definitions, mounted into containers
49
49
-**SSE passthrough**: backend relays container output to frontend (no AI response generation in backend)
50
+
-**Session persistence**: stream-json events are batch-persisted to `session_events` table (JSONB) during SSE streaming via `stream_and_persist()`. Completed sessions can be replayed from `GET /sessions/{id}/events`. The streaming pipeline is layered: `stream_events()` (raw tuples) -> `stream_and_persist()` (SSE + DB writes) or `stream_output()` (SSE only, for tests).
50
51
-**Stream-json protocol**: containers emit NDJSON with 5 event types: `system` (init/retry), `assistant` (one event per content block — thinking/text/tool_use), `user` (tool_result), `result` (session end + metadata), `rate_limit_event`. The `result.result` field duplicates the last assistant text — only display assistant events, use result for status only. No `--include-partial-messages` flag, so no `stream_event` deltas.
51
52
-**API prefix**: all routes under `/api/v1`
52
53
-**Admin panel**: SQLAdmin at `/admin`, configured in `admin/views.py`
@@ -76,6 +77,8 @@ cd apps/api && uv run alembic revision --autogenerate -m "description" # New mi
76
77
77
78
- Tests use `AsyncClient` with `ASGITransport` (no real server)
78
79
-`conftest.py` sets env vars (DATABASE_URL, SECRET_KEY, etc.) **before** any app imports — order matters
80
+
-**`FakeDockerClient` log_lines**: must include trailing `\n` for the line-buffering logic in `stream_events()` to split correctly
81
+
-**`get_db_context` in tests**: tests that call `stream_and_persist()` need a `db_with_factory` fixture that calls `set_session_factory()` / `clear_session_factory()` — see `test_service.py` for the pattern
0 commit comments