community-agent-framework-postgres 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,367 @@
1
+ Metadata-Version: 2.5
2
+ Name: community-agent-framework-postgres
3
+ Version: 0.1.1
4
+ Summary: PostgreSQL storage for Microsoft Agent Framework: chat history, sessions, workflow checkpoints, AG-UI thread snapshots and leases.
5
+ Project-URL: Source, https://github.com/matias-io/community-agent-framework-postgres
6
+ Project-URL: Issues, https://github.com/matias-io/community-agent-framework-postgres/issues
7
+ Project-URL: Changelog, https://github.com/matias-io/community-agent-framework-postgres/blob/main/CHANGELOG.md
8
+ Project-URL: Documentation, https://github.com/matias-io/community-agent-framework-postgres/tree/main/docs
9
+ Author: Matias Suxo Salinas
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent-framework,checkpoint,history-provider,microsoft-agent-framework,postgresql,psycopg,session-store
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Database
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: agent-framework-core<2,>=1.20.0
24
+ Requires-Dist: psycopg[binary,pool]<4,>=3.3.5
25
+ Provides-Extra: ag-ui
26
+ Requires-Dist: agent-framework-ag-ui<2,>=1.5.0; extra == 'ag-ui'
27
+ Provides-Extra: azure
28
+ Requires-Dist: aiohttp<4,>=3.9; extra == 'azure'
29
+ Requires-Dist: azure-identity<2,>=1.19; extra == 'azure'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # community-agent-framework-postgres
33
+ A community-maintained PostgreSQL persistence implementation for Microsoft Agent Framework (MAF), supporting agent chat history, sessions, workflow checkpoints, AG-UI snapshots, and a lease that serializes work across replicas.
34
+
35
+ The package provides `PostgresHistoryProvider` for chat history, `PostgresSessionStore` for `AgentSession` snapshots, `PostgresCheckpointStorage` for workflow checkpoints, `PostgresAGUIThreadSnapshotStore` for AG-UI thread snapshots and `PostgresLeases` for a lock across replicas. `PostgresPersistence` creates all of them over one connection pool and runs the schema migrations. This is a community package written by Matias Suxo Salinas. Microsoft does not maintain or support it.
36
+
37
+ ## Status
38
+
39
+ Alpha. Public names and constructor arguments may change in a 0.x minor release, and the [changelog](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/CHANGELOG.md) lists every such change. Schema changes ship as new numbered migrations, so `migrate()` upgrades an existing database in place.
40
+
41
+ ## Compatibility
42
+
43
+ | 0.1.1 | Declared | Tested |
44
+ |---|---|---|
45
+ | `agent-framework-core` | `>=1.20.0,<2` | 1.20.0, 1.21.0 |
46
+ | `agent-framework-ag-ui` (`ag-ui` extra) | `>=1.5.0,<2` | 1.5.0, 1.5.1 |
47
+ | Python | `>=3.11` | 3.11, 3.12, 3.13, 3.14 |
48
+ | PostgreSQL | | 16, 17 |
49
+ | psycopg | `>=3.3.5,<4` | 3.3.6 |
50
+
51
+ pip and uv refuse a MAF version outside the declared range. A newer minor inside it installs. The first store or hub built in a process then logs one warning for each untested distribution, and a new major, which pip and uv install only when forced, is logged as outside the declared range. `compatibility()` returns the installed and tested versions for a diagnostics command. A newer minor is expected to work, because MAF keeps breaking changes for major versions. [docs/compatibility.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/compatibility.md) explains the support policy and the compatibility check.
52
+
53
+ MAF 1.19 and older are not supported. Their history dedupe drops any new message that matches one already stored, so a user who answers "yes" twice loses the second "yes". MAF 1.20 fixed that, so 1.20 is the floor.
54
+
55
+ 0.1.1 is the first release on PyPI. 0.1.0 was never published.
56
+
57
+ ## What it implements
58
+
59
+ | Class | MAF interface | Extra | Conditions |
60
+ |---|---|---|---|
61
+ | `PostgresHistoryProvider` | `HistoryProvider` | none | Drops replayed messages with MAF's own `filter_new_messages` |
62
+ | `PostgresSessionStore` | `SessionStore` | none | MAF marks `SessionStore` experimental |
63
+ | `PostgresCheckpointStorage` | `CheckpointStorage` | none | Decodes only MAF's safe types and its own; `allowed_checkpoint_types` adds more |
64
+ | `PostgresAGUIThreadSnapshotStore` | `AGUIThreadSnapshotStore` | `ag-ui` | The AG-UI endpoint needs a `snapshot_scope_resolver` next to `snapshot_store` |
65
+ | `PostgresLeases` | none (this package's own) | none | A cross-replica lock; MAF hosting serializes runs per process only |
66
+ | `PostgresPersistence` | none (creates the stores above over one pool) | none | |
67
+ | Microsoft Entra ID sign-in (`credential=`) | none | `azure` | Azure Database for PostgreSQL with Entra authentication |
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ uv add community-agent-framework-postgres
73
+ uv add "community-agent-framework-postgres[ag-ui]" # adds PostgresAGUIThreadSnapshotStore
74
+ uv add "community-agent-framework-postgres[azure]" # adds Microsoft Entra ID sign-in
75
+ pip install community-agent-framework-postgres
76
+ pip install "community-agent-framework-postgres[ag-ui]"
77
+ pip install "community-agent-framework-postgres[azure]"
78
+ ```
79
+
80
+ The `ag-ui` extra installs `agent-framework-ag-ui`. Without it, every store except `PostgresAGUIThreadSnapshotStore` works. `from agent_framework_community_postgres import *` also needs the extra, because `__all__` lists that store. The `azure` extra installs `azure-identity`, and `aiohttp` for its async credentials, for [Microsoft Entra ID sign-in](#microsoft-entra-id-on-azure).
81
+
82
+ On Windows, async psycopg cannot run on the default `ProactorEventLoop`. Run a script's entry point on a selector loop:
83
+
84
+ ```python
85
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
86
+ runner.run(main())
87
+ ```
88
+
89
+ Run uvicorn 0.36 or later with `--loop asyncio:SelectorEventLoop`. uvicorn imports that value as the loop factory. Its plain `--loop asyncio` picks `ProactorEventLoop` on Windows unless reload or workers are on. Use `127.0.0.1` rather than `localhost` in local connection strings. libpq tries `::1` first, and some Windows and WSL setups drop that traffic without an error.
90
+
91
+ ## Quick start
92
+
93
+ This script runs against the database from `docker-compose.yml`. It needs the `ag-ui` extra.
94
+
95
+ ```python
96
+ import asyncio
97
+ import sys
98
+
99
+ from agent_framework import AgentSession, Message
100
+
101
+ from agent_framework_community_postgres import PostgresPersistence
102
+
103
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
104
+
105
+
106
+ async def main() -> None:
107
+ async with PostgresPersistence(application_id="quickstart", connection_string=DSN) as hub:
108
+ await hub.migrate()
109
+ history = hub.history_provider()
110
+ await history.save_messages("session-1", [Message(role="user", contents=["hello"])])
111
+ print([m.text for m in await history.get_messages("session-1")])
112
+ await hub.session_store().set("session-1", AgentSession(session_id="session-1"))
113
+ print(await hub.checkpoint_storage(scope="session-1").list_checkpoint_ids(workflow_name="demo"))
114
+ print(await hub.thread_snapshot_store().get(scope="user-1", thread_id="thread-1"))
115
+
116
+
117
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
118
+ runner.run(main())
119
+ ```
120
+
121
+ `compatibility()` returns the installed and tested MAF versions, for a startup check or a diagnostics command.
122
+
123
+ Every store the hub creates shares its pool, `application_id`, `schema` and `table_prefix`. The factories raise `TypeError` if you pass `application_id`, `client`, `credential`, `pool_kwargs`, `schema` or `table_prefix`. For another schema or prefix, create a separate hub or a standalone store. You can override `retention` per store. `hub.purge()` covers every store created by that hub, including one with its own `retention`, and needs no policy of its own. It purges the rows whose `expires_at` has passed, in the mode you pass or else the hub policy's mode. `hub.leases()` returns a `PostgresLeases` over the same pool. After `hub.close()`, every store it created raises `PostgresStorageError`, even when the hub was built over your own pool.
124
+
125
+ ## Use with Agent Framework
126
+
127
+ Give an agent the history provider in `context_providers` and pass a session to `run`. The provider loads and saves that session's messages. This snippet needs a chat client. It uses `OpenAIChatClient` from `agent-framework-openai` with `OPENAI_API_KEY` set, but any MAF chat client works.
128
+
129
+ ```python
130
+ import asyncio
131
+ import sys
132
+
133
+ from agent_framework import Agent
134
+ from agent_framework.openai import OpenAIChatClient
135
+
136
+ from agent_framework_community_postgres import PostgresPersistence
137
+
138
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
139
+
140
+
141
+ async def main() -> None:
142
+ async with PostgresPersistence(application_id="my-app", connection_string=DSN) as hub:
143
+ await hub.migrate()
144
+ agent = Agent(OpenAIChatClient(model="gpt-4o"), "Answer briefly.", context_providers=[hub.history_provider()])
145
+ session = agent.create_session()
146
+ print((await agent.run("My name is Ada.", session=session)).text)
147
+ print((await agent.run("What is my name?", session=session)).text)
148
+
149
+
150
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
151
+ runner.run(main())
152
+ ```
153
+
154
+ Pass the checkpoint storage to `WorkflowBuilder`. The workflow then writes checkpoints you can resume from. [docs/checkpoints.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/checkpoints.md) has the full example.
155
+
156
+ ```python
157
+ import asyncio
158
+ import sys
159
+
160
+ from agent_framework import Executor, WorkflowBuilder, WorkflowContext, handler
161
+
162
+ from agent_framework_community_postgres import PostgresPersistence
163
+
164
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
165
+
166
+
167
+ class Shout(Executor):
168
+ @handler
169
+ async def run(self, text: str, ctx: WorkflowContext[str, str]) -> None:
170
+ await ctx.yield_output(text.upper())
171
+
172
+
173
+ async def main() -> None:
174
+ async with PostgresPersistence(application_id="my-app", connection_string=DSN) as hub:
175
+ await hub.migrate()
176
+ storage = hub.checkpoint_storage(scope="conversation-1")
177
+ workflow = WorkflowBuilder(start_executor=Shout(id="shout"), checkpoint_storage=storage).build()
178
+ print((await workflow.run("hello")).get_outputs())
179
+ print(await storage.get_latest(workflow_name=workflow.name) is not None)
180
+
181
+
182
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
183
+ runner.run(main())
184
+ ```
185
+
186
+ Save the `AgentSession` with `PostgresSessionStore` to continue a conversation in another process. An application loads the session at the start of a request and saves it after `agent.run` returns.
187
+
188
+ ```python
189
+ import asyncio
190
+ import sys
191
+
192
+ from agent_framework import AgentSession
193
+
194
+ from agent_framework_community_postgres import PostgresPersistence
195
+
196
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
197
+
198
+
199
+ async def main() -> None:
200
+ async with PostgresPersistence(application_id="my-app", connection_string=DSN) as hub:
201
+ await hub.migrate()
202
+ sessions = hub.session_store()
203
+ session = await sessions.get("user-1:chat-1") or AgentSession(session_id="chat-1")
204
+ session.state["visits"] = session.state.get("visits", 0) + 1
205
+ await sessions.set("user-1:chat-1", session)
206
+ print(session.session_id, session.state)
207
+
208
+
209
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
210
+ runner.run(main())
211
+ ```
212
+
213
+ In an agent, create the new session with `agent.create_session(session_id=...)` instead of `AgentSession(...)`. See [docs/history.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/history.md) and [docs/sessions.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/sessions.md).
214
+
215
+ ## Standalone use
216
+
217
+ Each store also works on its own. A standalone store does not migrate, so create the tables first with `hub.migrate()` or the CLI.
218
+
219
+ ```bash
220
+ POSTGRES_CONNECTION_STRING=postgresql://postgres:postgres@127.0.0.1:5433/agent_framework python -m agent_framework_community_postgres migrate
221
+ ```
222
+
223
+ In PowerShell:
224
+
225
+ ```powershell
226
+ $env:POSTGRES_CONNECTION_STRING = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"; python -m agent_framework_community_postgres migrate
227
+ ```
228
+
229
+ ```python
230
+ import asyncio
231
+ import sys
232
+
233
+ from agent_framework import Message
234
+
235
+ from agent_framework_community_postgres import PostgresHistoryProvider
236
+
237
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
238
+
239
+
240
+ async def main() -> None:
241
+ async with PostgresHistoryProvider(application_id="my-app", connection_string=DSN) as history:
242
+ await history.save_messages("session-1", [Message(role="user", contents=["hello"])])
243
+ print([m.text for m in await history.get_messages("session-1")])
244
+
245
+
246
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
247
+ runner.run(main())
248
+ ```
249
+
250
+ Every store and the hub take the same connection arguments.
251
+
252
+ - `connection_string` or `client`. Pass exactly one. When both are absent, the connection string comes from `POSTGRES_CONNECTION_STRING`, read through MAF's `load_settings`. An explicit argument wins over the `.env` file named by `env_file_path`, which wins over the environment.
253
+ - `client` is a psycopg `AsyncConnection` or `AsyncConnectionPool`. A pool that is not open is opened on first use. The package never closes a client it did not create.
254
+ - `credential` signs the owned pool in to Azure Database for PostgreSQL with Microsoft Entra ID tokens. It needs a connection string and cannot be combined with `client`. With a credential, the pool defaults to `sslmode=verify-full` with `sslrootcert=system`. An `sslmode` or `sslrootcert` set in `pool_kwargs["kwargs"]`, the connection string, `PGSSLMODE` or `PGSSLROOTCERT` takes precedence over these defaults, and the store or hub raises `ValueError` when it is created if the `sslmode` in effect is `disable`, `allow` or `prefer`. An explicit `sslmode` also drops the `sslrootcert=system` default. On Windows, `system` finds no certificates in psycopg's binary wheel, so set `PGSSLROOTCERT` to a CA file; [docs/azure-entra.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/azure-entra.md) says which. See [Microsoft Entra ID on Azure](#microsoft-entra-id-on-azure).
255
+ - A single `AsyncConnection` runs one call at a time. Every store and hub over it waits for the same lock, so a server should pass a pool. On a connection already inside your own transaction, writes and multi-statement calls become a savepoint, single-statement reads run directly inside your transaction, and transaction-scoped locks (the history save lock and the migration lock) are held until your transaction ends.
256
+ - A connection string makes the store own a pool with `min_size=1`, `max_size=10`, autocommit, libpq `connect_timeout=10` unless the string sets one, and a pool `timeout` of 10 seconds.
257
+ - The owned pool also sets TCP keepalives (`keepalives=1`, `keepalives_idle=30`, `keepalives_interval=10`, `keepalives_count=3`) and `tcp_user_timeout=30000` unless the connection string sets them, so a silently dead connection fails within about a minute. The pool sets no statement or lock timeout; `migrate()` sets its own lock timeout.
258
+ - `pool_kwargs` is merged over the owned pool's defaults, and the `kwargs` entry inside it over the connection defaults; for example `{"max_size": 50, "kwargs": {"prepare_threshold": None}}` for PgBouncer. A connection parameter in `kwargs` wins over the same parameter in the connection string. It cannot be combined with `client`.
259
+ - `schema="public"` must be a lowercase identifier that does not start with `pg_`. `table_prefix="af_"` allows lowercase letters, digits and underscores, up to 28 bytes.
260
+
261
+ When the database is down or rejects the login, or every pooled connection stays busy, each call raises `PostgresStorageError("No PostgreSQL connection became available within 10 seconds: ...")` after about 10 seconds. psycopg_pool raises the same timeout in both cases, so the message names both. Over your own pool, the message gives that pool's `timeout`. The owned pool keeps retrying in the background and recovers when the database returns. psycopg_pool logs the driver's reason on the `psycopg.pool` logger. If you need other timeouts, set them through `pool_kwargs` or pass your own pool. A malformed connection string raises `PostgresStorageError("Invalid connection string.")` and never repeats libpq's text, which can contain the password.
262
+
263
+ A driver failure raises `PostgresStorageError` with the driver exception chained as the cause. Its `sqlstate` is the server's error code when there was one. Its `retryable` is `True` for SQLSTATE classes `08` (connection exceptions), `40` (serialization failures and deadlocks), `53` (insufficient resources, such as too many connections), codes `57P0x` (operator intervention, such as an administrator shutdown) and `55P03` (a lock timeout), for a connection lost without a SQLSTATE, and for the pool timeout above. A fresh call may get past these.
264
+
265
+ After a database restart or failover, each stale connection in the owned pool fails one call before the pool replaces it. Behind PgBouncer in transaction mode older than 1.21, or without `max_prepared_statements`, psycopg's prepared statements fail, because psycopg prepares a query after its fifth run. For either case, pass your own pool. `check=AsyncConnectionPool.check_connection` tests each connection as the pool hands it out, at the cost of one round trip, and `prepare_threshold=None` turns prepared statements off.
266
+
267
+ ```python
268
+ import asyncio
269
+ import sys
270
+
271
+ from psycopg_pool import AsyncConnectionPool
272
+
273
+ from agent_framework_community_postgres import PostgresPersistence
274
+
275
+ DSN = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
276
+
277
+
278
+ async def main() -> None:
279
+ pool = AsyncConnectionPool(
280
+ DSN,
281
+ open=False,
282
+ kwargs={"autocommit": True, "prepare_threshold": None},
283
+ check=AsyncConnectionPool.check_connection,
284
+ )
285
+ async with pool, PostgresPersistence(application_id="my-app", client=pool) as hub:
286
+ await hub.migrate()
287
+ print(await hub.pending_migrations())
288
+
289
+
290
+ with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop if sys.platform == "win32" else None) as runner:
291
+ runner.run(main())
292
+ ```
293
+
294
+ ## Microsoft Entra ID on Azure
295
+
296
+ With the `azure` extra, pass an `azure-identity` credential as `credential=` and the owned pool signs in to Azure Database for PostgreSQL with a Microsoft Entra ID token for each new connection, instead of a password:
297
+
298
+ ```python
299
+ from azure.identity.aio import DefaultAzureCredential
300
+
301
+ DSN = "host=my-server.postgres.database.azure.com dbname=agent_framework user=ada@contoso.com"
302
+
303
+
304
+ async def main() -> None:
305
+ async with (
306
+ DefaultAzureCredential() as credential,
307
+ PostgresPersistence(application_id="my-app", connection_string=DSN, credential=credential) as hub,
308
+ ):
309
+ await hub.migrate()
310
+ ```
311
+
312
+ A failing credential raises `PostgresStorageError` naming its error on the first call. A managed identity or a service principal has no user name in its token, so set `user=` to its database role.
313
+
314
+ This is verified with a fake credential against PostgreSQL 17 locally, and the CI matrix runs the same tests on PostgreSQL 16 and 17. No real Azure server has been used yet. [docs/azure-entra.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/azure-entra.md) covers the server setup, managed identities, token lifetime and the CLI.
315
+
316
+ ## Schema
317
+
318
+ Tables are created in `schema` and named `{table_prefix}{name}`. With the defaults:
319
+
320
+ | Table | Key | Holds |
321
+ |---|---|---|
322
+ | `af_history_messages` | `id`, read by application, tenant, agent, source and session | One `Message.to_dict()` per row |
323
+ | `af_sessions` | `application_id, session_id` | `AgentSession.to_dict()` |
324
+ | `af_checkpoints` | `application_id, tenant_id, scope, checkpoint_id` | The encoded `WorkflowCheckpoint` |
325
+ | `af_thread_snapshots` | `application_id, scope, thread_id` | The latest AG-UI thread snapshot |
326
+ | `af_leases` | `application_id, resource` | Lease owner, token and expiry |
327
+ | `af_migrations` | `version` | Applied migration versions |
328
+
329
+ ## Retention
330
+
331
+ Retention is off unless you pass `RetentionPolicy(ttl=...)`. With a TTL, every write sets `expires_at` to now plus the TTL, and `purge()` or the `purge` CLI command tombstones or deletes the rows whose `expires_at` has passed. History messages, checkpoints and expired leases are always deleted. Leases take no policy. A history session is purged whole, once every one of its messages has expired, so an active conversation keeps its transcript. A purge works in batches of 1000 rows, one transaction per batch, and skips rows another purge has locked. `hub.purge()` covers every table, leases included. It returns a `PurgeReport`; `report.counts` is a read-only mapping, so use `dict(report.counts)` for a copy you can change. Until a purge runs, reads still return expired rows. See [docs/retention.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/retention.md).
332
+
333
+ ## Private Agent Framework imports
334
+
335
+ The package uses three names MAF does not export. `filter_new_messages` deduplicates history, and `encode_checkpoint_value` and `decode_checkpoint_value` encode checkpoints. Microsoft's own Cosmos DB package imports all three, and its Redis package imports `filter_new_messages`. `_framework.py` is the only module that imports them, and `tests/unit/test_framework.py` fails when one of them moves. CI also runs the suite against the newest `agent-framework-core` the declared range allows, and a weekly canary runs it against the newest MAF release, so an upstream rename shows up there first. See [docs/compatibility.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/compatibility.md).
336
+
337
+ ## Development
338
+
339
+ ```bash
340
+ docker compose up -d --wait
341
+ export POSTGRES_TEST_CONNECTION_STRING=postgresql://postgres:postgres@127.0.0.1:5433/agent_framework
342
+ # PowerShell: $env:POSTGRES_TEST_CONNECTION_STRING = "postgresql://postgres:postgres@127.0.0.1:5433/agent_framework"
343
+ uv run pytest
344
+ uv run ruff check .
345
+ uv run ruff format --check .
346
+ uv run pyright
347
+ ```
348
+
349
+ Without `POSTGRES_TEST_CONNECTION_STRING`, the integration and conformance tests are skipped. See [CONTRIBUTING.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/CONTRIBUTING.md).
350
+
351
+ ## Documentation
352
+
353
+ - [docs/history.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/history.md) covers `PostgresHistoryProvider`.
354
+ - [docs/sessions.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/sessions.md) covers `PostgresSessionStore`.
355
+ - [docs/checkpoints.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/checkpoints.md) covers `PostgresCheckpointStorage`.
356
+ - [docs/thread-snapshots.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/thread-snapshots.md) covers `PostgresAGUIThreadSnapshotStore`.
357
+ - [docs/leases.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/leases.md) covers `PostgresLeases`.
358
+ - [docs/retention.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/retention.md) covers `RetentionPolicy` and purging.
359
+ - [docs/migrations.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/migrations.md) covers `migrate()`, the CLI and SQL for a DBA.
360
+ - [docs/azure-entra.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/azure-entra.md) covers Microsoft Entra ID sign-in on Azure Database for PostgreSQL.
361
+ - [docs/compatibility.md](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/docs/compatibility.md) covers versions, private imports, JSONB limits and Windows.
362
+
363
+ Runnable scripts for each store are in [samples/](https://github.com/matias-io/community-agent-framework-postgres/tree/v0.1.1/samples).
364
+
365
+ ## License
366
+
367
+ MIT. See [LICENSE](https://github.com/matias-io/community-agent-framework-postgres/blob/v0.1.1/LICENSE).
@@ -0,0 +1,21 @@
1
+ agent_framework_community_postgres/__init__.py,sha256=1EXV3in5WdfdYqn0CX0r425KhUaqwWXLRytoEgEpWrE,2195
2
+ agent_framework_community_postgres/__main__.py,sha256=g-q3cJEP2vv6Ppov3S-YHp3sJsGGZH4v3gYvvwdFOxg,49
3
+ agent_framework_community_postgres/_checkpoint_storage.py,sha256=uKW_nXx_ClE9z6jxYpNnpuSuKtwCH24gErna029Zg7E,10640
4
+ agent_framework_community_postgres/_cli.py,sha256=xd8caEm2Ifab2z6Gfl09cvj3ufvdBgNPMyvTN-ko0k0,4378
5
+ agent_framework_community_postgres/_client.py,sha256=TK7DpPyWEPJuzfy_kVlKGM9AxGzXXAEbKbrZJmD9xp4,18575
6
+ agent_framework_community_postgres/_entra.py,sha256=s45zJHYSUibbR4IRM3cpjiQ69gO4aq3k-orQRcxZUd4,7872
7
+ agent_framework_community_postgres/_framework.py,sha256=kEZZ6nwXRh74v_d-wnGYuxsUOtPXPtfmyQy3zQ2kQjc,5825
8
+ agent_framework_community_postgres/_history_provider.py,sha256=OOtVr3axvX06bVOroIWtoeyLcFA95GrvGm6s2FcxWiw,12443
9
+ agent_framework_community_postgres/_json.py,sha256=PrycccJ2WIKh8J7XxgPJ958hcjuNcnhGA8DRnWB2zOk,1526
10
+ agent_framework_community_postgres/_leases.py,sha256=JisCXs40Sz5A8AcZ1-kTY5-g7z5rOJG0RFKhWYnFGyA,8660
11
+ agent_framework_community_postgres/_migrations.py,sha256=qW6qX0rg2lVIcV4HTWxg0DLRrup4JZ4hbru4cr5CVLE,11201
12
+ agent_framework_community_postgres/_persistence.py,sha256=AMCt_Tmaxu2VcHcc2FN4TgqylremtW82BNyDIVUZimM,7941
13
+ agent_framework_community_postgres/_retention.py,sha256=pscxWfErYFFnZ1eqVTMSc_zWVTb_HR4NZ1GfLzOcVhQ,5917
14
+ agent_framework_community_postgres/_session_store.py,sha256=PcGu0VnIeZt7zDPwqh-qWtd0v0yeIOZdjmHB45KIMVE,5907
15
+ agent_framework_community_postgres/_store.py,sha256=WdegHovFy4VDxz0H4CZEWAAAxBl4hqoHliHJAdFg-mo,1985
16
+ agent_framework_community_postgres/_thread_snapshot_store.py,sha256=Ze9WAeEyE7k8j5Q8Tk3Z42v8MNh7gcvs1B5pUbDUQkE,7502
17
+ agent_framework_community_postgres/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ community_agent_framework_postgres-0.1.1.dist-info/METADATA,sha256=wN1Xp4DYDd6AueYLItpm_YvMPOvSNTtq6S5X8hwluBs,24439
19
+ community_agent_framework_postgres-0.1.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
20
+ community_agent_framework_postgres-0.1.1.dist-info/licenses/LICENSE,sha256=o29n5hirCtF4mmDf_uNd_dSLl0gaxsQiFwwRtRZnRJ0,1076
21
+ community_agent_framework_postgres-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matias Suxo Salinas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.