@hef2024/llmasaservice-ui 0.24.4 → 0.24.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -583,6 +583,25 @@ var SearchingIcon = () => /* @__PURE__ */ React10.createElement(
583
583
  /* @__PURE__ */ React10.createElement("circle", { cx: "11", cy: "11", r: "8" }),
584
584
  /* @__PURE__ */ React10.createElement("path", { d: "m21 21-4.3-4.3" })
585
585
  );
586
+ var PlanningIcon = () => /* @__PURE__ */ React10.createElement(
587
+ "svg",
588
+ {
589
+ xmlns: "http://www.w3.org/2000/svg",
590
+ viewBox: "0 0 24 24",
591
+ fill: "none",
592
+ stroke: "currentColor",
593
+ strokeWidth: "2",
594
+ strokeLinecap: "round",
595
+ strokeLinejoin: "round",
596
+ className: "thinking-block__icon"
597
+ },
598
+ /* @__PURE__ */ React10.createElement("path", { d: "M9 5h11" }),
599
+ /* @__PURE__ */ React10.createElement("path", { d: "M9 12h11" }),
600
+ /* @__PURE__ */ React10.createElement("path", { d: "M9 19h11" }),
601
+ /* @__PURE__ */ React10.createElement("path", { d: "m4 5 1.5 1.5L7 4.5" }),
602
+ /* @__PURE__ */ React10.createElement("path", { d: "m4 12 1.5 1.5L7 11.5" }),
603
+ /* @__PURE__ */ React10.createElement("path", { d: "m4 19 1.5 1.5L7 18.5" })
604
+ );
586
605
  var ChevronIcon = ({ isCollapsed }) => /* @__PURE__ */ React10.createElement(
587
606
  "svg",
588
607
  {
@@ -605,6 +624,8 @@ var getIcon = (type) => {
605
624
  return /* @__PURE__ */ React10.createElement(ReasoningIcon, null);
606
625
  case "searching":
607
626
  return /* @__PURE__ */ React10.createElement(SearchingIcon, null);
627
+ case "planning":
628
+ return /* @__PURE__ */ React10.createElement(PlanningIcon, null);
608
629
  }
609
630
  };
610
631
  var getDefaultTitle = (type) => {
@@ -615,6 +636,8 @@ var getDefaultTitle = (type) => {
615
636
  return "Reasoning";
616
637
  case "searching":
617
638
  return "Searching";
639
+ case "planning":
640
+ return "Planning";
618
641
  }
619
642
  };
620
643
  var ThinkingBlock = ({
@@ -1373,46 +1396,51 @@ var ChatPanel = ({
1373
1396
  const urlToFetch = `${publicAPIUrl}/tools/${encodeURIComponent(
1374
1397
  m.url
1375
1398
  )}`;
1376
- try {
1377
- const requestHeaders = yield buildMcpRequestHeaders({
1378
- phase: "list",
1379
- mcpServer: m
1380
- });
1381
- const response2 = yield fetch(urlToFetch, {
1382
- headers: requestHeaders
1383
- });
1384
- if (!response2.ok) {
1385
- console.error(
1386
- `Error fetching tools from ${m.url}: ${response2.status} ${response2.statusText}`
1387
- );
1388
- const errorBody = yield response2.text();
1389
- console.error(`Error body: ${errorBody}`);
1390
- throw new Error(
1391
- `HTTP ${response2.status}: ${response2.statusText}`
1392
- );
1393
- }
1394
- const toolsFromServer = yield response2.json();
1395
- if (Array.isArray(toolsFromServer)) {
1396
- return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
1397
- url: m.url,
1398
- accessToken: m.accessToken || "",
1399
- headers: requestHeaders
1400
- }));
1401
- } else {
1402
- return [];
1403
- }
1404
- } catch (fetchError) {
1399
+ const requestHeaders = yield buildMcpRequestHeaders({
1400
+ phase: "list",
1401
+ mcpServer: m
1402
+ });
1403
+ const response2 = yield fetch(urlToFetch, {
1404
+ headers: requestHeaders
1405
+ });
1406
+ if (!response2.ok) {
1405
1407
  console.error(
1406
- `Network or parsing error fetching tools from ${m.url}:`,
1407
- fetchError
1408
+ `Error fetching tools from ${m.url}: ${response2.status} ${response2.statusText}`
1408
1409
  );
1409
- throw fetchError;
1410
+ const errorBody = yield response2.text();
1411
+ console.error(`Error body: ${errorBody}`);
1412
+ throw new Error(
1413
+ `HTTP ${response2.status}: ${response2.statusText}`
1414
+ );
1415
+ }
1416
+ const toolsFromServer = yield response2.json();
1417
+ if (Array.isArray(toolsFromServer)) {
1418
+ return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
1419
+ url: m.url,
1420
+ accessToken: m.accessToken || "",
1421
+ headers: requestHeaders
1422
+ }));
1410
1423
  }
1424
+ return [];
1411
1425
  }));
1412
- const results = yield Promise.all(fetchPromises);
1413
- const allTools = results.flat();
1426
+ const settledResults = yield Promise.allSettled(fetchPromises);
1427
+ const allTools = settledResults.flatMap((result, index) => {
1428
+ if (result.status === "fulfilled") {
1429
+ return result.value;
1430
+ }
1431
+ const server = mcpServers == null ? void 0 : mcpServers[index];
1432
+ const failingUrl = typeof (server == null ? void 0 : server.url) === "string" ? server.url : `mcp[${index}]`;
1433
+ console.error(
1434
+ `Network or parsing error fetching tools from ${failingUrl}:`,
1435
+ result.reason
1436
+ );
1437
+ return [];
1438
+ });
1439
+ const failedCount = settledResults.filter(
1440
+ (result) => result.status === "rejected"
1441
+ ).length;
1414
1442
  setToolList(allTools);
