iranti 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -5
- package/dist/scripts/claude-code-memory-hook.js +3 -1
- package/dist/scripts/codex-setup.js +150 -0
- package/dist/scripts/iranti-cli.js +367 -29
- package/dist/scripts/iranti-mcp.js +39 -1
- package/dist/src/api/middleware/rateLimit.d.ts +15 -14
- package/dist/src/api/middleware/rateLimit.d.ts.map +1 -1
- package/dist/src/api/middleware/rateLimit.js +110 -25
- package/dist/src/api/middleware/rateLimit.js.map +1 -1
- package/dist/src/api/server.js +10 -1
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/lib/cliHelpCatalog.d.ts.map +1 -1
- package/dist/src/lib/cliHelpCatalog.js +18 -0
- package/dist/src/lib/cliHelpCatalog.js.map +1 -1
- package/dist/src/lib/hostMemoryFormatting.d.ts.map +1 -1
- package/dist/src/lib/hostMemoryFormatting.js +19 -2
- package/dist/src/lib/hostMemoryFormatting.js.map +1 -1
- package/dist/src/lib/scaffoldCloseout.d.ts +1 -1
- package/dist/src/lib/scaffoldCloseout.d.ts.map +1 -1
- package/dist/src/lib/scaffoldCloseout.js +15 -7
- package/dist/src/lib/scaffoldCloseout.js.map +1 -1
- package/dist/src/security/apiKeys.d.ts.map +1 -1
- package/dist/src/security/apiKeys.js +18 -3
- package/dist/src/security/apiKeys.js.map +1 -1
- package/package.json +18 -2
package/README.md
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
# Iranti
|
|
2
2
|
|
|
3
3
|
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
4
|
+
[](https://modelcontextprotocol.io)
|
|
5
|
+
[](https://www.npmjs.com/package/iranti)
|
|
4
6
|
[](https://www.python.org/downloads/)
|
|
5
7
|
[](https://www.typescriptlang.org/)
|
|
6
8
|
[](https://www.crewai.com/)
|
|
7
9
|
|
|
8
|
-
**Memory infrastructure for multi-agent AI systems.**
|
|
10
|
+
**Memory infrastructure for multi-agent AI systems — with a built-in MCP server.**
|
|
9
11
|
|
|
10
12
|
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
13
|
|
|
12
|
-
**Repo version:** `0.3.
|
|
14
|
+
**Repo version:** `0.3.11`
|
|
13
15
|
Published packages:
|
|
14
|
-
- npm `iranti@0.3.
|
|
15
|
-
- npm `@iranti/sdk@0.3.
|
|
16
|
-
- PyPI `iranti==0.3.
|
|
16
|
+
- npm `iranti@0.3.11`
|
|
17
|
+
- npm `@iranti/sdk@0.3.11`
|
|
18
|
+
- PyPI `iranti==0.3.11`
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
@@ -23,6 +25,32 @@ Iranti is a knowledge base for multi-agent systems. The primary read path is ide
|
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
28
|
+
## MCP Server
|
|
29
|
+
|
|
30
|
+
Iranti ships a stdio MCP server compatible with Claude Code, GitHub Copilot, Codex, and any MCP-compliant client:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Fast path for Claude Code
|
|
34
|
+
iranti claude-setup
|
|
35
|
+
|
|
36
|
+
# Fast path for Codex
|
|
37
|
+
iranti codex-setup
|
|
38
|
+
|
|
39
|
+
# Fast path for GitHub Copilot
|
|
40
|
+
iranti copilot-setup
|
|
41
|
+
|
|
42
|
+
# Manual / other MCP clients
|
|
43
|
+
iranti mcp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
MCP tools exposed: `iranti_handshake`, `iranti_attend`, `iranti_write`, `iranti_query`, `iranti_search`, `iranti_checkpoint`, `iranti_ingest`, `iranti_relate`, `iranti_related`, `iranti_related_deep`, `iranti_history`, `iranti_who_knows`, `iranti_observe`, and more.
|
|
47
|
+
|
|
48
|
+
Full setup guides:
|
|
49
|
+
- [Claude Code](docs/guides/claude-code.md)
|
|
50
|
+
- [Codex](docs/guides/codex.md)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
26
54
|
## Runtime Roles
|
|
27
55
|
|
|
28
56
|
- **User**: Person who interacts with an app or chatbot built on Iranti.
|
|
@@ -315,7 +315,9 @@ function emitHookContext(event, additionalContext) {
|
|
|
315
315
|
additionalContext,
|
|
316
316
|
},
|
|
317
317
|
};
|
|
318
|
-
|
|
318
|
+
// Use synchronous write to fd 1 to avoid libuv UV_HANDLE_CLOSING assertion
|
|
319
|
+
// on Windows when Node exits while an async stdout write is still pending.
|
|
320
|
+
require('fs').writeSync(1, `${JSON.stringify(payload)}\n`);
|
|
319
321
|
}
|
|
320
322
|
function shouldFetchMemory(prompt) {
|
|
321
323
|
const normalized = prompt.trim();
|
|
@@ -364,6 +364,147 @@ function writeWorkspaceVsCodeMcpFile(projectEnv, options) {
|
|
|
364
364
|
}, null, 2)}\n`, 'utf8');
|
|
365
365
|
return { filePath: mcpFile, status: 'updated' };
|
|
366
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Protocol reminder hook script content for Codex UserPromptSubmit.
|
|
369
|
+
* Same content as the Claude Code version — fires before every user prompt.
|
|
370
|
+
*/
|
|
371
|
+
function buildProtocolReminderHookScript() {
|
|
372
|
+
return [
|
|
373
|
+
'#!/usr/bin/env node',
|
|
374
|
+
"'use strict';",
|
|
375
|
+
'// Iranti protocol reminder hook — fires on UserPromptSubmit for any Iranti project.',
|
|
376
|
+
'// Cross-platform: runs on Windows, macOS, Linux via Node.js.',
|
|
377
|
+
'// Exits cleanly with no output for non-Iranti projects.',
|
|
378
|
+
"const fs = require('fs');",
|
|
379
|
+
"const path = require('path');",
|
|
380
|
+
'',
|
|
381
|
+
"const envFile = path.join(process.cwd(), '.env.iranti');",
|
|
382
|
+
'if (!fs.existsSync(envFile)) process.exit(0);',
|
|
383
|
+
'',
|
|
384
|
+
'const content = [',
|
|
385
|
+
" 'IRANTI PROTOCOL (required this turn):',",
|
|
386
|
+
" '1. iranti_attend(phase=pre-response) BEFORE replying',",
|
|
387
|
+
" '2. iranti_attend BEFORE each Read / Grep / Glob / Bash / WebSearch / WebFetch',",
|
|
388
|
+
" '3. iranti_write AFTER each Edit or Write:',",
|
|
389
|
+
" ' entity: project/[id]/file/[filename] -- not the broad project entity',",
|
|
390
|
+
" ' value must include: absolutePath, lines, before, after, verify, why',",
|
|
391
|
+
" '4. iranti_write AFTER each Bash that reveals system state (build, errors, ports, env)',",
|
|
392
|
+
" '5. iranti_write AFTER each WebSearch/WebFetch -- write findings AND dead ends / 404s',",
|
|
393
|
+
" '6. iranti_attend(phase=post-response) AFTER every response without exception',",
|
|
394
|
+
"].join('\\n') + '\\n';",
|
|
395
|
+
"require('fs').writeSync(1, content);",
|
|
396
|
+
'',
|
|
397
|
+
].join('\n');
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Write the protocol-reminder hook script into the project's .codex/ directory
|
|
401
|
+
* and return a status result matching the workspace file pattern.
|
|
402
|
+
*/
|
|
403
|
+
function writeProtocolReminderHook(projectEnv) {
|
|
404
|
+
const projectPath = node_path_1.default.dirname(projectEnv);
|
|
405
|
+
const codexDir = node_path_1.default.join(projectPath, '.codex');
|
|
406
|
+
const hookFile = node_path_1.default.join(codexDir, 'iranti-protocol-hook.js');
|
|
407
|
+
const hookContent = buildProtocolReminderHookScript();
|
|
408
|
+
node_fs_1.default.mkdirSync(codexDir, { recursive: true });
|
|
409
|
+
if (!node_fs_1.default.existsSync(hookFile)) {
|
|
410
|
+
node_fs_1.default.writeFileSync(hookFile, hookContent, 'utf8');
|
|
411
|
+
return { filePath: hookFile, status: 'created' };
|
|
412
|
+
}
|
|
413
|
+
const existing = node_fs_1.default.readFileSync(hookFile, 'utf8');
|
|
414
|
+
if (existing !== hookContent) {
|
|
415
|
+
node_fs_1.default.writeFileSync(hookFile, hookContent, 'utf8');
|
|
416
|
+
return { filePath: hookFile, status: 'updated' };
|
|
417
|
+
}
|
|
418
|
+
return { filePath: hookFile, status: 'unchanged' };
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Write a .codex/hooks.json referencing the protocol-reminder hook.
|
|
422
|
+
* This fires on UserPromptSubmit when the codex_hooks feature is enabled.
|
|
423
|
+
*/
|
|
424
|
+
function writeCodexHooksConfig(projectEnv) {
|
|
425
|
+
const projectPath = node_path_1.default.dirname(projectEnv);
|
|
426
|
+
const codexDir = node_path_1.default.join(projectPath, '.codex');
|
|
427
|
+
const hooksConfigFile = node_path_1.default.join(codexDir, 'hooks.json');
|
|
428
|
+
const hookScriptPath = node_path_1.default.join(codexDir, 'iranti-protocol-hook.js');
|
|
429
|
+
const hooksConfig = {
|
|
430
|
+
hooks: {
|
|
431
|
+
UserPromptSubmit: [
|
|
432
|
+
{
|
|
433
|
+
matcher: '',
|
|
434
|
+
hooks: [
|
|
435
|
+
{
|
|
436
|
+
type: 'command',
|
|
437
|
+
command: `node ${hookScriptPath.replace(/\\/g, '/')}`,
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
node_fs_1.default.mkdirSync(codexDir, { recursive: true });
|
|
445
|
+
const nextContent = `${JSON.stringify(hooksConfig, null, 2)}\n`;
|
|
446
|
+
if (!node_fs_1.default.existsSync(hooksConfigFile)) {
|
|
447
|
+
node_fs_1.default.writeFileSync(hooksConfigFile, nextContent, 'utf8');
|
|
448
|
+
return { filePath: hooksConfigFile, status: 'created' };
|
|
449
|
+
}
|
|
450
|
+
const existing = node_fs_1.default.readFileSync(hooksConfigFile, 'utf8');
|
|
451
|
+
if (existing === nextContent) {
|
|
452
|
+
return { filePath: hooksConfigFile, status: 'unchanged' };
|
|
453
|
+
}
|
|
454
|
+
// Merge: keep existing hooks, add/replace UserPromptSubmit from iranti.
|
|
455
|
+
try {
|
|
456
|
+
const parsed = JSON.parse(existing);
|
|
457
|
+
const existingHooks = parsed.hooks && typeof parsed.hooks === 'object' && !Array.isArray(parsed.hooks)
|
|
458
|
+
? parsed.hooks
|
|
459
|
+
: {};
|
|
460
|
+
const merged = {
|
|
461
|
+
...parsed,
|
|
462
|
+
hooks: {
|
|
463
|
+
...existingHooks,
|
|
464
|
+
UserPromptSubmit: hooksConfig.hooks.UserPromptSubmit,
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
const mergedContent = `${JSON.stringify(merged, null, 2)}\n`;
|
|
468
|
+
if (mergedContent === existing) {
|
|
469
|
+
return { filePath: hooksConfigFile, status: 'unchanged' };
|
|
470
|
+
}
|
|
471
|
+
node_fs_1.default.writeFileSync(hooksConfigFile, mergedContent, 'utf8');
|
|
472
|
+
return { filePath: hooksConfigFile, status: 'updated' };
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
// Can't parse existing — overwrite
|
|
476
|
+
node_fs_1.default.writeFileSync(hooksConfigFile, nextContent, 'utf8');
|
|
477
|
+
return { filePath: hooksConfigFile, status: 'updated' };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Check if the codex_hooks feature is enabled globally.
|
|
482
|
+
*/
|
|
483
|
+
function isCodexHooksFeatureEnabled(repoRoot) {
|
|
484
|
+
try {
|
|
485
|
+
const output = run('codex', ['features', 'list'], repoRoot);
|
|
486
|
+
const match = output.match(/codex_hooks\s+\S+\s+(true|false)/);
|
|
487
|
+
return match?.[1] === 'true';
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Enable the codex_hooks feature flag if not already enabled.
|
|
495
|
+
*/
|
|
496
|
+
function ensureCodexHooksFeature(repoRoot) {
|
|
497
|
+
if (isCodexHooksFeatureEnabled(repoRoot)) {
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
run('codex', ['features', 'enable', 'codex_hooks'], repoRoot);
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
367
508
|
function canUseInstalledIranti(repoRoot) {
|
|
368
509
|
try {
|
|
369
510
|
run('iranti', ['mcp', '--help'], repoRoot);
|
|
@@ -428,8 +569,12 @@ async function main() {
|
|
|
428
569
|
vscode: writeWorkspaceVsCodeMcpFile(workspaceProjectEnv, options),
|
|
429
570
|
agents: writeWorkspaceAgentsFile(workspaceProjectEnv),
|
|
430
571
|
irantiMd: writeWorkspaceIrantiMdFile(workspaceProjectEnv),
|
|
572
|
+
protocolHook: writeProtocolReminderHook(workspaceProjectEnv),
|
|
573
|
+
hooksConfig: writeCodexHooksConfig(workspaceProjectEnv),
|
|
431
574
|
}
|
|
432
575
|
: null;
|
|
576
|
+
// Enable the codex_hooks feature flag so the UserPromptSubmit hook fires.
|
|
577
|
+
const hooksFeatureEnabled = ensureCodexHooksFeature(repoRoot);
|
|
433
578
|
const registered = run('codex', ['mcp', 'get', options.name], repoRoot);
|
|
434
579
|
console.log(registered);
|
|
435
580
|
console.log('');
|
|
@@ -468,6 +613,9 @@ async function main() {
|
|
|
468
613
|
console.log(`Workspace .vscode/mcp.json: ${workspaceFilesResult.vscode.status} (${workspaceFilesResult.vscode.filePath})`);
|
|
469
614
|
console.log(`Workspace AGENTS.md: ${workspaceFilesResult.agents.status} (${workspaceFilesResult.agents.filePath})`);
|
|
470
615
|
console.log(`Workspace IRANTI.md: ${workspaceFilesResult.irantiMd.status} (${workspaceFilesResult.irantiMd.filePath})`);
|
|
616
|
+
console.log(`Protocol hook: ${workspaceFilesResult.protocolHook.status} (${workspaceFilesResult.protocolHook.filePath})`);
|
|
617
|
+
console.log(`Hooks config: ${workspaceFilesResult.hooksConfig.status} (${workspaceFilesResult.hooksConfig.filePath})`);
|
|
618
|
+
console.log(`codex_hooks feature: ${hooksFeatureEnabled ? 'enabled' : 'not enabled (UserPromptSubmit hook requires codex_hooks feature)'}`);
|
|
471
619
|
const closeout = await (0, scaffoldCloseout_1.writeProjectScaffoldCloseout)({
|
|
472
620
|
tool: 'codex',
|
|
473
621
|
projectPath: node_path_1.default.dirname(boundProjectEnv),
|
|
@@ -477,6 +625,8 @@ async function main() {
|
|
|
477
625
|
{ path: workspaceFilesResult.vscode.filePath, status: workspaceFilesResult.vscode.status },
|
|
478
626
|
{ path: workspaceFilesResult.agents.filePath, status: workspaceFilesResult.agents.status },
|
|
479
627
|
{ path: workspaceFilesResult.irantiMd.filePath, status: workspaceFilesResult.irantiMd.status },
|
|
628
|
+
{ path: workspaceFilesResult.protocolHook.filePath, status: workspaceFilesResult.protocolHook.status },
|
|
629
|
+
{ path: workspaceFilesResult.hooksConfig.filePath, status: workspaceFilesResult.hooksConfig.status },
|
|
480
630
|
],
|
|
481
631
|
agentId: options.agent || 'codex_code',
|
|
482
632
|
});
|