@open-mercato/ui 0.6.2-develop.3467.1.2a1818709d → 0.6.2

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 (71) hide show
  1. package/dist/ai/conversation-store.js +2 -0
  2. package/dist/ai/conversation-store.js.map +2 -2
  3. package/dist/ai/parts/MutationPreviewCard.js +4 -3
  4. package/dist/ai/parts/MutationPreviewCard.js.map +2 -2
  5. package/dist/ai/parts/MutationResultCard.js +9 -16
  6. package/dist/ai/parts/MutationResultCard.js.map +2 -2
  7. package/dist/backend/CrudForm.js +2 -1
  8. package/dist/backend/CrudForm.js.map +2 -2
  9. package/dist/backend/FilterOverlay.js +1 -1
  10. package/dist/backend/FilterOverlay.js.map +1 -1
  11. package/dist/backend/dashboard/widgetRegistry.js +2 -1
  12. package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
  13. package/dist/backend/date-range/dateRanges.js +16 -18
  14. package/dist/backend/date-range/dateRanges.js.map +2 -2
  15. package/dist/backend/detail/AttachmentMetadataDialog.js +3 -1
  16. package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
  17. package/dist/backend/detail/InlineEditors.js +1 -1
  18. package/dist/backend/detail/InlineEditors.js.map +1 -1
  19. package/dist/backend/injection/useAppEvent.js +2 -7
  20. package/dist/backend/injection/useAppEvent.js.map +2 -2
  21. package/dist/backend/injection/widgetRegistry.js +2 -1
  22. package/dist/backend/injection/widgetRegistry.js.map +2 -2
  23. package/dist/backend/messages/useMessageCompose.js +62 -5
  24. package/dist/backend/messages/useMessageCompose.js.map +2 -2
  25. package/dist/backend/messages/useMessageComposeOperations.js +64 -5
  26. package/dist/backend/messages/useMessageComposeOperations.js.map +2 -2
  27. package/dist/backend/schedule/ScheduleCalendar.js +11 -1
  28. package/dist/backend/schedule/ScheduleCalendar.js.map +2 -2
  29. package/dist/backend/schedule/ScheduleToolbar.js +11 -1
  30. package/dist/backend/schedule/ScheduleToolbar.js.map +2 -2
  31. package/dist/primitives/calendar.js +1 -1
  32. package/dist/primitives/calendar.js.map +1 -1
  33. package/dist/primitives/date-format.js +1 -1
  34. package/dist/primitives/date-format.js.map +1 -1
  35. package/dist/primitives/date-picker-helpers.js +7 -9
  36. package/dist/primitives/date-picker-helpers.js.map +2 -2
  37. package/dist/primitives/date-picker.js +1 -1
  38. package/dist/primitives/date-picker.js.map +2 -2
  39. package/dist/primitives/date-range-picker.js +1 -1
  40. package/dist/primitives/date-range-picker.js.map +2 -2
  41. package/jest.config.cjs +4 -1
  42. package/package.json +6 -7
  43. package/src/ai/__tests__/AiAssistantLauncher.test.tsx +1 -1
  44. package/src/ai/__tests__/AiChat.test.tsx +17 -2
  45. package/src/ai/__tests__/conversation-store.test.ts +29 -0
  46. package/src/ai/conversation-store.ts +2 -0
  47. package/src/ai/parts/MutationPreviewCard.tsx +25 -20
  48. package/src/ai/parts/MutationResultCard.tsx +15 -27
  49. package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +26 -1
  50. package/src/ai/parts/__tests__/MutationResultCard.test.tsx +16 -1
  51. package/src/backend/CrudForm.tsx +3 -2
  52. package/src/backend/FilterOverlay.tsx +1 -1
  53. package/src/backend/__tests__/AppShell.test.tsx +1 -1
  54. package/src/backend/__tests__/SidebarCustomizationEditor.test.tsx +8 -1
  55. package/src/backend/dashboard/widgetRegistry.ts +2 -1
  56. package/src/backend/date-range/dateRanges.ts +16 -18
  57. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  58. package/src/backend/detail/InlineEditors.tsx +1 -1
  59. package/src/backend/injection/__tests__/useAppEvent.test.ts +16 -0
  60. package/src/backend/injection/useAppEvent.ts +2 -9
  61. package/src/backend/injection/widgetRegistry.ts +2 -1
  62. package/src/backend/messages/__tests__/MessageComposer.test.tsx +77 -0
  63. package/src/backend/messages/useMessageCompose.ts +64 -5
  64. package/src/backend/messages/useMessageComposeOperations.ts +70 -4
  65. package/src/backend/schedule/ScheduleCalendar.tsx +11 -1
  66. package/src/backend/schedule/ScheduleToolbar.tsx +11 -1
  67. package/src/primitives/calendar.tsx +2 -2
  68. package/src/primitives/date-format.ts +2 -2
  69. package/src/primitives/date-picker-helpers.ts +7 -9
  70. package/src/primitives/date-picker.tsx +2 -2
  71. package/src/primitives/date-range-picker.tsx +2 -2
