opencami 1.9.0 → 1.9.1

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 (60) hide show
  1. package/dist/client/assets/{CSPContext-TfUptlEu.js → CSPContext-B3PAVjBL.js} +1 -1
  2. package/dist/client/assets/{DirectionContext-CQMv7g2N.js → DirectionContext-CR5CCisG.js} +1 -1
  3. package/dist/client/assets/_sessionKey-Bg_9uype.js +23 -0
  4. package/dist/client/assets/agents-BiTHBb6Z.js +2 -0
  5. package/dist/client/assets/{agents-screen-fSZJpRi_.js → agents-screen--ZMzN5cB.js} +1 -1
  6. package/dist/client/assets/bots-DxhRnQp5.js +2 -0
  7. package/dist/client/assets/{bots-screen-4yT-e3cM.js → bots-screen-C_uJBNwI.js} +1 -1
  8. package/dist/client/assets/{button-DqP4GZwZ.js → button-BciDmec0.js} +1 -1
  9. package/dist/client/assets/{composite-BLgu_EOL.js → composite-DArWbFHm.js} +1 -1
  10. package/dist/client/assets/{connect-CiqRvR6s.js → connect-B48HecjN.js} +1 -1
  11. package/dist/client/assets/{dashboard-CyWDWpbj.js → dashboard-BVKx9FMy.js} +1 -1
  12. package/dist/client/assets/{event-2_Dxdv7h.js → event-BCwqPPkP.js} +1 -1
  13. package/dist/client/assets/{file-explorer-screen-CZ2QKk-0.js → file-explorer-screen-DPs-FWeA.js} +1 -1
  14. package/dist/client/assets/files-SEycwYCa.js +2 -0
  15. package/dist/client/assets/{follow-up-suggestions-Bi3Ci2my.js → follow-up-suggestions-BPjWBpiy.js} +1 -1
  16. package/dist/client/assets/{index-ygitKeM-.js → index-0gdwm1_H.js} +1 -1
  17. package/dist/client/assets/{index-C_gsW9fo.js → index-CGeJcqZ3.js} +1 -1
  18. package/dist/client/assets/{keyboard-shortcuts-dialog-z-amTZVi.js → keyboard-shortcuts-dialog-CK_XTzLr.js} +1 -1
  19. package/dist/client/assets/{main-ZBMVSJTF.js → main-B2CrcRuC.js} +3 -3
  20. package/dist/client/assets/{markdown-CHUjmWcv.js → markdown-CdkuX06F.js} +1 -1
  21. package/dist/client/assets/memory-C7lKdkmc.js +2 -0
  22. package/dist/client/assets/{memory-screen-C_ZNDGLd.js → memory-screen-C1RfLy-d.js} +1 -1
  23. package/dist/client/assets/{menu-CB88T7R1.js → menu-CIwnliij.js} +1 -1
  24. package/dist/client/assets/{opencami-logo-C0Kj1DiT.js → opencami-logo-SVuYD55V.js} +1 -1
  25. package/dist/client/assets/{proxy-D-juuhw6.js → proxy-BijR8W1L.js} +1 -1
  26. package/dist/client/assets/{react-Akh4y69S.js → react-DWx7OvUo.js} +1 -1
  27. package/dist/client/assets/{search-dialog-BasfzCyM.js → search-dialog-CB4KE8ec.js} +1 -1
  28. package/dist/client/assets/{search-sources-badge-DwFHWd7S.js → search-sources-badge-B8Z-8sSf.js} +1 -1
  29. package/dist/client/assets/{session-export-dialog-CAl3iJnD.js → session-export-dialog-tDiFuv3a.js} +1 -1
  30. package/dist/client/assets/{settings-dialog-C8OoRXwX.js → settings-dialog-aL-AH4Rt.js} +1 -1
  31. package/dist/client/assets/skills-D1T6uemU.js +2 -0
  32. package/dist/client/assets/{skills-panel-B7BRAofP.js → skills-panel-fjJQVMog.js} +1 -1
  33. package/dist/client/assets/{styles-CXa-SiWC.css → styles-D0L88B64.css} +1 -1
  34. package/dist/client/assets/{switch-DYEbEgy5.js → switch-Bn5uei2k.js} +1 -1
  35. package/dist/client/assets/{tabs-eiBvL0H7.js → tabs-DOBNAUVE.js} +1 -1
  36. package/dist/client/assets/{thinking-CariuioI.js → thinking-dfGrFAMV.js} +1 -1
  37. package/dist/client/assets/{tooltip-CekkGEYG.js → tooltip-DOKkNFvu.js} +1 -1
  38. package/dist/client/assets/{use-file-explorer-state-Dfyh4GwR.js → use-file-explorer-state-BAa6Cxyr.js} +1 -1
  39. package/dist/client/assets/{useBaseUiId-DLhdkHJl.js → useBaseUiId-DFpBD0sg.js} +1 -1
  40. package/dist/client/assets/{useCompositeItem-DTSTTR0Z.js → useCompositeItem-B-Axq9-D.js} +1 -1
  41. package/dist/client/assets/{useControlled-CpliTEve.js → useControlled-CQHE0ITz.js} +1 -1
  42. package/dist/client/assets/{useMutation-CpD2Pn0F.js → useMutation-BFl-7GnD.js} +1 -1
  43. package/dist/client/assets/{useOnFirstRender-DsFYFJoB.js → useOnFirstRender-DlXHIIGk.js} +1 -1
  44. package/dist/client/assets/{useQuery-DMTgpIql.js → useQuery-D-sF8Tld.js} +1 -1
  45. package/dist/server/assets/{_sessionKey-Bhksr7VP.js → _sessionKey-D8TGrDRM.js} +337 -132
  46. package/dist/server/assets/{_tanstack-start-manifest_v-D-5ReiD4.js → _tanstack-start-manifest_v-DalBo2bY.js} +1 -1
  47. package/dist/server/assets/{follow-up-suggestions-B3hol2KT.js → follow-up-suggestions-C65ptDij.js} +2 -2
  48. package/dist/server/assets/{index-4G_4vZNY.js → index-gRco4Ina.js} +1 -1
  49. package/dist/server/assets/{router-C9JRmWMm.js → router-DaKDqc9w.js} +12 -10
  50. package/dist/server/assets/{search-dialog-CTJULPB8.js → search-dialog-DSSK93kq.js} +2 -2
  51. package/dist/server/assets/{settings-dialog-B5yR2pBy.js → settings-dialog-DyWNblva.js} +2 -2
  52. package/dist/server/assets/{thinking-CHx4Oouj.js → thinking-CU0FRlzT.js} +2 -2
  53. package/dist/server/server.js +2 -2
  54. package/package.json +1 -1
  55. package/dist/client/assets/_sessionKey-DYknvaDS.js +0 -23
  56. package/dist/client/assets/agents-DNywJUai.js +0 -2
  57. package/dist/client/assets/bots-Bqjqhws8.js +0 -2
  58. package/dist/client/assets/files-Cbhud0J8.js +0 -2
  59. package/dist/client/assets/memory-BRa-0plj.js +0 -2
  60. package/dist/client/assets/skills-Cx12984a.js +0 -2
