@open-mercato/ai-assistant 0.6.3-develop.3894.1.352abf4240 → 0.6.3

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 (80) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js +87 -0
  3. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js.map +7 -0
  4. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js +119 -0
  5. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js.map +7 -0
  6. package/dist/modules/ai_assistant/acl.js +1 -0
  7. package/dist/modules/ai_assistant/acl.js.map +2 -2
  8. package/dist/modules/ai_assistant/api/ai/chat/route.js +3 -0
  9. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
  10. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js +128 -0
  11. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js.map +7 -0
  12. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js +271 -0
  13. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js.map +7 -0
  14. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js +9 -1
  15. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js.map +2 -2
  16. package/dist/modules/ai_assistant/api/ai/conversations/route.js +4 -1
  17. package/dist/modules/ai_assistant/api/ai/conversations/route.js.map +2 -2
  18. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +5 -1
  19. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
  20. package/dist/modules/ai_assistant/components/ConversationShareButton.js +5 -0
  21. package/dist/modules/ai_assistant/components/ConversationShareButton.js.map +7 -0
  22. package/dist/modules/ai_assistant/components/ConversationShareDialog.js +5 -0
  23. package/dist/modules/ai_assistant/components/ConversationShareDialog.js.map +7 -0
  24. package/dist/modules/ai_assistant/data/entities.js +3 -0
  25. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  26. package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js +235 -5
  27. package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js.map +2 -2
  28. package/dist/modules/ai_assistant/events.js +14 -0
  29. package/dist/modules/ai_assistant/events.js.map +2 -2
  30. package/dist/modules/ai_assistant/i18n/de.json +17 -0
  31. package/dist/modules/ai_assistant/i18n/en.json +17 -0
  32. package/dist/modules/ai_assistant/i18n/es.json +17 -0
  33. package/dist/modules/ai_assistant/i18n/pl.json +17 -0
  34. package/dist/modules/ai_assistant/lib/conversation-storage.js +12 -3
  35. package/dist/modules/ai_assistant/lib/conversation-storage.js.map +2 -2
  36. package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js +15 -0
  37. package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js.map +7 -0
  38. package/dist/modules/ai_assistant/notifications.client.js +30 -0
  39. package/dist/modules/ai_assistant/notifications.client.js.map +7 -0
  40. package/dist/modules/ai_assistant/notifications.js +27 -0
  41. package/dist/modules/ai_assistant/notifications.js.map +7 -0
  42. package/dist/modules/ai_assistant/setup.js +2 -1
  43. package/dist/modules/ai_assistant/setup.js.map +2 -2
  44. package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js +59 -0
  45. package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js.map +7 -0
  46. package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js +123 -0
  47. package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js.map +7 -0
  48. package/generated/entities/ai_chat_conversation_participant/index.ts +1 -0
  49. package/generated/entity-fields-registry.ts +1 -0
  50. package/package.json +7 -8
  51. package/src/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.ts +117 -0
  52. package/src/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.ts +159 -0
  53. package/src/modules/ai_assistant/__tests__/integration/ai-chat-sharing.test.ts +406 -0
  54. package/src/modules/ai_assistant/acl.ts +1 -0
  55. package/src/modules/ai_assistant/api/ai/chat/route.ts +3 -0
  56. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.ts +149 -0
  57. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.ts +314 -0
  58. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/route.ts +9 -1
  59. package/src/modules/ai_assistant/api/ai/conversations/route.ts +4 -1
  60. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +4 -0
  61. package/src/modules/ai_assistant/components/ConversationShareButton.tsx +1 -0
  62. package/src/modules/ai_assistant/components/ConversationShareDialog.tsx +1 -0
  63. package/src/modules/ai_assistant/data/entities.ts +4 -0
  64. package/src/modules/ai_assistant/data/repositories/AiChatConversationRepository.ts +270 -7
  65. package/src/modules/ai_assistant/data/repositories/__tests__/AiChatConversationRepository.test.ts +297 -3
  66. package/src/modules/ai_assistant/events.ts +31 -0
  67. package/src/modules/ai_assistant/i18n/__tests__/conversation-share-translations.test.ts +59 -0
  68. package/src/modules/ai_assistant/i18n/de.json +17 -0
  69. package/src/modules/ai_assistant/i18n/en.json +17 -0
  70. package/src/modules/ai_assistant/i18n/es.json +17 -0
  71. package/src/modules/ai_assistant/i18n/pl.json +17 -0
  72. package/src/modules/ai_assistant/lib/conversation-storage.ts +22 -1
  73. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +25 -0
  74. package/src/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.ts +15 -0
  75. package/src/modules/ai_assistant/notifications.client.ts +29 -0
  76. package/src/modules/ai_assistant/notifications.ts +25 -0
  77. package/src/modules/ai_assistant/setup.ts +2 -1
  78. package/src/modules/ai_assistant/subscribers/__tests__/conversation-shared-notify.test.ts +116 -0
  79. package/src/modules/ai_assistant/subscribers/conversation-shared-notify.ts +78 -0
  80. package/src/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.tsx +121 -0
@@ -216,9 +216,11 @@
216
216
  "ai_assistant.chat.mutation_cards.result.successWithCommand": "Completado",
217
217
  "ai_assistant.chat.mutation_cards.result.viewRecord": "Ver registro",
218
218
  "ai_assistant.chat.newConversation": "Start new conversation",
219
+ "ai_assistant.chat.ownerRoleLabel": "Owner",
219
220
  "ai_assistant.chat.pending_phase3.body": "Esta tarjeta interactiva se integrará en la Fase 3 del marco unificado de IA.",
220
221
  "ai_assistant.chat.pending_phase3.title": "Tarjeta de aprobación de mutación pendiente",
221
222
  "ai_assistant.chat.placeholder": "Pregunte lo que necesite...",
223
+ "ai_assistant.chat.readOnlyNotice": "Esta es una conversación compartida. Puede leerla, pero no puede responder.",
222
224
  "ai_assistant.chat.reasoning": "Reasoning",
223
225
  "ai_assistant.chat.records.fields.amount": "Importe",
224
226
  "ai_assistant.chat.records.fields.category": "Categoría",
@@ -282,6 +284,7 @@
282
284
  "ai_assistant.dock.left": "Anclar a la izquierda",
283
285
  "ai_assistant.dock.minimize": "Minimizar",
284
286
  "ai_assistant.dock.right": "Anclar a la derecha",
287
+ "ai_assistant.launcher.composerPlaceholder": "Pregunte lo que necesite…",
285
288
  "ai_assistant.launcher.dialogTitle": "AI assistants",
286
289
  "ai_assistant.launcher.dock.subtitle": "AI assistant",
287
290
  "ai_assistant.launcher.empty": "No assistants match your search.",
@@ -323,6 +326,9 @@
323
326
  "ai_assistant.modelPicker.triggerAriaLabel": "Selector de modelo de IA",
324
327
  "ai_assistant.modelPicker.useDefault": "Usar predeterminado",
325
328
  "ai_assistant.modelPicker.useDefaultWithModel": "Usar predeterminado del agente: {{model}}",
329
+ "ai_assistant.notifications.conversation_shared.body": "Se ha compartido una conversación de IA con usted.",
330
+ "ai_assistant.notifications.conversation_shared.title": "Conversación compartida con usted",
331
+ "ai_assistant.notifications.conversation_shared.view_button": "Ver conversación",
326
332
  "ai_assistant.playground.agentPickerLabel": "Agente",
327
333
  "ai_assistant.playground.chat.notSupportedBody": "Elija un agente cuyo modo de ejecución sea \"chat\" o cambie a la pestaña de modo objeto.",
