iranti 0.2.50 → 0.3.0

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