@@ -19,7 +19,7 @@ import { u as useChatSettings$1 } from "./index-B_F4DTUu.js";
19
19
  import { createPortal } from "react-dom";
20
20
  import { create } from "zustand";
21
21
  import { persist } from "zustand/middleware";
22
- import { a as Route } from "./router-C9JRmWMm.js";
22
+ import { a as Route } from "./router-DaKDqc9w.js";
23
23
  function deriveFriendlyIdFromKey(key) {
24
24
  if (!key) return "main";
25
25
  const trimmed = key.trim();
@@ -1686,7 +1686,7 @@ function areSidebarSessionsEqual(prev, next) {
1686
1686
  return true;
1687
1687
  }
1688
1688
  const SettingsDialog = lazy(
1689
- () => import("./settings-dialog-B5yR2pBy.js").then((m) => ({ default: m.SettingsDialog }))
1689
+ () => import("./settings-dialog-DyWNblva.js").then((m) => ({ default: m.SettingsDialog }))
1690
1690
  );
1691
1691
  const SessionExportDialog = lazy(
1692
1692
  () => import("./session-export-dialog-CgtlOnwf.js").then((m) => ({
@@ -2833,7 +2833,7 @@ function Tool({ toolPart, defaultOpen = false }) {
2833
2833
  ] }) });
2834
2834
  }