1415
- setToolsFetchError(false);
1443
+ setToolsFetchError(failedCount > 0 && allTools.length === 0);
1416
1444
  } catch (error2) {
1417
1445
  console.error(
1418
1446
  "An error occurred while processing tool fetches:",
@@ -4762,6 +4790,87 @@ var buildThinkingBlockMarker = (type, signature) => {
4762
4790
  normalizedSignature
4763
4791
  )}${INLINE_THINKING_MARKER_SUFFIX}`;
4764
4792
  };
4793
+ var PLAN_STEP_REGEX = /^\s*\[plan-step\]\s*([a-zA-Z_-]+)\s*:\s*(.+?)\s*$/i;
4794
+ var normalizePlanningStatus = (value) => {
4795
+ const normalized = String(value || "").trim().toLowerCase();
4796
+ if (normalized === "doing" || normalized === "in_progress" || normalized === "in-progress") {
4797
+ return "doing";
4798
+ }
4799
+ if (normalized === "done" || normalized === "completed") {
4800
+ return "done";
4801
+ }
4802
+ return "todo";
4803
+ };
4804
+ var formatPlanningStatus = (status) => {
4805
+ if (status === "doing") return "Doing";
4806
+ if (status === "done") return "Done";
4807
+ return "Todo";
4808
+ };
4809
+ var parsePlanningStep = (line) => {
4810
+ const match = PLAN_STEP_REGEX.exec(String(line || ""));
4811
+ if (!match) return null;
4812
+ const title = String(match[2] || "").trim();
4813
+ if (!title) return null;
4814
+ return {
4815
+ status: normalizePlanningStatus(match[1] || ""),
4816
+ title
4817
+ };
4818
+ };
4819
+ var buildPlanningBlockContent = (steps) => {
4820
+ return steps.filter((step) => !!step && typeof step.title === "string" && step.title.trim().length > 0).map((step) => `${formatPlanningStatus(step.status)}: ${step.title.trim()}`).join("\n");
4821
+ };
4822
+ var extractPlanningBlocks = (text) => {
4823
+ const source = typeof text === "string" ? text : "";
4824
+ if (!source) {
4825
+ return {
4826
+ textWithMarkers: "",
4827
+ planningBlocks: []
4828
+ };
4829
+ }
4830
+ const lines = source.split("\n");
4831
+ const planningBlocks = [];
4832
+ const rebuiltSegments = [];
4833
+ let currentOffset = 0;
4834
+ let pendingSteps = [];
4835
+ let pendingIndex = 0;
4836
+ const flushPendingPlanning = () => {
4837
+ if (pendingSteps.length === 0) return;
4838
+ const signature = `planning-${pendingIndex}`;
4839
+ planningBlocks.push({
4840
+ type: "planning",
4841
+ content: buildPlanningBlockContent(pendingSteps),
4842
+ index: pendingIndex,
4843
+ signature,
4844
+ steps: pendingSteps
4845
+ });
4846
+ rebuiltSegments.push(`
4847
+
4848
+ ${buildThinkingBlockMarker("planning", signature)}
4849
+
4850
+ `);
4851
+ pendingSteps = [];
4852
+ };
4853
+ lines.forEach((line, index) => {
4854
+ const lineWithNewline = index < lines.length - 1 ? `${line}
4855
+ ` : line;
4856
+ const planningStep = parsePlanningStep(line);
4857
+ if (planningStep) {
4858
+ if (pendingSteps.length === 0) {
4859
+ pendingIndex = currentOffset;
4860
+ }
4861
+ pendingSteps = [...pendingSteps, planningStep];
4862
+ } else {
4863
+ flushPendingPlanning();
4864
+ rebuiltSegments.push(lineWithNewline);
4865
+ }
4866
+ currentOffset += lineWithNewline.length;
4867
+ });
4868
+ flushPendingPlanning();
4869
+ return {
4870
+ textWithMarkers: rebuiltSegments.join(""),
4871
+ planningBlocks
4872
+ };
4873
+ };
4765
4874
  var buildInlineToolMarker = (toolName, callId) => {
4766
4875
  const normalizedToolName = String(toolName || "").trim() || "tool";
4767
4876
  const normalizedCallId = String(callId || "").trim() || `${normalizedToolName}-call`;
@@ -4827,7 +4936,7 @@ var parseInlineThinkingMarkers = (text) => {
4827
4936
  signature = encodedSignature;
4828
4937
  }
4829
4938
  const normalizedType = String(rawType || "").trim().toLowerCase();
4830
- const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : "thinking";
4939
+ const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : normalizedType === "planning" ? "planning" : "thinking";
4831
4940
  markers.push({
4832
4941
  type,
4833
4942
  signature: String(signature || "").trim()
@@ -5228,6 +5337,7 @@ var AIChatPanel = ({
5228
5337
  const [lastPrompt, setLastPrompt] = useState7(null);
5229
5338
  const [lastKey, setLastKey] = useState7(null);
5230
5339
  const [currentConversation, setCurrentConversation] = useState7(conversation);
5340
+ const ensuredConversationIdsRef = useRef6(/* @__PURE__ */ new Set());
5231
5341
  const [followOnQuestionsState, setFollowOnQuestionsState] = useState7(followOnQuestions);
5232
5342
  const [thinkingBlocks, setThinkingBlocks] = useState7([]);
5233
5343
  const [currentThinkingIndex, setCurrentThinkingIndex] = useState7(0);
@@ -5383,7 +5493,7 @@ var AIChatPanel = ({
5383
5493
  return;
5384
5494
  }
5385
5495
  try {
5386
- const enriched = yield Promise.all(
5496
+ const settled = yield Promise.allSettled(
5387
5497
  mcpServers.map((server) => __async(void 0, null, function* () {
5388
5498
  const resolved = yield resolveMcpAuthHeaders({
5389
5499
  phase: "list",
@@ -5398,6 +5508,18 @@ var AIChatPanel = ({
5398
5508
  });
5399
5509
  }))
5400
5510
  );
5511
+ const enriched = settled.map((result, index) => {
5512
+ var _a2;
5513
+ if (result.status === "fulfilled") {
5514
+ return result.value;
5515
+ }
5516
+ const failingUrl = typeof ((_a2 = mcpServers == null ? void 0 : mcpServers[index]) == null ? void 0 : _a2.url) === "string" ? mcpServers[index].url : `mcp[${index}]`;
5517
+ console.error(
5518
+ `[AIChatPanel] Failed to resolve MCP auth headers for ${failingUrl}:`,
5519
+ result.reason
5520
+ );
5521
+ return mcpServers[index];
5522
+ });
5401
5523
  if (!cancelled) setResolvedMcpServers(enriched);
5402
5524
  } catch (error2) {
5403
5525
  console.error("[AIChatPanel] Failed to resolve MCP auth headers:", error2);
@@ -5489,10 +5611,22 @@ var AIChatPanel = ({
5489
5611
  headers: requestHeaders
5490
5612
  }));
5491
5613
  }));
5492
- const results = yield Promise.all(fetchPromises);
5493
- const allTools = results.flat();
5614
+ const settled = yield Promise.allSettled(fetchPromises);
5615
+ const allTools = settled.flatMap((result, index) => {
5616
+ var _a2;
5617
+ if (result.status === "fulfilled") {
5618
+ return result.value;
5619
+ }
5620
+ const failingUrl = typeof ((_a2 = resolvedMcpServers == null ? void 0 : resolvedMcpServers[index]) == null ? void 0 : _a2.url) === "string" ? resolvedMcpServers[index].url : `mcp[${index}]`;
5621
+ console.error(
5622
+ `[AIChatPanel] Failed to load MCP tools from ${failingUrl}:`,
5623
+ result.reason
5624
+ );
5625
+ return [];
5626
+ });
5627
+ const failedCount = settled.filter((result) => result.status === "rejected").length;
5494
5628
  setToolList(allTools);
5495
- setToolsFetchError(false);
5629
+ setToolsFetchError(failedCount > 0 && allTools.length === 0);
5496
5630
  } catch (error2) {
5497
5631
  console.error("[AIChatPanel] Failed to load MCP tools:", error2);
5498
5632
  setToolList([]);
@@ -5563,22 +5697,25 @@ var AIChatPanel = ({
5563
5697
  }, [propOnToggleSection]);
5564
5698
  const ensureConversation = useCallback2(() => {
5565
5699
  const normalizedConversationId = typeof currentConversation === "string" ? currentConversation.trim() : "";
5700
+ const hasConversationHistory = Object.keys(latestHistoryRef.current || {}).length > 0;
5701
+ const shouldInitializeProvidedConversation = normalizedConversationId.length > 10 && createConversationOnFirstChat && !hasConversationHistory && !ensuredConversationIdsRef.current.has(normalizedConversationId);
5566
5702
  console.log("ensureConversation - called with:", {
5567
5703
  currentConversation: normalizedConversationId || null,
5568
5704
  createConversationOnFirstChat,
5705
+ shouldInitializeProvidedConversation,
5569
5706
  project_id,
5570
5707
  publicAPIUrl
5571
5708
  });
5572
- if (normalizedConversationId) {
5709
+ if (normalizedConversationId && !shouldInitializeProvidedConversation) {
5573
5710
  console.log("ensureConversation - using existing conversation:", normalizedConversationId);
5574
5711
  return Promise.resolve(normalizedConversationId);
5575
5712
  }
5576
5713
  if (!createConversationOnFirstChat) {
5577
- return Promise.resolve("");
5714
+ return Promise.resolve(normalizedConversationId || "");
5578
5715
  }
5579
5716
  if (!project_id) {
5580
5717
  console.error("ensureConversation - Cannot create conversation without project_id");
5581
- return Promise.resolve("");
5718
+ return Promise.resolve(normalizedConversationId || "");
5582
5719
  }
5583
5720
  const createConversation = () => {
5584
5721
  var _a2, _b;
@@ -5590,6 +5727,9 @@ var AIChatPanel = ({
5590
5727
  timezone: browserInfo == null ? void 0 : browserInfo.userTimezone,
5591
5728
  language: browserInfo == null ? void 0 : browserInfo.userLanguage
5592
5729
  };
5730
+ if (shouldInitializeProvidedConversation) {
5731
+ requestBody.conversationId = normalizedConversationId;
5732
+ }
5593
5733
  console.log("ensureConversation - Creating conversation with:", requestBody);
5594
5734
  console.log("ensureConversation - API URL:", `${publicAPIUrl}/conversations`);
5595
5735
  return fetch(`${publicAPIUrl}/conversations`, {
@@ -5610,16 +5750,31 @@ var AIChatPanel = ({
5610
5750
  var _a3;
5611
5751
  console.log("ensureConversation - API response:", newConvo);
5612
5752
  const createdId = typeof (newConvo == null ? void 0 : newConvo.id) === "string" && newConvo.id.trim() || typeof (newConvo == null ? void 0 : newConvo.conversationId) === "string" && newConvo.conversationId.trim() || typeof (newConvo == null ? void 0 : newConvo.conversation_id) === "string" && newConvo.conversation_id.trim() || typeof ((_a3 = newConvo == null ? void 0 : newConvo.conversation) == null ? void 0 : _a3.id) === "string" && newConvo.conversation.id.trim() || "";
5753
+ const resolvedConversationId = normalizedConversationId || createdId;
5613
5754
  if (createdId) {
5614
5755
  console.log("ensureConversation - New conversation ID:", createdId);
5756
+ }
5757
+ if (resolvedConversationId) {
5758
+ ensuredConversationIdsRef.current.add(resolvedConversationId);
5759
+ }
5760
+ if (normalizedConversationId && createdId && createdId !== normalizedConversationId) {
5761
+ console.warn(
5762
+ "ensureConversation - API returned a different ID than supplied conversationId; keeping caller-provided ID",
5763
+ { suppliedConversationId: normalizedConversationId, returnedConversationId: createdId }
5764
+ );
5765
+ }
5766
+ if (!normalizedConversationId && createdId) {
5615
5767
  setCurrentConversation(createdId);
5616
5768
  return createdId;
5617
5769
  }
5770
+ if (resolvedConversationId) {
5771
+ return resolvedConversationId;
5772
+ }
5618
5773
  console.warn("ensureConversation - No ID in response");
5619
- return "";
5774
+ return normalizedConversationId || "";
5620
5775
  }).catch((error2) => {
5621
5776
  console.error("Error creating new conversation", error2);
5622
- return "";
5777
+ return normalizedConversationId || "";
5623
5778
  });
5624
5779
  };
5625
5780
  return createConversation();
@@ -6403,6 +6558,13 @@ ${buildThinkingBlockMarker(type, signature)}
6403
6558
  `;
6404
6559
  }
