iranti 0.2.32 → 0.2.33

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.
package/README.md CHANGED
@@ -1,61 +1,61 @@
1
- # Iranti
2
-
3
- [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html)
4
- [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
- [![TypeScript](https://img.shields.io/badge/typescript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
- [![CrewAI Compatible](https://img.shields.io/badge/CrewAI-compatible-green.svg)](https://www.crewai.com/)
7
-
8
- **Memory infrastructure for multi-agent AI systems.**
9
-
10
- Iranti gives agents persistent, identity-based memory. Facts written by one agent are retrievable by any other agent through exact entity+key lookup. Iranti also supports hybrid search (lexical + vector) when exact keys are unknown. Memory persists across sessions and survives context window limits.
11
-
1
+ # Iranti
2
+
3
+ [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html)
4
+ [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
+ [![TypeScript](https://img.shields.io/badge/typescript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![CrewAI Compatible](https://img.shields.io/badge/CrewAI-compatible-green.svg)](https://www.crewai.com/)
7
+
8
+ **Memory infrastructure for multi-agent AI systems.**
9
+
10
+ Iranti gives agents persistent, identity-based memory. Facts written by one agent are retrievable by any other agent through exact entity+key lookup. Iranti also supports hybrid search (lexical + vector) when exact keys are unknown. Memory persists across sessions and survives context window limits.
11
+
12
12
  **Latest release:** [`v0.2.21`](https://github.com/nfemmanuel/iranti/releases/tag/v0.2.21)
13
13
  Published packages:
14
14
  - `iranti@0.2.21`
15
15
  - `@iranti/sdk@0.2.21`
16
-
17
- ---
18
-
19
- ## What is Iranti?
20
-
21
- Iranti is a knowledge base for multi-agent systems. The primary read path is identity retrieval — this specific entity (`project/nexus_prime`), this specific key (`deadline`), with confidence attached. When Agent A writes a fact, Agent B can retrieve it by exact lookup without being told it exists. Facts persist in PostgreSQL and survive context window boundaries through the `observe()` API. For discovery workflows, Iranti supports hybrid search (full-text + vector similarity).
22
-
23
- ---
24
-
25
- ## Runtime Roles
26
-
27
- - **User**: Person who interacts with an app or chatbot built on Iranti.
28
- - **Agent**: External AI worker that writes/reads facts through Iranti APIs.
29
- - **Attendant**: Per-agent memory manager that decides what to inject for each turn.
30
- - **Librarian**: Conflict-aware writer that owns all KB writes.
31
- - **Library**: Active truth store (`knowledge_base`) in PostgreSQL.
32
- - **Archive**: Historical/superseded truth store (`archive`) in PostgreSQL.
33
- - **Archivist**: Maintenance worker that archives stale/low-confidence facts and processes resolved escalations.
34
- - **Resolutionist**: Interactive CLI reviewer that guides humans through pending escalation files and writes valid authoritative resolutions.
35
-
36
- ---
37
-
38
- ## Why Not a Vector Database?
39
-
40
- | Feature | Vector DB | Iranti |
41
- |---|---|---|
42
- | **Retrieval** | Similarity (nearest neighbor) | Identity-first + optional hybrid search |
43
- | **Storage** | Embeddings in vector space | Structured facts with keys |
44
- | **Persistence** | Stateless between calls | Persistent across sessions |
45
- | **Confidence** | No confidence tracking | Per-fact confidence scores |
46
- | **Conflicts** | No conflict resolution | Automatic resolution + escalation |
47
- | **Context** | No context awareness | `observe()` injects missing facts |
48
-
49
- Vector databases answer "what's similar to X?" Iranti answers "what do we know about X?" and can run hybrid search when exact keys are unknown.
50
-
51
- ---
52
-
53
- ## Benchmark Summary
54
-
16
+
17
+ ---
18
+
19
+ ## What is Iranti?
20
+
21
+ Iranti is a knowledge base for multi-agent systems. The primary read path is identity retrieval — this specific entity (`project/nexus_prime`), this specific key (`deadline`), with confidence attached. When Agent A writes a fact, Agent B can retrieve it by exact lookup without being told it exists. Facts persist in PostgreSQL and survive context window boundaries through the `observe()` API. For discovery workflows, Iranti supports hybrid search (full-text + vector similarity).
22
+
23
+ ---
24
+
25
+ ## Runtime Roles
26
+
27
+ - **User**: Person who interacts with an app or chatbot built on Iranti.
28
+ - **Agent**: External AI worker that writes/reads facts through Iranti APIs.
29
+ - **Attendant**: Per-agent memory manager that decides what to inject for each turn.
30
+ - **Librarian**: Conflict-aware writer that owns all KB writes.
31
+ - **Library**: Active truth store (`knowledge_base`) in PostgreSQL.
32
+ - **Archive**: Historical/superseded truth store (`archive`) in PostgreSQL.
33
+ - **Archivist**: Maintenance worker that archives stale/low-confidence facts and processes resolved escalations.
34
+ - **Resolutionist**: Interactive CLI reviewer that guides humans through pending escalation files and writes valid authoritative resolutions.
35
+
36
+ ---
37
+
38
+ ## Why Not a Vector Database?
39
+
40
+ | Feature | Vector DB | Iranti |
41
+ |---|---|---|
42
+ | **Retrieval** | Similarity (nearest neighbor) | Identity-first + optional hybrid search |
43
+ | **Storage** | Embeddings in vector space | Structured facts with keys |
44
+ | **Persistence** | Stateless between calls | Persistent across sessions |
45
+ | **Confidence** | No confidence tracking | Per-fact confidence scores |
46
+ | **Conflicts** | No conflict resolution | Automatic resolution + escalation |
47
+ | **Context** | No context awareness | `observe()` injects missing facts |
48
+
49
+ Vector databases answer "what's similar to X?" Iranti answers "what do we know about X?" and can run hybrid search when exact keys are unknown.
50
+
51
+ ---
52
+
53
+ ## Benchmark Summary
54
+
55
55
  Iranti has now been rerun against a broader benchmark program covering 11 active capability tracks in `v0.2.21`. The current picture is stronger and narrower than the early validation story: exact, durable, shared memory is benchmark-backed; broad semantic-memory and autonomous-memory claims still need tighter boundaries.
56
-
57
- ### Confirmed Strengths
58
-
56
+
57
+ ### Confirmed Strengths
58
+
59
59
  - **Exact lookup (`iranti_query`)**: Retrieval remains exact and durable across genuine session and process breaks. At tested scale (`N=1938`, about `107k` tokens), the measured advantage is efficiency, not accuracy: Iranti answered `10/10` with zero haystack tokens while the baseline also answered `10/10` after reading the full document.
60
60
  - **Persistence across sessions**: Facts survive context-window loss and genuine process boundaries. `iranti_query` remained `8/8` across isolated session breaks in the rerun.
61
61
  - **Conflict handling**: Reliable when confidence differentials are large and explicit.
@@ -72,10 +72,10 @@ Iranti has now been rerun against a broader benchmark program covering 11 active
72
72
  - **Observe performs better with explicit entity hints than with cold-start discovery.**
73
73
  - **Upgrade durability should be scoped carefully.** The `v0.2.21` upgrade procedure reinitialized the instance under test; do not assume KB data survives upgrades without an explicit preservation or migration path.
74
74
  - **Relationship and provenance reflection surfaces remain partially permission-gated in benchmark sessions.** The rerun did not prove `iranti_relate`, `iranti_related`, `iranti_related_deep`, or `iranti_who_knows` end-to-end under the benchmark session policy.
75
-
76
- ### Practical Position
77
-
78
- Iranti is strongest today as **structured memory infrastructure for multi-agent systems**:
75
+
76
+ ### Practical Position
77
+
78
+ Iranti is strongest today as **structured memory infrastructure for multi-agent systems**:
79
79
  - exact entity/key lookup
80
80
  - durable shared memory
81
81
  - provenance-aware writes
@@ -83,21 +83,21 @@ Iranti is strongest today as **structured memory infrastructure for multi-agent
83
83
  - session-aware recovery
84
84
 
85
85
  It should not yet be described as a fully general semantic-memory, semantic-search, or autonomous-memory-injection system.
86
-
87
- Historical benchmark material remains available here:
88
- - [`docs/internal/validation_results.md`](docs/internal/validation_results.md)
89
- - [`docs/internal/MULTI_FRAMEWORK_VALIDATION.md`](docs/internal/MULTI_FRAMEWORK_VALIDATION.md)
90
- - [`docs/internal/conflict_benchmark.md`](docs/internal/conflict_benchmark.md)
91
- - [`docs/internal/consistency_model.md`](docs/internal/consistency_model.md)
92
-
93
- ## Gap Analysis
94
-
95
- Iranti targets a specific gap in the agent infrastructure stack: most competing systems give you semantic retrieval, framework-specific memory, or raw vector storage, but not the same combination of structured fact storage, cross-agent sharing, identity-based lookup, explicit confidence, and developer-visible conflict handling in one self-hostable package.
96
-
97
- The current competitive case for Iranti is strongest when a team needs memory that behaves more like shared infrastructure than a chat transcript: facts are attached to entities, retrieved deterministically by `entityType/entityId + key`, versioned over time, and made available across agents without framework lock-in.
98
-
99
- ### Where Iranti Is Differentiated
100
-
86
+
87
+ Historical benchmark material remains available here:
88
+ - [`docs/internal/validation_results.md`](docs/internal/validation_results.md)
89
+ - [`docs/internal/MULTI_FRAMEWORK_VALIDATION.md`](docs/internal/MULTI_FRAMEWORK_VALIDATION.md)
90
+ - [`docs/internal/conflict_benchmark.md`](docs/internal/conflict_benchmark.md)
91
+ - [`docs/internal/consistency_model.md`](docs/internal/consistency_model.md)
92
+
93
+ ## Gap Analysis
94
+
95
+ Iranti targets a specific gap in the agent infrastructure stack: most competing systems give you semantic retrieval, framework-specific memory, or raw vector storage, but not the same combination of structured fact storage, cross-agent sharing, identity-based lookup, explicit confidence, and developer-visible conflict handling in one self-hostable package.
96
+
97
+ The current competitive case for Iranti is strongest when a team needs memory that behaves more like shared infrastructure than a chat transcript: facts are attached to entities, retrieved deterministically by `entityType/entityId + key`, versioned over time, and made available across agents without framework lock-in.
98
+
99
+ ### Where Iranti Is Differentiated
100
+
101
101
  - Identity-first fact retrieval through `entityType/entityId + key`
102
102
  - Cross-agent fact sharing as a first-class model
103
103
  - Conflict-aware writes through the Librarian
@@ -108,58 +108,58 @@ The current competitive case for Iranti is strongest when a team needs memory th
108
108
  - Hybrid retrieval when exact keys are unknown
109
109
  - Local install + project binding flow for Claude Code and Codex
110
110
  - Published npm / PyPI surfaces with machine-level CLI setup
111
-
112
- ### Why That Gap Exists
113
-
114
- The current landscape splits into three buckets:
115
-
116
- 1. **Memory libraries**
117
- - Systems like Mem0, Zep, Letta, and framework-native memory layers solve parts of the problem.
118
- - They usually optimize for semantic retrieval, agent-local memory, or framework integration.
119
- - They rarely expose deterministic `entity + key` lookup, explicit confidence surfaces, and developer-controlled conflict handling together.
120
-
121
- 2. **Vector databases**
122
- - Pinecone, Weaviate, Qdrant, Chroma, Milvus, LanceDB, and `pgvector` solve storage and retrieval infrastructure.
123
- - They do not, by themselves, solve memory semantics such as conflict resolution, context injection, fact lifecycle, or shared agent-facing state.
124
-
125
- 3. **Multi-agent frameworks**
126
- - CrewAI, LangGraph, AutoGen, CAMEL, MetaGPT, and similar frameworks often include some memory support.
127
- - In practice, that memory is usually framework-coupled, shallow on conflict semantics, and difficult to reuse outside the framework that created it.
128
-
129
- ### Main Gaps
130
-
131
- 1. **Operational maturity**
132
- - Local PostgreSQL setup is still a real source of friction.
133
- - The product needs stronger diagnostics, connection recovery, and less dependence on users debugging local database state by hand.
134
-
135
- 2. **Onboarding still has sharp edges**
136
- - `iranti setup` is materially better than before, but first-run still assumes too much infrastructure literacy.
137
- - Managed Postgres paths, cleaner bootstrap verification, and fewer environment-level surprises are still needed.
138
-
139
- 3. **No operator UI yet**
140
- - Iranti is still CLI-first.
141
- - There is no control plane yet for provider keys, project bindings, integrations, memory inspection, and escalation review.
142
-
143
- 4. **Adoption proof is still early**
144
- - The repo has validation experiments and real local end-to-end usage, but broad production adoption is still limited.
145
- - The next product truth has to come from external users and real workloads, not more speculative architecture alone.
146
-
147
- 5. **Hosted product is not built**
148
- - Open-source/local infrastructure is the active surface today.
149
- - Hosted deployment, multi-tenant operations, billing, and cloud onboarding remain future work.
150
-
151
- 6. **Graph-native reasoning is still limited**
152
- - Iranti supports explicit entity relationships today.
153
- - It does not yet compete with graph-first systems on temporal graph traversal or graph-native reasoning depth.
154
-
155
- 7. **Memory extraction is not the main model**
156
- - Iranti supports structured writes and ingest/chunking, but it is not primarily a "dump arbitrary conversations in and auto-magically derive perfect memory" system.
157
- - That is a deliberate tradeoff in favor of explicit, inspectable facts, but it increases integration work.
158
-
159
- ### Current Position
160
-
161
- Iranti is strongest today as infrastructure for developers building multi-agent systems who need shared, structured, queryable memory rather than pure semantic recall. The current benchmark base now supports a more concrete product claim:
162
-
111
+
112
+ ### Why That Gap Exists
113
+
114
+ The current landscape splits into three buckets:
115
+
116
+ 1. **Memory libraries**
117
+ - Systems like Mem0, Zep, Letta, and framework-native memory layers solve parts of the problem.
118
+ - They usually optimize for semantic retrieval, agent-local memory, or framework integration.
119
+ - They rarely expose deterministic `entity + key` lookup, explicit confidence surfaces, and developer-controlled conflict handling together.
120
+
121
+ 2. **Vector databases**
122
+ - Pinecone, Weaviate, Qdrant, Chroma, Milvus, LanceDB, and `pgvector` solve storage and retrieval infrastructure.
123
+ - They do not, by themselves, solve memory semantics such as conflict resolution, context injection, fact lifecycle, or shared agent-facing state.
124
+
125
+ 3. **Multi-agent frameworks**
126
+ - CrewAI, LangGraph, AutoGen, CAMEL, MetaGPT, and similar frameworks often include some memory support.
127
+ - In practice, that memory is usually framework-coupled, shallow on conflict semantics, and difficult to reuse outside the framework that created it.
128
+
129
+ ### Main Gaps
130
+
131
+ 1. **Operational maturity**
132
+ - Local PostgreSQL setup is still a real source of friction.
133
+ - The product needs stronger diagnostics, connection recovery, and less dependence on users debugging local database state by hand.
134
+
135
+ 2. **Onboarding still has sharp edges**
136
+ - `iranti setup` is materially better than before, but first-run still assumes too much infrastructure literacy.
137
+ - Managed Postgres paths, cleaner bootstrap verification, and fewer environment-level surprises are still needed.
138
+
139
+ 3. **No operator UI yet**
140
+ - Iranti is still CLI-first.
141
+ - There is no control plane yet for provider keys, project bindings, integrations, memory inspection, and escalation review.
142
+
143
+ 4. **Adoption proof is still early**
144
+ - The repo has validation experiments and real local end-to-end usage, but broad production adoption is still limited.
145
+ - The next product truth has to come from external users and real workloads, not more speculative architecture alone.
146
+
147
+ 5. **Hosted product is not built**
148
+ - Open-source/local infrastructure is the active surface today.
149
+ - Hosted deployment, multi-tenant operations, billing, and cloud onboarding remain future work.
150
+
151
+ 6. **Graph-native reasoning is still limited**
152
+ - Iranti supports explicit entity relationships today.
153
+ - It does not yet compete with graph-first systems on temporal graph traversal or graph-native reasoning depth.
154
+
155
+ 7. **Memory extraction is not the main model**
156
+ - Iranti supports structured writes and ingest/chunking, but it is not primarily a "dump arbitrary conversations in and auto-magically derive perfect memory" system.
157
+ - That is a deliberate tradeoff in favor of explicit, inspectable facts, but it increases integration work.
158
+
159
+ ### Current Position
160
+
161
+ Iranti is strongest today as infrastructure for developers building multi-agent systems who need shared, structured, queryable memory rather than pure semantic recall. The current benchmark base now supports a more concrete product claim:
162
+
163
163
  - exact cross-agent fact transfer works at meaningful context scales
164
164
  - facts survive session loss and genuine process breaks
165
165
  - same-key conflicting writes are serialized and observable
@@ -167,30 +167,30 @@ Iranti is strongest today as infrastructure for developers building multi-agent
167
167
  - attended recovery works with explicit hints, while autonomous attend classification remains a known defect
168
168
 
169
169
  That is still not a claim that multi-agent memory is solved. It is a claim that Iranti now has broader evidence for durable, structured, attribution-aware memory with exact retrieval and bounded recovery behavior.
170
-
171
- The next leverage is still product simplicity: setup, operations, and day-to-day inspection need to be simple enough that real users keep Iranti in the loop.
172
-
173
- ## Quickstart
174
-
170
+
171
+ The next leverage is still product simplicity: setup, operations, and day-to-day inspection need to be simple enough that real users keep Iranti in the loop.
172
+
173
+ ## Quickstart
174
+
175
175
  **Requirements**: Node.js 18+, PostgreSQL with pgvector support, Python 3.8+
176
-
177
- Docker is optional. It is one local way to run PostgreSQL if you do not already have a database. Iranti still requires PostgreSQL; the setup improvement is smarter bootstrap and clearer guidance, not a second storage backend.
178
-
179
- ```bash
180
- # 1. Install the CLI
181
- npm install -g iranti
182
-
183
- # 2. Run the guided setup
184
- iranti setup
185
-
186
- # 3. Start the instance
187
- iranti run --instance local
188
- ```
189
-
190
- `iranti setup` now defaults to an isolated per-project runtime. Shared machine-level instances are still supported, but they are now an explicit choice rather than the default.
191
-
176
+
177
+ Docker is optional. It is one local way to run PostgreSQL if you do not already have a database. Iranti still requires PostgreSQL; the setup improvement is smarter bootstrap and clearer guidance, not a second storage backend.
178
+
179
+ ```bash
180
+ # 1. Install the CLI
181
+ npm install -g iranti
182
+
183
+ # 2. Run the guided setup
184
+ iranti setup
185
+
186
+ # 3. Start the instance
187
+ iranti run --instance local
188
+ ```
189
+
190
+ `iranti setup` now defaults to an isolated per-project runtime. Shared machine-level instances are still supported, but they are now an explicit choice rather than the default.
191
+
192
192
  If local PostgreSQL is available and pgvector-capable, setup can bootstrap a localhost database for you. If Docker is available, setup now prefers the Docker path over a plain local listener because it guarantees pgvector. If local PostgreSQL is reachable but does not provide pgvector, setup now fails early with a direct action path instead of a late Prisma migration error.
193
-
193
+
194
194
  Long-running agents can now checkpoint and recover interrupted work. Programmatic session lifecycle methods are available through the SDK and REST API:
195
195
  - `checkpoint()`
196
196
  - `inspectSession()`
@@ -198,16 +198,16 @@ Long-running agents can now checkpoint and recover interrupted work. Programmati
198
198
  - `resumeSession()`
199
199
  - `completeSession()`
200
200
  - `abandonSession()`
201
-
202
- Running instances now publish runtime metadata in `/health`, and the CLI can see that state through `iranti status`, `iranti instance show`, and `iranti upgrade --check`. When you want an installed upgrade to immediately take effect on an instance-backed API server, use:
203
-
204
- ```bash
205
- iranti upgrade --restart --instance local
206
- ```
207
-
201
+
202
+ Running instances now publish runtime metadata in `/health`, and the CLI can see that state through `iranti status`, `iranti instance show`, and `iranti upgrade --check`. When you want an installed upgrade to immediately take effect on an instance-backed API server, use:
203
+
204
+ ```bash
205
+ iranti upgrade --restart --instance local
206
+ ```
207
+
208
208
  If something still fails and you need more detail, use:
209
-
210
- ```bash
209
+
210
+ ```bash
211
211
  iranti doctor --debug
212
212
  iranti run --instance local --debug
213
213
  iranti upgrade --verbose
@@ -221,187 +221,187 @@ iranti uninstall --all --yes
221
221
  ```
222
222
 
223
223
  Default uninstall keeps runtime data and project bindings. `--all` removes discovered runtime roots plus project-local Iranti integrations.
224
-
225
- Advanced/manual path:
226
-
227
- ```bash
228
- # 1. Clone and configure
229
- git clone https://github.com/nfemmanuel/iranti
230
- cd iranti
231
- cp .env.example .env # Set DATABASE_URL and IRANTI_API_KEY
232
-
233
- # Optional runtime hygiene
234
- # IRANTI_ESCALATION_DIR=C:/Users/<you>/.iranti/escalation
235
- # IRANTI_ARCHIVIST_WATCH=true
236
- # IRANTI_ARCHIVIST_DEBOUNCE_MS=60000
237
- # IRANTI_ARCHIVIST_INTERVAL_MS=21600000
238
-
239
- # 2. Start PostgreSQL
240
- docker-compose up -d
241
-
242
- # 3. Install and initialize
243
- npm install
244
- npm run setup # Runs migrations
245
-
246
- # 4. Start API server
247
- npm run api # Runs on port 3001
248
-
249
- # 5. Install Python client
250
- pip install iranti
251
-
252
- # Optional: install the TypeScript client
253
- npm install @iranti/sdk
254
- ```
255
-
256
- ### Archivist Scheduling Knobs
257
-
258
- - `IRANTI_ARCHIVIST_WATCH=true` enables file-change watching on escalation `active/`.
259
- - `IRANTI_ARCHIVIST_DEBOUNCE_MS=60000` runs maintenance 60s after the latest file change.
260
- - `IRANTI_ARCHIVIST_INTERVAL_MS=21600000` runs maintenance every 6 hours (set `0` to disable).
261
- - `IRANTI_ESCALATION_DIR` sets escalation storage root. Default is `~/.iranti/escalation`, keeping escalation files out of the repo by default.
262
-
263
- ### Per-User API Keys (Recommended)
264
-
265
- ```bash
266
- # Create a key for one user/app (prints token once)
267
- npm run api-key:create -- --key-id chatbot_alice --owner "Alice chatbot" --scopes "kb:read,kb:write,memory:read,memory:write,agents:read,agents:write"
268
-
269
- # List keys
270
- npm run api-key:list
271
-
272
- # Revoke a key
273
- npm run api-key:revoke -- --key-id chatbot_alice
274
- ```
275
-
276
- Use the printed token (`keyId.secret`) as `X-Iranti-Key`.
277
- Scopes use `resource:action` format (for example `kb:read`, `memory:write`, `metrics:read`, `proxy:chat`).
278
-
279
- ### Security Baseline
280
-
281
- - Use one scoped key per app/service identity.
282
- - Rotate any key that is exposed in logs, screenshots, or chat.
283
- - Keep escalation/log paths outside the repo working tree.
284
- - Use TLS/reverse proxy for non-local deployments.
285
-
286
- Security quickstart: [`docs/guides/security-quickstart.md`](docs/guides/security-quickstart.md)
287
- Operator manual: [`docs/guides/manual.md`](docs/guides/manual.md)
288
- Claude Code guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
289
- Codex guide: [`docs/guides/codex.md`](docs/guides/codex.md)
290
- Release guide: [`docs/guides/releasing.md`](docs/guides/releasing.md)
291
- Vector backend guide: [`docs/guides/vector-backends.md`](docs/guides/vector-backends.md)
292
-
293
- ### Claude Code via MCP
294
-
295
- Iranti ships a local stdio MCP server for Claude Code and other MCP clients:
296
-
297
- ```bash
298
- iranti mcp
299
- ```
300
-
301
- Use it with a project-local `.mcp.json`, and optionally add `iranti claude-hook` for `SessionStart` and `UserPromptSubmit`.
302
-
303
- Fast path:
304
-
305
- ```bash
306
- iranti claude-setup
307
- ```
308
-
309
- Guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
310
-
311
- ### Codex via MCP
312
-
224
+
225
+ Advanced/manual path:
226
+
227
+ ```bash
228
+ # 1. Clone and configure
229
+ git clone https://github.com/nfemmanuel/iranti
230
+ cd iranti
231
+ cp .env.example .env # Set DATABASE_URL and IRANTI_API_KEY
232
+
233
+ # Optional runtime hygiene
234
+ # IRANTI_ESCALATION_DIR=C:/Users/<you>/.iranti/escalation
235
+ # IRANTI_ARCHIVIST_WATCH=true
236
+ # IRANTI_ARCHIVIST_DEBOUNCE_MS=60000
237
+ # IRANTI_ARCHIVIST_INTERVAL_MS=21600000
238
+
239
+ # 2. Start PostgreSQL
240
+ docker-compose up -d
241
+
242
+ # 3. Install and initialize
243
+ npm install
244
+ npm run setup # Runs migrations
245
+
246
+ # 4. Start API server
247
+ npm run api # Runs on port 3001
248
+
249
+ # 5. Install Python client
250
+ pip install iranti
251
+
252
+ # Optional: install the TypeScript client
253
+ npm install @iranti/sdk
254
+ ```
255
+
256
+ ### Archivist Scheduling Knobs
257
+
258
+ - `IRANTI_ARCHIVIST_WATCH=true` enables file-change watching on escalation `active/`.
259
+ - `IRANTI_ARCHIVIST_DEBOUNCE_MS=60000` runs maintenance 60s after the latest file change.
260
+ - `IRANTI_ARCHIVIST_INTERVAL_MS=21600000` runs maintenance every 6 hours (set `0` to disable).
261
+ - `IRANTI_ESCALATION_DIR` sets escalation storage root. Default is `~/.iranti/escalation`, keeping escalation files out of the repo by default.
262
+
263
+ ### Per-User API Keys (Recommended)
264
+
265
+ ```bash
266
+ # Create a key for one user/app (prints token once)
267
+ npm run api-key:create -- --key-id chatbot_alice --owner "Alice chatbot" --scopes "kb:read,kb:write,memory:read,memory:write,agents:read,agents:write"
268
+
269
+ # List keys
270
+ npm run api-key:list
271
+
272
+ # Revoke a key
273
+ npm run api-key:revoke -- --key-id chatbot_alice
274
+ ```
275
+
276
+ Use the printed token (`keyId.secret`) as `X-Iranti-Key`.
277
+ Scopes use `resource:action` format (for example `kb:read`, `memory:write`, `metrics:read`, `proxy:chat`).
278
+
279
+ ### Security Baseline
280
+
281
+ - Use one scoped key per app/service identity.
282
+ - Rotate any key that is exposed in logs, screenshots, or chat.
283
+ - Keep escalation/log paths outside the repo working tree.
284
+ - Use TLS/reverse proxy for non-local deployments.
285
+
286
+ Security quickstart: [`docs/guides/security-quickstart.md`](docs/guides/security-quickstart.md)
287
+ Operator manual: [`docs/guides/manual.md`](docs/guides/manual.md)
288
+ Claude Code guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
289
+ Codex guide: [`docs/guides/codex.md`](docs/guides/codex.md)
290
+ Release guide: [`docs/guides/releasing.md`](docs/guides/releasing.md)
291
+ Vector backend guide: [`docs/guides/vector-backends.md`](docs/guides/vector-backends.md)
292
+
293
+ ### Claude Code via MCP
294
+
295
+ Iranti ships a local stdio MCP server for Claude Code and other MCP clients:
296
+
297
+ ```bash
298
+ iranti mcp
299
+ ```
300
+
301
+ Use it with a project-local `.mcp.json`, and optionally add `iranti claude-hook` for `SessionStart` and `UserPromptSubmit`.
302
+
303
+ Fast path:
304
+
305
+ ```bash
306
+ iranti claude-setup
307
+ ```
308
+
309
+ Guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
310
+
311
+ ### Codex via MCP
312
+
313
313
  Codex uses a global MCP registry rather than a project-local `.mcp.json`. Register Iranti once, then launch Codex in the bound project so `.env.iranti` is in scope:
314
-
315
- ```bash
314
+
315
+ ```bash
316
316
  iranti codex-setup
317
317
  codex -C /path/to/your/project
318
318
  ```
319
319
 
320
320
  By default, `iranti codex-setup` does not pin a project binding globally. `iranti mcp` resolves `.env.iranti` from the active project/workspace at runtime. Use `--project-env` only if you deliberately want to pin Codex globally to one project binding.
321
-
322
- Alias:
323
-
324
- ```bash
325
- iranti integrate codex
326
- ```
327
-
328
- Guide: [`docs/guides/codex.md`](docs/guides/codex.md)
329
-
330
- ### Resolve Pending Escalations
331
-
332
- Review unresolved human-escalation files from the CLI:
333
-
334
- ```bash
335
- iranti resolve
336
- ```
337
-
338
- Use `--dir` to point at a non-default escalation root. Guide: [`docs/guides/conflict-resolution.md`](docs/guides/conflict-resolution.md)
339
-
340
- ### Native Chat
341
-
342
- Start a CLI chat session against the configured Iranti instance:
343
-
344
- ```bash
345
- iranti chat
346
- ```
347
-
348
- Use `--agent`, `--provider`, and `--model` to pin the session identity and model routing.
349
- The chat surface now includes slash commands for fact history, relationships, conflict-resolution handoff, and confidence updates in addition to memory search/write operations.
350
- Guide: [`docs/guides/chat.md`](docs/guides/chat.md)
351
-
352
- ### Manual Attendant Inspection
353
-
354
- For debugging and operator visibility, Iranti also exposes manual Attendant commands:
355
-
356
- ```bash
357
- iranti handshake --task "Working on ProofScript repo"
358
- iranti attend "What did we decide about the parser?" --context-file transcript.txt
359
- ```
360
-
361
- Both commands accept `--json`.
362
- They are useful for verifying what the Attendant would load or inject for a given agent and project binding.
363
- They are not a replacement for Claude Code hooks or MCP tools in normal use.
364
-
365
- ---
366
-
367
- ## Install Strategy (Double Layer)
368
-
369
- Iranti now supports a two-layer install flow:
370
-
371
- 1. **Machine/runtime layer**: one local runtime root with one or more named Iranti instances.
372
- 2. **Project layer**: each chatbot/app binds to one instance with a local `.env.iranti`.
373
-
374
- ### 1) Install CLI
375
-
376
- ```bash
377
- # If published package is available
378
- npm install -g iranti
379
-
380
- # Or from this repo (local simulation)
381
- npm install -g .
382
- ```
383
-
384
- ### 2) Initialize machine runtime root
385
-
386
- ```bash
387
- iranti setup
388
-
389
- # non-interactive automation
390
- iranti setup --defaults --db-url "postgresql://postgres:realpassword@localhost:5432/iranti_local"
391
- iranti setup --config ./iranti.setup.json
392
-
393
- # or, if you want the lower-level manual path:
394
- iranti install --scope user
395
- ```
396
-
397
- `iranti setup` is the recommended first-run path. It walks through:
398
- - shared vs isolated runtime setup
399
- - instance creation or update
400
- - API port selection with conflict detection and next-free suggestions
401
- - database onboarding:
402
- - existing Postgres
403
- - managed Postgres
404
- - optional Docker-hosted Postgres for local development
321
+
322
+ Alias:
323
+
324
+ ```bash
325
+ iranti integrate codex
326
+ ```
327
+
328
+ Guide: [`docs/guides/codex.md`](docs/guides/codex.md)
329
+
330
+ ### Resolve Pending Escalations
331
+
332
+ Review unresolved human-escalation files from the CLI:
333
+
334
+ ```bash
335
+ iranti resolve
336
+ ```
337
+
338
+ Use `--dir` to point at a non-default escalation root. Guide: [`docs/guides/conflict-resolution.md`](docs/guides/conflict-resolution.md)
339
+
340
+ ### Native Chat
341
+
342
+ Start a CLI chat session against the configured Iranti instance:
343
+
344
+ ```bash
345
+ iranti chat
346
+ ```
347
+
348
+ Use `--agent`, `--provider`, and `--model` to pin the session identity and model routing.
349
+ The chat surface now includes slash commands for fact history, relationships, conflict-resolution handoff, and confidence updates in addition to memory search/write operations.
350
+ Guide: [`docs/guides/chat.md`](docs/guides/chat.md)
351
+
352
+ ### Manual Attendant Inspection
353
+
354
+ For debugging and operator visibility, Iranti also exposes manual Attendant commands:
355
+
356
+ ```bash
357
+ iranti handshake --task "Working on ProofScript repo"
358
+ iranti attend "What did we decide about the parser?" --context-file transcript.txt
359
+ ```
360
+
361
+ Both commands accept `--json`.
362
+ They are useful for verifying what the Attendant would load or inject for a given agent and project binding.
363
+ They are not a replacement for Claude Code hooks or MCP tools in normal use.
364
+
365
+ ---
366
+
367
+ ## Install Strategy (Double Layer)
368
+
369
+ Iranti now supports a two-layer install flow:
370
+
371
+ 1. **Machine/runtime layer**: one local runtime root with one or more named Iranti instances.
372
+ 2. **Project layer**: each chatbot/app binds to one instance with a local `.env.iranti`.
373
+
374
+ ### 1) Install CLI
375
+
376
+ ```bash
377
+ # If published package is available
378
+ npm install -g iranti
379
+
380
+ # Or from this repo (local simulation)
381
+ npm install -g .
382
+ ```
383
+
384
+ ### 2) Initialize machine runtime root
385
+
386
+ ```bash
387
+ iranti setup
388
+
389
+ # non-interactive automation
390
+ iranti setup --defaults --db-url "postgresql://postgres:realpassword@localhost:5432/iranti_local"
391
+ iranti setup --config ./iranti.setup.json
392
+
393
+ # or, if you want the lower-level manual path:
394
+ iranti install --scope user
395
+ ```
396
+
397
+ `iranti setup` is the recommended first-run path. It walks through:
398
+ - shared vs isolated runtime setup
399
+ - instance creation or update
400
+ - API port selection with conflict detection and next-free suggestions
401
+ - database onboarding:
402
+ - existing Postgres
403
+ - managed Postgres
404
+ - optional Docker-hosted Postgres for local development
405
405
  - provider API keys
406
406
  - Iranti client API key generation
407
407
  - one or more project bindings
@@ -410,456 +410,456 @@ iranti install --scope user
410
410
  Operator-facing CLI help now includes short "what it does" and "use this when" guidance, so `iranti <command> --help` is the quickest way to choose the right entry point.
411
411
 
412
412
  For automation:
413
- - `iranti setup --defaults` uses sensible defaults plus environment/flag input, but still requires a real `DATABASE_URL`.
414
- - `iranti setup --config <file>` reads a JSON setup plan for repeatable bootstrap.
415
- - `--bootstrap-db` runs migrations and seeding during automated setup when the database is reachable.
416
- - Example config: [docs/guides/iranti.setup.example.json](docs/guides/iranti.setup.example.json)
417
-
418
- Default API port remains `3001`. The setup wizard now warns when that port is already in use and suggests the next free port instead of forcing users to debug the collision manually.
419
-
420
- Defaults:
421
- - Windows user scope: `%USERPROFILE%\\.iranti`
422
- - Windows system scope: `%ProgramData%\\Iranti`
423
- - Linux system scope: `/var/lib/iranti`
424
- - macOS system scope: `/Library/Application Support/Iranti`
425
-
426
- ### 3) Create a named instance
427
-
428
- ```bash
429
- iranti instance create local --port 3001 --db-url "postgresql://postgres:yourpassword@localhost:5432/iranti_local" --provider mock
430
- iranti instance show local
431
- ```
432
-
433
- Finish onboarding or change settings later with:
434
-
435
- ```bash
436
- # Provider/db updates
413
+ - `iranti setup --defaults` uses sensible defaults plus environment/flag input, but still requires a real `DATABASE_URL`.
414
+ - `iranti setup --config <file>` reads a JSON setup plan for repeatable bootstrap.
415
+ - `--bootstrap-db` runs migrations and seeding during automated setup when the database is reachable.
416
+ - Example config: [docs/guides/iranti.setup.example.json](docs/guides/iranti.setup.example.json)
417
+
418
+ Default API port remains `3001`. The setup wizard now warns when that port is already in use and suggests the next free port instead of forcing users to debug the collision manually.
419
+
420
+ Defaults:
421
+ - Windows user scope: `%USERPROFILE%\\.iranti`
422
+ - Windows system scope: `%ProgramData%\\Iranti`
423
+ - Linux system scope: `/var/lib/iranti`
424
+ - macOS system scope: `/Library/Application Support/Iranti`
425
+
426
+ ### 3) Create a named instance
427
+
428
+ ```bash
429
+ iranti instance create local --port 3001 --db-url "postgresql://postgres:yourpassword@localhost:5432/iranti_local" --provider mock
430
+ iranti instance show local
431
+ ```
432
+
433
+ Finish onboarding or change settings later with:
434
+
435
+ ```bash
436
+ # Provider/db updates
437
437
  iranti configure instance local --provider openai --provider-key replace-with-real-openai-key --db-url "postgresql://postgres:realpassword@localhost:5432/iranti_local"
438
- iranti configure instance local --interactive
439
-
440
- # Provider key shortcuts
441
- iranti list api-keys --instance local
442
- iranti add api-key openai --instance local
443
- iranti update api-key claude --instance local
444
- iranti remove api-key gemini --instance local
445
-
446
- # Create a registry-backed API key and sync it into the instance env
447
- iranti auth create-key --instance local --key-id local_admin --owner "Local Admin" --scopes "kb:read,kb:write,memory:read,memory:write,agents:read,agents:write" --write-instance
448
- ```
449
-
450
- `iranti add|update|remove api-key` updates the stored upstream provider credentials in the instance env without hand-editing `.env` files. `iranti list api-keys` shows which provider keys are currently stored. Supported remote providers are OpenAI, Claude, Gemini, Groq, and Mistral. `mock` and `ollama` do not require remote API keys, and Perplexity is not yet supported.
451
-
452
- ### 4) Run Iranti from that instance
453
-
454
- ```bash
455
- iranti run --instance local
456
- ```
457
-
458
- If a provider rejects requests because credits are exhausted, billing is disabled, or the account is quota-limited, Iranti now surfaces a direct message such as `OpenAI quota or billing limit reached. Add credits, update the API key, or switch providers.`
459
-
460
- ### 5) Bind any chatbot/app project to that instance
461
-
462
- ```bash
463
- cd /path/to/your/chatbot
464
- iranti project init . --instance local --agent-id chatbot_main
465
- ```
466
-
467
- This writes `.env.iranti` in the project with the correct `IRANTI_URL`, `IRANTI_API_KEY`, and default agent identity.
468
-
469
- Later changes use the same surface:
470
-
471
- ```bash
472
- iranti configure project . --instance local --agent-id chatbot_worker
473
- iranti configure project . --interactive
474
- iranti auth create-key --instance local --key-id chatbot_worker --owner "Chatbot Worker" --scopes "kb:read,memory:read,memory:write" --project .
475
- ```
476
-
477
- For multi-agent systems, bind once per project and set unique agent IDs per worker (for example `planner_agent`, `research_agent`, `critic_agent`).
478
-
479
- ### Installation Diagnostics
480
-
481
- Use the CLI doctor command before first run or before a release check:
482
-
483
- ```bash
484
- iranti doctor
485
- iranti doctor --instance local
486
- iranti status
487
- iranti upgrade --check
488
- iranti upgrade --dry-run
489
- iranti upgrade --yes
490
- ```
491
-
492
- This validates the active env file, database URL, API key presence, provider selection, and provider-specific credentials.
493
- `iranti status` shows the current runtime root, known instances, and local binding files.
494
- `iranti upgrade` detects repo/global/Python install paths, compares current vs latest published versions, prints the exact plan, and executes the selected upgrade path when you pass `--yes`.
495
- On Windows, if the currently running CLI is itself the global npm install being upgraded, Iranti now hands that npm-global step off to a detached updater process instead of trying to replace the live binary in place.
496
- `iranti configure ...` updates instance/project credentials without manual env editing.
497
- `iranti auth ...` manages registry-backed API keys and can sync them into instance or project bindings.
498
-
499
- ---
500
-
501
- ## Core API
502
-
503
- ### Write a Fact
504
-
505
- ```python
506
- from clients.python.iranti import IrantiClient
507
-
508
- client = IrantiClient(
509
- base_url="http://localhost:3001",
510
- api_key="your_api_key_here"
511
- )
512
-
513
- result = client.write(
514
- entity="researcher/jane_smith", # Format: entityType/entityId
515
- key="affiliation",
516
- value={"institution": "MIT", "department": "CSAIL"},
517
- summary="Affiliated with MIT CSAIL", # Compressed for working memory
518
- confidence=85, # 0-100
519
- source="OpenAlex",
520
- agent="research_agent_001"
521
- )
522
-
523
- print(result.action) # 'created', 'updated', 'escalated', or 'rejected'
524
- ```
525
-
526
- ### Query a Fact
527
-
528
- ```python
529
- result = client.query("researcher/jane_smith", "affiliation")
530
-
531
- if result.found:
532
- print(result.value) # {"institution": "MIT", "department": "CSAIL"}
533
- print(result.confidence) # 85
534
- print(result.source) # "OpenAlex"
535
- ```
536
-
537
- ### Query All Facts for an Entity
538
-
539
- ```python
540
- facts = client.query_all("researcher/jane_smith")
541
-
542
- for fact in facts:
543
- print(f"[{fact['key']}] {fact['summary']} (confidence: {fact['confidence']})")
544
- ```
545
-
546
- ### Graph Traversal
547
-
548
- ```python
549
- from clients.python.iranti import IrantiClient
550
-
551
- client = IrantiClient(base_url="http://localhost:3001", api_key="your_api_key_here")
552
-
553
- # Agent 1 writes facts and links them into a graph.
554
- client.write("researcher/jane_smith", "affiliation", {"lab": "CSAIL"}, "Jane Smith is affiliated with CSAIL", 90, "OpenAlex", "research_agent")
555
- client.write("project/quantum_bridge", "status", {"phase": "active"}, "Quantum Bridge is active", 88, "project_brief", "research_agent")
556
-
557
- client.relate("researcher/jane_smith", "MEMBER_OF", "lab/csail", created_by="research_agent")
558
- client.relate("lab/csail", "LEADS", "project/quantum_bridge", created_by="research_agent")
559
-
560
- # Agent 2 starts cold and traverses outward from Jane Smith.
561
- one_hop = client.related("researcher/jane_smith")
562
- labs = [f"{r['toType']}/{r['toId']}" for r in one_hop if r["relationshipType"] == "MEMBER_OF"]
563
-
564
- projects = []
565
- for lab in labs:
566
- for rel in client.related(lab):
567
- if rel["relationshipType"] == "LEADS":
568
- project = f"{rel['toType']}/{rel['toId']}"
569
- status = client.query(project, "status")
570
- projects.append((project, status.value["phase"]))
571
-
572
- print(projects)
573
- # Agent 2 learned which project Jane Smith is connected to without being told the project directly.
574
- ```
575
-
576
- ### Relationship Types
577
-
578
- Relationship types are caller-defined strings. Common conventions:
579
-
580
- | Relationship Type | Meaning |
581
- |---|---|
582
- | `MEMBER_OF` | Entity belongs to a team, lab, org, or group |
583
- | `PART_OF` | Entity is a component or sub-unit of another entity |
584
- | `AUTHORED` | Person or agent created a document, paper, or artifact |
585
- | `LEADS` | Person, team, or org leads a project or effort |
586
- | `DEPENDS_ON` | Project, service, or task depends on another entity |
587
- | `REPORTS_TO` | Directed reporting relationship between people or agents |
588
-
589
- Use uppercase snake case for consistency. Iranti does not enforce a fixed ontology here; the calling application owns the relationship vocabulary.
590
-
591
- ### Hybrid Search
592
-
593
- ```python
594
- matches = client.search(
595
- query="current blocker launch readiness",
596
- entity_type="project",
597
- limit=5,
598
- lexical_weight=0.45,
599
- vector_weight=0.55,
600
- )
601
-
602
- for item in matches:
603
- print(item["entity"], item["key"], item["score"])
604
- ```
605
-
606
- ### Context Persistence (attend)
607
-
608
- ```python
609
- # Before each LLM call, let Attendant decide if memory is needed
610
- result = client.attend(
611
- agent_id="research_agent_001",
612
- latest_message="What's Jane Smith's current affiliation?",
613
- current_context="User: What's Jane Smith's current affiliation?\nAssistant: Let me check...",
614
- max_facts=5
615
- )
616
-
617
- if result["shouldInject"]:
618
- for fact in result['facts']:
619
- print(f"Inject: [{fact['entityKey']}] {fact['summary']}")
620
- ```
621
-
622
- ### Working Memory (handshake)
623
-
624
- ```python
625
- # At session start, get personalized brief for agent's current task
438
+ iranti configure instance local --interactive
439
+
440
+ # Provider key shortcuts
441
+ iranti list api-keys --instance local
442
+ iranti add api-key openai --instance local
443
+ iranti update api-key claude --instance local
444
+ iranti remove api-key gemini --instance local
445
+
446
+ # Create a registry-backed API key and sync it into the instance env
447
+ iranti auth create-key --instance local --key-id local_admin --owner "Local Admin" --scopes "kb:read,kb:write,memory:read,memory:write,agents:read,agents:write" --write-instance
448
+ ```
449
+
450
+ `iranti add|update|remove api-key` updates the stored upstream provider credentials in the instance env without hand-editing `.env` files. `iranti list api-keys` shows which provider keys are currently stored. Supported remote providers are OpenAI, Claude, Gemini, Groq, and Mistral. `mock` and `ollama` do not require remote API keys, and Perplexity is not yet supported.
451
+
452
+ ### 4) Run Iranti from that instance
453
+
454
+ ```bash
455
+ iranti run --instance local
456
+ ```
457
+
458
+ If a provider rejects requests because credits are exhausted, billing is disabled, or the account is quota-limited, Iranti now surfaces a direct message such as `OpenAI quota or billing limit reached. Add credits, update the API key, or switch providers.`
459
+
460
+ ### 5) Bind any chatbot/app project to that instance
461
+
462
+ ```bash
463
+ cd /path/to/your/chatbot
464
+ iranti project init . --instance local --agent-id chatbot_main
465
+ ```
466
+
467
+ This writes `.env.iranti` in the project with the correct `IRANTI_URL`, `IRANTI_API_KEY`, and default agent identity.
468
+
469
+ Later changes use the same surface:
470
+
471
+ ```bash
472
+ iranti configure project . --instance local --agent-id chatbot_worker
473
+ iranti configure project . --interactive
474
+ iranti auth create-key --instance local --key-id chatbot_worker --owner "Chatbot Worker" --scopes "kb:read,memory:read,memory:write" --project .
475
+ ```
476
+
477
+ For multi-agent systems, bind once per project and set unique agent IDs per worker (for example `planner_agent`, `research_agent`, `critic_agent`).
478
+
479
+ ### Installation Diagnostics
480
+
481
+ Use the CLI doctor command before first run or before a release check:
482
+
483
+ ```bash
484
+ iranti doctor
485
+ iranti doctor --instance local
486
+ iranti status
487
+ iranti upgrade --check
488
+ iranti upgrade --dry-run
489
+ iranti upgrade --yes
490
+ ```
491
+
492
+ This validates the active env file, database URL, API key presence, provider selection, and provider-specific credentials.
493
+ `iranti status` shows the current runtime root, known instances, and local binding files.
494
+ `iranti upgrade` detects repo/global/Python install paths, compares current vs latest published versions, prints the exact plan, and executes the selected upgrade path when you pass `--yes`.
495
+ On Windows, if the currently running CLI is itself the global npm install being upgraded, Iranti now hands that npm-global step off to a detached updater process instead of trying to replace the live binary in place.
496
+ `iranti configure ...` updates instance/project credentials without manual env editing.
497
+ `iranti auth ...` manages registry-backed API keys and can sync them into instance or project bindings.
498
+
499
+ ---
500
+
501
+ ## Core API
502
+
503
+ ### Write a Fact
504
+
505
+ ```python
506
+ from clients.python.iranti import IrantiClient
507
+
508
+ client = IrantiClient(
509
+ base_url="http://localhost:3001",
510
+ api_key="your_api_key_here"
511
+ )
512
+
513
+ result = client.write(
514
+ entity="researcher/jane_smith", # Format: entityType/entityId
515
+ key="affiliation",
516
+ value={"institution": "MIT", "department": "CSAIL"},
517
+ summary="Affiliated with MIT CSAIL", # Compressed for working memory
518
+ confidence=85, # 0-100
519
+ source="OpenAlex",
520
+ agent="research_agent_001"
521
+ )
522
+
523
+ print(result.action) # 'created', 'updated', 'escalated', or 'rejected'
524
+ ```
525
+
526
+ ### Query a Fact
527
+
528
+ ```python
529
+ result = client.query("researcher/jane_smith", "affiliation")
530
+
531
+ if result.found:
532
+ print(result.value) # {"institution": "MIT", "department": "CSAIL"}
533
+ print(result.confidence) # 85
534
+ print(result.source) # "OpenAlex"
535
+ ```
536
+
537
+ ### Query All Facts for an Entity
538
+
539
+ ```python
540
+ facts = client.query_all("researcher/jane_smith")
541
+
542
+ for fact in facts:
543
+ print(f"[{fact['key']}] {fact['summary']} (confidence: {fact['confidence']})")
544
+ ```
545
+
546
+ ### Graph Traversal
547
+
548
+ ```python
549
+ from clients.python.iranti import IrantiClient
550
+
551
+ client = IrantiClient(base_url="http://localhost:3001", api_key="your_api_key_here")
552
+
553
+ # Agent 1 writes facts and links them into a graph.
554
+ client.write("researcher/jane_smith", "affiliation", {"lab": "CSAIL"}, "Jane Smith is affiliated with CSAIL", 90, "OpenAlex", "research_agent")
555
+ client.write("project/quantum_bridge", "status", {"phase": "active"}, "Quantum Bridge is active", 88, "project_brief", "research_agent")
556
+
557
+ client.relate("researcher/jane_smith", "MEMBER_OF", "lab/csail", created_by="research_agent")
558
+ client.relate("lab/csail", "LEADS", "project/quantum_bridge", created_by="research_agent")
559
+
560
+ # Agent 2 starts cold and traverses outward from Jane Smith.
561
+ one_hop = client.related("researcher/jane_smith")
562
+ labs = [f"{r['toType']}/{r['toId']}" for r in one_hop if r["relationshipType"] == "MEMBER_OF"]
563
+
564
+ projects = []
565
+ for lab in labs:
566
+ for rel in client.related(lab):
567
+ if rel["relationshipType"] == "LEADS":
568
+ project = f"{rel['toType']}/{rel['toId']}"
569
+ status = client.query(project, "status")
570
+ projects.append((project, status.value["phase"]))
571
+
572
+ print(projects)
573
+ # Agent 2 learned which project Jane Smith is connected to without being told the project directly.
574
+ ```
575
+
576
+ ### Relationship Types
577
+
578
+ Relationship types are caller-defined strings. Common conventions:
579
+
580
+ | Relationship Type | Meaning |
581
+ |---|---|
582
+ | `MEMBER_OF` | Entity belongs to a team, lab, org, or group |
583
+ | `PART_OF` | Entity is a component or sub-unit of another entity |
584
+ | `AUTHORED` | Person or agent created a document, paper, or artifact |
585
+ | `LEADS` | Person, team, or org leads a project or effort |
586
+ | `DEPENDS_ON` | Project, service, or task depends on another entity |
587
+ | `REPORTS_TO` | Directed reporting relationship between people or agents |
588
+
589
+ Use uppercase snake case for consistency. Iranti does not enforce a fixed ontology here; the calling application owns the relationship vocabulary.
590
+
591
+ ### Hybrid Search
592
+
593
+ ```python
594
+ matches = client.search(
595
+ query="current blocker launch readiness",
596
+ entity_type="project",
597
+ limit=5,
598
+ lexical_weight=0.45,
599
+ vector_weight=0.55,
600
+ )
601
+
602
+ for item in matches:
603
+ print(item["entity"], item["key"], item["score"])
604
+ ```
605
+
606
+ ### Context Persistence (attend)
607
+
608
+ ```python
609
+ # Before each LLM call, let Attendant decide if memory is needed
610
+ result = client.attend(
611
+ agent_id="research_agent_001",
612
+ latest_message="What's Jane Smith's current affiliation?",
613
+ current_context="User: What's Jane Smith's current affiliation?\nAssistant: Let me check...",
614
+ max_facts=5
615
+ )
616
+
617
+ if result["shouldInject"]:
618
+ for fact in result['facts']:
619
+ print(f"Inject: [{fact['entityKey']}] {fact['summary']}")
620
+ ```
621
+
622
+ ### Working Memory (handshake)
623
+
624
+ ```python
625
+ # At session start, get personalized brief for agent's current task
626
626
  brief = client.handshake(
627
627
  agent_id="research_agent_001",
628
628
  task="Research publication history for Dr. Jane Smith",
629
629
  recent_messages=["Starting literature review..."]
630
630
  )
631
-
632
- print(brief.operating_rules) # Staff namespace rules for this agent
633
- print(brief.inferred_task_type) # e.g. "research", "verification"
634
-
635
- for entry in brief.working_memory:
636
- print(f"{entry.entity_key}: {entry.summary}")
637
- ```
638
-
639
- ---
640
-
641
- ## CrewAI Integration
642
-
643
- Minimal working example based on validated experiments:
644
-
645
- ```python
646
- from crewai import Agent, Task, Crew, LLM
647
- from crewai.tools import tool
648
- from clients.python.iranti import IrantiClient
649
-
650
- iranti = IrantiClient(base_url="http://localhost:3001", api_key="your_key")
651
- ENTITY = "project/my_project"
652
-
653
- @tool("Write finding to shared memory")
654
- def write_finding(key: str, value: str, summary: str, confidence: int) -> str:
655
- """Write a fact to Iranti so other agents can access it."""
656
- result = iranti.write(
657
- entity=ENTITY,
658
- key=key,
659
- value={"data": value},
660
- summary=summary,
661
- confidence=confidence,
662
- source="briefing_doc",
663
- agent="researcher_agent"
664
- )
665
- return f"Saved '{key}': {result.action}"
666
-
667
- @tool("Get all findings")
668
- def get_all_findings() -> str:
669
- """Load all facts from Iranti."""
670
- facts = iranti.query_all(ENTITY)
671
- if not facts:
672
- return "No findings in shared memory."
673
- lines = [f"[{f['key']}] {f['summary']} (confidence: {f['confidence']})" for f in facts]
674
- return "\n".join(lines)
675
-
676
- # Researcher agent: writes to Iranti
677
- researcher = Agent(
678
- role="Research Analyst",
679
- goal="Extract facts from documents and save to shared memory",
680
- tools=[write_finding],
681
- llm=LLM(model="gpt-4o-mini")
682
- )
683
-
684
- # Analyst agent: reads from Iranti
685
- analyst = Agent(
686
- role="Project Analyst",
687
- goal="Summarize projects using shared memory",
688
- tools=[get_all_findings],
689
- llm=LLM(model="gpt-4o-mini")
690
- )
691
-
692
- # Researcher extracts facts, analyst loads them — no direct communication needed
693
- crew = Crew(agents=[researcher, analyst], tasks=[...])
694
- crew.kickoff()
695
- ```
696
-
697
- **Result**: Analyst successfully loads all facts written by researcher (validated 6/6 transfer rate).
698
-
699
- ---
700
-
701
- ## Middleware for Any LLM
702
-
703
- Add Iranti memory to Claude, ChatGPT, or any LLM via API wrapper:
704
-
705
- ```python
706
- from clients.middleware.iranti_middleware import IrantiMiddleware
707
-
708
- middleware = IrantiMiddleware(
709
- agent_id="my_agent",
710
- iranti_url="http://localhost:3001"
711
- )
712
-
713
- # Before sending to LLM
714
- augmented = middleware.before_send(
715
- user_message="What was the blocker?",
716
- conversation_history=[...]
717
- )
718
-
719
- # After receiving response
720
- middleware.after_receive(
721
- response="The blocker is...",
722
- conversation_history=[...]
723
- )
724
- ```
725
-
726
- **How it works**:
727
- 1. `before_send()` calls `attend()` with conversation context
728
- 2. Forgotten facts are prepended as `[MEMORY: ...]`
729
- 3. `after_receive()` extracts new facts and saves them (best-effort)
730
-
731
- **Note**: Browser extensions are blocked by ChatGPT and Claude's Content Security Policy. Use API-based middleware instead.
732
-
733
- **Examples**: [`clients/middleware/claude_example.py`](clients/middleware/claude_example.py)
734
-
735
- ---
736
-
737
- ## Architecture
738
-
739
- Iranti has five internal components:
740
-
741
- | Component | Role |
742
- |---|---|
743
- | **Library** | PostgreSQL knowledge base. Current truth lives in `knowledge_base`; closed and contested intervals live in `archive`. |
744
- | **Librarian** | Manages all writes. Detects conflicts, reasons about resolution, escalates when uncertain. |
745
- | **Attendant** | Per-agent working memory manager. Implements `attend()`, `observe()`, and `handshake()` APIs. |
746
- | **Archivist** | Periodic cleanup. Archives expired and low-confidence entries. Processes human-resolved conflicts. |
747
- | **Resolutionist** | Interactive CLI helper that walks pending escalation files, writes `AUTHORITATIVE_JSON`, and marks them resolved for the Archivist. |
748
-
749
- ### REST API
750
-
751
- Express server on port 3001 with endpoints:
752
-
753
- - `POST /kb/write` - Write atomic fact
754
- - `POST /kb/ingest` - Ingest raw text for one entity, auto-chunk into facts with per-fact confidence and per-fact write outcomes
755
- - `GET /kb/query/:entityType/:entityId/:key` - Query specific fact
756
- - `GET /kb/query/:entityType/:entityId` - Query all facts for entity
757
- - `GET /kb/search` - Hybrid search across facts
631
+
632
+ print(brief.operating_rules) # Staff namespace rules for this agent
633
+ print(brief.inferred_task_type) # e.g. "research", "verification"
634
+
635
+ for entry in brief.working_memory:
636
+ print(f"{entry.entity_key}: {entry.summary}")
637
+ ```
638
+
639
+ ---
640
+
641
+ ## CrewAI Integration
642
+
643
+ Minimal working example based on validated experiments:
644
+
645
+ ```python
646
+ from crewai import Agent, Task, Crew, LLM
647
+ from crewai.tools import tool
648
+ from clients.python.iranti import IrantiClient
649
+
650
+ iranti = IrantiClient(base_url="http://localhost:3001", api_key="your_key")
651
+ ENTITY = "project/my_project"
652
+
653
+ @tool("Write finding to shared memory")
654
+ def write_finding(key: str, value: str, summary: str, confidence: int) -> str:
655
+ """Write a fact to Iranti so other agents can access it."""
656
+ result = iranti.write(
657
+ entity=ENTITY,
658
+ key=key,
659
+ value={"data": value},
660
+ summary=summary,
661
+ confidence=confidence,
662
+ source="briefing_doc",
663
+ agent="researcher_agent"
664
+ )
665
+ return f"Saved '{key}': {result.action}"
666
+
667
+ @tool("Get all findings")
668
+ def get_all_findings() -> str:
669
+ """Load all facts from Iranti."""
670
+ facts = iranti.query_all(ENTITY)
671
+ if not facts:
672
+ return "No findings in shared memory."
673
+ lines = [f"[{f['key']}] {f['summary']} (confidence: {f['confidence']})" for f in facts]
674
+ return "\n".join(lines)
675
+
676
+ # Researcher agent: writes to Iranti
677
+ researcher = Agent(
678
+ role="Research Analyst",
679
+ goal="Extract facts from documents and save to shared memory",
680
+ tools=[write_finding],
681
+ llm=LLM(model="gpt-4o-mini")
682
+ )
683
+
684
+ # Analyst agent: reads from Iranti
685
+ analyst = Agent(
686
+ role="Project Analyst",
687
+ goal="Summarize projects using shared memory",
688
+ tools=[get_all_findings],
689
+ llm=LLM(model="gpt-4o-mini")
690
+ )
691
+
692
+ # Researcher extracts facts, analyst loads them — no direct communication needed
693
+ crew = Crew(agents=[researcher, analyst], tasks=[...])
694
+ crew.kickoff()
695
+ ```
696
+
697
+ **Result**: Analyst successfully loads all facts written by researcher (validated 6/6 transfer rate).
698
+
699
+ ---
700
+
701
+ ## Middleware for Any LLM
702
+
703
+ Add Iranti memory to Claude, ChatGPT, or any LLM via API wrapper:
704
+
705
+ ```python
706
+ from clients.middleware.iranti_middleware import IrantiMiddleware
707
+
708
+ middleware = IrantiMiddleware(
709
+ agent_id="my_agent",
710
+ iranti_url="http://localhost:3001"
711
+ )
712
+
713
+ # Before sending to LLM
714
+ augmented = middleware.before_send(
715
+ user_message="What was the blocker?",
716
+ conversation_history=[...]
717
+ )
718
+
719
+ # After receiving response
720
+ middleware.after_receive(
721
+ response="The blocker is...",
722
+ conversation_history=[...]
723
+ )
724
+ ```
725
+
726
+ **How it works**:
727
+ 1. `before_send()` calls `attend()` with conversation context
728
+ 2. Forgotten facts are prepended as `[MEMORY: ...]`
729
+ 3. `after_receive()` extracts new facts and saves them (best-effort)
730
+
731
+ **Note**: Browser extensions are blocked by ChatGPT and Claude's Content Security Policy. Use API-based middleware instead.
732
+
733
+ **Examples**: [`clients/middleware/claude_example.py`](clients/middleware/claude_example.py)
734
+
735
+ ---
736
+
737
+ ## Architecture
738
+
739
+ Iranti has five internal components:
740
+
741
+ | Component | Role |
742
+ |---|---|
743
+ | **Library** | PostgreSQL knowledge base. Current truth lives in `knowledge_base`; closed and contested intervals live in `archive`. |
744
+ | **Librarian** | Manages all writes. Detects conflicts, reasons about resolution, escalates when uncertain. |
745
+ | **Attendant** | Per-agent working memory manager. Implements `attend()`, `observe()`, and `handshake()` APIs. |
746
+ | **Archivist** | Periodic cleanup. Archives expired and low-confidence entries. Processes human-resolved conflicts. |
747
+ | **Resolutionist** | Interactive CLI helper that walks pending escalation files, writes `AUTHORITATIVE_JSON`, and marks them resolved for the Archivist. |
748
+
749
+ ### REST API
750
+
751
+ Express server on port 3001 with endpoints:
752
+
753
+ - `POST /kb/write` - Write atomic fact
754
+ - `POST /kb/ingest` - Ingest raw text for one entity, auto-chunk into facts with per-fact confidence and per-fact write outcomes
755
+ - `GET /kb/query/:entityType/:entityId/:key` - Query specific fact
756
+ - `GET /kb/query/:entityType/:entityId` - Query all facts for entity
757
+ - `GET /kb/search` - Hybrid search across facts
758
758
  - `POST /memory/attend` - Decide whether to inject memory for this turn
759
759
  - `POST /memory/observe` - Context persistence (inject missing facts)
760
760
  - `POST /memory/handshake` - Working memory brief for agent session
761
761
  - `GET /memory/sessions` - List persisted operator-visible session checkpoints across agents, with optional operator filters/sorting
762
762
  - `GET /memory/session/:agentId` - Inspect the current persisted session checkpoint/recovery state
763
763
  - `POST /kb/relate` - Create entity relationship
764
- - `GET /kb/related/:entityType/:entityId` - Get related entities
765
- - `POST /agents/register` - Register agent in registry
766
-
767
- All endpoints require `X-Iranti-Key` header for authentication.
768
-
769
- ---
770
-
771
- ## Schema
772
-
773
- Six PostgreSQL tables:
774
-
775
- ```
776
- knowledge_base - current truth (one live row per entity/key)
777
- archive - temporal and provenance history for superseded, contradicted, escalated, and expired rows
778
- entity_relationships - directional graph: MEMBER_OF, PART_OF, AUTHORED, etc.
779
- entities - canonical entity identity registry
780
- entity_aliases - normalized aliases mapped to canonical entities
781
- write_receipts - idempotency receipts for requestId replay safety
782
- ```
783
-
784
- New entity types, relationship types, and fact keys do not require migrations; they are caller-defined strings.
785
-
786
- **Archive semantics**: When a current fact is superseded or contested, the current row is removed from `knowledge_base` and a closed historical interval is written to `archive`. Temporal queries use `validFrom` / `validUntil` plus archive metadata to answer point-in-time reads.
787
-
788
- ---
789
-
790
- ## Running Tests
791
-
792
- ```bash
793
- npm run test:integration # Full end-to-end
794
- npm run test:librarian # Conflict resolution
795
- npm run test:attendant # Working memory
796
- npm run test:reliability # Source scoring
797
-
798
- # Python validation experiments
799
- cd clients/experiments
800
- python validate_nexus_observe.py # Context persistence
801
- python validate_nexus_treatment.py # Cross-agent transfer
802
- ```
803
-
804
- ---
805
-
806
- ## Contributing
807
-
808
- Contributions welcome! Please:
809
-
810
- 1. Fork the repository
811
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
812
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
813
- 4. Push to the branch (`git push origin feature/amazing-feature`)
814
- 5. Open a Pull Request
815
-
816
- ---
817
-
818
- ## License
819
-
820
- GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
821
-
822
- Free to use, modify, and distribute under AGPL terms. If you offer Iranti as a hosted service and modify it, AGPL requires publishing those modifications.
823
-
824
- ---
825
-
826
- ## Name
827
-
828
- Iranti is the Yoruba word for memory and remembrance.
829
-
830
- ---
831
-
832
- ## Project Structure
833
-
834
- ```
835
- src/
836
- ├── library/ — DB client, queries, relationships, agent registry
837
- ├── librarian/ — Write logic, conflict resolution, reliability
838
- ├── attendant/ — Per-agent working memory, observe() implementation
839
- ├── archivist/ — Periodic cleanup, escalation processing
840
- ├── lib/ — LLM abstraction, model router, providers
841
- ├── sdk/ — Public TypeScript API
842
- └── api/ — REST API server
843
-
844
- clients/
845
- ├── python/ — Python client (IrantiClient)
846
- ├── middleware/ — LLM conversation wrappers (Claude, ChatGPT, etc.)
847
- └── experiments/ — Validated experiments with real results
848
-
849
- docs/
850
- └── internal/validation_results.md — Full experiment outputs and analysis
851
- ```
852
-
853
- ---
854
-
855
- ## Support
856
-
857
- - **Issues**: [GitHub Issues](https://github.com/nfemmanuel/iranti/issues)
858
- - **Discussions**: [GitHub Discussions](https://github.com/nfemmanuel/iranti/discussions)
859
- - **Email**: oluwaniifemi.emmanuel@uni.minerva.edu
860
- - **Changelog**: [`CHANGELOG.md`](CHANGELOG.md)
861
-
862
- ---
863
-
864
- **Built with ❤️ for the multi-agent AI community.**
865
-
764
+ - `GET /kb/related/:entityType/:entityId` - Get related entities
765
+ - `POST /agents/register` - Register agent in registry
766
+
767
+ All endpoints require `X-Iranti-Key` header for authentication.
768
+
769
+ ---
770
+
771
+ ## Schema
772
+
773
+ Six PostgreSQL tables:
774
+
775
+ ```
776
+ knowledge_base - current truth (one live row per entity/key)
777
+ archive - temporal and provenance history for superseded, contradicted, escalated, and expired rows
778
+ entity_relationships - directional graph: MEMBER_OF, PART_OF, AUTHORED, etc.
779
+ entities - canonical entity identity registry
780
+ entity_aliases - normalized aliases mapped to canonical entities
781
+ write_receipts - idempotency receipts for requestId replay safety
782
+ ```
783
+
784
+ New entity types, relationship types, and fact keys do not require migrations; they are caller-defined strings.
785
+
786
+ **Archive semantics**: When a current fact is superseded or contested, the current row is removed from `knowledge_base` and a closed historical interval is written to `archive`. Temporal queries use `validFrom` / `validUntil` plus archive metadata to answer point-in-time reads.
787
+
788
+ ---
789
+
790
+ ## Running Tests
791
+
792
+ ```bash
793
+ npm run test:integration # Full end-to-end
794
+ npm run test:librarian # Conflict resolution
795
+ npm run test:attendant # Working memory
796
+ npm run test:reliability # Source scoring
797
+
798
+ # Python validation experiments
799
+ cd clients/experiments
800
+ python validate_nexus_observe.py # Context persistence
801
+ python validate_nexus_treatment.py # Cross-agent transfer
802
+ ```
803
+
804
+ ---
805
+
806
+ ## Contributing
807
+
808
+ Contributions welcome! Please:
809
+
810
+ 1. Fork the repository
811
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
812
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
813
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
814
+ 5. Open a Pull Request
815
+
816
+ ---
817
+
818
+ ## License
819
+
820
+ GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
821
+
822
+ Free to use, modify, and distribute under AGPL terms. If you offer Iranti as a hosted service and modify it, AGPL requires publishing those modifications.
823
+
824
+ ---
825
+
826
+ ## Name
827
+
828
+ Iranti is the Yoruba word for memory and remembrance.
829
+
830
+ ---
831
+
832
+ ## Project Structure
833
+
834
+ ```
835
+ src/
836
+ ├── library/ — DB client, queries, relationships, agent registry
837
+ ├── librarian/ — Write logic, conflict resolution, reliability
838
+ ├── attendant/ — Per-agent working memory, observe() implementation
839
+ ├── archivist/ — Periodic cleanup, escalation processing
840
+ ├── lib/ — LLM abstraction, model router, providers
841
+ ├── sdk/ — Public TypeScript API
842
+ └── api/ — REST API server
843
+
844
+ clients/
845
+ ├── python/ — Python client (IrantiClient)
846
+ ├── middleware/ — LLM conversation wrappers (Claude, ChatGPT, etc.)
847
+ └── experiments/ — Validated experiments with real results
848
+
849
+ docs/
850
+ └── internal/validation_results.md — Full experiment outputs and analysis
851
+ ```
852
+
853
+ ---
854
+
855
+ ## Support
856
+
857
+ - **Issues**: [GitHub Issues](https://github.com/nfemmanuel/iranti/issues)
858
+ - **Discussions**: [GitHub Discussions](https://github.com/nfemmanuel/iranti/discussions)
859
+ - **Email**: oluwaniifemi.emmanuel@uni.minerva.edu
860
+ - **Changelog**: [`CHANGELOG.md`](CHANGELOG.md)
861
+
862
+ ---
863
+
864
+ **Built with ❤️ for the multi-agent AI community.**
865
+