iranti 0.2.2 → 0.2.4

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 (51) hide show
  1. package/README.md +38 -11
  2. package/dist/scripts/iranti-cli.js +525 -50
  3. package/dist/scripts/iranti-mcp.js +1 -1
  4. package/dist/scripts/seed.js +10 -10
  5. package/dist/src/api/server.js +1 -1
  6. package/dist/src/chat/index.d.ts +8 -0
  7. package/dist/src/chat/index.d.ts.map +1 -0
  8. package/dist/src/chat/index.js +565 -0
  9. package/dist/src/chat/index.js.map +1 -0
  10. package/dist/src/lib/llm.d.ts +1 -0
  11. package/dist/src/lib/llm.d.ts.map +1 -1
  12. package/dist/src/lib/llm.js +4 -0
  13. package/dist/src/lib/llm.js.map +1 -1
  14. package/dist/src/lib/router.d.ts.map +1 -1
  15. package/dist/src/lib/router.js +46 -42
  16. package/dist/src/lib/router.js.map +1 -1
  17. package/dist/src/librarian/contextual-conflicts.d.ts +9 -0
  18. package/dist/src/librarian/contextual-conflicts.d.ts.map +1 -0
  19. package/dist/src/librarian/contextual-conflicts.js +243 -0
  20. package/dist/src/librarian/contextual-conflicts.js.map +1 -0
  21. package/dist/src/librarian/index.d.ts.map +1 -1
  22. package/dist/src/librarian/index.js +50 -0
  23. package/dist/src/librarian/index.js.map +1 -1
  24. package/dist/src/library/backends/chromaBackend.d.ts +27 -0
  25. package/dist/src/library/backends/chromaBackend.d.ts.map +1 -0
  26. package/dist/src/library/backends/chromaBackend.js +99 -0
  27. package/dist/src/library/backends/chromaBackend.js.map +1 -0
  28. package/dist/src/library/backends/index.d.ts +15 -0
  29. package/dist/src/library/backends/index.d.ts.map +1 -0
  30. package/dist/src/library/backends/index.js +39 -0
  31. package/dist/src/library/backends/index.js.map +1 -0
  32. package/dist/src/library/backends/pgvectorBackend.d.ts +8 -0
  33. package/dist/src/library/backends/pgvectorBackend.d.ts.map +1 -0
  34. package/dist/src/library/backends/pgvectorBackend.js +128 -0
  35. package/dist/src/library/backends/pgvectorBackend.js.map +1 -0
  36. package/dist/src/library/backends/qdrantBackend.d.ts +21 -0
  37. package/dist/src/library/backends/qdrantBackend.d.ts.map +1 -0
  38. package/dist/src/library/backends/qdrantBackend.js +107 -0
  39. package/dist/src/library/backends/qdrantBackend.js.map +1 -0
  40. package/dist/src/library/queries.d.ts.map +1 -1
  41. package/dist/src/library/queries.js +105 -123
  42. package/dist/src/library/queries.js.map +1 -1
  43. package/dist/src/library/vectorBackend.d.ts +19 -0
  44. package/dist/src/library/vectorBackend.d.ts.map +1 -0
  45. package/dist/src/library/vectorBackend.js +3 -0
  46. package/dist/src/library/vectorBackend.js.map +1 -0
  47. package/dist/src/resolutionist/index.d.ts +8 -0
  48. package/dist/src/resolutionist/index.d.ts.map +1 -0
  49. package/dist/src/resolutionist/index.js +265 -0
  50. package/dist/src/resolutionist/index.js.map +1 -0
  51. package/package.json +2 -1
