agentos-ledger-client 0.1.0__tar.gz

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.
Files changed (33) hide show
  1. agentos_ledger_client-0.1.0/PKG-INFO +156 -0
  2. agentos_ledger_client-0.1.0/README.md +142 -0
  3. agentos_ledger_client-0.1.0/pyproject.toml +66 -0
  4. agentos_ledger_client-0.1.0/setup.cfg +4 -0
  5. agentos_ledger_client-0.1.0/src/agentos_ledger_client/__init__.py +69 -0
  6. agentos_ledger_client-0.1.0/src/agentos_ledger_client/blobs.py +359 -0
  7. agentos_ledger_client-0.1.0/src/agentos_ledger_client/bootstrap.py +61 -0
  8. agentos_ledger_client-0.1.0/src/agentos_ledger_client/chain.py +98 -0
  9. agentos_ledger_client-0.1.0/src/agentos_ledger_client/client.py +245 -0
  10. agentos_ledger_client-0.1.0/src/agentos_ledger_client/integration.py +117 -0
  11. agentos_ledger_client-0.1.0/src/agentos_ledger_client/journal.py +245 -0
  12. agentos_ledger_client-0.1.0/src/agentos_ledger_client/py.typed +0 -0
  13. agentos_ledger_client-0.1.0/src/agentos_ledger_client/schema.py +297 -0
  14. agentos_ledger_client-0.1.0/src/agentos_ledger_client/schema.sql +112 -0
  15. agentos_ledger_client-0.1.0/src/agentos_ledger_client/testing.py +124 -0
  16. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/__init__.py +30 -0
  17. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/conftest.py +91 -0
  18. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_a_bare_database_becomes_a_ledger.py +294 -0
  19. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_a_placeholder_dsn_is_not_a_database.py +125 -0
  20. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_blobs.py +178 -0
  21. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_chain.py +67 -0
  22. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_client.py +44 -0
  23. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_dsn_for_survives_a_unix_socket.py +68 -0
  24. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_journal.py +137 -0
  25. agentos_ledger_client-0.1.0/src/agentos_ledger_client/tests/test_schema.py +17 -0
  26. agentos_ledger_client-0.1.0/src/agentos_ledger_client/types.py +107 -0
  27. agentos_ledger_client-0.1.0/src/agentos_ledger_client.egg-info/PKG-INFO +156 -0
  28. agentos_ledger_client-0.1.0/src/agentos_ledger_client.egg-info/SOURCES.txt +31 -0
  29. agentos_ledger_client-0.1.0/src/agentos_ledger_client.egg-info/dependency_links.txt +1 -0
  30. agentos_ledger_client-0.1.0/src/agentos_ledger_client.egg-info/requires.txt +9 -0
  31. agentos_ledger_client-0.1.0/src/agentos_ledger_client.egg-info/top_level.txt +1 -0
  32. agentos_ledger_client-0.1.0/tests/test_the_package_is_safe_to_depend_on.py +150 -0
  33. agentos_ledger_client-0.1.0/tests/test_the_shipped_schema_matches_the_migrations.py +292 -0
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentos-ledger-client
3
+ Version: 0.1.0
4
+ Summary: Canonical AgentOS State Ledger client and content-addressed blob storage
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: asyncpg>=0.29.0
8
+ Provides-Extra: gcs
9
+ Requires-Dist: google-cloud-storage>=2.0.0; extra == "gcs"
10
+ Provides-Extra: test
11
+ Requires-Dist: pytest>=8.0.0; extra == "test"
12
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
13
+ Requires-Dist: pgserver>=0.1.4; extra == "test"
14
+
15
+ # agentos-ledger-client
16
+
17
+ Canonical AgentOS State Ledger client, hash-chain verification, and content-addressed blob storage.
18
+
19
+ ## Features
20
+
21
+ - **Advisory-Locked Journal Appends**: Uses canonical `GLOBAL_CHAIN_LOCK_ID = 0x4C454447` to serialize concurrent writers and eliminate silent chain forks.
22
+ - **SHA-256 Hash Chaining**: Verifiable hash chain where `integrity_hash = sha256(previous_hash + payload_json)`.
23
+ - **Envelope Digest**: Secondary actor-bound digest `sha256(event_type + actor_id + actor_type + on_behalf_of + payload_json)` ensuring tamper-evident provenance.
24
+ - **Content-Addressed Blob Storage**: Tiered binary blob storage with deduplication:
25
+ - Payloads $\le$ 1MB stored inline (`BYTEA`).
26
+ - Payloads $>$ 1MB stored in Google Cloud Storage (`gs://...`).
27
+ - **Complete Schema Definitions**: Canonical event namespace constants for tasks, goals, iam, approval, and rlagt events.
28
+ - **Lightweight Dependencies**: Standard library + `asyncpg`. Optional GCS client support.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install agentos-ledger-client
34
+ ```
35
+
36
+ That is the whole thing. It is on PyPI, so there is no index to
37
+ configure, no credential to obtain, and nothing to do differently on a
38
+ laptop, in CI, or inside an AgentOS execution sandbox — PyPI is already
39
+ reachable from all three.
40
+
41
+ Blobs over 1MB need Google Cloud Storage; at or under 1MB they are
42
+ stored inline as BYTEA and nothing external is required:
43
+
44
+ ```bash
45
+ pip install "agentos-ledger-client[gcs]"
46
+ ```
47
+
48
+ **As a dependency**, declare it the ordinary way and pin the series:
49
+
50
+ ```toml
51
+ dependencies = [ "agentos-ledger-client>=0.1,<0.2" ]
52
+ ```
53
+
54
+ ### Versions
55
+
56
+ Releases are cut automatically and only when the code changes. The
57
+ build asks PyPI for the highest `0.1.x`, compares that release's
58
+ contents with the tree, and publishes `0.1.<n+1>` only if they differ —
59
+ so a version number always means the same bytes, and a run of builds
60
+ that did not touch this package produces no releases at all.
61
+
62
+ `>=0.1,<0.2` therefore resolves to the newest build of the current
63
+ series. The series moves to `0.2` only for a breaking change, which is
64
+ the one judgement nobody automates.
65
+
66
+ Published from `agent_os_refactor_dev` alone. Other branches never
67
+ publish, so a version cannot mean two different things depending on
68
+ where it was built.
69
+
70
+ **If you are changing this package** — person or agent — change the
71
+ code and stop there. Do not set `__version__`: `scripts/ci/publish_ledger_client.py`
72
+ writes it, after reading what PyPI already has, and a hand-set number
73
+ either collides with one that is taken or is overwritten. A PyPI
74
+ version is burned forever, so a collision is not recoverable.
75
+
76
+ Raise it explicitly if you break the public API, since moving the
77
+ series is the one decision here that is not automatic. The full rules
78
+ are in `CONTRIBUTING.md` under *Releasing agentos-ledger-client*.
79
+
80
+
81
+ ## Quick Start
82
+
83
+ ```python
84
+ import asyncpg
85
+ from agentos_ledger_client import (
86
+ chain_hash,
87
+ GLOBAL_CHAIN_LOCK_ID,
88
+ append_event,
89
+ store_blob,
90
+ read_blob,
91
+ )
92
+
93
+
94
+ async def example(conn: asyncpg.Connection):
95
+ # Store blob
96
+ ref = await store_blob(conn, b"example payload", content_type="text/plain")
97
+ print(ref.sha256, ref.storage_backend)
98
+
99
+ # Append ledger event
100
+ res = await append_event(
101
+ conn,
102
+ event_type="task.created",
103
+ payload={"title": "Test task", "blob_sha256": ref.sha256},
104
+ actor_id=my_actor_id,
105
+ actor_type="independent_agent", # one of:
106
+ # human | shca | independent_agent | system (CHECK constraint)
107
+ )
108
+ print(res["event_id"], res["integrity_hash"])
109
+ ```
110
+
111
+ `actor_id` is `UUID NOT NULL` and `actor_type` is constrained to the four
112
+ values above — a consumer whose actors are strings (`"ecommerce-sim"`)
113
+ needs a stable UUID per actor and must pick one of the four types.
114
+
115
+ ## Creating the tables
116
+
117
+ ```python
118
+ from agentos_ledger_client import init_schema
119
+
120
+ await init_schema(conn) # idempotent; safe in a test fixture
121
+
122
+ ```
123
+
124
+ ## Checking it against your own database
125
+
126
+ The suite that verifies this client ships *inside* it, so you can run it
127
+ where it matters — against the Postgres you intend to use, on your
128
+ Python, without cloning `pwt_agent_os`.
129
+
130
+ ```bash
131
+ pip install "agentos-ledger-client[test]"
132
+ pytest --pyargs agentos_ledger_client
133
+ ```
134
+
135
+ **No database setup is required.** The `[test]` extra brings `pgserver`,
136
+ a real PostgreSQL shipped as a wheel, and the fixtures start one and
137
+ tear it down. To point the suite at a server you already have, set
138
+ `DATABASE_URL` instead — it is used when reachable and ignored when not,
139
+ so a stale value degrades to a skip rather than a confusing error.
140
+
141
+ **Why bother.** What you share with AgentOS is a *hash-chained* table. A
142
+ schema disagreement does not announce itself as "the schema differs" —
143
+ it surfaces later as a chain that will not verify, by which point the
144
+ rows exist. This suite builds the schema from the shipped `schema.sql`
145
+ on an empty database, appends a chained event, and verifies it.
146
+
147
+ It is not a hypothetical check. `schema.sql` shipped a `CHECK` on
148
+ `actor_type` that refused `goal_orchestrator`, `executor` and
149
+ `verifier` — 29% of the events on a live deployment — because it was
150
+ copied from the first migration and two later ones widened it. AgentOS
151
+ never noticed: its own tables come from `migrations/`, not from this
152
+ file. The first person to execute it would have been a consumer.
153
+
154
+ Requires Python 3.12: `pgserver` publishes cp312 wheels and no sdist.
155
+ The library itself has no such constraint at runtime, but the project
156
+ declares 3.12 because that is the only version it is tested on.
@@ -0,0 +1,142 @@
1
+ # agentos-ledger-client
2
+
3
+ Canonical AgentOS State Ledger client, hash-chain verification, and content-addressed blob storage.
4
+
5
+ ## Features
6
+
7
+ - **Advisory-Locked Journal Appends**: Uses canonical `GLOBAL_CHAIN_LOCK_ID = 0x4C454447` to serialize concurrent writers and eliminate silent chain forks.
8
+ - **SHA-256 Hash Chaining**: Verifiable hash chain where `integrity_hash = sha256(previous_hash + payload_json)`.
9
+ - **Envelope Digest**: Secondary actor-bound digest `sha256(event_type + actor_id + actor_type + on_behalf_of + payload_json)` ensuring tamper-evident provenance.
10
+ - **Content-Addressed Blob Storage**: Tiered binary blob storage with deduplication:
11
+ - Payloads $\le$ 1MB stored inline (`BYTEA`).
12
+ - Payloads $>$ 1MB stored in Google Cloud Storage (`gs://...`).
13
+ - **Complete Schema Definitions**: Canonical event namespace constants for tasks, goals, iam, approval, and rlagt events.
14
+ - **Lightweight Dependencies**: Standard library + `asyncpg`. Optional GCS client support.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install agentos-ledger-client
20
+ ```
21
+
22
+ That is the whole thing. It is on PyPI, so there is no index to
23
+ configure, no credential to obtain, and nothing to do differently on a
24
+ laptop, in CI, or inside an AgentOS execution sandbox — PyPI is already
25
+ reachable from all three.
26
+
27
+ Blobs over 1MB need Google Cloud Storage; at or under 1MB they are
28
+ stored inline as BYTEA and nothing external is required:
29
+
30
+ ```bash
31
+ pip install "agentos-ledger-client[gcs]"
32
+ ```
33
+
34
+ **As a dependency**, declare it the ordinary way and pin the series:
35
+
36
+ ```toml
37
+ dependencies = [ "agentos-ledger-client>=0.1,<0.2" ]
38
+ ```
39
+
40
+ ### Versions
41
+
42
+ Releases are cut automatically and only when the code changes. The
43
+ build asks PyPI for the highest `0.1.x`, compares that release's
44
+ contents with the tree, and publishes `0.1.<n+1>` only if they differ —
45
+ so a version number always means the same bytes, and a run of builds
46
+ that did not touch this package produces no releases at all.
47
+
48
+ `>=0.1,<0.2` therefore resolves to the newest build of the current
49
+ series. The series moves to `0.2` only for a breaking change, which is
50
+ the one judgement nobody automates.
51
+
52
+ Published from `agent_os_refactor_dev` alone. Other branches never
53
+ publish, so a version cannot mean two different things depending on
54
+ where it was built.
55
+
56
+ **If you are changing this package** — person or agent — change the
57
+ code and stop there. Do not set `__version__`: `scripts/ci/publish_ledger_client.py`
58
+ writes it, after reading what PyPI already has, and a hand-set number
59
+ either collides with one that is taken or is overwritten. A PyPI
60
+ version is burned forever, so a collision is not recoverable.
61
+
62
+ Raise it explicitly if you break the public API, since moving the
63
+ series is the one decision here that is not automatic. The full rules
64
+ are in `CONTRIBUTING.md` under *Releasing agentos-ledger-client*.
65
+
66
+
67
+ ## Quick Start
68
+
69
+ ```python
70
+ import asyncpg
71
+ from agentos_ledger_client import (
72
+ chain_hash,
73
+ GLOBAL_CHAIN_LOCK_ID,
74
+ append_event,
75
+ store_blob,
76
+ read_blob,
77
+ )
78
+
79
+
80
+ async def example(conn: asyncpg.Connection):
81
+ # Store blob
82
+ ref = await store_blob(conn, b"example payload", content_type="text/plain")
83
+ print(ref.sha256, ref.storage_backend)
84
+
85
+ # Append ledger event
86
+ res = await append_event(
87
+ conn,
88
+ event_type="task.created",
89
+ payload={"title": "Test task", "blob_sha256": ref.sha256},
90
+ actor_id=my_actor_id,
91
+ actor_type="independent_agent", # one of:
92
+ # human | shca | independent_agent | system (CHECK constraint)
93
+ )
94
+ print(res["event_id"], res["integrity_hash"])
95
+ ```
96
+
97
+ `actor_id` is `UUID NOT NULL` and `actor_type` is constrained to the four
98
+ values above — a consumer whose actors are strings (`"ecommerce-sim"`)
99
+ needs a stable UUID per actor and must pick one of the four types.
100
+
101
+ ## Creating the tables
102
+
103
+ ```python
104
+ from agentos_ledger_client import init_schema
105
+
106
+ await init_schema(conn) # idempotent; safe in a test fixture
107
+
108
+ ```
109
+
110
+ ## Checking it against your own database
111
+
112
+ The suite that verifies this client ships *inside* it, so you can run it
113
+ where it matters — against the Postgres you intend to use, on your
114
+ Python, without cloning `pwt_agent_os`.
115
+
116
+ ```bash
117
+ pip install "agentos-ledger-client[test]"
118
+ pytest --pyargs agentos_ledger_client
119
+ ```
120
+
121
+ **No database setup is required.** The `[test]` extra brings `pgserver`,
122
+ a real PostgreSQL shipped as a wheel, and the fixtures start one and
123
+ tear it down. To point the suite at a server you already have, set
124
+ `DATABASE_URL` instead — it is used when reachable and ignored when not,
125
+ so a stale value degrades to a skip rather than a confusing error.
126
+
127
+ **Why bother.** What you share with AgentOS is a *hash-chained* table. A
128
+ schema disagreement does not announce itself as "the schema differs" —
129
+ it surfaces later as a chain that will not verify, by which point the
130
+ rows exist. This suite builds the schema from the shipped `schema.sql`
131
+ on an empty database, appends a chained event, and verifies it.
132
+
133
+ It is not a hypothetical check. `schema.sql` shipped a `CHECK` on
134
+ `actor_type` that refused `goal_orchestrator`, `executor` and
135
+ `verifier` — 29% of the events on a live deployment — because it was
136
+ copied from the first migration and two later ones widened it. AgentOS
137
+ never noticed: its own tables come from `migrations/`, not from this
138
+ file. The first person to execute it would have been a consumer.
139
+
140
+ Requires Python 3.12: `pgserver` publishes cp312 wheels and no sdist.
141
+ The library itself has no such constraint at runtime, but the project
142
+ declares 3.12 because that is the only version it is tested on.
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentos-ledger-client"
7
+ # THE VERSION LIVES IN `__init__.py`, and is read from there.
8
+ #
9
+ # It was declared in both places. Two declarations of one fact drift,
10
+ # and the one that drifts here is invisible: a wheel built from a
11
+ # pyproject saying 0.1.0 while the module says 0.1.7 installs as
12
+ # 0.1.0 and reports 0.1.7 when asked. Nothing errors.
13
+ #
14
+ # CI rewrites the single line in `__init__.py` before building --
15
+ # `0.1.<commits touching this package>` -- so the published version,
16
+ # the installed version and `agentos_ledger_client.__version__` are
17
+ # necessarily the same string.
18
+ dynamic = ["version"]
19
+ description = "Canonical AgentOS State Ledger client and content-addressed blob storage"
20
+ readme = "README.md"
21
+ # 3.12, NOT ">=3.10", WHICH WE NEVER TESTED. Nothing that uses this
22
+ # package runs below 3.12: the app image, the sandbox image and
23
+ # multi-agent-RL are all 3.12, and the only 3.11 image in the
24
+ # repository (the task runner) does not import it. The old floor was
25
+ # an untested compatibility claim on a package whose whole purpose is
26
+ # being depended on -- the wrong place for optimism, as the Linux
27
+ # bootstrap and the actor_type CHECK both demonstrated.
28
+ #
29
+ # It also makes the test extra below honest: `pgserver` publishes
30
+ # cp312 wheels and no sdist, so on 3.10 or 3.11 the embedded database
31
+ # is simply unavailable and the conformance suite could not run.
32
+ requires-python = ">=3.12"
33
+ dependencies = [
34
+ "asyncpg>=0.29.0",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ gcs = [
39
+ "google-cloud-storage>=2.0.0",
40
+ ]
41
+ # `pip install "agentos-ledger-client[test]"` must yield a RUNNABLE
42
+ # suite. It used to give pytest and no database, so every test that
43
+ # matters -- the ones that put the schema on a real Postgres and append
44
+ # a chained event -- skipped, and the install looked fine.
45
+ test = [
46
+ "pytest>=8.0.0",
47
+ "pytest-asyncio>=0.23.0",
48
+ # An actual PostgreSQL, shipped as a wheel: no Docker, no system
49
+ # install, no port to remember. This is what makes
50
+ # `pytest --pyargs agentos_ledger_client` work on a consumer's
51
+ # machine with nothing set up.
52
+ "pgserver>=0.1.4",
53
+ ]
54
+
55
+ [tool.setuptools.dynamic]
56
+ version = {attr = "agentos_ledger_client.__version__"}
57
+
58
+ [tool.setuptools.packages.find]
59
+ where = ["src"]
60
+
61
+ [tool.setuptools.package-data]
62
+ agentos_ledger_client = ["py.typed", "schema.sql"]
63
+
64
+ [tool.pytest.ini_options]
65
+ asyncio_mode = "strict"
66
+ testpaths = ["src/agentos_ledger_client/tests", "tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,69 @@
1
+ """AgentOS State Ledger Client Package.
2
+
3
+ Lightweight client and content-addressed blob storage for the AgentOS State Ledger.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from agentos_ledger_client.blobs import (
8
+ INLINE_THRESHOLD_BYTES,
9
+ STORAGE_GCS,
10
+ STORAGE_INLINE,
11
+ BlobRef,
12
+ get_blob_ref,
13
+ get_blob_ref_by_sha256,
14
+ read_blob,
15
+ read_blob_by_sha256,
16
+ store_blob,
17
+ )
18
+ from agentos_ledger_client.bootstrap import init_schema, schema_sql
19
+ from agentos_ledger_client.chain import (
20
+ GENESIS,
21
+ GLOBAL_CHAIN_LOCK_ID,
22
+ chain_hash,
23
+ envelope_digest,
24
+ new_event_id,
25
+ payload_json,
26
+ )
27
+ from agentos_ledger_client.client import LedgerClient
28
+ from agentos_ledger_client.journal import (
29
+ append_event,
30
+ generate_event_id,
31
+ register_post_append_hook,
32
+ register_pre_append_hook,
33
+ )
34
+ from agentos_ledger_client.types import AppendResult, ChainHead, LedgerEvent
35
+
36
+ __version__ = "0.1.0"
37
+
38
+ __all__ = [
39
+ "init_schema",
40
+ "schema_sql",
41
+ # Chain & Lock
42
+ "GENESIS",
43
+ "GLOBAL_CHAIN_LOCK_ID",
44
+ "chain_hash",
45
+ "envelope_digest",
46
+ "new_event_id",
47
+ "payload_json",
48
+ # Journal
49
+ "append_event",
50
+ "generate_event_id",
51
+ "register_pre_append_hook",
52
+ "register_post_append_hook",
53
+ # Blobs
54
+ "BlobRef",
55
+ "store_blob",
56
+ "read_blob",
57
+ "get_blob_ref",
58
+ "get_blob_ref_by_sha256",
59
+ "read_blob_by_sha256",
60
+ "INLINE_THRESHOLD_BYTES",
61
+ "STORAGE_INLINE",
62
+ "STORAGE_GCS",
63
+ # Client
64
+ "LedgerClient",
65
+ # Types
66
+ "AppendResult",
67
+ "ChainHead",
68
+ "LedgerEvent",
69
+ ]