@ixo/common 1.1.3 → 1.1.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/.turbo/turbo-build.log +1 -1
- package/dist/ai/models/openai.d.ts +16 -0
- package/dist/ai/models/openai.d.ts.map +1 -1
- package/dist/ai/models/openai.js +26 -0
- package/dist/ai/models/openai.js.map +1 -1
- package/dist/ai/nodes/generic-chat/generic-chat.node.d.ts +1 -1
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts +1 -2
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts.map +1 -1
- package/dist/ai/semantic-router-factory/create-semantic-router.js +2 -3
- package/dist/ai/semantic-router-factory/create-semantic-router.js.map +1 -1
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts +1 -1
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts.map +1 -1
- package/dist/ai/tools/parser-action-tool.d.ts +1 -1
- package/dist/ai/tools/parser-action-tool.d.ts.map +1 -1
- package/dist/ai/tools/parser-action-tool.js +10 -3
- package/dist/ai/tools/parser-action-tool.js.map +1 -1
- package/dist/ai/tools/parser-browser-tool.d.ts +1 -1
- package/dist/ai/tools/parser-browser-tool.d.ts.map +1 -1
- package/dist/ai/tools/scrape-web-page.d.ts +1 -1
- package/dist/ai/tools/scrape-web-page.d.ts.map +1 -1
- package/dist/ai/tools/web-search-tool.d.ts +1 -1
- package/dist/ai/tools/web-search-tool.d.ts.map +1 -1
- package/dist/ai/utils/load-file.d.ts +1 -0
- package/dist/ai/utils/load-file.d.ts.map +1 -1
- package/dist/ai/utils/load-file.js +8 -0
- package/dist/ai/utils/load-file.js.map +1 -1
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts +10 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts.map +1 -1
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js +4 -1
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js.map +1 -1
- package/dist/services/env/env.service.js.map +1 -1
- package/dist/services/memory-engine/memory-engine.service.d.ts +13 -11
- package/dist/services/memory-engine/memory-engine.service.d.ts.map +1 -1
- package/dist/services/memory-engine/memory-engine.service.js +133 -106
- package/dist/services/memory-engine/memory-engine.service.js.map +1 -1
- package/dist/services/memory-engine/types.d.ts +16 -0
- package/dist/services/memory-engine/types.d.ts.map +1 -1
- package/dist/services/memory-engine/types.js +4 -1
- package/dist/services/memory-engine/types.js.map +1 -1
- package/dist/services/session-manager/dto.d.ts +3 -0
- package/dist/services/session-manager/dto.d.ts.map +1 -1
- package/dist/services/session-manager/dto.js +18 -0
- package/dist/services/session-manager/dto.js.map +1 -1
- package/dist/services/session-manager/session-manager.service.d.ts +2 -2
- package/dist/services/session-manager/session-manager.service.d.ts.map +1 -1
- package/dist/services/session-manager/session-manager.service.js +78 -38
- package/dist/services/session-manager/session-manager.service.js.map +1 -1
- package/dist/utils/get-user-subscription.d.ts +2 -1
- package/dist/utils/get-user-subscription.d.ts.map +1 -1
- package/dist/utils/get-user-subscription.js +9 -5
- package/dist/utils/get-user-subscription.js.map +1 -1
- package/package.json +24 -24
- package/src/ai/models/openai.ts +31 -0
- package/src/ai/semantic-router-factory/create-semantic-router.test.ts +0 -3
- package/src/ai/semantic-router-factory/create-semantic-router.ts +2 -7
- package/src/ai/tools/parser-action-tool.ts +19 -3
- package/src/ai/utils/load-file.ts +17 -0
- package/src/ai/utils/transformGraphStateMessageToListMessageResponse.ts +18 -1
- package/src/services/env/env.service.ts +1 -1
- package/src/services/memory-engine/memory-engine.service.ts +196 -222
- package/src/services/memory-engine/types.ts +34 -0
- package/src/services/session-manager/dto.ts +14 -0
- package/src/services/session-manager/session-manager.service.ts +93 -40
- package/src/utils/get-user-subscription.ts +11 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Logger } from '@ixo/logger';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {
|
|
3
|
+
isBatchErrorSlot,
|
|
4
|
+
type SearchEnhancedBatchRequest,
|
|
5
|
+
type SearchEnhancedBatchResponse,
|
|
6
|
+
type SearchEnhancedRequest,
|
|
7
|
+
type SearchEnhancedResponse,
|
|
8
|
+
type UserContextData,
|
|
6
9
|
} from './types.js';
|
|
7
10
|
|
|
8
11
|
interface MemoryEngineAuthHeaders {
|
|
@@ -10,27 +13,40 @@ interface MemoryEngineAuthHeaders {
|
|
|
10
13
|
userToken: string;
|
|
11
14
|
oracleHomeServer: string;
|
|
12
15
|
userHomeServer: string;
|
|
16
|
+
/** When set, uses UCAN auth instead of Matrix tokens */
|
|
17
|
+
ucanInvocation?: string;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export class MemoryEngineService {
|
|
16
|
-
|
|
21
|
+
// Batch covers 6 queries running in parallel server-side. Bound by the
|
|
22
|
+
// slowest query, not 6× — but we leave headroom for cold caches.
|
|
23
|
+
private readonly BATCH_TIMEOUT_MS = 15000;
|
|
17
24
|
|
|
18
25
|
constructor(private readonly memoryEngineUrl: string) {}
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
|
-
*
|
|
28
|
+
* Build HTTP headers for memory engine requests (UCAN or Matrix)
|
|
22
29
|
*/
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
private buildHeaders(
|
|
31
|
+
auth: MemoryEngineAuthHeaders,
|
|
32
|
+
roomId: string,
|
|
33
|
+
): Record<string, string> {
|
|
34
|
+
if (auth.ucanInvocation) {
|
|
35
|
+
return {
|
|
36
|
+
Authorization: `Bearer ${auth.ucanInvocation}`,
|
|
37
|
+
'X-Auth-Type': 'ucan',
|
|
38
|
+
'x-room-id': roomId,
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
'x-oracle-token': auth.oracleToken,
|
|
44
|
+
'x-user-token': auth.userToken,
|
|
45
|
+
'x-oracle-matrix-homeserver': auth.oracleHomeServer,
|
|
46
|
+
'x-user-matrix-homeserver': auth.userHomeServer,
|
|
47
|
+
'x-room-id': roomId,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
};
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
/**
|
|
@@ -43,6 +59,8 @@ export class MemoryEngineService {
|
|
|
43
59
|
userToken: string;
|
|
44
60
|
oracleHomeServer: string;
|
|
45
61
|
userHomeServer: string;
|
|
62
|
+
/** When set, uses UCAN auth instead of Matrix tokens */
|
|
63
|
+
ucanInvocation?: string;
|
|
46
64
|
}): Promise<UserContextData> {
|
|
47
65
|
const {
|
|
48
66
|
oracleDid,
|
|
@@ -51,105 +69,160 @@ export class MemoryEngineService {
|
|
|
51
69
|
userToken,
|
|
52
70
|
oracleHomeServer,
|
|
53
71
|
userHomeServer,
|
|
72
|
+
ucanInvocation,
|
|
54
73
|
} = params;
|
|
55
74
|
|
|
56
75
|
Logger.info(
|
|
57
76
|
`[MemoryEngineService] Gathering user context for oracle: ${oracleDid}, room: ${roomId}`,
|
|
58
77
|
);
|
|
59
78
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
79
|
+
const authHeaders: MemoryEngineAuthHeaders = {
|
|
80
|
+
oracleToken,
|
|
81
|
+
userToken,
|
|
82
|
+
oracleHomeServer,
|
|
83
|
+
userHomeServer,
|
|
84
|
+
ucanInvocation,
|
|
85
|
+
};
|
|
68
86
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
results[0].status === 'fulfilled' ? results[0].value : undefined;
|
|
105
|
-
const work =
|
|
106
|
-
results[1].status === 'fulfilled' ? results[1].value : undefined;
|
|
107
|
-
const goals =
|
|
108
|
-
results[2].status === 'fulfilled' ? results[2].value : undefined;
|
|
109
|
-
const interests =
|
|
110
|
-
results[3].status === 'fulfilled' ? results[3].value : undefined;
|
|
111
|
-
const relationships =
|
|
112
|
-
results[4].status === 'fulfilled' ? results[4].value : undefined;
|
|
113
|
-
const recent =
|
|
114
|
-
results[5].status === 'fulfilled' ? results[5].value : undefined;
|
|
115
|
-
|
|
116
|
-
// Log any failures
|
|
117
|
-
results.forEach((result, index) => {
|
|
118
|
-
if (result.status === 'rejected') {
|
|
87
|
+
// The 6 queries that make up userContext. Order matters: it determines
|
|
88
|
+
// how we map batch result slots back to UserContextData fields.
|
|
89
|
+
const labels = [
|
|
90
|
+
'identity',
|
|
91
|
+
'work',
|
|
92
|
+
'goals',
|
|
93
|
+
'interests',
|
|
94
|
+
'relationships',
|
|
95
|
+
'recent',
|
|
96
|
+
] as const;
|
|
97
|
+
const requests: SearchEnhancedRequest[] = [
|
|
98
|
+
this.buildIdentityRequest(oracleDid),
|
|
99
|
+
this.buildWorkRequest(oracleDid),
|
|
100
|
+
this.buildGoalsRequest(oracleDid),
|
|
101
|
+
this.buildInterestsRequest(oracleDid),
|
|
102
|
+
this.buildRelationshipsRequest(oracleDid),
|
|
103
|
+
this.buildRecentRequest(oracleDid),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const gatherStart = Date.now();
|
|
107
|
+
const batch = await this.executeBatch(requests, roomId, authHeaders);
|
|
108
|
+
const gatherElapsed = Date.now() - gatherStart;
|
|
109
|
+
|
|
110
|
+
if (!batch) {
|
|
111
|
+
Logger.error(
|
|
112
|
+
`[MemoryEngineService] gatherUserContext failed after ${gatherElapsed}ms — returning empty context`,
|
|
113
|
+
);
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Map each slot back to the labelled field. Error slots become undefined.
|
|
118
|
+
const fields: (SearchEnhancedResponse | undefined)[] = batch.results.map(
|
|
119
|
+
(slot, index) => {
|
|
120
|
+
const label = labels[index];
|
|
121
|
+
if (isBatchErrorSlot(slot)) {
|
|
119
122
|
Logger.warn(
|
|
120
|
-
`[MemoryEngineService]
|
|
121
|
-
result.reason,
|
|
123
|
+
`[MemoryEngineService] Batch slot "${label}" failed (${slot.error.status_code}): ${slot.error.detail}`,
|
|
122
124
|
);
|
|
125
|
+
return undefined;
|
|
123
126
|
}
|
|
124
|
-
|
|
127
|
+
return slot;
|
|
128
|
+
},
|
|
129
|
+
);
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
goals,
|
|
130
|
-
interests,
|
|
131
|
-
relationships,
|
|
132
|
-
recent,
|
|
133
|
-
};
|
|
134
|
-
} catch (error) {
|
|
135
|
-
Logger.error(
|
|
136
|
-
'[MemoryEngineService] Failed to gather user context:',
|
|
137
|
-
error,
|
|
131
|
+
if (batch.results.length !== labels.length) {
|
|
132
|
+
Logger.warn(
|
|
133
|
+
`[MemoryEngineService] Batch length mismatch: expected ${labels.length}, got ${batch.results.length}`,
|
|
138
134
|
);
|
|
139
|
-
// Return empty context on error
|
|
140
|
-
return {};
|
|
141
135
|
}
|
|
136
|
+
|
|
137
|
+
const summary = labels.map((label, index) => {
|
|
138
|
+
const value = fields[index];
|
|
139
|
+
if (value === undefined) return `${label}=missing`;
|
|
140
|
+
return `${label}=ok(f${value.total_results.facts}/e${value.total_results.entities})`;
|
|
141
|
+
});
|
|
142
|
+
Logger.info(
|
|
143
|
+
`[MemoryEngineService] gatherUserContext completed in ${gatherElapsed}ms (batch) — ${summary.join(', ')}`,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
identity: fields[0],
|
|
148
|
+
work: fields[1],
|
|
149
|
+
goals: fields[2],
|
|
150
|
+
interests: fields[3],
|
|
151
|
+
relationships: fields[4],
|
|
152
|
+
recent: fields[5],
|
|
153
|
+
};
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
/**
|
|
145
|
-
*
|
|
157
|
+
* POST /search-enhanced-batch — single round-trip for N parallel queries.
|
|
158
|
+
* Returns undefined on transport/HTTP failure; a partially-failed batch
|
|
159
|
+
* still resolves with per-slot error markers (handled by caller via
|
|
160
|
+
* `isBatchErrorSlot`).
|
|
146
161
|
*/
|
|
147
|
-
private async
|
|
148
|
-
|
|
162
|
+
private async executeBatch(
|
|
163
|
+
queries: SearchEnhancedRequest[],
|
|
149
164
|
roomId: string,
|
|
150
165
|
auth: MemoryEngineAuthHeaders,
|
|
151
|
-
): Promise<
|
|
152
|
-
|
|
166
|
+
): Promise<SearchEnhancedBatchResponse | undefined> {
|
|
167
|
+
if (!roomId) {
|
|
168
|
+
Logger.warn(
|
|
169
|
+
`[MemoryEngineService] No room id provided, skipping batch search`,
|
|
170
|
+
);
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
if (!auth.ucanInvocation && (!auth.oracleToken || !auth.userToken)) {
|
|
174
|
+
Logger.warn(
|
|
175
|
+
`[MemoryEngineService] Missing auth (no UCAN and no Matrix tokens), skipping batch search`,
|
|
176
|
+
);
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const body: SearchEnhancedBatchRequest = { queries };
|
|
181
|
+
|
|
182
|
+
const controller = new AbortController();
|
|
183
|
+
const timer = setTimeout(() => controller.abort(), this.BATCH_TIMEOUT_MS);
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(
|
|
187
|
+
`${this.memoryEngineUrl}/search-enhanced-batch`,
|
|
188
|
+
{
|
|
189
|
+
method: 'POST',
|
|
190
|
+
headers: this.buildHeaders(auth, roomId),
|
|
191
|
+
body: JSON.stringify(body),
|
|
192
|
+
signal: controller.signal,
|
|
193
|
+
},
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
const errorText = await response.text();
|
|
198
|
+
Logger.warn(
|
|
199
|
+
`[MemoryEngineService] Batch search failed (${response.status}): ${errorText}`,
|
|
200
|
+
);
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (await response.json()) as SearchEnhancedBatchResponse;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if ((error as Error).name === 'AbortError') {
|
|
207
|
+
Logger.warn(
|
|
208
|
+
`[MemoryEngineService] Batch search aborted after ${this.BATCH_TIMEOUT_MS}ms`,
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
Logger.error(`[MemoryEngineService] Batch search threw:`, error);
|
|
212
|
+
}
|
|
213
|
+
return undefined;
|
|
214
|
+
} finally {
|
|
215
|
+
clearTimeout(timer);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ── Per-query request builders ────────────────────────────────────────────
|
|
220
|
+
// These produce SearchEnhancedRequest payloads consumed by gatherUserContext
|
|
221
|
+
// via the batch endpoint. Order matches the labels array in
|
|
222
|
+
// gatherUserContext — keep the two in sync.
|
|
223
|
+
|
|
224
|
+
private buildIdentityRequest(oracleDid: string): SearchEnhancedRequest {
|
|
225
|
+
return {
|
|
153
226
|
oracle_dids: [oracleDid],
|
|
154
227
|
query:
|
|
155
228
|
'username and nickname and age user identity traits values personality characteristics communication style beliefs preferences',
|
|
@@ -173,19 +246,10 @@ export class MemoryEngineService {
|
|
|
173
246
|
invalid_at: [[{ date: null, comparison_operator: 'IS NULL' }]],
|
|
174
247
|
},
|
|
175
248
|
};
|
|
176
|
-
|
|
177
|
-
return this.executeQuery(request, roomId, auth);
|
|
178
249
|
}
|
|
179
250
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
*/
|
|
183
|
-
private async queryWork(
|
|
184
|
-
oracleDid: string,
|
|
185
|
-
roomId: string,
|
|
186
|
-
auth: MemoryEngineAuthHeaders,
|
|
187
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
188
|
-
const request: SearchEnhancedRequest = {
|
|
251
|
+
private buildWorkRequest(oracleDid: string): SearchEnhancedRequest {
|
|
252
|
+
return {
|
|
189
253
|
oracle_dids: [oracleDid],
|
|
190
254
|
query:
|
|
191
255
|
'work job career projects skills organization employment role responsibilities expertise',
|
|
@@ -216,19 +280,10 @@ export class MemoryEngineService {
|
|
|
216
280
|
invalid_at: [[{ date: null, comparison_operator: 'IS NULL' }]],
|
|
217
281
|
},
|
|
218
282
|
};
|
|
219
|
-
|
|
220
|
-
return this.executeQuery(request, roomId, auth);
|
|
221
283
|
}
|
|
222
284
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
*/
|
|
226
|
-
private async queryGoals(
|
|
227
|
-
oracleDid: string,
|
|
228
|
-
roomId: string,
|
|
229
|
-
auth: MemoryEngineAuthHeaders,
|
|
230
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
231
|
-
const request: SearchEnhancedRequest = {
|
|
285
|
+
private buildGoalsRequest(oracleDid: string): SearchEnhancedRequest {
|
|
286
|
+
return {
|
|
232
287
|
oracle_dids: [oracleDid],
|
|
233
288
|
query:
|
|
234
289
|
'goals aspirations objectives milestones habits routines patterns achievements progress',
|
|
@@ -251,19 +306,10 @@ export class MemoryEngineService {
|
|
|
251
306
|
invalid_at: [[{ date: null, comparison_operator: 'IS NULL' }]],
|
|
252
307
|
},
|
|
253
308
|
};
|
|
254
|
-
|
|
255
|
-
return this.executeQuery(request, roomId, auth);
|
|
256
309
|
}
|
|
257
310
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
*/
|
|
261
|
-
private async queryInterests(
|
|
262
|
-
oracleDid: string,
|
|
263
|
-
roomId: string,
|
|
264
|
-
auth: MemoryEngineAuthHeaders,
|
|
265
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
266
|
-
const request: SearchEnhancedRequest = {
|
|
311
|
+
private buildInterestsRequest(oracleDid: string): SearchEnhancedRequest {
|
|
312
|
+
return {
|
|
267
313
|
oracle_dids: [oracleDid],
|
|
268
314
|
query:
|
|
269
315
|
'interests hobbies passions preferences likes dislikes expertise topics content',
|
|
@@ -293,19 +339,10 @@ export class MemoryEngineService {
|
|
|
293
339
|
invalid_at: [[{ date: null, comparison_operator: 'IS NULL' }]],
|
|
294
340
|
},
|
|
295
341
|
};
|
|
296
|
-
|
|
297
|
-
return this.executeQuery(request, roomId, auth);
|
|
298
342
|
}
|
|
299
343
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
*/
|
|
303
|
-
private async queryRelationships(
|
|
304
|
-
oracleDid: string,
|
|
305
|
-
roomId: string,
|
|
306
|
-
auth: MemoryEngineAuthHeaders,
|
|
307
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
308
|
-
const request: SearchEnhancedRequest = {
|
|
344
|
+
private buildRelationshipsRequest(oracleDid: string): SearchEnhancedRequest {
|
|
345
|
+
return {
|
|
309
346
|
oracle_dids: [oracleDid],
|
|
310
347
|
query:
|
|
311
348
|
'relationships people connections social network colleagues friends family contacts',
|
|
@@ -328,24 +365,17 @@ export class MemoryEngineService {
|
|
|
328
365
|
invalid_at: [[{ date: null, comparison_operator: 'IS NULL' }]],
|
|
329
366
|
},
|
|
330
367
|
};
|
|
331
|
-
|
|
332
|
-
return this.executeQuery(request, roomId, auth);
|
|
333
368
|
}
|
|
334
369
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
oracleDid: string,
|
|
340
|
-
roomId: string,
|
|
341
|
-
auth: MemoryEngineAuthHeaders,
|
|
342
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
343
|
-
// Calculate date 90 days ago for recent context
|
|
370
|
+
private buildRecentRequest(oracleDid: string): SearchEnhancedRequest {
|
|
371
|
+
// Server-side `recent_memory` strategy auto-injects a created_at >= now-90d
|
|
372
|
+
// filter. We still pass it explicitly as defense-in-depth — the server's
|
|
373
|
+
// merge logic respects an existing lower bound and won't double-apply.
|
|
344
374
|
const ninetyDaysAgo = new Date();
|
|
345
375
|
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
|
346
376
|
const dateString = ninetyDaysAgo.toISOString();
|
|
347
377
|
|
|
348
|
-
|
|
378
|
+
return {
|
|
349
379
|
oracle_dids: [oracleDid],
|
|
350
380
|
query:
|
|
351
381
|
'recent conversations messages discussions activities updates interactions',
|
|
@@ -359,8 +389,6 @@ export class MemoryEngineService {
|
|
|
359
389
|
created_at: [[{ date: dateString, comparison_operator: '>=' }]],
|
|
360
390
|
},
|
|
361
391
|
};
|
|
362
|
-
|
|
363
|
-
return this.executeQuery(request, roomId, auth);
|
|
364
392
|
}
|
|
365
393
|
|
|
366
394
|
/**
|
|
@@ -373,6 +401,7 @@ export class MemoryEngineService {
|
|
|
373
401
|
userToken,
|
|
374
402
|
oracleHomeServer,
|
|
375
403
|
userHomeServer,
|
|
404
|
+
ucanInvocation,
|
|
376
405
|
}: {
|
|
377
406
|
messages: Array<{
|
|
378
407
|
content: string;
|
|
@@ -386,6 +415,8 @@ export class MemoryEngineService {
|
|
|
386
415
|
userToken: string;
|
|
387
416
|
oracleHomeServer: string;
|
|
388
417
|
userHomeServer: string;
|
|
418
|
+
/** When set, uses UCAN auth instead of Matrix tokens */
|
|
419
|
+
ucanInvocation?: string;
|
|
389
420
|
}): Promise<{ success: boolean }> {
|
|
390
421
|
if (!roomId) {
|
|
391
422
|
Logger.warn(
|
|
@@ -393,9 +424,9 @@ export class MemoryEngineService {
|
|
|
393
424
|
);
|
|
394
425
|
return { success: false };
|
|
395
426
|
}
|
|
396
|
-
if (!oracleToken || !userToken) {
|
|
427
|
+
if (!ucanInvocation && (!oracleToken || !userToken)) {
|
|
397
428
|
Logger.warn(
|
|
398
|
-
`[MemoryEngineService] Missing
|
|
429
|
+
`[MemoryEngineService] Missing auth (no UCAN and no Matrix tokens), skipping conversation processing`,
|
|
399
430
|
);
|
|
400
431
|
return { success: false };
|
|
401
432
|
}
|
|
@@ -407,16 +438,16 @@ export class MemoryEngineService {
|
|
|
407
438
|
}
|
|
408
439
|
|
|
409
440
|
try {
|
|
441
|
+
const auth: MemoryEngineAuthHeaders = {
|
|
442
|
+
oracleToken,
|
|
443
|
+
userToken,
|
|
444
|
+
oracleHomeServer,
|
|
445
|
+
userHomeServer,
|
|
446
|
+
ucanInvocation,
|
|
447
|
+
};
|
|
410
448
|
const response = await fetch(`${this.memoryEngineUrl}/messages`, {
|
|
411
449
|
method: 'POST',
|
|
412
|
-
headers:
|
|
413
|
-
'x-oracle-token': oracleToken,
|
|
414
|
-
'x-user-token': userToken,
|
|
415
|
-
'x-oracle-matrix-homeserver': oracleHomeServer,
|
|
416
|
-
'x-user-matrix-homeserver': userHomeServer,
|
|
417
|
-
'x-room-id': roomId,
|
|
418
|
-
'Content-Type': 'application/json',
|
|
419
|
-
},
|
|
450
|
+
headers: this.buildHeaders(auth, roomId),
|
|
420
451
|
body: JSON.stringify({ messages }),
|
|
421
452
|
});
|
|
422
453
|
|
|
@@ -440,61 +471,4 @@ export class MemoryEngineService {
|
|
|
440
471
|
return { success: false };
|
|
441
472
|
}
|
|
442
473
|
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Execute a search query against the Memory Engine API
|
|
446
|
-
*/
|
|
447
|
-
private async executeQuery(
|
|
448
|
-
request: SearchEnhancedRequest,
|
|
449
|
-
roomId: string,
|
|
450
|
-
auth: MemoryEngineAuthHeaders,
|
|
451
|
-
): Promise<SearchEnhancedResponse | undefined> {
|
|
452
|
-
if (!roomId) {
|
|
453
|
-
Logger.warn(
|
|
454
|
-
`[MemoryEngineService] No room id provided, skipping query "${request.query}"`,
|
|
455
|
-
);
|
|
456
|
-
return undefined;
|
|
457
|
-
}
|
|
458
|
-
if (!auth.oracleToken || !auth.userToken) {
|
|
459
|
-
Logger.warn(
|
|
460
|
-
`[MemoryEngineService] Missing oracle or user token, skipping query "${request.query}"`,
|
|
461
|
-
);
|
|
462
|
-
return undefined;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
const response = await fetch(`${this.memoryEngineUrl}/search-enhanced`, {
|
|
467
|
-
method: 'POST',
|
|
468
|
-
headers: {
|
|
469
|
-
'x-oracle-token': auth.oracleToken,
|
|
470
|
-
'x-user-token': auth.userToken,
|
|
471
|
-
'x-oracle-matrix-homeserver': auth.oracleHomeServer,
|
|
472
|
-
'x-user-matrix-homeserver': auth.userHomeServer,
|
|
473
|
-
'x-room-id': roomId,
|
|
474
|
-
'Content-Type': 'application/json',
|
|
475
|
-
},
|
|
476
|
-
body: JSON.stringify(request),
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
if (!response.ok) {
|
|
480
|
-
const errorText = await response.text();
|
|
481
|
-
Logger.warn(
|
|
482
|
-
`[MemoryEngineService] Memory Engine query failed (${response.status}): ${errorText}`,
|
|
483
|
-
);
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const result = (await response.json()) as SearchEnhancedResponse;
|
|
488
|
-
Logger.info(
|
|
489
|
-
`[MemoryEngineService] Query "${request.query}" returned ${result.total_results.facts} facts, ${result.total_results.entities} entities`,
|
|
490
|
-
);
|
|
491
|
-
return result;
|
|
492
|
-
} catch (error) {
|
|
493
|
-
Logger.error(
|
|
494
|
-
`[MemoryEngineService] Failed to execute query "${request.query}":`,
|
|
495
|
-
error,
|
|
496
|
-
);
|
|
497
|
-
return undefined;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
474
|
}
|
|
@@ -206,3 +206,37 @@ export interface UserContextData {
|
|
|
206
206
|
relationships?: SearchEnhancedResponse;
|
|
207
207
|
recent?: SearchEnhancedResponse;
|
|
208
208
|
}
|
|
209
|
+
|
|
210
|
+
// Batch search types — backend endpoint POST /search-enhanced-batch
|
|
211
|
+
export interface SearchEnhancedBatchRequest {
|
|
212
|
+
queries: SearchEnhancedRequest[];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// A failed slot in the batch response. The server returns this in place of
|
|
216
|
+
// SearchEnhancedResponse when a single query fails — the rest of the batch
|
|
217
|
+
// still completes.
|
|
218
|
+
export interface SearchEnhancedBatchErrorSlot {
|
|
219
|
+
error: {
|
|
220
|
+
status_code: number;
|
|
221
|
+
detail: string;
|
|
222
|
+
};
|
|
223
|
+
query: string;
|
|
224
|
+
strategy_used: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type SearchEnhancedBatchSlot =
|
|
228
|
+
| SearchEnhancedResponse
|
|
229
|
+
| SearchEnhancedBatchErrorSlot;
|
|
230
|
+
|
|
231
|
+
export interface SearchEnhancedBatchResponse {
|
|
232
|
+
results: SearchEnhancedBatchSlot[];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function isBatchErrorSlot(
|
|
236
|
+
slot: SearchEnhancedBatchSlot,
|
|
237
|
+
): slot is SearchEnhancedBatchErrorSlot {
|
|
238
|
+
return (
|
|
239
|
+
typeof (slot as SearchEnhancedBatchErrorSlot).error === 'object' &&
|
|
240
|
+
(slot as SearchEnhancedBatchErrorSlot).error !== null
|
|
241
|
+
);
|
|
242
|
+
}
|
|
@@ -32,6 +32,11 @@ export class ListChatSessionsDto extends UserAuthDto {
|
|
|
32
32
|
@IsOptional()
|
|
33
33
|
@Min(0)
|
|
34
34
|
offset?: number;
|
|
35
|
+
|
|
36
|
+
/** Filter sessions by roomId. When set, only sessions in this room are returned. */
|
|
37
|
+
@IsString()
|
|
38
|
+
@IsOptional()
|
|
39
|
+
roomId?: string;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export class CreateChatSessionDto extends UserAuthDto {
|
|
@@ -66,6 +71,15 @@ export class CreateChatSessionDto extends UserAuthDto {
|
|
|
66
71
|
@IsString()
|
|
67
72
|
@IsOptional()
|
|
68
73
|
userHomeServer?: string;
|
|
74
|
+
|
|
75
|
+
@IsString()
|
|
76
|
+
@IsOptional()
|
|
77
|
+
ucanInvocation?: string;
|
|
78
|
+
|
|
79
|
+
/** Override the roomId stored on the session (e.g. task-specific room). */
|
|
80
|
+
@IsString()
|
|
81
|
+
@IsOptional()
|
|
82
|
+
roomId?: string;
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
export class DeleteChatSessionDto extends UserAuthDto {
|