@hef2024/llmasaservice-ui 0.16.9 → 0.17.0

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.d.mts CHANGED
@@ -170,6 +170,12 @@ interface AgentConfig {
170
170
  localName?: string;
171
171
  avatarUrl?: string;
172
172
  }
173
+ /**
174
+ * Imperative handle for programmatic control of AIAgentPanel
175
+ */
176
+ interface AIAgentPanelHandle {
177
+ startNewConversation: (prompt: string, agent?: string) => void;
178
+ }
173
179
  /**
174
180
  * Props for AIAgentPanel
175
181
  */
@@ -218,12 +224,15 @@ interface AIAgentPanelProps {
218
224
  clickCode?: string;
219
225
  style?: string;
220
226
  }[];
227
+ initialPrompt?: string;
228
+ initialMessage?: string;
229
+ hideInitialPrompt?: boolean;
221
230
  followOnQuestions?: string[];
222
231
  followOnPrompt?: string;
223
232
  historyListLimit?: number;
224
233
  showConversationHistory?: boolean;
225
234
  }
226
- declare const AIAgentPanel: React__default.FC<AIAgentPanelProps>;
235
+ declare const AIAgentPanel: React__default.ForwardRefExoticComponent<AIAgentPanelProps & React__default.RefAttributes<AIAgentPanelHandle>>;
227
236
 
228
237
  /**
229
238
  * AIChatPanel - A modern chat interface using shadcn-style components
package/dist/index.d.ts CHANGED
@@ -170,6 +170,12 @@ interface AgentConfig {
170
170
  localName?: string;
171
171
  avatarUrl?: string;
172
172
  }
173
+ /**
174
+ * Imperative handle for programmatic control of AIAgentPanel
175
+ */
176
+ interface AIAgentPanelHandle {
177
+ startNewConversation: (prompt: string, agent?: string) => void;
178
+ }
173
179
  /**
174
180
  * Props for AIAgentPanel
175
181
  */
@@ -218,12 +224,15 @@ interface AIAgentPanelProps {
218
224
  clickCode?: string;
219
225
  style?: string;
220
226
  }[];
227
+ initialPrompt?: string;
228
+ initialMessage?: string;
229
+ hideInitialPrompt?: boolean;
221
230
  followOnQuestions?: string[];
222
231
  followOnPrompt?: string;
223
232
  historyListLimit?: number;
224
233
  showConversationHistory?: boolean;
225
234
  }
226
- declare const AIAgentPanel: React__default.FC<AIAgentPanelProps>;
235
+ declare const AIAgentPanel: React__default.ForwardRefExoticComponent<AIAgentPanelProps & React__default.RefAttributes<AIAgentPanelHandle>>;
227
236
 
