Problem
The CMA memory store — the only shipped CmaStore implementation — has no TTL, no eviction, and no session deletion (src/stores/memory/cma-event-broker.ts:29-34, src/stores/memory/cma-memory-store.ts:77-85; the CmaStore interface has no delete operation, src/stores/cma-store.ts:184-207). Every lossless event (up to 1 MiB each) and every settled inbound claim (full command + result payload, kept for idempotency) is retained forever, including for terminated sessions.
Two concrete consequences:
- Unbounded memory growth. Steady heap growth across session churn → OOM kill of the service container over days/weeks of uptime.
- Permanent 413 on the events endpoint.
listSessionEvents() always replays the full history with no cursor (cma-memory-store.ts:399-402), so once a session's cumulative events cross CMA_MAX_REPLAY_BYTES (8 MiB), GET /v1/sessions/:id/events throws RangeError → 413 (src/surfaces/cma-http/response.ts:151-153) on every subsequent request, and cursor-less SSE reconnects fail the same way. Because history is never pruned, the threshold is only ever crossed, never uncrossed — and there is no remediation API.
Reproduction
Persist lossless events totalling more than 8 MiB to one session (as few as 9 max-size events), then:
curl http://<host>/v1/sessions/<id>/events # → 413, permanently
For the growth half: run sessions continuously against the memory store and observe monotonic heap increase with no eviction.
Impact
OOM of a long-lived service; per-session permanent API failure with total event-stream loss for consumers that did not persist a cursor.
Proposed direction
Add a retention/eviction policy (per-session cap and/or post-termination cleanup, including settled inbound claims), and make full-history listing cursor-aware rather than unconditionally replaying everything.
Acceptance criteria
- Memory usage stays bounded across session churn and terminated sessions.
- Sessions with large histories remain listable via cursors; no permanent 413 state.
Problem
The CMA memory store — the only shipped
CmaStoreimplementation — has no TTL, no eviction, and no session deletion (src/stores/memory/cma-event-broker.ts:29-34,src/stores/memory/cma-memory-store.ts:77-85; theCmaStoreinterface has no delete operation,src/stores/cma-store.ts:184-207). Every lossless event (up to 1 MiB each) and every settled inbound claim (full command + result payload, kept for idempotency) is retained forever, including for terminated sessions.Two concrete consequences:
listSessionEvents()always replays the full history with no cursor (cma-memory-store.ts:399-402), so once a session's cumulative events crossCMA_MAX_REPLAY_BYTES(8 MiB),GET /v1/sessions/:id/eventsthrowsRangeError→ 413 (src/surfaces/cma-http/response.ts:151-153) on every subsequent request, and cursor-less SSE reconnects fail the same way. Because history is never pruned, the threshold is only ever crossed, never uncrossed — and there is no remediation API.Reproduction
Persist lossless events totalling more than 8 MiB to one session (as few as 9 max-size events), then:
For the growth half: run sessions continuously against the memory store and observe monotonic heap increase with no eviction.
Impact
OOM of a long-lived service; per-session permanent API failure with total event-stream loss for consumers that did not persist a cursor.
Proposed direction
Add a retention/eviction policy (per-session cap and/or post-termination cleanup, including settled inbound claims), and make full-history listing cursor-aware rather than unconditionally replaying everything.
Acceptance criteria