328
334
  "ai_assistant.playground.chat.notSupportedTitle": "El modo chat no está disponible para este agente.",
@@ -428,6 +434,17 @@
428
434
  "ai_assistant.settings.visibilityEnabled": "Habilitado",
429
435
  "ai_assistant.settings.visibilityTitle": "Visibilidad del asistente de IA",
430
436
  "ai_assistant.settings.visibilityToggleLabel": "Mostrar el asistente de IA a los usuarios",
437
+ "ai_assistant.share.addParticipant": "Añadir participante",
438
+ "ai_assistant.share.allUsersAdded": "Todos los usuarios ya añadidos",
439
+ "ai_assistant.share.dialogDescription": "Comparta esta conversación con otros usuarios. Obtendrán acceso de solo lectura.",
440
+ "ai_assistant.share.dialogTitle": "Compartir conversación",
441
+ "ai_assistant.share.noParticipants": "Aún no hay participantes. Añada a alguien para compartir esta conversación.",
442
+ "ai_assistant.share.participantPlaceholder": "Buscar usuario...",
443
+ "ai_assistant.share.removeParticipant": "Quitar",
444
+ "ai_assistant.share.saved": "Guardado",
445
+ "ai_assistant.share.saving": "Guardando...",
446
+ "ai_assistant.share.selectUser": "Seleccione un usuario...",
447
+ "ai_assistant.share.shareButton": "Compartir",
431
448
  "ai_assistant.status.analyzing": "Analizando solicitud...",
432
449
  "ai_assistant.status.executing": "Ejecutando herramientas...",
433
450
  "ai_assistant.status.responding": "Respondiendo...",
@@ -216,9 +216,11 @@
216
216
  "ai_assistant.chat.mutation_cards.result.successWithCommand": "Zakończono",
217
217
  "ai_assistant.chat.mutation_cards.result.viewRecord": "Pokaż rekord",
218
218
  "ai_assistant.chat.newConversation": "Start new conversation",
219
+ "ai_assistant.chat.ownerRoleLabel": "Owner",
219
220
  "ai_assistant.chat.pending_phase3.body": "Ta interaktywna karta pojawi się w Fazie 3 zunifikowanego frameworku AI.",
220
221
  "ai_assistant.chat.pending_phase3.title": "Karta zatwierdzania mutacji oczekuje",
221
222
  "ai_assistant.chat.placeholder": "Zapytaj mnie o cokolwiek...",
223
+ "ai_assistant.chat.readOnlyNotice": "To jest udostępniona konwersacja. Możesz ją czytać, ale nie możesz odpowiadać.",
222
224
  "ai_assistant.chat.reasoning": "Reasoning",
223
225
  "ai_assistant.chat.records.fields.amount": "Kwota",
224
226
  "ai_assistant.chat.records.fields.category": "Kategoria",
@@ -282,6 +284,7 @@
282
284
  "ai_assistant.dock.left": "Dokuj po lewej",
283
285
  "ai_assistant.dock.minimize": "Minimalizuj",
284
286
  "ai_assistant.dock.right": "Dokuj po prawej",
287
+ "ai_assistant.launcher.composerPlaceholder": "Zapytaj o cokolwiek…",
285
288
  "ai_assistant.launcher.dialogTitle": "AI assistants",
286
289
  "ai_assistant.launcher.dock.subtitle": "AI assistant",
287
290
  "ai_assistant.launcher.empty": "No assistants match your search.",
@@ -323,6 +326,9 @@
323
326
  "ai_assistant.modelPicker.triggerAriaLabel": "Wybierz model AI",
324
327
  "ai_assistant.modelPicker.useDefault": "Użyj domyślnego agenta",
325
328
  "ai_assistant.modelPicker.useDefaultWithModel": "Użyj domyślnego agenta: {{model}}",
329
+ "ai_assistant.notifications.conversation_shared.body": "Udostępniono Ci konwersację z asystentem AI.",
330
+ "ai_assistant.notifications.conversation_shared.title": "Udostępniono Ci konwersację",
331
+ "ai_assistant.notifications.conversation_shared.view_button": "Otwórz konwersację",
326
332
  "ai_assistant.playground.agentPickerLabel": "Agent",
327
333
  "ai_assistant.playground.chat.notSupportedBody": "Wybierz agenta, którego tryb wykonywania to \"chat\", lub przełącz się na zakładkę trybu obiektowego.",
328
334
  "ai_assistant.playground.chat.notSupportedTitle": "Tryb czatu nie jest dostępny dla tego agenta.",
@@ -428,6 +434,17 @@
428
434
  "ai_assistant.settings.visibilityEnabled": "Widoczny w nagłówku ze skrótem Cmd+J.",
429
435
  "ai_assistant.settings.visibilityTitle": "Asystent AI",
430
436
  "ai_assistant.settings.visibilityToggleLabel": "Widoczność",
437
+ "ai_assistant.share.addParticipant": "Dodaj uczestnika",
438
+ "ai_assistant.share.allUsersAdded": "Dodano już wszystkich użytkowników",
439
+ "ai_assistant.share.dialogDescription": "Udostępnij tę konwersację innym użytkownikom. Otrzymają dostęp tylko do odczytu.",
440
+ "ai_assistant.share.dialogTitle": "Udostępnij konwersację",
441
+ "ai_assistant.share.noParticipants": "Brak uczestników. Dodaj kogoś, aby udostępnić tę konwersację.",
442
+ "ai_assistant.share.participantPlaceholder": "Szukaj użytkownika...",
443
+ "ai_assistant.share.removeParticipant": "Usuń",
444
+ "ai_assistant.share.saved": "Zapisano",
445
+ "ai_assistant.share.saving": "Zapisywanie...",
446
+ "ai_assistant.share.selectUser": "Wybierz użytkownika...",
447
+ "ai_assistant.share.shareButton": "Udostępnij",
431
448
  "ai_assistant.status.analyzing": "Analizowanie żądania...",
432
449
  "ai_assistant.status.executing": "Wykonywanie narzędzi...",
433
450
  "ai_assistant.status.responding": "Odpowiadanie...",
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  AiChatConversationAccessError,
3
+ AiChatConversationDuplicateParticipantError,
4
+ AiChatConversationOrgNotFoundError,
5
+ AiChatParticipantNotFoundError,
3
6
  AiChatConversationRepository
4
7
  } from "../data/repositories/AiChatConversationRepository.js";
5
8
  function createConversationStorage(container) {
6
9
  const em = container.resolve("em");
7
10
  return new AiChatConversationRepository(em);
8
11
  }
