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.
- package/README.md +38 -11
- package/dist/scripts/iranti-cli.js +525 -50
- package/dist/scripts/iranti-mcp.js +1 -1
- package/dist/scripts/seed.js +10 -10
- package/dist/src/api/server.js +1 -1
- package/dist/src/chat/index.d.ts +8 -0
- package/dist/src/chat/index.d.ts.map +1 -0
- package/dist/src/chat/index.js +565 -0
- package/dist/src/chat/index.js.map +1 -0
- package/dist/src/lib/llm.d.ts +1 -0
- package/dist/src/lib/llm.d.ts.map +1 -1
- package/dist/src/lib/llm.js +4 -0
- package/dist/src/lib/llm.js.map +1 -1
- package/dist/src/lib/router.d.ts.map +1 -1
- package/dist/src/lib/router.js +46 -42
- package/dist/src/lib/router.js.map +1 -1
- package/dist/src/librarian/contextual-conflicts.d.ts +9 -0
- package/dist/src/librarian/contextual-conflicts.d.ts.map +1 -0
- package/dist/src/librarian/contextual-conflicts.js +243 -0
- package/dist/src/librarian/contextual-conflicts.js.map +1 -0
- package/dist/src/librarian/index.d.ts.map +1 -1
- package/dist/src/librarian/index.js +50 -0
- package/dist/src/librarian/index.js.map +1 -1
- package/dist/src/library/backends/chromaBackend.d.ts +27 -0
- package/dist/src/library/backends/chromaBackend.d.ts.map +1 -0
- package/dist/src/library/backends/chromaBackend.js +99 -0
- package/dist/src/library/backends/chromaBackend.js.map +1 -0
- package/dist/src/library/backends/index.d.ts +15 -0
- package/dist/src/library/backends/index.d.ts.map +1 -0
- package/dist/src/library/backends/index.js +39 -0
- package/dist/src/library/backends/index.js.map +1 -0
- package/dist/src/library/backends/pgvectorBackend.d.ts +8 -0
- package/dist/src/library/backends/pgvectorBackend.d.ts.map +1 -0
- package/dist/src/library/backends/pgvectorBackend.js +128 -0
- package/dist/src/library/backends/pgvectorBackend.js.map +1 -0
- package/dist/src/library/backends/qdrantBackend.d.ts +21 -0
- package/dist/src/library/backends/qdrantBackend.d.ts.map +1 -0
- package/dist/src/library/backends/qdrantBackend.js +107 -0
- package/dist/src/library/backends/qdrantBackend.js.map +1 -0
- package/dist/src/library/queries.d.ts.map +1 -1
- package/dist/src/library/queries.js +105 -123
- package/dist/src/library/queries.js.map +1 -1
- package/dist/src/library/vectorBackend.d.ts +19 -0
- package/dist/src/library/vectorBackend.d.ts.map +1 -0
- package/dist/src/library/vectorBackend.js +3 -0
- package/dist/src/library/vectorBackend.js.map +1 -0
- package/dist/src/resolutionist/index.d.ts +8 -0
- package/dist/src/resolutionist/index.d.ts.map +1 -0
- package/dist/src/resolutionist/index.js +265 -0
- package/dist/src/resolutionist/index.js.map +1 -0
- 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.
|
|
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.
|
package/dist/scripts/seed.js
CHANGED
|
@@ -15,7 +15,7 @@ const STAFF_ENTRIES = [
|
|
|
15
15
|
entityId: 'librarian',
|
|
16
16
|
key: 'operating_rules',
|
|
17
17
|
valueRaw: {
|
|
18
|
-
version: '0.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
240
|
+
version: '0.2.4',
|
|
241
241
|
events: [
|
|
242
242
|
{
|
|
243
243
|
at: new Date().toISOString(),
|
package/dist/src/api/server.js
CHANGED
|
@@ -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
|