@hef2024/llmasaservice-ui 0.25.2 → 0.26.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.mjs CHANGED
@@ -4153,6 +4153,58 @@ var ToolInfoModal2 = ({
4153
4153
  var ToolInfoModal_default2 = ToolInfoModal2;
4154
4154
 
4155
4155
  // src/AIChatPanel.tsx
4156
+ var USER_INPUT_TOOL_NAMES = /* @__PURE__ */ new Set(["request_user_input", "ff_request_user_input"]);
4157
+ var MAX_USER_INPUT_QUESTIONS = 5;
4158
+ var MAX_USER_INPUT_OPTIONS = 8;
4159
+ var MAX_USER_INPUT_WRITE_IN_CHARS = 1e3;
4160
+ var DEFAULT_USER_INPUT_TITLE = "Need your input";
4161
+ var DEFAULT_USER_INPUT_INSTRUCTIONS = "Answer these quick questions to continue.";
4162
+ var DEFAULT_CONTEXT_WARN_RATIO = 0.7;
4163
+ var DEFAULT_CONTEXT_COMPACT_RATIO = 0.9;
4164
+ var DEFAULT_CONTEXT_TARGET_RATIO = 0.65;
4165
+ var DEFAULT_COMPACTION_PRESERVE_TURNS = 8;
4166
+ function toBoundedRatio(value, fallback) {
4167
+ if (!Number.isFinite(value)) return fallback;
4168
+ if (value <= 0) return fallback;
4169
+ if (value >= 1) return 1;
4170
+ return value;
4171
+ }
4172
+ function estimateTokensFromText(text) {
4173
+ const normalized = typeof text === "string" ? text : "";
4174
+ if (!normalized) return 0;
4175
+ return Math.ceil(normalized.length / 4);
4176
+ }
4177
+ function estimateTokensFromMessages(messages) {
4178
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
4179
+ return messages.reduce((sum, message) => {
4180
+ const role = typeof (message == null ? void 0 : message.role) === "string" ? message.role : "";
4181
+ const content = typeof (message == null ? void 0 : message.content) === "string" ? message.content : "";
4182
+ return sum + estimateTokensFromText(role) + estimateTokensFromText(content);
4183
+ }, 0);
4184
+ }
4185
+ function estimateTokensFromData(data) {
4186
+ if (!Array.isArray(data) || data.length === 0) return 0;
4187
+ return data.reduce((sum, entry) => {
4188
+ const key = typeof (entry == null ? void 0 : entry.key) === "string" ? entry.key : "";
4189
+ const value = typeof (entry == null ? void 0 : entry.data) === "string" ? entry.data : "";
4190
+ return sum + estimateTokensFromText(key) + estimateTokensFromText(value);
4191
+ }, 0);
4192
+ }
4193
+ function formatTokenCount(tokens) {
4194
+ const safeTokens = Number.isFinite(tokens) ? Math.max(0, Math.floor(tokens)) : 0;
4195
+ if (safeTokens >= 1e3) {
4196
+ return `${(safeTokens / 1e3).toFixed(1)}K`;
4197
+ }
4198
+ return safeTokens.toString();
4199
+ }
4200
+ function buildFallbackWindowMessages(messages, preserveTurns) {
4201
+ if (!Array.isArray(messages) || messages.length === 0) return [];
4202
+ const keepMessageCount = Math.max(2, Math.floor(preserveTurns) * 2);
4203
+ if (messages.length <= keepMessageCount) {
4204
+ return messages;
4205
+ }
4206
+ return messages.slice(messages.length - keepMessageCount);
4207
+ }
4156
4208
  var areToolRequestListsEqual = (a, b) => {
4157
4209
  if (a.length !== b.length) return false;
4158
4210
  for (let index = 0; index < a.length; index += 1) {
@@ -4226,6 +4278,106 @@ var shouldPreserveBoundaryDroppedStreamText = (existingContent, incomingContent)
4226
4278
  return isBoundarySubsetByLines(existingLines, incomingLines);
4227
4279
  };
4228
4280
  var isObjectRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
4281
+ var toTrimmedString = (value) => typeof value === "string" ? value.trim() : "";
4282
+ var toBooleanWithDefault = (value, fallback) => typeof value === "boolean" ? value : fallback;
4283
+ var parseMaybeJsonString = (value) => {
4284
+ if (typeof value !== "string") return value;
4285
+ const trimmed = value.trim();
4286
+ if (!trimmed) return value;
4287
+ if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) return value;
4288
+ try {
4289
+ return JSON.parse(trimmed);
4290
+ } catch (_error) {
4291
+ return value;
4292
+ }
4293
+ };
4294
+ var toArrayFromUnknown = (value) => {
4295
+ const parsed = parseMaybeJsonString(value);
4296
+ return Array.isArray(parsed) ? parsed : [];
4297
+ };
4298
+ var toSlugValue = (value, fallback) => {
4299
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4300
+ return normalized || fallback;
4301
+ };
4302
+ var normalizeUserInputOptionEntry = (value, optionIndex, questionId) => {
4303
+ const normalizedValue = parseMaybeJsonString(value);
4304
+ if (typeof normalizedValue === "string") {
4305
+ const label2 = normalizedValue.trim();
4306
+ if (!label2) return null;
4307
+ return {
4308
+ label: label2,
4309
+ value: toSlugValue(label2, `${questionId}-option-${optionIndex + 1}`),
4310
+ description: ""
4311
+ };
4312
+ }
4313
+ if (!isObjectRecord(normalizedValue)) return null;
4314
+ const label = toTrimmedString(normalizedValue.label) || toTrimmedString(normalizedValue.title) || toTrimmedString(normalizedValue.value);
4315
+ if (!label) return null;
4316
+ const explicitValue = toTrimmedString(normalizedValue.value);
4317
+ return {
4318
+ label,
4319
+ value: explicitValue || toSlugValue(label, `${questionId}-option-${optionIndex + 1}`),
4320
+ description: toTrimmedString(normalizedValue.description)
4321
+ };
4322
+ };
4323
+ var normalizeUserInputQuestionEntry = (value, questionIndex) => {
4324
+ const normalizedValue = parseMaybeJsonString(value);
4325
+ if (!isObjectRecord(normalizedValue)) return null;
4326
+ const fallbackId = `question_${questionIndex + 1}`;
4327
+ const id = toSlugValue(
4328
+ toTrimmedString(normalizedValue.id) || toTrimmedString(normalizedValue.key) || fallbackId,
4329
+ fallbackId
4330
+ );
4331
+ const header = toTrimmedString(normalizedValue.header) || `Question ${questionIndex + 1}`;
4332
+ const question = toTrimmedString(normalizedValue.question) || toTrimmedString(normalizedValue.prompt) || toTrimmedString(normalizedValue.text);
4333
+ if (!question) return null;
4334
+ const rawOptions = toArrayFromUnknown(normalizedValue.options);
4335
+ const normalizedOptions = rawOptions.map((entry, optionIndex) => normalizeUserInputOptionEntry(entry, optionIndex, id)).filter((entry) => Boolean(entry)).slice(0, MAX_USER_INPUT_OPTIONS);
4336
+ if (normalizedOptions.length === 0) {
4337
+ return null;
4338
+ }
4339
+ const parsedWriteInConfig = parseMaybeJsonString(normalizedValue.writeIn);
4340
+ const writeInConfig = isObjectRecord(parsedWriteInConfig) ? parsedWriteInConfig : null;
4341
+ const allowWriteIn = toBooleanWithDefault(normalizedValue.allowWriteIn, false) || toBooleanWithDefault(writeInConfig == null ? void 0 : writeInConfig.enabled, false);
4342
+ const writeInPlaceholder = toTrimmedString(normalizedValue.writeInPlaceholder) || toTrimmedString(writeInConfig == null ? void 0 : writeInConfig.placeholder) || "Add details";
4343
+ return {
4344
+ id,
4345
+ header,
4346
+ question,
4347
+ required: toBooleanWithDefault(normalizedValue.required, true),
4348
+ allowWriteIn,
4349
+ writeInPlaceholder,
4350
+ options: normalizedOptions
4351
+ };
4352
+ };
4353
+ var normalizeUserInputRequest = (args, callId) => {
4354
+ const rawQuestions = toArrayFromUnknown(args.questions);
4355
+ const fallbackQuestion = toTrimmedString(args.question) || toTrimmedString(args.prompt) || toTrimmedString(args.text);
4356
+ const fallbackOptions = toArrayFromUnknown(args.options);
4357
+ const baseQuestions = rawQuestions.length > 0 ? rawQuestions : fallbackQuestion ? [
4358
+ {
4359
+ id: "question_1",
4360
+ header: "Question 1",
4361
+ question: fallbackQuestion,
4362
+ options: fallbackOptions,
4363
+ allowWriteIn: toBooleanWithDefault(args.allowWriteIn, false),
4364
+ writeInPlaceholder: toTrimmedString(args.writeInPlaceholder)
4365
+ }
4366
+ ] : [];
4367
+ const questions = baseQuestions.map((entry, index) => normalizeUserInputQuestionEntry(entry, index)).filter((entry) => Boolean(entry)).slice(0, MAX_USER_INPUT_QUESTIONS);
4368
+ if (questions.length === 0) {
4369
+ throw new Error("request_user_input requires at least one valid question with options.");
4370
+ }
4371
+ return {
4372
+ requestId: toTrimmedString(args.requestId) || toTrimmedString(args.id) || callId,
4373
+ title: toTrimmedString(args.title) || DEFAULT_USER_INPUT_TITLE,
4374
+ instructions: toTrimmedString(args.instructions) || DEFAULT_USER_INPUT_INSTRUCTIONS,
4375
+ submitLabel: toTrimmedString(args.submitLabel) || "Submit",
4376
+ cancelLabel: toTrimmedString(args.cancelLabel) || "Cancel",
4377
+ allowCancel: toBooleanWithDefault(args.allowCancel, true),
4378
+ questions
4379
+ };
4380
+ };
4229
4381
  var stringifyToolArgs = (value) => {
4230
4382
  if (typeof value === "string") return value;
4231
4383
  try {
@@ -4962,6 +5114,8 @@ var ChatInput = React14.memo(({
4962
5114
  onSubmit,
4963
5115
  onQueueSubmit,
4964
5116
  onStop,
5117
+ userInputRequest = null,
5118
+ onCompleteUserInputRequest,
4965
5119
  queuedPrompts = [],
4966
5120
  onClearQueuedPrompt,
4967
5121
  agentOptions,
@@ -4975,7 +5129,8 @@ var ChatInput = React14.memo(({
4975
5129
  maxContextTokens = 8e3,
4976
5130
  enableContextDetailView = false,
4977
5131
  disabledSectionIds = /* @__PURE__ */ new Set(),
4978
- onToggleSection
5132
+ onToggleSection,
5133
+ composerAgentModeControl
4979
5134
  }) => {
4980
5135
  const [inputValue, setInputValue] = useState7("");
4981
5136
  const [dropdownOpen, setDropdownOpen] = useState7(false);
@@ -4985,6 +5140,59 @@ var ChatInput = React14.memo(({
4985
5140
  const textareaRef = useRef6(null);
4986
5141
  const containerRef = useRef6(null);
4987
5142
  const contextPopoverRef = useRef6(null);
5143
+ const [userInputSelections, setUserInputSelections] = useState7({});
5144
+ const [userInputWriteIns, setUserInputWriteIns] = useState7({});
5145
+ const [userInputValidationError, setUserInputValidationError] = useState7("");
5146
+ const [userInputQuestionIndex, setUserInputQuestionIndex] = useState7(0);
5147
+ const [agentModeChipExpanded, setAgentModeChipExpanded] = useState7(false);
5148
+ const isAgentModeActionVisible = composerAgentModeControl !== void 0;
5149
+ const isAgentModeActive = Boolean(composerAgentModeControl == null ? void 0 : composerAgentModeControl.active);
5150
+ const isAgentModeActionDisabled = Boolean(composerAgentModeControl == null ? void 0 : composerAgentModeControl.disabled);
5151
+ const agentModeLabel = String((composerAgentModeControl == null ? void 0 : composerAgentModeControl.label) || "Agent").trim() || "Agent";
5152
+ const agentModeTitle = String((composerAgentModeControl == null ? void 0 : composerAgentModeControl.title) || "").trim() || (isAgentModeActive ? "Agent Mode active for this session" : "Enable Agent Mode");
5153
+ useEffect8(() => {
5154
+ if (isAgentModeActionVisible) return;
5155
+ setAgentModeChipExpanded(false);
5156
+ }, [isAgentModeActionVisible]);
5157
+ useEffect8(() => {
5158
+ if (!isAgentModeActive) return;
5159
+ setAgentModeChipExpanded(true);
5160
+ }, [isAgentModeActive]);
5161
+ const handleAgentModePillClick = useCallback2(() => {
5162
+ if (!isAgentModeActionVisible || isAgentModeActionDisabled) return;
5163
+ if (isAgentModeActive) {
5164
+ setAgentModeChipExpanded(true);
5165
+ return;
5166
+ }
5167
+ setAgentModeChipExpanded(true);
5168
+ if (typeof (composerAgentModeControl == null ? void 0 : composerAgentModeControl.onActivate) === "function") {
5169
+ composerAgentModeControl.onActivate();
5170
+ }
5171
+ }, [
5172
+ composerAgentModeControl,
5173
+ isAgentModeActionDisabled,
5174
+ isAgentModeActionVisible,
5175
+ isAgentModeActive
5176
+ ]);
5177
+ useEffect8(() => {
5178
+ if (!userInputRequest) {
5179
+ setUserInputSelections({});
5180
+ setUserInputWriteIns({});
5181
+ setUserInputValidationError("");
5182
+ setUserInputQuestionIndex(0);
5183
+ return;
5184
+ }
5185
+ const nextSelections = {};
5186
+ const nextWriteIns = {};
5187
+ userInputRequest.questions.forEach((question) => {
5188
+ nextSelections[question.id] = "";
5189
+ nextWriteIns[question.id] = "";
5190
+ });
5191
+ setUserInputSelections(nextSelections);
5192
+ setUserInputWriteIns(nextWriteIns);
5193
+ setUserInputValidationError("");
5194
+ setUserInputQuestionIndex(0);
5195
+ }, [userInputRequest]);
4988
5196
  const autoResize = useCallback2(() => {
4989
5197
  const textarea = textareaRef.current;
4990
5198
  if (textarea) {
@@ -5071,12 +5279,159 @@ var ChatInput = React14.memo(({
5071
5279
  const hasQueuedPrompts = normalizedQueuedPrompts.length > 0;
5072
5280
  const shouldQueueSubmission = isBusy || hasQueuedPrompts;
5073
5281
  const showStopAction = isBusy && !hasDraft;
5282
+ const composerLockedByUserInput = Boolean(userInputRequest);
5283
+ const totalUserInputQuestions = (userInputRequest == null ? void 0 : userInputRequest.questions.length) || 0;
5284
+ const boundedUserInputQuestionIndex = totalUserInputQuestions > 0 ? Math.min(Math.max(userInputQuestionIndex, 0), totalUserInputQuestions - 1) : 0;
5285
+ const activeUserInputQuestion = totalUserInputQuestions > 0 ? (userInputRequest == null ? void 0 : userInputRequest.questions[boundedUserInputQuestionIndex]) || null : null;
5286
+ const isFirstUserInputQuestion = boundedUserInputQuestionIndex <= 0;
5287
+ const isLastUserInputQuestion = totalUserInputQuestions > 0 && boundedUserInputQuestionIndex >= totalUserInputQuestions - 1;
5288
+ const getQuestionAnswerState = useCallback2((question) => {
5289
+ const selectedValue = toTrimmedString(userInputSelections[question.id]);
5290
+ const writeInRaw = toTrimmedString(userInputWriteIns[question.id]);
5291
+ const writeIn = writeInRaw.slice(0, MAX_USER_INPUT_WRITE_IN_CHARS);
5292
+ const selectedOption = question.options.find((option) => option.value === selectedValue) || null;
5293
+ const hasAnswer = Boolean(selectedOption) || question.allowWriteIn && writeIn.length > 0;
5294
+ return {
5295
+ selectedOption,
5296
+ writeIn,
5297
+ hasAnswer
5298
+ };
5299
+ }, [userInputSelections, userInputWriteIns]);
5300
+ const validateUserInputQuestion = useCallback2((question) => {
5301
+ const answerState = getQuestionAnswerState(question);
5302
+ if (question.required && !answerState.hasAnswer) {
5303
+ setUserInputValidationError(`Please answer "${question.header}".`);
5304
+ return false;
5305
+ }
5306
+ return true;
5307
+ }, [getQuestionAnswerState]);
5308
+ const handleUserInputNext = useCallback2(() => {
5309
+ if (!activeUserInputQuestion || isLastUserInputQuestion) return;
5310
+ if (!validateUserInputQuestion(activeUserInputQuestion)) return;
5311
+ setUserInputValidationError("");
5312
+ setUserInputQuestionIndex((prev) => Math.min(prev + 1, totalUserInputQuestions - 1));
5313
+ }, [activeUserInputQuestion, isLastUserInputQuestion, totalUserInputQuestions, validateUserInputQuestion]);
5314
+ const handleUserInputBack = useCallback2(() => {
5315
+ setUserInputValidationError("");
5316
+ setUserInputQuestionIndex((prev) => Math.max(prev - 1, 0));
5317
+ }, []);
5318
+ const handleUserInputSubmit = useCallback2(() => {
5319
+ var _a, _b, _c;
5320
+ if (!userInputRequest || typeof onCompleteUserInputRequest !== "function") {
5321
+ return;
5322
+ }
5323
+ const answers = [];
5324
+ for (let questionIndex = 0; questionIndex < userInputRequest.questions.length; questionIndex += 1) {
5325
+ const question = userInputRequest.questions[questionIndex];
5326
+ const answerState = getQuestionAnswerState(question);
5327
+ if (question.required && !answerState.hasAnswer) {
5328
+ setUserInputQuestionIndex(questionIndex);
5329
+ setUserInputValidationError(`Please answer "${question.header}".`);
5330
+ return;
5331
+ }
5332
+ answers.push({
5333
+ id: question.id,
5334
+ header: question.header,
5335
+ question: question.question,
5336
+ selectedOptionValue: ((_a = answerState.selectedOption) == null ? void 0 : _a.value) || null,
5337
+ selectedOptionLabel: ((_b = answerState.selectedOption) == null ? void 0 : _b.label) || null,
5338
+ writeIn: answerState.writeIn || null,
5339
+ answer: answerState.writeIn || ((_c = answerState.selectedOption) == null ? void 0 : _c.label) || ""
5340
+ });
5341
+ }
5342
+ setUserInputValidationError("");
5343
+ onCompleteUserInputRequest({
5344
+ status: "submitted",
5345
+ requestId: userInputRequest.requestId,
5346
+ answers
5347
+ });
5348
+ }, [getQuestionAnswerState, onCompleteUserInputRequest, userInputRequest]);
5349
+ const handleUserInputCancel = useCallback2(() => {
5350
+ if (!userInputRequest || typeof onCompleteUserInputRequest !== "function") {
5351
+ return;
5352
+ }
5353
+ onCompleteUserInputRequest({
5354
+ status: "cancelled",
5355
+ requestId: userInputRequest.requestId,
5356
+ cancelledBy: "user"
5357
+ });
5358
+ }, [onCompleteUserInputRequest, userInputRequest]);
5074
5359
  return /* @__PURE__ */ React14.createElement(
5075
5360
  "div",
5076
5361
  {
5077
5362
  className: `ai-chat-panel__input-container ${dropdownOpen ? "ai-chat-panel__input-container--dropdown-open" : ""}`,
5078
5363
  ref: containerRef
5079
5364
  },
5365
+ userInputRequest && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover", role: "dialog", "aria-live": "polite", "aria-label": userInputRequest.title }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__header" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__title" }, userInputRequest.title), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__header-meta" }, totalUserInputQuestions > 1 && /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__progress" }, boundedUserInputQuestionIndex + 1, " of ", totalUserInputQuestions), userInputRequest.allowCancel && /* @__PURE__ */ React14.createElement(
5366
+ "button",
5367
+ {
5368
+ type: "button",
5369
+ className: "ai-chat-user-input-popover__cancel-inline",
5370
+ onClick: handleUserInputCancel
5371
+ },
5372
+ userInputRequest.cancelLabel
5373
+ ))), userInputRequest.instructions && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__instructions" }, userInputRequest.instructions), activeUserInputQuestion && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__question" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__question-header" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__question-index" }, boundedUserInputQuestionIndex + 1), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__question-title" }, activeUserInputQuestion.header)), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__question-text" }, activeUserInputQuestion.question), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__options" }, activeUserInputQuestion.options.map((option) => /* @__PURE__ */ React14.createElement("label", { key: `${activeUserInputQuestion.id}:${option.value}`, className: "ai-chat-user-input-popover__option" }, /* @__PURE__ */ React14.createElement(
5374
+ "input",
5375
+ {
5376
+ type: "radio",
5377
+ name: `user-input-${activeUserInputQuestion.id}`,
5378
+ value: option.value,
5379
+ checked: (userInputSelections[activeUserInputQuestion.id] || "") === option.value,
5380
+ onChange: () => {
5381
+ setUserInputValidationError("");
5382
+ setUserInputSelections((prev) => __spreadProps(__spreadValues({}, prev), {
5383
+ [activeUserInputQuestion.id]: option.value
5384
+ }));
5385
+ }
5386
+ }
5387
+ ), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__option-content" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__option-label" }, option.label), option.description ? /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-user-input-popover__option-description" }, option.description) : null)))), activeUserInputQuestion.allowWriteIn && /* @__PURE__ */ React14.createElement(
5388
+ "textarea",
5389
+ {
5390
+ className: "ai-chat-user-input-popover__writein",
5391
+ placeholder: activeUserInputQuestion.writeInPlaceholder,
5392
+ value: userInputWriteIns[activeUserInputQuestion.id] || "",
5393
+ onChange: (event) => {
5394
+ const nextValue = event.target.value.slice(0, MAX_USER_INPUT_WRITE_IN_CHARS);
5395
+ setUserInputValidationError("");
5396
+ setUserInputWriteIns((prev) => __spreadProps(__spreadValues({}, prev), {
5397
+ [activeUserInputQuestion.id]: nextValue
5398
+ }));
5399
+ },
5400
+ rows: 2
5401
+ }
5402
+ )), userInputValidationError ? /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__error" }, userInputValidationError) : null, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__actions" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__actions-left" }, userInputRequest.allowCancel && /* @__PURE__ */ React14.createElement(
5403
+ "button",
5404
+ {
5405
+ type: "button",
5406
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--secondary",
5407
+ onClick: handleUserInputCancel
5408
+ },
5409
+ userInputRequest.cancelLabel
5410
+ )), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-user-input-popover__actions-right" }, !isFirstUserInputQuestion && /* @__PURE__ */ React14.createElement(
5411
+ "button",
5412
+ {
5413
+ type: "button",
5414
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--secondary",
5415
+ onClick: handleUserInputBack
5416
+ },
5417
+ "Back"
5418
+ ), isLastUserInputQuestion ? /* @__PURE__ */ React14.createElement(
5419
+ "button",
5420
+ {
5421
+ type: "button",
5422
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--primary",
5423
+ onClick: handleUserInputSubmit
5424
+ },
5425
+ userInputRequest.submitLabel
5426
+ ) : /* @__PURE__ */ React14.createElement(
5427
+ "button",
5428
+ {
5429
+ type: "button",
5430
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--primary",
5431
+ onClick: handleUserInputNext
5432
+ },
5433
+ "Next"
5434
+ )))),
5080
5435
  hasQueuedPrompts && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-queued-prompts" }, normalizedQueuedPrompts.map((queuedPrompt, index) => /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-queued-prompt", title: queuedPrompt, key: `${index}-${queuedPrompt}` }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-queued-prompt__content" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-queued-prompt__label" }, index === 0 ? "Queued next" : `Queued ${index + 1}`), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-queued-prompt__text" }, queuedPrompt)), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-queued-prompt__actions" }, /* @__PURE__ */ React14.createElement(
5081
5436
  "button",
5082
5437
  {
@@ -5100,8 +5455,9 @@ var ChatInput = React14.memo(({
5100
5455
  {
5101
5456
  ref: textareaRef,
5102
5457
  className: "ai-chat-input",
5103
- placeholder,
5458
+ placeholder: composerLockedByUserInput ? "Answer the question above to continue\u2026" : placeholder,
5104
5459
  value: inputValue,
5460
+ disabled: composerLockedByUserInput,
5105
5461
  onChange: (e) => {
5106
5462
  setInputValue(e.target.value);
5107
5463
  setTimeout(autoResize, 0);
@@ -5115,7 +5471,7 @@ var ChatInput = React14.memo(({
5115
5471
  rows: 1
5116
5472
  }
5117
5473
  )),
5118
- /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__input-footer" }, agentOptions.length > 0 ? /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-agent-selector" }, /* @__PURE__ */ React14.createElement(
5474
+ /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__input-footer" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-input-footer__left" }, agentOptions.length > 0 ? /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-agent-selector" }, /* @__PURE__ */ React14.createElement(
5119
5475
  "button",
5120
5476
  {
5121
5477
  className: "ai-chat-agent-selector__trigger",
@@ -5132,7 +5488,20 @@ var ChatInput = React14.memo(({
5132
5488
  ) : /* @__PURE__ */ React14.createElement(AgentIcon, null),
5133
5489
  /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-agent-selector__label" }, agentsLoading ? "Loading..." : currentAgentLabel || "Select agent"),
5134
5490
  dropdownOpen ? /* @__PURE__ */ React14.createElement(ChevronUpIcon, null) : /* @__PURE__ */ React14.createElement(ChevronDownIcon, null)
5135
- )) : /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }), contextSections.length > 0 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ React14.createElement(
5491
+ )) : null, isAgentModeActionVisible && /* @__PURE__ */ React14.createElement(
5492
+ "button",
5493
+ {
5494
+ className: `ai-chat-agent-mode-trigger ${agentModeChipExpanded ? "ai-chat-agent-mode-trigger--expanded" : ""} ${isAgentModeActive ? "ai-chat-agent-mode-trigger--active" : ""}`,
5495
+ onClick: handleAgentModePillClick,
5496
+ type: "button",
5497
+ title: agentModeTitle,
5498
+ "aria-label": isAgentModeActive ? "Agent active" : "Enable Agent Mode",
5499
+ "aria-pressed": isAgentModeActive,
5500
+ disabled: isAgentModeActionDisabled
5501
+ },
5502
+ /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-agent-mode-trigger__icon", "aria-hidden": "true" }, /* @__PURE__ */ React14.createElement(AgentIcon, null)),
5503
+ /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-agent-mode-trigger__label" }, agentModeLabel)
5504
+ ), agentOptions.length === 0 && !isAgentModeActionVisible ? /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }) : null), contextSections.length > 0 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-pill-anchor" }, /* @__PURE__ */ React14.createElement(
5136
5505
  "button",
5137
5506
  {
5138
5507
  className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""}`,
@@ -5267,13 +5636,13 @@ var ChatInput = React14.memo(({
5267
5636
  /* @__PURE__ */ React14.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ React14.createElement("code", null, isRawSection ? section.rawData : getStructuredContent(section)))
5268
5637
  );
5269
5638
  })))
5270
- )), /* @__PURE__ */ React14.createElement(
5639
+ ))), /* @__PURE__ */ React14.createElement(
5271
5640
  "button",
5272
5641
  {
5273
- className: `ai-chat-send-button ${!showStopAction && !hasDraft ? "ai-chat-send-button--disabled" : ""} ${showStopAction ? "ai-chat-send-button--stop" : ""}`,
5642
+ className: `ai-chat-send-button ${!showStopAction && (!hasDraft || composerLockedByUserInput) ? "ai-chat-send-button--disabled" : ""} ${showStopAction ? "ai-chat-send-button--stop" : ""}`,
5274
5643
  onClick: () => showStopAction ? onStop() : handleSubmit(),
5275
- disabled: !showStopAction && !hasDraft,
5276
- title: showStopAction ? "Stop response" : shouldQueueSubmission ? "Queue prompt" : "Send prompt"
5644
+ disabled: !showStopAction && !hasDraft || !showStopAction && composerLockedByUserInput,
5645
+ title: showStopAction ? "Stop response" : composerLockedByUserInput ? "Answer the question above to continue" : shouldQueueSubmission ? "Queue prompt" : "Send prompt"
5277
5646
  },
5278
5647
  showStopAction ? /* @__PURE__ */ React14.createElement(StopIcon, null) : /* @__PURE__ */ React14.createElement(ArrowUpIcon, null)
5279
5648
  )),
@@ -5350,6 +5719,8 @@ var AIChatPanel = ({
5350
5719
  onToggleSection: propOnToggleSection,
5351
5720
  onConversationCreated,
5352
5721
  onBeforeSend,
5722
+ compactContext,
5723
+ compactionPreserveTurns = DEFAULT_COMPACTION_PRESERVE_TURNS,
5353
5724
  // UI Customization Props
5354
5725
  cssUrl,
5355
5726
  markdownClass,
@@ -5369,7 +5740,8 @@ var AIChatPanel = ({
5369
5740
  // Customer Email Capture Props
5370
5741
  customerEmailCaptureMode = "HIDE",
5371
5742
  customerEmailCapturePlaceholder = "Please enter your email...",
5372
- toolStatusLabelFormatter
5743
+ toolStatusLabelFormatter,
5744
+ composerAgentModeControl
5373
5745
  }) => {
5374
5746
  var _a;
5375
5747
  const publicAPIUrl = "https://api.llmasaservice.io";
@@ -5395,6 +5767,7 @@ var AIChatPanel = ({
5395
5767
  const [copiedCallId, setCopiedCallId] = useState7(null);
5396
5768
  const [feedbackCallId, setFeedbackCallId] = useState7(null);
5397
5769
  const [error, setError] = useState7(null);
5770
+ const [compactionNotice, setCompactionNotice] = useState7(null);
5398
5771
  const lastProcessedErrorRef = useRef6(null);
5399
5772
  const [emailSent, setEmailSent] = useState7(false);
5400
5773
  const [isToolInfoModalOpen, setIsToolInfoModalOpen] = useState7(false);
@@ -5414,6 +5787,9 @@ var AIChatPanel = ({
5414
5787
  const [toolsFetchError, setToolsFetchError] = useState7(false);
5415
5788
  const [resolvedMcpServers, setResolvedMcpServers] = useState7(mcpServers || []);
5416
5789
  const [activeToolCalls, setActiveToolCalls] = useState7([]);
5790
+ const [pendingUserInputRequest, setPendingUserInputRequest] = useState7(null);
5791
+ const pendingUserInputRequestRef = useRef6(null);
5792
+ const pendingUserInputResolverRef = useRef6(null);
5417
5793
  const normalizeToolName = useCallback2((toolName) => {
5418
5794
  return String(toolName != null ? toolName : "").trim().toLowerCase();
5419
5795
  }, []);
@@ -5426,6 +5802,32 @@ var AIChatPanel = ({
5426
5802
  },
5427
5803
  [normalizeToolName]
5428
5804
  );
5805
+ const completePendingUserInputRequest = useCallback2((payload) => {
5806
+ const resolver = pendingUserInputResolverRef.current;
5807
+ pendingUserInputResolverRef.current = null;
5808
+ pendingUserInputRequestRef.current = null;
5809
+ setPendingUserInputRequest(null);
5810
+ if (resolver) {
5811
+ resolver(payload);
5812
+ }
5813
+ }, []);
5814
+ useEffect8(() => {
5815
+ pendingUserInputRequestRef.current = pendingUserInputRequest;
5816
+ }, [pendingUserInputRequest]);
5817
+ useEffect8(() => {
5818
+ return () => {
5819
+ var _a2;
5820
+ const resolver = pendingUserInputResolverRef.current;
5821
+ if (!resolver) return;
5822
+ pendingUserInputResolverRef.current = null;
5823
+ const pendingRequestId = ((_a2 = pendingUserInputRequestRef.current) == null ? void 0 : _a2.requestId) || "request_user_input";
5824
+ resolver({
5825
+ status: "cancelled",
5826
+ requestId: pendingRequestId,
5827
+ cancelledBy: "unmount"
5828
+ });
5829
+ };
5830
+ }, []);
5429
5831
  const alwaysApprovedToolsStorageKey = useMemo2(() => {
5430
5832
  const customerId = (customer == null ? void 0 : customer.customer_id) || (customer == null ? void 0 : customer.id) || (customer == null ? void 0 : customer.customer_user_email) || "anonymous";
5431
5833
  const agentIdForScope = currentAgentId || agent || "default";
@@ -6055,6 +6457,42 @@ var AIChatPanel = ({
6055
6457
  if (hasInFlightTurnWork()) return;
6056
6458
  turnLockRef.current = false;
6057
6459
  }, [hasInFlightTurnWork]);
6460
+ const runRequestUserInputTool = useCallback2(
6461
+ (toolCall) => __async(void 0, null, function* () {
6462
+ const normalizedToolName = normalizeToolName(toolCall.toolName);
6463
+ if (!USER_INPUT_TOOL_NAMES.has(normalizedToolName)) {
6464
+ throw new Error(`Unsupported user input tool: ${toolCall.toolName}`);
6465
+ }
6466
+ if (pendingUserInputResolverRef.current || pendingUserInputRequestRef.current) {
6467
+ throw new Error("A user input request is already pending. Wait for completion before requesting another.");
6468
+ }
6469
+ const request = normalizeUserInputRequest(
6470
+ isObjectRecord(toolCall.args) ? toolCall.args : {},
6471
+ toolCall.callId || toolCall.toolName
6472
+ );
6473
+ pendingUserInputRequestRef.current = request;
6474
+ setPendingUserInputRequest(request);
6475
+ const completion = yield new Promise((resolve) => {
6476
+ pendingUserInputResolverRef.current = resolve;
6477
+ });
6478
+ if (completion.status === "cancelled") {
6479
+ return {
6480
+ status: "cancelled",
6481
+ requestId: request.requestId,
6482
+ cancelledBy: completion.cancelledBy || "user",
6483
+ answers: []
6484
+ };
6485
+ }
6486
+ const answers = Array.isArray(completion.answers) ? completion.answers : [];
6487
+ return {
6488
+ status: "ok",
6489
+ requestId: request.requestId,
6490
+ answers,
6491
+ answeredCount: answers.length
6492
+ };
6493
+ }),
6494
+ [normalizeToolName]
6495
+ );
6058
6496
  const processGivenToolRequests = useCallback2(
6059
6497
  (requests) => __async(void 0, null, function* () {
6060
6498
  var _a2, _b, _c;
@@ -6309,6 +6747,29 @@ ${traceSummary}` : traceSummary;
6309
6747
  var _a3, _b2, _c2, _d, _e, _f, _g;
6310
6748
  const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName) || null;
6311
6749
  const localExecutor = localToolExecutors && typeof localToolExecutors[toolCall.toolName] === "function" ? localToolExecutors[toolCall.toolName] : null;
6750
+ const normalizedToolName = normalizeToolName(toolCall.toolName);
6751
+ if (USER_INPUT_TOOL_NAMES.has(normalizedToolName)) {
6752
+ try {
6753
+ const requestResult = yield runRequestUserInputTool({
6754
+ toolName: toolCall.toolName,
6755
+ callId: toolCall.callId,
6756
+ args: toolCall.args
6757
+ });
6758
+ return {
6759
+ tool_call_id: toolCall.callId,
6760
+ tool_name: toolCall.toolName,
6761
+ result: JSON.stringify(requestResult),
6762
+ isError: false
6763
+ };
6764
+ } catch (error2) {
6765
+ return {
6766
+ tool_call_id: toolCall.callId,
6767
+ tool_name: toolCall.toolName,
6768
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6769
+ isError: true
6770
+ };
6771
+ }
6772
+ }
6312
6773
  if (localExecutor) {
6313
6774
  try {
6314
6775
  const localResult = yield localExecutor(toolCall.args, {
@@ -6625,6 +7086,8 @@ ${traceSummary}` : traceSummary;
6625
7086
  idle,
6626
7087
  stop,
6627
7088
  lastController,
7089
+ normalizeToolName,
7090
+ runRequestUserInputTool,
6628
7091
  waitForStreamIdle,
6629
7092
  releaseTurnLockIfSettled
6630
7093
  ]
@@ -7020,6 +7483,7 @@ ${traceSummary}` : traceSummary;
7020
7483
  hasAutoCollapsedRef.current = false;
7021
7484
  prevBlockCountRef.current = 0;
7022
7485
  setError(null);
7486
+ setCompactionNotice(null);
7023
7487
  setUserHasScrolled(false);
7024
7488
  prevResponseLengthRef.current = 0;
7025
7489
  setResponse("");
@@ -7038,144 +7502,232 @@ ${traceSummary}` : traceSummary;
7038
7502
  setTimeout(() => {
7039
7503
  scrollToBottom(true);
7040
7504
  }, 0);
7041
- console.log("AIChatPanel.continueChat - about to call ensureConversation");
7042
- ensureConversation().then((convId) => {
7043
- console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
7044
- const historyForCall = latestHistoryRef.current || {};
7045
- const messagesAndHistory = [];
7046
- Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
7047
- if (historyPrompt === promptKey) return;
7048
- const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
7049
- const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
7050
- messagesAndHistory.push({ role: "user", content: promptForHistory });
7051
- messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
7052
- });
7053
- let fullPromptToSend = promptToSend;
7054
- if (messagesAndHistory.length === 0 && promptTemplate) {
7055
- fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
7056
- }
7057
- const newController = new AbortController();
7058
- setLastController(newController);
7059
- if (onBeforeSend) {
7060
- void Promise.resolve(
7061
- onBeforeSend({
7062
- prompt: promptToSend,
7505
+ void (() => __async(void 0, null, function* () {
7506
+ var _a2, _b;
7507
+ try {
7508
+ console.log("AIChatPanel.continueChat - about to call ensureConversation");
7509
+ const convId = yield ensureConversation();
7510
+ console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
7511
+ const historyForCall = latestHistoryRef.current || {};
7512
+ const messagesAndHistory = [];
7513
+ Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
7514
+ if (historyPrompt === promptKey) return;
7515
+ const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
7516
+ const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
7517
+ messagesAndHistory.push({ role: "user", content: promptForHistory });
7518
+ messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
7519
+ });
7520
+ let fullPromptToSend = promptToSend;
7521
+ if (messagesAndHistory.length === 0 && promptTemplate) {
7522
+ fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
7523
+ }
7524
+ const newController = new AbortController();
7525
+ setLastController(newController);
7526
+ if (onBeforeSend) {
7527
+ try {
7528
+ yield Promise.resolve(
7529
+ onBeforeSend({
7530
+ prompt: promptToSend,
7531
+ conversationId: convId || null,
7532
+ agentId: agent,
7533
+ service,
7534
+ messages: messagesAndHistory
7535
+ })
7536
+ );
7537
+ } catch (error2) {
7538
+ console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
7539
+ }
7540
+ }
7541
+ const warnRatio = toBoundedRatio(DEFAULT_CONTEXT_WARN_RATIO, DEFAULT_CONTEXT_WARN_RATIO);
7542
+ const compactRatio = toBoundedRatio(DEFAULT_CONTEXT_COMPACT_RATIO, DEFAULT_CONTEXT_COMPACT_RATIO);
7543
+ const targetRatio = toBoundedRatio(DEFAULT_CONTEXT_TARGET_RATIO, DEFAULT_CONTEXT_TARGET_RATIO);
7544
+ const maxTokens = Math.max(1, Number.isFinite(Number(maxContextTokens)) ? Math.floor(Number(maxContextTokens)) : 8e3);
7545
+ const dataEntries = dataWithExtras();
7546
+ const projectedBefore = estimateTokensFromText(fullPromptToSend) + estimateTokensFromMessages(messagesAndHistory) + Math.max(Number(totalContextTokens) || 0, estimateTokensFromData(dataEntries));
7547
+ const warnThreshold = Math.floor(maxTokens * warnRatio);
7548
+ const compactThreshold = Math.floor(maxTokens * compactRatio);
7549
+ const targetThreshold = Math.floor(maxTokens * targetRatio);
7550
+ const preserveTurnsForCompaction = Number.isFinite(Number(compactionPreserveTurns)) && Number(compactionPreserveTurns) > 0 ? Math.floor(Number(compactionPreserveTurns)) : DEFAULT_COMPACTION_PRESERVE_TURNS;
7551
+ const canDropOlderTurns = buildFallbackWindowMessages(messagesAndHistory, preserveTurnsForCompaction).length < messagesAndHistory.length;
7552
+ let sendPrompt = fullPromptToSend;
7553
+ let sendMessages = messagesAndHistory;
7554
+ if (projectedBefore >= warnThreshold && projectedBefore < compactThreshold) {
7555
+ const nearLimitMessage = "Context is approaching the configured limit. A later turn may auto-compact.";
7556
+ setCompactionNotice({
7557
+ level: "warning",
7558
+ message: nearLimitMessage
7559
+ });
7560
+ }
7561
+ if (compactContext && projectedBefore >= compactThreshold && canDropOlderTurns) {
7562
+ setCompactionNotice({
7563
+ level: "info",
7564
+ message: "Automatically compacting context"
7565
+ });
7566
+ const compactionInput = {
7567
+ prompt: fullPromptToSend,
7063
7568
  conversationId: convId || null,
7064
7569
  agentId: agent,
7065
7570
  service,
7066
- messages: messagesAndHistory
7067
- })
7068
- ).catch((error2) => {
7069
- console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
7070
- });
7071
- }
7072
- send(
7073
- fullPromptToSend,
7074
- messagesAndHistory,
7075
- [
7076
- ...dataWithExtras(),
7077
- { key: "--messages", data: messagesAndHistory.length.toString() }
7078
- ],
7079
- true,
7080
- // stream
7081
- true,
7082
- // includeHistory
7083
- service,
7084
- // group_id from agent config
7085
- convId,
7086
- // Use the conversation ID from ensureConversation
7087
- newController,
7088
- void 0,
7089
- // onComplete
7090
- (errorMsg) => {
7091
- console.log("[AIChatPanel] Error callback triggered:", errorMsg);
7092
- const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
7093
- if (isAbortError) {
7094
- if (suppressAbortHistoryUpdateRef.current) {
7095
- setIsLoading(false);
7096
- releaseTurnLockIfSettled();
7097
- return;
7571
+ messages: messagesAndHistory,
7572
+ data: dataEntries,
7573
+ maxContextTokens: maxTokens,
7574
+ totalContextTokens: Number(totalContextTokens) || 0,
7575
+ warnRatio,
7576
+ compactRatio,
7577
+ targetRatio,
7578
+ preserveTurns: preserveTurnsForCompaction,
7579
+ projectedTokens: {
7580
+ projectedBefore,
7581
+ projectedAfter: projectedBefore,
7582
+ warnThreshold,
7583
+ compactThreshold,
7584
+ targetThreshold
7098
7585
  }
7099
- console.log("[AIChatPanel] Request was aborted by user");
7100
- if (promptKey) {
7101
- setHistory((prev) => {
7102
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7103
- return __spreadProps(__spreadValues({}, prev), {
7104
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7105
- content: "Response canceled",
7106
- callId: lastCallId || existingEntry.callId || ""
7107
- })
7586
+ };
7587
+ try {
7588
+ const compactResult = yield Promise.resolve(compactContext(compactionInput));
7589
+ if (compactResult && typeof compactResult === "object") {
7590
+ if (typeof compactResult.prompt === "string" && compactResult.prompt.trim()) {
7591
+ sendPrompt = compactResult.prompt;
7592
+ }
7593
+ if (Array.isArray(compactResult.messages)) {
7594
+ sendMessages = compactResult.messages;
7595
+ }
7596
+ if (compactResult.action === "compacted") {
7597
+ const usageBefore = Number((_a2 = compactResult.tokenUsage) == null ? void 0 : _a2.projectedBefore);
7598
+ const usageAfter = Number((_b = compactResult.tokenUsage) == null ? void 0 : _b.projectedAfter);
7599
+ const hasUsageNumbers = Number.isFinite(usageBefore) && Number.isFinite(usageAfter);
7600
+ const compactedMessage = hasUsageNumbers ? `Context compacted for this send (${formatTokenCount(usageBefore)} -> ${formatTokenCount(usageAfter)} tokens).` : "Older turns were compacted to keep this thread within context limits.";
7601
+ setCompactionNotice({
7602
+ level: "success",
7603
+ message: compactResult.warning || compactedMessage
7108
7604
  });
7109
- });
7605
+ } else if (compactResult.action === "fallback_window") {
7606
+ setCompactionNotice({
7607
+ level: "warning",
7608
+ message: compactResult.warning || "Compaction fallback applied. Sending only the most recent turns."
7609
+ });
7610
+ } else {
7611
+ setCompactionNotice(null);
7612
+ }
7110
7613
  }
7111
- } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
7112
- setError({
7113
- message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7114
- code: "413"
7614
+ } catch (error2) {
7615
+ console.warn("[AIChatPanel] compactContext failed, applying local fallback window", error2);
7616
+ sendMessages = buildFallbackWindowMessages(messagesAndHistory, preserveTurnsForCompaction);
7617
+ setCompactionNotice({
7618
+ level: "warning",
7619
+ message: "Compaction service unavailable. Sending a reduced recent-window context."
7115
7620
  });
7116
- if (promptKey) {
7117
- setHistory((prev) => {
7118
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7119
- return __spreadProps(__spreadValues({}, prev), {
7120
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7121
- content: `Error: ${errorMsg}`,
7122
- callId: lastCallId || existingEntry.callId || ""
7123
- })
7621
+ }
7622
+ }
7623
+ send(
7624
+ sendPrompt,
7625
+ sendMessages,
7626
+ [
7627
+ ...dataEntries,
7628
+ { key: "--messages", data: sendMessages.length.toString() }
7629
+ ],
7630
+ true,
7631
+ // stream
7632
+ true,
7633
+ // includeHistory
7634
+ service,
7635
+ // group_id from agent config
7636
+ convId,
7637
+ // Use the conversation ID from ensureConversation
7638
+ newController,
7639
+ void 0,
7640
+ // onComplete
7641
+ (errorMsg) => {
7642
+ console.log("[AIChatPanel] Error callback triggered:", errorMsg);
7643
+ const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
7644
+ if (isAbortError) {
7645
+ if (suppressAbortHistoryUpdateRef.current) {
7646
+ setIsLoading(false);
7647
+ releaseTurnLockIfSettled();
7648
+ return;
7649
+ }
7650
+ console.log("[AIChatPanel] Request was aborted by user");
7651
+ if (promptKey) {
7652
+ setHistory((prev) => {
7653
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7654
+ return __spreadProps(__spreadValues({}, prev), {
7655
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7656
+ content: "Response canceled",
7657
+ callId: lastCallId || existingEntry.callId || ""
7658
+ })
7659
+ });
7124
7660
  });
7661
+ }
7662
+ } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
7663
+ setError({
7664
+ message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7665
+ code: "413"
7125
7666
  });
7126
- }
7127
- } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
7128
- setError({
7129
- message: "Network error. Please check your connection and try again.",
7130
- code: "NETWORK_ERROR"
7131
- });
7132
- if (promptKey) {
7133
- setHistory((prev) => {
7134
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7135
- return __spreadProps(__spreadValues({}, prev), {
7136
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7137
- content: `Error: ${errorMsg}`,
7138
- callId: lastCallId || existingEntry.callId || ""
7139
- })
7667
+ if (promptKey) {
7668
+ setHistory((prev) => {
7669
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7670
+ return __spreadProps(__spreadValues({}, prev), {
7671
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7672
+ content: `Error: ${errorMsg}`,
7673
+ callId: lastCallId || existingEntry.callId || ""
7674
+ })
7675
+ });
7140
7676
  });
7677
+ }
7678
+ } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
7679
+ setError({
7680
+ message: "Network error. Please check your connection and try again.",
7681
+ code: "NETWORK_ERROR"
7141
7682
  });
7142
- }
7143
- } else {
7144
- setError({
7145
- message: errorMsg,
7146
- code: "UNKNOWN_ERROR"
7147
- });
7148
- if (promptKey) {
7149
- setHistory((prev) => {
7150
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7151
- return __spreadProps(__spreadValues({}, prev), {
7152
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7153
- content: `Error: ${errorMsg}`,
7154
- callId: lastCallId || existingEntry.callId || ""
7155
- })
7683
+ if (promptKey) {
7684
+ setHistory((prev) => {
7685
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7686
+ return __spreadProps(__spreadValues({}, prev), {
7687
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7688
+ content: `Error: ${errorMsg}`,
7689
+ callId: lastCallId || existingEntry.callId || ""
7690
+ })
7691
+ });
7156
7692
  });
7693
+ }
7694
+ } else {
7695
+ setError({
7696
+ message: errorMsg,
7697
+ code: "UNKNOWN_ERROR"
7157
7698
  });
7699
+ if (promptKey) {
7700
+ setHistory((prev) => {
7701
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7702
+ return __spreadProps(__spreadValues({}, prev), {
7703
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7704
+ content: `Error: ${errorMsg}`,
7705
+ callId: lastCallId || existingEntry.callId || ""
7706
+ })
7707
+ });
7708
+ });
7709
+ }
7158
7710
  }
7711
+ setIsLoading(false);
7712
+ releaseTurnLockIfSettled();
7159
7713
  }
7160
- setIsLoading(false);
7161
- releaseTurnLockIfSettled();
7714
+ );
7715
+ setLastMessages(sendMessages);
7716
+ if (convId && onConversationCreated) {
7717
+ setTimeout(() => {
7718
+ onConversationCreated(convId);
7719
+ }, 100);
7162
7720
  }
7163
- );
7164
- setLastMessages(messagesAndHistory);
7165
- if (convId && onConversationCreated) {
7166
- setTimeout(() => {
7167
- onConversationCreated(convId);
7168
- }, 100);
7721
+ } catch (error2) {
7722
+ console.error("[AIChatPanel] Failed to send prompt:", error2);
7723
+ setError({
7724
+ message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7725
+ code: "UNKNOWN_ERROR"
7726
+ });
7727
+ setIsLoading(false);
7728
+ releaseTurnLockIfSettled();
7169
7729
  }
7170
- }).catch((error2) => {
7171
- console.error("[AIChatPanel] Failed to send prompt:", error2);
7172
- setError({
7173
- message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7174
- code: "UNKNOWN_ERROR"
7175
- });
7176
- setIsLoading(false);
7177
- releaseTurnLockIfSettled();
7178
- });
7730
+ }))();
7179
7731
  }, [
7180
7732
  clearFollowOnQuestionsNextPrompt,
7181
7733
  promptTemplate,
@@ -7190,6 +7742,10 @@ ${traceSummary}` : traceSummary;
7190
7742
  scrollToBottom,
7191
7743
  onConversationCreated,
7192
7744
  onBeforeSend,
7745
+ compactContext,
7746
+ compactionPreserveTurns,
7747
+ maxContextTokens,
7748
+ totalContextTokens,
7193
7749
  hasInFlightTurnWork,
7194
7750
  queuePromptForLater,
7195
7751
  releaseTurnLockIfSettled,
@@ -7209,8 +7765,15 @@ ${traceSummary}` : traceSummary;
7209
7765
  continueChat(question);
7210
7766
  }, [continueChat]);
7211
7767
  const handleStop = useCallback2(() => {
7768
+ if (pendingUserInputRequest) {
7769
+ completePendingUserInputRequest({
7770
+ status: "cancelled",
7771
+ requestId: pendingUserInputRequest.requestId,
7772
+ cancelledBy: "stop"
7773
+ });
7774
+ }
7212
7775
  stop(lastController);
7213
- }, [stop, lastController]);
7776
+ }, [completePendingUserInputRequest, lastController, pendingUserInputRequest, stop]);
7214
7777
  const handleNewConversation = useCallback2(() => {
7215
7778
  if (!newConversationConfirm) {
7216
7779
  setNewConversationConfirm(true);
@@ -7221,8 +7784,16 @@ ${traceSummary}` : traceSummary;
7221
7784
  if (!idle) {
7222
7785
  stop(lastController);
7223
7786
  }
7787
+ if (pendingUserInputRequest) {
7788
+ completePendingUserInputRequest({
7789
+ status: "cancelled",
7790
+ requestId: pendingUserInputRequest.requestId,
7791
+ cancelledBy: "reset"
7792
+ });
7793
+ }
7224
7794
  setResponse("");
7225
7795
  setHistory({});
7796
+ setCompactionNotice(null);
7226
7797
  latestHistoryRef.current = {};
7227
7798
  hasNotifiedCompletionRef.current = true;
7228
7799
  queuedPromptsRef.current = [];
@@ -7244,6 +7815,7 @@ ${traceSummary}` : traceSummary;
7244
7815
  setThinkingBlocks([]);
7245
7816
  setCurrentThinkingIndex(0);
7246
7817
  setCollapsedBlocks(/* @__PURE__ */ new Set());
7818
+ setPendingUserInputRequest(null);
7247
7819
  hasAutoCollapsedRef.current = false;
7248
7820
  prevBlockCountRef.current = 0;
7249
7821
  setJustReset(true);
@@ -7257,7 +7829,16 @@ ${traceSummary}` : traceSummary;
7257
7829
  setJustReset(false);
7258
7830
  (_a2 = responseAreaRef.current) == null ? void 0 : _a2.scrollTo({ top: 0, behavior: "smooth" });
7259
7831
  }, 100);
7260
- }, [newConversationConfirm, idle, stop, lastController, setResponse, followOnQuestions]);
7832
+ }, [
7833
+ completePendingUserInputRequest,
7834
+ followOnQuestions,
7835
+ idle,
7836
+ lastController,
7837
+ newConversationConfirm,
7838
+ pendingUserInputRequest,
7839
+ setResponse,
7840
+ stop
7841
+ ]);
7261
7842
  useEffect8(() => {
7262
7843
  if (!response || !lastKey || justReset) return;
7263
7844
  const extractedToolRequests = extractToolRequests(response);
@@ -7971,6 +8552,15 @@ ${traceSummary}` : traceSummary;
7971
8552
  },
7972
8553
  /* @__PURE__ */ React14.createElement(CloseIcon, null)
7973
8554
  )),
8555
+ compactionNotice && compactionNotice.level !== "info" && /* @__PURE__ */ React14.createElement("div", { className: `ai-chat-compaction-banner ai-chat-compaction-banner--${compactionNotice.level}` }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-compaction-banner__message" }, compactionNotice.message), /* @__PURE__ */ React14.createElement(
8556
+ "button",
8557
+ {
8558
+ className: "ai-chat-compaction-banner__close",
8559
+ onClick: () => setCompactionNotice(null),
8560
+ "aria-label": "Dismiss context notice"
8561
+ },
8562
+ /* @__PURE__ */ React14.createElement(CloseIcon, null)
8563
+ )),
7974
8564
  /* @__PURE__ */ React14.createElement(ScrollArea, { className: "ai-chat-panel__messages", ref: responseAreaRef }, initialMessage && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__content" }, markdownClass ? /* @__PURE__ */ React14.createElement("div", { className: markdownClass }, /* @__PURE__ */ React14.createElement(
7975
8565
  ReactMarkdown2,
7976
8566
  {
@@ -8153,6 +8743,7 @@ ${traceSummary}` : traceSummary;
8153
8743
  },
8154
8744
  question
8155
8745
  ))), /* @__PURE__ */ React14.createElement("div", { ref: bottomRef })),
