keel-context 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 (57) hide show
  1. keel_context-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +24 -0
  2. keel_context-0.1.0/.github/ISSUE_TEMPLATE/cartridge_proposal.yml +23 -0
  3. keel_context-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  4. keel_context-0.1.0/.github/workflows/docs.yml +27 -0
  5. keel_context-0.1.0/.github/workflows/release.yml +26 -0
  6. keel_context-0.1.0/.gitignore +9 -0
  7. keel_context-0.1.0/CLAUDE.md +81 -0
  8. keel_context-0.1.0/CONTRIBUTING.md +58 -0
  9. keel_context-0.1.0/LICENSE +202 -0
  10. keel_context-0.1.0/NOTICE +4 -0
  11. keel_context-0.1.0/PKG-INFO +178 -0
  12. keel_context-0.1.0/README.md +139 -0
  13. keel_context-0.1.0/ROADMAP.md +66 -0
  14. keel_context-0.1.0/cartridges/clinics/cartridge.yaml +45 -0
  15. keel_context-0.1.0/cartridges/clinics/handlers.py +28 -0
  16. keel_context-0.1.0/cartridges/crm/cartridge.yaml +50 -0
  17. keel_context-0.1.0/cartridges/crm/handlers.py +35 -0
  18. keel_context-0.1.0/cartridges/finance/cartridge.yaml +83 -0
  19. keel_context-0.1.0/cartridges/finance/handlers.py +59 -0
  20. keel_context-0.1.0/cartridges/fleet/cartridge.yaml +101 -0
  21. keel_context-0.1.0/cartridges/fleet/handlers.py +83 -0
  22. keel_context-0.1.0/docs/SPEC.md +353 -0
  23. keel_context-0.1.0/docs/connect.md +170 -0
  24. keel_context-0.1.0/docs/contributing.md +1 -0
  25. keel_context-0.1.0/docs/demo.gif +0 -0
  26. keel_context-0.1.0/docs/first-cartridge.md +119 -0
  27. keel_context-0.1.0/docs/index.md +93 -0
  28. keel_context-0.1.0/docs/roadmap.md +1 -0
  29. keel_context-0.1.0/examples/agent_minimal.py +35 -0
  30. keel_context-0.1.0/examples/demo.tape +53 -0
  31. keel_context-0.1.0/examples/events/clinics.jsonl +7 -0
  32. keel_context-0.1.0/examples/events/crm.jsonl +4 -0
  33. keel_context-0.1.0/examples/events/finance.jsonl +14 -0
  34. keel_context-0.1.0/examples/events/fleet.jsonl +13 -0
  35. keel_context-0.1.0/keel/__init__.py +28 -0
  36. keel_context-0.1.0/keel/adapters/__init__.py +7 -0
  37. keel_context-0.1.0/keel/adapters/postgres.py +108 -0
  38. keel_context-0.1.0/keel/adapters/webhook.py +38 -0
  39. keel_context-0.1.0/keel/cartridge.py +151 -0
  40. keel_context-0.1.0/keel/cli.py +182 -0
  41. keel_context-0.1.0/keel/envelope.py +72 -0
  42. keel_context-0.1.0/keel/handlers.py +56 -0
  43. keel_context-0.1.0/keel/ingest.py +79 -0
  44. keel_context-0.1.0/keel/mcp_server.py +67 -0
  45. keel_context-0.1.0/keel/rules.py +85 -0
  46. keel_context-0.1.0/keel/scopes.py +58 -0
  47. keel_context-0.1.0/keel/store/__init__.py +4 -0
  48. keel_context-0.1.0/keel/store/base.py +86 -0
  49. keel_context-0.1.0/keel/store/memory.py +163 -0
  50. keel_context-0.1.0/keel/store/sqlite.py +196 -0
  51. keel_context-0.1.0/keel/tools.py +132 -0
  52. keel_context-0.1.0/mkdocs.yml +51 -0
  53. keel_context-0.1.0/pyproject.toml +52 -0
  54. keel_context-0.1.0/tests/test_adapters.py +110 -0
  55. keel_context-0.1.0/tests/test_core.py +191 -0
  56. keel_context-0.1.0/tests/test_store.py +131 -0
  57. keel_context-0.1.0/uv.lock +2549 -0
