Measured on openai-agents 0.20.0, macOS arm64, Python 3.12, with the open MIT suite agmi (https://github.com/tech4biz-yasha/agmi, row openai-agents-sqlite-session).
Method: seed a session through SQLiteSession.add_items(), apply one edit directly to the SQLite file with no keys, open a fresh SQLiteSession on the same file, read through get_items(), and record what the SDK does.
Result: all eight storage-level edits are accepted, meaning get_items() returns without raising and the edited history is fed to the model on the next run.
T1 text changed inside message_data
T2 newest rows deleted, so the agent resumes from an older state
T3 one row removed from the middle
T4 two rows swapped
T5 a new row inserted by the attacker
T6 another session's row copied over this session's row, session_id kept (user B's item served as user A's)
T7 an older row of the same session copied over the newest
T8 the row's created_at rewritten, text untouched. Separately, rewriting a row's session_id moves that item into another user's history, also with no error.
One detail worth noting: a row whose JSON no longer decodes is skipped silently in get_items(), so even a corrupting edit does not surface.
Reproduction script and pinned tests: https://github.com/tech4biz-yasha/agmi/blob/main/tests/test_openai_agents_session.py
Explainer per edit: https://agentmemoryintegrity.org/edits.html
I realise the store is a local file and the threat model may be "trust the filesystem". Two things would still help users who put this behind a shared volume or a backup: a note in the SQLiteSession docs saying the store carries no integrity protection, and optionally a per-row MAC over session_id + position + message_data with a keyed verify on read. The reference at-rest store in agmi (agmi/adapters/reference_atrest.py) shows the minimum that rejects all eight. Happy to open a PR for either, and I will re-measure and update the public row when anything changes.
Measured on openai-agents 0.20.0, macOS arm64, Python 3.12, with the open MIT suite agmi (https://github.com/tech4biz-yasha/agmi, row
openai-agents-sqlite-session).Method: seed a session through
SQLiteSession.add_items(), apply one edit directly to the SQLite file with no keys, open a freshSQLiteSessionon the same file, read throughget_items(), and record what the SDK does.Result: all eight storage-level edits are accepted, meaning
get_items()returns without raising and the edited history is fed to the model on the next run.T1 text changed inside
message_dataT2 newest rows deleted, so the agent resumes from an older state
T3 one row removed from the middle
T4 two rows swapped
T5 a new row inserted by the attacker
T6 another session's row copied over this session's row,
session_idkept (user B's item served as user A's)T7 an older row of the same session copied over the newest
T8 the row's
created_atrewritten, text untouched. Separately, rewriting a row'ssession_idmoves that item into another user's history, also with no error.One detail worth noting: a row whose JSON no longer decodes is skipped silently in
get_items(), so even a corrupting edit does not surface.Reproduction script and pinned tests: https://github.com/tech4biz-yasha/agmi/blob/main/tests/test_openai_agents_session.py
Explainer per edit: https://agentmemoryintegrity.org/edits.html
I realise the store is a local file and the threat model may be "trust the filesystem". Two things would still help users who put this behind a shared volume or a backup: a note in the
SQLiteSessiondocs saying the store carries no integrity protection, and optionally a per-row MAC oversession_id + position + message_datawith a keyed verify on read. The reference at-rest store in agmi (agmi/adapters/reference_atrest.py) shows the minimum that rejects all eight. Happy to open a PR for either, and I will re-measure and update the public row when anything changes.