8746
+ (compactionNotice == null ? void 0 : compactionNotice.level) === "info" && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-compaction-inline-status", role: "status", "aria-live": "polite" }, /* @__PURE__ */ React14.createElement("span", null, compactionNotice.message)),
8156
8747
  (showSaveButton || showEmailButton || showCallToAction) && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-button-container" }, showSaveButton && /* @__PURE__ */ React14.createElement(
8157
8748
  Button,
8158
8749
  {
@@ -8292,6 +8883,8 @@ ${traceSummary}` : traceSummary;
8292
8883
  onSubmit: continueChat,
8293
8884
  onQueueSubmit: handleQueuePrompt,
8294
8885
  onStop: handleStop,
8886
+ userInputRequest: pendingUserInputRequest,
8887
+ onCompleteUserInputRequest: completePendingUserInputRequest,
8295
8888
  queuedPrompts,
8296
8889
  onClearQueuedPrompt: handleClearQueuedPrompt,
8297
8890
  agentOptions,
@@ -8305,7 +8898,8 @@ ${traceSummary}` : traceSummary;
8305
8898
  maxContextTokens,
8306
8899
  enableContextDetailView,
8307
8900
  disabledSectionIds,
8308
- onToggleSection: handleToggleSection
8901
+ onToggleSection: handleToggleSection,
8902
+ composerAgentModeControl
8309
8903
  }