2835
2835
  const Thinking = lazy(
2836
- () => import("./thinking-CHx4Oouj.js").then((m) => ({
2836
+ () => import("./thinking-CU0FRlzT.js").then((m) => ({
2837
2837
  default: m.Thinking
2838
2838
  }))
2839
2839
  );
@@ -3015,7 +3015,9 @@ function MessageItemComponent({
3015
3015
  wrapperClassName,
3016
3016
  wrapperScrollMarginTop,
3017
3017
  messageDomId,
3018
- highlighted = false
3018
+ highlighted = false,
3019
+ onRetry,
3020
+ onDismiss
3019
3021
  }) {
3020
3022
  const { settings } = useChatSettings$1();
3021
3023
  const role = message.role || "assistant";
@@ -3023,6 +3025,7 @@ function MessageItemComponent({
3023
3025
  const thinking = thinkingFromMessage(message);
3024
3026
  const images = imagesFromMessage(message);
3025
3027
  const isUser = role === "user";
3028
+ const isFailed = isUser && message.status === "error";
3026
3029
  const imageDataUris = useMemo(
3027
3030
  () => images.map(
3028
3031
  (img) => `data:${img.source.media_type};base64,${img.source.data}`
@@ -3165,13 +3168,35 @@ function MessageItemComponent({
3165
3168
  className: cn(
3166
3169
  "text-primary-900 opencami-text-size min-w-0 max-w-full",
3167
3170
  isUser ? "opencami-message-user bg-primary-100 px-4 py-[var(--opencami-user-bubble-py)] max-w-[85%]" : "opencami-message-assistant bg-transparent w-full",
3168
- !isUser && isStreaming && "stream-fade-in"
3171
+ !isUser && isStreaming && "stream-fade-in",
3172
+ isFailed && "opacity-60"
3169
3173
  ),
3170
3174
  children: displayText
3171
3175
  }
3172
3176
  )
3173
3177
  }
3174
3178
  ),
3179
+ isFailed && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-red-500 dark:text-red-400", children: [
3180
+ /* @__PURE__ */ jsx("span", { children: "Failed to send" }),
3181
+ onRetry && /* @__PURE__ */ jsx(
3182
+ "button",
3183
+ {
3184
+ type: "button",
3185
+ className: "underline hover:text-red-700 dark:hover:text-red-300",
3186
+ onClick: () => onRetry(message),
3187
+ children: "Retry"
3188
+ }
3189
+ ),
3190
+ onDismiss && /* @__PURE__ */ jsx(
3191
+ "button",
3192
+ {
3193
+ type: "button",
3194
+ className: "underline hover:text-red-700 dark:hover:text-red-300",
3195
+ onClick: () => onDismiss(message),
3196
+ children: "Dismiss"
3197
+ }
3198
+ )
3199
+ ] }),
3175
3200
  hasToolCalls && settings.showToolMessages && /* @__PURE__ */ jsx("div", { className: "mt-2 flex w-full min-w-0 max-w-[var(--opencami-chat-width)] flex-col gap-3 overflow-x-hidden", children: toolCalls.map((toolCall) => {
3176
3201
  const resultMessage = toolCall.id ? toolResultsByCallId?.get(toolCall.id) : void 0;
3177
3202
  const toolPart = mapToolCallToToolPart(toolCall, resultMessage);
@@ -3213,6 +3238,9 @@ function areMessagesEqual(prevProps, nextProps) {
3213
3238
  }
3214
3239
  if (prevProps.messageDomId !== nextProps.messageDomId) return false;
3215
3240
  if (prevProps.highlighted !== nextProps.highlighted) return false;
3241
+ if (prevProps.onRetry !== nextProps.onRetry) return false;
3242
+ if (prevProps.onDismiss !== nextProps.onDismiss) return false;
3243
+ if (prevProps.message.status !== nextProps.message.status) return false;
3216
3244
  if ((prevProps.message.role || "assistant") !== (nextProps.message.role || "assistant")) {
3217
3245
  return false;
3218
3246
  }
@@ -3474,7 +3502,7 @@ function TypingIndicator({ className }) {
3474
3502
  ] });
3475
3503
  }
3476
3504
  const FollowUpSuggestions = lazy(
3477
- () => import("./follow-up-suggestions-B3hol2KT.js").then((m) => ({
3505
+ () => import("./follow-up-suggestions-C65ptDij.js").then((m) => ({
3478
3506
  default: m.FollowUpSuggestions
3479
3507
  }))
3480
3508
  );
@@ -3493,7 +3521,9 @@ function ChatMessageListComponent({
3493
3521
  headerHeight,
3494
3522
  contentStyle,
3495
3523
  onFollowUpClick,
3496
- jumpToMessageId
3524
+ jumpToMessageId,
3525
+ onRetryMessage,
3526
+ onDismissMessage
3497
3527
  }) {
3498
3528
  const anchorRef = useRef(null);
3499
3529
  const lastUserRef = useRef(null);
@@ -3689,7 +3719,9 @@ function ChatMessageListComponent({
3689
3719
  isLastAssistant,
3690
3720
  aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0,
3691
3721
  messageDomId: messageId ? `message-${messageId}` : void 0,
3692
- highlighted: highlightedMessageId === messageId
3722
+ highlighted: highlightedMessageId === messageId,
3723
+ onRetry: onRetryMessage,
3724
+ onDismiss: onDismissMessage
3693
3725
  },
3694
3726
  messageKey
3695
3727
  );
@@ -3723,7 +3755,9 @@ function ChatMessageListComponent({
3723
3755
  wrapperClassName,
3724
3756
  wrapperScrollMarginTop,
3725
3757
  messageDomId: messageId ? `message-${messageId}` : void 0,
3726
- highlighted: highlightedMessageId === messageId
3758
+ highlighted: highlightedMessageId === messageId,
3759
+ onRetry: onRetryMessage,
3760
+ onDismiss: onDismissMessage
3727
3761
  },
3728
3762
  messageKey
3729
3763
  );
@@ -3757,7 +3791,9 @@ function ChatMessageListComponent({
3757
3791
  isLastAssistant,
3758
3792
  aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0,
3759
3793
  messageDomId: messageId ? `message-${messageId}` : void 0,
3760
- highlighted: highlightedMessageId === messageId
3794
+ highlighted: highlightedMessageId === messageId,
3795
+ onRetry: onRetryMessage,
3796
+ onDismiss: onDismissMessage
3761
3797
  },
3762
3798
  messageKey
3763
3799
  );
@@ -3782,7 +3818,7 @@ function ChatMessageListComponent({
3782
3818
  );
3783
3819
  }
3784
3820
  function areChatMessageListEqual(prev, next) {
3785
- return prev.messages === next.messages && prev.loading === next.loading && prev.empty === next.empty && prev.emptyState === next.emptyState && prev.notice === next.notice && prev.noticePosition === next.noticePosition && prev.waitingForResponse === next.waitingForResponse && prev.isStreaming === next.isStreaming && prev.sessionKey === next.sessionKey && prev.pinToTop === next.pinToTop && prev.pinGroupMinHeight === next.pinGroupMinHeight && prev.headerHeight === next.headerHeight && prev.contentStyle === next.contentStyle && prev.onFollowUpClick === next.onFollowUpClick && prev.jumpToMessageId === next.jumpToMessageId;
3821
+ return prev.messages === next.messages && prev.loading === next.loading && prev.empty === next.empty && prev.emptyState === next.emptyState && prev.notice === next.notice && prev.noticePosition === next.noticePosition && prev.waitingForResponse === next.waitingForResponse && prev.isStreaming === next.isStreaming && prev.sessionKey === next.sessionKey && prev.pinToTop === next.pinToTop && prev.pinGroupMinHeight === next.pinGroupMinHeight && prev.headerHeight === next.headerHeight && prev.contentStyle === next.contentStyle && prev.onFollowUpClick === next.onFollowUpClick && prev.jumpToMessageId === next.jumpToMessageId && prev.onRetryMessage === next.onRetryMessage && prev.onDismissMessage === next.onDismissMessage;
3786
3822
  }
3787
3823
  const MemoizedChatMessageList = memo(
3788
3824
  ChatMessageListComponent,
@@ -5450,13 +5486,20 @@ function useChatHistory({
5450
5486
  if (message.__optimisticId) return true;
5451
5487
  return Boolean(message.clientId);
5452
5488
  }) : [];
5489
+ const cachedMessages = Array.isArray(cached?.messages) ? cached.messages : [];
5453
5490
  const serverData = await fetchHistory({
5454
5491
  sessionKey: sessionKeyForHistory,
5455
5492
  friendlyId: activeFriendlyId
5456
5493
  });
5457
- if (!optimisticMessages.length) return serverData;
5458
- const merged = mergeOptimisticHistoryMessages(
5494
+ const serverMessages = restoreCachedImageParts(
5459
5495
  serverData.messages,
5496
+ cachedMessages
5497
+ );
5498
+ if (!optimisticMessages.length) {
5499
+ return { ...serverData, messages: serverMessages };
5500
+ }
5501
+ const merged = mergeOptimisticHistoryMessages(
5502
+ serverMessages,
5460
5503
  optimisticMessages
5461
5504
  );
5462
5505
  return {
@@ -5502,30 +5545,101 @@ function useChatHistory({
5502
5545
  sessionKeyForHistory
5503
5546
  };
5504
5547
  }
5505
- function mergeOptimisticHistoryMessages(serverMessages, optimisticMessages) {
5506
- if (!optimisticMessages.length) return serverMessages;
5507
- const merged = [...serverMessages];
5508
- for (const optimisticMessage of optimisticMessages) {
5509
- const hasMatch = serverMessages.some((serverMessage) => {
5510
- if (optimisticMessage.clientId && serverMessage.clientId && optimisticMessage.clientId === serverMessage.clientId) {
5548
+ function findMatchIndex(serverMessages, optimisticMessage) {
5549
+ return serverMessages.findIndex((serverMessage) => {
5550
+ if (optimisticMessage.clientId && serverMessage.clientId && optimisticMessage.clientId === serverMessage.clientId) {
5551
+ return true;
5552
+ }
5553
+ if (optimisticMessage.__optimisticId && serverMessage.__optimisticId && optimisticMessage.__optimisticId === serverMessage.__optimisticId) {
5554
+ return true;
5555
+ }
5556
+ if (optimisticMessage.role && serverMessage.role) {
5557
+ if (optimisticMessage.role !== serverMessage.role) return false;
5558
+ }
5559
+ const optimisticText = textFromMessage(optimisticMessage);
5560
+ if (!optimisticText) return false;
5561
+ if (optimisticText !== textFromMessage(serverMessage)) return false;
5562
+ const optimisticTime = getMessageTimestamp(optimisticMessage);
5563
+ const serverTime = getMessageTimestamp(serverMessage);
5564
+ return Math.abs(optimisticTime - serverTime) <= 1e4;
5565
+ });
5566
+ }
5567
+ function getImageParts(message) {
5568
+ if (!Array.isArray(message.content)) return [];
5569
+ return message.content.filter((part) => {
5570
+ if (typeof part !== "object" || part === null) return false;
5571
+ const p = part;
5572
+ if (p.type !== "image") return false;
5573
+ const source = p.source;
5574
+ return typeof source?.data === "string" && source.data.length > 0;
5575
+ });
5576
+ }
5577
+ function restoreCachedImageParts(serverMessages, cachedMessages) {
5578
+ const cachedMessagesWithImages = cachedMessages.filter(
5579
+ (message) => getImageParts(message).length > 0
5580
+ );
5581
+ if (!cachedMessagesWithImages.length) return serverMessages;
5582
+ return serverMessages.map((serverMessage) => {
5583
+ if (getImageParts(serverMessage).length > 0) return serverMessage;
5584
+ const cachedMatch = cachedMessagesWithImages.find((cachedMessage) => {
5585
+ if (serverMessage.role !== cachedMessage.role) return false;
5586
+ if (serverMessage.id && cachedMessage.id && serverMessage.id === cachedMessage.id) {
5511
5587
  return true;
5512
5588
  }
5513
- if (optimisticMessage.__optimisticId && serverMessage.__optimisticId && optimisticMessage.__optimisticId === serverMessage.__optimisticId) {
5589
+ if (serverMessage.clientId && cachedMessage.clientId && serverMessage.clientId === cachedMessage.clientId) {
5514
5590
  return true;
5515
5591
  }
5516
- if (optimisticMessage.role && serverMessage.role) {
5517
- if (optimisticMessage.role !== serverMessage.role) return false;
5518
- }
5519
- const optimisticText = textFromMessage(optimisticMessage);
5520
- if (!optimisticText) return false;
5521
- if (optimisticText !== textFromMessage(serverMessage)) return false;
5522
- const optimisticTime = getMessageTimestamp(optimisticMessage);
5523
- const serverTime = getMessageTimestamp(serverMessage);
5524
- return Math.abs(optimisticTime - serverTime) <= 1e4;
5592
+ const serverText = textFromMessage(serverMessage);
5593
+ const cachedText = textFromMessage(cachedMessage);
5594
+ if (!serverText || serverText !== cachedText) return false;
5595
+ const timeDiff = Math.abs(
5596
+ getMessageTimestamp(serverMessage) - getMessageTimestamp(cachedMessage)
5597
+ );
5598
+ return timeDiff <= 3e4;
5525
5599
  });
5526
- if (!hasMatch) {
5600
+ if (!cachedMatch) return serverMessage;
5601
+ const imageParts = getImageParts(cachedMatch);
5602
+ if (!imageParts.length) return serverMessage;
5603
+ return {
5604
+ ...serverMessage,
5605
+ content: [
5606
+ ...imageParts,
5607
+ ...Array.isArray(serverMessage.content) ? serverMessage.content : []
5608
+ ]
5609
+ };
5610
+ });
5611
+ }
5612
+ function mergeOptimisticHistoryMessages(serverMessages, optimisticMessages) {
5613
+ if (!optimisticMessages.length) return serverMessages;
5614
+ const merged = [...serverMessages];
5615
+ for (const optimisticMessage of optimisticMessages) {
5616
+ const matchIndex = findMatchIndex(merged, optimisticMessage);
5617
+ if (matchIndex === -1) {
5527
5618
  merged.push(optimisticMessage);
5619
+ continue;
5528
5620
  }
5621
+ const imageParts = getImageParts(optimisticMessage);
5622
+ const serverMessage = merged[matchIndex];
5623
+ const serverImageParts = getImageParts(serverMessage);
5624
+ const needsImages = imageParts.length > 0 && serverImageParts.length === 0;
5625
+ const needsOptimisticId = Boolean(
5626
+ optimisticMessage.__optimisticId && !serverMessage.__optimisticId
5627
+ );
5628
+ const needsClientId = Boolean(
5629
+ optimisticMessage.clientId && !serverMessage.clientId
5630
+ );
5631
+ if (!needsImages && !needsOptimisticId && !needsClientId) continue;
5632
+ merged[matchIndex] = {
5633
+ ...serverMessage,
5634
+ ...needsOptimisticId ? { __optimisticId: optimisticMessage.__optimisticId } : {},
5635
+ ...needsClientId ? { clientId: optimisticMessage.clientId } : {},
5636
+ ...needsImages ? {
5637
+ content: [
5638
+ ...imageParts,
5639
+ ...Array.isArray(serverMessage.content) ? serverMessage.content : []
5640
+ ]
5641
+ } : {}
5642
+ };
5529
5643
  }
5530
5644
  return merged;
5531
5645
  }
@@ -5902,6 +6016,101 @@ function generateHeuristicTitle(message) {
5902
6016
  function useLlmTitlesEnabled() {
5903
6017
  return useLlmSettingsStore((state) => state.settings.useLlmTitles);
5904
6018
  }
6019
+ function handleAgentEvent(payload, fallbackSessionKey, options) {
6020
+ const agentPayload = asRecord(payload);
6021
+ const stream = normalizeString(agentPayload?.stream);
6022
+ const runId = normalizeString(agentPayload?.runId);
6023
+ const sessionKey = normalizeString(agentPayload?.sessionKey) || fallbackSessionKey;
6024
+ const streamData = asRecord(agentPayload?.data);
6025
+ if (runId) {
6026
+ options.anyRunSeen.current = true;
6027
+ if (stream === "lifecycle") {
6028
+ const phase = normalizeString(streamData?.phase);
6029
+ if (phase === "end" || phase === "error" || phase === "abort") {
6030
+ options.activeRuns.delete(runId);
6031
+ } else if (phase) {
6032
+ options.activeRuns.add(runId);
6033
+ }
6034
+ } else {
6035
+ options.activeRuns.add(runId);
6036
+ }
6037
+ }
6038
+ if (stream === "assistant") {
6039
+ const text = normalizeString(streamData?.delta) || normalizeString(streamData?.text);
6040
+ if (!text) return;
6041
+ options.setState((prev) => {
6042
+ const blocks = [...prev.contentBlocks];
6043
+ const lastBlock = blocks[blocks.length - 1];
6044
+ if (lastBlock?.kind === "text") {
6045
+ blocks[blocks.length - 1] = { ...lastBlock, text: lastBlock.text + text };
6046
+ } else {
6047
+ blocks.push({ kind: "text", text });
6048
+ }
6049
+ return {
6050
+ ...prev,
6051
+ sessionKey,
6052
+ text: prev.text + text,
6053
+ contentBlocks: blocks
6054
+ };
6055
+ });
6056
+ options.onAssistantDelta?.({ text, sessionKey });
6057
+ return;
6058
+ }
6059
+ if (!stream.includes("tool")) return;
6060
+ const toolId = normalizeString(streamData?.toolCallId) || normalizeString(streamData?.id) || normalizeString(streamData?.callId) || `${runId || "tool"}:${normalizeString(streamData?.toolName) || normalizeString(streamData?.name) || "unknown"}`;
6061
+ const toolName = normalizeString(streamData?.toolName) || normalizeString(streamData?.name) || "Tool";
6062
+ const toolStatus = deriveToolStatus(stream, streamData);
6063
+ const toolArgs = asRecord(streamData?.arguments) || asRecord(streamData?.input) || asRecord(streamData?.params) || null;
6064
+ const toolOutput = normalizeString(streamData?.result) || normalizeString(streamData?.output) || (stream.includes("result") ? normalizeString(streamData?.text) : "");
6065
+ options.setState((prev) => {
6066
+ const tools = [...prev.tools];
6067
+ const toolIndex = tools.findIndex((tool) => tool.id === toolId);
6068
+ const nextTool = { id: toolId, name: toolName, status: toolStatus };
6069
+ if (toolIndex >= 0) {
6070
+ tools[toolIndex] = nextTool;
6071
+ } else {
6072
+ tools.push(nextTool);
6073
+ }
6074
+ const blocks = [...prev.contentBlocks];
6075
+ const blockIndex = blocks.findIndex(
6076
+ (b) => b.kind === "tool" && b.id === toolId
6077
+ );
6078
+ const existingBlock = blockIndex >= 0 ? blocks[blockIndex] : null;
6079
+ const nextBlock = {
6080
+ kind: "tool",
6081
+ name: toolName,
6082
+ id: toolId,
6083
+ status: toolStatus,
6084
+ // Merge: keep existing arguments/output if new event doesn't carry them
6085
+ arguments: toolArgs ?? existingBlock?.arguments,
6086
+ output: toolOutput || existingBlock?.output || void 0
6087
+ };
6088
+ if (blockIndex >= 0) {
6089
+ blocks[blockIndex] = nextBlock;
6090
+ } else {
6091
+ blocks.push(nextBlock);
6092
+ }
6093
+ return {
6094
+ ...prev,
6095
+ sessionKey,
6096
+ tools,
6097
+ contentBlocks: blocks
6098
+ };
6099
+ });
6100
+ }
6101
+ function deriveToolStatus(stream, data) {
6102
+ const explicitStatus = normalizeString(data?.phase) || normalizeString(data?.status) || normalizeString(data?.state);
6103
+ if (explicitStatus) return explicitStatus;
6104
+ if (stream.includes("result") || stream.includes("output")) return "done";
6105
+ if (stream.includes("call")) return "running";
6106
+ return "running";
6107
+ }
6108
+ function asRecord(value) {
6109
+ return value && typeof value === "object" ? value : null;
6110
+ }
6111
+ function normalizeString(value) {
6112
+ return typeof value === "string" ? value.trim() : "";
6113
+ }
5905
6114
  const INITIAL_STATE = {
5906
6115
  active: false,
5907
6116
  text: "",
@@ -6018,101 +6227,6 @@ function useStreaming(options) {
6018
6227
  }, []);
6019
6228
  return { streaming: state, startStream: start, stopStream: stop };
6020
6229
  }
6021
- function handleAgentEvent(payload, fallbackSessionKey, options) {
6022
- const agentPayload = asRecord(payload);
6023
- const stream = normalizeString(agentPayload?.stream);
6024
- const runId = normalizeString(agentPayload?.runId);
6025
- const sessionKey = normalizeString(agentPayload?.sessionKey) || fallbackSessionKey;
6026
- const streamData = asRecord(agentPayload?.data);
6027
- if (runId) {
6028
- options.anyRunSeen.current = true;
6029
- if (stream === "lifecycle") {
6030
- const phase = normalizeString(streamData?.phase);
6031
- if (phase === "end" || phase === "error" || phase === "abort") {
6032
- options.activeRuns.delete(runId);
6033
- } else if (phase) {
6034
- options.activeRuns.add(runId);
6035
- }
6036
- } else {
6037
- options.activeRuns.add(runId);
6038
- }
6039
- }
6040
- if (stream === "assistant") {
6041
- const text = normalizeString(streamData?.delta) || normalizeString(streamData?.text);
6042
- if (!text) return;
6043
- options.setState((prev) => {
6044
- const blocks = [...prev.contentBlocks];
6045
- const lastBlock = blocks[blocks.length - 1];
6046
- if (lastBlock?.kind === "text") {
6047
- blocks[blocks.length - 1] = { ...lastBlock, text: lastBlock.text + text };
6048
- } else {
6049
- blocks.push({ kind: "text", text });
6050
- }
6051
- return {
6052
- ...prev,
6053
- sessionKey,
6054
- text: prev.text + text,
6055
- contentBlocks: blocks
6056
- };
6057
- });
6058
- options.onAssistantDelta?.({ text, sessionKey });
6059
- return;
6060
- }
6061
- if (!stream.includes("tool")) return;
6062
- const toolId = normalizeString(streamData?.toolCallId) || normalizeString(streamData?.id) || normalizeString(streamData?.callId) || `${runId || "tool"}:${normalizeString(streamData?.toolName) || normalizeString(streamData?.name) || "unknown"}`;
6063
- const toolName = normalizeString(streamData?.toolName) || normalizeString(streamData?.name) || "Tool";
6064
- const toolStatus = deriveToolStatus(stream, streamData);
6065
- const toolArgs = asRecord(streamData?.arguments) || asRecord(streamData?.input) || asRecord(streamData?.params) || null;
6066
- const toolOutput = normalizeString(streamData?.result) || normalizeString(streamData?.output) || (stream.includes("result") ? normalizeString(streamData?.text) : "");
6067
- options.setState((prev) => {
6068
- const tools = [...prev.tools];
6069
- const toolIndex = tools.findIndex((tool) => tool.id === toolId);
6070
- const nextTool = { id: toolId, name: toolName, status: toolStatus };
6071
- if (toolIndex >= 0) {
6072
- tools[toolIndex] = nextTool;
6073
- } else {
6074
- tools.push(nextTool);
6075
- }
6076
- const blocks = [...prev.contentBlocks];
6077
- const blockIndex = blocks.findIndex(
6078
- (b) => b.kind === "tool" && b.id === toolId
6079
- );
6080
- const existingBlock = blockIndex >= 0 ? blocks[blockIndex] : null;
6081
- const nextBlock = {
6082
- kind: "tool",
6083
- name: toolName,
6084
- id: toolId,
6085
- status: toolStatus,
6086
- // Merge: keep existing arguments/output if new event doesn't carry them
6087
- arguments: toolArgs ?? existingBlock?.arguments,
6088
- output: toolOutput || existingBlock?.output || void 0
6089
- };
6090
- if (blockIndex >= 0) {
6091
- blocks[blockIndex] = nextBlock;
6092
- } else {
6093
- blocks.push(nextBlock);
6094
- }
6095
- return {
6096
- ...prev,
6097
- sessionKey,
6098
- tools,
6099
- contentBlocks: blocks
6100
- };
6101
- });
6102
- }
6103
- function deriveToolStatus(stream, data) {
6104
- const explicitStatus = normalizeString(data?.phase) || normalizeString(data?.status) || normalizeString(data?.state);
6105
- if (explicitStatus) return explicitStatus;
6106
- if (stream.includes("result") || stream.includes("output")) return "done";
6107
- if (stream.includes("call")) return "running";
6108
- return "running";
6109
- }
6110
- function asRecord(value) {
6111
- return value && typeof value === "object" ? value : null;
6112
- }
6113
- function normalizeString(value) {
6114
- return typeof value === "string" ? value.trim() : "";
6115
- }
6116
6230
  function useKeyboardShortcuts(handlers) {
6117
6231
  const handleKeyDown = useCallback(
6118
6232
  (event) => {
@@ -6324,11 +6438,32 @@ const KeyboardShortcutsDialog = lazy(
6324
6438
  }))
6325
6439
  );
6326
6440
  const SearchDialog = lazy(
6327
- () => import("./search-dialog-CTJULPB8.js").then((m) => ({
6441
+ () => import("./search-dialog-DSSK93kq.js").then((m) => ({
6328
6442
  default: m.SearchDialog
6329
6443
  }))
6330
6444
  );
6331
6445
  const SEARCH_JUMP_TARGET_KEY = "opencami-search-jump-target";
6446
+ function extractRetryImageAttachments(message) {
6447
+ const parts = Array.isArray(message.content) ? message.content : [];
6448
+ const attachments = [];
6449
+ const payload = [];
6450
+ for (const [index, part] of parts.entries()) {
6451
+ if (part.type !== "image" || !("source" in part) || typeof part.source?.data !== "string" || part.source.data.length === 0) {
6452
+ continue;
6453
+ }
6454
+ const mimeType = typeof part.source?.media_type === "string" && part.source.media_type.length > 0 ? part.source.media_type : "image/jpeg";
6455
+ const content = part.source.data;
6456
+ attachments.push({
6457
+ id: crypto.randomUUID(),
6458
+ file: new File([], `retry-attachment-${index + 1}`, { type: mimeType }),
6459
+ preview: null,
6460
+ type: "image",
6461
+ base64: content
6462
+ });
6463
+ payload.push({ mimeType, content });
6464
+ }
6465
+ return { attachments, payload };
6466
+ }
6332
6467
  function ChatScreen({
6333
6468
  activeFriendlyId,
6334
6469
  isNewChat = false,
@@ -6808,7 +6943,7 @@ function ChatScreen({
6808
6943
  queryClient,
6809
6944
  resolvedSessionKey
6810
6945
  ]);
6811
- function sendMessage(sessionKey, friendlyId, body, skipOptimistic = false, attachments, model) {
6946
+ function sendMessage(sessionKey, friendlyId, body, skipOptimistic = false, attachments, model, retryAttachmentsPayload) {
6812
6947
  let optimisticClientId = "";
6813
6948
  if (!skipOptimistic) {
6814
6949
  const { clientId, optimisticMessage } = createOptimisticMessage(
@@ -6834,10 +6969,14 @@ function ChatScreen({
6834
6969
  setError(null);
6835
6970
  setWaitingForResponse(true);
6836
6971
  setPinToTop(true);
6837
- const attachmentsPayload = attachments?.map((a) => ({
6838
- mimeType: a.file.type,
6839
- content: a.base64
6840
- }));
6972
+ const attachmentsPayload = retryAttachmentsPayload ?? attachments?.flatMap(
6973
+ (a) => a.base64 ? [
6974
+ {
6975
+ mimeType: a.file.type,
6976
+ content: a.base64
6977
+ }
6978
+ ] : []
6979
+ );
6841
6980
  streamingNotificationTextRef.current = "";
6842
6981
  streamStart();
6843
6982
  const preStreamKey = sessionKey || friendlyId;
@@ -7006,6 +7145,70 @@ function ChatScreen({
7006
7145
  return { ...state, isSidebarCollapsed: false };
7007
7146
  });
7008
7147
  }, [queryClient]);
7148
+ const handleRetryMessage = useCallback(
7149
+ (message) => {
7150
+ const messageText = textFromMessage(message);
7151
+ const {
7152
+ attachments: retryAttachments,
7153
+ payload: retryAttachmentsPayload
7154
+ } = extractRetryImageAttachments(message);
7155
+ if (!messageText && retryAttachments.length === 0) return;
7156
+ const sessionKeyForSend = forcedSessionKey || resolvedSessionKey || activeSessionKey;
7157
+ if (!sessionKeyForSend) return;
7158
+ const clientId = message.clientId;
7159
+ const optimisticId = message.__optimisticId;
7160
+ if (clientId) {
7161
+ removeHistoryMessageByClientId(
7162
+ queryClient,
7163
+ activeFriendlyId,
7164
+ sessionKeyForSend,
7165
+ clientId,
7166
+ optimisticId
7167
+ );
7168
+ }
7169
+ sendMessage(
7170
+ sessionKeyForSend,
7171
+ activeFriendlyId,
7172
+ messageText,
7173
+ false,
7174
+ retryAttachments,
7175
+ void 0,
7176
+ retryAttachmentsPayload
7177
+ );
7178
+ },
7179
+ [
7180
+ activeFriendlyId,
7181
+ activeSessionKey,
7182
+ forcedSessionKey,
7183
+ queryClient,
7184
+ resolvedSessionKey
7185
+ ]
7186
+ );
7187
+ const handleDismissMessage = useCallback(
7188
+ (message) => {
7189
+ const sessionKeyForSend = forcedSessionKey || resolvedSessionKey || activeSessionKey;
7190
+ if (!sessionKeyForSend) return;
7191
+ const clientId = message.clientId;
7192
+ const optimisticId = message.__optimisticId;
7193
+ if (clientId) {
7194
+ removeHistoryMessageByClientId(
7195
+ queryClient,
7196
+ activeFriendlyId,
7197
+ sessionKeyForSend,
7198
+ clientId,
7199
+ optimisticId
7200
+ );
7201
+ }
7202
+ setError(null);
7203
+ },
7204
+ [
7205
+ activeFriendlyId,
7206
+ activeSessionKey,
7207
+ forcedSessionKey,
7208
+ queryClient,
7209
+ resolvedSessionKey
7210
+ ]
7211
+ );
7009
7212
  const handleFollowUpClick = useCallback(
7010
7213
  (suggestion) => {
7011
7214
  if (isNewChat || !suggestion.trim()) return;
@@ -7164,7 +7367,9 @@ function ChatScreen({
7164
7367
  headerHeight,
7165
7368
  contentStyle: stableContentStyle,
7166
7369
  onFollowUpClick: handleFollowUpClick,
7167
- jumpToMessageId: searchJumpMessageId
7370
+ jumpToMessageId: searchJumpMessageId,
7371
+ onRetryMessage: handleRetryMessage,
7372
+ onDismissMessage: handleDismissMessage
7168
7373
  }
7169
7374
  ),
7170
7375
  /* @__PURE__ */ jsx(