iranti 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -178,11 +178,10 @@ Release guide: [`docs/guides/releasing.md`](docs/guides/releasing.md)
178
178
  Iranti ships a local stdio MCP server for Claude Code and other MCP clients:
179
179
 
180
180
  ```bash
181
- npm run build
182
- node dist/scripts/iranti-mcp.js
181
+ iranti mcp
183
182
  ```
184
183
 
185
- Use it with a project-local `.mcp.json`, and optionally add the Claude Code hook helper for `SessionStart` and `UserPromptSubmit`.
184
+ Use it with a project-local `.mcp.json`, and optionally add `iranti claude-hook` for `SessionStart` and `UserPromptSubmit`.
186
185
 
187
186
  Guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
188
187
 
@@ -6,6 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  require("dotenv/config");
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const sdk_1 = require("../src/sdk");
9
+ const runtimeEnv_1 = require("../src/lib/runtimeEnv");
10
+ const MEMORY_NEED_POSITIVE_PATTERNS = [
11
+ /\bwhat(?:'s| is| was)?\s+my\b/i,
12
+ /\bdo you remember\b/i,
13
+ /\bremind me\b/i,
14
+ /\bmy\s+(?:favorite|favourite|name|email|phone|address|city|country|movie|snack|color|colour)\b/i,
15
+ /\bwe decided\b/i,
16
+ /\bearlier\b/i,
17
+ /\bprevious(?:ly)?\b/i,
18
+ /\bagain\b/i,
19
+ ];
20
+ const MEMORY_NEED_NEGATIVE_PATTERNS = [
21
+ /^\s*(hi|hello|hey|yo|sup|good (?:morning|afternoon|evening))\b[!.?\s]*$/i,
22
+ /^\s*(thanks|thank you|cool|great|nice)\b[!.?\s]*$/i,
23
+ ];
9
24
  function printHelp() {
10
25
  console.log([
11
26
  'Claude Code -> Iranti hook helper',
@@ -14,6 +29,11 @@ function printHelp() {
14
29
  ' ts-node scripts/claude-code-memory-hook.ts --event SessionStart',
15
30
  ' ts-node scripts/claude-code-memory-hook.ts --event UserPromptSubmit',
16
31
  '',
32
+ 'Optional flags:',
33
+ ' --project-env <path> Explicit .env.iranti path',
34
+ ' --instance-env <path> Explicit instance env path',
35
+ ' --env-file <path> Explicit base .env path',
36
+ '',
17
37
  'Reads Claude Code hook JSON from stdin and returns hookSpecificOutput.additionalContext on stdout.',
18
38
  ].join('\n'));
19
39
  }
@@ -77,6 +97,9 @@ function getDefaultAgentId(payload) {
77
97
  const explicit = process.env.IRANTI_CLAUDE_AGENT_ID?.trim();
78
98
  if (explicit)
79
99
  return explicit;
100
+ const projectBindingAgent = process.env.IRANTI_AGENT_ID?.trim();
101
+ if (projectBindingAgent)
102
+ return projectBindingAgent;
80
103
  const base = path_1.default.basename(getCwd(payload));
81
104
  return `claude_code_${slugify(base || 'project')}`;
82
105
  }
@@ -85,6 +108,10 @@ function getEntityHints(payload) {
85
108
  const cwd = getCwd(payload);
86
109
  const projectHint = `project/${slugify(path_1.default.basename(cwd) || 'project')}`;
87
110
  out.add(projectHint);
111
+ const memoryEntity = process.env.IRANTI_MEMORY_ENTITY?.trim();
112
+ if (memoryEntity) {
113
+ out.add(memoryEntity);
114
+ }
88
115
  const envHints = (process.env.IRANTI_CLAUDE_ENTITY_HINTS ?? '')
89
116
  .split(',')
90
117
  .map((value) => value.trim())
@@ -105,7 +132,17 @@ async function ensureHookAgent(iranti, payload) {
105
132
  return agentId;
106
133
  }
107
134
  function getPrompt(payload) {
108
- return typeof payload.prompt === 'string' ? payload.prompt.trim() : '';
135
+ const candidates = [
136
+ payload.prompt,
137
+ payload.message,
138
+ payload.text,
139
+ ];
140
+ for (const candidate of candidates) {
141
+ if (typeof candidate === 'string' && candidate.trim()) {
142
+ return candidate.trim();
143
+ }
144
+ }
145
+ return '';
109
146
  }
110
147
  function getMaxFacts() {
111
148
  const raw = Number(process.env.IRANTI_CLAUDE_MAX_FACTS ?? 6);
@@ -113,41 +150,26 @@ function getMaxFacts() {
113
150
  return 6;
114
151
  return Math.min(12, Math.trunc(raw));
115
152
  }
116
- function formatHandshakeContext(brief, cwd) {
117
- const facts = Array.isArray(brief?.workingMemory) ? brief.workingMemory.slice(0, getMaxFacts()) : [];
153
+ function formatSessionContext(facts, cwd) {
154
+ const limited = facts.slice(0, getMaxFacts());
118
155
  const lines = [
119
156
  '[Iranti Session Memory]',
120
157
  `Project: ${path_1.default.basename(cwd)}`,
121
158
  ];
122
- if (typeof brief?.inferredTaskType === 'string' && brief.inferredTaskType) {
123
- lines.push(`Task type: ${brief.inferredTaskType}`);
124
- }
125
- if (facts.length > 0) {
159
+ if (limited.length > 0) {
126
160
  lines.push('Relevant memory:');
127
- for (const fact of facts) {
128
- const label = typeof fact?.entityKey === 'string' ? fact.entityKey : `${fact?.entityType ?? 'entity'}/${fact?.entityId ?? 'unknown'}`;
129
- const summary = typeof fact?.summary === 'string' ? fact.summary : fact?.valueSummary;
130
- if (!summary)
131
- continue;
132
- lines.push(`- ${label}: ${summary}`);
161
+ for (const fact of limited) {
162
+ lines.push(`- ${fact.entity}/${fact.key}: ${fact.summary}`);
133
163
  }
134
164
  }
135
165
  return lines.join('\n');
136
166
  }
137
- function formatAttendContext(attend) {
138
- const facts = Array.isArray(attend?.facts) ? attend.facts.slice(0, getMaxFacts()) : [];
139
- if (!attend?.shouldInject || facts.length === 0)
167
+ function formatPromptContext(facts) {
168
+ if (facts.length === 0)
140
169
  return '';
141
170
  const lines = ['[Iranti Retrieved Memory]'];
142
- if (typeof attend?.reason === 'string' && attend.reason) {
143
- lines.push(`Reason: ${attend.reason}`);
144
- }
145
171
  for (const fact of facts) {
146
- const label = typeof fact?.entityKey === 'string' ? fact.entityKey : `${fact?.entityType ?? 'entity'}/${fact?.entityId ?? 'unknown'}`;
147
- const summary = typeof fact?.summary === 'string' ? fact.summary : fact?.valueSummary;
148
- if (!summary)
149
- continue;
150
- lines.push(`- ${label}: ${summary}`);
172
+ lines.push(`- ${fact.entity}/${fact.key}: ${fact.summary}`);
151
173
  }
152
174
  return lines.join('\n');
153
175
  }
@@ -160,6 +182,98 @@ function emitHookContext(event, additionalContext) {
160
182
  };
161
183
  process.stdout.write(`${JSON.stringify(payload)}\n`);
162
184
  }
185
+ function shouldFetchMemory(prompt) {
186
+ const normalized = prompt.trim();
187
+ if (!normalized)
188
+ return false;
189
+ if (MEMORY_NEED_NEGATIVE_PATTERNS.some((pattern) => pattern.test(normalized))) {
190
+ return false;
191
+ }
192
+ if (MEMORY_NEED_POSITIVE_PATTERNS.some((pattern) => pattern.test(normalized))) {
193
+ return true;
194
+ }
195
+ if (/\b(my|our|we)\b/i.test(normalized)) {
196
+ return true;
197
+ }
198
+ return normalized.includes('/');
199
+ }
200
+ function dedupeFacts(facts) {
201
+ const byKey = new Map();
202
+ for (const fact of facts) {
203
+ const identity = `${fact.entity}/${fact.key}`;
204
+ const existing = byKey.get(identity);
205
+ if (!existing || fact.confidence > existing.confidence) {
206
+ byKey.set(identity, fact);
207
+ }
208
+ }
209
+ return Array.from(byKey.values())
210
+ .sort((a, b) => b.confidence - a.confidence || a.entity.localeCompare(b.entity) || a.key.localeCompare(b.key))
211
+ .slice(0, getMaxFacts());
212
+ }
213
+ async function loadAttendantStateFacts(iranti, agent) {
214
+ const state = await iranti.query(`agent/${agent}`, 'attendant_state');
215
+ if (!state.found || !state.value || typeof state.value !== 'object') {
216
+ return [];
217
+ }
218
+ const workingMemory = Array.isArray(state.value.workingMemory)
219
+ ? state.value.workingMemory
220
+ : [];
221
+ return workingMemory.flatMap((entry) => {
222
+ const entityKey = typeof entry.entityKey === 'string' ? entry.entityKey.trim() : '';
223
+ const summary = typeof entry.summary === 'string' ? entry.summary.trim() : '';
224
+ if (!entityKey || !summary)
225
+ return [];
226
+ const segments = entityKey.split('/');
227
+ if (segments.length < 3)
228
+ return [];
229
+ return [{
230
+ entity: `${segments[0]}/${segments[1]}`,
231
+ key: segments.slice(2).join('/'),
232
+ summary,
233
+ confidence: typeof entry.confidence === 'number' ? entry.confidence : 0,
234
+ source: typeof entry.source === 'string' ? entry.source : 'attendant',
235
+ }];
236
+ });
237
+ }
238
+ async function loadEntityFacts(iranti, entities) {
239
+ const out = [];
240
+ for (const entity of entities) {
241
+ const trimmed = entity.trim();
242
+ if (!trimmed)
243
+ continue;
244
+ const entries = await iranti.queryAll(trimmed).catch(() => []);
245
+ for (const entry of entries) {
246
+ out.push({
247
+ entity: trimmed,
248
+ key: entry.key,
249
+ summary: entry.summary,
250
+ confidence: entry.confidence,
251
+ source: entry.source,
252
+ });
253
+ }
254
+ }
255
+ return out;
256
+ }
257
+ async function searchPromptFacts(iranti, prompt, entityHints) {
258
+ if (!prompt.trim())
259
+ return [];
260
+ const results = await iranti.search({
261
+ query: prompt,
262
+ limit: getMaxFacts(),
263
+ minScore: Number(process.env.IRANTI_CLAUDE_MIN_SEARCH_SCORE ?? 0.05),
264
+ }).catch(() => []);
265
+ const searched = results.map((result) => ({
266
+ entity: result.entity,
267
+ key: result.key,
268
+ summary: result.summary,
269
+ confidence: result.confidence,
270
+ source: result.source,
271
+ }));
272
+ if (searched.length > 0) {
273
+ return searched;
274
+ }
275
+ return loadEntityFacts(iranti, entityHints);
276
+ }
163
277
  async function main() {
164
278
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
165
279
  printHelp();
@@ -171,6 +285,12 @@ async function main() {
171
285
  throw new Error('--event must be SessionStart or UserPromptSubmit');
172
286
  }
173
287
  const payload = parsePayload(await readStdin());
288
+ (0, runtimeEnv_1.loadRuntimeEnv)({
289
+ payloadCwd: getCwd(payload),
290
+ projectEnvFile: args['project-env'],
291
+ instanceEnvFile: args['instance-env'],
292
+ explicitEnvFile: args['env-file'],
293
+ });
174
294
  const cwd = getCwd(payload);
175
295
  const iranti = new sdk_1.Iranti({
176
296
  connectionString: requireConnectionString(),
@@ -179,30 +299,27 @@ async function main() {
179
299
  const agent = await ensureHookAgent(iranti, payload);
180
300
  const entityHints = getEntityHints(payload);
181
301
  if (event === 'SessionStart') {
182
- const task = process.env.IRANTI_CLAUDE_SESSION_TASK?.trim()
183
- || `Claude Code session in ${path_1.default.basename(cwd)}`;
184
- const brief = await iranti.handshake({
185
- agent,
186
- task,
187
- recentMessages: [],
188
- });
189
- emitHookContext(event, formatHandshakeContext(brief, cwd));
190
- return;
302
+ const persistedFacts = await loadAttendantStateFacts(iranti, agent);
303
+ const directFacts = persistedFacts.length > 0
304
+ ? persistedFacts
305
+ : await loadEntityFacts(iranti, entityHints);
306
+ emitHookContext(event, formatSessionContext(dedupeFacts(directFacts), cwd));
307
+ process.exit(0);
191
308
  }
192
309
  const prompt = getPrompt(payload);
193
- if (!prompt)
194
- return;
195
- const attend = await iranti.attend({
196
- agent,
197
- latestMessage: prompt,
198
- currentContext: prompt,
199
- entityHints,
200
- maxFacts: getMaxFacts(),
201
- });
202
- const context = formatAttendContext(attend);
203
- if (!context)
204
- return;
310
+ if (!prompt) {
311
+ process.exit(0);
312
+ }
313
+ if (!shouldFetchMemory(prompt)) {
314
+ process.exit(0);
315
+ }
316
+ const facts = await searchPromptFacts(iranti, prompt, entityHints);
317
+ const context = formatPromptContext(dedupeFacts(facts));
318
+ if (!context) {
319
+ process.exit(0);
320
+ }
205
321
  emitHookContext(event, context);
322
+ process.exit(0);
206
323
  }
207
324
  main().catch((error) => {
208
325
  console.error('[claude-code-memory-hook] fatal:', error instanceof Error ? error.message : String(error));
@@ -8,6 +8,7 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const promises_1 = __importDefault(require("fs/promises"));
9
9
  const os_1 = __importDefault(require("os"));
10
10
  const path_1 = __importDefault(require("path"));
11
+ const child_process_1 = require("child_process");
11
12
  const promises_2 = __importDefault(require("readline/promises"));
12
13
  const stream_1 = require("stream");
13
14
  const client_1 = require("../src/library/client");
@@ -118,6 +119,54 @@ function getPackageVersion() {
118
119
  }
119
120
  return '0.0.0';
120
121
  }
122
+ function builtScriptPath(scriptName) {
123
+ return path_1.default.resolve(__dirname, `${scriptName}.js`);
124
+ }
125
+ async function handoffToScript(scriptName, rawArgs) {
126
+ const builtPath = builtScriptPath(scriptName);
127
+ if (fs_1.default.existsSync(builtPath)) {
128
+ await new Promise((resolve, reject) => {
129
+ const child = (0, child_process_1.spawn)(process.execPath, [builtPath, ...rawArgs], {
130
+ stdio: 'inherit',
131
+ env: process.env,
132
+ });
133
+ child.on('error', reject);
134
+ child.on('exit', (code, signal) => {
135
+ if (signal) {
136
+ reject(new Error(`${scriptName} terminated with signal ${signal}`));
137
+ return;
138
+ }
139
+ if ((code ?? 0) !== 0) {
140
+ process.exit(code ?? 1);
141
+ }
142
+ resolve();
143
+ });
144
+ });
145
+ return;
146
+ }
147
+ const sourcePath = path_1.default.resolve(process.cwd(), 'scripts', `${scriptName}.ts`);
148
+ if (!fs_1.default.existsSync(sourcePath)) {
149
+ throw new Error(`Unable to locate ${scriptName} implementation.`);
150
+ }
151
+ await new Promise((resolve, reject) => {
152
+ const child = (0, child_process_1.spawn)('npx', ['ts-node', sourcePath, ...rawArgs], {
153
+ stdio: 'inherit',
154
+ env: process.env,
155
+ shell: process.platform === 'win32',
156
+ });
157
+ child.on('error', reject);
158
+ child.on('exit', (code, signal) => {
159
+ if (signal) {
160
+ reject(new Error(`${scriptName} terminated with signal ${signal}`));
161
+ return;
162
+ }
163
+ if ((code ?? 0) !== 0) {
164
+ process.exit(code ?? 1);
165
+ }
166
+ resolve();
167
+ });
168
+ });
169
+ }
121
170
  async function ensureDir(dir) {
122
171
  await promises_1.default.mkdir(dir, { recursive: true });
123
172
  }
@@ -1077,6 +1126,10 @@ Diagnostics:
1077
1126
  iranti doctor [--instance <name>] [--scope user|system] [--env <file>] [--json]
1078
1127
  iranti status [--scope user|system] [--json]
1079
1128
  iranti upgrade [--json]
1129
+
1130
+ Integrations:
1131
+ iranti mcp [--help]
1132
+ iranti claude-hook --event SessionStart|UserPromptSubmit [--project-env <path>] [--instance-env <path>] [--env-file <path>]
1080
1133
  `);
1081
1134
  }
1082
1135
  async function main() {
@@ -1150,6 +1203,14 @@ async function main() {
1150
1203
  await upgradeCommand(args);
1151
1204
  return;
1152
1205
  }
1206
+ if (args.command === 'mcp') {
1207
+ await handoffToScript('iranti-mcp', process.argv.slice(3));
1208
+ return;
1209
+ }
1210
+ if (args.command === 'claude-hook') {
1211
+ await handoffToScript('claude-code-memory-hook', process.argv.slice(3));
1212
+ return;
1213
+ }
1153
1214
  throw new Error(`Unknown command '${args.command}'. Run: iranti help`);
1154
1215
  }
1155
1216
  main().catch((err) => {
@@ -32,35 +32,13 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
- const node_fs_1 = __importDefault(require("node:fs"));
40
- const node_path_1 = __importDefault(require("node:path"));
41
- const dotenv_1 = __importDefault(require("dotenv"));
42
36
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
43
37
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
44
38
  const z = __importStar(require("zod/v4"));
45
39
  const sdk_1 = require("../src/sdk");
46
- function loadEnv() {
47
- const explicitPath = process.env.IRANTI_ENV_FILE?.trim();
48
- const candidates = explicitPath
49
- ? [explicitPath]
50
- : [
51
- node_path_1.default.resolve(process.cwd(), '.env'),
52
- node_path_1.default.resolve(__dirname, '..', '.env'),
53
- node_path_1.default.resolve(__dirname, '..', '..', '.env'),
54
- ];
55
- for (const candidate of candidates) {
56
- if (candidate && node_fs_1.default.existsSync(candidate)) {
57
- dotenv_1.default.config({ path: candidate });
58
- return;
59
- }
60
- }
61
- dotenv_1.default.config();
62
- }
63
- loadEnv();
40
+ const runtimeEnv_1 = require("../src/lib/runtimeEnv");
41
+ (0, runtimeEnv_1.loadRuntimeEnv)();
64
42
  function printHelp() {
65
43
  console.log([
66
44
  'Iranti MCP Server',
@@ -72,6 +50,8 @@ function printHelp() {
72
50
  'Environment:',
73
51
  ' DATABASE_URL PostgreSQL connection string (required)',
74
52
  ' LLM_PROVIDER Optional Iranti provider override',
53
+ ' IRANTI_PROJECT_ENV Optional project binding path (.env.iranti)',
54
+ ' IRANTI_INSTANCE_ENV Optional instance env path',
75
55
  ' IRANTI_MCP_DEFAULT_AGENT Default agent id (default: claude_code)',
76
56
  ' IRANTI_MCP_AGENT_NAME Default agent display name',
77
57
  ' IRANTI_MCP_AGENT_DESCRIPTION Default agent description',
@@ -89,7 +69,9 @@ function requireConnectionString() {
89
69
  return connectionString;
90
70
  }
91
71
  function defaultAgentId() {
92
- return process.env.IRANTI_MCP_DEFAULT_AGENT?.trim() || 'claude_code';
72
+ return process.env.IRANTI_MCP_DEFAULT_AGENT?.trim()
73
+ || process.env.IRANTI_AGENT_ID?.trim()
74
+ || 'claude_code';
93
75
  }
94
76
  function defaultWriteSource() {
95
77
  return process.env.IRANTI_MCP_DEFAULT_SOURCE?.trim() || 'ClaudeCode';
@@ -162,7 +144,7 @@ async function main() {
162
144
  await ensureDefaultAgent(iranti);
163
145
  const server = new mcp_js_1.McpServer({
164
146
  name: 'iranti-mcp',
165
- version: '0.1.1',
147
+ version: '0.1.2',
166
148
  });
167
149
  server.registerTool('iranti_handshake', {
168
150
  description: 'Initialize or refresh Claude working memory for the current task.',
@@ -15,7 +15,7 @@ const STAFF_ENTRIES = [
15
15
  entityId: 'librarian',
16
16
  key: 'operating_rules',
17
17
  valueRaw: {
18
- version: '0.1.1',
18
+ version: '0.1.2',
19
19
  rules: [
20
20
  'All writes from external agents go through the Librarian — never directly to the database',
21
21
  'Check for existing entries before every write',
@@ -39,7 +39,7 @@ const STAFF_ENTRIES = [
39
39
  entityId: 'attendant',
40
40
  key: 'operating_rules',
41
41
  valueRaw: {
42
- version: '0.1.1',
42
+ version: '0.1.2',
43
43
  rules: [
44
44
  'Assigned one-per-external-agent — serve the agent, not the user',
45
45
  'On handshake: read AGENTS.md and MCP config, query Librarian for relevant rules and task context',
@@ -61,7 +61,7 @@ const STAFF_ENTRIES = [
61
61
  entityId: 'archivist',
62
62
  key: 'operating_rules',
63
63
  valueRaw: {
64
- version: '0.1.1',
64
+ version: '0.1.2',
65
65
  rules: [
66
66
  'Run on schedule or when conflict flags exceed threshold — not on every write',
67
67
  'Scan for expired, low-confidence, flagged, and duplicate entries',
@@ -82,7 +82,7 @@ const STAFF_ENTRIES = [
82
82
  entityType: 'system',
83
83
  entityId: 'library',
84
84
  key: 'schema_version',
85
- valueRaw: { version: '0.1.1' },
85
+ valueRaw: { version: '0.1.2' },
86
86
  valueSummary: 'Current Library schema version.',
87
87
  confidence: 100,
88
88
  source: 'seed',
@@ -95,7 +95,7 @@ const STAFF_ENTRIES = [
95
95
  key: 'initialization_log',
96
96
  valueRaw: {
97
97
  initializedAt: new Date().toISOString(),
98
- seedVersion: '0.1.1',
98
+ seedVersion: '0.1.2',
99
99
  },
100
100
  valueSummary: 'Record of when and how this Library was initialized.',
101
101
  confidence: 100,
@@ -108,7 +108,7 @@ const STAFF_ENTRIES = [
108
108
  entityId: 'ontology',
109
109
  key: 'core_schema',
110
110
  valueRaw: {
111
- version: '0.1.1',
111
+ version: '0.1.2',
112
112
  states: ['candidate', 'provisional', 'canonical'],
113
113
  coreEntityTypes: [
114
114
  'person',
@@ -156,7 +156,7 @@ const STAFF_ENTRIES = [
156
156
  entityId: 'ontology',
157
157
  key: 'extension_registry',
158
158
  valueRaw: {
159
- version: '0.1.1',
159
+ version: '0.1.2',
160
160
  namespaces: {
161
161
  education: {
162
162
  status: 'provisional',
@@ -187,7 +187,7 @@ const STAFF_ENTRIES = [
187
187
  entityId: 'ontology',
188
188
  key: 'candidate_terms',
189
189
  valueRaw: {
190
- version: '0.1.1',
190
+ version: '0.1.2',
191
191
  terms: [],
192
192
  },
193
193
  valueSummary: 'Staging area for ontology terms detected repeatedly but not yet promoted.',
@@ -201,7 +201,7 @@ const STAFF_ENTRIES = [
201
201
  entityId: 'ontology',
202
202
  key: 'promotion_policy',
203
203
  valueRaw: {
204
- version: '0.1.1',
204
+ version: '0.1.2',
205
205
  candidateToProvisional: {
206
206
  minSeenCount: 3,
207
207
  minDistinctAgents: 2,
@@ -237,7 +237,7 @@ const STAFF_ENTRIES = [
237
237
  entityId: 'ontology',
238
238
  key: 'change_log',
239
239
  valueRaw: {
240
- version: '0.1.1',
240
+ version: '0.1.2',
241
241
  events: [
242
242
  {
243
243
  at: new Date().toISOString(),
@@ -69,7 +69,7 @@ app.use(express_1.default.json({ limit: process.env.IRANTI_MAX_BODY_BYTES ?? '25
69
69
  app.get(ROUTES.health, (_req, res) => {
70
70
  res.json({
71
71
  status: 'ok',
72
- version: '0.1.1',
72
+ version: '0.1.2',
73
73
  provider: process.env.LLM_PROVIDER ?? 'mock',
74
74
  });
75
75
  });
@@ -0,0 +1,15 @@
1
+ type RuntimeEnvOptions = {
2
+ cwd?: string;
3
+ payloadCwd?: string;
4
+ projectEnvFile?: string;
5
+ instanceEnvFile?: string;
6
+ explicitEnvFile?: string;
7
+ };
8
+ type RuntimeEnvLoadResult = {
9
+ loadedFiles: string[];
10
+ projectEnvFile?: string;
11
+ instanceEnvFile?: string;
12
+ };
13
+ export declare function loadRuntimeEnv(options?: RuntimeEnvOptions): RuntimeEnvLoadResult;
14
+ export {};
15
+ //# sourceMappingURL=runtimeEnv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeEnv.d.ts","sourceRoot":"","sources":["../../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAIA,KAAK,iBAAiB,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAuDF,wBAAgB,cAAc,CAAC,OAAO,GAAE,iBAAsB,GAAG,oBAAoB,CAsCpF"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadRuntimeEnv = loadRuntimeEnv;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ function parseEnvFile(filePath) {
11
+ const raw = fs_1.default.readFileSync(filePath, 'utf-8');
12
+ return dotenv_1.default.parse(raw);
13
+ }
14
+ function applyEnvVars(vars, initialEnvKeys) {
15
+ for (const [key, value] of Object.entries(vars)) {
16
+ if (initialEnvKeys.has(key))
17
+ continue;
18
+ process.env[key] = value;
19
+ }
20
+ }
21
+ function dedupePaths(paths) {
22
+ const seen = new Set();
23
+ const out = [];
24
+ for (const candidate of paths) {
25
+ if (!candidate)
26
+ continue;
27
+ const resolved = path_1.default.resolve(candidate);
28
+ if (seen.has(resolved))
29
+ continue;
30
+ seen.add(resolved);
31
+ out.push(resolved);
32
+ }
33
+ return out;
34
+ }
35
+ function findProjectEnvFile(options) {
36
+ const explicit = options.projectEnvFile?.trim() || process.env.IRANTI_PROJECT_ENV?.trim();
37
+ if (explicit && fs_1.default.existsSync(explicit))
38
+ return path_1.default.resolve(explicit);
39
+ const candidates = dedupePaths([
40
+ options.payloadCwd ? path_1.default.join(options.payloadCwd, '.env.iranti') : null,
41
+ options.cwd ? path_1.default.join(options.cwd, '.env.iranti') : null,
42
+ path_1.default.join(process.cwd(), '.env.iranti'),
43
+ ]);
44
+ return candidates.find((candidate) => fs_1.default.existsSync(candidate));
45
+ }
46
+ function findFallbackEnvFile(options) {
47
+ const explicit = options.explicitEnvFile?.trim() || process.env.IRANTI_ENV_FILE?.trim();
48
+ if (explicit && fs_1.default.existsSync(explicit))
49
+ return path_1.default.resolve(explicit);
50
+ const candidates = dedupePaths([
51
+ options.payloadCwd ? path_1.default.join(options.payloadCwd, '.env') : null,
52
+ options.cwd ? path_1.default.join(options.cwd, '.env') : null,
53
+ path_1.default.join(process.cwd(), '.env'),
54
+ path_1.default.resolve(__dirname, '..', '..', '.env'),
55
+ path_1.default.resolve(__dirname, '..', '..', '..', '.env'),
56
+ ]);
57
+ return candidates.find((candidate) => fs_1.default.existsSync(candidate));
58
+ }
59
+ function loadRuntimeEnv(options = {}) {
60
+ const loadedFiles = [];
61
+ const initialEnvKeys = new Set(Object.keys(process.env));
62
+ const fallbackEnvFile = findFallbackEnvFile(options);
63
+ if (fallbackEnvFile) {
64
+ applyEnvVars(parseEnvFile(fallbackEnvFile), initialEnvKeys);
65
+ loadedFiles.push(fallbackEnvFile);
66
+ }
67
+ const projectEnvFile = findProjectEnvFile(options);
68
+ const projectEnv = projectEnvFile && fs_1.default.existsSync(projectEnvFile)
69
+ ? parseEnvFile(projectEnvFile)
70
+ : null;
71
+ const instanceEnvFile = options.instanceEnvFile?.trim()
72
+ || process.env.IRANTI_INSTANCE_ENV?.trim()
73
+ || projectEnv?.IRANTI_INSTANCE_ENV;
74
+ const resolvedInstanceEnvFile = instanceEnvFile && fs_1.default.existsSync(instanceEnvFile)
75
+ ? path_1.default.resolve(instanceEnvFile)
76
+ : undefined;
77
+ if (resolvedInstanceEnvFile) {
78
+ applyEnvVars(parseEnvFile(resolvedInstanceEnvFile), initialEnvKeys);
79
+ loadedFiles.push(resolvedInstanceEnvFile);
80
+ }
81
+ if (projectEnvFile && projectEnv) {
82
+ applyEnvVars(projectEnv, initialEnvKeys);
83
+ loadedFiles.push(projectEnvFile);
84
+ }
85
+ return {
86
+ loadedFiles,
87
+ projectEnvFile,
88
+ instanceEnvFile: resolvedInstanceEnvFile,
89
+ };
90
+ }
91
+ //# sourceMappingURL=runtimeEnv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeEnv.js","sourceRoot":"","sources":["../../../src/lib/runtimeEnv.ts"],"names":[],"mappings":";;;;;AAuEA,wCAsCC;AA7GD,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAgB5B,SAAS,YAAY,CAAC,QAAgB;IAClC,MAAM,GAAG,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,IAA4B,EAAE,cAA2B;IAC3E,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACtC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC7B,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,KAAuC;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS;YAAE,SAAS;QACzB,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,OAA0B;IAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IAC1F,IAAI,QAAQ,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAG,WAAW,CAAC;QAC3B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;QACxE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC;KAC1C,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,mBAAmB,CAAC,OAA0B;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACxF,IAAI,QAAQ,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAG,WAAW,CAAC;QAC3B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;QACjE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;QACnD,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC;QAChC,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;QAC3C,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;KACpD,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAgB,cAAc,CAAC,UAA6B,EAAE;IAC1D,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,eAAe,EAAE,CAAC;QAClB,YAAY,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,cAAc,CAAC,CAAC;QAC5D,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,cAAc,IAAI,YAAE,CAAC,UAAU,CAAC,cAAc,CAAC;QAC9D,CAAC,CAAC,YAAY,CAAC,cAAc,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,EAAE;WAChD,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE;WACvC,UAAU,EAAE,mBAAmB,CAAC;IAEvC,MAAM,uBAAuB,GAAG,eAAe,IAAI,YAAE,CAAC,UAAU,CAAC,eAAe,CAAC;QAC7E,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,eAAe,CAAC;QAC/B,CAAC,CAAC,SAAS,CAAC;IAEhB,IAAI,uBAAuB,EAAE,CAAC;QAC1B,YAAY,CAAC,YAAY,CAAC,uBAAuB,CAAC,EAAE,cAAc,CAAC,CAAC;QACpE,WAAW,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,cAAc,IAAI,UAAU,EAAE,CAAC;QAC/B,YAAY,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QACzC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACH,WAAW;QACX,cAAc;QACd,eAAe,EAAE,uBAAuB;KAC3C,CAAC;AACN,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iranti",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Memory infrastructure for multi-agent AI systems",
5
5
  "main": "dist/src/sdk/index.js",
6
6
  "files": [