9
- function serializeAiChatConversation(row) {
12
+ function serializeAiChatConversation(row, enrich = {}) {
10
13
  return {
11
14
  conversationId: row.conversationId,
12
15
  agentId: row.agentId,
@@ -17,7 +20,9 @@ function serializeAiChatConversation(row) {
17
20
  createdAt: row.createdAt.toISOString(),
18
21
  updatedAt: row.updatedAt.toISOString(),
19
22
  lastMessageAt: row.lastMessageAt ? row.lastMessageAt.toISOString() : null,
20
- importedFromLocalAt: row.importedFromLocalAt ? row.importedFromLocalAt.toISOString() : null
23
+ importedFromLocalAt: row.importedFromLocalAt ? row.importedFromLocalAt.toISOString() : null,
24
+ participantCount: enrich.participantCount ?? 0,
25
+ isOwner: enrich.callerUserId != null ? row.ownerUserId === enrich.callerUserId : null
21
26
  };
22
27
  }
23
28
  function serializeAiChatMessage(row) {
@@ -31,11 +36,15 @@ function serializeAiChatMessage(row) {
31
36
  files: Array.isArray(row.filesMetadata) ? row.filesMetadata : [],
32
37
  model: row.model ?? null,
33
38
  metadata: row.metadata ?? null,
34
- createdAt: row.createdAt.toISOString()
39
+ createdAt: row.createdAt.toISOString(),
40
+ senderUserId: row.createdByUserId ?? null
35
41
  };
36
42
  }
37
43
  export {
38
44
  AiChatConversationAccessError,
45
+ AiChatConversationDuplicateParticipantError,
46
+ AiChatConversationOrgNotFoundError,
47
+ AiChatParticipantNotFoundError,
39
48
  createConversationStorage,
40
49
  serializeAiChatConversation,
41
50
  serializeAiChatMessage
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/conversation-storage.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport {\n AiChatConversation,\n AiChatMessage,\n} from '../data/entities'\nimport {\n AiChatConversationAccessError,\n AiChatConversationRepository,\n type AiChatConversationContext,\n} from '../data/repositories/AiChatConversationRepository'\n\n/**\n * Thin service-layer wrapper that resolves the entity manager from the\n * Awilix container and exposes a typed API on top of\n * `AiChatConversationRepository`. The REST routes for the conversation APIs\n * call into this surface; the future chat dispatcher write path will reuse\n * the same helpers so persistence stays consistent across entry points.\n *\n * Spec: `2026-05-05-ai-chat-server-side-conversation-storage` \u00A7\"Commands\".\n *\n * Re-exports the access error so route handlers can map it to a 404 without\n * importing the repository directly.\n */\nexport { AiChatConversationAccessError }\nexport type { AiChatConversationContext }\n\nexport function createConversationStorage(\n container: AwilixContainer,\n): AiChatConversationRepository {\n const em = container.resolve<EntityManager>('em')\n return new AiChatConversationRepository(em)\n}\n\nexport interface SerializedAiChatConversation {\n conversationId: string\n agentId: string\n title: string | null\n status: 'open' | 'closed'\n visibility: 'private' | 'shared' | 'organization'\n pageContext: Record<string, unknown> | null\n createdAt: string\n updatedAt: string\n lastMessageAt: string | null\n importedFromLocalAt: string | null\n}\n\nexport interface SerializedAiChatMessage {\n id: string\n clientMessageId: string | null\n role: 'user' | 'assistant' | 'system'\n content: string\n uiParts: unknown[]\n attachmentIds: string[]\n files: Array<Record<string, unknown>>\n model: string | null\n metadata: Record<string, unknown> | null\n createdAt: string\n}\n\nexport function serializeAiChatConversation(\n row: AiChatConversation,\n): SerializedAiChatConversation {\n return {\n conversationId: row.conversationId,\n agentId: row.agentId,\n title: row.title ?? null,\n status: row.status,\n visibility: row.visibility,\n pageContext: row.pageContext ?? null,\n createdAt: row.createdAt.toISOString(),\n updatedAt: row.updatedAt.toISOString(),\n lastMessageAt: row.lastMessageAt ? row.lastMessageAt.toISOString() : null,\n importedFromLocalAt: row.importedFromLocalAt\n ? row.importedFromLocalAt.toISOString()\n : null,\n }\n}\n\nexport function serializeAiChatMessage(row: AiChatMessage): SerializedAiChatMessage {\n return {\n id: row.id,\n clientMessageId: row.clientMessageId ?? null,\n role: row.role,\n content: row.content,\n uiParts: Array.isArray(row.uiParts) ? row.uiParts : [],\n attachmentIds: Array.isArray(row.attachmentIds) ? row.attachmentIds : [],\n files: Array.isArray(row.filesMetadata) ? row.filesMetadata : [],\n model: row.model ?? null,\n metadata: row.metadata ?? null,\n createdAt: row.createdAt.toISOString(),\n }\n}\n"],
5
- "mappings": "AAMA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAiBA,SAAS,0BACd,WAC8B;AAC9B,QAAM,KAAK,UAAU,QAAuB,IAAI;AAChD,SAAO,IAAI,6BAA6B,EAAE;AAC5C;AA4BO,SAAS,4BACd,KAC8B;AAC9B,SAAO;AAAA,IACL,gBAAgB,IAAI;AAAA,IACpB,SAAS,IAAI;AAAA,IACb,OAAO,IAAI,SAAS;AAAA,IACpB,QAAQ,IAAI;AAAA,IACZ,YAAY,IAAI;AAAA,IAChB,aAAa,IAAI,eAAe;AAAA,IAChC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,eAAe,IAAI,gBAAgB,IAAI,cAAc,YAAY,IAAI;AAAA,IACrE,qBAAqB,IAAI,sBACrB,IAAI,oBAAoB,YAAY,IACpC;AAAA,EACN;AACF;AAEO,SAAS,uBAAuB,KAA6C;AAClF,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,iBAAiB,IAAI,mBAAmB;AAAA,IACxC,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,IACb,SAAS,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,CAAC;AAAA,IACrD,eAAe,MAAM,QAAQ,IAAI,aAAa,IAAI,IAAI,gBAAgB,CAAC;AAAA,IACvE,OAAO,MAAM,QAAQ,IAAI,aAAa,IAAI,IAAI,gBAAgB,CAAC;AAAA,IAC/D,OAAO,IAAI,SAAS;AAAA,IACpB,UAAU,IAAI,YAAY;AAAA,IAC1B,WAAW,IAAI,UAAU,YAAY;AAAA,EACvC;AACF;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport {\n AiChatConversation,\n AiChatMessage,\n} from '../data/entities'\nimport {\n AiChatConversationAccessError,\n AiChatConversationDuplicateParticipantError,\n AiChatConversationOrgNotFoundError,\n AiChatParticipantNotFoundError,\n AiChatConversationRepository,\n type AiChatConversationContext,\n} from '../data/repositories/AiChatConversationRepository'\n\n/**\n * Thin service-layer wrapper that resolves the entity manager from the\n * Awilix container and exposes a typed API on top of\n * `AiChatConversationRepository`. The REST routes for the conversation APIs\n * call into this surface; the future chat dispatcher write path will reuse\n * the same helpers so persistence stays consistent across entry points.\n *\n * Spec: `2026-05-05-ai-chat-server-side-conversation-storage` \u00A7\"Commands\".\n *\n * Re-exports the access error so route handlers can map it to a 404 without\n * importing the repository directly.\n */\nexport {\n AiChatConversationAccessError,\n AiChatConversationDuplicateParticipantError,\n AiChatConversationOrgNotFoundError,\n AiChatParticipantNotFoundError,\n}\nexport type { AiChatConversationContext }\n\nexport function createConversationStorage(\n container: AwilixContainer,\n): AiChatConversationRepository {\n const em = container.resolve<EntityManager>('em')\n return new AiChatConversationRepository(em)\n}\n\nexport interface SerializedAiChatConversation {\n conversationId: string\n agentId: string\n title: string | null\n status: 'open' | 'closed'\n visibility: 'private' | 'shared' | 'organization'\n pageContext: Record<string, unknown> | null\n createdAt: string\n updatedAt: string\n lastMessageAt: string | null\n importedFromLocalAt: string | null\n participantCount: number\n isOwner: boolean | null\n}\n\nexport interface AiChatConversationSerializeEnrich {\n callerUserId?: string | null\n participantCount?: number\n}\n\nexport interface SerializedAiChatMessage {\n id: string\n clientMessageId: string | null\n role: 'user' | 'assistant' | 'system'\n content: string\n uiParts: unknown[]\n attachmentIds: string[]\n files: Array<Record<string, unknown>>\n model: string | null\n metadata: Record<string, unknown> | null\n createdAt: string\n senderUserId: string | null\n}\n\nexport function serializeAiChatConversation(\n row: AiChatConversation,\n enrich: AiChatConversationSerializeEnrich = {},\n): SerializedAiChatConversation {\n return {\n conversationId: row.conversationId,\n agentId: row.agentId,\n title: row.title ?? null,\n status: row.status,\n visibility: row.visibility,\n pageContext: row.pageContext ?? null,\n createdAt: row.createdAt.toISOString(),\n updatedAt: row.updatedAt.toISOString(),\n lastMessageAt: row.lastMessageAt ? row.lastMessageAt.toISOString() : null,\n importedFromLocalAt: row.importedFromLocalAt\n ? row.importedFromLocalAt.toISOString()\n : null,\n participantCount: enrich.participantCount ?? 0,\n isOwner:\n enrich.callerUserId != null ? row.ownerUserId === enrich.callerUserId : null,\n }\n}\n\nexport function serializeAiChatMessage(row: AiChatMessage): SerializedAiChatMessage {\n return {\n id: row.id,\n clientMessageId: row.clientMessageId ?? null,\n role: row.role,\n content: row.content,\n uiParts: Array.isArray(row.uiParts) ? row.uiParts : [],\n attachmentIds: Array.isArray(row.attachmentIds) ? row.attachmentIds : [],\n files: Array.isArray(row.filesMetadata) ? row.filesMetadata : [],\n model: row.model ?? null,\n metadata: row.metadata ?? null,\n createdAt: row.createdAt.toISOString(),\n senderUserId: row.createdByUserId ?? null,\n }\n}\n"],
5
+ "mappings": "AAMA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAsBA,SAAS,0BACd,WAC8B;AAC9B,QAAM,KAAK,UAAU,QAAuB,IAAI;AAChD,SAAO,IAAI,6BAA6B,EAAE;AAC5C;AAoCO,SAAS,4BACd,KACA,SAA4C,CAAC,GACf;AAC9B,SAAO;AAAA,IACL,gBAAgB,IAAI;AAAA,IACpB,SAAS,IAAI;AAAA,IACb,OAAO,IAAI,SAAS;AAAA,IACpB,QAAQ,IAAI;AAAA,IACZ,YAAY,IAAI;AAAA,IAChB,aAAa,IAAI,eAAe;AAAA,IAChC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,eAAe,IAAI,gBAAgB,IAAI,cAAc,YAAY,IAAI;AAAA,IACrE,qBAAqB,IAAI,sBACrB,IAAI,oBAAoB,YAAY,IACpC;AAAA,IACJ,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,SACE,OAAO,gBAAgB,OAAO,IAAI,gBAAgB,OAAO,eAAe;AAAA,EAC5E;AACF;AAEO,SAAS,uBAAuB,KAA6C;AAClF,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,iBAAiB,IAAI,mBAAmB;AAAA,IACxC,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,IACb,SAAS,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,CAAC;AAAA,IACrD,eAAe,MAAM,QAAQ,IAAI,aAAa,IAAI,IAAI,gBAAgB,CAAC;AAAA,IACvE,OAAO,MAAM,QAAQ,IAAI,aAAa,IAAI,IAAI,gBAAgB,CAAC;AAAA,IAC/D,OAAO,IAAI,SAAS;AAAA,IACpB,UAAU,IAAI,YAAY;AAAA,IAC1B,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,cAAc,IAAI,mBAAmB;AAAA,EACvC;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,15 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260522120000_ai_assistant extends Migration {
3
+ async up() {
4
+ this.addSql(`alter table "ai_chat_conversation_participants" add column "deleted_at" timestamptz null;`);
5
+ this.addSql(`create index "ai_chat_conv_participants_active_conv_user_idx" on "ai_chat_conversation_participants" ("tenant_id", "organization_id", "conversation_id", "user_id") where "deleted_at" is null;`);
6
+ }
7
+ async down() {
8
+ this.addSql(`drop index if exists "ai_chat_conv_participants_active_conv_user_idx";`);
9
+ this.addSql(`alter table "ai_chat_conversation_participants" drop column if exists "deleted_at";`);
10
+ }
11
+ }
12
+ export {
13
+ Migration20260522120000_ai_assistant
14
+ };
15
+ //# sourceMappingURL=Migration20260522120000_ai_assistant.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260522120000_ai_assistant extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`alter table \"ai_chat_conversation_participants\" add column \"deleted_at\" timestamptz null;`)\n this.addSql(`create index \"ai_chat_conv_participants_active_conv_user_idx\" on \"ai_chat_conversation_participants\" (\"tenant_id\", \"organization_id\", \"conversation_id\", \"user_id\") where \"deleted_at\" is null;`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop index if exists \"ai_chat_conv_participants_active_conv_user_idx\";`)\n this.addSql(`alter table \"ai_chat_conversation_participants\" drop column if exists \"deleted_at\";`)\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,6CAA6C,UAAU;AAAA,EAElE,MAAe,KAAoB;AACjC,SAAK,OAAO,2FAA2F;AACvG,SAAK,OAAO,iMAAiM;AAAA,EAC/M;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,wEAAwE;AACpF,SAAK,OAAO,qFAAqF;AAAA,EACnG;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { ConversationSharedRenderer } from "./widgets/notifications/ConversationSharedRenderer.js";
3
+ const aiAssistantNotificationTypes = [
4
+ {
5
+ type: "ai_assistant.conversation_shared",
6
+ module: "ai_assistant",
7
+ titleKey: "ai_assistant.notifications.conversation_shared.title",
8
+ bodyKey: "ai_assistant.notifications.conversation_shared.body",
9
+ icon: "share-2",
10
+ severity: "info",
11
+ actions: [
12
+ {
13
+ id: "view",
14
+ labelKey: "common.view",
15
+ variant: "outline",
16
+ href: "/backend?openAiConversation={sourceEntityId}",
17
+ icon: "external-link"
18
+ }
19
+ ],
20
+ linkHref: "/backend",
21
+ Renderer: ConversationSharedRenderer,
22
+ expiresAfterHours: 168
23
+ }
24
+ ];
25
+ var notifications_client_default = aiAssistantNotificationTypes;
26
+ export {
27
+ aiAssistantNotificationTypes,
28
+ notifications_client_default as default
29
+ };
30
+ //# sourceMappingURL=notifications.client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/ai_assistant/notifications.client.ts"],
4
+ "sourcesContent": ["'use client'\n\nimport type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\nimport { ConversationSharedRenderer } from './widgets/notifications/ConversationSharedRenderer'\n\nexport const aiAssistantNotificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'ai_assistant.conversation_shared',\n module: 'ai_assistant',\n titleKey: 'ai_assistant.notifications.conversation_shared.title',\n bodyKey: 'ai_assistant.notifications.conversation_shared.body',\n icon: 'share-2',\n severity: 'info',\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend?openAiConversation={sourceEntityId}',\n icon: 'external-link',\n },\n ],\n linkHref: '/backend',\n Renderer: ConversationSharedRenderer,\n expiresAfterHours: 168,\n },\n]\n\nexport default aiAssistantNotificationTypes\n"],
5
+ "mappings": ";AAGA,SAAS,kCAAkC;AAEpC,MAAM,+BAA6D;AAAA,EACxE;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AACF;AAEA,IAAO,+BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,27 @@
1
+ const notificationTypes = [
2
+ {
3
+ type: "ai_assistant.conversation_shared",
4
+ module: "ai_assistant",
5
+ titleKey: "ai_assistant.notifications.conversation_shared.title",
6
+ bodyKey: "ai_assistant.notifications.conversation_shared.body",
7
+ icon: "share-2",
8
+ severity: "info",
9
+ actions: [
10
+ {
11
+ id: "view",
12
+ labelKey: "common.view",
13
+ variant: "outline",
14
+ href: "/backend?openAiConversation={sourceEntityId}",
15
+ icon: "external-link"
16
+ }
17
+ ],
18
+ linkHref: "/backend",
19
+ expiresAfterHours: 168
20
+ }
21
+ ];
22
+ var notifications_default = notificationTypes;
23
+ export {
24
+ notifications_default as default,
25
+ notificationTypes
26
+ };
27
+ //# sourceMappingURL=notifications.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/ai_assistant/notifications.ts"],
4
+ "sourcesContent": ["import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\n\nexport const notificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'ai_assistant.conversation_shared',\n module: 'ai_assistant',\n titleKey: 'ai_assistant.notifications.conversation_shared.title',\n bodyKey: 'ai_assistant.notifications.conversation_shared.body',\n icon: 'share-2',\n severity: 'info',\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend?openAiConversation={sourceEntityId}',\n icon: 'external-link',\n },\n ],\n linkHref: '/backend',\n expiresAfterHours: 168,\n },\n]\n\nexport default notificationTypes\n"],
5
+ "mappings": "AAEO,MAAM,oBAAkD;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AACF;AAEA,IAAO,wBAAQ;",
6
+ "names": []
7
+ }
@@ -70,12 +70,13 @@ const setup = {
70
70
  "ai_assistant.view",
71
71
  "ai_assistant.settings.manage",
72
72
  "ai_assistant.conversations.manage",
73
+ "ai_assistant.conversations.share",
73
74
  "ai_assistant.mcp.serve",
74
75
  "ai_assistant.tools.list",
75
76
  "ai_assistant.mcp_servers.view",
76
77
  "ai_assistant.mcp_servers.manage"
77
78
  ],