@@ -144,7 +144,7 @@ async function main() {
144
144
  await ensureDefaultAgent(iranti);
145
145
  const server = new mcp_js_1.McpServer({
146
146
  name: 'iranti-mcp',
147
- version: '0.2.2',
147
+ version: '0.2.4',
148
148
  });
149
149
  server.registerTool('iranti_handshake', {
150
150
  description: `Initialize or refresh an agent's working-memory brief 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.2.2',
18
+ version: '0.2.4',
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.2.2',
42
+ version: '0.2.4',
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.2.2',
64
+ version: '0.2.4',
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.2.2' },
85
+ valueRaw: { version: '0.2.4' },
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.2.2',
98
+ seedVersion: '0.2.4',
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.2.2',
111
+ version: '0.2.4',
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.2.2',
159
+ version: '0.2.4',
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.2.2',
190
+ version: '0.2.4',
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.2.2',
204
+ version: '0.2.4',
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.2.2',
240
+ version: '0.2.4',
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.2.2',
72
+ version: '0.2.4',
73
73
  provider: process.env.LLM_PROVIDER ?? 'mock',
74
74
  });
75
75
  });
@@ -0,0 +1,8 @@
1
+ export type ChatSessionOptions = {
2
+ agentId?: string;
3
+ provider?: string;
4
+ model?: string;
5
+ cwd?: string;
6
+ };
7
+ export declare function startChatSession(options?: ChatSessionOptions): Promise<void>;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/chat/index.ts"],"names":[],"mappings":"AA+FA,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwOF,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6XtF"}
@@ -0,0 +1,565 @@
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.startChatSession = startChatSession;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const promises_2 = __importDefault(require("readline/promises"));
11
+ const llm_1 = require("../lib/llm");
12
+ const router_1 = require("../lib/router");
13
+ const runtimeEnv_1 = require("../lib/runtimeEnv");
14
+ const resolutionist_1 = require("../resolutionist");
15
+ class ApiClient {
16
+ constructor(baseUrl, apiKey) {
17
+ this.baseUrl = baseUrl;
18
+ this.apiKey = apiKey;
19
+ }
20
+ async request(method, route, body) {
21
+ const response = await fetch(`${this.baseUrl}${route}`, {
22
+ method,
23
+ headers: {
24
+ 'Content-Type': 'application/json',
25
+ 'X-Iranti-Key': this.apiKey,
26
+ },
27
+ body: body === undefined ? undefined : JSON.stringify(body),
28
+ });
29
+ const text = await response.text();
30
+ const payload = text ? JSON.parse(text) : null;
31
+ if (!response.ok) {
32
+ const error = typeof payload?.error === 'string' ? payload.error : `API error ${response.status}`;
33
+ throw new Error(error);
34
+ }
35
+ return payload;
36
+ }
37
+ handshake(agentId) {
38
+ return this.request('POST', '/memory/handshake', {
39
+ agent: agentId,
40
+ task: 'Interactive chat session',
41
+ recentMessages: [],
42
+ });
43
+ }
44
+ attend(agentId, currentContext, latestMessage) {
45
+ return this.request('POST', '/memory/attend', {
46
+ agentId,
47
+ currentContext,
48
+ latestMessage,
49
+ });
50
+ }
51
+ observe(agentId, currentContext) {
52
+ return this.request('POST', '/memory/observe', {
53
+ agentId,
54
+ currentContext,
55
+ });
56
+ }
57
+ query(entity, key) {
58
+ const [entityType, entityId] = splitEntity(entity);
59
+ return this.request('GET', `/kb/query/${entityType}/${entityId}/${encodeURIComponent(key)}`);
60
+ }
61
+ queryAll(entity) {
62
+ const [entityType, entityId] = splitEntity(entity);
63
+ return this.request('GET', `/kb/query/${entityType}/${entityId}`);
64
+ }
65
+ history(entity, key) {
66
+ const [entityType, entityId] = splitEntity(entity);
67
+ return this.request('GET', `/kb/history/${entityType}/${entityId}/${encodeURIComponent(key)}`);
68
+ }
69
+ async search(query) {
70
+ const search = new URLSearchParams({ query });
71
+ const payload = await this.request('GET', `/kb/search?${search.toString()}`);
72
+ return payload.results;
73
+ }
74
+ write(agentId, entity, key, value, summary, confidence, source = 'iranti_chat') {
75
+ return this.request('POST', '/kb/write', {
76
+ entity,
77
+ key,
78
+ value,
79
+ summary,
80
+ confidence,
81
+ source,
82
+ agent: agentId,
83
+ });
84
+ }
85
+ relate(params) {
86
+ return this.request('POST', '/kb/relate', params);
87
+ }
88
+ related(entity) {
89
+ const [entityType, entityId] = splitEntity(entity);
90
+ return this.request('GET', `/kb/related/${entityType}/${entityId}`);
91
+ }
92
+ }
93
+ function splitEntity(entity) {
94
+ const trimmed = entity.trim();
95
+ const separator = trimmed.indexOf('/');
96
+ if (separator <= 0 || separator === trimmed.length - 1) {
97
+ throw new Error(`Invalid entity format: "${entity}". Expected entityType/entityId.`);
98
+ }
99
+ return [trimmed.slice(0, separator), trimmed.slice(separator + 1)];
100
+ }
101
+ function isStrictEntity(value) {
102
+ if (!value)
103
+ return false;
104
+ const trimmed = value.trim();
105
+ if (!trimmed)
106
+ return false;
107
+ const parts = trimmed.split('/');
108
+ return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
109
+ }
110
+ function tokenizeCommand(input) {
111
+ const matches = input.match(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+/g);
112
+ if (!matches)
113
+ return [];
114
+ return matches.map((token) => {
115
+ if ((token.startsWith('"') && token.endsWith('"')) || (token.startsWith('\'') && token.endsWith('\''))) {
116
+ return token.slice(1, -1);
117
+ }
118
+ return token;
119
+ });
120
+ }
121
+ function parseLooseJson(value) {
122
+ try {
123
+ return JSON.parse(value);
124
+ }
125
+ catch {
126
+ return value;
127
+ }
128
+ }
129
+ function formatJson(value) {
130
+ return typeof value === 'string' ? value : JSON.stringify(value);
131
+ }
132
+ function buildMemoryBlock(fact) {
133
+ return `[MEMORY: ${fact.entityKey}] ${fact.summary} | value=${formatJson(fact.value)} | confidence=${fact.confidence} | source=${fact.source}`;
134
+ }
135
+ function formatConversation(history) {
136
+ return history.map((turn) => `${turn.role.toUpperCase()}: ${turn.content}`).join('\n');
137
+ }
138
+ function buildPreamble(agentId, brief) {
139
+ const workingMemory = brief.workingMemory.length === 0
140
+ ? '- none loaded'
141
+ : brief.workingMemory
142
+ .slice(0, 10)
143
+ .map((entry) => `- ${entry.entityKey}: ${entry.summary} (${entry.confidence}, ${entry.source})`)
144
+ .join('\n');
145
+ return [
146
+ `You are chatting through Iranti.`,
147
+ `Agent ID: ${agentId}`,
148
+ `Inferred task: ${brief.inferredTaskType}`,
149
+ `Operating rules: ${brief.operatingRules}`,
150
+ 'Known working memory:',
151
+ workingMemory,
152
+ ].join('\n');
153
+ }
154
+ function normalizeProvider(raw) {
155
+ const trimmed = raw?.trim().toLowerCase();
156
+ return trimmed && trimmed.length > 0 ? trimmed : undefined;
157
+ }
158
+ function resolveDefaultModel() {
159
+ return (0, router_1.getAllProfiles)().summarization.model;
160
+ }
161
+ function buildSummary(key, value) {
162
+ const raw = formatJson(value);
163
+ return `${key}: ${raw.length > 96 ? `${raw.slice(0, 93)}...` : raw}`;
164
+ }
165
+ function formatDate(value) {
166
+ if (!value)
167
+ return 'now';
168
+ return value.slice(0, 10);
169
+ }
170
+ function formatHistoryStatus(entry) {
171
+ if (entry.isCurrent)
172
+ return 'current';
173
+ if (entry.archivedReason)
174
+ return entry.archivedReason;
175
+ return 'historical';
176
+ }
177
+ function line(width) {
178
+ return '-'.repeat(width);
179
+ }
180
+ function resolveEscalationRoot() {
181
+ return path_1.default.resolve(process.env.IRANTI_ESCALATION_DIR ?? path_1.default.join(os_1.default.homedir(), '.iranti', 'escalation'));
182
+ }
183
+ async function hasPendingEscalations(root) {
184
+ const activeDir = path_1.default.join(root, 'active');
185
+ try {
186
+ const entries = await promises_1.default.readdir(activeDir, { withFileTypes: true });
187
+ for (const entry of entries) {
188
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
189
+ continue;
190
+ }
191
+ const content = await promises_1.default.readFile(path_1.default.join(activeDir, entry.name), 'utf-8');
192
+ if (content.includes('**Status:** PENDING')) {
193
+ return true;
194
+ }
195
+ }
196
+ return false;
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ }
202
+ function printHelp() {
203
+ console.log('/memory show all memory facts for this session');
204
+ console.log('/search <query> search facts by keyword or concept');
205
+ console.log('/inject <entity> <key> inject a specific fact into the next turn');
206
+ console.log('/write <key> <value> [conf] write a fact to session memory');
207
+ console.log('/observe manually pull memory from conversation history');
208
+ console.log('/history <entity> <key> show temporal history for a fact');
209
+ console.log('/relate <from> <to> <type> create a relationship between two entities');
210
+ console.log('/related <entity> show all relationships for an entity');
211
+ console.log('/resolve walk through pending conflict escalations');
212
+ console.log('/confidence <entity> <key> <n> update confidence score for a fact (0-100)');
213
+ console.log('/clear clear conversation history');
214
+ console.log('/provider <name> [model] switch LLM provider for this session');
215
+ console.log('/exit quit');
216
+ }
217
+ async function startChatSession(options = {}) {
218
+ (0, runtimeEnv_1.loadRuntimeEnv)({ cwd: options.cwd ?? process.cwd() });
219
+ const baseUrl = (process.env.IRANTI_URL ?? '').trim();
220
+ const apiKey = (process.env.IRANTI_API_KEY ?? '').trim();
221
+ if (!baseUrl) {
222
+ throw new Error('IRANTI_URL is required. Load .env.iranti or set IRANTI_URL before running iranti chat.');
223
+ }
224
+ if (!apiKey) {
225
+ throw new Error('IRANTI_API_KEY is required. Load .env.iranti or set IRANTI_API_KEY before running iranti chat.');
226
+ }
227
+ const supportedProviders = (0, llm_1.getSupportedProviders)();
228
+ const initialProvider = normalizeProvider(options.provider ?? process.env.LLM_PROVIDER) ?? 'mock';
229
+ if (!supportedProviders.includes(initialProvider)) {
230
+ throw new Error(`Unsupported provider "${initialProvider}". Available: ${supportedProviders.join(', ')}`);
231
+ }
232
+ process.env.LLM_PROVIDER = initialProvider;
233
+ const agentId = options.agentId?.trim() || 'iranti_chat';
234
+ const sessionEntity = `session/${agentId}`;
235
+ const client = new ApiClient(baseUrl.replace(/\/+$/, ''), apiKey);
236
+ let provider = initialProvider;
237
+ let model = options.model?.trim() || resolveDefaultModel();
238
+ let manualInjections = [];
239
+ const history = [];
240
+ const brief = await client.handshake(agentId);
241
+ console.log(`Iranti Chat — provider: ${provider}, model: ${model}`);
242
+ console.log('Type /help for commands. Ctrl+C to exit.');
243
+ if (brief.workingMemory.length > 0) {
244
+ console.log(`Loaded ${brief.workingMemory.length} memory entries.`);
245
+ }
246
+ const createInterface = () => promises_2.default.createInterface({
247
+ input: process.stdin,
248
+ output: process.stdout,
249
+ });
250
+ let rl = createInterface();
251
+ let closing = false;
252
+ const closeHandler = () => {
253
+ if (closing)
254
+ return;
255
+ closing = true;
256
+ console.log('\nExiting chat.');
257
+ rl.close();
258
+ };
259
+ process.on('SIGINT', closeHandler);
260
+ try {
261
+ while (!closing) {
262
+ const input = (await rl.question('> ')).trim();
263
+ if (!input) {
264
+ continue;
265
+ }
266
+ if (input.startsWith('/')) {
267
+ const parts = tokenizeCommand(input);
268
+ const command = parts[0]?.toLowerCase();
269
+ if (command === '/help') {
270
+ printHelp();
271
+ continue;
272
+ }
273
+ if (command === '/memory') {
274
+ const facts = await client.queryAll(sessionEntity);
275
+ if (facts.length === 0) {
276
+ console.log('No memory entries for this session.');
277
+ continue;
278
+ }
279
+ for (const fact of facts) {
280
+ console.log(`${fact.key} | ${fact.summary} | ${fact.confidence} | ${fact.source}`);
281
+ }
282
+ continue;
283
+ }
284
+ if (command === '/search') {
285
+ const query = input.replace(/^\/search\s*/i, '').trim();
286
+ if (!query) {
287
+ console.log('Usage: /search <query>');
288
+ continue;
289
+ }
290
+ const results = await client.search(query);
291
+ if (results.length === 0) {
292
+ console.log('No search results.');
293
+ continue;
294
+ }
295
+ for (const result of results) {
296
+ console.log(`${result.entity} | ${result.key} | ${result.summary} | ${result.score.toFixed(3)}`);
297
+ }
298
+ continue;
299
+ }
300
+ if (command === '/inject') {
301
+ if (parts.length < 3) {
302
+ console.log('Usage: /inject <entity> <key>');
303
+ continue;
304
+ }
305
+ const entity = parts[1];
306
+ const key = parts[2];
307
+ const fact = await client.query(entity, key);
308
+ if (!fact.found) {
309
+ console.log(`No fact found for ${entity}/${key}.`);
310
+ continue;
311
+ }
312
+ manualInjections.push({
313
+ entityKey: `${entity}/${key}`,
314
+ summary: fact.summary ?? buildSummary(key, fact.value),
315
+ value: fact.value,
316
+ confidence: fact.confidence ?? 0,
317
+ source: fact.source ?? 'unknown',
318
+ });
319
+ console.log(`Injected: [${entity}/${key}] ${fact.summary ?? buildSummary(key, fact.value)}`);
320
+ continue;
321
+ }
322
+ if (command === '/write') {
323
+ if (parts.length < 3) {
324
+ console.log('Usage: /write <key> <value> [confidence]');
325
+ continue;
326
+ }
327
+ const key = parts[1];
328
+ const maybeConfidence = parts.length >= 4 ? Number.parseInt(parts[parts.length - 1], 10) : NaN;
329
+ const hasConfidence = Number.isFinite(maybeConfidence);
330
+ const rawValue = hasConfidence
331
+ ? parts.slice(2, -1).join(' ')
332
+ : parts.slice(2).join(' ');
333
+ const confidence = hasConfidence ? maybeConfidence : 75;
334
+ const value = parseLooseJson(rawValue);
335
+ const result = await client.write(agentId, sessionEntity, key, value, buildSummary(key, value), confidence);
336
+ console.log(`${result.action} | ${result.reason}`);
337
+ continue;
338
+ }
339
+ if (command === '/observe') {
340
+ const observed = await client.observe(agentId, formatConversation(history));
341
+ if (observed.facts.length === 0) {
342
+ console.log('Nothing to inject.');
343
+ continue;
344
+ }
345
+ manualInjections = [...manualInjections, ...observed.facts];
346
+ console.log(`Queued ${observed.facts.length} memory facts for the next turn.`);
347
+ continue;
348
+ }
349
+ if (command === '/history') {
350
+ if (parts.length < 3) {
351
+ console.log('Usage: /history <entity> <key>');
352
+ continue;
353
+ }
354
+ const entity = parts[1];
355
+ const key = parts[2];
356
+ if (!isStrictEntity(entity)) {
357
+ console.log('Invalid entity format. Use entityType/entityId (e.g. project/acme).');
358
+ continue;
359
+ }
360
+ try {
361
+ const entries = await client.history(entity, key);
362
+ if (entries.length === 0) {
363
+ console.log(`No history found for ${entity}/${key}.`);
364
+ continue;
365
+ }
366
+ const ordered = [...entries].sort((left, right) => left.validFrom.localeCompare(right.validFrom));
367
+ console.log(`History: ${entity} -> ${key}`);
368
+ console.log(line(53));
369
+ ordered.forEach((entry, index) => {
370
+ console.log(` ${index + 1}. [${formatDate(entry.validFrom)} -> ${formatDate(entry.validUntil)}] ${formatJson(entry.value)} conf:${entry.confidence} source:${entry.source} ${formatHistoryStatus(entry)}`);
371
+ });
372
+ console.log(line(53));
373
+ console.log(`${ordered.length} interval${ordered.length === 1 ? '' : 's'}`);
374
+ }
375
+ catch (error) {
376
+ console.log(error instanceof Error ? error.message : String(error));
377
+ }
378
+ continue;
379
+ }
380
+ if (command === '/relate') {
381
+ if (parts.length < 4) {
382
+ console.log('Usage: /relate <from> <to> <type>');
383
+ continue;
384
+ }
385
+ const fromEntity = parts[1];
386
+ const toEntity = parts[2];
387
+ const relationshipType = parts.slice(3).join(' ').trim();
388
+ if (!isStrictEntity(fromEntity) || !isStrictEntity(toEntity)) {
389
+ console.log('Invalid entity format. Use entityType/entityId (e.g. project/acme).');
390
+ continue;
391
+ }
392
+ if (!relationshipType) {
393
+ console.log('Usage: /relate <from> <to> <type>');
394
+ continue;
395
+ }
396
+ try {
397
+ await client.relate({
398
+ fromEntity,
399
+ toEntity,
400
+ relationshipType,
401
+ createdBy: agentId,
402
+ });
403
+ console.log(`Related: ${fromEntity} -> ${toEntity} [${relationshipType}]`);
404
+ }
405
+ catch (error) {
406
+ console.log(error instanceof Error ? error.message : String(error));
407
+ }
408
+ continue;
409
+ }
410
+ if (command === '/related') {
411
+ if (parts.length < 2) {
412
+ console.log('Invalid entity format. Use entityType/entityId (e.g. project/acme).');
413
+ continue;
414
+ }
415
+ const entity = parts[1];
416
+ if (!isStrictEntity(entity)) {
417
+ console.log('Invalid entity format. Use entityType/entityId (e.g. project/acme).');
418
+ continue;
419
+ }
420
+ try {
421
+ const relationships = await client.related(entity);
422
+ if (relationships.length === 0) {
423
+ console.log(`No relationships found for ${entity}.`);
424
+ continue;
425
+ }
426
+ console.log(`Related entities: ${entity}`);
427
+ console.log(line(33));
428
+ for (const relationship of relationships) {
429
+ const arrow = relationship.direction === 'outbound' ? '->' : '<-';
430
+ console.log(` ${relationship.relationshipType} ${arrow} ${relationship.entityType}/${relationship.entityId}`);
431
+ }
432
+ console.log(line(33));
433
+ console.log(`${relationships.length} relationship${relationships.length === 1 ? '' : 's'}`);
434
+ }
435
+ catch (error) {
436
+ console.log(error instanceof Error ? error.message : String(error));
437
+ }
438
+ continue;
439
+ }
440
+ if (command === '/resolve') {
441
+ try {
442
+ const escalationRoot = resolveEscalationRoot();
443
+ if (!(await hasPendingEscalations(escalationRoot))) {
444
+ console.log('No pending escalations.');
445
+ continue;
446
+ }
447
+ console.log(line(48));
448
+ console.log('Resolutionist');
449
+ console.log(line(48));
450
+ rl.close();
451
+ await (0, resolutionist_1.resolveInteractive)(escalationRoot);
452
+ if (!closing) {
453
+ rl = createInterface();
454
+ console.log(line(48));
455
+ console.log('Back in chat');
456
+ console.log(line(48));
457
+ }
458
+ }
459
+ catch (error) {
460
+ if (!closing) {
461
+ rl = createInterface();
462
+ }
463
+ console.log(error instanceof Error ? error.message : String(error));
464
+ }
465
+ continue;
466
+ }
467
+ if (command === '/confidence') {
468
+ if (parts.length < 4) {
469
+ console.log('Usage: /confidence <entity> <key> <new_value>');
470
+ continue;
471
+ }
472
+ const entity = parts[1];
473
+ const key = parts[2];
474
+ const nextConfidence = Number.parseInt(parts[3], 10);
475
+ if (!isStrictEntity(entity)) {
476
+ console.log('Invalid entity format. Use entityType/entityId (e.g. project/acme).');
477
+ continue;
478
+ }
479
+ if (!Number.isInteger(nextConfidence) || nextConfidence < 0 || nextConfidence > 100) {
480
+ console.log('Confidence must be an integer between 0 and 100.');
481
+ continue;
482
+ }
483
+ try {
484
+ const current = await client.query(entity, key);
485
+ if (!current.found) {
486
+ console.log(`No fact found for ${entity}/${key}.`);
487
+ continue;
488
+ }
489
+ const oldConfidence = current.confidence ?? 0;
490
+ const value = current.value;
491
+ const summary = current.summary ?? buildSummary(key, value);
492
+ const result = await client.write(agentId, entity, key, value, summary, nextConfidence, current.source ?? 'iranti_chat');
493
+ console.log(`confidence updated: ${oldConfidence} -> ${nextConfidence} | ${result.action}`);
494
+ }
495
+ catch (error) {
496
+ console.log(error instanceof Error ? error.message : String(error));
497
+ }
498
+ continue;
499
+ }
500
+ if (command === '/clear') {
501
+ history.length = 0;
502
+ manualInjections = [];
503
+ console.log('Conversation history cleared.');
504
+ continue;
505
+ }
506
+ if (command === '/provider') {
507
+ const nextProvider = normalizeProvider(parts[1]);
508
+ if (!nextProvider) {
509
+ console.log(`Usage: /provider <${supportedProviders.join('|')}> [model]`);
510
+ continue;
511
+ }
512
+ if (!supportedProviders.includes(nextProvider)) {
513
+ console.log(`Unsupported provider "${nextProvider}". Available: ${supportedProviders.join(', ')}`);
514
+ continue;
515
+ }
516
+ provider = nextProvider;
517
+ process.env.LLM_PROVIDER = nextProvider;
518
+ model = parts[2]?.trim() || resolveDefaultModel();
519
+ console.log(`Switched to provider: ${provider} (${model})`);
520
+ continue;
521
+ }
522
+ if (command === '/exit' || command === '/quit') {
523
+ closeHandler();
524
+ continue;
525
+ }
526
+ console.log(`Unknown command: ${command}. Type /help for available commands.`);
527
+ continue;
528
+ }
529
+ const currentContext = formatConversation(history);
530
+ const attended = await client.attend(agentId, currentContext, input);
531
+ const autoBlocks = attended.shouldInject ? attended.facts.map(buildMemoryBlock) : [];
532
+ const manualBlocks = manualInjections.map(buildMemoryBlock);
533
+ const memoryBlocks = [...manualBlocks, ...autoBlocks];
534
+ manualInjections = [];
535
+ if (memoryBlocks.length > 0) {
536
+ console.log(`Injecting ${memoryBlocks.length} memory fact(s).`);
537
+ }
538
+ const messages = [
539
+ { role: 'user', content: buildPreamble(agentId, brief) },
540
+ ...history.map((turn) => ({ role: turn.role, content: turn.content })),
541
+ {
542
+ role: 'user',
543
+ content: [
544
+ memoryBlocks.join('\n'),
545
+ input,
546
+ ].filter(Boolean).join('\n\n'),
547
+ },
548
+ ];
549
+ const response = await (0, llm_1.completeWithFallback)(messages, {
550
+ preferredProvider: provider,
551
+ model,
552
+ });
553
+ console.log(response.text.trim());
554
+ history.push({ role: 'user', content: input });
555
+ history.push({ role: 'assistant', content: response.text.trim() });
556
+ // observe() is retrieval-only in the current codebase; this background call just warms the next-turn memory path.
557
+ void client.observe(agentId, formatConversation(history)).catch(() => undefined);
558
+ }
559
+ }
560
+ finally {
561
+ process.off('SIGINT', closeHandler);
562
+ rl.close();
563
+ }
564
+ }
565
+ //# sourceMappingURL=index.js.map