@@ -24,6 +24,8 @@ async function requestJson(input, init) {
24
24
  ...init,
25
25
  headers: {
26
26
  Accept: "application/json",
27
+ "x-om-forbidden-redirect": "0",
28
+ "x-om-unauthorized-redirect": "0",
27
29
  ...init?.body ? { "Content-Type": "application/json" } : {},
28
30
  ...init?.headers ?? {}
29
31
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/conversation-store.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport type { AiChatMessage, AiChatMessageUiPart } from './useAiChat'\n\nconst CONVERSATIONS_ENDPOINT = '/api/ai_assistant/ai/conversations'\n\nexport interface AiServerConversation {\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 AiServerMessage {\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 interface AiServerConversationListResponse {\n items: AiServerConversation[]\n nextCursor: string | null\n}\n\nexport interface AiServerTranscriptResponse {\n conversation: AiServerConversation\n messages: AiServerMessage[]\n nextCursor: string | null\n}\n\nexport interface AiServerImportResponse {\n conversation: AiServerConversation\n importedMessageCount: number\n skippedMessageCount: number\n}\n\nfunction readString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction buildAttachmentImagePreviewUrl(attachmentId: string): string {\n const params = new URLSearchParams({\n width: '320',\n height: '320',\n cropType: 'contain',\n })\n return `/api/attachments/image/${encodeURIComponent(attachmentId)}?${params.toString()}`\n}\n\nfunction getFetch(): typeof fetch | null {\n const fetchImpl = (globalThis as typeof globalThis & { fetch?: typeof fetch }).fetch\n return typeof fetchImpl === 'function' ? fetchImpl.bind(globalThis) : null\n}\n\nasync function requestJson<T>(\n input: string,\n init?: RequestInit,\n): Promise<{ ok: true; status: number; data: T } | { ok: false; status: number; data: null }> {\n const fetchImpl = getFetch()\n if (!fetchImpl) return { ok: false, status: 0, data: null }\n try {\n const response = await fetchImpl(input, {\n credentials: 'include',\n ...init,\n headers: {\n Accept: 'application/json',\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n ...(init?.headers ?? {}),\n },\n })\n if (!response.ok) return { ok: false, status: response.status, data: null }\n return { ok: true, status: response.status, data: (await response.json()) as T }\n } catch {\n return { ok: false, status: 0, data: null }\n }\n}\n\nexport async function listAiServerConversations(options: {\n agentId?: string | null\n status?: 'open' | 'closed' | null\n limit?: number\n} = {}): Promise<AiServerConversation[] | null> {\n const params = new URLSearchParams()\n if (options.agentId) params.set('agent', options.agentId)\n if (options.status) params.set('status', options.status)\n params.set('limit', String(options.limit ?? 100))\n const result = await requestJson<AiServerConversationListResponse>(\n `${CONVERSATIONS_ENDPOINT}?${params.toString()}`,\n )\n return result.ok ? result.data.items : null\n}\n\nexport async function createAiServerConversation(input: {\n agentId: string\n conversationId: string\n title?: string | null\n pageContext?: Record<string, unknown> | null\n}): Promise<AiServerConversation | null> {\n const result = await requestJson<AiServerConversation>(CONVERSATIONS_ENDPOINT, {\n method: 'POST',\n body: JSON.stringify({\n agentId: input.agentId,\n conversationId: input.conversationId,\n title: input.title ?? undefined,\n pageContext: input.pageContext ?? undefined,\n }),\n })\n return result.ok ? result.data : null\n}\n\nexport async function updateAiServerConversation(\n conversationId: string,\n patch: {\n title?: string | null\n status?: 'open' | 'closed'\n pageContext?: Record<string, unknown> | null\n },\n): Promise<AiServerConversation | null> {\n const result = await requestJson<AiServerConversation>(\n `${CONVERSATIONS_ENDPOINT}/${encodeURIComponent(conversationId)}`,\n {\n method: 'PATCH',\n body: JSON.stringify(patch),\n },\n )\n return result.ok ? result.data : null\n}\n\nexport async function loadAiServerTranscript(\n conversationId: string,\n options: { limit?: number } = {},\n): Promise<AiServerTranscriptResponse | null> {\n const params = new URLSearchParams()\n params.set('limit', String(options.limit ?? 100))\n const result = await requestJson<AiServerTranscriptResponse>(\n `${CONVERSATIONS_ENDPOINT}/${encodeURIComponent(conversationId)}?${params.toString()}`,\n )\n return result.ok ? result.data : null\n}\n\nexport async function importAiLocalConversation(input: {\n agentId: string\n conversationId: string\n title?: string | null\n status?: 'open' | 'closed'\n pageContext?: Record<string, unknown> | null\n messages: AiChatMessage[]\n}): Promise<AiServerImportResponse | null> {\n const result = await requestJson<AiServerImportResponse>(`${CONVERSATIONS_ENDPOINT}/import`, {\n method: 'POST',\n body: JSON.stringify({\n conversation: {\n conversationId: input.conversationId,\n agentId: input.agentId,\n title: input.title ?? undefined,\n status: input.status ?? 'open',\n pageContext: input.pageContext ?? undefined,\n },\n messages: input.messages.slice(-100).map((message) => ({\n clientMessageId: message.id,\n role: message.role,\n content: message.content,\n uiParts: message.uiParts ?? [],\n files: (message.files ?? []).map((file) => ({\n id: file.id,\n name: file.name,\n mimeType: file.type,\n })),\n })),\n }),\n })\n return result.ok ? result.data : null\n}\n\nexport function serverMessageToChatMessage(message: AiServerMessage): AiChatMessage | null {\n if (message.role !== 'user' && message.role !== 'assistant') return null\n const uiParts: AiChatMessageUiPart[] = []\n message.uiParts.forEach((part, index) => {\n if (!part || typeof part !== 'object') return\n const value = part as Record<string, unknown>\n const componentId =\n typeof value.componentId === 'string'\n ? value.componentId\n : typeof value.type === 'string'\n ? value.type\n : null\n if (!componentId) return\n uiParts.push({\n componentId,\n payload: value.payload ?? value.props,\n pendingActionId:\n typeof value.pendingActionId === 'string' ? value.pendingActionId : undefined,\n key:\n typeof value.key === 'string'\n ? value.key\n : `${message.id}:${index}:${componentId}`,\n })\n })\n return {\n id: message.clientMessageId ?? message.id,\n role: message.role,\n content: message.content,\n files: message.files\n .map((file, index) => {\n const name = readString(file.name)\n if (!name) return null\n const type =\n typeof file.mimeType === 'string'\n ? file.mimeType\n : typeof file.type === 'string'\n ? file.type\n : 'application/octet-stream'\n const id = readString(file.id) ?? readString(message.attachmentIds[index])\n const rawPreviewUrl =\n readString(file.previewUrl) ??\n readString(file.thumbnailUrl) ??\n readString(file.url)\n const previewUrl = type.startsWith('image/')\n ? rawPreviewUrl ?? (id ? buildAttachmentImagePreviewUrl(id) : undefined)\n : undefined\n return {\n ...(id ? { id } : {}),\n name,\n type,\n ...(previewUrl ? { previewUrl } : {}),\n }\n })\n .filter((file): file is NonNullable<AiChatMessage['files']>[number] => file !== null),\n uiParts,\n }\n}\n"],
5
- "mappings": ";AAIA,MAAM,yBAAyB;AA6C/B,SAAS,WAAW,OAA+B;AACjD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,+BAA+B,cAA8B;AACpE,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AACD,SAAO,0BAA0B,mBAAmB,YAAY,CAAC,IAAI,OAAO,SAAS,CAAC;AACxF;AAEA,SAAS,WAAgC;AACvC,QAAM,YAAa,WAA4D;AAC/E,SAAO,OAAO,cAAc,aAAa,UAAU,KAAK,UAAU,IAAI;AACxE;AAEA,eAAe,YACb,OACA,MAC4F;AAC5F,QAAM,YAAY,SAAS;AAC3B,MAAI,CAAC,UAAW,QAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,MAAM,KAAK;AAC1D,MAAI;AACF,UAAM,WAAW,MAAM,UAAU,OAAO;AAAA,MACtC,aAAa;AAAA,MACb,GAAG;AAAA,MACH,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,GAAI,MAAM,OAAO,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,QAC3D,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,QAAO,EAAE,IAAI,OAAO,QAAQ,SAAS,QAAQ,MAAM,KAAK;AAC1E,WAAO,EAAE,IAAI,MAAM,QAAQ,SAAS,QAAQ,MAAO,MAAM,SAAS,KAAK,EAAQ;AAAA,EACjF,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,MAAM,KAAK;AAAA,EAC5C;AACF;AAEA,eAAsB,0BAA0B,UAI5C,CAAC,GAA2C;AAC9C,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,QAAS,QAAO,IAAI,SAAS,QAAQ,OAAO;AACxD,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACvD,SAAO,IAAI,SAAS,OAAO,QAAQ,SAAS,GAAG,CAAC;AAChD,QAAM,SAAS,MAAM;AAAA,IACnB,GAAG,sBAAsB,IAAI,OAAO,SAAS,CAAC;AAAA,EAChD;AACA,SAAO,OAAO,KAAK,OAAO,KAAK,QAAQ;AACzC;AAEA,eAAsB,2BAA2B,OAKR;AACvC,QAAM,SAAS,MAAM,YAAkC,wBAAwB;AAAA,IAC7E,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACnB,SAAS,MAAM;AAAA,MACf,gBAAgB,MAAM;AAAA,MACtB,OAAO,MAAM,SAAS;AAAA,MACtB,aAAa,MAAM,eAAe;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACD,SAAO,OAAO,KAAK,OAAO,OAAO;AACnC;AAEA,eAAsB,2BACpB,gBACA,OAKsC;AACtC,QAAM,SAAS,MAAM;AAAA,IACnB,GAAG,sBAAsB,IAAI,mBAAmB,cAAc,CAAC;AAAA,IAC/D;AAAA,MACE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,OAAO,KAAK,OAAO,OAAO;AACnC;AAEA,eAAsB,uBACpB,gBACA,UAA8B,CAAC,GACa;AAC5C,QAAM,SAAS,IAAI,gBAAgB;AACnC,SAAO,IAAI,SAAS,OAAO,QAAQ,SAAS,GAAG,CAAC;AAChD,QAAM,SAAS,MAAM;AAAA,IACnB,GAAG,sBAAsB,IAAI,mBAAmB,cAAc,CAAC,IAAI,OAAO,SAAS,CAAC;AAAA,EACtF;AACA,SAAO,OAAO,KAAK,OAAO,OAAO;AACnC;AAEA,eAAsB,0BAA0B,OAOL;AACzC,QAAM,SAAS,MAAM,YAAoC,GAAG,sBAAsB,WAAW;AAAA,IAC3F,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACnB,cAAc;AAAA,QACZ,gBAAgB,MAAM;AAAA,QACtB,SAAS,MAAM;AAAA,QACf,OAAO,MAAM,SAAS;AAAA,QACtB,QAAQ,MAAM,UAAU;AAAA,QACxB,aAAa,MAAM,eAAe;AAAA,MACpC;AAAA,MACA,UAAU,MAAM,SAAS,MAAM,IAAI,EAAE,IAAI,CAAC,aAAa;AAAA,QACrD,iBAAiB,QAAQ;AAAA,QACzB,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,SAAS,QAAQ,WAAW,CAAC;AAAA,QAC7B,QAAQ,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,UAAU;AAAA,UAC1C,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,QACjB,EAAE;AAAA,MACJ,EAAE;AAAA,IACJ,CAAC;AAAA,EACH,CAAC;AACD,SAAO,OAAO,KAAK,OAAO,OAAO;AACnC;AAEO,SAAS,2BAA2B,SAAgD;AACzF,MAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,YAAa,QAAO;AACpE,QAAM,UAAiC,CAAC;AACxC,UAAQ,QAAQ,QAAQ,CAAC,MAAM,UAAU;AACvC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,QAAQ;AACd,UAAM,cACJ,OAAO,MAAM,gBAAgB,WACzB,MAAM,cACN,OAAO,MAAM,SAAS,WACpB,MAAM,OACN;AACR,QAAI,CAAC,YAAa;AAClB,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,SAAS,MAAM,WAAW,MAAM;AAAA,MAChC,iBACE,OAAO,MAAM,oBAAoB,WAAW,MAAM,kBAAkB;AAAA,MACtE,KACE,OAAO,MAAM,QAAQ,WACjB,MAAM,MACN,GAAG,QAAQ,EAAE,IAAI,KAAK,IAAI,WAAW;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AAAA,IACL,IAAI,QAAQ,mBAAmB,QAAQ;AAAA,IACvC,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,IACjB,OAAO,QAAQ,MACZ,IAAI,CAAC,MAAM,UAAU;AACpB,YAAM,OAAO,WAAW,KAAK,IAAI;AACjC,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,OACJ,OAAO,KAAK,aAAa,WACrB,KAAK,WACL,OAAO,KAAK,SAAS,WACnB,KAAK,OACL;AACR,YAAM,KAAK,WAAW,KAAK,EAAE,KAAK,WAAW,QAAQ,cAAc,KAAK,CAAC;AACzE,YAAM,gBACJ,WAAW,KAAK,UAAU,KAC1B,WAAW,KAAK,YAAY,KAC5B,WAAW,KAAK,GAAG;AACrB,YAAM,aAAa,KAAK,WAAW,QAAQ,IACvC,kBAAkB,KAAK,+BAA+B,EAAE,IAAI,UAC5D;AACJ,aAAO;AAAA,QACL,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,QACnB;AAAA,QACA;AAAA,QACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACrC;AAAA,IACF,CAAC,EACA,OAAO,CAAC,SAA8D,SAAS,IAAI;AAAA,IACtF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["\"use client\"\n\nimport type { AiChatMessage, AiChatMessageUiPart } from './useAiChat'\n\nconst CONVERSATIONS_ENDPOINT = '/api/ai_assistant/ai/conversations'\n\nexport interface AiServerConversation {\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 AiServerMessage {\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 interface AiServerConversationListResponse {\n items: AiServerConversation[]\n nextCursor: string | null\n}\n\nexport interface AiServerTranscriptResponse {\n conversation: AiServerConversation\n messages: AiServerMessage[]\n nextCursor: string | null\n}\n\nexport interface AiServerImportResponse {\n conversation: AiServerConversation\n importedMessageCount: number\n skippedMessageCount: number\n}\n\nfunction readString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction buildAttachmentImagePreviewUrl(attachmentId: string): string {\n const params = new URLSearchParams({\n width: '320',\n height: '320',\n cropType: 'contain',\n })\n return `/api/attachments/image/${encodeURIComponent(attachmentId)}?${params.toString()}`\n}\n\nfunction getFetch(): typeof fetch | null {\n const fetchImpl = (globalThis as typeof globalThis & { fetch?: typeof fetch }).fetch\n return typeof fetchImpl === 'function' ? fetchImpl.bind(globalThis) : null\n}\n\nasync function requestJson<T>(\n input: string,\n init?: RequestInit,\n): Promise<{ ok: true; status: number; data: T } | { ok: false; status: number; data: null }> {\n const fetchImpl = getFetch()\n if (!fetchImpl) return { ok: false, status: 0, data: null }\n try {\n const response = await fetchImpl(input, {\n credentials: 'include',\n ...init,\n headers: {\n Accept: 'application/json',\n 'x-om-forbidden-redirect': '0',\n 'x-om-unauthorized-redirect': '0',\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n ...(init?.headers ?? {}),\n },\n })\n if (!response.ok) return { ok: false, status: response.status, data: null }\n return { ok: true, status: response.status, data: (await response.json()) as T }\n } catch {\n return { ok: false, status: 0, data: null }\n }\n}\n\nexport async function listAiServerConversations(options: {\n agentId?: string | null\n status?: 'open' | 'closed' | null\n limit?: number\n} = {}): Promise<AiServerConversation[] | null> {\n const params = new URLSearchParams()\n if (options.agentId) params.set('agent', options.agentId)\n if (options.status) params.set('status', options.status)\n params.set('limit', String(options.limit ?? 100))\n const result = await requestJson<AiServerConversationListResponse>(\n `${CONVERSATIONS_ENDPOINT}?${params.toString()}`,\n )\n return result.ok ? result.data.items : null\n}\n\nexport async function createAiServerConversation(input: {\n agentId: string\n conversationId: string\n title?: string | null\n pageContext?: Record<string, unknown> | null\n}): Promise<AiServerConversation | null> {\n const result = await requestJson<AiServerConversation>(CONVERSATIONS_ENDPOINT, {\n method: 'POST',\n body: JSON.stringify({\n agentId: input.agentId,\n conversationId: input.conversationId,\n title: input.title ?? undefined,\n pageContext: input.pageContext ?? undefined,\n }),\n })\n return result.ok ? result.data : null\n}\n\nexport async function updateAiServerConversation(\n conversationId: string,\n patch: {\n title?: string | null\n status?: 'open' | 'closed'\n pageContext?: Record<string, unknown> | null\n },\n): Promise<AiServerConversation | null> {\n const result = await requestJson<AiServerConversation>(\n `${CONVERSATIONS_ENDPOINT}/${encodeURIComponent(conversationId)}`,\n {\n method: 'PATCH',\n body: JSON.stringify(patch),\n },\n )\n return result.ok ? result.data : null\n}\n\nexport async function loadAiServerTranscript(\n conversationId: string,\n options: { limit?: number } = {},\n): Promise<AiServerTranscriptResponse | null> {\n const params = new URLSearchParams()\n params.set('limit', String(options.limit ?? 100))\n const result = await requestJson<AiServerTranscriptResponse>(\n `${CONVERSATIONS_ENDPOINT}/${encodeURIComponent(conversationId)}?${params.toString()}`,\n )\n return result.ok ? result.data : null\n}\n\nexport async function importAiLocalConversation(input: {\n agentId: string\n conversationId: string\n title?: string | null\n status?: 'open' | 'closed'\n pageContext?: Record<string, unknown> | null\n messages: AiChatMessage[]\n}): Promise<AiServerImportResponse | null> {\n const result = await requestJson<AiServerImportResponse>(`${CONVERSATIONS_ENDPOINT}/import`, {\n method: 'POST',\n body: JSON.stringify({\n conversation: {\n conversationId: input.conversationId,\n agentId: input.agentId,\n title: input.title ?? undefined,\n status: input.status ?? 'open',\n pageContext: input.pageContext ?? undefined,\n },\n messages: input.messages.slice(-100).map((message) => ({\n clientMessageId: message.id,\n role: message.role,\n content: message.content,\n uiParts: message.uiParts ?? [],\n files: (message.files ?? []).map((file) => ({\n id: file.id,\n name: file.name,\n mimeType: file.type,\n })),\n })),\n }),\n })\n return result.ok ? result.data : null\n}\n\nexport function serverMessageToChatMessage(message: AiServerMessage): AiChatMessage | null {\n if (message.role !== 'user' && message.role !== 'assistant') return null\n const uiParts: AiChatMessageUiPart[] = []\n message.uiParts.forEach((part, index) => {\n if (!part || typeof part !== 'object') return\n const value = part as Record<string, unknown>\n const componentId =\n typeof value.componentId === 'string'\n ? value.componentId\n : typeof value.type === 'string'\n ? value.type\n : null\n if (!componentId) return\n uiParts.push({\n componentId,\n payload: value.payload ?? value.props,\n pendingActionId:\n typeof value.pendingActionId === 'string' ? value.pendingActionId : undefined,\n key:\n typeof value.key === 'string'\n ? value.key\n : `${message.id}:${index}:${componentId}`,\n })\n })\n return {\n id: message.clientMessageId ?? message.id,\n role: message.role,\n content: message.content,\n files: message.files\n .map((file, index) => {\n const name = readString(file.name)\n if (!name) return null\n const type =\n typeof file.mimeType === 'string'\n ? file.mimeType\n : typeof file.type === 'string'\n ? file.type\n : 'application/octet-stream'\n const id = readString(file.id) ?? readString(message.attachmentIds[index])\n const rawPreviewUrl =\n readString(file.previewUrl) ??\n readString(file.thumbnailUrl) ??\n readString(file.url)\n const previewUrl = type.startsWith('image/')\n ? rawPreviewUrl ?? (id ? buildAttachmentImagePreviewUrl(id) : undefined)\n : undefined\n return {\n ...(id ? { id } : {}),\n name,\n type,\n ...(previewUrl ? { previewUrl } : {}),\n }\n })\n .filter((file): file is NonNullable<AiChatMessage['files']>[number] => file !== null),\n uiParts,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -107,6 +107,7 @@ function MutationPreviewCard(props) {
107
107
  }
108
108
  const batch = Array.isArray(action?.records) && action.records.length > 0 ? action.records : null;
109
109
  const summary = batch ? summarizeBatch(batch) : null;
110
+ const hasReviewDetails = Boolean(batch);
110
111
  return /* @__PURE__ */ jsxs(
111
112
  "section",
112
113
  {
@@ -152,7 +153,7 @@ function MutationPreviewCard(props) {
152
153
  ]
153
154
  }
154
155
  ) : /* @__PURE__ */ jsx(FieldDiffCard, { fieldDiff: action?.fieldDiff ?? null }) }),
155
- expanded ? /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-md border border-border bg-muted/20 p-3", "data-ai-mutation-preview-details": true, children: /* @__PURE__ */ jsx(
156
+ expanded && hasReviewDetails ? /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-md border border-border bg-muted/20 p-3", "data-ai-mutation-preview-details": true, children: /* @__PURE__ */ jsx(
156
157
  FieldDiffCard,
157
158
  {
158
159
  fieldDiff: action?.fieldDiff ?? null,
@@ -160,7 +161,7 @@ function MutationPreviewCard(props) {
160
161
  }
161
162
  ) }) : null,
162
163
  /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
163
- /* @__PURE__ */ jsxs(
164
+ hasReviewDetails ? /* @__PURE__ */ jsxs(
164
165
  Button,
165
166
  {
166
167
  type: "button",
@@ -183,7 +184,7 @@ function MutationPreviewCard(props) {
183
184
  )
184
185
  ]
185
186
  }
186
- ),
187
+ ) : /* @__PURE__ */ jsx("span", { "aria-hidden": true }),
187
188
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
188
189
  /* @__PURE__ */ jsx(
189
190
  Button,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/ai/parts/MutationPreviewCard.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown, Eye, ShieldAlert } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { useAiShortcuts } from '../useAiShortcuts'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { confirmPendingAction, cancelPendingAction } from './pending-action-api'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport { FieldDiffCard } from './FieldDiffCard'\nimport { ConfirmationCard } from './ConfirmationCard'\nimport { MutationResultCard } from './MutationResultCard'\nimport type { AiPendingActionCardAction } from './types'\n\n/**\n * Mutation-approval preview card. Rendered by the server-emitted\n * `mutation-preview-card` UI part (spec \u00A79.2).\n *\n * Responsibilities:\n * - Fetch the current pending-action row via the shared polling hook so the\n * card recovers state on page reload (reconnect behavior).\n * - Render the top-level `fieldDiff` OR a per-record `records[]` summary\n * with a drill-in link.\n * - Provide `Confirm` / `Cancel` / `Review Details` actions with shared\n * keyboard shortcuts (`Cmd/Ctrl+Enter` \u2192 confirm, `Escape` \u2192 cancel).\n * - Flip to the {@link ConfirmationCard} once the user confirms, and\n * further to the {@link MutationResultCard} once the row becomes\n * terminal.\n */\nexport interface MutationPreviewCardPayload {\n /** Optional server-serialized pending action snapshot for the initial render. */\n pendingAction?: AiPendingActionCardAction\n}\n\nexport interface MutationPreviewCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Endpoint base override for tests. */\n endpoint?: string\n}\n\nfunction summarizeBatch(\n records: NonNullable<AiPendingActionCardAction['records']>,\n): { count: number; labels: string[] } {\n const labels = records.slice(0, 3).map((record) => record.label)\n return { count: records.length, labels }\n}\n\nexport function MutationPreviewCard(props: MutationPreviewCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationPreviewCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled, refresh } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId,\n })\n const action = polled ?? injected\n\n const [expanded, setExpanded] = React.useState(false)\n const [phase, setPhase] = React.useState<'preview' | 'confirming'>('preview')\n const [confirmError, setConfirmError] = React.useState<{\n status: number\n code?: string\n message: string\n extra?: Record<string, unknown>\n } | null>(null)\n\n const handleConfirm = React.useCallback(async () => {\n if (!pendingActionId) return\n if (phase !== 'preview') return\n setPhase('confirming')\n setConfirmError(null)\n const result = await confirmPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n // Network / timeout / 4xx / 5xx \u2014 surface the envelope and rewind the\n // card to the preview phase so the operator can read the error,\n // edit the proposal upstream, or retry.\n setConfirmError(result.error)\n setPhase('preview')\n await refresh()\n return\n }\n // HTTP 200 path. The dispatcher returns `ok: false` AND a populated\n // `mutationResult.error` when the wrapped tool handler failed inside\n // the confirm route \u2014 the row is already in a terminal state but the\n // overall HTTP call succeeded. Treat that as a confirm error too so\n // the alert renders inline instead of leaving the card on the\n // generic \"applying\u2026\" spinner forever.\n const handlerError = result.data?.mutationResult?.error\n if (result.data?.ok === false || handlerError) {\n const mappedCode =\n typeof handlerError?.code === 'string' && handlerError.code.length > 0\n ? handlerError.code\n : 'execution_failed'\n setConfirmError({\n status: 200,\n code: mappedCode,\n message:\n handlerError?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.preview.handlerError',\n 'The mutation handler reported an error. Review the details and re-propose if needed.',\n ),\n })\n }\n await refresh()\n }, [pendingActionId, phase, props.endpoint, refresh, t])\n\n const handleCancel = React.useCallback(async () => {\n if (!pendingActionId) return\n const result = await cancelPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n setConfirmError(result.error)\n }\n await refresh()\n }, [pendingActionId, props.endpoint, refresh])\n\n const currentStatus = action?.status ?? null\n const executionError = action?.executionResult?.error\n // Treat any captured handler error as terminal too \u2014 the dispatcher\n // sometimes returns the action before the row has fully transitioned\n // out of `executing`, and we must never leave the spinner masking a\n // real failure. The MutationResultCard's failure path renders the\n // error envelope as long as `executionResult.error` is set.\n const isTerminal =\n currentStatus === 'confirmed' ||\n currentStatus === 'failed' ||\n currentStatus === 'cancelled' ||\n currentStatus === 'expired' ||\n Boolean(executionError)\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: () => {\n void handleConfirm()\n },\n onCancel: () => {\n void handleCancel()\n },\n enabled: phase === 'preview' && !isTerminal,\n })\n\n // Terminal \u2014 short-circuit into the result card.\n if (isTerminal && action) {\n return (\n <MutationResultCard\n componentId=\"mutation-result-card\"\n pendingActionId={pendingActionId}\n initialAction={action}\n endpoint={props.endpoint}\n />\n )\n }\n\n // Confirming \u2014 flip to the spinner card. Propagate the confirmError so\n // the user sees the structured envelope even though the confirm call has\n // already resolved.\n if (phase === 'confirming') {\n return (\n <ConfirmationCard\n componentId=\"confirmation-card\"\n pendingActionId={pendingActionId}\n initialAction={action ?? undefined}\n endpoint={props.endpoint}\n payload={{\n sideEffectsSummary: action?.sideEffectsSummary ?? null,\n pendingAction: action ?? undefined,\n confirmError: confirmError ?? undefined,\n }}\n />\n )\n }\n\n const batch = Array.isArray(action?.records) && action!.records!.length > 0 ? action!.records! : null\n const summary = batch ? summarizeBatch(batch) : null\n\n return (\n <section\n className=\"rounded-md border border-border bg-background p-4 text-sm outline-none\"\n tabIndex={0}\n onKeyDown={handleKeyDown}\n data-ai-mutation-preview\n data-ai-mutation-preview-mode={batch ? 'batch' : 'single'}\n >\n <header className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-2\">\n <ShieldAlert className=\"mt-0.5 size-4 text-status-warning-icon\" aria-hidden />\n <div>\n <h4 className=\"text-sm font-semibold\">\n {t(\n 'ai_assistant.chat.mutation_cards.preview.title',\n 'Review proposed changes',\n )}\n </h4>\n {action?.sideEffectsSummary ? (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {action.sideEffectsSummary}\n </p>\n ) : null}\n </div>\n </div>\n </header>\n\n <div className=\"mt-3\" data-ai-mutation-preview-body>\n {summary ? (\n <div\n className=\"rounded-md border border-border bg-muted/30 p-3 text-sm\"\n data-ai-mutation-preview-batch-summary\n >\n <p className=\"font-medium\">\n {t(\n 'ai_assistant.chat.mutation_cards.preview.batchSummary',\n 'Batch update',\n )}\n {': '}\n <span data-ai-mutation-preview-count>{summary.count}</span>{' '}\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.preview.batchRecords',\n 'records',\n )}\n </span>\n </p>\n {summary.labels.length > 0 ? (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {summary.labels.join(', ')}\n {summary.count > summary.labels.length\n ? ` +${summary.count - summary.labels.length}`\n : ''}\n </p>\n ) : null}\n </div>\n ) : (\n <FieldDiffCard fieldDiff={action?.fieldDiff ?? null} />\n )}\n </div>\n\n {expanded ? (\n <div className=\"mt-3 rounded-md border border-border bg-muted/20 p-3\" data-ai-mutation-preview-details>\n <FieldDiffCard\n fieldDiff={action?.fieldDiff ?? null}\n records={action?.records ?? null}\n />\n </div>\n ) : null}\n\n <div className=\"mt-3 flex items-center justify-between gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setExpanded((value) => !value)}\n data-ai-mutation-preview-review\n >\n <Eye className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.preview.reviewDetails',\n 'Review details',\n )}\n </span>\n <ChevronDown\n className={`size-4 transition-transform ${expanded ? 'rotate-180' : ''}`}\n aria-hidden\n />\n </Button>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n void handleCancel()\n }}\n data-ai-mutation-preview-cancel\n >\n {t('ai_assistant.chat.mutation_cards.preview.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n void handleConfirm()\n }}\n data-ai-mutation-preview-confirm\n >\n {t('ai_assistant.chat.mutation_cards.preview.confirm', 'Confirm')}\n </Button>\n </div>\n </div>\n </section>\n )\n}\n\nexport default MutationPreviewCard\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown, Eye, ShieldAlert } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { useAiShortcuts } from '../useAiShortcuts'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { confirmPendingAction, cancelPendingAction } from './pending-action-api'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport { FieldDiffCard } from './FieldDiffCard'\nimport { ConfirmationCard } from './ConfirmationCard'\nimport { MutationResultCard } from './MutationResultCard'\nimport type { AiPendingActionCardAction } from './types'\n\n/**\n * Mutation-approval preview card. Rendered by the server-emitted\n * `mutation-preview-card` UI part (spec \u00A79.2).\n *\n * Responsibilities:\n * - Fetch the current pending-action row via the shared polling hook so the\n * card recovers state on page reload (reconnect behavior).\n * - Render the top-level `fieldDiff` OR a per-record `records[]` summary\n * with a drill-in link.\n * - Provide `Confirm` / `Cancel` / `Review Details` actions with shared\n * keyboard shortcuts (`Cmd/Ctrl+Enter` \u2192 confirm, `Escape` \u2192 cancel).\n * - Flip to the {@link ConfirmationCard} once the user confirms, and\n * further to the {@link MutationResultCard} once the row becomes\n * terminal.\n */\nexport interface MutationPreviewCardPayload {\n /** Optional server-serialized pending action snapshot for the initial render. */\n pendingAction?: AiPendingActionCardAction\n}\n\nexport interface MutationPreviewCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Endpoint base override for tests. */\n endpoint?: string\n}\n\nfunction summarizeBatch(\n records: NonNullable<AiPendingActionCardAction['records']>,\n): { count: number; labels: string[] } {\n const labels = records.slice(0, 3).map((record) => record.label)\n return { count: records.length, labels }\n}\n\nexport function MutationPreviewCard(props: MutationPreviewCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationPreviewCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled, refresh } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId,\n })\n const action = polled ?? injected\n\n const [expanded, setExpanded] = React.useState(false)\n const [phase, setPhase] = React.useState<'preview' | 'confirming'>('preview')\n const [confirmError, setConfirmError] = React.useState<{\n status: number\n code?: string\n message: string\n extra?: Record<string, unknown>\n } | null>(null)\n\n const handleConfirm = React.useCallback(async () => {\n if (!pendingActionId) return\n if (phase !== 'preview') return\n setPhase('confirming')\n setConfirmError(null)\n const result = await confirmPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n // Network / timeout / 4xx / 5xx \u2014 surface the envelope and rewind the\n // card to the preview phase so the operator can read the error,\n // edit the proposal upstream, or retry.\n setConfirmError(result.error)\n setPhase('preview')\n await refresh()\n return\n }\n // HTTP 200 path. The dispatcher returns `ok: false` AND a populated\n // `mutationResult.error` when the wrapped tool handler failed inside\n // the confirm route \u2014 the row is already in a terminal state but the\n // overall HTTP call succeeded. Treat that as a confirm error too so\n // the alert renders inline instead of leaving the card on the\n // generic \"applying\u2026\" spinner forever.\n const handlerError = result.data?.mutationResult?.error\n if (result.data?.ok === false || handlerError) {\n const mappedCode =\n typeof handlerError?.code === 'string' && handlerError.code.length > 0\n ? handlerError.code\n : 'execution_failed'\n setConfirmError({\n status: 200,\n code: mappedCode,\n message:\n handlerError?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.preview.handlerError',\n 'The mutation handler reported an error. Review the details and re-propose if needed.',\n ),\n })\n }\n await refresh()\n }, [pendingActionId, phase, props.endpoint, refresh, t])\n\n const handleCancel = React.useCallback(async () => {\n if (!pendingActionId) return\n const result = await cancelPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n setConfirmError(result.error)\n }\n await refresh()\n }, [pendingActionId, props.endpoint, refresh])\n\n const currentStatus = action?.status ?? null\n const executionError = action?.executionResult?.error\n // Treat any captured handler error as terminal too \u2014 the dispatcher\n // sometimes returns the action before the row has fully transitioned\n // out of `executing`, and we must never leave the spinner masking a\n // real failure. The MutationResultCard's failure path renders the\n // error envelope as long as `executionResult.error` is set.\n const isTerminal =\n currentStatus === 'confirmed' ||\n currentStatus === 'failed' ||\n currentStatus === 'cancelled' ||\n currentStatus === 'expired' ||\n Boolean(executionError)\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: () => {\n void handleConfirm()\n },\n onCancel: () => {\n void handleCancel()\n },\n enabled: phase === 'preview' && !isTerminal,\n })\n\n // Terminal \u2014 short-circuit into the result card.\n if (isTerminal && action) {\n return (\n <MutationResultCard\n componentId=\"mutation-result-card\"\n pendingActionId={pendingActionId}\n initialAction={action}\n endpoint={props.endpoint}\n />\n )\n }\n\n // Confirming \u2014 flip to the spinner card. Propagate the confirmError so\n // the user sees the structured envelope even though the confirm call has\n // already resolved.\n if (phase === 'confirming') {\n return (\n <ConfirmationCard\n componentId=\"confirmation-card\"\n pendingActionId={pendingActionId}\n initialAction={action ?? undefined}\n endpoint={props.endpoint}\n payload={{\n sideEffectsSummary: action?.sideEffectsSummary ?? null,\n pendingAction: action ?? undefined,\n confirmError: confirmError ?? undefined,\n }}\n />\n )\n }\n\n const batch = Array.isArray(action?.records) && action!.records!.length > 0 ? action!.records! : null\n const summary = batch ? summarizeBatch(batch) : null\n const hasReviewDetails = Boolean(batch)\n\n return (\n <section\n className=\"rounded-md border border-border bg-background p-4 text-sm outline-none\"\n tabIndex={0}\n onKeyDown={handleKeyDown}\n data-ai-mutation-preview\n data-ai-mutation-preview-mode={batch ? 'batch' : 'single'}\n >\n <header className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-2\">\n <ShieldAlert className=\"mt-0.5 size-4 text-status-warning-icon\" aria-hidden />\n <div>\n <h4 className=\"text-sm font-semibold\">\n {t(\n 'ai_assistant.chat.mutation_cards.preview.title',\n 'Review proposed changes',\n )}\n </h4>\n {action?.sideEffectsSummary ? (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {action.sideEffectsSummary}\n </p>\n ) : null}\n </div>\n </div>\n </header>\n\n <div className=\"mt-3\" data-ai-mutation-preview-body>\n {summary ? (\n <div\n className=\"rounded-md border border-border bg-muted/30 p-3 text-sm\"\n data-ai-mutation-preview-batch-summary\n >\n <p className=\"font-medium\">\n {t(\n 'ai_assistant.chat.mutation_cards.preview.batchSummary',\n 'Batch update',\n )}\n {': '}\n <span data-ai-mutation-preview-count>{summary.count}</span>{' '}\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.preview.batchRecords',\n 'records',\n )}\n </span>\n </p>\n {summary.labels.length > 0 ? (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {summary.labels.join(', ')}\n {summary.count > summary.labels.length\n ? ` +${summary.count - summary.labels.length}`\n : ''}\n </p>\n ) : null}\n </div>\n ) : (\n <FieldDiffCard fieldDiff={action?.fieldDiff ?? null} />\n )}\n </div>\n\n {expanded && hasReviewDetails ? (\n <div className=\"mt-3 rounded-md border border-border bg-muted/20 p-3\" data-ai-mutation-preview-details>\n <FieldDiffCard\n fieldDiff={action?.fieldDiff ?? null}\n records={action?.records ?? null}\n />\n </div>\n ) : null}\n\n <div className=\"mt-3 flex items-center justify-between gap-2\">\n {hasReviewDetails ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setExpanded((value) => !value)}\n data-ai-mutation-preview-review\n >\n <Eye className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.preview.reviewDetails',\n 'Review details',\n )}\n </span>\n <ChevronDown\n className={`size-4 transition-transform ${expanded ? 'rotate-180' : ''}`}\n aria-hidden\n />\n </Button>\n ) : (\n <span aria-hidden />\n )}\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n void handleCancel()\n }}\n data-ai-mutation-preview-cancel\n >\n {t('ai_assistant.chat.mutation_cards.preview.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n void handleConfirm()\n }}\n data-ai-mutation-preview-confirm\n >\n {t('ai_assistant.chat.mutation_cards.preview.confirm', 'Confirm')}\n </Button>\n </div>\n </div>\n </section>\n )\n}\n\nexport default MutationPreviewCard\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -67,29 +67,22 @@ function MutationResultCard(props) {
67
67
  return /* @__PURE__ */ jsxs(Alert, { variant: "success", "data-ai-mutation-result": "success", children: [
68
68
  /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.chat.mutation_cards.result.successTitle", "Action applied") }),
69
69
  /* @__PURE__ */ jsxs("div", { className: "text-sm leading-relaxed", children: [
70
- /* @__PURE__ */ jsx("p", { children: result?.commandName ? t(
71
- "ai_assistant.chat.mutation_cards.result.successWithCommand",
72
- "Completed"
73
- ) + `: ${result.commandName}` : t(
70
+ /* @__PURE__ */ jsx("p", { children: t(
74
71
  "ai_assistant.chat.mutation_cards.result.successBody",
75
- "The mutation completed successfully."
72
+ "The requested change was saved successfully."
76
73
  ) }),
77
- recordId ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs", children: href ? /* @__PURE__ */ jsxs(
74
+ recordId && href ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs", children: /* @__PURE__ */ jsx(
78
75
  "a",
79
76
  {
80
- className: "font-mono text-primary underline",
77
+ className: "text-primary underline",
81
78
  href,
82
79
  "data-ai-mutation-result-link": true,
83
- children: [
84
- t(
85
- "ai_assistant.chat.mutation_cards.result.viewRecord",
86
- "View record"
87
- ),
88
- ": ",
89
- recordId
90
- ]
80
+ children: t(
81
+ "ai_assistant.chat.mutation_cards.result.viewRecord",
82
+ "View record"
83
+ )
91
84
  }
92
- ) : /* @__PURE__ */ jsx("span", { className: "font-mono", "data-ai-mutation-result-record-id": true, children: recordId }) }) : null
85
+ ) }) : null
93
86
  ] })
94
87
  ] });
