@open-mercato/ai-assistant 0.6.3-develop.3901.1.ddad60693a → 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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js +87 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js +119 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js.map +7 -0
- package/dist/modules/ai_assistant/acl.js +1 -0
- package/dist/modules/ai_assistant/acl.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/chat/route.js +3 -0
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js +128 -0
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js +271 -0
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js +9 -1
- package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/conversations/route.js +4 -1
- package/dist/modules/ai_assistant/api/ai/conversations/route.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +5 -1
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/components/ConversationShareButton.js +5 -0
- package/dist/modules/ai_assistant/components/ConversationShareButton.js.map +7 -0
- package/dist/modules/ai_assistant/components/ConversationShareDialog.js +5 -0
- package/dist/modules/ai_assistant/components/ConversationShareDialog.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities.js +3 -0
- package/dist/modules/ai_assistant/data/entities.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js +235 -5
- package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js.map +2 -2
- package/dist/modules/ai_assistant/events.js +14 -0
- package/dist/modules/ai_assistant/events.js.map +2 -2
- package/dist/modules/ai_assistant/i18n/de.json +17 -0
- package/dist/modules/ai_assistant/i18n/en.json +17 -0
- package/dist/modules/ai_assistant/i18n/es.json +17 -0
- package/dist/modules/ai_assistant/i18n/pl.json +17 -0
- package/dist/modules/ai_assistant/lib/conversation-storage.js +12 -3
- package/dist/modules/ai_assistant/lib/conversation-storage.js.map +2 -2
- package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js +15 -0
- package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js.map +7 -0
- package/dist/modules/ai_assistant/notifications.client.js +30 -0
- package/dist/modules/ai_assistant/notifications.client.js.map +7 -0
- package/dist/modules/ai_assistant/notifications.js +27 -0
- package/dist/modules/ai_assistant/notifications.js.map +7 -0
- package/dist/modules/ai_assistant/setup.js +2 -1
- package/dist/modules/ai_assistant/setup.js.map +2 -2
- package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js +59 -0
- package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js.map +7 -0
- package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js +123 -0
- package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js.map +7 -0
- package/generated/entities/ai_chat_conversation_participant/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +7 -8
- package/src/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.ts +117 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.ts +159 -0
- package/src/modules/ai_assistant/__tests__/integration/ai-chat-sharing.test.ts +406 -0
- package/src/modules/ai_assistant/acl.ts +1 -0
- package/src/modules/ai_assistant/api/ai/chat/route.ts +3 -0
- package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.ts +149 -0
- package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.ts +314 -0
- package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/route.ts +9 -1
- package/src/modules/ai_assistant/api/ai/conversations/route.ts +4 -1
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +4 -0
- package/src/modules/ai_assistant/components/ConversationShareButton.tsx +1 -0
- package/src/modules/ai_assistant/components/ConversationShareDialog.tsx +1 -0
- package/src/modules/ai_assistant/data/entities.ts +4 -0
- package/src/modules/ai_assistant/data/repositories/AiChatConversationRepository.ts +270 -7
- package/src/modules/ai_assistant/data/repositories/__tests__/AiChatConversationRepository.test.ts +297 -3
- package/src/modules/ai_assistant/events.ts +31 -0
- package/src/modules/ai_assistant/i18n/__tests__/conversation-share-translations.test.ts +59 -0
- package/src/modules/ai_assistant/i18n/de.json +17 -0
- package/src/modules/ai_assistant/i18n/en.json +17 -0
- package/src/modules/ai_assistant/i18n/es.json +17 -0
- package/src/modules/ai_assistant/i18n/pl.json +17 -0
- package/src/modules/ai_assistant/lib/conversation-storage.ts +22 -1
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +25 -0
- package/src/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.ts +15 -0
- package/src/modules/ai_assistant/notifications.client.ts +29 -0
- package/src/modules/ai_assistant/notifications.ts +25 -0
- package/src/modules/ai_assistant/setup.ts +2 -1
- package/src/modules/ai_assistant/subscribers/__tests__/conversation-shared-notify.test.ts +116 -0
- package/src/modules/ai_assistant/subscribers/conversation-shared-notify.ts +78 -0
- package/src/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.tsx +121 -0
|
@@ -47,6 +47,20 @@ const events = [
|
|
|
47
47
|
clientBroadcast: false,
|
|
48
48
|
portalBroadcast: false,
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
id: 'ai_assistant.conversation.shared',
|
|
52
|
+
label: 'AI Conversation Shared',
|
|
53
|
+
entity: 'ai_chat_conversation',
|
|
54
|
+
category: 'lifecycle' as const,
|
|
55
|
+
clientBroadcast: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'ai_assistant.conversation.unshared',
|
|
59
|
+
label: 'AI Conversation Unshared',
|
|
60
|
+
entity: 'ai_chat_conversation',
|
|
61
|
+
category: 'lifecycle' as const,
|
|
62
|
+
clientBroadcast: true,
|
|
63
|
+
},
|
|
50
64
|
] as const
|
|
51
65
|
|
|
52
66
|
export const eventsConfig = createModuleEvents({
|
|
@@ -117,4 +131,21 @@ export interface AiActionExpiredPayload {
|
|
|
117
131
|
expiredAt?: string
|
|
118
132
|
}
|
|
119
133
|
|
|
134
|
+
export interface AiConversationSharedPayload {
|
|
135
|
+
conversationId: string
|
|
136
|
+
tenantId: string
|
|
137
|
+
organizationId: string | null
|
|
138
|
+
ownerUserId: string
|
|
139
|
+
participantUserId: string
|
|
140
|
+
role: string
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface AiConversationUnsharedPayload {
|
|
144
|
+
conversationId: string
|
|
145
|
+
tenantId: string
|
|
146
|
+
organizationId: string | null
|
|
147
|
+
ownerUserId: string
|
|
148
|
+
participantUserId: string
|
|
149
|
+
}
|
|
150
|
+
|
|
120
151
|
export default eventsConfig
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression coverage for issue #2097 (BUG-004): the conversation-sharing
|
|
3
|
+
* UI and notification strings MUST ship localized values in every supported
|
|
4
|
+
* locale, not the English copy. Catches drift when keys are renamed/added
|
|
5
|
+
* but their PL/DE/ES values are forgotten.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import enDict from '../en.json'
|
|
9
|
+
import plDict from '../pl.json'
|
|
10
|
+
import deDict from '../de.json'
|
|
11
|
+
import esDict from '../es.json'
|
|
12
|
+
|
|
13
|
+
type Dict = Record<string, string>
|
|
14
|
+
|
|
15
|
+
const KEYS_TO_LOCALIZE = [
|
|
16
|
+
'ai_assistant.chat.readOnlyNotice',
|
|
17
|
+
'ai_assistant.launcher.composerPlaceholder',
|
|
18
|
+
'ai_assistant.notifications.conversation_shared.title',
|
|
19
|
+
'ai_assistant.notifications.conversation_shared.body',
|
|
20
|
+
'ai_assistant.notifications.conversation_shared.view_button',
|
|
21
|
+
'ai_assistant.share.addParticipant',
|
|
22
|
+
'ai_assistant.share.allUsersAdded',
|
|
23
|
+
'ai_assistant.share.dialogDescription',
|
|
24
|
+
'ai_assistant.share.dialogTitle',
|
|
25
|
+
'ai_assistant.share.noParticipants',
|
|
26
|
+
'ai_assistant.share.participantPlaceholder',
|
|
27
|
+
'ai_assistant.share.removeParticipant',
|
|
28
|
+
'ai_assistant.share.saved',
|
|
29
|
+
'ai_assistant.share.saving',
|
|
30
|
+
'ai_assistant.share.selectUser',
|
|
31
|
+
'ai_assistant.share.shareButton',
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const LOCALE_DICTS: Array<[string, Dict]> = [
|
|
35
|
+
['pl', plDict as Dict],
|
|
36
|
+
['de', deDict as Dict],
|
|
37
|
+
['es', esDict as Dict],
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
describe('ai_assistant conversation-share i18n keys', () => {
|
|
41
|
+
it.each(KEYS_TO_LOCALIZE)('en.json defines %s', (key) => {
|
|
42
|
+
expect((enDict as Dict)[key]).toBeTruthy()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
for (const [locale, dict] of LOCALE_DICTS) {
|
|
46
|
+
describe(`${locale}.json`, () => {
|
|
47
|
+
it.each(KEYS_TO_LOCALIZE)('defines %s', (key) => {
|
|
48
|
+
expect(dict[key]).toBeTruthy()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it.each(KEYS_TO_LOCALIZE)('localizes %s (value differs from en)', (key) => {
|
|
52
|
+
const enValue = (enDict as Dict)[key]
|
|
53
|
+
const localizedValue = dict[key]
|
|
54
|
+
expect(localizedValue).toBeTruthy()
|
|
55
|
+
expect(localizedValue).not.toBe(enValue)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
})
|
|
@@ -216,9 +216,11 @@
|
|
|
216
216
|
"ai_assistant.chat.mutation_cards.result.successWithCommand": "Abgeschlossen",
|
|
217
217
|
"ai_assistant.chat.mutation_cards.result.viewRecord": "Datensatz anzeigen",
|
|
218
218
|
"ai_assistant.chat.newConversation": "Start new conversation",
|
|
219
|
+
"ai_assistant.chat.ownerRoleLabel": "Owner",
|
|
219
220
|
"ai_assistant.chat.pending_phase3.body": "Diese interaktive Karte kommt in Phase 3 des vereinheitlichten KI-Frameworks.",
|
|
220
221
|
"ai_assistant.chat.pending_phase3.title": "Mutations-Freigabe-Karte ausstehend",
|
|
221
222
|
"ai_assistant.chat.placeholder": "Fragen Sie mich etwas...",
|
|
223
|
+
"ai_assistant.chat.readOnlyNotice": "Dies ist eine geteilte Konversation. Sie können lesen, aber nicht antworten.",
|
|
222
224
|
"ai_assistant.chat.reasoning": "Reasoning",
|
|
223
225
|
"ai_assistant.chat.records.fields.amount": "Betrag",
|
|
224
226
|
"ai_assistant.chat.records.fields.category": "Kategorie",
|
|
@@ -282,6 +284,7 @@
|
|
|
282
284
|
"ai_assistant.dock.left": "Links andocken",
|
|
283
285
|
"ai_assistant.dock.minimize": "Minimieren",
|
|
284
286
|
"ai_assistant.dock.right": "Rechts andocken",
|
|
287
|
+
"ai_assistant.launcher.composerPlaceholder": "Fragen Sie mich etwas…",
|
|
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": "KI-Modell auswählen",
|
|
324
327
|
"ai_assistant.modelPicker.useDefault": "Standard des Agenten verwenden",
|
|
325
328
|
"ai_assistant.modelPicker.useDefaultWithModel": "Standard des Agenten verwenden: {{model}}",
|
|
329
|
+
"ai_assistant.notifications.conversation_shared.body": "Eine KI-Konversation wurde mit Ihnen geteilt.",
|
|
330
|
+
"ai_assistant.notifications.conversation_shared.title": "Konversation mit Ihnen geteilt",
|
|
331
|
+
"ai_assistant.notifications.conversation_shared.view_button": "Konversation öffnen",
|
|
326
332
|
"ai_assistant.playground.agentPickerLabel": "Agent",
|
|
327
333
|
"ai_assistant.playground.chat.notSupportedBody": "Wählen Sie einen Agenten, dessen Ausführungsmodus \"chat\" ist, oder wechseln Sie zum Objekt-Modus-Tab.",
|
|
328
334
|
"ai_assistant.playground.chat.notSupportedTitle": "Chat-Modus ist für diesen Agenten nicht verfügbar.",
|
|
@@ -428,6 +434,17 @@
|
|
|
428
434
|
"ai_assistant.settings.visibilityEnabled": "Im Header sichtbar mit aktiviertem Cmd+J-Shortcut.",
|
|
429
435
|
"ai_assistant.settings.visibilityTitle": "KI-Assistent",
|
|
430
436
|
"ai_assistant.settings.visibilityToggleLabel": "Sichtbarkeit",
|
|
437
|
+
"ai_assistant.share.addParticipant": "Teilnehmer hinzufügen",
|
|
438
|
+
"ai_assistant.share.allUsersAdded": "Alle Benutzer bereits hinzugefügt",
|
|
439
|
+
"ai_assistant.share.dialogDescription": "Teilen Sie diese Konversation mit anderen Benutzern. Sie erhalten nur Lesezugriff.",
|
|
440
|
+
"ai_assistant.share.dialogTitle": "Konversation teilen",
|
|
441
|
+
"ai_assistant.share.noParticipants": "Noch keine Teilnehmer. Fügen Sie jemanden hinzu, um diese Konversation zu teilen.",
|
|
442
|
+
"ai_assistant.share.participantPlaceholder": "Nach Benutzer suchen...",
|
|
443
|
+
"ai_assistant.share.removeParticipant": "Entfernen",
|
|
444
|
+
"ai_assistant.share.saved": "Gespeichert",
|
|
445
|
+
"ai_assistant.share.saving": "Wird gespeichert...",
|
|
446
|
+
"ai_assistant.share.selectUser": "Benutzer auswählen...",
|
|
447
|
+
"ai_assistant.share.shareButton": "Teilen",
|
|
431
448
|
"ai_assistant.status.analyzing": "Anfrage wird analysiert...",
|
|
432
449
|
"ai_assistant.status.executing": "Tools werden ausgeführt...",
|
|
433
450
|
"ai_assistant.status.responding": "Antwort wird erstellt...",
|
|
@@ -216,9 +216,11 @@
|
|
|
216
216
|
"ai_assistant.chat.mutation_cards.result.successWithCommand": "Completed",
|
|
217
217
|
"ai_assistant.chat.mutation_cards.result.viewRecord": "View record",
|
|
218
218
|
"ai_assistant.chat.newConversation": "Start new conversation",
|
|
219
|
+
"ai_assistant.chat.ownerRoleLabel": "Owner",
|
|
219
220
|
"ai_assistant.chat.pending_phase3.body": "This interactive card will land in Phase 3 of the unified AI framework.",
|
|
220
221
|
"ai_assistant.chat.pending_phase3.title": "Mutation approval card pending",
|
|
221
222
|
"ai_assistant.chat.placeholder": "Ask me anything...",
|
|
223
|
+
"ai_assistant.chat.readOnlyNotice": "This is a shared conversation. You can read but not reply.",
|
|
222
224
|
"ai_assistant.chat.reasoning": "Reasoning",
|
|
223
225
|
"ai_assistant.chat.records.fields.amount": "Amount",
|
|
224
226
|
"ai_assistant.chat.records.fields.category": "Category",
|
|
@@ -282,6 +284,7 @@
|
|
|
282
284
|
"ai_assistant.dock.left": "Dock Left",
|
|
283
285
|
"ai_assistant.dock.minimize": "Minimize",
|
|
284
286
|
"ai_assistant.dock.right": "Dock Right",
|
|
287
|
+
"ai_assistant.launcher.composerPlaceholder": "Ask anything…",
|
|
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": "Select AI model",
|
|
324
327
|
"ai_assistant.modelPicker.useDefault": "Use agent default",
|
|
325
328
|
"ai_assistant.modelPicker.useDefaultWithModel": "Use agent default: {{model}}",
|
|
329
|
+
"ai_assistant.notifications.conversation_shared.body": "An AI conversation has been shared with you.",
|
|
330
|
+
"ai_assistant.notifications.conversation_shared.title": "Conversation shared with you",
|
|
331
|
+
"ai_assistant.notifications.conversation_shared.view_button": "View Conversation",
|
|
326
332
|
"ai_assistant.playground.agentPickerLabel": "Agent",
|
|
327
333
|
"ai_assistant.playground.chat.notSupportedBody": "Pick an agent whose execution mode is \"chat\", or switch to the object-mode tab.",
|
|
328
334
|
"ai_assistant.playground.chat.notSupportedTitle": "Chat mode is not available for this agent.",
|
|
@@ -428,6 +434,17 @@
|
|
|
428
434
|
"ai_assistant.settings.visibilityEnabled": "Visible in header with Cmd+J shortcut enabled.",
|
|
429
435
|
"ai_assistant.settings.visibilityTitle": "AI Assistant",
|
|
430
436
|
"ai_assistant.settings.visibilityToggleLabel": "Visibility",
|
|
437
|
+
"ai_assistant.share.addParticipant": "Add participant",
|
|
438
|
+
"ai_assistant.share.allUsersAdded": "All users already added",
|
|
439
|
+
"ai_assistant.share.dialogDescription": "Share this conversation with other users. They will get read-only access.",
|
|
440
|
+
"ai_assistant.share.dialogTitle": "Share Conversation",
|
|
441
|
+
"ai_assistant.share.noParticipants": "No participants yet. Add someone to share this conversation.",
|
|
442
|
+
"ai_assistant.share.participantPlaceholder": "Search by user...",
|
|
443
|
+
"ai_assistant.share.removeParticipant": "Remove",
|
|
444
|
+
"ai_assistant.share.saved": "Saved",
|
|
445
|
+
"ai_assistant.share.saving": "Saving...",
|
|
446
|
+
"ai_assistant.share.selectUser": "Select a user...",
|
|
447
|
+
"ai_assistant.share.shareButton": "Share",
|
|
431
448
|
"ai_assistant.status.analyzing": "Analyzing request...",
|
|
432
449
|
"ai_assistant.status.executing": "Executing tools...",
|
|
433
450
|
"ai_assistant.status.responding": "Responding...",
|
|
@@ -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...",
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
} from '../data/entities'
|
|
7
7
|
import {
|
|
8
8
|
AiChatConversationAccessError,
|
|
9
|
+
AiChatConversationDuplicateParticipantError,
|
|
10
|
+
AiChatConversationOrgNotFoundError,
|
|
11
|
+
AiChatParticipantNotFoundError,
|
|
9
12
|
AiChatConversationRepository,
|
|
10
13
|
type AiChatConversationContext,
|
|
11
14
|
} from '../data/repositories/AiChatConversationRepository'
|
|
@@ -22,7 +25,12 @@ import {
|
|
|
22
25
|
* Re-exports the access error so route handlers can map it to a 404 without
|
|
23
26
|
* importing the repository directly.
|
|
24
27
|
*/
|
|
25
|
-
export {
|
|
28
|
+
export {
|
|
29
|
+
AiChatConversationAccessError,
|
|
30
|
+
AiChatConversationDuplicateParticipantError,
|
|
31
|
+
AiChatConversationOrgNotFoundError,
|
|
32
|
+
AiChatParticipantNotFoundError,
|
|
33
|
+
}
|
|
26
34
|
export type { AiChatConversationContext }
|
|
27
35
|
|
|
28
36
|
export function createConversationStorage(
|
|
@@ -43,6 +51,13 @@ export interface SerializedAiChatConversation {
|
|
|
43
51
|
updatedAt: string
|
|
44
52
|
lastMessageAt: string | null
|
|
45
53
|
importedFromLocalAt: string | null
|
|
54
|
+
participantCount: number
|
|
55
|
+
isOwner: boolean | null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AiChatConversationSerializeEnrich {
|
|
59
|
+
callerUserId?: string | null
|
|
60
|
+
participantCount?: number
|
|
46
61
|
}
|
|
47
62
|
|
|
48
63
|
export interface SerializedAiChatMessage {
|
|
@@ -56,10 +71,12 @@ export interface SerializedAiChatMessage {
|
|
|
56
71
|
model: string | null
|
|
57
72
|
metadata: Record<string, unknown> | null
|
|
58
73
|
createdAt: string
|
|
74
|
+
senderUserId: string | null
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
export function serializeAiChatConversation(
|
|
62
78
|
row: AiChatConversation,
|
|
79
|
+
enrich: AiChatConversationSerializeEnrich = {},
|
|
63
80
|
): SerializedAiChatConversation {
|
|
64
81
|
return {
|
|
65
82
|
conversationId: row.conversationId,
|
|
@@ -74,6 +91,9 @@ export function serializeAiChatConversation(
|
|
|
74
91
|
importedFromLocalAt: row.importedFromLocalAt
|
|
75
92
|
? row.importedFromLocalAt.toISOString()
|
|
76
93
|
: null,
|
|
94
|
+
participantCount: enrich.participantCount ?? 0,
|
|
95
|
+
isOwner:
|
|
96
|
+
enrich.callerUserId != null ? row.ownerUserId === enrich.callerUserId : null,
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
|
|
@@ -89,5 +109,6 @@ export function serializeAiChatMessage(row: AiChatMessage): SerializedAiChatMess
|
|
|
89
109
|
model: row.model ?? null,
|
|
90
110
|
metadata: row.metadata ?? null,
|
|
91
111
|
createdAt: row.createdAt.toISOString(),
|
|
112
|
+
senderUserId: row.createdByUserId ?? null,
|
|
92
113
|
}
|
|
93
114
|
}
|
|
@@ -856,6 +856,22 @@
|
|
|
856
856
|
"enumItems": [],
|
|
857
857
|
"mappedType": "uuid"
|
|
858
858
|
},
|
|
859
|
+
"deleted_at": {
|
|
860
|
+
"name": "deleted_at",
|
|
861
|
+
"type": "timestamptz(6)",
|
|
862
|
+
"unsigned": false,
|
|
863
|
+
"autoincrement": false,
|
|
864
|
+
"primary": false,
|
|
865
|
+
"nullable": true,
|
|
866
|
+
"unique": false,
|
|
867
|
+
"length": 6,
|
|
868
|
+
"precision": null,
|
|
869
|
+
"scale": null,
|
|
870
|
+
"default": null,
|
|
871
|
+
"comment": null,
|
|
872
|
+
"enumItems": [],
|
|
873
|
+
"mappedType": "datetime"
|
|
874
|
+
},
|
|
859
875
|
"last_read_at": {
|
|
860
876
|
"name": "last_read_at",
|
|
861
877
|
"type": "timestamptz(6)",
|
|
@@ -972,6 +988,15 @@
|
|
|
972
988
|
"unique": false,
|
|
973
989
|
"expression": "create unique index \"ai_chat_conv_participants_tenant_org_conv_user_uq\" on \"ai_chat_conversation_participants\" (\"tenant_id\", \"organization_id\", \"conversation_id\", \"user_id\") where \"organization_id\" is not null"
|
|
974
990
|
},
|
|
991
|
+
{
|
|
992
|
+
"columnNames": [],
|
|
993
|
+
"composite": false,
|
|
994
|
+
"constraint": false,
|
|
995
|
+
"keyName": "ai_chat_conv_participants_active_conv_user_idx",
|
|
996
|
+
"primary": false,
|
|
997
|
+
"unique": false,
|
|
998
|
+
"expression": "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"
|
|
999
|
+
},
|
|
975
1000
|
{
|
|
976
1001
|
"columnNames": [
|
|
977
1002
|
"tenant_id",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260522120000_ai_assistant extends Migration {
|
|
4
|
+
|
|
5
|
+
override async up(): Promise<void> {
|
|
6
|
+
this.addSql(`alter table "ai_chat_conversation_participants" add column "deleted_at" timestamptz null;`)
|
|
7
|
+
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;`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
override async down(): Promise<void> {
|
|
11
|
+
this.addSql(`drop index if exists "ai_chat_conv_participants_active_conv_user_idx";`)
|
|
12
|
+
this.addSql(`alter table "ai_chat_conversation_participants" drop column if exists "deleted_at";`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
|
|
4
|
+
import { ConversationSharedRenderer } from './widgets/notifications/ConversationSharedRenderer'
|
|
5
|
+
|
|
6
|
+
export const aiAssistantNotificationTypes: NotificationTypeDefinition[] = [
|
|
7
|
+
{
|
|
8
|
+
type: 'ai_assistant.conversation_shared',
|
|
9
|
+
module: 'ai_assistant',
|
|
10
|
+
titleKey: 'ai_assistant.notifications.conversation_shared.title',
|
|
11
|
+
bodyKey: 'ai_assistant.notifications.conversation_shared.body',
|
|
12
|
+
icon: 'share-2',
|
|
13
|
+
severity: 'info',
|
|
14
|
+
actions: [
|
|
15
|
+
{
|
|
16
|
+
id: 'view',
|
|
17
|
+
labelKey: 'common.view',
|
|
18
|
+
variant: 'outline',
|
|
19
|
+
href: '/backend?openAiConversation={sourceEntityId}',
|
|
20
|
+
icon: 'external-link',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
linkHref: '/backend',
|
|
24
|
+
Renderer: ConversationSharedRenderer,
|
|
25
|
+
expiresAfterHours: 168,
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
export default aiAssistantNotificationTypes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
|
|
2
|
+
|
|
3
|
+
export const notificationTypes: NotificationTypeDefinition[] = [
|
|
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
|
+
expiresAfterHours: 168,
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export default notificationTypes
|
|
@@ -103,12 +103,13 @@ export const setup: ModuleSetupConfig = {
|
|
|
103
103
|
'ai_assistant.view',
|
|
104
104
|
'ai_assistant.settings.manage',
|
|
105
105
|
'ai_assistant.conversations.manage',
|
|
106
|
+
'ai_assistant.conversations.share',
|
|
106
107
|
'ai_assistant.mcp.serve',
|
|
107
108
|
'ai_assistant.tools.list',
|
|
108
109
|
'ai_assistant.mcp_servers.view',
|
|
109
110
|
'ai_assistant.mcp_servers.manage',
|
|
110
111
|
],
|
|
111
|
-
employee: ['ai_assistant.view'],
|
|
112
|
+
employee: ['ai_assistant.view', 'ai_assistant.conversations.share'],
|
|
112
113
|
},
|
|
113
114
|
|
|
114
115
|
async seedDefaults({ container }) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression coverage for issue #2097 (BUG-004).
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the conversation-shared notification subscriber persists a
|
|
5
|
+
* resolved title/body string rather than the raw i18n key, so that
|
|
6
|
+
* consumers that do not run the client renderer (email, export, digest)
|
|
7
|
+
* see human-readable text instead of `ai_assistant.notifications.…`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
jest.mock('@open-mercato/shared/lib/i18n/server', () => ({
|
|
11
|
+
loadDictionary: jest.fn(async () => ({
|
|
12
|
+
'ai_assistant.notifications.conversation_shared.title': 'Conversation shared with you',
|
|
13
|
+
'ai_assistant.notifications.conversation_shared.body':
|
|
14
|
+
'An AI conversation has been shared with you.',
|
|
15
|
+
})),
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
jest.mock('@open-mercato/core/modules/notifications/lib/notificationService', () => ({
|
|
19
|
+
resolveNotificationService: jest.fn(),
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
import handleConversationShared from '../conversation-shared-notify'
|
|
23
|
+
import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'
|
|
24
|
+
import type { AiConversationSharedPayload } from '../../events'
|
|
25
|
+
|
|
26
|
+
const RESOLVE_NOTIFICATION_SERVICE = resolveNotificationService as jest.MockedFunction<
|
|
27
|
+
typeof resolveNotificationService
|
|
28
|
+
>
|
|
29
|
+
|
|
30
|
+
function makePayload(
|
|
31
|
+
overrides: Partial<AiConversationSharedPayload> = {},
|
|
32
|
+
): AiConversationSharedPayload {
|
|
33
|
+
return {
|
|
34
|
+
conversationId: 'conv-1',
|
|
35
|
+
tenantId: 'tenant-1',
|
|
36
|
+
organizationId: 'org-1',
|
|
37
|
+
ownerUserId: 'user-owner',
|
|
38
|
+
participantUserId: 'user-recipient',
|
|
39
|
+
role: 'viewer',
|
|
40
|
+
...overrides,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeCtx(create: jest.Mock) {
|
|
45
|
+
const container = { resolve: jest.fn() }
|
|
46
|
+
RESOLVE_NOTIFICATION_SERVICE.mockReturnValue({ create } as never)
|
|
47
|
+
return { resolve: (name: string) => container.resolve(name), container }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('conversation-shared-notify subscriber', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
jest.clearAllMocks()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('persists a resolved default-locale title and body, not the raw i18n key', async () => {
|
|
56
|
+
const create = jest.fn(async () => ({ ok: true }))
|
|
57
|
+
const ctx = makeCtx(create)
|
|
58
|
+
|
|
59
|
+
await handleConversationShared(makePayload(), ctx)
|
|
60
|
+
|
|
61
|
+
expect(create).toHaveBeenCalledTimes(1)
|
|
62
|
+
const [input] = create.mock.calls[0]
|
|
63
|
+
expect(input.title).toBe('Conversation shared with you')
|
|
64
|
+
expect(input.body).toBe('An AI conversation has been shared with you.')
|
|
65
|
+
expect(input.titleKey).toBe('ai_assistant.notifications.conversation_shared.title')
|
|
66
|
+
expect(input.bodyKey).toBe('ai_assistant.notifications.conversation_shared.body')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('still calls the notification service with the recipient tenant scope', async () => {
|
|
70
|
+
const create = jest.fn(async () => ({ ok: true }))
|
|
71
|
+
const ctx = makeCtx(create)
|
|
72
|
+
|
|
73
|
+
await handleConversationShared(makePayload(), ctx)
|
|
74
|
+
|
|
75
|
+
const [, scope] = create.mock.calls[0]
|
|
76
|
+
expect(scope).toEqual({ tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('returns early without calling create when participantUserId is missing', async () => {
|
|
80
|
+
const create = jest.fn(async () => ({ ok: true }))
|
|
81
|
+
const ctx = makeCtx(create)
|
|
82
|
+
|
|
83
|
+
await handleConversationShared(
|
|
84
|
+
makePayload({ participantUserId: '' as unknown as string }),
|
|
85
|
+
ctx,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
expect(create).not.toHaveBeenCalled()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('returns early without calling create when tenantId is missing', async () => {
|
|
92
|
+
const create = jest.fn(async () => ({ ok: true }))
|
|
93
|
+
const ctx = makeCtx(create)
|
|
94
|
+
|
|
95
|
+
await handleConversationShared(
|
|
96
|
+
makePayload({ tenantId: '' as unknown as string }),
|
|
97
|
+
ctx,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
expect(create).not.toHaveBeenCalled()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('falls back to the i18n key when the dictionary lookup throws', async () => {
|
|
104
|
+
const i18nServer = jest.requireMock('@open-mercato/shared/lib/i18n/server')
|
|
105
|
+
i18nServer.loadDictionary.mockRejectedValueOnce(new Error('dictionary unavailable'))
|
|
106
|
+
const create = jest.fn(async () => ({ ok: true }))
|
|
107
|
+
const ctx = makeCtx(create)
|
|
108
|
+
|
|
109
|
+
await handleConversationShared(makePayload(), ctx)
|
|
110
|
+
|
|
111
|
+
expect(create).toHaveBeenCalledTimes(1)
|
|
112
|
+
const [input] = create.mock.calls[0]
|
|
113
|
+
expect(input.title).toBe('ai_assistant.notifications.conversation_shared.title')
|
|
114
|
+
expect(input.body).toBe('ai_assistant.notifications.conversation_shared.body')
|
|
115
|
+
})
|
|
116
|
+
})
|