Skip to content

Session stop re-runs graph extraction over the full observation set even when nothing changed #1063

Description

@yulin0629

Summary

The event::session::stopped handler sends the session's entire compressed observation set to mem::graph-extract on every session stop, with no change detection. Sessions that stop repeatedly without new activity (heartbeat-style loops) re-extract the identical set every time.

Root cause

src/triggers/events.ts (session-stopped handler):

if (isGraphExtractionEnabled()) {
  const observations = await kv.list<CompressedObservation>(
    KV.observations(data.sessionId),
  );
  const compressed = observations.filter((o) => o.title);
  if (compressed.length > 0) {
    sdk.trigger({
      function_id: "mem::graph-extract",
      payload: { observations: compressed },   // full set, every stop
      action: TriggerAction.Void(),
    });
  }
}

There is no fingerprint/dedup — every stop re-processes the full history even when zero observations were added since the last extraction.

Observed impact (self-hosted, v0.9.27)

A session capped at 500 observations that stopped every 30 minutes produced a ~213 KB (~50K input tokens) graph-extraction LLM request per cycle, around the clock, with the input identical each time (extraction yields mostly already-known nodes/edges).

This is the same shape as the double/redundant summarize issue (#1062) — full re-processing on session stop with no change detection — just in a different function.

Fix

Included in #1061 (shares the computeInputFingerprint helper introduced there): fingerprint the compressed set, store it per session (KV.graphExtractState), and trigger mem::graph-extract only when the fingerprint changed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions