iranti 0.1.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.
- package/.env.example +63 -0
- package/LICENSE +12 -0
- package/README.md +520 -0
- package/bin/iranti.js +24 -0
- package/dist/scripts/api-key-create.js +57 -0
- package/dist/scripts/api-key-list.js +42 -0
- package/dist/scripts/api-key-revoke.js +42 -0
- package/dist/scripts/iranti-cli.js +387 -0
- package/dist/scripts/seed-codebase.js +138 -0
- package/dist/scripts/seed.js +121 -0
- package/dist/scripts/setup.js +86 -0
- package/dist/src/api/archivistScheduler.d.ts +8 -0
- package/dist/src/api/archivistScheduler.d.ts.map +1 -0
- package/dist/src/api/archivistScheduler.js +100 -0
- package/dist/src/api/archivistScheduler.js.map +1 -0
- package/dist/src/api/diag.d.ts +2 -0
- package/dist/src/api/diag.d.ts.map +1 -0
- package/dist/src/api/diag.js +54 -0
- package/dist/src/api/diag.js.map +1 -0
- package/dist/src/api/middleware/auth.d.ts +3 -0
- package/dist/src/api/middleware/auth.d.ts.map +1 -0
- package/dist/src/api/middleware/auth.js +36 -0
- package/dist/src/api/middleware/auth.js.map +1 -0
- package/dist/src/api/middleware/authorization.d.ts +4 -0
- package/dist/src/api/middleware/authorization.d.ts.map +1 -0
- package/dist/src/api/middleware/authorization.js +54 -0
- package/dist/src/api/middleware/authorization.js.map +1 -0
- package/dist/src/api/middleware/rateLimit.d.ts +23 -0
- package/dist/src/api/middleware/rateLimit.d.ts.map +1 -0
- package/dist/src/api/middleware/rateLimit.js +70 -0
- package/dist/src/api/middleware/rateLimit.js.map +1 -0
- package/dist/src/api/middleware/validation.d.ts +128 -0
- package/dist/src/api/middleware/validation.d.ts.map +1 -0
- package/dist/src/api/middleware/validation.js +137 -0
- package/dist/src/api/middleware/validation.js.map +1 -0
- package/dist/src/api/repro.d.ts +2 -0
- package/dist/src/api/repro.d.ts.map +1 -0
- package/dist/src/api/repro.js +25 -0
- package/dist/src/api/repro.js.map +1 -0
- package/dist/src/api/routes/agents.d.ts +4 -0
- package/dist/src/api/routes/agents.d.ts.map +1 -0
- package/dist/src/api/routes/agents.js +56 -0
- package/dist/src/api/routes/agents.js.map +1 -0
- package/dist/src/api/routes/batch.d.ts +2 -0
- package/dist/src/api/routes/batch.d.ts.map +1 -0
- package/dist/src/api/routes/batch.js +63 -0
- package/dist/src/api/routes/batch.js.map +1 -0
- package/dist/src/api/routes/dev.d.ts +2 -0
- package/dist/src/api/routes/dev.d.ts.map +1 -0
- package/dist/src/api/routes/dev.js +29 -0
- package/dist/src/api/routes/dev.js.map +1 -0
- package/dist/src/api/routes/knowledge.d.ts +4 -0
- package/dist/src/api/routes/knowledge.d.ts.map +1 -0
- package/dist/src/api/routes/knowledge.js +184 -0
- package/dist/src/api/routes/knowledge.js.map +1 -0
- package/dist/src/api/routes/memory.d.ts +4 -0
- package/dist/src/api/routes/memory.d.ts.map +1 -0
- package/dist/src/api/routes/memory.js +150 -0
- package/dist/src/api/routes/memory.js.map +1 -0
- package/dist/src/api/server.d.ts +2 -0
- package/dist/src/api/server.d.ts.map +1 -0
- package/dist/src/api/server.js +191 -0
- package/dist/src/api/server.js.map +1 -0
- package/dist/src/archivist/index.d.ts +10 -0
- package/dist/src/archivist/index.d.ts.map +1 -0
- package/dist/src/archivist/index.js +232 -0
- package/dist/src/archivist/index.js.map +1 -0
- package/dist/src/attendant/AttendantInstance.d.ts +96 -0
- package/dist/src/attendant/AttendantInstance.d.ts.map +1 -0
- package/dist/src/attendant/AttendantInstance.js +808 -0
- package/dist/src/attendant/AttendantInstance.js.map +1 -0
- package/dist/src/attendant/index.d.ts +12 -0
- package/dist/src/attendant/index.d.ts.map +1 -0
- package/dist/src/attendant/index.js +39 -0
- package/dist/src/attendant/index.js.map +1 -0
- package/dist/src/attendant/registry.d.ts +6 -0
- package/dist/src/attendant/registry.d.ts.map +1 -0
- package/dist/src/attendant/registry.js +27 -0
- package/dist/src/attendant/registry.js.map +1 -0
- package/dist/src/generated/prisma/browser.d.ts +35 -0
- package/dist/src/generated/prisma/browser.d.ts.map +1 -0
- package/dist/src/generated/prisma/browser.js +57 -0
- package/dist/src/generated/prisma/browser.js.map +1 -0
- package/dist/src/generated/prisma/client.d.ts +54 -0
- package/dist/src/generated/prisma/client.d.ts.map +1 -0
- package/dist/src/generated/prisma/client.js +71 -0
- package/dist/src/generated/prisma/client.js.map +1 -0
- package/dist/src/generated/prisma/commonInputTypes.d.ts +415 -0
- package/dist/src/generated/prisma/commonInputTypes.d.ts.map +1 -0
- package/dist/src/generated/prisma/commonInputTypes.js +12 -0
- package/dist/src/generated/prisma/commonInputTypes.js.map +1 -0
- package/dist/src/generated/prisma/enums.d.ts +2 -0
- package/dist/src/generated/prisma/enums.d.ts.map +1 -0
- package/dist/src/generated/prisma/enums.js +12 -0
- package/dist/src/generated/prisma/enums.js.map +1 -0
- package/dist/src/generated/prisma/internal/class.d.ts +186 -0
- package/dist/src/generated/prisma/internal/class.d.ts.map +1 -0
- package/dist/src/generated/prisma/internal/class.js +86 -0
- package/dist/src/generated/prisma/internal/class.js.map +1 -0
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts +1015 -0
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/src/generated/prisma/internal/prismaNamespace.js +220 -0
- package/dist/src/generated/prisma/internal/prismaNamespace.js.map +1 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts +152 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js +191 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/src/generated/prisma/models/Archive.d.ts +1425 -0
- package/dist/src/generated/prisma/models/Archive.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/Archive.js +3 -0
- package/dist/src/generated/prisma/models/Archive.js.map +1 -0
- package/dist/src/generated/prisma/models/Entity.d.ts +1129 -0
- package/dist/src/generated/prisma/models/Entity.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/Entity.js +3 -0
- package/dist/src/generated/prisma/models/Entity.js.map +1 -0
- package/dist/src/generated/prisma/models/EntityAlias.d.ts +1347 -0
- package/dist/src/generated/prisma/models/EntityAlias.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/EntityAlias.js +3 -0
- package/dist/src/generated/prisma/models/EntityAlias.js.map +1 -0
- package/dist/src/generated/prisma/models/EntityRelationship.d.ts +1143 -0
- package/dist/src/generated/prisma/models/EntityRelationship.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/EntityRelationship.js +3 -0
- package/dist/src/generated/prisma/models/EntityRelationship.js.map +1 -0
- package/dist/src/generated/prisma/models/KnowledgeEntry.d.ts +1322 -0
- package/dist/src/generated/prisma/models/KnowledgeEntry.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/KnowledgeEntry.js +3 -0
- package/dist/src/generated/prisma/models/KnowledgeEntry.js.map +1 -0
- package/dist/src/generated/prisma/models/WriteReceipt.d.ts +1147 -0
- package/dist/src/generated/prisma/models/WriteReceipt.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/WriteReceipt.js +3 -0
- package/dist/src/generated/prisma/models/WriteReceipt.js.map +1 -0
- package/dist/src/generated/prisma/models.d.ts +8 -0
- package/dist/src/generated/prisma/models.d.ts.map +1 -0
- package/dist/src/generated/prisma/models.js +3 -0
- package/dist/src/generated/prisma/models.js.map +1 -0
- package/dist/src/lib/escalationPaths.d.ts +9 -0
- package/dist/src/lib/escalationPaths.d.ts.map +1 -0
- package/dist/src/lib/escalationPaths.js +38 -0
- package/dist/src/lib/escalationPaths.js.map +1 -0
- package/dist/src/lib/llm.d.ts +32 -0
- package/dist/src/lib/llm.d.ts.map +1 -0
- package/dist/src/lib/llm.js +161 -0
- package/dist/src/lib/llm.js.map +1 -0
- package/dist/src/lib/metrics.d.ts +21 -0
- package/dist/src/lib/metrics.d.ts.map +1 -0
- package/dist/src/lib/metrics.js +46 -0
- package/dist/src/lib/metrics.js.map +1 -0
- package/dist/src/lib/providers/claude.d.ts +7 -0
- package/dist/src/lib/providers/claude.d.ts.map +1 -0
- package/dist/src/lib/providers/claude.js +9 -0
- package/dist/src/lib/providers/claude.js.map +1 -0
- package/dist/src/lib/providers/gemini.d.ts +10 -0
- package/dist/src/lib/providers/gemini.d.ts.map +1 -0
- package/dist/src/lib/providers/gemini.js +40 -0
- package/dist/src/lib/providers/gemini.js.map +1 -0
- package/dist/src/lib/providers/groq.d.ts +10 -0
- package/dist/src/lib/providers/groq.d.ts.map +1 -0
- package/dist/src/lib/providers/groq.js +39 -0
- package/dist/src/lib/providers/groq.js.map +1 -0
- package/dist/src/lib/providers/mistral.d.ts +10 -0
- package/dist/src/lib/providers/mistral.d.ts.map +1 -0
- package/dist/src/lib/providers/mistral.js +39 -0
- package/dist/src/lib/providers/mistral.js.map +1 -0
- package/dist/src/lib/providers/mock.d.ts +24 -0
- package/dist/src/lib/providers/mock.d.ts.map +1 -0
- package/dist/src/lib/providers/mock.js +129 -0
- package/dist/src/lib/providers/mock.js.map +1 -0
- package/dist/src/lib/providers/ollama.d.ts +10 -0
- package/dist/src/lib/providers/ollama.d.ts.map +1 -0
- package/dist/src/lib/providers/ollama.js +39 -0
- package/dist/src/lib/providers/ollama.js.map +1 -0
- package/dist/src/lib/providers/openai.d.ts +11 -0
- package/dist/src/lib/providers/openai.d.ts.map +1 -0
- package/dist/src/lib/providers/openai.js +38 -0
- package/dist/src/lib/providers/openai.js.map +1 -0
- package/dist/src/lib/requestContext.d.ts +8 -0
- package/dist/src/lib/requestContext.d.ts.map +1 -0
- package/dist/src/lib/requestContext.js +10 -0
- package/dist/src/lib/requestContext.js.map +1 -0
- package/dist/src/lib/router.d.ts +16 -0
- package/dist/src/lib/router.d.ts.map +1 -0
- package/dist/src/lib/router.js +63 -0
- package/dist/src/lib/router.js.map +1 -0
- package/dist/src/librarian/chunker.d.ts +16 -0
- package/dist/src/librarian/chunker.d.ts.map +1 -0
- package/dist/src/librarian/chunker.js +67 -0
- package/dist/src/librarian/chunker.js.map +1 -0
- package/dist/src/librarian/getPolicy.d.ts +3 -0
- package/dist/src/librarian/getPolicy.d.ts.map +1 -0
- package/dist/src/librarian/getPolicy.js +22 -0
- package/dist/src/librarian/getPolicy.js.map +1 -0
- package/dist/src/librarian/guards.d.ts +9 -0
- package/dist/src/librarian/guards.d.ts.map +1 -0
- package/dist/src/librarian/guards.js +52 -0
- package/dist/src/librarian/guards.js.map +1 -0
- package/dist/src/librarian/index.d.ts +20 -0
- package/dist/src/librarian/index.d.ts.map +1 -0
- package/dist/src/librarian/index.js +512 -0
- package/dist/src/librarian/index.js.map +1 -0
- package/dist/src/librarian/policy.d.ts +13 -0
- package/dist/src/librarian/policy.d.ts.map +1 -0
- package/dist/src/librarian/policy.js +20 -0
- package/dist/src/librarian/policy.js.map +1 -0
- package/dist/src/librarian/scoring.d.ts +8 -0
- package/dist/src/librarian/scoring.d.ts.map +1 -0
- package/dist/src/librarian/scoring.js +10 -0
- package/dist/src/librarian/scoring.js.map +1 -0
- package/dist/src/librarian/source-reliability.d.ts +8 -0
- package/dist/src/librarian/source-reliability.d.ts.map +1 -0
- package/dist/src/librarian/source-reliability.js +105 -0
- package/dist/src/librarian/source-reliability.js.map +1 -0
- package/dist/src/library/agent-registry.d.ts +31 -0
- package/dist/src/library/agent-registry.d.ts.map +1 -0
- package/dist/src/library/agent-registry.js +197 -0
- package/dist/src/library/agent-registry.js.map +1 -0
- package/dist/src/library/client.d.ts +5 -0
- package/dist/src/library/client.d.ts.map +1 -0
- package/dist/src/library/client.js +39 -0
- package/dist/src/library/client.js.map +1 -0
- package/dist/src/library/entity-resolution.d.ts +47 -0
- package/dist/src/library/entity-resolution.d.ts.map +1 -0
- package/dist/src/library/entity-resolution.js +344 -0
- package/dist/src/library/entity-resolution.js.map +1 -0
- package/dist/src/library/locks.d.ts +9 -0
- package/dist/src/library/locks.d.ts.map +1 -0
- package/dist/src/library/locks.js +38 -0
- package/dist/src/library/locks.js.map +1 -0
- package/dist/src/library/queries.d.ts +66 -0
- package/dist/src/library/queries.d.ts.map +1 -0
- package/dist/src/library/queries.js +169 -0
- package/dist/src/library/queries.js.map +1 -0
- package/dist/src/library/relationships.d.ts +30 -0
- package/dist/src/library/relationships.d.ts.map +1 -0
- package/dist/src/library/relationships.js +97 -0
- package/dist/src/library/relationships.js.map +1 -0
- package/dist/src/sdk/index.d.ts +108 -0
- package/dist/src/sdk/index.d.ts.map +1 -0
- package/dist/src/sdk/index.js +323 -0
- package/dist/src/sdk/index.js.map +1 -0
- package/dist/src/security/apiKeys.d.ts +48 -0
- package/dist/src/security/apiKeys.d.ts.map +1 -0
- package/dist/src/security/apiKeys.js +279 -0
- package/dist/src/security/apiKeys.js.map +1 -0
- package/dist/src/types.d.ts +54 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +4 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +86 -0
- package/prisma/migrations/20260228090200_init/migration.sql +49 -0
- package/prisma/migrations/20260228121746_add_properties_and_relationships/migration.sql +29 -0
- package/prisma/migrations/20260301223834_add_superseded_by_pointer/migration.sql +4 -0
- package/prisma/migrations/20260301225152_add_write_receipts/migration.sql +20 -0
- package/prisma/migrations/20260302135650_entity_resolution/migration.sql +33 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +118 -0
package/.env.example
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Database
|
|
2
|
+
DATABASE_URL=postgresql://postgres:yourpassword@localhost:5432/iranti
|
|
3
|
+
POSTGRES_PASSWORD=yourpassword
|
|
4
|
+
|
|
5
|
+
# LLM Provider (mock | gemini | claude | openai | groq | mistral | ollama)
|
|
6
|
+
LLM_PROVIDER=mock
|
|
7
|
+
|
|
8
|
+
# Fallback chain (comma-separated, tried in order if primary fails)
|
|
9
|
+
# Always falls back to mock as final safety net
|
|
10
|
+
LLM_PROVIDER_FALLBACK=openai,groq,mistral,mock
|
|
11
|
+
|
|
12
|
+
# Gemini (when LLM_PROVIDER=gemini)
|
|
13
|
+
GEMINI_API_KEY=
|
|
14
|
+
GEMINI_MODEL=gemini-2.0-flash-001
|
|
15
|
+
|
|
16
|
+
# Model routing overrides (optional)
|
|
17
|
+
CLASSIFICATION_MODEL=gemini-2.0-flash-001
|
|
18
|
+
RELEVANCE_MODEL=gemini-2.0-flash-001
|
|
19
|
+
CONFLICT_MODEL=gemini-2.5-pro
|
|
20
|
+
SUMMARIZATION_MODEL=gemini-2.0-flash-001
|
|
21
|
+
TASK_INFERENCE_MODEL=gemini-2.0-flash-001
|
|
22
|
+
EXTRACTION_MODEL=gemini-2.0-flash-001
|
|
23
|
+
|
|
24
|
+
# Anthropic (when LLM_PROVIDER=claude)
|
|
25
|
+
ANTHROPIC_API_KEY=
|
|
26
|
+
|
|
27
|
+
# OpenAI (when LLM_PROVIDER=openai)
|
|
28
|
+
OPENAI_API_KEY=
|
|
29
|
+
OPENAI_MODEL=gpt-4o-mini
|
|
30
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
31
|
+
|
|
32
|
+
# Groq (when LLM_PROVIDER=groq)
|
|
33
|
+
GROQ_API_KEY=
|
|
34
|
+
GROQ_MODEL=llama-3.3-70b-versatile
|
|
35
|
+
|
|
36
|
+
# Mistral (when LLM_PROVIDER=mistral)
|
|
37
|
+
MISTRAL_API_KEY=
|
|
38
|
+
MISTRAL_MODEL=mistral-small-latest
|
|
39
|
+
|
|
40
|
+
# Ollama (when LLM_PROVIDER=ollama, local, no API key needed)
|
|
41
|
+
OLLAMA_MODEL=llama3.2
|
|
42
|
+
OLLAMA_BASE_URL=http://localhost:11434
|
|
43
|
+
|
|
44
|
+
# API Server
|
|
45
|
+
IRANTI_PORT=3001
|
|
46
|
+
IRANTI_API_KEY=your_secret_api_key_here
|
|
47
|
+
# Optional additional legacy keys (comma-separated plaintext)
|
|
48
|
+
IRANTI_API_KEYS=
|
|
49
|
+
# Optional pepper used when hashing registry key secrets
|
|
50
|
+
IRANTI_API_KEY_PEPPER=
|
|
51
|
+
|
|
52
|
+
# Escalation storage and Archivist automation
|
|
53
|
+
# Default escalation root (if unset): ~/.iranti/escalation
|
|
54
|
+
IRANTI_ESCALATION_DIR=
|
|
55
|
+
# Watch escalation/active for changes and run maintenance after debounce
|
|
56
|
+
IRANTI_ARCHIVIST_WATCH=true
|
|
57
|
+
# Delay after the last escalation file change before maintenance runs
|
|
58
|
+
IRANTI_ARCHIVIST_DEBOUNCE_MS=60000
|
|
59
|
+
# Periodic maintenance interval; set >0 to enable (e.g. 21600000 = 6h)
|
|
60
|
+
IRANTI_ARCHIVIST_INTERVAL_MS=0
|
|
61
|
+
|
|
62
|
+
# Node environment
|
|
63
|
+
NODE_ENV=development
|
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
5
|
+
|
|
6
|
+
This project is licensed under the GNU Affero General Public License v3.0
|
|
7
|
+
or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
You may obtain a copy of the full license text at:
|
|
10
|
+
https://www.gnu.org/licenses/agpl-3.0.txt
|
|
11
|
+
|
|
12
|
+
SPDX-License-Identifier: AGPL-3.0-or-later
|
package/README.md
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# Iranti
|
|
2
|
+
|
|
3
|
+
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](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, not similarity search. Memory persists across sessions and survives context window limits.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What is Iranti?
|
|
15
|
+
|
|
16
|
+
Iranti is a knowledge base for multi-agent systems. Unlike vector databases that retrieve by semantic similarity, Iranti retrieves by identity — 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.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Runtime Roles
|
|
21
|
+
|
|
22
|
+
- **User**: Person who interacts with an app or chatbot built on Iranti.
|
|
23
|
+
- **Agent**: External AI worker that writes/reads facts through Iranti APIs.
|
|
24
|
+
- **Attendant**: Per-agent memory manager that decides what to inject for each turn.
|
|
25
|
+
- **Librarian**: Conflict-aware writer that owns all KB writes.
|
|
26
|
+
- **Library**: Active truth store (`knowledge_base`) in PostgreSQL.
|
|
27
|
+
- **Archive**: Historical/superseded truth store (`archive`) in PostgreSQL.
|
|
28
|
+
- **Archivist**: Maintenance worker that archives stale/low-confidence facts and processes resolved escalations.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why Not a Vector Database?
|
|
33
|
+
|
|
34
|
+
| Feature | Vector DB | Iranti |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| **Retrieval** | Similarity (nearest neighbor) | Identity (entity+key) |
|
|
37
|
+
| **Storage** | Embeddings in vector space | Structured facts with keys |
|
|
38
|
+
| **Persistence** | Stateless between calls | Persistent across sessions |
|
|
39
|
+
| **Confidence** | No confidence tracking | Per-fact confidence scores |
|
|
40
|
+
| **Conflicts** | No conflict resolution | Automatic resolution + escalation |
|
|
41
|
+
| **Context** | No context awareness | `observe()` injects missing facts |
|
|
42
|
+
|
|
43
|
+
Vector databases answer "what's similar to X?" Iranti answers "what do we know about X?"
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Validated Results
|
|
48
|
+
|
|
49
|
+
All five goals validated with fictional entities and invented facts that GPT-4o-mini cannot know from training data.
|
|
50
|
+
|
|
51
|
+
| Goal | Experiment | Score | Status |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| **1. Easy Integration** | Raw HTTP (9 lines) | 3/3 facts | ✓ PASSED |
|
|
54
|
+
| **2. Context Persistence** | observe() API | 6/6 injected | ✓ PASSED |
|
|
55
|
+
| **3. Working Retrieval** | Cross-agent query | 5/5 facts | ✓ PASSED |
|
|
56
|
+
| **4. Per-Agent Persistence** | Cross-process | 5/5 facts | ✓ PASSED |
|
|
57
|
+
| **5. Response Quality** | Memory injection | 0/2 → 2/2 | ✓ PASSED |
|
|
58
|
+
|
|
59
|
+
### Framework Compatibility
|
|
60
|
+
|
|
61
|
+
Validated with multiple agent frameworks:
|
|
62
|
+
|
|
63
|
+
| Framework | Entity | Facts | Score | Time |
|
|
64
|
+
|---|---|---|---|---|
|
|
65
|
+
| **Raw OpenAI API** | project/void_runner | 5 | 5/5 ✓ | 14.0s |
|
|
66
|
+
| **LangChain** | project/stellar_drift | 5 | 5/5 ✓ | 2.9s |
|
|
67
|
+
| **CrewAI** | project/nexus_prime | 6 | 6/6 ✓ | 60s |
|
|
68
|
+
|
|
69
|
+
**Total: 16/16 facts transferred (100%)**
|
|
70
|
+
|
|
71
|
+
Full validation report: [`docs/internal/validation_results.md`](docs/internal/validation_results.md) | Multi-framework details: [`docs/internal/MULTI_FRAMEWORK_VALIDATION.md`](docs/internal/MULTI_FRAMEWORK_VALIDATION.md)
|
|
72
|
+
|
|
73
|
+
### Goal 1: Easy Integration
|
|
74
|
+
|
|
75
|
+
- **Entity**: `project/quantum_bridge`
|
|
76
|
+
- **Test**: Integrate Iranti with raw HTTP in under 20 lines of Python
|
|
77
|
+
- **Result**: 9 lines of code, 3/3 facts written and retrieved
|
|
78
|
+
- **Conclusion**: No SDK or framework dependencies required, just standard `requests` library
|
|
79
|
+
|
|
80
|
+
### Goal 2: Context Persistence
|
|
81
|
+
|
|
82
|
+
- **Entity**: `project/nexus_prime`
|
|
83
|
+
- **Control**: Facts already in context → `observe()` returns 0 to inject (correct, avoids duplication)
|
|
84
|
+
- **Treatment**: Facts missing from context → `observe()` returns 6/6 facts for injection
|
|
85
|
+
- **Result**: 100% recovery rate when facts fall out of context window
|
|
86
|
+
|
|
87
|
+
### Goal 3: Working Retrieval
|
|
88
|
+
|
|
89
|
+
- **Entity**: `project/photon_cascade`
|
|
90
|
+
- **Test**: Agent 2 retrieves facts written by Agent 1 with zero shared context
|
|
91
|
+
- **Result**: 5/5 facts retrieved via identity-based lookup (entity+key)
|
|
92
|
+
- **Conclusion**: Facts accessible across agents with no context window dependency
|
|
93
|
+
|
|
94
|
+
### Goal 4: Per-Agent Knowledge Persistence
|
|
95
|
+
|
|
96
|
+
- **Entity**: `project/resonance_field`
|
|
97
|
+
- **Test**: Process 1 writes facts and exits, Process 2 reads in new process
|
|
98
|
+
- **Result**: 5/5 facts retrieved with no shared state between processes
|
|
99
|
+
- **Conclusion**: PostgreSQL storage validated, facts survive across sessions
|
|
100
|
+
|
|
101
|
+
### Goal 5: Response Quality
|
|
102
|
+
|
|
103
|
+
- **Entity**: `project/meridian_core`
|
|
104
|
+
- **Test**: Ask LLM question requiring facts from earlier in long conversation
|
|
105
|
+
- **Control**: Without Iranti → 0/2 facts correct (hallucinated answers)
|
|
106
|
+
- **Treatment**: With Iranti memory injection → 2/2 facts correct (accurate answers)
|
|
107
|
+
- **Conclusion**: Memory injection eliminates hallucination, improves response accuracy
|
|
108
|
+
|
|
109
|
+
Full validation report: [`docs/internal/validation_results.md`](docs/internal/validation_results.md)
|
|
110
|
+
|
|
111
|
+
## Quickstart
|
|
112
|
+
|
|
113
|
+
**Requirements**: Node.js 18+, Docker, Python 3.8+
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# 1. Clone and configure
|
|
117
|
+
git clone https://github.com/nfemmanuel/iranti
|
|
118
|
+
cd iranti
|
|
119
|
+
cp .env.example .env # Set DATABASE_URL and IRANTI_API_KEY
|
|
120
|
+
|
|
121
|
+
# Optional runtime hygiene
|
|
122
|
+
# IRANTI_ESCALATION_DIR=C:/Users/<you>/.iranti/escalation
|
|
123
|
+
# IRANTI_ARCHIVIST_WATCH=true
|
|
124
|
+
# IRANTI_ARCHIVIST_DEBOUNCE_MS=60000
|
|
125
|
+
# IRANTI_ARCHIVIST_INTERVAL_MS=21600000
|
|
126
|
+
|
|
127
|
+
# 2. Start PostgreSQL
|
|
128
|
+
docker-compose up -d
|
|
129
|
+
|
|
130
|
+
# 3. Install and initialize
|
|
131
|
+
npm install
|
|
132
|
+
npm run setup # Runs migrations
|
|
133
|
+
|
|
134
|
+
# 4. Start API server
|
|
135
|
+
npm run api # Runs on port 3001
|
|
136
|
+
|
|
137
|
+
# 5. Install Python client
|
|
138
|
+
pip install iranti
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Archivist Scheduling Knobs
|
|
142
|
+
|
|
143
|
+
- `IRANTI_ARCHIVIST_WATCH=true` enables file-change watching on escalation `active/`.
|
|
144
|
+
- `IRANTI_ARCHIVIST_DEBOUNCE_MS=60000` runs maintenance 60s after the latest file change.
|
|
145
|
+
- `IRANTI_ARCHIVIST_INTERVAL_MS=21600000` runs maintenance every 6 hours (set `0` to disable).
|
|
146
|
+
- `IRANTI_ESCALATION_DIR` sets escalation storage root. Default is `~/.iranti/escalation`, keeping escalation files out of the repo by default.
|
|
147
|
+
|
|
148
|
+
### Per-User API Keys (Recommended)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Create a key for one user/app (prints token once)
|
|
152
|
+
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"
|
|
153
|
+
|
|
154
|
+
# List keys
|
|
155
|
+
npm run api-key:list
|
|
156
|
+
|
|
157
|
+
# Revoke a key
|
|
158
|
+
npm run api-key:revoke -- --key-id chatbot_alice
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Use the printed token (`keyId.secret`) as `X-Iranti-Key`.
|
|
162
|
+
Scopes use `resource:action` format (for example `kb:read`, `memory:write`, `metrics:read`, `proxy:chat`).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Install Strategy (Double Layer)
|
|
167
|
+
|
|
168
|
+
Iranti now supports a two-layer install flow:
|
|
169
|
+
|
|
170
|
+
1. **Machine/runtime layer**: one local runtime root with one or more named Iranti instances.
|
|
171
|
+
2. **Project layer**: each chatbot/app binds to one instance with a local `.env.iranti`.
|
|
172
|
+
|
|
173
|
+
### 1) Install CLI
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# If published package is available
|
|
177
|
+
npm install -g iranti
|
|
178
|
+
|
|
179
|
+
# Or from this repo (local simulation)
|
|
180
|
+
npm install -g .
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 2) Initialize machine runtime root
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
iranti install --scope user
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Defaults:
|
|
190
|
+
- Windows user scope: `%USERPROFILE%\\.iranti`
|
|
191
|
+
- Windows system scope: `%ProgramData%\\Iranti`
|
|
192
|
+
- Linux system scope: `/var/lib/iranti`
|
|
193
|
+
- macOS system scope: `/Library/Application Support/Iranti`
|
|
194
|
+
|
|
195
|
+
### 3) Create a named instance
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
iranti instance create local --port 3001 --db-url "postgresql://postgres:yourpassword@localhost:5432/iranti_local"
|
|
199
|
+
iranti instance show local
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Then edit the printed instance `.env` file and set:
|
|
203
|
+
- `DATABASE_URL` (real value)
|
|
204
|
+
- `IRANTI_API_KEY` (real token)
|
|
205
|
+
|
|
206
|
+
### 4) Run Iranti from that instance
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
iranti run --instance local
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 5) Bind any chatbot/app project to that instance
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
cd /path/to/your/chatbot
|
|
216
|
+
iranti project init . --instance local --agent-id chatbot_main
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This writes `.env.iranti` in the project with the correct `IRANTI_URL`, `IRANTI_API_KEY`, and default agent identity.
|
|
220
|
+
|
|
221
|
+
For multi-agent systems, bind once per project and set unique agent IDs per worker (for example `planner_agent`, `research_agent`, `critic_agent`).
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Core API
|
|
226
|
+
|
|
227
|
+
### Write a Fact
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from clients.python.iranti import IrantiClient
|
|
231
|
+
|
|
232
|
+
client = IrantiClient(
|
|
233
|
+
base_url="http://localhost:3001",
|
|
234
|
+
api_key="your_api_key_here"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
result = client.write(
|
|
238
|
+
entity="researcher/jane_smith", # Format: entityType/entityId
|
|
239
|
+
key="affiliation",
|
|
240
|
+
value={"institution": "MIT", "department": "CSAIL"},
|
|
241
|
+
summary="Affiliated with MIT CSAIL", # Compressed for working memory
|
|
242
|
+
confidence=85, # 0-100
|
|
243
|
+
source="OpenAlex",
|
|
244
|
+
agent="research_agent_001"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
print(result.action) # 'created', 'updated', 'escalated', or 'rejected'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Query a Fact
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
result = client.query("researcher/jane_smith", "affiliation")
|
|
254
|
+
|
|
255
|
+
if result.found:
|
|
256
|
+
print(result.value) # {"institution": "MIT", "department": "CSAIL"}
|
|
257
|
+
print(result.confidence) # 85
|
|
258
|
+
print(result.source) # "OpenAlex"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Query All Facts for an Entity
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
facts = client.query_all("researcher/jane_smith")
|
|
265
|
+
|
|
266
|
+
for fact in facts:
|
|
267
|
+
print(f"[{fact['key']}] {fact['summary']} (confidence: {fact['confidence']})")
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Context Persistence (attend)
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
# Before each LLM call, let Attendant decide if memory is needed
|
|
274
|
+
result = client.attend(
|
|
275
|
+
agent_id="research_agent_001",
|
|
276
|
+
latest_message="What's Jane Smith's current affiliation?",
|
|
277
|
+
current_context="User: What's Jane Smith's current affiliation?\nAssistant: Let me check...",
|
|
278
|
+
max_facts=5
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
if result["shouldInject"]:
|
|
282
|
+
for fact in result['facts']:
|
|
283
|
+
print(f"Inject: [{fact['entityKey']}] {fact['summary']}")
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Working Memory (handshake)
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
# At session start, get personalized brief for agent's current task
|
|
290
|
+
brief = client.handshake(
|
|
291
|
+
agent="research_agent_001",
|
|
292
|
+
task="Research publication history for Dr. Jane Smith",
|
|
293
|
+
recent_messages=["Starting literature review..."]
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
print(brief.operating_rules) # Staff namespace rules for this agent
|
|
297
|
+
print(brief.inferred_task_type) # e.g. "research", "verification"
|
|
298
|
+
|
|
299
|
+
for entry in brief.working_memory:
|
|
300
|
+
print(f"{entry.entity_key}: {entry.summary}")
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## CrewAI Integration
|
|
306
|
+
|
|
307
|
+
Minimal working example based on validated experiments:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from crewai import Agent, Task, Crew, LLM
|
|
311
|
+
from crewai.tools import tool
|
|
312
|
+
from clients.python.iranti import IrantiClient
|
|
313
|
+
|
|
314
|
+
iranti = IrantiClient(base_url="http://localhost:3001", api_key="your_key")
|
|
315
|
+
ENTITY = "project/my_project"
|
|
316
|
+
|
|
317
|
+
@tool("Write finding to shared memory")
|
|
318
|
+
def write_finding(key: str, value: str, summary: str, confidence: int) -> str:
|
|
319
|
+
"""Write a fact to Iranti so other agents can access it."""
|
|
320
|
+
result = iranti.write(
|
|
321
|
+
entity=ENTITY,
|
|
322
|
+
key=key,
|
|
323
|
+
value={"data": value},
|
|
324
|
+
summary=summary,
|
|
325
|
+
confidence=confidence,
|
|
326
|
+
source="briefing_doc",
|
|
327
|
+
agent="researcher_agent"
|
|
328
|
+
)
|
|
329
|
+
return f"Saved '{key}': {result.action}"
|
|
330
|
+
|
|
331
|
+
@tool("Get all findings")
|
|
332
|
+
def get_all_findings() -> str:
|
|
333
|
+
"""Load all facts from Iranti."""
|
|
334
|
+
facts = iranti.query_all(ENTITY)
|
|
335
|
+
if not facts:
|
|
336
|
+
return "No findings in shared memory."
|
|
337
|
+
lines = [f"[{f['key']}] {f['summary']} (confidence: {f['confidence']})" for f in facts]
|
|
338
|
+
return "\n".join(lines)
|
|
339
|
+
|
|
340
|
+
# Researcher agent: writes to Iranti
|
|
341
|
+
researcher = Agent(
|
|
342
|
+
role="Research Analyst",
|
|
343
|
+
goal="Extract facts from documents and save to shared memory",
|
|
344
|
+
tools=[write_finding],
|
|
345
|
+
llm=LLM(model="gpt-4o-mini")
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# Analyst agent: reads from Iranti
|
|
349
|
+
analyst = Agent(
|
|
350
|
+
role="Project Analyst",
|
|
351
|
+
goal="Summarize projects using shared memory",
|
|
352
|
+
tools=[get_all_findings],
|
|
353
|
+
llm=LLM(model="gpt-4o-mini")
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Researcher extracts facts, analyst loads them — no direct communication needed
|
|
357
|
+
crew = Crew(agents=[researcher, analyst], tasks=[...])
|
|
358
|
+
crew.kickoff()
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Result**: Analyst successfully loads all facts written by researcher (validated 6/6 transfer rate).
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Middleware for Any LLM
|
|
366
|
+
|
|
367
|
+
Add Iranti memory to Claude, ChatGPT, or any LLM via API wrapper:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from clients.middleware.iranti_middleware import IrantiMiddleware
|
|
371
|
+
|
|
372
|
+
middleware = IrantiMiddleware(
|
|
373
|
+
agent_id="my_agent",
|
|
374
|
+
iranti_url="http://localhost:3001"
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Before sending to LLM
|
|
378
|
+
augmented = middleware.before_send(
|
|
379
|
+
user_message="What was the blocker?",
|
|
380
|
+
conversation_history=[...]
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# After receiving response
|
|
384
|
+
middleware.after_receive(
|
|
385
|
+
response="The blocker is...",
|
|
386
|
+
conversation_history=[...]
|
|
387
|
+
)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**How it works**:
|
|
391
|
+
1. `before_send()` calls `attend()` with conversation context
|
|
392
|
+
2. Forgotten facts are prepended as `[MEMORY: ...]`
|
|
393
|
+
3. `after_receive()` extracts new facts and saves them (best-effort)
|
|
394
|
+
|
|
395
|
+
**Note**: Browser extensions are blocked by ChatGPT and Claude's Content Security Policy. Use API-based middleware instead.
|
|
396
|
+
|
|
397
|
+
**Examples**: [`clients/middleware/claude_example.py`](clients/middleware/claude_example.py)
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Architecture
|
|
402
|
+
|
|
403
|
+
Iranti has four internal components:
|
|
404
|
+
|
|
405
|
+
| Component | Role |
|
|
406
|
+
|---|---|
|
|
407
|
+
| **Library** | PostgreSQL knowledge base. Active truth (soft-deleted entries marked as archived). Full provenance in Archive table. |
|
|
408
|
+
| **Librarian** | Manages all writes. Detects conflicts, reasons about resolution, escalates when uncertain. |
|
|
409
|
+
| **Attendant** | Per-agent working memory manager. Implements `attend()`, `observe()`, and `handshake()` APIs. |
|
|
410
|
+
| **Archivist** | Periodic cleanup. Archives expired and low-confidence entries. Processes human-resolved conflicts. |
|
|
411
|
+
|
|
412
|
+
### REST API
|
|
413
|
+
|
|
414
|
+
Express server on port 3001 with endpoints:
|
|
415
|
+
|
|
416
|
+
- `POST /kb/write` - Write atomic fact
|
|
417
|
+
- `POST /kb/ingest` - Ingest raw text, auto-chunk into facts
|
|
418
|
+
- `GET /kb/query/:entityType/:entityId/:key` - Query specific fact
|
|
419
|
+
- `GET /kb/query/:entityType/:entityId` - Query all facts for entity
|
|
420
|
+
- `POST /memory/attend` - Decide whether to inject memory for this turn
|
|
421
|
+
- `POST /memory/observe` - Context persistence (inject missing facts)
|
|
422
|
+
- `POST /memory/handshake` - Working memory brief for agent session
|
|
423
|
+
- `POST /kb/relate` - Create entity relationship
|
|
424
|
+
- `GET /kb/related/:entityType/:entityId` - Get related entities
|
|
425
|
+
- `POST /agents/register` - Register agent in registry
|
|
426
|
+
|
|
427
|
+
All endpoints require `X-Iranti-Key` header for authentication.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Schema
|
|
432
|
+
|
|
433
|
+
Three PostgreSQL tables:
|
|
434
|
+
|
|
435
|
+
```
|
|
436
|
+
knowledge_base — active truth (archived entries soft-deleted with confidence=0)
|
|
437
|
+
archive — full provenance history, never deleted, includes supersededBy links
|
|
438
|
+
entity_relationships — directional graph: MEMBER_OF, PART_OF, AUTHORED, etc.
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Every table has a `properties` JSON column for caller-defined metadata. New entity types, relationship types, and fact keys never require migrations — they are just strings you define.
|
|
442
|
+
|
|
443
|
+
**Archive semantics**: When an entry is archived, it remains in knowledge_base with confidence set to 0 and summary marked as `[ARCHIVED]`. A full copy is written to the archive table with supersededBy linking for traceability. Nothing is ever truly deleted.
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Running Tests
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
npm run test:integration # Full end-to-end
|
|
451
|
+
npm run test:librarian # Conflict resolution
|
|
452
|
+
npm run test:attendant # Working memory
|
|
453
|
+
npm run test:reliability # Source scoring
|
|
454
|
+
|
|
455
|
+
# Python validation experiments
|
|
456
|
+
cd clients/experiments
|
|
457
|
+
python validate_nexus_observe.py # Context persistence
|
|
458
|
+
python validate_nexus_treatment.py # Cross-agent transfer
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Contributing
|
|
464
|
+
|
|
465
|
+
Contributions welcome! Please:
|
|
466
|
+
|
|
467
|
+
1. Fork the repository
|
|
468
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
469
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
470
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
471
|
+
5. Open a Pull Request
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## License
|
|
476
|
+
|
|
477
|
+
GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
|
|
478
|
+
|
|
479
|
+
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.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Name
|
|
484
|
+
|
|
485
|
+
Iranti is the Yoruba word for memory and remembrance.
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Project Structure
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
src/
|
|
493
|
+
├── library/ — DB client, queries, relationships, agent registry
|
|
494
|
+
├── librarian/ — Write logic, conflict resolution, reliability
|
|
495
|
+
├── attendant/ — Per-agent working memory, observe() implementation
|
|
496
|
+
├── archivist/ — Periodic cleanup, escalation processing
|
|
497
|
+
├── lib/ — LLM abstraction, model router, providers
|
|
498
|
+
├── sdk/ — Public TypeScript API
|
|
499
|
+
└── api/ — REST API server
|
|
500
|
+
|
|
501
|
+
clients/
|
|
502
|
+
├── python/ — Python client (IrantiClient)
|
|
503
|
+
├── middleware/ — LLM conversation wrappers (Claude, ChatGPT, etc.)
|
|
504
|
+
└── experiments/ — Validated experiments with real results
|
|
505
|
+
|
|
506
|
+
docs/
|
|
507
|
+
└── internal/validation_results.md — Full experiment outputs and analysis
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Support
|
|
513
|
+
|
|
514
|
+
- **Issues**: [GitHub Issues](https://github.com/nfemmanuel/iranti/issues)
|
|
515
|
+
- **Discussions**: [GitHub Discussions](https://github.com/nfemmanuel/iranti/discussions)
|
|
516
|
+
- **Email**: oluwaniifemi.emmanuel@uni.minerva.edu
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
**Built with ❤️ for the multi-agent AI community.**
|
package/bin/iranti.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const root = path.resolve(__dirname, '..');
|
|
8
|
+
const distCli = path.join(root, 'dist', 'scripts', 'iranti-cli.js');
|
|
9
|
+
const srcCli = path.join(root, 'scripts', 'iranti-cli.ts');
|
|
10
|
+
|
|
11
|
+
if (fs.existsSync(distCli)) {
|
|
12
|
+
require(distCli);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
require('ts-node/register/transpile-only');
|
|
18
|
+
require(srcCli);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21
|
+
console.error('Iranti CLI is not built. Run "npm run build" first.');
|
|
22
|
+
console.error(message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("dotenv/config");
|
|
4
|
+
const client_1 = require("../src/library/client");
|
|
5
|
+
const apiKeys_1 = require("../src/security/apiKeys");
|
|
6
|
+
function parseArg(flag) {
|
|
7
|
+
const idx = process.argv.indexOf(flag);
|
|
8
|
+
if (idx === -1)
|
|
9
|
+
return undefined;
|
|
10
|
+
return process.argv[idx + 1];
|
|
11
|
+
}
|
|
12
|
+
async function main() {
|
|
13
|
+
const keyId = parseArg('--key-id') ?? parseArg('-k');
|
|
14
|
+
const owner = parseArg('--owner') ?? parseArg('-o');
|
|
15
|
+
const scopesRaw = parseArg('--scopes') ?? '';
|
|
16
|
+
const description = parseArg('--description');
|
|
17
|
+
const scopes = scopesRaw
|
|
18
|
+
.split(',')
|
|
19
|
+
.map((s) => s.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
if (!keyId || !owner) {
|
|
22
|
+
console.error('Usage: npm run api-key:create -- --key-id <id> --owner <owner> [--scopes read,write] [--description "text"]');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
26
|
+
if (!dbUrl) {
|
|
27
|
+
console.error('DATABASE_URL is required.');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
(0, client_1.initDb)(dbUrl);
|
|
31
|
+
const created = await (0, apiKeys_1.createOrRotateApiKey)({
|
|
32
|
+
keyId,
|
|
33
|
+
owner,
|
|
34
|
+
scopes,
|
|
35
|
+
description,
|
|
36
|
+
});
|
|
37
|
+
console.log('\nAPI key created (or rotated):');
|
|
38
|
+
console.log(` keyId: ${created.record.keyId}`);
|
|
39
|
+
console.log(` owner: ${created.record.owner}`);
|
|
40
|
+
console.log(` scopes: ${created.record.scopes.join(',') || '(none)'}`);
|
|
41
|
+
console.log(` active: ${created.record.isActive}`);
|
|
42
|
+
console.log('\nCopy this token now (it will not be shown again):');
|
|
43
|
+
console.log(created.token);
|
|
44
|
+
console.log('\nUse it as: X-Iranti-Key: <token>\n');
|
|
45
|
+
await (0, client_1.getDb)().$disconnect();
|
|
46
|
+
}
|
|
47
|
+
main().catch(async (err) => {
|
|
48
|
+
console.error('Failed to create API key:', err instanceof Error ? err.message : String(err));
|
|
49
|
+
try {
|
|
50
|
+
await (0, client_1.getDb)().$disconnect();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// ignore
|
|
54
|
+
}
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=api-key-create.js.map
|