@@ -0,0 +1,24 @@
1
+ name: Bug report
2
+ description: The core, the CLI or a shipped cartridge does not behave as documented.
3
+ labels: [bug]
4
+ body:
5
+ - type: textarea
6
+ id: what
7
+ attributes:
8
+ label: What happened
9
+ description: What you did, what you expected, what you got. A failing test or a `keel` command is ideal.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: events
14
+ attributes:
15
+ label: Events to reproduce
16
+ description: The smallest `.jsonl` stream (or table rows) that triggers it. Strip anything confidential.
17
+ render: json
18
+ - type: input
19
+ id: version
20
+ attributes:
21
+ label: Keel version and Python version
22
+ placeholder: keel 0.0.1, Python 3.12
23
+ validations:
24
+ required: true
@@ -0,0 +1,23 @@
1
+ name: Cartridge proposal
2
+ description: Propose a new vertical, or report that writing one needed a change under keel/.
3
+ labels: [cartridge]
4
+ body:
5
+ - type: input
6
+ id: vertical
7
+ attributes:
8
+ label: Vertical
9
+ placeholder: clinics, last-mile logistics, property management
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: ontology
14
+ attributes:
15
+ label: Entities, relations and the three most important event types
16
+ description: A sketch is enough. See cartridges/fleet/cartridge.yaml for the shape.
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: core
21
+ attributes:
22
+ label: Did the core get in the way?
23
+ description: Anything under keel/ you had to change or wished existed. This is the most valuable part.
@@ -0,0 +1,11 @@
1
+ ## What
2
+
3
+ ## Why
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `uv run pytest` and `uv run ruff check .` pass
8
+ - [ ] `cartridges/fleet` and `cartridges/crm` still validate without edits
9
+ - [ ] Core change: a test in `tests/` covers it. Cartridge change: events in `examples/events/` and assertions.
10
+ - [ ] Nothing under `keel/` names an entity kind, relation, predicate, rule or event type
11
+ - [ ] If `examples/agent_minimal.py` output changed, the reason is in this description
@@ -0,0 +1,27 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ paths:
7
+ - docs/**
8
+ - mkdocs.yml
9
+ - ROADMAP.md
10
+ - CONTRIBUTING.md
11
+ - .github/workflows/docs.yml
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ deploy:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - run: |
23
+ git config user.name github-actions[bot]
24
+ git config user.email 41898282+github-actions[bot]@users.noreply.github.com
25
+ - uses: astral-sh/setup-uv@v5
26
+ - run: uv sync --extra docs
27
+ - run: uv run mkdocs gh-deploy --force
@@ -0,0 +1,26 @@
1
+ name: release
2
+
3
+ # Publishes to PyPI with trusted publishing (no token stored here) when a v* tag is pushed.
4
+ # One-time setup on PyPI: add a pending publisher for project keel-context, owner angelkurten,
5
+ # repository keel, workflow release.yml, environment pypi.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ pypi:
16
+ runs-on: ubuntu-latest
17
+ environment: pypi
18
+ permissions:
19
+ id-token: write
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: astral-sh/setup-uv@v5
23
+ - run: uv sync --extra dev
24
+ - run: uv run pytest -q
25
+ - run: uv build
26
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ *.egg-info/
8
+ .env
9
+ site/
@@ -0,0 +1,81 @@
1
+ # Keel — project spec for coding agents
2
+
3
+ Read this before changing anything. It says what Keel is, what it is not, and the rules that keep the core generic. `ROADMAP.md` says what comes next.
4
+
5
+ ## What Keel is
6
+
7
+ A context layer for AI agents inside vertical SaaS products. Domain events from the SaaS go in; a bitemporal context graph **per end-customer** (the tenant of the tenant) comes out; every agent reads and writes it through the same five tools.
8
+
9
+ The domain is declared once in a **cartridge** (YAML + a few ten-line handlers). The core knows nothing about any domain.
10
+
11
+ Positioning, in one line: *stop rebuilding the pipeline for every agent.* "Shared memory for agents" is the consequence, never the headline.
12
+
13
+ ## What Keel is not
14
+
15
+ - Not an agent framework. Agents live in LangGraph, ADK, the Claude SDK, wherever. Keel is what they consult.
16
+ - Not a document/RAG memory. We ingest structured events with time, not text. Ingestion is deterministic. No LLM in the ingest path (see Open decisions).
17
+ - Not a copy of the SaaS's database. The graph holds pointers, semantics and earned memory.
18
+ - Not a single global memory. Memory has scopes and nothing is promoted by accident.
19
+
20
+ ## Architecture
21
+
22
+ ```
23
+ sources (Postgres table/CDC, webhooks, Kafka later)
24
+ -> Event envelope (keel/envelope.py) tenant, type, subject, occurred_at, recorded_at, source, payload
25
+ -> Ingestor (keel/ingest.py) reads an adapter, dispatches by event.type
26
+ -> HandlerRegistry (keel/handlers.py) cartridge handlers map payload -> store primitives
27
+ -> Store (keel/store/) bitemporal: entities, relations, facts; one subgraph per tenant (memory or sqlite)
28
+ -> Tools (keel/tools.py) context / history / search / annotate / related
29
+ -> MCP server (keel/mcp_server.py) optional; exposes the same five tools
30
+ cartridges/<name>/cartridge.yaml + handlers.py ontology, event catalog, rules, memory policy, handlers
31
+ ```
32
+
33
+ Store primitives are the only write path: `upsert_entity`, `link`, `unlink`, `open_fact`, `close_fact`. `open_fact` closes the previous open fact on the same predicate; nothing is overwritten.
34
+
35
+ ## Design rules (enforce these in review)
36
+
37
+ 1. **Events are the truth.** The log is append-only; the graph is derived and must be rebuildable with `Ingestor.replay(tenant)`. Any state that cannot be rebuilt from events is a bug.
38
+ 2. **Pointers, not copies.** An entity carries `ref` to the row in the system of record and the few attributes a tool needs to label it. If a PR copies a whole row or table into the graph, reject it.
39
+ 3. **Explicit time, bitemporal.** `valid_from`/`valid_to` = world time. `recorded_at` = when Keel learned it. Facts close; they are never mutated or deleted by handlers. "The tire was critical on Oct 3" and "the tire is critical" are different queries and both must work.
40
+ 4. **Provenance on every fact.** `source`, `evidence` (event id, row ref, photo), and `corrected_by` when a human or agent corrected it.
41
+ 5. **Scopes, not a flat store.** `global` > `tenant` > `task` > `agent`. Policy (TTL, evidence requirement) is enforced in `Tools.annotate` via `MemoryPolicy`, never trusted to the calling agent. Promotion to a wider scope is an explicit `annotate` with evidence.
42
+ 6. **The core is domain-agnostic.** No entity kind, relation name, predicate, rule or event type appears anywhere under `keel/`. The acceptance test for any core change: both `cartridges/fleet` and `cartridges/crm` still pass without edits. Adding a third cartridge must require zero changes under `keel/`.
43
+ 7. **Five tools, and only five.** New capabilities go into the semantics of these five (richer `context`, filters on `history`) before anyone adds a sixth. The tool names and signatures are part of the public standard.
44
+ 8. **One subgraph per tenant.** Every store call takes `tenant` first. Cross-tenant reads do not exist. A tool instance is bound to one tenant.
45
+ 9. **Handlers are small.** A handler maps one payload to primitives in ~10 lines. Business logic (what "critical" means) lives in `rules`, read by tools, not in handlers and not in agents.
46
+ 10. **Deterministic ingest.** No network calls, no LLM calls, no randomness in the ingest path. Replay must produce an identical graph.
47
+
48
+ ## Conventions
49
+
50
+ - Python 3.10+, `uv`, `pytest`, `ruff` (line length 100). Type hints everywhere; pydantic for the envelope only, dataclasses in the store.
51
+ - Subject keys are `<kind>:<id>` strings. Event types are dotted lowercase: `inspection.created`.
52
+ - Datetimes are timezone-aware UTC. Naive datetimes are rejected at the envelope.
53
+ - Public surface: `keel/__init__.py` exports. Anything else is internal and may change.
54
+ - All user-facing text (README, docs, CLI help, errors) is in English. Commit messages in English.
55
+ - Tests: every core change adds or updates a test in `tests/`; cartridge changes add events to `examples/events/*.jsonl` and assertions in `tests/test_core.py`.
56
+
57
+ ## How to verify a change
58
+
59
+ ```bash
60
+ uv sync --extra dev
61
+ uv run pytest
62
+ uv run keel validate cartridges/fleet && uv run keel validate cartridges/crm
63
+ uv run python examples/agent_minimal.py
64
+ ```
65
+
66
+ If `examples/agent_minimal.py` output changes, say why in the PR.
67
+
68
+ ## Open decisions (do not resolve silently)
69
+
70
+ - **Durable store.** Decided 2026-10-14: pluggable by design, never a core change. `Store` protocol (`keel/store/base.py`), `MemoryStore` as reference, `SqliteStore` as the default durable one, and a conformance suite any store can run (`KEEL_STORE=pkg.module:factory uv run pytest tests/test_store.py`). Graphiti or Postgres would be third implementations; if Graphiti forces an LLM in the ingest path, use only its graph store and keep our deterministic ingest.
71
+ - **Consolidation/decay.** Expiry is shipped (`expires_at`, `store.expire`, `keel expire`). Summarizing old episodic facts into durable ones is deliberately left open (2026-10-14) until a pilot brings a real case; the leading candidate is a deterministic job, never an LLM in the core.
72
+ - **Multi-cartridge tenants.** One tenant, one cartridge for now. Composing cartridges is a later decision.
73
+ - **Pricing unit** (hosted): events ingested per month. Not a code concern yet, but metering must be possible from the log.
74
+
75
+ ## Things to never do
76
+
77
+ - Never add a dependency to the core for one cartridge's convenience.
78
+ - Never let a tool return facts from another tenant.
79
+ - Never delete or mutate a fact from a handler. Close it.
80
+ - Never read `event.payload` in `keel/`. Only handlers do. One exception: `Cartridge.check_event` (behind `keel validate --events`) compares payload keys and types with the catalog; it never reads values.
81
+ - Never write generic placeholders (`TBD`, `n/a`) into cartridge YAML; leave the key out.
@@ -0,0 +1,58 @@
1
+ # Contributing to Keel
2
+
3
+ Keel is built in the open, part-time. Small, focused pull requests land fastest.
4
+
5
+ ## Set up
6
+
7
+ ```bash
8
+ uv sync --extra dev
9
+ uv run pytest
10
+ uv run keel validate cartridges/fleet --events examples/events/fleet.jsonl && uv run keel validate cartridges/crm --events examples/events/crm.jsonl && uv run keel validate cartridges/finance --events examples/events/finance.jsonl && uv run keel validate cartridges/clinics --events examples/events/clinics.jsonl
11
+ ```
12
+
13
+ ## What we are looking for
14
+
15
+ - **Cartridges.** The best contribution. A new vertical (clinics, logistics, property management, ...) in `cartridges/<name>/` with `cartridge.yaml`, `handlers.py`, a sample stream in `examples/events/<name>.jsonl` and a test in `tests/`. It must pass `keel validate` and require zero changes under `keel/`. If it does need a core change, open an issue first: that is a bug in the core, and we want to fix it generically.
16
+ - **Adapters.** Sources that yield `Event`s (see `keel/adapters/`). An adapter never reads a payload.
17
+ - **Bug reports with a failing test.** The fastest way to get something fixed.
18
+
19
+ ## Rules the review enforces
20
+
21
+ `CLAUDE.md` is the spec and `docs/SPEC.md` is the normative contract. The short version:
22
+
23
+ 1. No domain names under `keel/`: no entity kind, relation, predicate, rule or event type.
24
+ 2. Pointers, not copies. Never copy a row or a table into the graph.
25
+ 3. Facts close. Handlers never mutate or delete them.
26
+ 4. Five tools. New capability goes into the semantics of the existing five.
27
+ 5. Deterministic ingest: no network, no LLM, no randomness.
28
+ 6. Every core change comes with a test. Every cartridge change comes with events and assertions.
29
+
30
+ ## Process
31
+
32
+ - Open an issue before a large change. For a cartridge or a small fix, a pull request is fine.
33
+ - Run `uv run ruff check .` and `uv run pytest` before pushing.
34
+ - Commit messages and all user-facing text in English.
35
+ - If `examples/agent_minimal.py` output changes, say why in the pull request.
36
+
37
+ By contributing you agree that your contributions are licensed under the Apache License 2.0.
38
+
39
+ ## Your own store
40
+
41
+ The core talks to a `Store` protocol (`keel/store/base.py`); `MemoryStore` is the reference and `SqliteStore` the default durable one. A third store (Postgres, Graphiti, anything) is a separate package, never a core change. Run the conformance suite against it:
42
+
43
+ ```bash
44
+ KEEL_STORE=your_pkg.module:factory uv run pytest tests/test_store.py
45
+ ```
46
+
47
+ `factory` is a zero-argument callable or class that returns a fresh, empty store.
48
+
49
+ ## Releasing
50
+
51
+ Bump `version` in `pyproject.toml` and `__version__` in `keel/__init__.py`, update the roadmap, then:
52
+
53
+ ```bash
54
+ git tag -a vX.Y.Z -m "keel-context X.Y.Z" && git push origin vX.Y.Z
55
+ gh release create vX.Y.Z --title "X.Y.Z" --notes-file notes.md
56
+ ```
57
+
58
+ The tag triggers `.github/workflows/release.yml`, which runs the tests, builds and publishes `keel-context` to PyPI through trusted publishing (one-time setup on PyPI: pending publisher for project `keel-context`, owner `angelkurten`, repository `keel`, workflow `release.yml`, environment `pypi`).
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,4 @@
1
+ Keel
2
+ Copyright 2026 Angel Kürten / Kubbo Software, Inc.
3
+
4
+ Licensed under the Apache License, Version 2.0. See LICENSE.
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.5
2
+ Name: keel-context
3
+ Version: 0.1.0
4
+ Summary: Context layer for AI agents in vertical SaaS: domain events in, a per-tenant bitemporal context graph out, shared by every agent through five MCP tools.
5
+ Project-URL: Homepage, https://github.com/angelkurten/keel
6
+ Project-URL: Documentation, https://angelkurten.github.io/keel/
7
+ Author-email: Angel Kürten <angel@angelkurten.com>
8
+ License: Apache-2.0
9
+ License-File: LICENSE
10
+ License-File: NOTICE
11
+ Keywords: agents,bitemporal,context,event-driven,knowledge-graph,mcp
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: pydantic>=2.6
18
+ Requires-Dist: pyyaml>=6.0
19
+ Requires-Dist: typer>=0.12
20
+ Provides-Extra: dev
21
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
22
+ Requires-Dist: httpx>=0.27; extra == 'dev'
23
+ Requires-Dist: mcp>=1.2; extra == 'dev'
24
+ Requires-Dist: pytest>=8.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.5; extra == 'dev'
26
+ Provides-Extra: docs
27
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
28
+ Requires-Dist: mkdocs<2; extra == 'docs'
29
+ Provides-Extra: graphiti
30
+ Requires-Dist: graphiti-core>=0.5; extra == 'graphiti'
31
+ Provides-Extra: mcp
32
+ Requires-Dist: mcp>=1.2; extra == 'mcp'
33
+ Provides-Extra: postgres
34
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
35
+ Provides-Extra: webhook
36
+ Requires-Dist: fastapi>=0.110; extra == 'webhook'
37
+ Requires-Dist: uvicorn>=0.27; extra == 'webhook'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # Keel
41
+
42
+ **Stop rebuilding the pipeline for every agent.**
43
+
44
+ Docs: [angelkurten.github.io/keel](https://angelkurten.github.io/keel/) · Why: [We built four agents and four copies of our customer context](https://angelkurten.com/blog/keel-four-agents-four-copies-of-context)
45
+
46
+ Keel is a context layer for AI agents inside vertical SaaS products. Domain events from your product go in; a bitemporal context graph per end-customer comes out; every agent you build reads and writes it through the same five MCP tools.
47
+
48
+ You describe your vertical once, in a *cartridge* (ontology, event catalog, semantic rules, memory policies). Keel's core knows nothing about your domain. Your second agent connects in a day instead of weeks, because there is no second pipeline to write.
49
+
50
+ > Status: 0.1.0, alpha. The MCP server (`keel serve`), the Postgres table adapter and the webhook adapter run against the in-memory store or the SQLite store (`--db keel.db`, stdlib, no server to run).
51
+
52
+ ![Terminal demo: validate the stream against the cartridge, ingest, ask for a tire's context with the rule's verdict, run an agent with no LLM](docs/demo.gif)
53
+
54
+ ## The problem
55
+
56
+ Every new agent in a SaaS product repeats the same work: extract the customer's data, normalize it, decide what "critical" means, keep its own memory, expose its own tools to the model. Four agents, four copies of the customer's context, four definitions of "critical", four memories that never talk.
57
+
58
+ Existing memory layers (Zep, Mem0, AgentCore Memory, Neo4j's MKG) ingest text and documents and model memory per user or per agent. None of them start from **domain events**, and none model the **tenant of the tenant**: the end-customer of a B2B SaaS.
59
+
60
+ ## How Keel is different
61
+
62
+ 1. **Cartridges.** A vertical is declared in YAML plus a few ten-line handlers. The core stays generic. Write a cartridge for fleets, clinics, logistics, or your own product.
63
+ 2. **Events in, not documents.** Keel ingests structured facts with time (Postgres tables, webhooks, Kafka later). Ingestion is deterministic and cheap. The graph is exact because it is born from events, not inferred from text.
64
+ 3. **Every fact carries its provenance.** Source, `valid_from`, `valid_to`, `recorded_at`, and who corrected it. Audit-ready by construction.
65
+ 4. **Tenant of the tenant.** One subgraph per end-customer, with its own permissions and expiry.
66
+
67
+ ## Five tools, any agent
68
+
69
+ ```
70
+ context(subject, as_of?) -> what is true now, or at as_of: facts, relations, rules and their verdicts
71
+ history(subject, since?, until?, predicate?, scope?) -> what was true, and when we learned it
72
+ search(text) -> entities and facts matching text
73
+ annotate(subject, predicate, value, scope, evidence) -> an agent (or a human) writes what it learned
74
+ related(subject, relation?) -> neighbors by relation type
75
+ ```
76
+
77
+ Expose them over MCP (`keel serve`) and point any agent framework at them.
78
+
79
+ ## Quick start
80
+
81
+ ```bash
82
+ uv sync --extra dev
83
+ uv run keel validate cartridges/fleet --events examples/events/fleet.jsonl
84
+ uv run keel ingest cartridges/fleet examples/events/fleet.jsonl
85
+ uv run keel context cartridges/fleet examples/events/fleet.jsonl unit:MXK-4821
86
+ ```
87
+
88
+ Then write your own cartridge: see [docs/first-cartridge.md](docs/first-cartridge.md) (20 minutes).
89
+
90
+ In your own product it is a library: `uv add keel-context` (or `pip install keel-context`); the import is `import keel`.
91
+
92
+ ## Connect a real event stream
93
+
94
+ Point an MCP client at the five tools:
95
+
96
+ ```bash
97
+ uv sync --extra mcp
98
+ uv run keel serve cartridges/fleet --tenant acme-logistics --events examples/events/fleet.jsonl
99
+ ```
100
+
101
+ With Claude Code, one command registers it and the agent sees exactly five tools:
102
+
103
+ ```bash
104
+ claude mcp add keel -- uv run --directory $PWD keel serve cartridges/fleet --tenant acme-logistics --events examples/events/fleet.jsonl
105
+ ```
106
+
107
+ Codex CLI (OpenAI) works the same way from `~/.codex/config.toml`; the four read tools are annotated read-only, so it runs them without prompting. Both clients answered the same questions through the same five tools: see [connect](docs/connect.md#serve-the-tools-over-mcp).
108
+
109
+ Poll an outbox table in your Postgres (a cursor file makes restarts resume, not re-ingest):
110
+
111
+ ```python
112
+ import psycopg
113
+ from keel.adapters.postgres import PostgresTableAdapter
114
+
115
+ adapter = PostgresTableAdapter(lambda: psycopg.connect(DSN), table="keel_events", cursor_path=".keel/cursor")
116
+ ingestor = Ingestor(cartridge, SqliteStore("keel.db")) # durable; MemoryStore() for a throwaway graph
117
+ ingestor.ingest(adapter.follow(interval=5))
118
+ ```
119
+
120
+ Or receive envelopes over HTTP (`uv sync --extra webhook`):
121
+
122
+ ```python
123
+ from keel.adapters.webhook import build_app
124
+
125
+ app = build_app(ingestor, token="shared-secret") # POST /events, Authorization: Bearer <token>
126
+ ```
127
+
128
+ The table shape and the envelope fields are in `keel/adapters/postgres.py` and `keel/envelope.py`. `keel export <tenant> --db keel.db` writes one tenant's whole audit trail as JSONL, closed facts and provenance included.
129
+
130
+ ## Architecture
131
+
132
+ ```
133
+ your SaaS (Postgres) webhooks Kafka / SQS (later)
134
+ \ | /
135
+ v v v
136
+ +-------------------------------------------------+ +------------------+
137
+ | event envelope: tenant, type, subject, time | | cartridge |
138
+ | ingestor + handler registry | <--- | ontology |
139
+ | bitemporal context graph, one subgraph/tenant | | event catalog |
140
+ | memory scopes: global / tenant / task / agent | | handlers |
141
+ | five MCP tools | | semantic rules |
142
+ +-------------------------------------------------+ | memory policies |
143
+ | | | +------------------+
144
+ agent A agent B agent N (no pipeline)
145
+ ```
146
+
147
+ Design rules the core enforces:
148
+
149
+ - **Events are the truth.** An append-only log; the graph is derived and can be replayed.
150
+ - **Pointers, not copies.** The graph holds references to your rows, the semantics, and earned memory. Never a copy of your tables.
151
+ - **Explicit time.** Facts close, they are not overwritten.
152
+ - **Scopes, not a flat store.** Nothing is promoted to a wider scope by accident.
153
+
154
+ ## Repository layout
155
+
156
+ ```
157
+ keel/ core: envelope, handlers, store, scopes, cartridge loader, ingest, tools, cli
158
+ cartridges/ fleet (reference), crm, finance and clinics: four domains, one core, no core change between them
159
+ examples/ sample event streams and a minimal agent
160
+ docs/ site sources (mkdocs.yml at the root): first-cartridge, connect, SPEC
161
+ tests/
162
+ ```
163
+
164
+ ## Roadmap
165
+
166
+ - [x] Event envelope and handler registry
167
+ - [x] In-memory bitemporal store
168
+ - [x] Memory scopes with expiry
169
+ - [x] Five tools as plain functions
170
+ - [x] MCP server (`keel serve`)
171
+ - [x] Durable store: SQLite (Graphiti still planned for graph-native queries)
172
+ - [x] Postgres (table + polling) and webhook adapters
173
+ - [ ] Consolidation and decay jobs
174
+ - [ ] Cartridge catalog
175
+
176
+ ## License
177
+
178
+ Apache 2.0. The event envelope, the cartridge format and the five tools are the standard; they will always be free.