78
- employee: ["ai_assistant.view"]
79
+ employee: ["ai_assistant.view", "ai_assistant.conversations.share"]
79
80
  },
80
81
  async seedDefaults({ container }) {
81
82
  await ensurePendingActionCleanupSchedule(container);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/ai_assistant/setup.ts"],
4
- "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nconst PENDING_ACTION_CLEANUP_SCHEDULE_ID = 'ai_assistant:pending-action-cleanup'\nconst TOKEN_USAGE_PRUNE_SCHEDULE_ID = 'ai_assistant:token-usage-prune'\n\n/**\n * System-scoped recurring schedule: every 5 minutes, enqueue a job to the\n * `ai-pending-action-cleanup` queue so the worker can sweep rows whose TTL\n * elapsed without any confirm/cancel activity (Step 5.12). The schedule id\n * is stable and `scheduler.register()` is an upsert, so calling this from\n * every tenant bootstrap stays idempotent.\n */\nasync function ensurePendingActionCleanupSchedule(\n container: import('awilix').AwilixContainer | undefined,\n): Promise<void> {\n if (!container) return\n let schedulerService:\n | {\n register: (registration: Record<string, unknown>) => Promise<void>\n }\n | undefined\n try {\n schedulerService = container.resolve('schedulerService')\n } catch {\n schedulerService = undefined\n }\n if (!schedulerService) return\n try {\n await schedulerService.register({\n id: PENDING_ACTION_CLEANUP_SCHEDULE_ID,\n name: 'AI pending-action cleanup',\n description:\n 'Sweep pending AI mutation approvals whose TTL elapsed without confirm/cancel and flip them to expired.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: '5m',\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'ai-pending-action-cleanup',\n targetPayload: {},\n sourceType: 'module',\n sourceModule: 'ai_assistant',\n isEnabled: true,\n })\n } catch (error) {\n console.warn(\n '[ai_assistant] Failed to register pending-action cleanup schedule:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\n/**\n * System-scoped daily schedule: enqueue a job to the `ai-token-usage-prune`\n * queue to prune events older than the retention window and reconcile the\n * daily rollup session counts.\n *\n * Phase 6.4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nasync function ensureTokenUsagePruneSchedule(\n container: import('awilix').AwilixContainer | undefined,\n): Promise<void> {\n if (!container) return\n let schedulerService:\n | {\n register: (registration: Record<string, unknown>) => Promise<void>\n }\n | undefined\n try {\n schedulerService = container.resolve('schedulerService')\n } catch {\n schedulerService = undefined\n }\n if (!schedulerService) return\n try {\n await schedulerService.register({\n id: TOKEN_USAGE_PRUNE_SCHEDULE_ID,\n name: 'AI token-usage prune',\n description:\n 'Delete ai_token_usage_events rows older than AI_TOKEN_USAGE_EVENTS_RETENTION_DAYS (default 90) and reconcile session_count on the daily rollup.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: '24h',\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'ai-token-usage-prune',\n targetPayload: {},\n sourceType: 'module',\n sourceModule: 'ai_assistant',\n isEnabled: true,\n })\n } catch (error) {\n console.warn(\n '[ai_assistant] Failed to register token-usage prune schedule:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: [\n 'ai_assistant.view',\n 'ai_assistant.settings.manage',\n 'ai_assistant.conversations.manage',\n 'ai_assistant.mcp.serve',\n 'ai_assistant.tools.list',\n 'ai_assistant.mcp_servers.view',\n 'ai_assistant.mcp_servers.manage',\n ],\n employee: ['ai_assistant.view'],\n },\n\n async seedDefaults({ container }) {\n await ensurePendingActionCleanupSchedule(container)\n await ensureTokenUsagePruneSchedule(container)\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AAEA,MAAM,qCAAqC;AAC3C,MAAM,gCAAgC;AAStC,eAAe,mCACb,WACe;AACf,MAAI,CAAC,UAAW;AAChB,MAAI;AAKJ,MAAI;AACF,uBAAmB,UAAU,QAAQ,kBAAkB;AAAA,EACzD,QAAQ;AACN,uBAAmB;AAAA,EACrB;AACA,MAAI,CAAC,iBAAkB;AACvB,MAAI;AACF,UAAM,iBAAiB,SAAS;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AASA,eAAe,8BACb,WACe;AACf,MAAI,CAAC,UAAW;AAChB,MAAI;AAKJ,MAAI;AACF,uBAAmB,UAAU,QAAQ,kBAAkB;AAAA,EACzD,QAAQ;AACN,uBAAmB;AAAA,EACrB;AACA,MAAI,CAAC,iBAAkB;AACvB,MAAI;AACF,UAAM,iBAAiB,SAAS;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,mBAAmB;AAAA,EAChC;AAAA,EAEA,MAAM,aAAa,EAAE,UAAU,GAAG;AAChC,UAAM,mCAAmC,SAAS;AAClD,UAAM,8BAA8B,SAAS;AAAA,EAC/C;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nconst PENDING_ACTION_CLEANUP_SCHEDULE_ID = 'ai_assistant:pending-action-cleanup'\nconst TOKEN_USAGE_PRUNE_SCHEDULE_ID = 'ai_assistant:token-usage-prune'\n\n/**\n * System-scoped recurring schedule: every 5 minutes, enqueue a job to the\n * `ai-pending-action-cleanup` queue so the worker can sweep rows whose TTL\n * elapsed without any confirm/cancel activity (Step 5.12). The schedule id\n * is stable and `scheduler.register()` is an upsert, so calling this from\n * every tenant bootstrap stays idempotent.\n */\nasync function ensurePendingActionCleanupSchedule(\n container: import('awilix').AwilixContainer | undefined,\n): Promise<void> {\n if (!container) return\n let schedulerService:\n | {\n register: (registration: Record<string, unknown>) => Promise<void>\n }\n | undefined\n try {\n schedulerService = container.resolve('schedulerService')\n } catch {\n schedulerService = undefined\n }\n if (!schedulerService) return\n try {\n await schedulerService.register({\n id: PENDING_ACTION_CLEANUP_SCHEDULE_ID,\n name: 'AI pending-action cleanup',\n description:\n 'Sweep pending AI mutation approvals whose TTL elapsed without confirm/cancel and flip them to expired.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: '5m',\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'ai-pending-action-cleanup',\n targetPayload: {},\n sourceType: 'module',\n sourceModule: 'ai_assistant',\n isEnabled: true,\n })\n } catch (error) {\n console.warn(\n '[ai_assistant] Failed to register pending-action cleanup schedule:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\n/**\n * System-scoped daily schedule: enqueue a job to the `ai-token-usage-prune`\n * queue to prune events older than the retention window and reconcile the\n * daily rollup session counts.\n *\n * Phase 6.4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nasync function ensureTokenUsagePruneSchedule(\n container: import('awilix').AwilixContainer | undefined,\n): Promise<void> {\n if (!container) return\n let schedulerService:\n | {\n register: (registration: Record<string, unknown>) => Promise<void>\n }\n | undefined\n try {\n schedulerService = container.resolve('schedulerService')\n } catch {\n schedulerService = undefined\n }\n if (!schedulerService) return\n try {\n await schedulerService.register({\n id: TOKEN_USAGE_PRUNE_SCHEDULE_ID,\n name: 'AI token-usage prune',\n description:\n 'Delete ai_token_usage_events rows older than AI_TOKEN_USAGE_EVENTS_RETENTION_DAYS (default 90) and reconcile session_count on the daily rollup.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: '24h',\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'ai-token-usage-prune',\n targetPayload: {},\n sourceType: 'module',\n sourceModule: 'ai_assistant',\n isEnabled: true,\n })\n } catch (error) {\n console.warn(\n '[ai_assistant] Failed to register token-usage prune schedule:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: [\n 'ai_assistant.view',\n 'ai_assistant.settings.manage',\n 'ai_assistant.conversations.manage',\n 'ai_assistant.conversations.share',\n 'ai_assistant.mcp.serve',\n 'ai_assistant.tools.list',\n 'ai_assistant.mcp_servers.view',\n 'ai_assistant.mcp_servers.manage',\n ],\n employee: ['ai_assistant.view', 'ai_assistant.conversations.share'],\n },\n\n async seedDefaults({ container }) {\n await ensurePendingActionCleanupSchedule(container)\n await ensureTokenUsagePruneSchedule(container)\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AAEA,MAAM,qCAAqC;AAC3C,MAAM,gCAAgC;AAStC,eAAe,mCACb,WACe;AACf,MAAI,CAAC,UAAW;AAChB,MAAI;AAKJ,MAAI;AACF,uBAAmB,UAAU,QAAQ,kBAAkB;AAAA,EACzD,QAAQ;AACN,uBAAmB;AAAA,EACrB;AACA,MAAI,CAAC,iBAAkB;AACvB,MAAI;AACF,UAAM,iBAAiB,SAAS;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AASA,eAAe,8BACb,WACe;AACf,MAAI,CAAC,UAAW;AAChB,MAAI;AAKJ,MAAI;AACF,uBAAmB,UAAU,QAAQ,kBAAkB;AAAA,EACzD,QAAQ;AACN,uBAAmB;AAAA,EACrB;AACA,MAAI,CAAC,iBAAkB;AACvB,MAAI;AACF,UAAM,iBAAiB,SAAS;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,qBAAqB,kCAAkC;AAAA,EACpE;AAAA,EAEA,MAAM,aAAa,EAAE,UAAU,GAAG;AAChC,UAAM,mCAAmC,SAAS;AAClD,UAAM,8BAA8B,SAAS;AAAA,EAC/C;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,59 @@
1
+ import { resolveNotificationService } from "@open-mercato/core/modules/notifications/lib/notificationService";
2
+ import { buildNotificationFromType } from "@open-mercato/core/modules/notifications/lib/notificationBuilder";
3
+ import { defaultLocale } from "@open-mercato/shared/lib/i18n/config";
4
+ import { loadDictionary } from "@open-mercato/shared/lib/i18n/server";
5
+ import { createTranslator } from "@open-mercato/shared/lib/i18n/translate";
6
+ import { notificationTypes } from "../notifications.js";
7
+ const metadata = {
8
+ event: "ai_assistant.conversation.shared",
9
+ persistent: true,
10
+ id: "ai_assistant:conversation-shared-notify"
11
+ };
12
+ async function resolveDefaultLocaleStrings(titleKey, bodyKey) {
13
+ if (!titleKey && !bodyKey) return { title: titleKey, body: bodyKey };
14
+ try {
15
+ const dict = await loadDictionary(defaultLocale);
16
+ const t = createTranslator(dict);
17
+ return {
18
+ title: titleKey ? t(titleKey) : titleKey,
19
+ body: bodyKey ? t(bodyKey) : bodyKey
20
+ };
21
+ } catch {
22
+ return { title: titleKey, body: bodyKey };
23
+ }
24
+ }
25
+ async function handleConversationShared(payload, ctx) {
26
+ if (!payload?.participantUserId || !payload.tenantId) return;
27
+ const typeDef = notificationTypes.find((t) => t.type === "ai_assistant.conversation_shared");
28
+ if (!typeDef) return;
29
+ const container = ctx.container ?? { resolve: ctx.resolve };
30
+ let notificationService;
31
+ try {
32
+ notificationService = resolveNotificationService(container);
33
+ } catch {
34
+ return;
35
+ }
36
+ const notificationInput = buildNotificationFromType(typeDef, {
37
+ recipientUserId: payload.participantUserId,
38
+ bodyVariables: {},
39
+ sourceEntityType: "ai_assistant:ai_chat_conversation",
40
+ sourceEntityId: payload.conversationId,
41
+ linkHref: `/backend?openAiConversation=${payload.conversationId}`
42
+ });
43
+ const resolved = await resolveDefaultLocaleStrings(typeDef.titleKey, typeDef.bodyKey);
44
+ if (resolved.title !== void 0) notificationInput.title = resolved.title;
45
+ if (resolved.body !== void 0) notificationInput.body = resolved.body;
46
+ try {
47
+ await notificationService.create(notificationInput, {
48
+ tenantId: payload.tenantId,
49
+ organizationId: payload.organizationId ?? null
50
+ });
51
+ } catch (err) {
52
+ console.warn("[ai_assistant.conversationSharedNotify] create failed", err);
53
+ }
54
+ }
55
+ export {
56
+ handleConversationShared as default,
57
+ metadata
58
+ };
59
+ //# sourceMappingURL=conversation-shared-notify.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/subscribers/conversation-shared-notify.ts"],
4
+ "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { defaultLocale } from '@open-mercato/shared/lib/i18n/config'\nimport { loadDictionary } from '@open-mercato/shared/lib/i18n/server'\nimport { createTranslator } from '@open-mercato/shared/lib/i18n/translate'\nimport { notificationTypes } from '../notifications'\nimport type { AiConversationSharedPayload } from '../events'\n\nexport const metadata = {\n event: 'ai_assistant.conversation.shared',\n persistent: true,\n id: 'ai_assistant:conversation-shared-notify',\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n container?: { resolve<T = unknown>(name: string): T }\n}\n\nasync function resolveDefaultLocaleStrings(\n titleKey: string | undefined,\n bodyKey: string | undefined,\n): Promise<{ title: string | undefined; body: string | undefined }> {\n if (!titleKey && !bodyKey) return { title: titleKey, body: bodyKey }\n try {\n const dict = await loadDictionary(defaultLocale)\n const t = createTranslator(dict)\n return {\n title: titleKey ? t(titleKey) : titleKey,\n body: bodyKey ? t(bodyKey) : bodyKey,\n }\n } catch {\n return { title: titleKey, body: bodyKey }\n }\n}\n\nexport default async function handleConversationShared(\n payload: AiConversationSharedPayload,\n ctx: ResolverContext,\n): Promise<void> {\n if (!payload?.participantUserId || !payload.tenantId) return\n\n const typeDef = notificationTypes.find((t) => t.type === 'ai_assistant.conversation_shared')\n if (!typeDef) return\n\n const container = ctx.container ?? { resolve: ctx.resolve }\n let notificationService: ReturnType<typeof resolveNotificationService> | null\n try {\n notificationService = resolveNotificationService(container)\n } catch {\n return\n }\n\n const notificationInput = buildNotificationFromType(typeDef, {\n recipientUserId: payload.participantUserId,\n bodyVariables: {},\n sourceEntityType: 'ai_assistant:ai_chat_conversation',\n sourceEntityId: payload.conversationId,\n linkHref: `/backend?openAiConversation=${payload.conversationId}`,\n })\n\n // Persist a human-readable fallback alongside the i18n keys so consumers\n // that do not run the client renderer (email digests, exports) display a\n // resolved string instead of the raw key. The client renderer continues\n // to re-translate via titleKey/bodyKey for the viewer's locale.\n const resolved = await resolveDefaultLocaleStrings(typeDef.titleKey, typeDef.bodyKey)\n if (resolved.title !== undefined) notificationInput.title = resolved.title\n if (resolved.body !== undefined) notificationInput.body = resolved.body\n\n try {\n await notificationService.create(notificationInput, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId ?? null,\n })\n } catch (err) {\n console.warn('[ai_assistant.conversationSharedNotify] create failed', err)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,iCAAiC;AAC1C,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAG3B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAOA,eAAe,4BACb,UACA,SACkE;AAClE,MAAI,CAAC,YAAY,CAAC,QAAS,QAAO,EAAE,OAAO,UAAU,MAAM,QAAQ;AACnE,MAAI;AACF,UAAM,OAAO,MAAM,eAAe,aAAa;AAC/C,UAAM,IAAI,iBAAiB,IAAI;AAC/B,WAAO;AAAA,MACL,OAAO,WAAW,EAAE,QAAQ,IAAI;AAAA,MAChC,MAAM,UAAU,EAAE,OAAO,IAAI;AAAA,IAC/B;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,UAAU,MAAM,QAAQ;AAAA,EAC1C;AACF;AAEA,eAAO,yBACL,SACA,KACe;AACf,MAAI,CAAC,SAAS,qBAAqB,CAAC,QAAQ,SAAU;AAEtD,QAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,kCAAkC;AAC3F,MAAI,CAAC,QAAS;AAEd,QAAM,YAAY,IAAI,aAAa,EAAE,SAAS,IAAI,QAAQ;AAC1D,MAAI;AACJ,MAAI;AACF,0BAAsB,2BAA2B,SAAS;AAAA,EAC5D,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,oBAAoB,0BAA0B,SAAS;AAAA,IAC3D,iBAAiB,QAAQ;AAAA,IACzB,eAAe,CAAC;AAAA,IAChB,kBAAkB;AAAA,IAClB,gBAAgB,QAAQ;AAAA,IACxB,UAAU,+BAA+B,QAAQ,cAAc;AAAA,EACjE,CAAC;AAMD,QAAM,WAAW,MAAM,4BAA4B,QAAQ,UAAU,QAAQ,OAAO;AACpF,MAAI,SAAS,UAAU,OAAW,mBAAkB,QAAQ,SAAS;AACrE,MAAI,SAAS,SAAS,OAAW,mBAAkB,OAAO,SAAS;AAEnE,MAAI;AACF,UAAM,oBAAoB,OAAO,mBAAmB;AAAA,MAClD,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ,kBAAkB;AAAA,IAC5C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,KAAK,yDAAyD,GAAG;AAAA,EAC3E;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Share2, ExternalLink, X } from "lucide-react";
5
+ import { useRouter } from "next/navigation";
6
+ import { Button } from "@open-mercato/ui/primitives/button";
7
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
8
+ import { cn } from "@open-mercato/shared/lib/utils";
9
+ import { formatRelativeTime } from "@open-mercato/shared/lib/time";
10
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
11
+ function ConversationSharedRenderer({
12
+ notification,
13
+ onAction,
14
+ onDismiss,
15
+ actions = []
16
+ }) {
17
+ const t = useT();
18
+ const router = useRouter();
19
+ const [executing, setExecuting] = React.useState(false);
20
+ const isUnread = notification.status === "unread";
21
+ const viewAction = actions.find((a) => a.id === "view") ?? actions[0] ?? null;
22
+ const handleView = async () => {
23
+ setExecuting(true);
24
+ try {
25
+ if (viewAction) {
26
+ await onAction(viewAction.id);
27
+ }
28
+ if (notification.linkHref) router.push(notification.linkHref);
29
+ } finally {
30
+ setExecuting(false);
31
+ }
32
+ };
33
+ const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? "";
34
+ return /* @__PURE__ */ jsxs(
35
+ "div",
36
+ {
37
+ className: cn(
38
+ "group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer",
39
+ isUnread && "bg-muted/20"
40
+ ),
41
+ onClick: executing ? void 0 : handleView,
42
+ onKeyDown: (e) => {
43
+ if (executing) return;
44
+ if (e.key === "Enter" || e.key === " ") {
45
+ e.preventDefault();
46
+ handleView();
47
+ }
48
+ },
49
+ role: "button",
50
+ tabIndex: 0,
51
+ children: [
52
+ /* @__PURE__ */ jsxs("div", { className: "relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg", children: [
53
+ /* @__PURE__ */ jsx(Share2, { className: "size-5 text-status-info-icon", "aria-hidden": "true" }),
54
+ isUnread ? /* @__PURE__ */ jsx(
55
+ "span",
56
+ {
57
+ className: "absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background",
58
+ "aria-hidden": "true"
59
+ }
60
+ ) : null
61
+ ] }),
62
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
63
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium leading-5 tracking-tight text-foreground", children: notification.title }),
64
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-4 text-muted-foreground", children: timeAgo ? /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: timeAgo }) : null }),
65
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
66
+ /* @__PURE__ */ jsxs(
67
+ Button,
68
+ {
69
+ type: "button",
70
+ size: "sm",
71
+ className: "h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90",
72
+ onClick: (e) => {
73
+ e.stopPropagation();
74
+ handleView();
75
+ },
76
+ disabled: executing || !viewAction && !notification.linkHref,
77
+ children: [
78
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3.5", "aria-hidden": "true" }),
79
+ t("ai_assistant.notifications.conversation_shared.view_button", "View Conversation")
80
+ ]
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsx(
84
+ Button,
85
+ {
86
+ type: "button",
87
+ variant: "outline",
88
+ size: "sm",
89
+ className: "h-8 rounded-md px-2.5",
90
+ onClick: (e) => {
91
+ e.stopPropagation();
92
+ onDismiss();
93
+ },
94
+ children: t("notifications.actions.dismiss", "Dismiss")
95
+ }
96
+ )
97
+ ] })
98
+ ] }),
99
+ /* @__PURE__ */ jsx(
100
+ IconButton,
101
+ {
102
+ type: "button",
103
+ variant: "ghost",
104
+ size: "xs",
105
+ className: "absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100",
106
+ onClick: (e) => {
107
+ e.stopPropagation();
108
+ onDismiss();
109
+ },
110
+ "aria-label": t("notifications.actions.dismiss", "Dismiss"),
111
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
112
+ }
113
+ )
114
+ ]
115
+ }
116
+ );
117
+ }
118
+ var ConversationSharedRenderer_default = ConversationSharedRenderer;
119
+ export {
120
+ ConversationSharedRenderer,
121
+ ConversationSharedRenderer_default as default
122
+ };
123
+ //# sourceMappingURL=ConversationSharedRenderer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Share2, ExternalLink, X } from 'lucide-react'\nimport { useRouter } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'\n\nexport function ConversationSharedRenderer({\n notification,\n onAction,\n onDismiss,\n actions = [],\n}: NotificationRendererProps) {\n const t = useT()\n const router = useRouter()\n const [executing, setExecuting] = React.useState(false)\n const isUnread = notification.status === 'unread'\n const viewAction = actions.find((a) => a.id === 'view') ?? actions[0] ?? null\n\n const handleView = async () => {\n setExecuting(true)\n try {\n if (viewAction) {\n await onAction(viewAction.id)\n }\n if (notification.linkHref) router.push(notification.linkHref)\n } finally {\n setExecuting(false)\n }\n }\n\n const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? ''\n\n return (\n <div\n className={cn(\n 'group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer',\n isUnread && 'bg-muted/20',\n )}\n onClick={executing ? undefined : handleView}\n onKeyDown={(e) => {\n if (executing) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleView()\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n <div className=\"relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg\">\n <Share2 className=\"size-5 text-status-info-icon\" aria-hidden=\"true\" />\n {isUnread ? (\n <span\n className=\"absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background\"\n aria-hidden=\"true\"\n />\n ) : null}\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n <p className=\"truncate text-sm font-medium leading-5 tracking-tight text-foreground\">\n {notification.title}\n </p>\n\n <div className=\"text-xs leading-4 text-muted-foreground\">\n {timeAgo ? <span className=\"whitespace-nowrap\">{timeAgo}</span> : null}\n </div>\n\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90\"\n onClick={(e) => {\n e.stopPropagation()\n handleView()\n }}\n disabled={executing || (!viewAction && !notification.linkHref)}\n >\n <ExternalLink className=\"size-3.5\" aria-hidden=\"true\" />\n {t('ai_assistant.notifications.conversation_shared.view_button', 'View Conversation')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 rounded-md px-2.5\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n >\n {t('notifications.actions.dismiss', 'Dismiss')}\n </Button>\n </div>\n </div>\n\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n aria-label={t('notifications.actions.dismiss', 'Dismiss')}\n >\n <X className=\"size-3.5\" />\n </IconButton>\n </div>\n )\n}\n\nexport default ConversationSharedRenderer\n"],
5
+ "mappings": ";AAuDM,SACE,KADF;AArDN,YAAY,WAAW;AACvB,SAAS,QAAQ,cAAc,SAAS;AACxC,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,YAAY;AAGd,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AACb,GAA8B;AAC5B,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AACtD,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK;AAEzE,QAAM,aAAa,YAAY;AAC7B,iBAAa,IAAI;AACjB,QAAI;AACF,UAAI,YAAY;AACd,cAAM,SAAS,WAAW,EAAE;AAAA,MAC9B;AACA,UAAI,aAAa,SAAU,QAAO,KAAK,aAAa,QAAQ;AAAA,IAC9D,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,UAAU,mBAAmB,aAAa,WAAW,EAAE,WAAW,EAAE,CAAC,KAAK;AAEhF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,MACd;AAAA,MACA,SAAS,YAAY,SAAY;AAAA,MACjC,WAAW,CAAC,MAAM;AAChB,YAAI,UAAW;AACf,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,qBAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,MAAK;AAAA,MACL,UAAU;AAAA,MAEV;AAAA,6BAAC,SAAI,WAAU,6FACb;AAAA,8BAAC,UAAO,WAAU,gCAA+B,eAAY,QAAO;AAAA,UACnE,WACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA;AAAA,UACd,IACE;AAAA,WACN;AAAA,QAEA,qBAAC,SAAI,WAAU,sCACb;AAAA,8BAAC,OAAE,WAAU,yEACV,uBAAa,OAChB;AAAA,UAEA,oBAAC,SAAI,WAAU,2CACZ,oBAAU,oBAAC,UAAK,WAAU,qBAAqB,mBAAQ,IAAU,MACpE;AAAA,UAEA,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,6BAAW;AAAA,gBACb;AAAA,gBACA,UAAU,aAAc,CAAC,cAAc,CAAC,aAAa;AAAA,gBAErD;AAAA,sCAAC,gBAAa,WAAU,YAAW,eAAY,QAAO;AAAA,kBACrD,EAAE,8DAA8D,mBAAmB;AAAA;AAAA;AAAA,YACtF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,4BAAU;AAAA,gBACZ;AAAA,gBAEC,YAAE,iCAAiC,SAAS;AAAA;AAAA,YAC/C;AAAA,aACF;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,wBAAU;AAAA,YACZ;AAAA,YACA,cAAY,EAAE,iCAAiC,SAAS;AAAA,YAExD,8BAAC,KAAE,WAAU,YAAW;AAAA;AAAA,QAC1B;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qCAAQ;",
6
+ "names": []
7
+ }
@@ -5,5 +5,6 @@ export const conversation_id = "conversation_id";
5
5
  export const user_id = "user_id";