8310
8904
  ),
8311
8905
  showPoweredBy && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__footer" }, mcpServers && mcpServers.length > 0 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-tools-status" }, /* @__PURE__ */ React14.createElement(
@@ -8543,7 +9137,7 @@ var normalizeConversationListPayload = (payload) => {
8543
9137
  if (!title) {
8544
9138
  title = conv.summary || extractTitleFromConversation(conv);
8545
9139
  }
8546
- const resolvedConversationId = conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
9140
+ const resolvedConversationId = conv.sessionId || conv.session_id || conv.sessionKey || conv.session_key || conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
8547
9141
  return {
8548
9142
  conversationId: resolvedConversationId,
8549
9143
  title,
@@ -9032,6 +9626,8 @@ var ChatPanelWrapper = ({
9032
9626
  onToggleSection,
9033
9627
  onConversationCreated,
9034
9628
  onBeforeSend,
9629
+ compactContext,
9630
+ compactionPreserveTurns,
9035
9631
  conversationInitialPrompt,
9036
9632
  // New props from ChatPanel port
9037
9633
  cssUrl,
@@ -9053,7 +9649,8 @@ var ChatPanelWrapper = ({
9053
9649
  localToolExecutors,
9054
9650
  traceContextMode,
9055
9651
  autoApproveTools,
9056
- toolStatusLabelFormatter
9652
+ toolStatusLabelFormatter,
9653
+ composerAgentModeControl
9057
9654
  }) => {
9058
9655
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
9059
9656
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -9151,6 +9748,8 @@ var ChatPanelWrapper = ({
9151
9748
  onToggleSection,
9152
9749
  onConversationCreated: conversationCreatedCallback,
9153
9750
  onBeforeSend: beforeSendCallback,
9751
+ compactContext,
9752
+ compactionPreserveTurns,
9154
9753
  cssUrl,
9155
9754
  markdownClass,
9156
9755
  width,
@@ -9170,7 +9769,8 @@ var ChatPanelWrapper = ({
9170
9769
  localToolExecutors,
9171
9770
  traceContextMode,
9172
9771
  autoApproveTools,
9173
- toolStatusLabelFormatter
9772
+ toolStatusLabelFormatter,
9773
+ composerAgentModeControl
9174
9774
  }
9175
9775
  )
9176
9776
  );
@@ -9222,6 +9822,8 @@ var AIAgentPanel = React15.forwardRef(({
9222
9822
  onAgentSwitch,
9223
9823
  onConversationChange,
9224
9824
  onBeforeSend,
9825
+ compactContext,
9826
+ compactionPreserveTurns,
9225
9827
  historyChangedCallback,
9226
9828
  responseCompleteCallback,
9227
9829
  thumbsUpClick,
@@ -9237,6 +9839,7 @@ var AIAgentPanel = React15.forwardRef(({
9237
9839
  followOnQuestions = [],
9238
9840
  followOnPrompt = "",
9239
9841
  historyListLimit = 50,
9842
+ conversationSubtitleResolver,
9240
9843
  showConversationHistory = true,
9241
9844
  // New props from ChatPanel port
9242
9845
  cssUrl,
@@ -9258,7 +9861,8 @@ var AIAgentPanel = React15.forwardRef(({
9258
9861
  localToolExecutors,
9259
9862
  traceContextMode = "standard",
9260
9863
  autoApproveTools,
9261
- toolStatusLabelFormatter
9864
+ toolStatusLabelFormatter,
9865
+ composerAgentModeControl
9262
9866
  }, ref) => {
9263
9867
  var _a, _b, _c, _d;
9264
9868
  useEffect10(() => {
@@ -9396,6 +10000,7 @@ var AIAgentPanel = React15.forwardRef(({
9396
10000
  "This Month": false,
9397
10001
  "Older": false
9398
10002
  });
10003
+ const [recentlyFocusedConversationId, setRecentlyFocusedConversationId] = useState9(null);
9399
10004
  const [disabledContextSections, setDisabledContextSections] = useState9(/* @__PURE__ */ new Map());
9400
10005
  const {
9401
10006
  agents: agentProfiles,
@@ -9781,8 +10386,11 @@ var AIAgentPanel = React15.forwardRef(({
9781
10386
  if (loadingConversationId === targetConversationId) {
9782
10387
  return;
9783
10388
  }
10389
+ if (currentConversationIdRef.current !== targetConversationId) {
10390
+ setCurrentConversationId(targetConversationId);
10391
+ }
9784
10392
  const existingActive = activeConversationsRef.current.get(targetConversationId);
9785
- if (existingActive && existingActive.transcriptLoaded && currentConversationIdRef.current === targetConversationId) {
10393
+ if (existingActive && existingActive.transcriptLoaded) {
9786
10394
  return;
9787
10395
  }
9788
10396
  const apiConversation = apiConversations.find(
@@ -9922,6 +10530,50 @@ var AIAgentPanel = React15.forwardRef(({
9922
10530
  }
9923
10531
  return groupConversationsByTime(filtered, true);
9924
10532
  }, [apiConversations, searchQuery, conversationFirstPrompts]);
10533
+ useEffect10(() => {
10534
+ if (!currentConversationId) {
10535
+ return;
10536
+ }
10537
+ const matchingGroup = groupedConversations.find(
10538
+ (group) => group.conversations.some((conversation2) => conversation2.conversationId === currentConversationId)
10539
+ );
10540
+ if (!matchingGroup) {
10541
+ return;
10542
+ }
10543
+ setExpandedSections((prev) => {
10544
+ if (prev[matchingGroup.label]) {
10545
+ return prev;
10546
+ }
10547
+ return __spreadProps(__spreadValues({}, prev), {
10548
+ [matchingGroup.label]: true
10549
+ });
10550
+ });
10551
+ }, [currentConversationId, groupedConversations]);
10552
+ useEffect10(() => {
10553
+ if (!currentConversationId) {
10554
+ return;
10555
+ }
10556
+ setRecentlyFocusedConversationId(currentConversationId);
10557
+ const timer = window.setTimeout(() => {
10558
+ setRecentlyFocusedConversationId((prev) => prev === currentConversationId ? null : prev);
10559
+ }, 1200);
10560
+ return () => window.clearTimeout(timer);
10561
+ }, [currentConversationId]);
10562
+ const conversationRowRefs = useRef7(/* @__PURE__ */ new Map());
10563
+ useEffect10(() => {
10564
+ if (!currentConversationId) {
10565
+ return;
10566
+ }
10567
+ const row = conversationRowRefs.current.get(currentConversationId);
10568
+ if (!row) {
10569
+ return;
10570
+ }
10571
+ row.scrollIntoView({
10572
+ block: "nearest",
10573
+ inline: "nearest",
10574
+ behavior: "smooth"
10575
+ });
10576
+ }, [currentConversationId, expandedSections, groupedConversations]);
9925
10577
  const effectiveCustomer = useMemo4(() => {
9926
10578
  return __spreadProps(__spreadValues({}, customer), {
9927
10579
  customer_id: customerId
@@ -10496,26 +11148,36 @@ var AIAgentPanel = React15.forwardRef(({
10496
11148
  onClick: handleNewConversation
10497
11149
  },
10498
11150
  /* @__PURE__ */ React15.createElement(PlusIcon, null)
10499
- ))), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse History", side: "bottom" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleHistoryCollapse }, /* @__PURE__ */ React15.createElement(SidebarIcon, null))), collapsible && /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null))), /* @__PURE__ */ React15.createElement(ScrollArea, { className: "ai-agent-panel__conversations" }, activeConversationsList.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group ai-agent-panel__group--active" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group-label" }, /* @__PURE__ */ React15.createElement("span", null, "Active (", activeConversationsList.length, ")")), activeConversationsList.map((activeConv) => /* @__PURE__ */ React15.createElement(
10500
- "div",
10501
- {
10502
- key: activeConv.stableKey,
10503
- className: `ai-agent-panel__conversation ai-agent-panel__conversation--active-item ${currentConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--current" : ""}`,
10504
- onClick: () => {
10505
- commitConversationSelection(activeConv.conversationId, true);
10506
- }
10507
- },
10508
- /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ React15.createElement(LoadingDotIcon, null), /* @__PURE__ */ React15.createElement("span", null, activeConv.title))),
10509
- /* @__PURE__ */ React15.createElement(
10510
- "button",
11151
+ ))), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse History", side: "bottom" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleHistoryCollapse }, /* @__PURE__ */ React15.createElement(SidebarIcon, null))), collapsible && /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null))), /* @__PURE__ */ React15.createElement(ScrollArea, { className: "ai-agent-panel__conversations" }, activeConversationsList.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group ai-agent-panel__group--active" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group-label" }, /* @__PURE__ */ React15.createElement("span", null, "Active (", activeConversationsList.length, ")")), activeConversationsList.map((activeConv) => {
11152
+ const subtitle = (conversationSubtitleResolver == null ? void 0 : conversationSubtitleResolver(activeConv.conversationId, activeConv)) || "";
11153
+ return /* @__PURE__ */ React15.createElement(
11154
+ "div",
10511
11155
  {
10512
- className: "ai-agent-panel__conversation-close",
10513
- onClick: (e) => handleCloseConversation(activeConv.conversationId, e),
10514
- title: "Close conversation"
11156
+ key: activeConv.stableKey,
11157
+ className: `ai-agent-panel__conversation ai-agent-panel__conversation--active-item ${currentConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${recentlyFocusedConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--just-focused" : ""}`,
11158
+ ref: (node) => {
11159
+ if (node) {
11160
+ conversationRowRefs.current.set(activeConv.conversationId, node);
11161
+ } else {
11162
+ conversationRowRefs.current.delete(activeConv.conversationId);
11163
+ }
11164
+ },
11165
+ onClick: () => {
11166
+ commitConversationSelection(activeConv.conversationId, true);
11167
+ }
10515
11168
  },
10516
- /* @__PURE__ */ React15.createElement(CloseIcon2, null)
10517
- )
10518
- ))), conversationsLoading ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("span", null, "Loading conversations...")) : conversationsError ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ React15.createElement("p", null, "Error: ", conversationsError), /* @__PURE__ */ React15.createElement(Button, { variant: "secondary", size: "sm", onClick: handleRefreshConversations }, "Retry")) : groupedConversations.length === 0 && activeConversationsList.length === 0 ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ React15.createElement(MessageIcon, null), /* @__PURE__ */ React15.createElement("p", null, "No conversations yet"), /* @__PURE__ */ React15.createElement("p", { className: "ai-agent-panel__empty-hint" }, "Start chatting to create your first conversation")) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, activeConversationsList.length > 0 && groupedConversations.some((g) => g.count > 0) && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group-divider" }), groupedConversations.map((group) => /* @__PURE__ */ React15.createElement("div", { key: group.label, className: "ai-agent-panel__group" }, /* @__PURE__ */ React15.createElement(
11169
+ /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ React15.createElement(LoadingDotIcon, null), /* @__PURE__ */ React15.createElement("span", null, activeConv.title)), subtitle ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null),
11170
+ /* @__PURE__ */ React15.createElement(
11171
+ "button",
11172
+ {
11173
+ className: "ai-agent-panel__conversation-close",
11174
+ onClick: (e) => handleCloseConversation(activeConv.conversationId, e),
11175
+ title: "Close conversation"
11176
+ },
11177
+ /* @__PURE__ */ React15.createElement(CloseIcon2, null)
11178
+ )
11179
+ );
11180
+ })), conversationsLoading ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("span", null, "Loading conversations...")) : conversationsError ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ React15.createElement("p", null, "Error: ", conversationsError), /* @__PURE__ */ React15.createElement(Button, { variant: "secondary", size: "sm", onClick: handleRefreshConversations }, "Retry")) : groupedConversations.length === 0 && activeConversationsList.length === 0 ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ React15.createElement(MessageIcon, null), /* @__PURE__ */ React15.createElement("p", null, "No conversations yet"), /* @__PURE__ */ React15.createElement("p", { className: "ai-agent-panel__empty-hint" }, "Start chatting to create your first conversation")) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, activeConversationsList.length > 0 && groupedConversations.some((g) => g.count > 0) && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__group-divider" }), groupedConversations.map((group) => /* @__PURE__ */ React15.createElement("div", { key: group.label, className: "ai-agent-panel__group" }, /* @__PURE__ */ React15.createElement(
10519
11181
  "div",
10520
11182
  {
10521
11183
  className: "ai-agent-panel__group-label ai-agent-panel__group-label--clickable",
@@ -10525,14 +11187,22 @@ var AIAgentPanel = React15.forwardRef(({
10525
11187
  /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__group-chevron" }, expandedSections[group.label] ? "\u25BC" : "\u25B6")
10526
11188
  ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv, convIndex) => {
10527
11189
  const isActive = activeConversations.has(conv.conversationId);
11190
+ const subtitle = (conversationSubtitleResolver == null ? void 0 : conversationSubtitleResolver(conv.conversationId, conv)) || "";
10528
11191
  return /* @__PURE__ */ React15.createElement(
10529
11192
  "div",
10530
11193
  {
10531
11194
  key: `${group.label}-${conv.conversationId}-${convIndex}`,
10532
- className: `ai-agent-panel__conversation ${currentConversationId === conv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${isActive ? "ai-agent-panel__conversation--in-active" : ""}`,
11195
+ className: `ai-agent-panel__conversation ${currentConversationId === conv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${recentlyFocusedConversationId === conv.conversationId ? "ai-agent-panel__conversation--just-focused" : ""} ${isActive ? "ai-agent-panel__conversation--in-active" : ""}`,
11196
+ ref: (node) => {
11197
+ if (node) {
11198
+ conversationRowRefs.current.set(conv.conversationId, node);
11199
+ } else {
11200
+ conversationRowRefs.current.delete(conv.conversationId);
11201
+ }
11202
+ },
10533
11203
  onClick: () => handleConversationSelect(conv)
10534
11204
  },
10535
- /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title))
11205
+ /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title), subtitle ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null)
10536
11206
  );
10537
11207
  }))))))),
10538
11208
  /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ React15.createElement(SparkleIcon, null), /* @__PURE__ */ React15.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ React15.createElement(
@@ -10570,6 +11240,8 @@ var AIAgentPanel = React15.forwardRef(({
10570
11240
  onToggleSection: handleContextSectionToggle,
10571
11241
  onConversationCreated: handleConversationCreated,
10572
11242
  onBeforeSend,
11243
+ compactContext,
11244
+ compactionPreserveTurns,
10573
11245
  conversationInitialPrompt: activeConv.conversationInitialPrompt,
10574
11246
  cssUrl,
10575
11247
  markdownClass,
@@ -10590,7 +11262,8 @@ var AIAgentPanel = React15.forwardRef(({
10590
11262
  localToolExecutors,
10591
11263
  traceContextMode,
10592
11264
  autoApproveTools,
10593
- toolStatusLabelFormatter
11265
+ toolStatusLabelFormatter,
11266
+ composerAgentModeControl
10594
11267
  }
10595
11268
  )), loadingConversationId && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ React15.createElement(MessageIcon, null), /* @__PURE__ */ React15.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ React15.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("p", null, "Loading agent..."))),
10596
11269
  /* @__PURE__ */ React15.createElement(