@ixo/common 1.1.34 → 1.1.36

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.
@@ -10,6 +10,8 @@ interface MemoryEngineAuthHeaders {
10
10
  userToken: string;
11
11
  oracleHomeServer: string;
12
12
  userHomeServer: string;
13
+ /** When set, uses UCAN auth instead of Matrix tokens */
14
+ ucanInvocation?: string;
13
15
  }
14
16
 
15
17
  export class MemoryEngineService {
@@ -17,6 +19,31 @@ export class MemoryEngineService {
17
19
 
18
20
  constructor(private readonly memoryEngineUrl: string) {}
19
21
 
22
+ /**
23
+ * Build HTTP headers for memory engine requests (UCAN or Matrix)
24
+ */
25
+ private buildHeaders(
26
+ auth: MemoryEngineAuthHeaders,
27
+ roomId: string,
28
+ ): Record<string, string> {
29
+ if (auth.ucanInvocation) {
30
+ return {
31
+ Authorization: `Bearer ${auth.ucanInvocation}`,
32
+ 'X-Auth-Type': 'ucan',
33
+ 'x-room-id': roomId,
34
+ 'Content-Type': 'application/json',
35
+ };
36
+ }
37
+ return {
38
+ 'x-oracle-token': auth.oracleToken,
39
+ 'x-user-token': auth.userToken,
40
+ 'x-oracle-matrix-homeserver': auth.oracleHomeServer,
41
+ 'x-user-matrix-homeserver': auth.userHomeServer,
42
+ 'x-room-id': roomId,
43
+ 'Content-Type': 'application/json',
44
+ };
45
+ }
46
+
20
47
  /**
21
48
  * Wraps a promise with a timeout, returning fallback value if timeout is exceeded
22
49
  */
@@ -43,6 +70,8 @@ export class MemoryEngineService {
43
70
  userToken: string;
44
71
  oracleHomeServer: string;
45
72
  userHomeServer: string;
73
+ /** When set, uses UCAN auth instead of Matrix tokens */
74
+ ucanInvocation?: string;
46
75
  }): Promise<UserContextData> {
47
76
  const {
48
77
  oracleDid,
@@ -51,6 +80,7 @@ export class MemoryEngineService {
51
80
  userToken,
52
81
  oracleHomeServer,
53
82
  userHomeServer,
83
+ ucanInvocation,
54
84
  } = params;
55
85
 
56
86
  Logger.info(
@@ -59,11 +89,12 @@ export class MemoryEngineService {
59
89
 
60
90
  try {
61
91
  // Execute all 6 queries in parallel with timeouts using Promise.allSettled
62
- const authHeaders = {
92
+ const authHeaders: MemoryEngineAuthHeaders = {
63
93
  oracleToken,
64
94
  userToken,
65
95
  oracleHomeServer,
66
96
  userHomeServer,
97
+ ucanInvocation,
67
98
  };
68
99
 
69
100
  const results = await Promise.allSettled([
@@ -373,6 +404,7 @@ export class MemoryEngineService {
373
404
  userToken,
374
405
  oracleHomeServer,
375
406
  userHomeServer,
407
+ ucanInvocation,
376
408
  }: {
377
409
  messages: Array<{
378
410
  content: string;
@@ -386,6 +418,8 @@ export class MemoryEngineService {
386
418
  userToken: string;
387
419
  oracleHomeServer: string;
388
420
  userHomeServer: string;
421
+ /** When set, uses UCAN auth instead of Matrix tokens */
422
+ ucanInvocation?: string;
389
423
  }): Promise<{ success: boolean }> {
390
424
  if (!roomId) {
391
425
  Logger.warn(
@@ -393,9 +427,9 @@ export class MemoryEngineService {
393
427
  );
394
428
  return { success: false };
395
429
  }
396
- if (!oracleToken || !userToken) {
430
+ if (!ucanInvocation && (!oracleToken || !userToken)) {
397
431
  Logger.warn(
398
- `[MemoryEngineService] Missing oracle or user token, skipping conversation processing`,
432
+ `[MemoryEngineService] Missing auth (no UCAN and no Matrix tokens), skipping conversation processing`,
399
433
  );
400
434
  return { success: false };
401
435
  }
@@ -407,16 +441,16 @@ export class MemoryEngineService {
407
441
  }
408
442
 
409
443
  try {
444
+ const auth: MemoryEngineAuthHeaders = {
445
+ oracleToken,
446
+ userToken,
447
+ oracleHomeServer,
448
+ userHomeServer,
449
+ ucanInvocation,
450
+ };
410
451
  const response = await fetch(`${this.memoryEngineUrl}/messages`, {
411
452
  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
- },
453
+ headers: this.buildHeaders(auth, roomId),
420
454
  body: JSON.stringify({ messages }),
421
455
  });
422
456
 
@@ -455,9 +489,9 @@ export class MemoryEngineService {
455
489
  );
456
490
  return undefined;
457
491
  }
458
- if (!auth.oracleToken || !auth.userToken) {
492
+ if (!auth.ucanInvocation && (!auth.oracleToken || !auth.userToken)) {
459
493
  Logger.warn(
460
- `[MemoryEngineService] Missing oracle or user token, skipping query "${request.query}"`,
494
+ `[MemoryEngineService] Missing auth (no UCAN and no Matrix tokens), skipping query "${request.query}"`,
461
495
  );
462
496
  return undefined;
463
497
  }
@@ -465,14 +499,7 @@ export class MemoryEngineService {
465
499
  try {
466
500
  const response = await fetch(`${this.memoryEngineUrl}/search-enhanced`, {
467
501
  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
- },
502
+ headers: this.buildHeaders(auth, roomId),
476
503
  body: JSON.stringify(request),
477
504
  });
478
505
 
@@ -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 {
@@ -326,19 +326,32 @@ ___________________________________________________________
326
326
  const limit = listSessionsDto.limit ?? 20;
327
327
  const offset = listSessionsDto.offset ?? 0;
328
328
 
329
- // Get paginated sessions with total count
330
- const rows = db
331
- .prepare(
332
- `SELECT
329
+ // Get paginated sessions with total count, optionally filtered by roomId
330
+ const hasRoomFilter = !!listSessionsDto.roomId;
331
+ const sql = hasRoomFilter
332
+ ? `SELECT
333
333
  session_id, title, last_updated_at, created_at, oracle_name,
334
334
  oracle_did, oracle_entity_did, last_processed_count,
335
335
  user_context, room_id, slack_thread_ts,
336
336
  COUNT(*) OVER() as total
337
337
  FROM sessions
338
+ WHERE room_id = ?
338
339
  ORDER BY last_updated_at DESC
339
- LIMIT ? OFFSET ?`,
340
- )
341
- .all(limit, offset) as Array<{
340
+ LIMIT ? OFFSET ?`
341
+ : `SELECT
342
+ session_id, title, last_updated_at, created_at, oracle_name,
343
+ oracle_did, oracle_entity_did, last_processed_count,
344
+ user_context, room_id, slack_thread_ts,
345
+ COUNT(*) OVER() as total
346
+ FROM sessions
347
+ ORDER BY last_updated_at DESC
348
+ LIMIT ? OFFSET ?`;
349
+
350
+ const params = hasRoomFilter
351
+ ? [listSessionsDto.roomId, limit, offset]
352
+ : [limit, offset];
353
+
354
+ const rows = db.prepare(sql).all(...params) as Array<{
342
355
  session_id: string;
343
356
  title: string | null;
344
357
  last_updated_at: string;
@@ -380,11 +393,18 @@ ___________________________________________________________
380
393
  const userHomeServer =
381
394
  createSessionDto.homeServer ||
382
395
  (await getMatrixHomeServerCroppedForDid(createSessionDto.did));
383
- const { roomId } = await this.matrixManger.getOracleRoomIdWithHomeServer({
384
- userDid: createSessionDto.did,
385
- oracleEntityDid: createSessionDto.oracleEntityDid,
386
- userHomeServer,
387
- });
396
+
397
+ // Use the provided roomId override (e.g. task-specific room),
398
+ // or fall back to resolving the user's main oracle room.
399
+ let roomId = createSessionDto.roomId;
400
+ if (!roomId) {
401
+ const resolved = await this.matrixManger.getOracleRoomIdWithHomeServer({
402
+ userDid: createSessionDto.did,
403
+ oracleEntityDid: createSessionDto.oracleEntityDid,
404
+ userHomeServer,
405
+ });
406
+ roomId = resolved.roomId;
407
+ }
388
408
 
389
409
  if (!roomId) {
390
410
  throw new Error('Room ID not found');
@@ -401,18 +421,19 @@ ___________________________________________________________
401
421
  let userContext: UserContextData | undefined;
402
422
  if (
403
423
  this.memoryEngineService &&
404
- createSessionDto.oracleToken &&
405
- createSessionDto.userToken
424
+ (createSessionDto.ucanInvocation ||
425
+ (createSessionDto.oracleToken && createSessionDto.userToken))
406
426
  ) {
407
427
  try {
408
428
  Logger.debug('Gathering user context from Memory Engine');
409
429
  userContext = await this.memoryEngineService.gatherUserContext({
410
430
  oracleDid: createSessionDto.oracleDid,
411
431
  roomId,
412
- oracleToken: createSessionDto.oracleToken,
413
- userToken: createSessionDto.userToken,
432
+ oracleToken: createSessionDto.oracleToken ?? '',
433
+ userToken: createSessionDto.userToken ?? '',
414
434
  oracleHomeServer: createSessionDto.oracleHomeServer ?? '',
415
435
  userHomeServer: createSessionDto.userHomeServer ?? '',
436
+ ucanInvocation: createSessionDto.ucanInvocation,
416
437
  });
417
438
  } catch (error) {
418
439
  Logger.error('Failed to gather user context:', error);
@@ -34,25 +34,32 @@ export interface GetUserSubscriptionParams {
34
34
  network: 'mainnet' | 'testnet' | 'devnet';
35
35
  bearerToken: string;
36
36
  subscriptionUrl?: string;
37
+ /** When set to 'ucan', includes X-Auth-Type header for UCAN invocation auth */
38
+ authType?: 'ucan';
37
39
  }
38
40
 
39
41
  export const getUserSubscription = async ({
40
42
  bearerToken,
41
43
  network,
42
44
  subscriptionUrl: _subscriptionUrl,
45
+ authType,
43
46
  }: GetUserSubscriptionParams): Promise<GetMySubscriptionsResponseDto | null> => {
44
47
  const subscriptionUrl =
45
48
  _subscriptionUrl ?? getSubscriptionUrlByNetwork(network);
46
49
  try {
47
50
  Logger.debug('Fetching user subscription from:', subscriptionUrl);
51
+ const headers: Record<string, string> = {
52
+ Authorization: `Bearer ${bearerToken}`,
53
+ 'Content-Type': 'application/json',
54
+ };
55
+ if (authType === 'ucan') {
56
+ headers['X-Auth-Type'] = 'ucan';
57
+ }
48
58
  const response = await fetch(
49
59
  `${subscriptionUrl.endsWith('/') ? subscriptionUrl.slice(0, -1) : subscriptionUrl}/api/v1/subscriptions`,
50
60
  {
51
61
  method: 'GET',
52
- headers: {
53
- Authorization: `Bearer ${bearerToken}`,
54
- 'Content-Type': 'application/json',
55
- },
62
+ headers,
56
63
  },
57
64
  );
58
65