228
237
  /**
229
238
  * AIChatPanel - A modern chat interface using shadcn-style components
package/dist/index.js CHANGED
@@ -3669,6 +3669,7 @@ var AIChatPanel = ({
3669
3669
  const prevIdleRef = (0, import_react11.useRef)(true);
3670
3670
  const hasNotifiedCompletionRef = (0, import_react11.useRef)(true);
3671
3671
  const latestHistoryRef = (0, import_react11.useRef)(initialHistory);
3672
+ const initialPromptSentRef = (0, import_react11.useRef)(false);
3672
3673
  (0, import_react11.useEffect)(() => {
3673
3674
  if (!initialHistory) return;
3674
3675
  setHistory((prev) => {
@@ -3913,11 +3914,22 @@ var AIChatPanel = ({
3913
3914
  const promptToSend = promptText;
3914
3915
  if (!promptToSend || !promptToSend.trim()) return;
3915
3916
  setIsLoading(true);
3917
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3918
+ const promptKey = `${timestamp}:${promptToSend.trim()}`;
3919
+ setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
3920
+ [promptKey]: { content: "", callId: "" }
3921
+ }));
3922
+ setLastPrompt(promptToSend.trim());
3923
+ setLastKey(promptKey);
3924
+ setTimeout(() => {
3925
+ scrollToBottom();
3926
+ }, 0);
3916
3927
  console.log("AIChatPanel.continueChat - about to call ensureConversation");
3917
3928
  ensureConversation().then((convId) => {
3918
3929
  console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
3919
3930
  const messagesAndHistory = [];
3920
3931
  Object.entries(history).forEach(([historyPrompt, historyEntry]) => {
3932
+ if (historyPrompt === promptKey) return;
3921
3933
  let promptForHistory = historyPrompt;
3922
3934
  const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
3923
3935
  if (isoTimestampRegex.test(historyPrompt)) {
@@ -3930,13 +3942,8 @@ var AIChatPanel = ({
3930
3942
  messagesAndHistory.push({ role: "user", content: promptForHistory });
3931
3943
  messagesAndHistory.push({ role: "assistant", content: historyEntry.content });
3932
3944
  });
3933
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3934
- const promptKey = `${timestamp}:${promptToSend.trim()}`;
3935
- setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
3936
- [promptKey]: { content: "", callId: "" }
3937
- }));
3938
3945
  let fullPromptToSend = promptToSend.trim();
3939
- if (Object.keys(history).length === 0 && promptTemplate) {
3946
+ if (messagesAndHistory.length === 0 && promptTemplate) {
3940
3947
  fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
3941
3948
  }
3942
3949
  if (followOnPrompt) {
@@ -3963,17 +3970,12 @@ ${followOnPrompt}`;
3963
3970
  // Use the conversation ID from ensureConversation
3964
3971
  newController
3965
3972
  );
3966
- setLastPrompt(promptToSend.trim());
3967
3973
  setLastMessages(messagesAndHistory);
3968
- setLastKey(promptKey);
3969
3974
  if (convId && onConversationCreated) {
3970
3975
  setTimeout(() => {
3971
3976
  onConversationCreated(convId);
3972
3977
  }, 100);
3973
3978
  }
3974
- setTimeout(() => {
3975
- scrollToBottom();
3976
- }, 0);
3977
3979
  });
3978
3980
  }, [
3979
3981
  idle,
@@ -4128,50 +4130,11 @@ ${followOnPrompt}`;
4128
4130
  };
4129
4131
  }, []);
4130
4132
  (0, import_react11.useEffect)(() => {
4131
- const hasLoadedHistory = initialHistory && Object.keys(initialHistory).length > 0;
4132
- if (!project_id) {
4133
- return;
4133
+ if (initialPrompt && initialPrompt !== "" && !initialPromptSentRef.current) {
4134
+ initialPromptSentRef.current = true;
4135
+ continueChat(initialPrompt);
4134
4136
  }
4135
- if (initialPrompt && initialPrompt !== "" && initialPrompt !== lastPrompt && !hasLoadedHistory) {
4136
- setIsLoading(true);
4137
- setThinkingBlocks([]);
4138
- setCurrentThinkingIndex(0);
4139
- setUserHasScrolled(false);
4140
- ensureConversation().then((convId) => {
4141
- const controller = new AbortController();
4142
- setLastController(controller);
4143
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4144
- const promptKey = `${timestamp}:${initialPrompt}`;
4145
- setHistory({ [promptKey]: { content: "", callId: "" } });
4146
- let fullPrompt = initialPrompt;
4147
- if (promptTemplate) {
4148
- fullPrompt = promptTemplate.replace("{{prompt}}", initialPrompt);
4149
- }
4150
- send(
4151
- fullPrompt,
4152
- [],
4153
- [
4154
- ...dataWithExtras(),
4155
- { key: "--messages", data: "0" }
4156
- ],
4157
- true,
4158
- true,
4159
- service,
4160
- convId,
4161
- // Use conversation ID from ensureConversation
4162
- controller
4163
- );
4164
- setLastPrompt(initialPrompt);
4165
- setLastMessages([]);
4166
- setLastKey(promptKey);
4167
- if (convId && onConversationCreated) {
4168
- setTimeout(() => {
4169
- onConversationCreated(convId);
4170
- }, 100);
4171
- }
4172
- });
4173
- }
4174
- }, [initialPrompt, initialHistory, ensureConversation, promptTemplate, send, dataWithExtras, service, lastPrompt, project_id, onConversationCreated]);
4137
+ }, [initialPrompt, continueChat]);
4175
4138
  const CodeBlock = (0, import_react11.useCallback)((_a) => {
4176
4139
  var _b = _a, { node, inline, className, children } = _b, props = __objRest(_b, ["node", "inline", "className", "children"]);
4177
4140
  const match = /language-(\w+)/.exec(className || "");
@@ -4226,7 +4189,13 @@ ${followOnPrompt}`;
4226
4189
  variant: "default",
4227
4190
  size: "sm",
4228
4191
  className: "ai-chat-agent-suggestion__button",
4229
- onClick: () => onAgentChange(agentId)
4192
+ onClick: () => {
4193
+ onAgentChange(agentId);
4194
+ setTimeout(() => {
4195
+ var _a;
4196
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "auto" });
4197
+ }, 50);
4198
+ }
4230
4199
  },
4231
4200
  "Switch"
4232
4201
  ));
@@ -4682,6 +4651,9 @@ var ChatPanelWrapper = ({
4682
4651
  effectiveCustomer,
4683
4652
  showPoweredBy,
4684
4653
  actions,
4654
+ initialPrompt,
4655
+ initialMessage,
4656
+ hideInitialPrompt,
4685
4657
  followOnQuestions,
4686
4658
  followOnPrompt,
4687
4659
  agentOptions,
@@ -4692,7 +4664,8 @@ var ChatPanelWrapper = ({
4692
4664
  totalContextTokens,
4693
4665
  maxContextTokens,
4694
4666
  enableContextDetailView,
4695
- onConversationCreated
4667
+ onConversationCreated,
4668
+ conversationInitialPrompt
4696
4669
  }) => {
4697
4670
  var _a, _b;
4698
4671
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -4728,6 +4701,7 @@ var ChatPanelWrapper = ({
4728
4701
  const mcpServers = (0, import_react13.useMemo)(() => {
4729
4702
  return (convAgentProfile == null ? void 0 : convAgentProfile.mcpServers) || EMPTY_ARRAY;
4730
4703
  }, [convAgentProfile == null ? void 0 : convAgentProfile.mcpServers]);
4704
+ const effectiveInitialPrompt = conversationInitialPrompt || initialPrompt;
4731
4705
  if (!convAgentMetadata) return null;
4732
4706
  return /* @__PURE__ */ import_react13.default.createElement(
4733
4707
  "div",
@@ -4744,10 +4718,10 @@ var ChatPanelWrapper = ({
4744
4718
  title: "",
4745
4719
  theme,
4746
4720
  promptTemplate: convAgentMetadata.displayPromptTemplate || "{{prompt}}",
4747
- initialMessage: convAgentMetadata.displayStartMessageOrPrompt === "message" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0,
4748
- initialPrompt: convAgentMetadata.displayStartMessageOrPrompt === "prompt" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0,
4721
+ initialMessage: initialMessage || (convAgentMetadata.displayStartMessageOrPrompt === "message" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0),
4722
+ initialPrompt: effectiveInitialPrompt || (convAgentMetadata.displayStartMessageOrPrompt === "prompt" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0),
4749
4723
  placeholder: convAgentMetadata.displayPlaceholder || "Type a message...",
4750
- hideInitialPrompt: (_a = convAgentMetadata.displayHideInitialPrompt) != null ? _a : true,
4724
+ hideInitialPrompt: (_a = hideInitialPrompt != null ? hideInitialPrompt : convAgentMetadata.displayHideInitialPrompt) != null ? _a : true,
4751
4725
  data: chatPanelData,
4752
4726
  agent: activeConv.agentId,
4753
4727
  conversation: activeConv.conversationId.startsWith("new-") ? void 0 : activeConv.conversationId,
@@ -4781,7 +4755,7 @@ var ChatPanelWrapper = ({
4781
4755
  );
4782
4756
  };
4783
4757
  ChatPanelWrapper.displayName = "ChatPanelWrapper";
4784
- var AIAgentPanel = ({
4758
+ var AIAgentPanel = import_react13.default.forwardRef(({
4785
4759
  agents,
4786
4760
  defaultAgent,
4787
4761
  customerId,
@@ -4813,11 +4787,14 @@ var AIAgentPanel = ({
4813
4787
  showPoweredBy = true,
4814
4788
  conversation,
4815
4789
  actions = [],
4790
+ initialPrompt,
4791
+ initialMessage,
4792
+ hideInitialPrompt,
4816
4793
  followOnQuestions = [],
4817
4794
  followOnPrompt = "",
4818
4795
  historyListLimit = 50,
4819
4796
  showConversationHistory = true
4820
- }) => {
4797
+ }, ref) => {
4821
4798
  var _a, _b, _c, _d;
4822
4799
  const [isCollapsed, setIsCollapsed] = (0, import_react13.useState)(defaultCollapsed);
4823
4800
  const [isHistoryCollapsed, setIsHistoryCollapsed] = (0, import_react13.useState)(() => {
@@ -4899,6 +4876,29 @@ var AIAgentPanel = ({
4899
4876
  activeConversationsRef.current = activeConversations;
4900
4877
  const currentConversationIdRef = (0, import_react13.useRef)(currentConversationId);
4901
4878
  currentConversationIdRef.current = currentConversationId;
4879
+ import_react13.default.useImperativeHandle(ref, () => ({
4880
+ startNewConversation: (prompt, agent) => {
4881
+ const targetAgent = agent || currentAgentId;
4882
+ const tempId = `new-${Date.now()}`;
4883
+ setActiveConversations((prev) => {
4884
+ const next = new Map(prev);
4885
+ next.set(tempId, {
4886
+ conversationId: tempId,
4887
+ stableKey: tempId,
4888
+ agentId: targetAgent,
4889
+ history: {},
4890
+ isLoading: false,
4891
+ title: "New conversation",
4892
+ conversationInitialPrompt: prompt
4893
+ });
4894
+ return next;
4895
+ });
4896
+ setCurrentConversationId(tempId);
4897
+ if (onConversationChange) {
4898
+ onConversationChange(tempId);
4899
+ }
4900
+ }
4901
+ }), [currentAgentId, onConversationChange]);
4902
4902
  const [showContextNotification, setShowContextNotification] = (0, import_react13.useState)(false);
4903
4903
  const prevContextRef = (0, import_react13.useRef)(null);
4904
4904
  const contextNotificationTimeoutRef = (0, import_react13.useRef)(null);
@@ -5242,15 +5242,17 @@ var AIAgentPanel = ({
5242
5242
  let filtered = apiConversations;
5243
5243
  if (searchQuery) {
5244
5244
  const query = searchQuery.toLowerCase();
5245
- filtered = filtered.filter(
5246
- (conv) => {
5247
- var _a2, _b2;
5248
- return ((_a2 = conv.title) == null ? void 0 : _a2.toLowerCase().includes(query)) || ((_b2 = conv.summary) == null ? void 0 : _b2.toLowerCase().includes(query));
5249
- }
5250
- );
5245
+ filtered = filtered.filter((conv) => {
5246
+ var _a2, _b2;
5247
+ if ((_a2 = conv.title) == null ? void 0 : _a2.toLowerCase().includes(query)) return true;
5248
+ if ((_b2 = conv.summary) == null ? void 0 : _b2.toLowerCase().includes(query)) return true;
5249
+ const firstPrompt = conversationFirstPrompts[conv.conversationId];
5250
+ if (firstPrompt == null ? void 0 : firstPrompt.toLowerCase().includes(query)) return true;
5251
+ return false;
5252
+ });
5251
5253
  }
5252
5254
  return groupConversationsByTime(filtered, true);
5253
- }, [apiConversations, searchQuery]);
5255
+ }, [apiConversations, searchQuery, conversationFirstPrompts]);
5254
5256
  const effectiveCustomer = (0, import_react13.useMemo)(() => {
5255
5257
  return __spreadProps(__spreadValues({}, customer), {
5256
5258
  customer_id: customerId
@@ -5859,6 +5861,9 @@ var AIAgentPanel = ({
5859
5861
  effectiveCustomer,
5860
5862
  showPoweredBy,
5861
5863
  actions,
5864
+ initialPrompt,
5865
+ initialMessage,
5866
+ hideInitialPrompt,
5862
5867
  followOnQuestions,
5863
5868
  followOnPrompt,
5864
5869
  agentOptions,
@@ -5869,7 +5874,8 @@ var AIAgentPanel = ({
5869
5874
  totalContextTokens: mergedContext.totalTokens || 0,
5870
5875
  maxContextTokens,
5871
5876
  enableContextDetailView,
5872
- onConversationCreated: handleConversationCreated
5877
+ onConversationCreated: handleConversationCreated,
5878
+ conversationInitialPrompt: activeConv.conversationInitialPrompt
5873
5879
  }
5874
5880
  )), loadingConversationId && /* @__PURE__ */ import_react13.default.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ import_react13.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react13.default.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ import_react13.default.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ import_react13.default.createElement(MessageIcon, null), /* @__PURE__ */ import_react13.default.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ import_react13.default.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ import_react13.default.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ import_react13.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react13.default.createElement("p", null, "Loading agent..."))),
5875
5881
  /* @__PURE__ */ import_react13.default.createElement(
@@ -5883,7 +5889,8 @@ var AIAgentPanel = ({
5883
5889
  /* @__PURE__ */ import_react13.default.createElement(DialogFooter, null, /* @__PURE__ */ import_react13.default.createElement(Button, { variant: "outline", onClick: handleHandoffCancel }, "Stay with current agent"), /* @__PURE__ */ import_react13.default.createElement(Button, { onClick: handleHandoffConfirm }, "Switch agent"))
5884
5890
  )
5885
5891
  );
5886
- };
5892
+ });
5893
+ AIAgentPanel.displayName = "AIAgentPanel";
5887
5894
  var AIAgentPanel_default = AIAgentPanel;
5888
5895
 
5889
5896
  // src/hooks/useConversationStore.ts
package/dist/index.mjs CHANGED
@@ -3636,6 +3636,7 @@ var AIChatPanel = ({
3636
3636
  const prevIdleRef = useRef5(true);
3637
3637
  const hasNotifiedCompletionRef = useRef5(true);
3638
3638
  const latestHistoryRef = useRef5(initialHistory);
3639
+ const initialPromptSentRef = useRef5(false);
3639
3640
  useEffect7(() => {
3640
3641
  if (!initialHistory) return;
3641
3642
  setHistory((prev) => {
@@ -3880,11 +3881,22 @@ var AIChatPanel = ({
3880
3881
  const promptToSend = promptText;
3881
3882
  if (!promptToSend || !promptToSend.trim()) return;
3882
3883
  setIsLoading(true);
3884
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3885
+ const promptKey = `${timestamp}:${promptToSend.trim()}`;
3886
+ setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
3887
+ [promptKey]: { content: "", callId: "" }
3888
+ }));
3889
+ setLastPrompt(promptToSend.trim());
3890
+ setLastKey(promptKey);
3891
+ setTimeout(() => {
3892
+ scrollToBottom();
3893
+ }, 0);
3883
3894
  console.log("AIChatPanel.continueChat - about to call ensureConversation");
3884
3895
  ensureConversation().then((convId) => {
3885
3896
  console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
3886
3897
  const messagesAndHistory = [];
3887
3898
  Object.entries(history).forEach(([historyPrompt, historyEntry]) => {
3899
+ if (historyPrompt === promptKey) return;
3888
3900
  let promptForHistory = historyPrompt;
3889
3901
  const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
3890
3902
  if (isoTimestampRegex.test(historyPrompt)) {
@@ -3897,13 +3909,8 @@ var AIChatPanel = ({
3897
3909
  messagesAndHistory.push({ role: "user", content: promptForHistory });
3898
3910
  messagesAndHistory.push({ role: "assistant", content: historyEntry.content });
3899
3911
  });
3900
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3901
- const promptKey = `${timestamp}:${promptToSend.trim()}`;
3902
- setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
3903
- [promptKey]: { content: "", callId: "" }
3904
- }));
3905
3912
  let fullPromptToSend = promptToSend.trim();
3906
- if (Object.keys(history).length === 0 && promptTemplate) {
3913
+ if (messagesAndHistory.length === 0 && promptTemplate) {
3907
3914
  fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
3908
3915
  }
3909
3916
  if (followOnPrompt) {
@@ -3930,17 +3937,12 @@ ${followOnPrompt}`;
3930
3937
  // Use the conversation ID from ensureConversation
3931
3938
  newController
3932
3939
  );
3933
- setLastPrompt(promptToSend.trim());
3934
3940
  setLastMessages(messagesAndHistory);
3935
- setLastKey(promptKey);
3936
3941
  if (convId && onConversationCreated) {
3937
3942
  setTimeout(() => {
3938
3943
  onConversationCreated(convId);
3939
3944
  }, 100);
3940
3945
  }
3941
- setTimeout(() => {
3942
- scrollToBottom();
3943
- }, 0);
3944
3946
  });
3945
3947
  }, [
3946
3948
  idle,
@@ -4095,50 +4097,11 @@ ${followOnPrompt}`;
4095
4097
  };
4096
4098
  }, []);
4097
4099
  useEffect7(() => {
4098
- const hasLoadedHistory = initialHistory && Object.keys(initialHistory).length > 0;
4099
- if (!project_id) {
4100
- return;
4100
+ if (initialPrompt && initialPrompt !== "" && !initialPromptSentRef.current) {
4101
+ initialPromptSentRef.current = true;
4102
+ continueChat(initialPrompt);
4101
4103
  }
4102
- if (initialPrompt && initialPrompt !== "" && initialPrompt !== lastPrompt && !hasLoadedHistory) {
4103
- setIsLoading(true);
4104
- setThinkingBlocks([]);
4105
- setCurrentThinkingIndex(0);
4106
- setUserHasScrolled(false);
4107
- ensureConversation().then((convId) => {
4108
- const controller = new AbortController();
4109
- setLastController(controller);
4110
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4111
- const promptKey = `${timestamp}:${initialPrompt}`;
4112
- setHistory({ [promptKey]: { content: "", callId: "" } });
4113
- let fullPrompt = initialPrompt;
4114
- if (promptTemplate) {
4115
- fullPrompt = promptTemplate.replace("{{prompt}}", initialPrompt);
4116
- }
4117
- send(
4118
- fullPrompt,
4119
- [],
4120
- [
4121
- ...dataWithExtras(),
4122
- { key: "--messages", data: "0" }
4123
- ],
4124
- true,
4125
- true,
4126
- service,
4127
- convId,
4128
- // Use conversation ID from ensureConversation
4129
- controller
4130
- );
4131
- setLastPrompt(initialPrompt);
4132
- setLastMessages([]);
4133
- setLastKey(promptKey);
4134
- if (convId && onConversationCreated) {
4135
- setTimeout(() => {
4136
- onConversationCreated(convId);
4137
- }, 100);
4138
- }
4139
- });
4140
- }
4141
- }, [initialPrompt, initialHistory, ensureConversation, promptTemplate, send, dataWithExtras, service, lastPrompt, project_id, onConversationCreated]);
4104
+ }, [initialPrompt, continueChat]);
4142
4105
  const CodeBlock = useCallback2((_a) => {
4143
4106
  var _b = _a, { node, inline, className, children } = _b, props = __objRest(_b, ["node", "inline", "className", "children"]);
4144
4107
  const match = /language-(\w+)/.exec(className || "");
@@ -4193,7 +4156,13 @@ ${followOnPrompt}`;
4193
4156
  variant: "default",
4194
4157
  size: "sm",
4195
4158
  className: "ai-chat-agent-suggestion__button",
4196
- onClick: () => onAgentChange(agentId)
4159
+ onClick: () => {
4160
+ onAgentChange(agentId);
4161
+ setTimeout(() => {
4162
+ var _a;
4163
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "auto" });
4164
+ }, 50);
4165
+ }
4197
4166
  },
4198
4167
  "Switch"
4199
4168
  ));
@@ -4649,6 +4618,9 @@ var ChatPanelWrapper = ({
4649
4618
  effectiveCustomer,
4650
4619
  showPoweredBy,
4651
4620
  actions,
4621
+ initialPrompt,
4622
+ initialMessage,
4623
+ hideInitialPrompt,
4652
4624
  followOnQuestions,
4653
4625
  followOnPrompt,
4654
4626
  agentOptions,
@@ -4659,7 +4631,8 @@ var ChatPanelWrapper = ({
4659
4631
  totalContextTokens,
4660
4632
  maxContextTokens,
4661
4633
  enableContextDetailView,
4662
- onConversationCreated
4634
+ onConversationCreated,
4635
+ conversationInitialPrompt
4663
4636
  }) => {
4664
4637
  var _a, _b;
4665
4638
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -4695,6 +4668,7 @@ var ChatPanelWrapper = ({
4695
4668
  const mcpServers = useMemo4(() => {
4696
4669
  return (convAgentProfile == null ? void 0 : convAgentProfile.mcpServers) || EMPTY_ARRAY;
4697
4670
  }, [convAgentProfile == null ? void 0 : convAgentProfile.mcpServers]);
4671
+ const effectiveInitialPrompt = conversationInitialPrompt || initialPrompt;
4698
4672
  if (!convAgentMetadata) return null;
4699
4673
  return /* @__PURE__ */ React12.createElement(
4700
4674
  "div",
@@ -4711,10 +4685,10 @@ var ChatPanelWrapper = ({
4711
4685
  title: "",
4712
4686
  theme,
4713
4687
  promptTemplate: convAgentMetadata.displayPromptTemplate || "{{prompt}}",
4714
- initialMessage: convAgentMetadata.displayStartMessageOrPrompt === "message" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0,
4715
- initialPrompt: convAgentMetadata.displayStartMessageOrPrompt === "prompt" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0,
4688
+ initialMessage: initialMessage || (convAgentMetadata.displayStartMessageOrPrompt === "message" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0),
4689
+ initialPrompt: effectiveInitialPrompt || (convAgentMetadata.displayStartMessageOrPrompt === "prompt" ? convAgentMetadata.displayInitialMessageOrPrompt : void 0),
4716
4690
  placeholder: convAgentMetadata.displayPlaceholder || "Type a message...",
4717
- hideInitialPrompt: (_a = convAgentMetadata.displayHideInitialPrompt) != null ? _a : true,
4691
+ hideInitialPrompt: (_a = hideInitialPrompt != null ? hideInitialPrompt : convAgentMetadata.displayHideInitialPrompt) != null ? _a : true,
4718
4692
  data: chatPanelData,
4719
4693
  agent: activeConv.agentId,
4720
4694
  conversation: activeConv.conversationId.startsWith("new-") ? void 0 : activeConv.conversationId,
@@ -4748,7 +4722,7 @@ var ChatPanelWrapper = ({
4748
4722
  );
4749
4723
  };
4750
4724
  ChatPanelWrapper.displayName = "ChatPanelWrapper";
4751
- var AIAgentPanel = ({
4725
+ var AIAgentPanel = React12.forwardRef(({
4752
4726
  agents,
4753
4727
  defaultAgent,
4754
4728
  customerId,
@@ -4780,11 +4754,14 @@ var AIAgentPanel = ({
4780
4754
  showPoweredBy = true,
4781
4755
  conversation,
4782
4756
  actions = [],
4757
+ initialPrompt,
4758
+ initialMessage,
4759
+ hideInitialPrompt,
4783
4760
  followOnQuestions = [],
4784
4761
  followOnPrompt = "",
4785
4762
  historyListLimit = 50,
4786
4763
  showConversationHistory = true
4787
- }) => {
4764
+ }, ref) => {
4788
4765
  var _a, _b, _c, _d;
4789
4766
  const [isCollapsed, setIsCollapsed] = useState8(defaultCollapsed);
4790
4767
  const [isHistoryCollapsed, setIsHistoryCollapsed] = useState8(() => {
@@ -4866,6 +4843,29 @@ var AIAgentPanel = ({
4866
4843
  activeConversationsRef.current = activeConversations;
4867
4844
  const currentConversationIdRef = useRef6(currentConversationId);
4868
4845
  currentConversationIdRef.current = currentConversationId;
4846
+ React12.useImperativeHandle(ref, () => ({
4847
+ startNewConversation: (prompt, agent) => {
4848
+ const targetAgent = agent || currentAgentId;
4849
+ const tempId = `new-${Date.now()}`;
4850
+ setActiveConversations((prev) => {
4851
+ const next = new Map(prev);
4852
+ next.set(tempId, {
4853
+ conversationId: tempId,
4854
+ stableKey: tempId,
4855
+ agentId: targetAgent,
4856
+ history: {},
4857
+ isLoading: false,
4858
+ title: "New conversation",
4859
+ conversationInitialPrompt: prompt
4860
+ });
4861
+ return next;
4862
+ });
4863
+ setCurrentConversationId(tempId);
4864
+ if (onConversationChange) {
4865
+ onConversationChange(tempId);
4866
+ }
4867
+ }
4868
+ }), [currentAgentId, onConversationChange]);
4869
4869
  const [showContextNotification, setShowContextNotification] = useState8(false);
4870
4870
  const prevContextRef = useRef6(null);
4871
4871
  const contextNotificationTimeoutRef = useRef6(null);
@@ -5209,15 +5209,17 @@ var AIAgentPanel = ({
5209
5209
  let filtered = apiConversations;
5210
5210
  if (searchQuery) {
5211
5211
  const query = searchQuery.toLowerCase();
5212
- filtered = filtered.filter(
5213
- (conv) => {
5214
- var _a2, _b2;
5215
- return ((_a2 = conv.title) == null ? void 0 : _a2.toLowerCase().includes(query)) || ((_b2 = conv.summary) == null ? void 0 : _b2.toLowerCase().includes(query));
5216
- }
5217
- );
5212
+ filtered = filtered.filter((conv) => {
5213
+ var _a2, _b2;
5214
+ if ((_a2 = conv.title) == null ? void 0 : _a2.toLowerCase().includes(query)) return true;
5215
+ if ((_b2 = conv.summary) == null ? void 0 : _b2.toLowerCase().includes(query)) return true;
5216
+ const firstPrompt = conversationFirstPrompts[conv.conversationId];
5217
+ if (firstPrompt == null ? void 0 : firstPrompt.toLowerCase().includes(query)) return true;
5218
+ return false;
5219
+ });
5218
5220
  }
5219
5221
  return groupConversationsByTime(filtered, true);
5220
- }, [apiConversations, searchQuery]);
5222
+ }, [apiConversations, searchQuery, conversationFirstPrompts]);
5221
5223
  const effectiveCustomer = useMemo4(() => {
5222
5224
  return __spreadProps(__spreadValues({}, customer), {
5223
5225
  customer_id: customerId
@@ -5826,6 +5828,9 @@ var AIAgentPanel = ({
5826
5828
  effectiveCustomer,
5827
5829
  showPoweredBy,
5828
5830
  actions,
5831
+ initialPrompt,
5832
+ initialMessage,
5833
+ hideInitialPrompt,
5829
5834
  followOnQuestions,
5830
5835
  followOnPrompt,
5831
5836
  agentOptions,
@@ -5836,7 +5841,8 @@ var AIAgentPanel = ({
5836
5841
  totalContextTokens: mergedContext.totalTokens || 0,
5837
5842
  maxContextTokens,
5838
5843
  enableContextDetailView,
5839
- onConversationCreated: handleConversationCreated
5844
+ onConversationCreated: handleConversationCreated,
5845
+ conversationInitialPrompt: activeConv.conversationInitialPrompt
5840
5846
  }
5841
5847
  )), loadingConversationId && /* @__PURE__ */ React12.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ React12.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React12.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ React12.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ React12.createElement(MessageIcon, null), /* @__PURE__ */ React12.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ React12.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ React12.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ React12.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React12.createElement("p", null, "Loading agent..."))),
5842
5848
  /* @__PURE__ */ React12.createElement(
@@ -5850,7 +5856,8 @@ var AIAgentPanel = ({
5850
5856
  /* @__PURE__ */ React12.createElement(DialogFooter, null, /* @__PURE__ */ React12.createElement(Button, { variant: "outline", onClick: handleHandoffCancel }, "Stay with current agent"), /* @__PURE__ */ React12.createElement(Button, { onClick: handleHandoffConfirm }, "Switch agent"))
5851
5857
  )
5852
5858
  );
5853
- };
5859
+ });
5860
+ AIAgentPanel.displayName = "AIAgentPanel";
5854
5861
  var AIAgentPanel_default = AIAgentPanel;
5855
5862
 
5856
5863
  // src/hooks/useConversationStore.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.16.9",
3
+ "version": "0.17.0",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -9,9 +9,9 @@
9
9
  "build": "tsup index.ts --format cjs,esm --dts",
10
10
  "lint": "tsc",
11
11
  "storybook": "storybook dev -p 6006",
12
- "build-storybook": "storybook build",
13
- "test": "vitest run",
14
- "test:watch": "vitest"
12
+ "build-storybook": "storybook build",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest"
15
15
  },
16
16
  "homepage": "https://llmasaservice.io",
17
17
  "repository": {
@@ -47,6 +47,7 @@ export interface ActiveConversation {
47
47
  history: Record<string, { content: string; callId: string }>;
48
48
  isLoading: boolean;
49
49
  title: string;
50
+ conversationInitialPrompt?: string; // Per-conversation initial prompt for programmatic start
50
51
  }
51
52
 
52
53
  /**
@@ -58,6 +59,13 @@ export interface AgentConfig {
58
59
  avatarUrl?: string;
59
60
  }
60
61
 
62
+ /**
63
+ * Imperative handle for programmatic control of AIAgentPanel
64
+ */
65
+ export interface AIAgentPanelHandle {
66
+ startNewConversation: (prompt: string, agent?: string) => void;
67
+ }
68
+
61
69
  /**
62
70
  * Props for AIAgentPanel
63
71
  */
@@ -120,7 +128,10 @@ export interface AIAgentPanelProps {
120
128
  style?: string;
121
129
  }[];
122
130
 
123
- // Follow-on
131
+ // Initial prompt and follow-on
132
+ initialPrompt?: string;
133
+ initialMessage?: string;
134
+ hideInitialPrompt?: boolean;
124
135
  followOnQuestions?: string[];
125
136
  followOnPrompt?: string;
126
137
 
@@ -351,6 +362,9 @@ interface ChatPanelWrapperProps {
351
362
  effectiveCustomer: any;
352
363
  showPoweredBy: boolean;
353
364
  actions: any[];
365
+ initialPrompt?: string;
366
+ initialMessage?: string;
367
+ hideInitialPrompt?: boolean;
354
368
  followOnQuestions: string[];
355
369
  followOnPrompt: string;
356
370
  agentOptions: any[];
@@ -364,6 +378,8 @@ interface ChatPanelWrapperProps {
364
378
  enableContextDetailView: boolean;
365
379
  // Conversation creation callback
366
380
  onConversationCreated: (tempId: string, realId: string) => void;
381
+ // Per-conversation initial prompt
382
+ conversationInitialPrompt?: string;
367
383
  }
368
384
 
369
385
  // Remove React.memo temporarily to debug - ChatPanelWrapper needs to re-render when agentId changes
@@ -382,6 +398,9 @@ const ChatPanelWrapper = (({
382
398
  effectiveCustomer,
383
399
  showPoweredBy,
384
400
  actions,
401
+ initialPrompt,
402
+ initialMessage,
403
+ hideInitialPrompt,
385
404
  followOnQuestions,
386
405
  followOnPrompt,
387
406
  agentOptions,
@@ -393,6 +412,7 @@ const ChatPanelWrapper = (({
393
412
  maxContextTokens,
394
413
  enableContextDetailView,
395
414
  onConversationCreated,
415
+ conversationInitialPrompt,
396
416
  }) => {
397
417
  const convAgentProfile = getAgent(activeConv.agentId);
398
418
  const convAgentMetadata = convAgentProfile?.metadata;
@@ -441,6 +461,9 @@ const ChatPanelWrapper = (({
441
461
  return convAgentProfile?.mcpServers || EMPTY_ARRAY;
442
462
  }, [convAgentProfile?.mcpServers]);
443
463
 
464
+ // Determine which initialPrompt to use - conversation-specific OR prop-based
465
+ const effectiveInitialPrompt = conversationInitialPrompt || initialPrompt;
466
+
444
467
  if (!convAgentMetadata) return null;
445
468
 
446
469
  return (
@@ -456,17 +479,19 @@ const ChatPanelWrapper = (({
456
479
  theme={theme}
457
480
  promptTemplate={convAgentMetadata.displayPromptTemplate || '{{prompt}}'}
458
481
  initialMessage={
459
- convAgentMetadata.displayStartMessageOrPrompt === 'message'
482
+ initialMessage ||
483
+ (convAgentMetadata.displayStartMessageOrPrompt === 'message'
460
484
  ? convAgentMetadata.displayInitialMessageOrPrompt
461
- : undefined
485
+ : undefined)
462
486
  }
463
487
  initialPrompt={
464
- convAgentMetadata.displayStartMessageOrPrompt === 'prompt'
488
+ effectiveInitialPrompt ||
489
+ (convAgentMetadata.displayStartMessageOrPrompt === 'prompt'
465
490
  ? convAgentMetadata.displayInitialMessageOrPrompt
466
- : undefined
491
+ : undefined)
467
492
  }
468
493
  placeholder={convAgentMetadata.displayPlaceholder || 'Type a message...'}
469
- hideInitialPrompt={convAgentMetadata.displayHideInitialPrompt ?? true}
494
+ hideInitialPrompt={hideInitialPrompt ?? convAgentMetadata.displayHideInitialPrompt ?? true}
470
495
  data={chatPanelData}
471
496
  agent={activeConv.agentId}
472
497
  conversation={activeConv.conversationId.startsWith('new-') ? undefined : activeConv.conversationId}
@@ -502,7 +527,7 @@ const ChatPanelWrapper = (({
502
527
 
503
528
  ChatPanelWrapper.displayName = 'ChatPanelWrapper';
504
529
 
505
- const AIAgentPanel: React.FC<AIAgentPanelProps> = ({
530
+ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
506
531
  agents,
507
532
  defaultAgent,
508
533
  customerId,
@@ -534,11 +559,14 @@ const AIAgentPanel: React.FC<AIAgentPanelProps> = ({
534
559
  showPoweredBy = true,
535
560
  conversation,
536
561
  actions = [],
562
+ initialPrompt,
563
+ initialMessage,
564
+ hideInitialPrompt,
537
565
  followOnQuestions = [],
538
566
  followOnPrompt = '',
539
567
  historyListLimit = 50,
540
568
  showConversationHistory = true,
541
- }) => {
569
+ }, ref) => {
542
570
  // Panel state
543
571
  const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
544
572
  const [isHistoryCollapsed, setIsHistoryCollapsed] = useState(() => {
@@ -653,6 +681,33 @@ const AIAgentPanel: React.FC<AIAgentPanelProps> = ({
653
681
  const currentConversationIdRef = useRef<string | null>(currentConversationId);
654
682
  currentConversationIdRef.current = currentConversationId;
655
683
 
684
+ // Imperative handle for programmatic control
685
+ React.useImperativeHandle(ref, () => ({
686
+ startNewConversation: (prompt: string, agent?: string) => {
687
+ const targetAgent = agent || currentAgentId;
688
+ const tempId = `new-${Date.now()}`;
689
+
690
+ setActiveConversations(prev => {
691
+ const next = new Map(prev);
692
+ next.set(tempId, {
693
+ conversationId: tempId,
694
+ stableKey: tempId,
695
+ agentId: targetAgent,
696
+ history: {},
697
+ isLoading: false,
698
+ title: 'New conversation',
699
+ conversationInitialPrompt: prompt,
700
+ });
701
+ return next;
702
+ });
703
+
704
+ setCurrentConversationId(tempId);
705
+ if (onConversationChange) {
706
+ onConversationChange(tempId);
707
+ }
708
+ }
709
+ }), [currentAgentId, onConversationChange]);
710
+
656
711
  // Context change notification state
657
712
  const [showContextNotification, setShowContextNotification] = useState(false);
658
713
  const prevContextRef = useRef<string | null>(null);
@@ -1138,15 +1193,24 @@ console.log("apiKey", apiKey);
1138
1193
  // Filter by search query
1139
1194
  if (searchQuery) {
1140
1195
  const query = searchQuery.toLowerCase();
1141
- filtered = filtered.filter(conv =>
1142
- conv.title?.toLowerCase().includes(query) ||
1143
- conv.summary?.toLowerCase().includes(query)
1144
- );
1196
+ filtered = filtered.filter(conv => {
1197
+ // Check title from API
1198
+ if (conv.title?.toLowerCase().includes(query)) return true;
1199
+
1200
+ // Check summary from API
1201
+ if (conv.summary?.toLowerCase().includes(query)) return true;
1202
+
1203
+ // Check the first prompt (what's actually displayed in the UI)
1204
+ const firstPrompt = conversationFirstPrompts[conv.conversationId];
1205
+ if (firstPrompt?.toLowerCase().includes(query)) return true;
1206
+
1207
+ return false;
1208
+ });
1145
1209
  }
1146
1210
 
1147
1211
  // Group all conversations, show all groups even if empty
1148
1212
  return groupConversationsByTime(filtered, true);
1149
- }, [apiConversations, searchQuery]);
1213
+ }, [apiConversations, searchQuery, conversationFirstPrompts]);
1150
1214
 
1151
1215
  // Build effective customer object with required customerId
1152
1216
  const effectiveCustomer = useMemo(() => {
@@ -2022,6 +2086,9 @@ console.log("apiKey", apiKey);
2022
2086
  effectiveCustomer={effectiveCustomer}
2023
2087
  showPoweredBy={showPoweredBy}
2024
2088
  actions={actions}
2089
+ initialPrompt={initialPrompt}
2090
+ initialMessage={initialMessage}
2091
+ hideInitialPrompt={hideInitialPrompt}
2025
2092
  followOnQuestions={followOnQuestions}
2026
2093
  followOnPrompt={followOnPrompt}
2027
2094
  agentOptions={agentOptions}
@@ -2033,6 +2100,7 @@ console.log("apiKey", apiKey);
2033
2100
  maxContextTokens={maxContextTokens}
2034
2101
  enableContextDetailView={enableContextDetailView}
2035
2102
  onConversationCreated={handleConversationCreated}
2103
+ conversationInitialPrompt={activeConv.conversationInitialPrompt}
2036
2104
  />
2037
2105
  ))}
2038
2106
 
@@ -2090,7 +2158,9 @@ console.log("apiKey", apiKey);
2090
2158
  </Dialog>
2091
2159
  </div>
2092
2160
  );
2093
- };
2161
+ });
2162
+
2163
+ AIAgentPanel.displayName = 'AIAgentPanel';
2094
2164
 
2095
2165
  export default AIAgentPanel;
2096
2166
 
@@ -703,6 +703,8 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
703
703
  const hasNotifiedCompletionRef = useRef<boolean>(true);
704
704
  // Store the latest processed history for callbacks (doesn't trigger re-renders)
705
705
  const latestHistoryRef = useRef<Record<string, HistoryEntry>>(initialHistory);
706
+ // Track if we've sent the initial prompt (prevents loops)
707
+ const initialPromptSentRef = useRef<boolean>(false);
706
708
 
707
709
  // Sync new entries from initialHistory into local history state
708
710
  // This allows parent components to inject messages (e.g., page-based agent suggestions)
@@ -1063,13 +1065,39 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1063
1065
 
1064
1066
  setIsLoading(true);
1065
1067
 
1068
+ // === OPTIMISTIC UPDATE: Show prompt immediately in UI ===
1069
+ // Generate unique key using ISO timestamp prefix + prompt
1070
+ const timestamp = new Date().toISOString();
1071
+ const promptKey = `${timestamp}:${promptToSend.trim()}`;
1072
+
1073
+ // Add prompt to history IMMEDIATELY - this makes it appear in the UI right away
1074
+ setHistory((prevHistory) => ({
1075
+ ...prevHistory,
1076
+ [promptKey]: { content: '', callId: '' },
1077
+ }));
1078
+
1079
+ // Store the key for later use
1080
+ setLastPrompt(promptToSend.trim());
1081
+ setLastKey(promptKey);
1082
+
1083
+ // Scroll to bottom immediately to show the new prompt
1084
+ // Use setTimeout to ensure the DOM has updated
1085
+ setTimeout(() => {
1086
+ scrollToBottom();
1087
+ }, 0);
1088
+
1089
+ // Now proceed with API calls in the background (conversation creation + LLM call)
1066
1090
  // Ensure conversation exists before sending (matches ChatPanel)
1067
1091
  console.log('AIChatPanel.continueChat - about to call ensureConversation');
1068
1092
  ensureConversation().then((convId) => {
1069
1093
  console.log('AIChatPanel.continueChat - ensureConversation resolved with:', convId);
1070
1094
  // Build messagesAndHistory from history (matches ChatPanel)
1095
+ // IMPORTANT: Exclude the current prompt (promptKey) since it's new and we're sending it now
1071
1096
  const messagesAndHistory: { role: string; content: string }[] = [];
1072
1097
  Object.entries(history).forEach(([historyPrompt, historyEntry]) => {
1098
+ // Skip the current prompt we just added optimistically
1099
+ if (historyPrompt === promptKey) return;
1100
+
1073
1101
  // Strip timestamp prefix from prompt before using it (matches ChatPanel)
1074
1102
  let promptForHistory = historyPrompt;
1075
1103
  const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
@@ -1085,19 +1113,10 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1085
1113
  messagesAndHistory.push({ role: 'assistant', content: historyEntry.content });
1086
1114
  });
1087
1115
 
1088
- // Generate unique key using ISO timestamp prefix + prompt (matches ChatPanel)
1089
- const timestamp = new Date().toISOString();
1090
- const promptKey = `${timestamp}:${promptToSend.trim()}`;
1091
-
1092
- // Set history entry before sending (matches ChatPanel)
1093
- setHistory((prevHistory) => ({
1094
- ...prevHistory,
1095
- [promptKey]: { content: '', callId: '' },
1096
- }));
1097
-
1098
1116
  // Build the full prompt - only apply template for first message (matches ChatPanel)
1117
+ // Check if this is the first message by seeing if messagesAndHistory is empty
1099
1118
  let fullPromptToSend = promptToSend.trim();
1100
- if (Object.keys(history).length === 0 && promptTemplate) {
1119
+ if (messagesAndHistory.length === 0 && promptTemplate) {
1101
1120
  fullPromptToSend = promptTemplate.replace('{{prompt}}', fullPromptToSend);
1102
1121
  }
1103
1122
 
@@ -1126,9 +1145,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1126
1145
  newController
1127
1146
  );
1128
1147
 
1129
- setLastPrompt(promptToSend.trim());
1130
1148
  setLastMessages(messagesAndHistory);
1131
- setLastKey(promptKey);
1132
1149
 
1133
1150
  // Notify parent of new conversation ID AFTER send() has started
1134
1151
  // This prevents the component from being remounted before send() runs
@@ -1138,12 +1155,6 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1138
1155
  onConversationCreated(convId);
1139
1156
  }, 100);
1140
1157
  }
1141
-
1142
- // Scroll to bottom after adding the new prompt to show it immediately
1143
- // Use setTimeout to ensure the DOM has updated with the new history entry
1144
- setTimeout(() => {
1145
- scrollToBottom();
1146
- }, 0);
1147
1158
  });
1148
1159
  }, [
1149
1160
  idle,
@@ -1376,69 +1387,14 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1376
1387
  };
1377
1388
  }, []); // Empty deps - only run cleanup on unmount
1378
1389
 
1379
- // Auto-send initialPrompt when it changes (matches ChatPanel behavior)
1380
- // IMPORTANT: Skip if we have initialHistory - that means we're loading an existing conversation
1390
+ // Auto-send initialPrompt exactly once when component mounts
1391
+ // Simple behavior matching original ChatPanel - just send the prompt
1381
1392
  useEffect(() => {
1382
- // Don't auto-send initialPrompt if we loaded existing history
1383
- // This prevents unwanted LLM calls when switching to loaded conversations
1384
- const hasLoadedHistory = initialHistory && Object.keys(initialHistory).length > 0;
1385
-
1386
- // Don't proceed if project_id is not yet available
1387
- if (!project_id) {
1388
- return;
1389
- }
1390
-
1391
- if (initialPrompt && initialPrompt !== '' && initialPrompt !== lastPrompt && !hasLoadedHistory) {
1392
- setIsLoading(true);
1393
- setThinkingBlocks([]);
1394
- setCurrentThinkingIndex(0);
1395
- setUserHasScrolled(false); // Enable auto-scroll for new prompt
1396
-
1397
- // Ensure conversation exists before sending (matches ChatPanel)
1398
- ensureConversation().then((convId) => {
1399
- const controller = new AbortController();
1400
- setLastController(controller);
1401
-
1402
- // Generate timestamp-prefixed key (matches ChatPanel)
1403
- const timestamp = new Date().toISOString();
1404
- const promptKey = `${timestamp}:${initialPrompt}`;
1405
-
1406
- // Set history entry before sending (matches ChatPanel)
1407
- setHistory({ [promptKey]: { content: '', callId: '' } });
1408
-
1409
- // Build prompt with template
1410
- let fullPrompt = initialPrompt;
1411
- if (promptTemplate) {
1412
- fullPrompt = promptTemplate.replace('{{prompt}}', initialPrompt);
1413
- }
1414
-
1415
- send(
1416
- fullPrompt,
1417
- [],
1418
- [
1419
- ...dataWithExtras(),
1420
- { key: '--messages', data: '0' },
1421
- ],
1422
- true,
1423
- true,
1424
- service,
1425
- convId, // Use conversation ID from ensureConversation
1426
- controller
1427
- );
1428
-
1429
- setLastPrompt(initialPrompt);
1430
- setLastMessages([]);
1431
- setLastKey(promptKey);
1432
-
1433
- // Notify parent of new conversation ID AFTER send() has started
1434
- if (convId && onConversationCreated) {
1435
- setTimeout(() => {
1436
- onConversationCreated(convId);
1437
- }, 100);
1438
- }
1439
- });
1393
+ if (initialPrompt && initialPrompt !== '' && !initialPromptSentRef.current) {
1394
+ initialPromptSentRef.current = true;
1395
+ continueChat(initialPrompt);
1440
1396
  }
1441
- }, [initialPrompt, initialHistory, ensureConversation, promptTemplate, send, dataWithExtras, service, lastPrompt, project_id, onConversationCreated]);
1397
+ }, [initialPrompt, continueChat]);
1442
1398
 
1443
1399
  // ============================================================================
1444
1400
  // Render Helpers
@@ -1581,7 +1537,13 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1581
1537
  variant="default"
1582
1538
  size="sm"
1583
1539
  className="ai-chat-agent-suggestion__button"
1584
- onClick={() => onAgentChange(agentId)}
1540
+ onClick={() => {
1541
+ onAgentChange(agentId);
1542
+ // Scroll to bottom after a brief delay to let React re-render
1543
+ setTimeout(() => {
1544
+ bottomRef.current?.scrollIntoView({ behavior: 'auto' });
1545
+ }, 50);
1546
+ }}
1585
1547
  >
1586
1548
  Switch
1587
1549
  </Button>
@@ -57,3 +57,5 @@ export default Button;
57
57
 
58
58
 
59
59
 
60
+
61
+
@@ -153,3 +153,5 @@ export default Dialog;
153
153
 
154
154
 
155
155
 
156
+
157
+
@@ -33,3 +33,5 @@ export default Input;
33
33
 
34
34
 
35
35
 
36
+
37
+
@@ -156,3 +156,5 @@ export default Select;
156
156
 
157
157
 
158
158
 
159
+
160
+
@@ -73,3 +73,5 @@ export default Tooltip;
73
73
 
74
74
 
75
75
 
76
+
77
+
@@ -20,3 +20,5 @@ export type { DialogProps, DialogFooterProps } from './Dialog';
20
20
 
21
21
 
22
22
 
23
+
24
+
package/DEPLOYMENT.md DELETED
@@ -1,193 +0,0 @@
1
- # 📦 Deployment Guide
2
-
3
- ## Current Status
4
- - ✅ Build successful (version 0.16.8)
5
- - ✅ Tests passing (17 tests passed)
6
- - ✅ Ready to publish
7
-
8
- ---
9
-
10
- ## Step 1: Login to npm
11
-
12
- **Important:** You need to be logged in as the owner of the `@hef2024` scope.
13
-
14
- ```bash
15
- npm login
16
- ```
17
-
18
- You'll be prompted for:
19
- - Username (must be the owner of @hef2024 scope)
20
- - Password
21
- - Email
22
- - OTP (if 2FA is enabled)
23
-
24
- Verify you're logged in:
25
- ```bash
26
- npm whoami
27
- ```
28
-
29
- **Note:** If this is the first time publishing this package, make sure you have access to the `@hef2024` scope on npm. If you don't own this scope, you'll need to either:
30
- 1. Create the scope on npm (if available)
31
- 2. Change the package name in `package.json` to a scope you own
32
- 3. Publish without a scope (remove `@hef2024/` prefix)
33
-
34
- ---
35
-
36
- ## Step 2: Publish the Package
37
-
38
- ```bash
39
- # Make sure you're in the llmasaservice-ui-1 directory
40
- cd /Users/indomitablehef/llmasaservice-ui-1
41
-
42
- # Build the package (already done, but good to verify)
43
- npm run build
44
-
45
- # Run tests (already done, but good to verify)
46
- npm test
47
-
48
- # Publish to npm
49
- npm publish --access public
50
- ```
51
-
52
- **Note:** The package name is `@hef2024/llmasaservice-ui` and it's scoped, so `--access public` is required.
53
-
54
- **If you get a 404 error:**
55
- - This might be the first publish of this package
56
- - Make sure you're logged in as the owner of the `@hef2024` scope
57
- - If the scope doesn't exist or you don't have access, you'll need to either create it on npm or change the package name
58
-
59
- ---
60
-
61
- ## Step 3: Unlink in FocusedFit
62
-
63
- Navigate to your FocusedFit project directory and run:
64
-
65
- ```bash
66
- # Unlink the local development version
67
- npm unlink @hef2024/llmasaservice-ui
68
-
69
- # Or if it was linked as 'llmasaservice-ui':
70
- npm unlink llmasaservice-ui
71
-
72
- # Restore to published version
73
- npm install
74
- ```
75
-
76
- **Alternative:** If the unlink doesn't work, you can manually remove the link:
77
-
78
- ```bash
79
- # Check if it's linked
80
- npm ls @hef2024/llmasaservice-ui
81
-
82
- # Remove node_modules and package-lock.json
83
- rm -rf node_modules package-lock.json
84
-
85
- # Reinstall
86
- npm install
87
- ```
88
-
89
- ---
90
-
91
- ## Step 4: Install Published Version in FocusedFit
92
-
93
- In your FocusedFit project:
94
-
95
- ```bash
96
- # Install the published version
97
- npm install @hef2024/llmasaservice-ui@latest
98
-
99
- # Or install a specific version
100
- npm install @hef2024/llmasaservice-ui@0.16.8
101
- ```
102
-
103
- ---
104
-
105
- ## Step 5: Verify Installation
106
-
107
- In your FocusedFit project:
108
-
109
- ```bash
110
- # Check installed version
111
- npm ls @hef2024/llmasaservice-ui
112
-
113
- # Should show something like:
114
- # @hef2024/llmasaservice-ui@0.16.8
115
- ```
116
-
117
- ---
118
-
119
- ## Troubleshooting
120
-
121
- ### If unlink fails:
122
- ```bash
123
- # Remove the symlink manually
124
- rm -rf node_modules/@hef2024/llmasaservice-ui
125
- rm -rf node_modules/llmasaservice-ui
126
-
127
- # Clear npm cache
128
- npm cache clean --force
129
-
130
- # Reinstall
131
- npm install
132
- ```
133
-
134
- ### If you see "local" or "file:" in npm ls:
135
- The package is still linked. Make sure you:
136
- 1. Ran `npm unlink` in FocusedFit
137
- 2. Removed `node_modules` and `package-lock.json`
138
- 3. Ran `npm install` again
139
-
140
- ### To verify the link is removed:
141
- ```bash
142
- # In FocusedFit directory
143
- cat node_modules/@hef2024/llmasaservice-ui/package.json
144
- # Should NOT show a "link" field or point to a local path
145
- ```
146
-
147
- ---
148
-
149
- ## Quick Reference
150
-
151
- ### In llmasaservice-ui-1 (this package):
152
- ```bash
153
- npm run build # Build the package
154
- npm test # Run tests
155
- npm publish --access public # Publish to npm
156
- ```
157
-
158
- ### In FocusedFit (consuming app):
159
- ```bash
160
- npm unlink @hef2024/llmasaservice-ui # Unlink local version
161
- npm install # Restore dependencies
162
- npm install @hef2024/llmasaservice-ui@latest # Install published version
163
- ```
164
-
165
- ---
166
-
167
- ## Version Bumping (for future releases)
168
-
169
- When you need to publish a new version:
170
-
171
- 1. Update version in `package.json`:
172
- ```json
173
- "version": "0.16.9"
174
- ```
175
-
176
- 2. Build and test:
177
- ```bash
178
- npm run build
179
- npm test
180
- ```
181
-
182
- 3. Commit and tag:
183
- ```bash
184
- git add package.json
185
- git commit -m "Bump version to 0.16.9"
186
- git tag v0.16.9
187
- git push origin main --tags
188
- ```
189
-
190
- 4. Publish:
191
- ```bash
192
- npm publish --access public
193
- ```