95
88
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/ai/parts/MutationResultCard.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Wand2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport type { AiPendingActionCardAction, AiPendingActionCardStatus } from './types'\n\n/** Custom DOM event the failure card dispatches when the operator clicks\n * \"Fix with AI\". `<AiChat>` listens for this and sends a follow-up user\n * message asking the agent to diagnose and retry the failed call. */\nexport const AI_CHAT_FIX_REQUEST_EVENT = 'om-ai-chat-fix-request'\n\nexport interface AiChatFixRequestDetail {\n message: string\n toolName?: string\n pendingActionId?: string\n}\n\nfunction dispatchFixRequest(detail: AiChatFixRequestDetail): void {\n if (typeof window === 'undefined') return\n window.dispatchEvent(new CustomEvent<AiChatFixRequestDetail>(AI_CHAT_FIX_REQUEST_EVENT, { detail }))\n}\n\n/**\n * Terminal-state card that renders the `executionResult` of a pending\n * action. Success \u2192 `Alert variant=\"success\"` with a record link; partial\n * success (batch `failedRecords[]`) \u2192 `variant=\"warning\"` with the list;\n * failure \u2192 `variant=\"destructive\"` with the error code + message.\n *\n * Reads the pending action via the shared polling hook so page reloads\n * still recover state. The hook short-circuits once the row is terminal\n * (spec's reconnect behavior), which is always the case for this card.\n */\nexport interface MutationResultCardPayload {\n /** Server-serialized pending action snapshot (optional \u2014 the hook refetches). */\n pendingAction?: AiPendingActionCardAction\n /** Optional link target for the success record. */\n recordHref?: string\n}\n\nexport interface MutationResultCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Poll endpoint override for tests. */\n endpoint?: string\n}\n\nfunction isSuccessStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'confirmed'\n}\n\nfunction isFailureStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'failed' || status === 'cancelled' || status === 'expired'\n}\n\nexport function MutationResultCard(props: MutationResultCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationResultCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId || Boolean(injected),\n })\n const action = injected ?? polled\n const status = action?.status ?? null\n const failedRecords = action?.failedRecords ?? null\n const result = action?.executionResult ?? null\n\n if (!action) {\n return null\n }\n\n if (isSuccessStatus(status) && failedRecords && failedRecords.length > 0) {\n return (\n <Alert variant=\"warning\" data-ai-mutation-result=\"partial\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialTitle',\n 'Action applied with failures',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialBody',\n 'Some records could not be updated.',\n )}\n </p>\n <ul\n className=\"mt-2 list-disc space-y-1 pl-5 text-xs\"\n data-ai-mutation-failed-records\n >\n {failedRecords.map((record) => (\n <li key={record.recordId} data-ai-mutation-failed-record={record.recordId}>\n <span className=\"font-mono\">{record.recordId}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2022</span>\n <span className=\"font-mono\">{record.error.code}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2014</span>\n <span>{record.error.message}</span>\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )\n }\n\n if (isSuccessStatus(status)) {\n const recordId = result?.recordId ?? action.targetRecordId ?? null\n const href = payload.recordHref ?? null\n return (\n <Alert variant=\"success\" data-ai-mutation-result=\"success\">\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.result.successTitle', 'Action applied')}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {result?.commandName\n ? t(\n 'ai_assistant.chat.mutation_cards.result.successWithCommand',\n 'Completed',\n ) + `: ${result.commandName}`\n : t(\n 'ai_assistant.chat.mutation_cards.result.successBody',\n 'The mutation completed successfully.',\n )}\n </p>\n {recordId ? (\n <p className=\"mt-1 text-xs\">\n {href ? (\n <a\n className=\"font-mono text-primary underline\"\n href={href}\n data-ai-mutation-result-link\n >\n {t(\n 'ai_assistant.chat.mutation_cards.result.viewRecord',\n 'View record',\n )}\n : {recordId}\n </a>\n ) : (\n <span className=\"font-mono\" data-ai-mutation-result-record-id>\n {recordId}\n </span>\n )}\n </p>\n ) : null}\n </div>\n </Alert>\n )\n }\n\n // Render the failure alert when the action is in a failure status OR when\n // the dispatcher already captured an `executionResult.error` (the row may\n // not yet have transitioned out of `executing` in the polling snapshot,\n // but the handler error is authoritative \u2014 surface it immediately so the\n // operator never sees a stuck \"applying\u2026\" state silently masking a real\n // failure).\n if (isFailureStatus(status) || result?.error) {\n const code = result?.error?.code ?? status ?? 'failed'\n const message =\n result?.error?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.result.failureBody',\n 'The mutation could not be applied.',\n )\n const errorObj = result?.error\n const errorDetails = errorObj?.details\n const errorInput = errorObj?.input\n const errorName = errorObj?.name\n const onFixWithAi = () => {\n // Build a structured prompt that gives the agent enough context to\n // diagnose and retry without copy/paste from the operator. Keeping\n // it explicit (\"retry with corrected arguments\") nudges the model\n // away from re-issuing the same args (which would just hit the\n // same error). The repository's idempotency check only dedupes\n // active `pending` rows, so a fresh prepareMutation call after a\n // terminal failure always produces a new pending action \u2014 the\n // retry is never silently collapsed.\n //\n // The prompt now embeds the full structured failure context the\n // server captured (Zod issues / fieldErrors, original arguments,\n // failedRecords for batch tools, error name + cause). Without this\n // the operator routinely saw \"Invalid input\" with no field path \u2014\n // the model literally could not fix what it could not see.\n const promptLines: string[] = [\n `The previous call to tool \"${action.toolName}\" failed.`,\n `Error: ${code} \u2014 ${message}.`,\n ]\n if (errorName && errorName !== 'Error') {\n promptLines.push(`Error class: ${errorName}.`)\n }\n if (action.targetEntityType || action.targetRecordId) {\n promptLines.push(\n `Target: ${action.targetEntityType ?? '?'}${action.targetRecordId ? ' / ' + action.targetRecordId : ''}.`,\n )\n }\n\n // Field-level validation issues (Zod, custom). Render as a bulleted\n // list of `path: message` so the model can locate the offender by\n // schema path instead of guessing from a generic message.\n const issues = errorDetails?.issues ?? []\n if (Array.isArray(issues) && issues.length > 0) {\n promptLines.push('', 'Validation issues:')\n for (const issue of issues) {\n const path = Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : '(root)'\n const msg = issue?.message ?? '(no message)'\n const codeHint = issue?.code ? ` [${issue.code}]` : ''\n const expHint =\n issue?.expected || issue?.received\n ? ` (expected ${issue?.expected ?? '?'}, got ${issue?.received ?? '?'})`\n : ''\n promptLines.push(`- ${path}: ${msg}${codeHint}${expHint}`)\n }\n } else if (errorDetails?.fieldErrors && typeof errorDetails.fieldErrors === 'object') {\n const entries = Object.entries(errorDetails.fieldErrors)\n if (entries.length > 0) {\n promptLines.push('', 'Field errors:')\n for (const [path, msgs] of entries) {\n const list = Array.isArray(msgs) ? msgs.join('; ') : String(msgs)\n promptLines.push(`- ${path}: ${list}`)\n }\n }\n }\n\n // Echo the arguments the handler was invoked with so the model can\n // see exactly what it sent and change at least one parameter on\n // retry. JSON-stringified inline for compactness; non-serializable\n // values are dropped on the server side already.\n if (errorInput !== undefined) {\n try {\n const json = JSON.stringify(errorInput, null, 2)\n if (json && json !== '{}' && json.length <= 4000) {\n promptLines.push('', 'Arguments you sent:', '```json', json, '```')\n } else if (json && json.length > 4000) {\n promptLines.push(\n '',\n 'Arguments you sent (truncated):',\n '```json',\n json.slice(0, 4000) + '\\n\u2026 [truncated]',\n '```',\n )\n }\n } catch {\n // ignore\n }\n }\n\n // Surface root-cause when the handler nested another error inside.\n if (errorDetails?.cause !== undefined) {\n try {\n const causeJson = JSON.stringify(errorDetails.cause, null, 2)\n if (causeJson && causeJson !== '{}' && causeJson.length <= 1500) {\n promptLines.push('', 'Underlying cause:', '```json', causeJson, '```')\n }\n } catch {\n // ignore\n }\n }\n\n // Per-record failures from batch tools (Step 5.14). These usually\n // carry the most actionable information for partial-success cases.\n if (failedRecords && failedRecords.length > 0) {\n promptLines.push('', 'Records that failed:')\n for (const rec of failedRecords) {\n promptLines.push(\n `- ${rec.recordId} \u2192 ${rec.error.code}: ${rec.error.message}`,\n )\n }\n }\n\n promptLines.push(\n '',\n 'Diagnose what went wrong using the validation issues / cause / arguments above, correct the arguments, and call the tool again. If the failure indicates missing prerequisites (e.g. a deal needs a linked person/company before commenting), tell me what to fix on the platform side instead of retrying blindly. Do not repeat the exact same arguments \u2014 you must change at least one parameter or stop and explain.',\n )\n dispatchFixRequest({\n message: promptLines.join('\\n'),\n toolName: action.toolName,\n pendingActionId: action.id,\n })\n }\n const visibleIssues = Array.isArray(errorDetails?.issues)\n ? errorDetails!.issues!.filter((entry) => entry && (entry.message || entry.path))\n : []\n return (\n <Alert variant=\"destructive\" data-ai-mutation-result=\"failure\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.failureTitle',\n 'Action failed',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <div>\n <span className=\"mr-2 font-mono text-xs\" data-ai-mutation-result-code>\n {code}\n </span>\n <span>{message}</span>\n </div>\n {visibleIssues.length > 0 ? (\n <ul\n className=\"mt-2 list-disc space-y-0.5 pl-5 text-xs\"\n data-ai-mutation-result-issues\n >\n {visibleIssues.map((issue, index) => {\n const path =\n Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : null\n return (\n <li key={index}>\n {path ? (\n <span className=\"font-mono\">{path}</span>\n ) : null}\n {path && issue?.message ? <span className=\"mx-1\">\u2014</span> : null}\n {issue?.message ? <span>{issue.message}</span> : null}\n </li>\n )\n })}\n </ul>\n ) : null}\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onFixWithAi}\n data-ai-mutation-result-fix\n >\n <Wand2 className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.result.fixWithAi',\n 'Fix with AI',\n )}\n </span>\n </Button>\n </div>\n </div>\n </Alert>\n )\n }\n\n return null\n}\n\nexport default MutationResultCard\n"],
5
- "mappings": ";AAkFQ,cAkBM,YAlBN;AA/ER,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,OAAyB,kBAAkB;AACpD,SAAS,cAAc;AAEvB,SAAS,iCAAiC;AAMnC,MAAM,4BAA4B;AAQzC,SAAS,mBAAmB,QAAsC;AAChE,MAAI,OAAO,WAAW,YAAa;AACnC,SAAO,cAAc,IAAI,YAAoC,2BAA2B,EAAE,OAAO,CAAC,CAAC;AACrG;AA0BA,SAAS,gBAAgB,QAAmD;AAC1E,SAAO,WAAW;AACpB;AAEA,SAAS,gBAAgB,QAAmD;AAC1E,SAAO,WAAW,YAAY,WAAW,eAAe,WAAW;AACrE;AAEO,SAAS,mBAAmB,OAAgC;AACjE,QAAM,IAAI,KAAK;AACf,QAAM,kBAAkB,MAAM,mBAAmB;AACjD,QAAM,UAAW,MAAM,WAAqD,CAAC;AAC7E,QAAM,WAAW,MAAM,iBAAiB,QAAQ,iBAAiB;AAEjE,QAAM,EAAE,QAAQ,OAAO,IAAI,0BAA0B;AAAA,IACnD;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,UAAU,CAAC,mBAAmB,QAAQ,QAAQ;AAAA,EAChD,CAAC;AACD,QAAM,SAAS,YAAY;AAC3B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,SAAS,QAAQ,mBAAmB;AAE1C,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,gBAAgB,MAAM,KAAK,iBAAiB,cAAc,SAAS,GAAG;AACxE,WACE,qBAAC,SAAM,SAAQ,WAAU,2BAAwB,WAC/C;AAAA,0BAAC,cACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OACE;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,mCAA+B;AAAA,YAE9B,wBAAc,IAAI,CAAC,WAClB,qBAAC,QAAyB,kCAAgC,OAAO,UAC/D;AAAA,kCAAC,UAAK,WAAU,aAAa,iBAAO,UAAS;AAAA,cAC7C,oBAAC,UAAK,WAAU,8BAA6B,oBAAC;AAAA,cAC9C,oBAAC,UAAK,WAAU,aAAa,iBAAO,MAAM,MAAK;AAAA,cAC/C,oBAAC,UAAK,WAAU,8BAA6B,oBAAC;AAAA,cAC9C,oBAAC,UAAM,iBAAO,MAAM,SAAQ;AAAA,iBALrB,OAAO,QAMhB,CACD;AAAA;AAAA,QACH;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,gBAAgB,MAAM,GAAG;AAC3B,UAAM,WAAW,QAAQ,YAAY,OAAO,kBAAkB;AAC9D,UAAM,OAAO,QAAQ,cAAc;AACnC,WACE,qBAAC,SAAM,SAAQ,WAAU,2BAAwB,WAC/C;AAAA,0BAAC,cACE,YAAE,wDAAwD,gBAAgB,GAC7E;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OACE,kBAAQ,cACL;AAAA,UACE;AAAA,UACA;AAAA,QACF,IAAI,KAAK,OAAO,WAAW,KAC3B;AAAA,UACE;AAAA,UACA;AAAA,QACF,GACN;AAAA,QACC,WACC,oBAAC,OAAE,WAAU,gBACV,iBACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV;AAAA,YACA,gCAA4B;AAAA,YAE3B;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,cACF;AAAA,cAAE;AAAA,cACC;AAAA;AAAA;AAAA,QACL,IAEA,oBAAC,UAAK,WAAU,aAAY,qCAAiC,MAC1D,oBACH,GAEJ,IACE;AAAA,SACN;AAAA,OACF;AAAA,EAEJ;AAQA,MAAI,gBAAgB,MAAM,KAAK,QAAQ,OAAO;AAC5C,UAAM,OAAO,QAAQ,OAAO,QAAQ,UAAU;AAC9C,UAAM,UACJ,QAAQ,OAAO,WACf;AAAA,MACE;AAAA,MACA;AAAA,IACF;AACF,UAAM,WAAW,QAAQ;AACzB,UAAM,eAAe,UAAU;AAC/B,UAAM,aAAa,UAAU;AAC7B,UAAM,YAAY,UAAU;AAC5B,UAAM,cAAc,MAAM;AAexB,YAAM,cAAwB;AAAA,QAC5B,8BAA8B,OAAO,QAAQ;AAAA,QAC7C,UAAU,IAAI,WAAM,OAAO;AAAA,MAC7B;AACA,UAAI,aAAa,cAAc,SAAS;AACtC,oBAAY,KAAK,gBAAgB,SAAS,GAAG;AAAA,MAC/C;AACA,UAAI,OAAO,oBAAoB,OAAO,gBAAgB;AACpD,oBAAY;AAAA,UACV,WAAW,OAAO,oBAAoB,GAAG,GAAG,OAAO,iBAAiB,QAAQ,OAAO,iBAAiB,EAAE;AAAA,QACxG;AAAA,MACF;AAKA,YAAM,SAAS,cAAc,UAAU,CAAC;AACxC,UAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC9C,oBAAY,KAAK,IAAI,oBAAoB;AACzC,mBAAW,SAAS,QAAQ;AAC1B,gBAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,KAAK,MAAM,KAAK,SAAS,IAC3D,MAAM,KAAK,KAAK,GAAG,IACnB;AACJ,gBAAM,MAAM,OAAO,WAAW;AAC9B,gBAAM,WAAW,OAAO,OAAO,KAAK,MAAM,IAAI,MAAM;AACpD,gBAAM,UACJ,OAAO,YAAY,OAAO,WACtB,cAAc,OAAO,YAAY,GAAG,SAAS,OAAO,YAAY,GAAG,MACnE;AACN,sBAAY,KAAK,KAAK,IAAI,KAAK,GAAG,GAAG,QAAQ,GAAG,OAAO,EAAE;AAAA,QAC3D;AAAA,MACF,WAAW,cAAc,eAAe,OAAO,aAAa,gBAAgB,UAAU;AACpF,cAAM,UAAU,OAAO,QAAQ,aAAa,WAAW;AACvD,YAAI,QAAQ,SAAS,GAAG;AACtB,sBAAY,KAAK,IAAI,eAAe;AACpC,qBAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,kBAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI;AAChE,wBAAY,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAMA,UAAI,eAAe,QAAW;AAC5B,YAAI;AACF,gBAAM,OAAO,KAAK,UAAU,YAAY,MAAM,CAAC;AAC/C,cAAI,QAAQ,SAAS,QAAQ,KAAK,UAAU,KAAM;AAChD,wBAAY,KAAK,IAAI,uBAAuB,WAAW,MAAM,KAAK;AAAA,UACpE,WAAW,QAAQ,KAAK,SAAS,KAAM;AACrC,wBAAY;AAAA,cACV;AAAA,cACA;AAAA,cACA;AAAA,cACA,KAAK,MAAM,GAAG,GAAI,IAAI;AAAA,cACtB;AAAA,YACF;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAGA,UAAI,cAAc,UAAU,QAAW;AACrC,YAAI;AACF,gBAAM,YAAY,KAAK,UAAU,aAAa,OAAO,MAAM,CAAC;AAC5D,cAAI,aAAa,cAAc,QAAQ,UAAU,UAAU,MAAM;AAC/D,wBAAY,KAAK,IAAI,qBAAqB,WAAW,WAAW,KAAK;AAAA,UACvE;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAIA,UAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,oBAAY,KAAK,IAAI,sBAAsB;AAC3C,mBAAW,OAAO,eAAe;AAC/B,sBAAY;AAAA,YACV,KAAK,IAAI,QAAQ,WAAM,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,OAAO;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAEA,kBAAY;AAAA,QACV;AAAA,QACA;AAAA,MACF;AACA,yBAAmB;AAAA,QACjB,SAAS,YAAY,KAAK,IAAI;AAAA,QAC9B,UAAU,OAAO;AAAA,QACjB,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,MAAM,QAAQ,cAAc,MAAM,IACpD,aAAc,OAAQ,OAAO,CAAC,UAAU,UAAU,MAAM,WAAW,MAAM,KAAK,IAC9E,CAAC;AACL,WACE,qBAAC,SAAM,SAAQ,eAAc,2BAAwB,WACnD;AAAA,0BAAC,cACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,SACC;AAAA,8BAAC,UAAK,WAAU,0BAAyB,gCAA4B,MAClE,gBACH;AAAA,UACA,oBAAC,UAAM,mBAAQ;AAAA,WACjB;AAAA,QACC,cAAc,SAAS,IACtB;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,kCAA8B;AAAA,YAE7B,wBAAc,IAAI,CAAC,OAAO,UAAU;AACnC,oBAAM,OACJ,MAAM,QAAQ,OAAO,IAAI,KAAK,MAAM,KAAK,SAAS,IAC9C,MAAM,KAAK,KAAK,GAAG,IACnB;AACN,qBACE,qBAAC,QACE;AAAA,uBACC,oBAAC,UAAK,WAAU,aAAa,gBAAK,IAChC;AAAA,gBACH,QAAQ,OAAO,UAAU,oBAAC,UAAK,WAAU,QAAO,oBAAC,IAAU;AAAA,gBAC3D,OAAO,UAAU,oBAAC,UAAM,gBAAM,SAAQ,IAAU;AAAA,mBAL1C,KAMT;AAAA,YAEJ,CAAC;AAAA;AAAA,QACH,IACE;AAAA,QACJ,oBAAC,SAAI,WAAU,gCACb;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,SAAS;AAAA,YACT,+BAA2B;AAAA,YAE3B;AAAA,kCAAC,SAAM,WAAU,UAAS,eAAW,MAAC;AAAA,cACtC,oBAAC,UACE;AAAA,gBACC;AAAA,gBACA;AAAA,cACF,GACF;AAAA;AAAA;AAAA,QACF,GACF;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,SAAO;AACT;AAEA,IAAO,6BAAQ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Wand2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport type { AiPendingActionCardAction, AiPendingActionCardStatus } from './types'\n\n/** Custom DOM event the failure card dispatches when the operator clicks\n * \"Fix with AI\". `<AiChat>` listens for this and sends a follow-up user\n * message asking the agent to diagnose and retry the failed call. */\nexport const AI_CHAT_FIX_REQUEST_EVENT = 'om-ai-chat-fix-request'\n\nexport interface AiChatFixRequestDetail {\n message: string\n toolName?: string\n pendingActionId?: string\n}\n\nfunction dispatchFixRequest(detail: AiChatFixRequestDetail): void {\n if (typeof window === 'undefined') return\n window.dispatchEvent(new CustomEvent<AiChatFixRequestDetail>(AI_CHAT_FIX_REQUEST_EVENT, { detail }))\n}\n\n/**\n * Terminal-state card that renders the `executionResult` of a pending\n * action. Success \u2192 `Alert variant=\"success\"` with a record link; partial\n * success (batch `failedRecords[]`) \u2192 `variant=\"warning\"` with the list;\n * failure \u2192 `variant=\"destructive\"` with the error code + message.\n *\n * Reads the pending action via the shared polling hook so page reloads\n * still recover state. The hook short-circuits once the row is terminal\n * (spec's reconnect behavior), which is always the case for this card.\n */\nexport interface MutationResultCardPayload {\n /** Server-serialized pending action snapshot (optional \u2014 the hook refetches). */\n pendingAction?: AiPendingActionCardAction\n /** Optional link target for the success record. */\n recordHref?: string\n}\n\nexport interface MutationResultCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Poll endpoint override for tests. */\n endpoint?: string\n}\n\nfunction isSuccessStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'confirmed'\n}\n\nfunction isFailureStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'failed' || status === 'cancelled' || status === 'expired'\n}\n\nexport function MutationResultCard(props: MutationResultCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationResultCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId || Boolean(injected),\n })\n const action = injected ?? polled\n const status = action?.status ?? null\n const failedRecords = action?.failedRecords ?? null\n const result = action?.executionResult ?? null\n\n if (!action) {\n return null\n }\n\n if (isSuccessStatus(status) && failedRecords && failedRecords.length > 0) {\n return (\n <Alert variant=\"warning\" data-ai-mutation-result=\"partial\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialTitle',\n 'Action applied with failures',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialBody',\n 'Some records could not be updated.',\n )}\n </p>\n <ul\n className=\"mt-2 list-disc space-y-1 pl-5 text-xs\"\n data-ai-mutation-failed-records\n >\n {failedRecords.map((record) => (\n <li key={record.recordId} data-ai-mutation-failed-record={record.recordId}>\n <span className=\"font-mono\">{record.recordId}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2022</span>\n <span className=\"font-mono\">{record.error.code}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2014</span>\n <span>{record.error.message}</span>\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )\n }\n\n if (isSuccessStatus(status)) {\n const recordId = result?.recordId ?? action.targetRecordId ?? null\n const href = payload.recordHref ?? null\n return (\n <Alert variant=\"success\" data-ai-mutation-result=\"success\">\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.result.successTitle', 'Action applied')}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.successBody',\n 'The requested change was saved successfully.',\n )}\n </p>\n {recordId && href ? (\n <p className=\"mt-1 text-xs\">\n <a\n className=\"text-primary underline\"\n href={href}\n data-ai-mutation-result-link\n >\n {t(\n 'ai_assistant.chat.mutation_cards.result.viewRecord',\n 'View record',\n )}\n </a>\n </p>\n ) : null}\n </div>\n </Alert>\n )\n }\n\n // Render the failure alert when the action is in a failure status OR when\n // the dispatcher already captured an `executionResult.error` (the row may\n // not yet have transitioned out of `executing` in the polling snapshot,\n // but the handler error is authoritative \u2014 surface it immediately so the\n // operator never sees a stuck \"applying\u2026\" state silently masking a real\n // failure).\n if (isFailureStatus(status) || result?.error) {\n const code = result?.error?.code ?? status ?? 'failed'\n const message =\n result?.error?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.result.failureBody',\n 'The mutation could not be applied.',\n )\n const errorObj = result?.error\n const errorDetails = errorObj?.details\n const errorInput = errorObj?.input\n const errorName = errorObj?.name\n const onFixWithAi = () => {\n // Build a structured prompt that gives the agent enough context to\n // diagnose and retry without copy/paste from the operator. Keeping\n // it explicit (\"retry with corrected arguments\") nudges the model\n // away from re-issuing the same args (which would just hit the\n // same error). The repository's idempotency check only dedupes\n // active `pending` rows, so a fresh prepareMutation call after a\n // terminal failure always produces a new pending action \u2014 the\n // retry is never silently collapsed.\n //\n // The prompt now embeds the full structured failure context the\n // server captured (Zod issues / fieldErrors, original arguments,\n // failedRecords for batch tools, error name + cause). Without this\n // the operator routinely saw \"Invalid input\" with no field path \u2014\n // the model literally could not fix what it could not see.\n const promptLines: string[] = [\n `The previous call to tool \"${action.toolName}\" failed.`,\n `Error: ${code} \u2014 ${message}.`,\n ]\n if (errorName && errorName !== 'Error') {\n promptLines.push(`Error class: ${errorName}.`)\n }\n if (action.targetEntityType || action.targetRecordId) {\n promptLines.push(\n `Target: ${action.targetEntityType ?? '?'}${action.targetRecordId ? ' / ' + action.targetRecordId : ''}.`,\n )\n }\n\n // Field-level validation issues (Zod, custom). Render as a bulleted\n // list of `path: message` so the model can locate the offender by\n // schema path instead of guessing from a generic message.\n const issues = errorDetails?.issues ?? []\n if (Array.isArray(issues) && issues.length > 0) {\n promptLines.push('', 'Validation issues:')\n for (const issue of issues) {\n const path = Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : '(root)'\n const msg = issue?.message ?? '(no message)'\n const codeHint = issue?.code ? ` [${issue.code}]` : ''\n const expHint =\n issue?.expected || issue?.received\n ? ` (expected ${issue?.expected ?? '?'}, got ${issue?.received ?? '?'})`\n : ''\n promptLines.push(`- ${path}: ${msg}${codeHint}${expHint}`)\n }\n } else if (errorDetails?.fieldErrors && typeof errorDetails.fieldErrors === 'object') {\n const entries = Object.entries(errorDetails.fieldErrors)\n if (entries.length > 0) {\n promptLines.push('', 'Field errors:')\n for (const [path, msgs] of entries) {\n const list = Array.isArray(msgs) ? msgs.join('; ') : String(msgs)\n promptLines.push(`- ${path}: ${list}`)\n }\n }\n }\n\n // Echo the arguments the handler was invoked with so the model can\n // see exactly what it sent and change at least one parameter on\n // retry. JSON-stringified inline for compactness; non-serializable\n // values are dropped on the server side already.\n if (errorInput !== undefined) {\n try {\n const json = JSON.stringify(errorInput, null, 2)\n if (json && json !== '{}' && json.length <= 4000) {\n promptLines.push('', 'Arguments you sent:', '```json', json, '```')\n } else if (json && json.length > 4000) {\n promptLines.push(\n '',\n 'Arguments you sent (truncated):',\n '```json',\n json.slice(0, 4000) + '\\n\u2026 [truncated]',\n '```',\n )\n }\n } catch {\n // ignore\n }\n }\n\n // Surface root-cause when the handler nested another error inside.\n if (errorDetails?.cause !== undefined) {\n try {\n const causeJson = JSON.stringify(errorDetails.cause, null, 2)\n if (causeJson && causeJson !== '{}' && causeJson.length <= 1500) {\n promptLines.push('', 'Underlying cause:', '```json', causeJson, '```')\n }\n } catch {\n // ignore\n }\n }\n\n // Per-record failures from batch tools (Step 5.14). These usually\n // carry the most actionable information for partial-success cases.\n if (failedRecords && failedRecords.length > 0) {\n promptLines.push('', 'Records that failed:')\n for (const rec of failedRecords) {\n promptLines.push(\n `- ${rec.recordId} \u2192 ${rec.error.code}: ${rec.error.message}`,\n )\n }\n }\n\n promptLines.push(\n '',\n 'Diagnose what went wrong using the validation issues / cause / arguments above, correct the arguments, and call the tool again. If the failure indicates missing prerequisites (e.g. a deal needs a linked person/company before commenting), tell me what to fix on the platform side instead of retrying blindly. Do not repeat the exact same arguments \u2014 you must change at least one parameter or stop and explain.',\n )\n dispatchFixRequest({\n message: promptLines.join('\\n'),\n toolName: action.toolName,\n pendingActionId: action.id,\n })\n }\n const visibleIssues = Array.isArray(errorDetails?.issues)\n ? errorDetails!.issues!.filter((entry) => entry && (entry.message || entry.path))\n : []\n return (\n <Alert variant=\"destructive\" data-ai-mutation-result=\"failure\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.failureTitle',\n 'Action failed',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <div>\n <span className=\"mr-2 font-mono text-xs\" data-ai-mutation-result-code>\n {code}\n </span>\n <span>{message}</span>\n </div>\n {visibleIssues.length > 0 ? (\n <ul\n className=\"mt-2 list-disc space-y-0.5 pl-5 text-xs\"\n data-ai-mutation-result-issues\n >\n {visibleIssues.map((issue, index) => {\n const path =\n Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : null\n return (\n <li key={index}>\n {path ? (\n <span className=\"font-mono\">{path}</span>\n ) : null}\n {path && issue?.message ? <span className=\"mx-1\">\u2014</span> : null}\n {issue?.message ? <span>{issue.message}</span> : null}\n </li>\n )\n })}\n </ul>\n ) : null}\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onFixWithAi}\n data-ai-mutation-result-fix\n >\n <Wand2 className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.result.fixWithAi',\n 'Fix with AI',\n )}\n </span>\n </Button>\n </div>\n </div>\n </Alert>\n )\n }\n\n return null\n}\n\nexport default MutationResultCard\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -73,7 +73,8 @@ import { buildFormFieldFromCustomFieldDef } from "./utils/customFieldForms.js";
73
73
  import { useT } from "@open-mercato/shared/lib/i18n/context";
74
74
  import { TagsInput } from "./inputs/TagsInput.js";
75
75
  import { ComboboxInput } from "./inputs/ComboboxInput.js";
76
- import { format, parseISO } from "date-fns";
76
+ import { format } from "date-fns/format";
77
+ import { parseISO } from "date-fns/parseISO";
77
78
  import { DateTimePicker } from "./inputs/DateTimePicker.js";
78
79
  import { TimePicker } from "./inputs/TimePicker.js";
79
80
  import { DatePicker } from "./inputs/DatePicker.js";