6
6
  export const role = "role";
7
7
  export const last_read_at = "last_read_at";
8
+ export const deleted_at = "deleted_at";
8
9
  export const created_at = "created_at";
9
10
  export const updated_at = "updated_at";
@@ -71,6 +71,7 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
71
71
  "user_id": "user_id",
72
72
  "role": "role",
73
73
  "last_read_at": "last_read_at",
74
+ "deleted_at": "deleted_at",
74
75
  "created_at": "created_at",
75
76
  "updated_at": "updated_at"
76
77
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ai-assistant",
3
- "version": "0.6.3-develop.3894.1.352abf4240",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -98,16 +98,16 @@
98
98
  "zod-to-json-schema": "^3.25.2"
99
99
  },
100
100
  "peerDependencies": {
101
- "@open-mercato/shared": "0.6.3-develop.3894.1.352abf4240",
102
- "@open-mercato/ui": "0.6.3-develop.3894.1.352abf4240",
101
+ "@open-mercato/shared": "0.6.3",
102
+ "@open-mercato/ui": "0.6.3",
103
103
  "react": "^19.0.0",
104
104
  "react-dom": "^19.0.0",
105
105
  "zod": ">=3.23.0"
106
106
  },
107
107
  "devDependencies": {
108
- "@open-mercato/cli": "0.6.3-develop.3894.1.352abf4240",
109
- "@open-mercato/shared": "0.6.3-develop.3894.1.352abf4240",
110
- "@open-mercato/ui": "0.6.3-develop.3894.1.352abf4240",
108
+ "@open-mercato/cli": "0.6.3",
109
+ "@open-mercato/shared": "0.6.3",
110
+ "@open-mercato/ui": "0.6.3",
111
111
  "@types/react": "^19.2.15",
112
112
  "@types/react-dom": "^19.2.3",
113
113
  "react": "19.2.6",
@@ -122,6 +122,5 @@
122
122
  "type": "git",
123
123
  "url": "https://github.com/open-mercato/open-mercato",
124
124
  "directory": "packages/ai-assistant"
125
- },
126
- "stableVersion": "0.6.2"
125
+ }
127
126
  }