6405
6560
  );
6561
+ const {
6562
+ textWithMarkers: textWithArtifactMarkers,
6563
+ planningBlocks
6564
+ } = extractPlanningBlocks(textWithCompleteMarkers);
6565
+ if (planningBlocks.length > 0) {
6566
+ completedBlocks.push(...planningBlocks);
6567
+ }
6406
6568
  let activeBlock = null;
6407
6569
  const tagTypes = ["thinking", "reasoning", "searching"];
6408
6570
  let latestIncompletePos = -1;
@@ -6432,7 +6594,7 @@ ${buildThinkingBlockMarker(type, signature)}
6432
6594
  }
6433
6595
  }
6434
6596
  }
6435
- let cleanedText = textWithCompleteMarkers.replace(/<think(?:i(?:n(?:g)?)?)?$/i, "").replace(/<reas(?:o(?:n(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<sear(?:c(?:h(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<thinking>[\s\S]*$/i, "").replace(/<reasoning>[\s\S]*$/i, "").replace(/<searching>[\s\S]*$/i, "").trim();
6597
+ let cleanedText = textWithArtifactMarkers.replace(/<think(?:i(?:n(?:g)?)?)?$/i, "").replace(/<reas(?:o(?:n(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<sear(?:c(?:h(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<thinking>[\s\S]*$/i, "").replace(/<reasoning>[\s\S]*$/i, "").replace(/<searching>[\s\S]*$/i, "").trim();
6436
6598
  let lastThinkingContent = "Thinking";
6437
6599
  if (completedBlocks.length > 0) {
6438
6600
  const lastBlock = completedBlocks[completedBlocks.length - 1];
@@ -6455,6 +6617,13 @@ ${buildThinkingBlockMarker(type, signature)}
6455
6617
  if (incomingSupersetPrefix) {
6456
6618
  return incoming;
6457
6619
  }
6620
+ const incomingSignaturePrefix = incoming.length >= existing.length && existing.every((block, index) => {
6621
+ const next = incoming[index];
6622
+ return !!next && next.type === block.type && next.signature === block.signature;
6623
+ });
6624
+ if (incomingSignaturePrefix) {
6625
+ return incoming;
6626
+ }
6458
6627
  const merged = [...existing];
6459
6628
  const seen = new Set(existing.map((block) => block.signature || `${block.type}::${block.content}`));
6460
6629
  for (const block of incoming) {
@@ -6673,7 +6842,6 @@ ${traceSummary}` : traceSummary;
6673
6842
  hasAutoCollapsedRef.current = false;
6674
6843
  prevBlockCountRef.current = 0;
6675
6844
  setError(null);
6676
- lastProcessedErrorRef.current = null;
6677
6845
  setUserHasScrolled(false);
6678
6846
  prevResponseLengthRef.current = 0;
6679
6847
  setResponse("");
@@ -6959,6 +7127,7 @@ ${traceSummary}` : traceSummary;
6959
7127
  });
6960
7128
  prevBlockCountRef.current = mergedBlocks.length;
6961
7129
  }
7130
+ const planningBlocks = mergedBlocks.filter((block) => block.type === "planning");
6962
7131
  setHistory((prev) => {
6963
7132
  const newHistory = __spreadValues({}, prev);
6964
7133
  const existingEntry = newHistory[lastKey] || { content: "", callId: "" };
@@ -6969,7 +7138,9 @@ ${traceSummary}` : traceSummary;
6969
7138
  newHistory[lastKey] = __spreadProps(__spreadValues({}, existingEntry), {
6970
7139
  content: nextContent,
6971
7140
  // Store raw content without tool JSON or thinking tags
6972
- callId: lastCallId || existingEntry.callId || ""
7141
+ callId: lastCallId || existingEntry.callId || "",
7142
+ artifactBlocks: mergedBlocks,
7143
+ planningBlocks
6973
7144
  });
6974
7145
  latestHistoryRef.current = newHistory;
6975
7146
  return newHistory;
@@ -7113,69 +7284,74 @@ ${traceSummary}` : traceSummary;
7113
7284
  }
7114
7285
  }, [followOnPrompt, continueChat]);
7115
7286
  useEffect8(() => {
7116
- if (llmError && llmError.trim()) {
7117
- if (lastProcessedErrorRef.current === llmError) {
7118
- console.log("[AIChatPanel] Skipping duplicate error:", llmError);
7119
- return;
7120
- }
7121
- console.log("[AIChatPanel] Error detected:", llmError);
7122
- lastProcessedErrorRef.current = llmError;
7123
- const errorMessage = llmError;
7124
- const isAbortError = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("canceled") || errorMessage.toLowerCase().includes("cancelled");
7125
- if (isAbortError) {
7126
- console.log("[AIChatPanel] Request was aborted by user (useEffect)");
7127
- } else if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
7128
- setError({
7129
- message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7130
- code: "413"
7131
- });
7132
- if (lastKey) {
7133
- setHistory((prev) => {
7134
- const existingEntry = prev[lastKey] || { content: "", callId: "" };
7135
- return __spreadProps(__spreadValues({}, prev), {
7136
- [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7137
- content: `Error: ${errorMessage}`,
7138
- callId: lastCallId || existingEntry.callId || ""
7139
- })
7140
- });
7287
+ const normalizedError = typeof llmError === "string" ? llmError.trim() : "";
7288
+ if (!normalizedError) {
7289
+ lastProcessedErrorRef.current = null;
7290
+ return;
7291
+ }
7292
+ if (lastProcessedErrorRef.current === normalizedError) {
7293
+ console.log("[AIChatPanel] Skipping duplicate error:", normalizedError);
7294
+ return;
7295
+ }
7296
+ console.log("[AIChatPanel] Error detected:", normalizedError);
7297
+ lastProcessedErrorRef.current = normalizedError;
7298
+ const errorMessage = normalizedError;
7299
+ const currentLastKey = lastKeyRef.current;
7300
+ const currentLastCallId = lastCallIdRef.current;
7301
+ const isAbortError = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("canceled") || errorMessage.toLowerCase().includes("cancelled");
7302
+ if (isAbortError) {
7303
+ console.log("[AIChatPanel] Request was aborted by user (useEffect)");
7304
+ } else if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
7305
+ setError({
7306
+ message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7307
+ code: "413"
7308
+ });
7309
+ if (currentLastKey) {
7310
+ setHistory((prev) => {
7311
+ const existingEntry = prev[currentLastKey] || { content: "", callId: "" };
7312
+ return __spreadProps(__spreadValues({}, prev), {
7313
+ [currentLastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7314
+ content: `Error: ${errorMessage}`,
7315
+ callId: currentLastCallId || existingEntry.callId || ""
7316
+ })
7141
7317
  });
7142
- }
7143
- } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
7144
- setError({
7145
- message: "Network error. Please check your connection and try again.",
7146
- code: "NETWORK_ERROR"
7147
7318
  });
7148
- if (lastKey) {
7149
- setHistory((prev) => {
7150
- const existingEntry = prev[lastKey] || { content: "", callId: "" };
7151
- return __spreadProps(__spreadValues({}, prev), {
7152
- [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7153
- content: `Error: ${errorMessage}`,
7154
- callId: lastCallId || existingEntry.callId || ""
7155
- })
7156
- });
7319
+ }
7320
+ } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
7321
+ setError({
7322
+ message: "Network error. Please check your connection and try again.",
7323
+ code: "NETWORK_ERROR"
7324
+ });
7325
+ if (currentLastKey) {
7326
+ setHistory((prev) => {
7327
+ const existingEntry = prev[currentLastKey] || { content: "", callId: "" };
7328
+ return __spreadProps(__spreadValues({}, prev), {
7329
+ [currentLastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7330
+ content: `Error: ${errorMessage}`,
7331
+ callId: currentLastCallId || existingEntry.callId || ""
7332
+ })
7157
7333
  });
7158
- }
7159
- } else {
7160
- setError({
7161
- message: errorMessage,
7162
- code: "UNKNOWN_ERROR"
7163
7334
  });
7164
- if (lastKey) {
7165
- setHistory((prev) => {
7166
- const existingEntry = prev[lastKey] || { content: "", callId: "" };
7167
- return __spreadProps(__spreadValues({}, prev), {
7168
- [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7169
- content: `Error: ${errorMessage}`,
7170
- callId: lastCallId || existingEntry.callId || ""
7171
- })
7172
- });
7335
+ }
7336
+ } else {
7337
+ setError({
7338
+ message: errorMessage,
7339
+ code: "UNKNOWN_ERROR"
7340
+ });
7341
+ if (currentLastKey) {
7342
+ setHistory((prev) => {
7343
+ const existingEntry = prev[currentLastKey] || { content: "", callId: "" };
7344
+ return __spreadProps(__spreadValues({}, prev), {
7345
+ [currentLastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7346
+ content: `Error: ${errorMessage}`,
7347
+ callId: currentLastCallId || existingEntry.callId || ""
7348
+ })
7173
7349
  });
7174
- }
7350
+ });
7175
7351
  }
7176
- setIsLoading(false);
7177
7352
  }
7178
- }, [llmError, lastKey, lastCallId]);
7353
+ setIsLoading(false);
7354
+ }, [llmError]);
7179
7355
  useEffect8(() => {
7180
7356
  const existingLinks = document.querySelectorAll(
7181
7357
  'link[data-source="ai-chat-panel"]'
@@ -8433,6 +8609,61 @@ var truncatePromptForTitle = (prompt) => {
8433
8609
  }
8434
8610
  return prompt;
8435
8611
  };
8612
+ var stripHistoryPromptTimestamp = (prompt) => {
8613
+ const isoMatch = prompt.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:(.+)/);
8614
+ if (isoMatch && isoMatch[1]) {
8615
+ return isoMatch[1];
8616
+ }
8617
+ return prompt.replace(/^\d+:/, "");
8618
+ };
8619
+ var upsertConversationSummary = (conversations, summary) => {
8620
+ const normalizedSummary = normalizeConversationListPayload([summary])[0];
8621
+ if (!normalizedSummary) {
8622
+ return conversations;
8623
+ }
8624
+ const dedupedById = /* @__PURE__ */ new Map();
8625
+ for (const conv of conversations) {
8626
+ dedupedById.set(conv.conversationId, conv);
8627
+ }
8628
+ const existing = dedupedById.get(normalizedSummary.conversationId);
8629
+ const existingUpdatedAt = existing ? new Date(existing.updatedAt).getTime() : 0;
8630
+ const incomingUpdatedAt = new Date(normalizedSummary.updatedAt).getTime();
8631
+ const preferIncoming = !existing || incomingUpdatedAt >= existingUpdatedAt;
8632
+ const latest = preferIncoming ? normalizedSummary : existing;
8633
+ const fallback = preferIncoming ? existing : normalizedSummary;
8634
+ dedupedById.set(normalizedSummary.conversationId, __spreadProps(__spreadValues(__spreadValues({}, fallback || {}), latest || {}), {
8635
+ createdAt: (existing == null ? void 0 : existing.createdAt) || normalizedSummary.createdAt,
8636
+ updatedAt: (latest == null ? void 0 : latest.updatedAt) || (fallback == null ? void 0 : fallback.updatedAt) || normalizedSummary.updatedAt,
8637
+ title: (latest == null ? void 0 : latest.title) || (fallback == null ? void 0 : fallback.title) || "",
8638
+ summary: (latest == null ? void 0 : latest.summary) || (fallback == null ? void 0 : fallback.summary),
8639
+ agentId: (latest == null ? void 0 : latest.agentId) || (fallback == null ? void 0 : fallback.agentId),
8640
+ messageCount: typeof (latest == null ? void 0 : latest.messageCount) === "number" ? latest.messageCount : fallback == null ? void 0 : fallback.messageCount
8641
+ }));
8642
+ return Array.from(dedupedById.values()).sort(
8643
+ (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
8644
+ );
8645
+ };
8646
+ var buildOptimisticConversationSummary = (conversation) => {
8647
+ const conversationId = typeof conversation.conversationId === "string" ? conversation.conversationId.trim() : "";
8648
+ if (!conversationId || conversationId.startsWith("new-")) {
8649
+ return null;
8650
+ }
8651
+ const historyKeys = Object.keys(conversation.history || {});
8652
+ if (historyKeys.length === 0 && conversation.title === "New conversation") {
8653
+ return null;
8654
+ }
8655
+ const firstPrompt = historyKeys[0] ? stripHistoryPromptTimestamp(historyKeys[0]) : "";
8656
+ const title = truncatePromptForTitle(firstPrompt || conversation.title || "New conversation");
8657
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8658
+ return {
8659
+ conversationId,
8660
+ title,
8661
+ createdAt: now,
8662
+ updatedAt: now,
8663
+ agentId: conversation.agentId,
8664
+ messageCount: historyKeys.length > 0 ? historyKeys.length * 2 : void 0
8665
+ };
8666
+ };
8436
8667
  var EMPTY_ARRAY = [];
8437
8668
  var EMPTY_HISTORY = {};
8438
8669
  var ChatPanelWrapper = ({
@@ -8836,9 +9067,13 @@ var AIAgentPanel = React15.forwardRef(({
8836
9067
  buildAgentAwarenessInstructions,
8837
9068
  agentList
8838
9069
  } = useAgentRegistry(agentIds, { url, localOverrides });
9070
+ const getAgentRef = useRef7(getAgent);
9071
+ useEffect10(() => {
9072
+ getAgentRef.current = getAgent;
9073
+ }, [getAgent]);
8839
9074
  const fetchInProgressRef = useRef7(false);
8840
9075
  const loadingTranscriptIdsRef = useRef7(/* @__PURE__ */ new Set());
8841
- const lastFetchedAgentRef = useRef7(null);
9076
+ const [conversationListRefreshNonce, setConversationListRefreshNonce] = useState9(0);
8842
9077
  const checkedPromptsRef = useRef7(/* @__PURE__ */ new Set());
8843
9078
  const fetchingPromptsRef = useRef7(/* @__PURE__ */ new Set());
8844
9079
  const failedPromptsRef = useRef7(/* @__PURE__ */ new Map());
@@ -8846,6 +9081,34 @@ var AIAgentPanel = React15.forwardRef(({
8846
9081
  activeConversationsRef.current = activeConversations;
8847
9082
  const currentConversationIdRef = useRef7(currentConversationId);
8848
9083
  currentConversationIdRef.current = currentConversationId;
9084
+ const controlledConversationIdRef = useRef7(controlledConversationId);
9085
+ controlledConversationIdRef.current = controlledConversationId;
9086
+ const requestConversationListRefresh = useCallback4(() => {
9087
+ setConversationListRefreshNonce((prev) => prev + 1);
9088
+ }, []);
9089
+ const upsertApiConversationFromHistory = useCallback4(
9090
+ (conversationId, history, fallbackTitle) => {
9091
+ const firstPromptKey = Object.keys(history)[0];
9092
+ const firstPrompt = firstPromptKey ? stripHistoryPromptTimestamp(firstPromptKey) : "";
9093
+ const title = truncatePromptForTitle(firstPrompt || fallbackTitle || "New conversation");
9094
+ const messageCount = Object.keys(history).length * 2;
9095
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
9096
+ setApiConversations(
9097
+ (prev) => {
9098
+ var _a2, _b2;
9099
+ return upsertConversationSummary(prev, {
9100
+ conversationId,
9101
+ title,
9102
+ createdAt: ((_a2 = prev.find((conversation2) => conversation2.conversationId === conversationId)) == null ? void 0 : _a2.createdAt) || timestamp,
9103
+ updatedAt: timestamp,
9104
+ agentId: ((_b2 = prev.find((conversation2) => conversation2.conversationId === conversationId)) == null ? void 0 : _b2.agentId) || currentAgentId,
9105
+ messageCount
9106
+ });
9107
+ }
9108
+ );
9109
+ },
9110
+ [currentAgentId]
9111
+ );
8849
9112
  const commitConversationSelection = useCallback4(
8850
9113
  (conversationId, notifyChange) => {
8851
9114
  const shouldSyncLocalState = !isConversationControlled || !notifyChange || controlledConversationId === conversationId;
@@ -8913,7 +9176,7 @@ var AIAgentPanel = React15.forwardRef(({
8913
9176
  fetchingPromptsRef.current.add(conversationId);
8914
9177
  setLoadingPrompts((prev) => new Set(prev).add(conversationId));
8915
9178
  const agentIdToUse = agentIdForConversation || currentAgentId;
8916
- const agentProfile = getAgent(agentIdToUse);
9179
+ const agentProfile = getAgentRef.current(agentIdToUse);
8917
9180
  const projectId = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
8918
9181
  if (!apiKey || !projectId) {
8919
9182
  fetchingPromptsRef.current.delete(conversationId);
@@ -8978,11 +9241,8 @@ var AIAgentPanel = React15.forwardRef(({
8978
9241
  return next;
8979
9242
  });
8980
9243
  }
8981
- }), [apiKey, currentAgentId, getAgent]);
8982
- const fetchConversations = useCallback4((agentId, signal) => __async(void 0, null, function* () {
8983
- var _a2;
8984
- const agentProfile = getAgent(agentId);
8985
- const projectId = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
9244
+ }), [apiKey, currentAgentId]);
9245
+ const fetchConversations = useCallback4((agentId, projectId, signal) => __async(void 0, null, function* () {
8986
9246
  if (!agentId || !apiKey || !projectId) {
8987
9247
  setApiConversations([]);
8988
9248
  return;
@@ -9008,7 +9268,38 @@ var AIAgentPanel = React15.forwardRef(({
9008
9268
  const payload = yield response.json();
9009
9269
  const normalized = normalizeConversationListPayload(payload);
9010
9270
  if (!(signal == null ? void 0 : signal.aborted)) {
9011
- setApiConversations(normalized);
9271
+ setApiConversations((prev) => {
9272
+ const next = [...normalized];
9273
+ const optimisticIds = /* @__PURE__ */ new Set();
9274
+ const trackedConversationId = controlledConversationIdRef.current;
9275
+ if (trackedConversationId) {
9276
+ optimisticIds.add(trackedConversationId);
9277
+ }
9278
+ activeConversationsRef.current.forEach((conversation2) => {
9279
+ const optimisticSummary = buildOptimisticConversationSummary(conversation2);
9280
+ if (!optimisticSummary) {
9281
+ return;
9282
+ }
9283
+ optimisticIds.add(optimisticSummary.conversationId);
9284
+ const existing = prev.find(
9285
+ (candidate) => candidate.conversationId === optimisticSummary.conversationId
9286
+ );
9287
+ next.push(__spreadProps(__spreadValues({}, optimisticSummary), {
9288
+ createdAt: (existing == null ? void 0 : existing.createdAt) || optimisticSummary.createdAt,
9289
+ updatedAt: optimisticSummary.updatedAt
9290
+ }));
9291
+ });
9292
+ let merged = normalizeConversationListPayload(next);
9293
+ if (optimisticIds.size > 0) {
9294
+ prev.forEach((conversation2) => {
9295
+ if (!optimisticIds.has(conversation2.conversationId)) {
9296
+ return;
9297
+ }
9298
+ merged = upsertConversationSummary(merged, conversation2);
9299
+ });
9300
+ }
9301
+ return merged;
9302
+ });
9012
9303
  const now = /* @__PURE__ */ new Date();
9013
9304
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
9014
9305
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
@@ -9029,7 +9320,7 @@ var AIAgentPanel = React15.forwardRef(({
9029
9320
  setConversationsLoading(false);
9030
9321
  }
9031
9322
  }
9032
- }), [apiKey, customerId, getAgent, fetchFirstPrompt]);
9323
+ }), [apiKey, customerId, fetchFirstPrompt]);
9033
9324
  const loadConversationTranscript = useCallback4((conversationId, agentIdForConversation, title, notifyConversationChange = true) => __async(void 0, null, function* () {
9034
9325
  var _a2;
9035
9326
  const existingActive = activeConversationsRef.current.get(conversationId);
@@ -9042,7 +9333,7 @@ var AIAgentPanel = React15.forwardRef(({
9042
9333
  return;
9043
9334
  }
9044
9335
  const agentIdToUse = agentIdForConversation || currentAgentId;
9045
- const agentProfile = getAgent(agentIdToUse);
9336
+ const agentProfile = getAgentRef.current(agentIdToUse);
9046
9337
  const projectId = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
9047
9338
  if (!apiKey || !projectId) {
9048
9339
  setConversationsError("Missing API key or project ID");
@@ -9118,7 +9409,7 @@ var AIAgentPanel = React15.forwardRef(({
9118
9409
  loadingTranscriptIdsRef.current.delete(conversationId);
9119
9410
  setLoadingConversationId((prev) => prev === conversationId ? null : prev);
9120
9411
  }
9121
- }), [apiKey, commitConversationSelection, conversationFirstPrompts, currentAgentId, getAgent]);
9412
+ }), [apiKey, commitConversationSelection, conversationFirstPrompts, currentAgentId]);
9122
9413
  useEffect10(() => {
9123
9414
  if (!isConversationControlled) return;
9124
9415
  const targetConversationId = controlledConversationId;
@@ -9183,21 +9474,32 @@ var AIAgentPanel = React15.forwardRef(({
9183
9474
  loadConversationTranscript,
9184
9475
  loadingConversationId
9185
9476
  ]);
9477
+ const currentAgentProjectId = useMemo4(() => {
9478
+ var _a2;
9479
+ const agentProfile = getAgent(currentAgentId);
9480
+ return ((_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId) || "";
9481
+ }, [currentAgentId, getAgent]);
9186
9482
  const handleRefreshConversations = useCallback4(() => {
9187
- fetchConversations(currentAgentId);
9188
- }, [currentAgentId, fetchConversations]);
9483
+ requestConversationListRefresh();
9484
+ }, [requestConversationListRefresh]);
9189
9485
  useEffect10(() => {
9190
- var _a2;
9191
9486
  if (!showConversationHistory) return;
9192
9487
  if (!agentsLoading && currentAgentId && apiKey) {
9193
- const agentProfile = getAgent(currentAgentId);
9194
- const projectId = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
9195
- if (projectId && lastFetchedAgentRef.current !== currentAgentId) {
9196
- lastFetchedAgentRef.current = currentAgentId;
9197
- fetchConversations(currentAgentId);
9488
+ if (currentAgentProjectId) {
9489
+ const controller = new AbortController();
9490
+ fetchConversations(currentAgentId, currentAgentProjectId, controller.signal);
9491
+ return () => controller.abort();
9198
9492
  }
9199
9493
  }
9200
- }, [agentsLoading, currentAgentId, apiKey, fetchConversations, getAgent, showConversationHistory]);
9494
+ }, [
9495
+ agentsLoading,
9496
+ apiKey,
9497
+ conversationListRefreshNonce,
9498
+ currentAgentId,
9499
+ currentAgentProjectId,
9500
+ fetchConversations,
9501
+ showConversationHistory
9502
+ ]);
9201
9503
  const handleNewConversation = useCallback4(() => {
9202
9504
  const tempId = createDraftConversation(currentAgentId);
9203
9505
  commitConversationSelection(tempId, true);
@@ -9521,11 +9823,7 @@ var AIAgentPanel = React15.forwardRef(({
9521
9823
  if (title === "New conversation" && Object.keys(history).length > 0) {
9522
9824
  const firstPrompt = Object.keys(history)[0];
9523
9825
  if (firstPrompt) {
9524
- let cleanPrompt = firstPrompt;
9525
- const isoMatch = cleanPrompt.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:(.+)/);
9526
- if (isoMatch && isoMatch[1]) {
9527
- cleanPrompt = isoMatch[1];
9528
- }
9826
+ const cleanPrompt = stripHistoryPromptTimestamp(firstPrompt);
9529
9827
  title = cleanPrompt.length > 60 ? cleanPrompt.slice(0, 57) + "..." : cleanPrompt;
9530
9828
  }
9531
9829
  }
@@ -9538,6 +9836,12 @@ var AIAgentPanel = React15.forwardRef(({
9538
9836
  }
9539
9837
  return prev;
9540
9838
  });
9839
+ const existingActiveConversation = activeConversationsRef.current.get(targetConversationId);
9840
+ upsertApiConversationFromHistory(
9841
+ targetConversationId,
9842
+ history,
9843
+ existingActiveConversation == null ? void 0 : existingActiveConversation.title
9844
+ );
9541
9845
  }
9542
9846
  const lastEntry = Object.entries(history).pop();
9543
9847
  if (lastEntry) {
@@ -9557,9 +9861,11 @@ var AIAgentPanel = React15.forwardRef(({
9557
9861
  }
9558
9862
  },
9559
9863
  [
9864
+ activeConversationsRef,
9560
9865
  currentAgentId,
9561
9866
  historyChangedCallback,
9562
- agents
9867
+ agents,
9868
+ upsertApiConversationFromHistory
9563
9869
  ]
9564
9870
  );
9565
9871
  const handleLoadingChange = useCallback4((isLoading, conversationId) => {
@@ -9568,6 +9874,9 @@ var AIAgentPanel = React15.forwardRef(({
9568
9874
  setActiveConversations((prev) => {
9569
9875
  const existing = prev.get(targetConversationId);
9570
9876
  if (existing && existing.isLoading !== isLoading) {
9877
+ if (existing.isLoading && !isLoading) {
9878
+ requestConversationListRefresh();
9879
+ }
9571
9880
  const next = new Map(prev);
9572
9881
  next.set(targetConversationId, __spreadProps(__spreadValues({}, existing), {
9573
9882
  isLoading
@@ -9577,7 +9886,7 @@ var AIAgentPanel = React15.forwardRef(({
9577
9886
  return prev;
9578
9887
  });
9579
9888
  }
9580
- }, []);
9889
+ }, [requestConversationListRefresh]);
9581
9890
  const handleHandoffConfirm = useCallback4(() => {
9582
9891
  if (suggestedAgent) {
9583
9892
  handleAgentSwitch(suggestedAgent);
@@ -9623,7 +9932,8 @@ var AIAgentPanel = React15.forwardRef(({
9623
9932
  if (currentConversationIdRef.current === tempId) {
9624
9933
  commitConversationSelection(realId, true);
9625
9934
  }
9626
- }, [commitConversationSelection]);
9935
+ requestConversationListRefresh();
9936
+ }, [commitConversationSelection, requestConversationListRefresh]);
9627
9937
  const toggleCollapse = useCallback4(() => {
9628
9938
  if (!collapsible) return;
9629
9939
  const newValue = !isCollapsed;