Skip to main content

Episodic memory for AI agents: compile raw screen capture into structured, deterministic activity frames.

Project description

activity-frames

tests PyPI Python License: MIT MCP

Episodic memory for AI agents.

Your agent can read your code, search the web, and call APIs - but it has no idea what you have been doing for the last 8 hours. It starts every conversation blind.

activity-frames gives your agent eyes. It records your screen locally, compiles what it sees into structured activity frames (bounded episodes of what you actually did), and serves them to any agent over MCP. No cloud, no LLM in the pipeline, no guessing.

pip install activity-frames
aframes record      # start capturing (local, audio off by default)
aframes context     # your last 2 hours, agent-ready

What your agent sees

Capture stores instants: thousands of snapshot rows a day, each one saying "at 22:53:05, Chrome showed linkedin.com/in/...". Useless to reason over.

activity-frames compiles those instants into episodes:

- id: f-0007
  app: Google Chrome
  site: linkedin.com
  start: "20:24:04"
  end: "20:42:11"
  duration_min: 18.0
  pages:
    - {kind: people_search, entity: "cto paris", count: 2}
    - {kind: profile, entity: najmuzzaman}
    - {kind: company, entity: nexdotai}
  input: {keys: 214, clicks: 31}
  evidence: {frame_ids: "99871..100147"}

And into a compact context block for any system prompt:

USER ACTIVITY (2026-07-04, local time; measured from screen capture, no interpretation):
coverage: 09:12-18:47, 342 active min, 11 apps
away: 12:30-13:15 (45m)
- 09:12-09:58 Cursor (46.2m): main.py - api
- 10:01-10:44 Google Chrome/github.com (41.3m): pull_request:acme/api#412; code:acme/api
- 20:24-20:42 Google Chrome/linkedin.com (18.0m): people_search:cto paris x2; profile:najmuzzaman; company:nexdotai

Drop that into a prompt and your agent knows your day. A full day compiles in under a second and costs zero tokens.

Episodic memory, done honestly

Agent memory today means conversation memory: what you told the model. Episodic memory is what you actually did - and the hard part is representing it without lying.

activity-frames enforces a two-tier contract (SPEC.md):

  • Tier 1, measured (this package): everything is derivable by deterministic code from capture data. Sessions, durations, typed page entities, input volume, coverage gaps. Same input, same output, every time. There are no intent labels - code cannot know that 2 profile views + a people search was "prospecting". That is your agent's job; it is an LLM.
  • Tier 2, inferred (optional extension): tools that add interpretation must namespace it, tag confidence (high | medium | speculative), and link evidence. Facts and guesses can never silently mix.

Every frame carries evidence pointers back to raw capture rows. Every document declares its blind spots. What the system did not see, it says it did not see.

Use it from an agent (MCP)

# Claude Code
claude mcp add activity-frames -- aframes mcp

Any MCP client works: command aframes, args ["mcp"]. Four tools: get_context, get_activity, get_day_summary, get_patterns (repetitive-workflow detection: repeated clicks, URL loops, daily habits).

Use it from Python

from activity_frames import ActivityLog

log = ActivityLog()
doc = log.day()                      # today, structured
doc = log.recent(hours=2)            # last 2 hours
print(log.context(hours=2))          # paste-ready context block

Privacy model

  • Local only. Capture, storage, and compilation all happen on your machine. Nothing is uploaded anywhere, ever.
  • Read-only compilation. The compiler opens the capture database read-only.
  • Content opt-in at the output. Compiled documents carry input counts by default; typed-text content appears only if you explicitly pass --include-text (this also gates the repeated-text pattern detector). Be clear about the boundary: the capture database itself does store what the recorder sees, locally, so protect it like any sensitive file (FileVault, permissions).
  • Audio off by default. aframes record --audio to opt in.
  • No LLM in the compile path. Compilation is plain code, so no language model, local or remote, is involved in producing memory. The capture engine does run on-device OCR to read what is on screen; that stays on your machine.
  • You choose what leaves, when you paste a context block into an agent. Note that window titles and page entities originate from your screen and can contain third-party text; agents should treat them as data, not instructions.

Architecture

 capture engine          compiler (this package)         your agent
 ------------------      ---------------------------     -----------------
 screen snapshots   -->  sessionize (dwell, gaps,   -->  MCP tools /
 accessibility tree      flicker merge)                  context blocks /
 input events            entity typing (20+ sites)       JSON, YAML, md
 (local SQLite)          enrichment, patterns

The default capture engine is screenpipe: aframes record provisions a pinned, MIT-licensed build (v0.3.324), verifies its published sha512 before first run, and manages it for you (see ACKNOWLEDGMENTS.md). Already running your own recorder? Point $AFRAMES_DB at any capture database with compatible frames / ui_events / elements tables and skip aframes record entirely.

CLI

aframes record                   # start capture (--stop / --status / --audio)
aframes today                    # today's frames (YAML*)
aframes day 2026-07-03 -f json   # any day, JSON
aframes context --hours 3        # agent context block
aframes apps                     # per-app time ledger
aframes patterns --days 7        # repetitive workflow detection
aframes mcp                      # MCP stdio server

*YAML output uses PyYAML (pip install "activity-frames[yaml]"); without it the CLI falls back to JSON.

Status

v0.1. Developed and tested on macOS (Apple Silicon); Intel macOS and Linux x64 engine builds exist but are less exercised - reports welcome. Entity parsers cover LinkedIn, GitHub, Google (Search/Docs/Gmail/Maps/Meet/Calendar), YouTube, X, Instagram, Reddit, Luma, Partiful, Product Hunt, Vercel, Supabase, Stripe, Discord, Notion, Figma, Stack Overflow, Calendly, ChatGPT/Claude, localhost; unknown sites fall back to a generic page reference - always total, never lossy. Issues and parser PRs welcome.

Built by Nossa Iyamu, maker of Nocta. MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

activity_frames-0.1.0.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

activity_frames-0.1.0-py3-none-any.whl (39.6 kB view details)

Uploaded Python 3

File details

Details for the file activity_frames-0.1.0.tar.gz.

File metadata

  • Download URL: activity_frames-0.1.0.tar.gz
  • Upload date:
  • Size: 50.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for activity_frames-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c6170eba14286189e959a16337a536ba4a10624a85b7e1fceaf8bb1b4ddab801
MD5 9723455bc6178ec598036014e4f44139
BLAKE2b-256 dafbdd7965d96918cda6743b74744ab5e0fa4981143b7040b6cae27e00400f38

See more details on using hashes here.

File details

Details for the file activity_frames-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for activity_frames-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 902160306488c9a742bb8cbf980ce8613e8fd0b6aadbc29328d895ac66dcdcec
MD5 191cacdce8aed0c22b10a06ad852e999
BLAKE2b-256 dae1c25ca3a120e2d5d0199fe795f473d6c0175fa35211bdff92138e7f97fed8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page