@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.js CHANGED
@@ -4188,6 +4188,58 @@ var ToolInfoModal2 = ({
4188
4188
  var ToolInfoModal_default2 = ToolInfoModal2;
4189
4189
 
4190
4190
  // src/AIChatPanel.tsx
4191
+ var USER_INPUT_TOOL_NAMES = /* @__PURE__ */ new Set(["request_user_input", "ff_request_user_input"]);
4192
+ var MAX_USER_INPUT_QUESTIONS = 5;
4193
+ var MAX_USER_INPUT_OPTIONS = 8;
4194
+ var MAX_USER_INPUT_WRITE_IN_CHARS = 1e3;
4195
+ var DEFAULT_USER_INPUT_TITLE = "Need your input";
4196
+ var DEFAULT_USER_INPUT_INSTRUCTIONS = "Answer these quick questions to continue.";
4197
+ var DEFAULT_CONTEXT_WARN_RATIO = 0.7;
4198
+ var DEFAULT_CONTEXT_COMPACT_RATIO = 0.9;
4199
+ var DEFAULT_CONTEXT_TARGET_RATIO = 0.65;
4200
+ var DEFAULT_COMPACTION_PRESERVE_TURNS = 8;
4201
+ function toBoundedRatio(value, fallback) {
4202
+ if (!Number.isFinite(value)) return fallback;
4203
+ if (value <= 0) return fallback;
4204
+ if (value >= 1) return 1;
4205
+ return value;
4206
+ }
4207
+ function estimateTokensFromText(text) {
4208
+ const normalized = typeof text === "string" ? text : "";
4209
+ if (!normalized) return 0;
4210
+ return Math.ceil(normalized.length / 4);
4211
+ }
4212
+ function estimateTokensFromMessages(messages) {
4213
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
4214
+ return messages.reduce((sum, message) => {
4215
+ const role = typeof (message == null ? void 0 : message.role) === "string" ? message.role : "";
4216
+ const content = typeof (message == null ? void 0 : message.content) === "string" ? message.content : "";
4217
+ return sum + estimateTokensFromText(role) + estimateTokensFromText(content);
4218
+ }, 0);
4219
+ }
4220
+ function estimateTokensFromData(data) {
4221
+ if (!Array.isArray(data) || data.length === 0) return 0;
4222
+ return data.reduce((sum, entry) => {
4223
+ const key = typeof (entry == null ? void 0 : entry.key) === "string" ? entry.key : "";
4224
+ const value = typeof (entry == null ? void 0 : entry.data) === "string" ? entry.data : "";
4225
+ return sum + estimateTokensFromText(key) + estimateTokensFromText(value);
4226
+ }, 0);
4227
+ }
4228
+ function formatTokenCount(tokens) {
4229
+ const safeTokens = Number.isFinite(tokens) ? Math.max(0, Math.floor(tokens)) : 0;
4230
+ if (safeTokens >= 1e3) {
4231
+ return `${(safeTokens / 1e3).toFixed(1)}K`;
4232
+ }
4233
+ return safeTokens.toString();
4234
+ }
4235
+ function buildFallbackWindowMessages(messages, preserveTurns) {
4236
+ if (!Array.isArray(messages) || messages.length === 0) return [];
4237
+ const keepMessageCount = Math.max(2, Math.floor(preserveTurns) * 2);
4238
+ if (messages.length <= keepMessageCount) {
4239
+ return messages;
4240
+ }
4241
+ return messages.slice(messages.length - keepMessageCount);
4242
+ }
4191
4243
  var areToolRequestListsEqual = (a, b) => {
4192
4244
  if (a.length !== b.length) return false;
4193
4245
  for (let index = 0; index < a.length; index += 1) {
@@ -4261,6 +4313,106 @@ var shouldPreserveBoundaryDroppedStreamText = (existingContent, incomingContent)
4261
4313
  return isBoundarySubsetByLines(existingLines, incomingLines);
4262
4314
  };
4263
4315
  var isObjectRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
4316
+ var toTrimmedString = (value) => typeof value === "string" ? value.trim() : "";
4317
+ var toBooleanWithDefault = (value, fallback) => typeof value === "boolean" ? value : fallback;
4318
+ var parseMaybeJsonString = (value) => {
4319
+ if (typeof value !== "string") return value;
4320
+ const trimmed = value.trim();
4321
+ if (!trimmed) return value;
4322
+ if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) return value;
4323
+ try {
4324
+ return JSON.parse(trimmed);
4325
+ } catch (_error) {
4326
+ return value;
4327
+ }
4328
+ };
4329
+ var toArrayFromUnknown = (value) => {
4330
+ const parsed = parseMaybeJsonString(value);
4331
+ return Array.isArray(parsed) ? parsed : [];
4332
+ };
4333
+ var toSlugValue = (value, fallback) => {
4334
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4335
+ return normalized || fallback;
4336
+ };
4337
+ var normalizeUserInputOptionEntry = (value, optionIndex, questionId) => {
4338
+ const normalizedValue = parseMaybeJsonString(value);
4339
+ if (typeof normalizedValue === "string") {
4340
+ const label2 = normalizedValue.trim();
4341
+ if (!label2) return null;
4342
+ return {
4343
+ label: label2,
4344
+ value: toSlugValue(label2, `${questionId}-option-${optionIndex + 1}`),
4345
+ description: ""
4346
+ };
4347
+ }
4348
+ if (!isObjectRecord(normalizedValue)) return null;
4349
+ const label = toTrimmedString(normalizedValue.label) || toTrimmedString(normalizedValue.title) || toTrimmedString(normalizedValue.value);
4350
+ if (!label) return null;
4351
+ const explicitValue = toTrimmedString(normalizedValue.value);
4352
+ return {
4353
+ label,
4354
+ value: explicitValue || toSlugValue(label, `${questionId}-option-${optionIndex + 1}`),
4355
+ description: toTrimmedString(normalizedValue.description)
4356
+ };
4357
+ };
4358
+ var normalizeUserInputQuestionEntry = (value, questionIndex) => {
4359
+ const normalizedValue = parseMaybeJsonString(value);
4360
+ if (!isObjectRecord(normalizedValue)) return null;
4361
+ const fallbackId = `question_${questionIndex + 1}`;
4362
+ const id = toSlugValue(
4363
+ toTrimmedString(normalizedValue.id) || toTrimmedString(normalizedValue.key) || fallbackId,
4364
+ fallbackId
4365
+ );
4366
+ const header = toTrimmedString(normalizedValue.header) || `Question ${questionIndex + 1}`;
4367
+ const question = toTrimmedString(normalizedValue.question) || toTrimmedString(normalizedValue.prompt) || toTrimmedString(normalizedValue.text);
4368
+ if (!question) return null;
4369
+ const rawOptions = toArrayFromUnknown(normalizedValue.options);
4370
+ const normalizedOptions = rawOptions.map((entry, optionIndex) => normalizeUserInputOptionEntry(entry, optionIndex, id)).filter((entry) => Boolean(entry)).slice(0, MAX_USER_INPUT_OPTIONS);
4371
+ if (normalizedOptions.length === 0) {
4372
+ return null;
4373
+ }
4374
+ const parsedWriteInConfig = parseMaybeJsonString(normalizedValue.writeIn);
4375
+ const writeInConfig = isObjectRecord(parsedWriteInConfig) ? parsedWriteInConfig : null;
4376
+ const allowWriteIn = toBooleanWithDefault(normalizedValue.allowWriteIn, false) || toBooleanWithDefault(writeInConfig == null ? void 0 : writeInConfig.enabled, false);
4377
+ const writeInPlaceholder = toTrimmedString(normalizedValue.writeInPlaceholder) || toTrimmedString(writeInConfig == null ? void 0 : writeInConfig.placeholder) || "Add details";
4378
+ return {
4379
+ id,
4380
+ header,
4381
+ question,
4382
+ required: toBooleanWithDefault(normalizedValue.required, true),
4383
+ allowWriteIn,
4384
+ writeInPlaceholder,
4385
+ options: normalizedOptions
4386
+ };
4387
+ };
4388
+ var normalizeUserInputRequest = (args, callId) => {
4389
+ const rawQuestions = toArrayFromUnknown(args.questions);
4390
+ const fallbackQuestion = toTrimmedString(args.question) || toTrimmedString(args.prompt) || toTrimmedString(args.text);
4391
+ const fallbackOptions = toArrayFromUnknown(args.options);
4392
+ const baseQuestions = rawQuestions.length > 0 ? rawQuestions : fallbackQuestion ? [
4393
+ {
4394
+ id: "question_1",
4395
+ header: "Question 1",
4396
+ question: fallbackQuestion,
4397
+ options: fallbackOptions,
4398
+ allowWriteIn: toBooleanWithDefault(args.allowWriteIn, false),
4399
+ writeInPlaceholder: toTrimmedString(args.writeInPlaceholder)
4400
+ }
4401
+ ] : [];
4402
+ const questions = baseQuestions.map((entry, index) => normalizeUserInputQuestionEntry(entry, index)).filter((entry) => Boolean(entry)).slice(0, MAX_USER_INPUT_QUESTIONS);
4403
+ if (questions.length === 0) {
4404
+ throw new Error("request_user_input requires at least one valid question with options.");
4405
+ }
4406
+ return {
4407
+ requestId: toTrimmedString(args.requestId) || toTrimmedString(args.id) || callId,
4408
+ title: toTrimmedString(args.title) || DEFAULT_USER_INPUT_TITLE,
4409
+ instructions: toTrimmedString(args.instructions) || DEFAULT_USER_INPUT_INSTRUCTIONS,
4410
+ submitLabel: toTrimmedString(args.submitLabel) || "Submit",
4411
+ cancelLabel: toTrimmedString(args.cancelLabel) || "Cancel",
4412
+ allowCancel: toBooleanWithDefault(args.allowCancel, true),
4413
+ questions
4414
+ };
4415
+ };
4264
4416
  var stringifyToolArgs = (value) => {
4265
4417
  if (typeof value === "string") return value;
4266
4418
  try {
@@ -4997,6 +5149,8 @@ var ChatInput = import_react14.default.memo(({
4997
5149
  onSubmit,
4998
5150
  onQueueSubmit,
4999
5151
  onStop,
5152
+ userInputRequest = null,
5153
+ onCompleteUserInputRequest,
5000
5154
  queuedPrompts = [],
5001
5155
  onClearQueuedPrompt,
5002
5156
  agentOptions,
@@ -5010,7 +5164,8 @@ var ChatInput = import_react14.default.memo(({
5010
5164
  maxContextTokens = 8e3,
5011
5165
  enableContextDetailView = false,
5012
5166
  disabledSectionIds = /* @__PURE__ */ new Set(),
5013
- onToggleSection
5167
+ onToggleSection,
5168
+ composerAgentModeControl
5014
5169
  }) => {
5015
5170
  const [inputValue, setInputValue] = (0, import_react14.useState)("");
5016
5171
  const [dropdownOpen, setDropdownOpen] = (0, import_react14.useState)(false);
@@ -5020,6 +5175,59 @@ var ChatInput = import_react14.default.memo(({
5020
5175
  const textareaRef = (0, import_react14.useRef)(null);
5021
5176
  const containerRef = (0, import_react14.useRef)(null);
5022
5177
  const contextPopoverRef = (0, import_react14.useRef)(null);
5178
+ const [userInputSelections, setUserInputSelections] = (0, import_react14.useState)({});
5179
+ const [userInputWriteIns, setUserInputWriteIns] = (0, import_react14.useState)({});
5180
+ const [userInputValidationError, setUserInputValidationError] = (0, import_react14.useState)("");
5181
+ const [userInputQuestionIndex, setUserInputQuestionIndex] = (0, import_react14.useState)(0);
5182
+ const [agentModeChipExpanded, setAgentModeChipExpanded] = (0, import_react14.useState)(false);
5183
+ const isAgentModeActionVisible = composerAgentModeControl !== void 0;
5184
+ const isAgentModeActive = Boolean(composerAgentModeControl == null ? void 0 : composerAgentModeControl.active);
5185
+ const isAgentModeActionDisabled = Boolean(composerAgentModeControl == null ? void 0 : composerAgentModeControl.disabled);
5186
+ const agentModeLabel = String((composerAgentModeControl == null ? void 0 : composerAgentModeControl.label) || "Agent").trim() || "Agent";
5187
+ const agentModeTitle = String((composerAgentModeControl == null ? void 0 : composerAgentModeControl.title) || "").trim() || (isAgentModeActive ? "Agent Mode active for this session" : "Enable Agent Mode");
5188
+ (0, import_react14.useEffect)(() => {
5189
+ if (isAgentModeActionVisible) return;
5190
+ setAgentModeChipExpanded(false);
5191
+ }, [isAgentModeActionVisible]);
5192
+ (0, import_react14.useEffect)(() => {
5193
+ if (!isAgentModeActive) return;
5194
+ setAgentModeChipExpanded(true);
5195
+ }, [isAgentModeActive]);
5196
+ const handleAgentModePillClick = (0, import_react14.useCallback)(() => {
5197
+ if (!isAgentModeActionVisible || isAgentModeActionDisabled) return;
5198
+ if (isAgentModeActive) {
5199
+ setAgentModeChipExpanded(true);
5200
+ return;
5201
+ }
5202
+ setAgentModeChipExpanded(true);
5203
+ if (typeof (composerAgentModeControl == null ? void 0 : composerAgentModeControl.onActivate) === "function") {
5204
+ composerAgentModeControl.onActivate();
5205
+ }
5206
+ }, [
5207
+ composerAgentModeControl,
5208
+ isAgentModeActionDisabled,
5209
+ isAgentModeActionVisible,
5210
+ isAgentModeActive
5211
+ ]);
5212
+ (0, import_react14.useEffect)(() => {
5213
+ if (!userInputRequest) {
5214
+ setUserInputSelections({});
5215
+ setUserInputWriteIns({});
5216
+ setUserInputValidationError("");
5217
+ setUserInputQuestionIndex(0);
5218
+ return;
5219
+ }
5220
+ const nextSelections = {};
5221
+ const nextWriteIns = {};
5222
+ userInputRequest.questions.forEach((question) => {
5223
+ nextSelections[question.id] = "";
5224
+ nextWriteIns[question.id] = "";
5225
+ });
5226
+ setUserInputSelections(nextSelections);
5227
+ setUserInputWriteIns(nextWriteIns);
5228
+ setUserInputValidationError("");
5229
+ setUserInputQuestionIndex(0);
5230
+ }, [userInputRequest]);
5023
5231
  const autoResize = (0, import_react14.useCallback)(() => {
5024
5232
  const textarea = textareaRef.current;
5025
5233
  if (textarea) {
@@ -5106,12 +5314,159 @@ var ChatInput = import_react14.default.memo(({
5106
5314
  const hasQueuedPrompts = normalizedQueuedPrompts.length > 0;
5107
5315
  const shouldQueueSubmission = isBusy || hasQueuedPrompts;
5108
5316
  const showStopAction = isBusy && !hasDraft;
5317
+ const composerLockedByUserInput = Boolean(userInputRequest);
5318
+ const totalUserInputQuestions = (userInputRequest == null ? void 0 : userInputRequest.questions.length) || 0;
5319
+ const boundedUserInputQuestionIndex = totalUserInputQuestions > 0 ? Math.min(Math.max(userInputQuestionIndex, 0), totalUserInputQuestions - 1) : 0;
5320
+ const activeUserInputQuestion = totalUserInputQuestions > 0 ? (userInputRequest == null ? void 0 : userInputRequest.questions[boundedUserInputQuestionIndex]) || null : null;
5321
+ const isFirstUserInputQuestion = boundedUserInputQuestionIndex <= 0;
5322
+ const isLastUserInputQuestion = totalUserInputQuestions > 0 && boundedUserInputQuestionIndex >= totalUserInputQuestions - 1;
5323
+ const getQuestionAnswerState = (0, import_react14.useCallback)((question) => {
5324
+ const selectedValue = toTrimmedString(userInputSelections[question.id]);
5325
+ const writeInRaw = toTrimmedString(userInputWriteIns[question.id]);
5326
+ const writeIn = writeInRaw.slice(0, MAX_USER_INPUT_WRITE_IN_CHARS);
5327
+ const selectedOption = question.options.find((option) => option.value === selectedValue) || null;
5328
+ const hasAnswer = Boolean(selectedOption) || question.allowWriteIn && writeIn.length > 0;
5329
+ return {
5330
+ selectedOption,
5331
+ writeIn,
5332
+ hasAnswer
5333
+ };
5334
+ }, [userInputSelections, userInputWriteIns]);
5335
+ const validateUserInputQuestion = (0, import_react14.useCallback)((question) => {
5336
+ const answerState = getQuestionAnswerState(question);
5337
+ if (question.required && !answerState.hasAnswer) {
5338
+ setUserInputValidationError(`Please answer "${question.header}".`);
5339
+ return false;
5340
+ }
5341
+ return true;
5342
+ }, [getQuestionAnswerState]);
5343
+ const handleUserInputNext = (0, import_react14.useCallback)(() => {
5344
+ if (!activeUserInputQuestion || isLastUserInputQuestion) return;
5345
+ if (!validateUserInputQuestion(activeUserInputQuestion)) return;
5346
+ setUserInputValidationError("");
5347
+ setUserInputQuestionIndex((prev) => Math.min(prev + 1, totalUserInputQuestions - 1));
5348
+ }, [activeUserInputQuestion, isLastUserInputQuestion, totalUserInputQuestions, validateUserInputQuestion]);
5349
+ const handleUserInputBack = (0, import_react14.useCallback)(() => {
5350
+ setUserInputValidationError("");
5351
+ setUserInputQuestionIndex((prev) => Math.max(prev - 1, 0));
5352
+ }, []);
5353
+ const handleUserInputSubmit = (0, import_react14.useCallback)(() => {
5354
+ var _a, _b, _c;
5355
+ if (!userInputRequest || typeof onCompleteUserInputRequest !== "function") {
5356
+ return;
5357
+ }
5358
+ const answers = [];
5359
+ for (let questionIndex = 0; questionIndex < userInputRequest.questions.length; questionIndex += 1) {
5360
+ const question = userInputRequest.questions[questionIndex];
5361
+ const answerState = getQuestionAnswerState(question);
5362
+ if (question.required && !answerState.hasAnswer) {
5363
+ setUserInputQuestionIndex(questionIndex);
5364
+ setUserInputValidationError(`Please answer "${question.header}".`);
5365
+ return;
5366
+ }
5367
+ answers.push({
5368
+ id: question.id,
5369
+ header: question.header,
5370
+ question: question.question,
5371
+ selectedOptionValue: ((_a = answerState.selectedOption) == null ? void 0 : _a.value) || null,
5372
+ selectedOptionLabel: ((_b = answerState.selectedOption) == null ? void 0 : _b.label) || null,
5373
+ writeIn: answerState.writeIn || null,
5374
+ answer: answerState.writeIn || ((_c = answerState.selectedOption) == null ? void 0 : _c.label) || ""
5375
+ });
5376
+ }
5377
+ setUserInputValidationError("");
5378
+ onCompleteUserInputRequest({
5379
+ status: "submitted",
5380
+ requestId: userInputRequest.requestId,
5381
+ answers
5382
+ });
5383
+ }, [getQuestionAnswerState, onCompleteUserInputRequest, userInputRequest]);
5384
+ const handleUserInputCancel = (0, import_react14.useCallback)(() => {
5385
+ if (!userInputRequest || typeof onCompleteUserInputRequest !== "function") {
5386
+ return;
5387
+ }
5388
+ onCompleteUserInputRequest({
5389
+ status: "cancelled",
5390
+ requestId: userInputRequest.requestId,
5391
+ cancelledBy: "user"
5392
+ });
5393
+ }, [onCompleteUserInputRequest, userInputRequest]);
5109
5394
  return /* @__PURE__ */ import_react14.default.createElement(
5110
5395
  "div",
5111
5396
  {
5112
5397
  className: `ai-chat-panel__input-container ${dropdownOpen ? "ai-chat-panel__input-container--dropdown-open" : ""}`,
5113
5398
  ref: containerRef
5114
5399
  },
5400
+ userInputRequest && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover", role: "dialog", "aria-live": "polite", "aria-label": userInputRequest.title }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__header" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__title" }, userInputRequest.title), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__header-meta" }, totalUserInputQuestions > 1 && /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__progress" }, boundedUserInputQuestionIndex + 1, " of ", totalUserInputQuestions), userInputRequest.allowCancel && /* @__PURE__ */ import_react14.default.createElement(
5401
+ "button",
5402
+ {
5403
+ type: "button",
5404
+ className: "ai-chat-user-input-popover__cancel-inline",
5405
+ onClick: handleUserInputCancel
5406
+ },
5407
+ userInputRequest.cancelLabel
5408
+ ))), userInputRequest.instructions && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__instructions" }, userInputRequest.instructions), activeUserInputQuestion && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__question" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__question-header" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__question-index" }, boundedUserInputQuestionIndex + 1), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__question-title" }, activeUserInputQuestion.header)), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__question-text" }, activeUserInputQuestion.question), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__options" }, activeUserInputQuestion.options.map((option) => /* @__PURE__ */ import_react14.default.createElement("label", { key: `${activeUserInputQuestion.id}:${option.value}`, className: "ai-chat-user-input-popover__option" }, /* @__PURE__ */ import_react14.default.createElement(
5409
+ "input",
5410
+ {
5411
+ type: "radio",
5412
+ name: `user-input-${activeUserInputQuestion.id}`,
5413
+ value: option.value,
5414
+ checked: (userInputSelections[activeUserInputQuestion.id] || "") === option.value,
5415
+ onChange: () => {
5416
+ setUserInputValidationError("");
5417
+ setUserInputSelections((prev) => __spreadProps(__spreadValues({}, prev), {
5418
+ [activeUserInputQuestion.id]: option.value
5419
+ }));
5420
+ }
5421
+ }
5422
+ ), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__option-content" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__option-label" }, option.label), option.description ? /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-user-input-popover__option-description" }, option.description) : null)))), activeUserInputQuestion.allowWriteIn && /* @__PURE__ */ import_react14.default.createElement(
5423
+ "textarea",
5424
+ {
5425
+ className: "ai-chat-user-input-popover__writein",
5426
+ placeholder: activeUserInputQuestion.writeInPlaceholder,
5427
+ value: userInputWriteIns[activeUserInputQuestion.id] || "",
5428
+ onChange: (event) => {
5429
+ const nextValue = event.target.value.slice(0, MAX_USER_INPUT_WRITE_IN_CHARS);
5430
+ setUserInputValidationError("");
5431
+ setUserInputWriteIns((prev) => __spreadProps(__spreadValues({}, prev), {
5432
+ [activeUserInputQuestion.id]: nextValue
5433
+ }));
5434
+ },
5435
+ rows: 2
5436
+ }
5437
+ )), userInputValidationError ? /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__error" }, userInputValidationError) : null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__actions" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__actions-left" }, userInputRequest.allowCancel && /* @__PURE__ */ import_react14.default.createElement(
5438
+ "button",
5439
+ {
5440
+ type: "button",
5441
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--secondary",
5442
+ onClick: handleUserInputCancel
5443
+ },
5444
+ userInputRequest.cancelLabel
5445
+ )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-user-input-popover__actions-right" }, !isFirstUserInputQuestion && /* @__PURE__ */ import_react14.default.createElement(
5446
+ "button",
5447
+ {
5448
+ type: "button",
5449
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--secondary",
5450
+ onClick: handleUserInputBack
5451
+ },
5452
+ "Back"
5453
+ ), isLastUserInputQuestion ? /* @__PURE__ */ import_react14.default.createElement(
5454
+ "button",
5455
+ {
5456
+ type: "button",
5457
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--primary",
5458
+ onClick: handleUserInputSubmit
5459
+ },
5460
+ userInputRequest.submitLabel
5461
+ ) : /* @__PURE__ */ import_react14.default.createElement(
5462
+ "button",
5463
+ {
5464
+ type: "button",
5465
+ className: "ai-chat-user-input-popover__button ai-chat-user-input-popover__button--primary",
5466
+ onClick: handleUserInputNext
5467
+ },
5468
+ "Next"
5469
+ )))),
5115
5470
  hasQueuedPrompts && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-queued-prompts" }, normalizedQueuedPrompts.map((queuedPrompt, index) => /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-queued-prompt", title: queuedPrompt, key: `${index}-${queuedPrompt}` }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-queued-prompt__content" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-queued-prompt__label" }, index === 0 ? "Queued next" : `Queued ${index + 1}`), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-queued-prompt__text" }, queuedPrompt)), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-queued-prompt__actions" }, /* @__PURE__ */ import_react14.default.createElement(
5116
5471
  "button",
5117
5472
  {
@@ -5135,8 +5490,9 @@ var ChatInput = import_react14.default.memo(({
5135
5490
  {
5136
5491
  ref: textareaRef,
5137
5492
  className: "ai-chat-input",
5138
- placeholder,
5493
+ placeholder: composerLockedByUserInput ? "Answer the question above to continue\u2026" : placeholder,
5139
5494
  value: inputValue,
5495
+ disabled: composerLockedByUserInput,
5140
5496
  onChange: (e) => {
5141
5497
  setInputValue(e.target.value);
5142
5498
  setTimeout(autoResize, 0);
@@ -5150,7 +5506,7 @@ var ChatInput = import_react14.default.memo(({
5150
5506
  rows: 1
5151
5507
  }
5152
5508
  )),
5153
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__input-footer" }, agentOptions.length > 0 ? /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-agent-selector" }, /* @__PURE__ */ import_react14.default.createElement(
5509
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__input-footer" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-input-footer__left" }, agentOptions.length > 0 ? /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-agent-selector" }, /* @__PURE__ */ import_react14.default.createElement(
5154
5510
  "button",
5155
5511
  {
5156
5512
  className: "ai-chat-agent-selector__trigger",
@@ -5167,7 +5523,20 @@ var ChatInput = import_react14.default.memo(({
5167
5523
  ) : /* @__PURE__ */ import_react14.default.createElement(AgentIcon, null),
5168
5524
  /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-agent-selector__label" }, agentsLoading ? "Loading..." : currentAgentLabel || "Select agent"),
5169
5525
  dropdownOpen ? /* @__PURE__ */ import_react14.default.createElement(ChevronUpIcon, null) : /* @__PURE__ */ import_react14.default.createElement(ChevronDownIcon, null)
5170
- )) : /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }), contextSections.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ import_react14.default.createElement(
5526
+ )) : null, isAgentModeActionVisible && /* @__PURE__ */ import_react14.default.createElement(
5527
+ "button",
5528
+ {
5529
+ className: `ai-chat-agent-mode-trigger ${agentModeChipExpanded ? "ai-chat-agent-mode-trigger--expanded" : ""} ${isAgentModeActive ? "ai-chat-agent-mode-trigger--active" : ""}`,
5530
+ onClick: handleAgentModePillClick,
5531
+ type: "button",
5532
+ title: agentModeTitle,
5533
+ "aria-label": isAgentModeActive ? "Agent active" : "Enable Agent Mode",
5534
+ "aria-pressed": isAgentModeActive,
5535
+ disabled: isAgentModeActionDisabled
5536
+ },
5537
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-agent-mode-trigger__icon", "aria-hidden": "true" }, /* @__PURE__ */ import_react14.default.createElement(AgentIcon, null)),
5538
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-agent-mode-trigger__label" }, agentModeLabel)
5539
+ ), agentOptions.length === 0 && !isAgentModeActionVisible ? /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }) : null), contextSections.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-pill-anchor" }, /* @__PURE__ */ import_react14.default.createElement(
5171
5540
  "button",
5172
5541
  {
5173
5542
  className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""}`,
@@ -5302,13 +5671,13 @@ var ChatInput = import_react14.default.memo(({
5302
5671
  /* @__PURE__ */ import_react14.default.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ import_react14.default.createElement("code", null, isRawSection ? section.rawData : getStructuredContent(section)))
5303
5672
  );
5304
5673
  })))
5305
- )), /* @__PURE__ */ import_react14.default.createElement(
5674
+ ))), /* @__PURE__ */ import_react14.default.createElement(
5306
5675
  "button",
5307
5676
  {
5308
- className: `ai-chat-send-button ${!showStopAction && !hasDraft ? "ai-chat-send-button--disabled" : ""} ${showStopAction ? "ai-chat-send-button--stop" : ""}`,
5677
+ className: `ai-chat-send-button ${!showStopAction && (!hasDraft || composerLockedByUserInput) ? "ai-chat-send-button--disabled" : ""} ${showStopAction ? "ai-chat-send-button--stop" : ""}`,
5309
5678
  onClick: () => showStopAction ? onStop() : handleSubmit(),
5310
- disabled: !showStopAction && !hasDraft,
5311
- title: showStopAction ? "Stop response" : shouldQueueSubmission ? "Queue prompt" : "Send prompt"
5679
+ disabled: !showStopAction && !hasDraft || !showStopAction && composerLockedByUserInput,
5680
+ title: showStopAction ? "Stop response" : composerLockedByUserInput ? "Answer the question above to continue" : shouldQueueSubmission ? "Queue prompt" : "Send prompt"
5312
5681
  },
5313
5682
  showStopAction ? /* @__PURE__ */ import_react14.default.createElement(StopIcon, null) : /* @__PURE__ */ import_react14.default.createElement(ArrowUpIcon, null)
5314
5683
  )),
@@ -5385,6 +5754,8 @@ var AIChatPanel = ({
5385
5754
  onToggleSection: propOnToggleSection,
5386
5755
  onConversationCreated,
5387
5756
  onBeforeSend,
5757
+ compactContext,
5758
+ compactionPreserveTurns = DEFAULT_COMPACTION_PRESERVE_TURNS,
5388
5759
  // UI Customization Props
5389
5760
  cssUrl,
5390
5761
  markdownClass,
@@ -5404,7 +5775,8 @@ var AIChatPanel = ({
5404
5775
  // Customer Email Capture Props
5405
5776
  customerEmailCaptureMode = "HIDE",
5406
5777
  customerEmailCapturePlaceholder = "Please enter your email...",
5407
- toolStatusLabelFormatter
5778
+ toolStatusLabelFormatter,
5779
+ composerAgentModeControl
5408
5780
  }) => {
5409
5781
  var _a;
5410
5782
  const publicAPIUrl = "https://api.llmasaservice.io";
@@ -5430,6 +5802,7 @@ var AIChatPanel = ({
5430
5802
  const [copiedCallId, setCopiedCallId] = (0, import_react14.useState)(null);
5431
5803
  const [feedbackCallId, setFeedbackCallId] = (0, import_react14.useState)(null);
5432
5804
  const [error, setError] = (0, import_react14.useState)(null);
5805
+ const [compactionNotice, setCompactionNotice] = (0, import_react14.useState)(null);
5433
5806
  const lastProcessedErrorRef = (0, import_react14.useRef)(null);
5434
5807
  const [emailSent, setEmailSent] = (0, import_react14.useState)(false);
5435
5808
  const [isToolInfoModalOpen, setIsToolInfoModalOpen] = (0, import_react14.useState)(false);
@@ -5449,6 +5822,9 @@ var AIChatPanel = ({
5449
5822
  const [toolsFetchError, setToolsFetchError] = (0, import_react14.useState)(false);
5450
5823
  const [resolvedMcpServers, setResolvedMcpServers] = (0, import_react14.useState)(mcpServers || []);
5451
5824
  const [activeToolCalls, setActiveToolCalls] = (0, import_react14.useState)([]);
5825
+ const [pendingUserInputRequest, setPendingUserInputRequest] = (0, import_react14.useState)(null);
5826
+ const pendingUserInputRequestRef = (0, import_react14.useRef)(null);
5827
+ const pendingUserInputResolverRef = (0, import_react14.useRef)(null);
5452
5828
  const normalizeToolName = (0, import_react14.useCallback)((toolName) => {
5453
5829
  return String(toolName != null ? toolName : "").trim().toLowerCase();
5454
5830
  }, []);
@@ -5461,6 +5837,32 @@ var AIChatPanel = ({
5461
5837
  },
5462
5838
  [normalizeToolName]
5463
5839
  );
5840
+ const completePendingUserInputRequest = (0, import_react14.useCallback)((payload) => {
5841
+ const resolver = pendingUserInputResolverRef.current;
5842
+ pendingUserInputResolverRef.current = null;
5843
+ pendingUserInputRequestRef.current = null;
5844
+ setPendingUserInputRequest(null);
5845
+ if (resolver) {
5846
+ resolver(payload);
5847
+ }
5848
+ }, []);
5849
+ (0, import_react14.useEffect)(() => {
5850
+ pendingUserInputRequestRef.current = pendingUserInputRequest;
5851
+ }, [pendingUserInputRequest]);
5852
+ (0, import_react14.useEffect)(() => {
5853
+ return () => {
5854
+ var _a2;
5855
+ const resolver = pendingUserInputResolverRef.current;
5856
+ if (!resolver) return;
5857
+ pendingUserInputResolverRef.current = null;
5858
+ const pendingRequestId = ((_a2 = pendingUserInputRequestRef.current) == null ? void 0 : _a2.requestId) || "request_user_input";
5859
+ resolver({
5860
+ status: "cancelled",
5861
+ requestId: pendingRequestId,
5862
+ cancelledBy: "unmount"
5863
+ });
5864
+ };
5865
+ }, []);
5464
5866
  const alwaysApprovedToolsStorageKey = (0, import_react14.useMemo)(() => {
5465
5867
  const customerId = (customer == null ? void 0 : customer.customer_id) || (customer == null ? void 0 : customer.id) || (customer == null ? void 0 : customer.customer_user_email) || "anonymous";
5466
5868
  const agentIdForScope = currentAgentId || agent || "default";
@@ -6090,6 +6492,42 @@ var AIChatPanel = ({
6090
6492
  if (hasInFlightTurnWork()) return;
6091
6493
  turnLockRef.current = false;
6092
6494
  }, [hasInFlightTurnWork]);
6495
+ const runRequestUserInputTool = (0, import_react14.useCallback)(
6496
+ (toolCall) => __async(void 0, null, function* () {
6497
+ const normalizedToolName = normalizeToolName(toolCall.toolName);
6498
+ if (!USER_INPUT_TOOL_NAMES.has(normalizedToolName)) {
6499
+ throw new Error(`Unsupported user input tool: ${toolCall.toolName}`);
6500
+ }
6501
+ if (pendingUserInputResolverRef.current || pendingUserInputRequestRef.current) {
6502
+ throw new Error("A user input request is already pending. Wait for completion before requesting another.");
6503
+ }
6504
+ const request = normalizeUserInputRequest(
6505
+ isObjectRecord(toolCall.args) ? toolCall.args : {},
6506
+ toolCall.callId || toolCall.toolName
6507
+ );
6508
+ pendingUserInputRequestRef.current = request;
6509
+ setPendingUserInputRequest(request);
6510
+ const completion = yield new Promise((resolve) => {
6511
+ pendingUserInputResolverRef.current = resolve;
6512
+ });
6513
+ if (completion.status === "cancelled") {
6514
+ return {
6515
+ status: "cancelled",
6516
+ requestId: request.requestId,
6517
+ cancelledBy: completion.cancelledBy || "user",
6518
+ answers: []
6519
+ };
6520
+ }
6521
+ const answers = Array.isArray(completion.answers) ? completion.answers : [];
6522
+ return {
6523
+ status: "ok",
6524
+ requestId: request.requestId,
6525
+ answers,
6526
+ answeredCount: answers.length
6527
+ };
6528
+ }),
6529
+ [normalizeToolName]
6530
+ );
6093
6531
  const processGivenToolRequests = (0, import_react14.useCallback)(
6094
6532
  (requests) => __async(void 0, null, function* () {
6095
6533
  var _a2, _b, _c;
@@ -6344,6 +6782,29 @@ ${traceSummary}` : traceSummary;
6344
6782
  var _a3, _b2, _c2, _d, _e, _f, _g;
6345
6783
  const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName) || null;
6346
6784
  const localExecutor = localToolExecutors && typeof localToolExecutors[toolCall.toolName] === "function" ? localToolExecutors[toolCall.toolName] : null;
6785
+ const normalizedToolName = normalizeToolName(toolCall.toolName);
6786
+ if (USER_INPUT_TOOL_NAMES.has(normalizedToolName)) {
6787
+ try {
6788
+ const requestResult = yield runRequestUserInputTool({
6789
+ toolName: toolCall.toolName,
6790
+ callId: toolCall.callId,
6791
+ args: toolCall.args
6792
+ });
6793
+ return {
6794
+ tool_call_id: toolCall.callId,
6795
+ tool_name: toolCall.toolName,
6796
+ result: JSON.stringify(requestResult),
6797
+ isError: false
6798
+ };
6799
+ } catch (error2) {
6800
+ return {
6801
+ tool_call_id: toolCall.callId,
6802
+ tool_name: toolCall.toolName,
6803
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6804
+ isError: true
6805
+ };
6806
+ }
6807
+ }
6347
6808
  if (localExecutor) {
6348
6809
  try {
6349
6810
  const localResult = yield localExecutor(toolCall.args, {
@@ -6660,6 +7121,8 @@ ${traceSummary}` : traceSummary;
6660
7121
  idle,
6661
7122
  stop,
6662
7123
  lastController,
7124
+ normalizeToolName,
7125
+ runRequestUserInputTool,
6663
7126
  waitForStreamIdle,
6664
7127
  releaseTurnLockIfSettled
6665
7128
  ]
@@ -7055,6 +7518,7 @@ ${traceSummary}` : traceSummary;
7055
7518
  hasAutoCollapsedRef.current = false;
7056
7519
  prevBlockCountRef.current = 0;
7057
7520
  setError(null);
7521
+ setCompactionNotice(null);
7058
7522
  setUserHasScrolled(false);
7059
7523
  prevResponseLengthRef.current = 0;
7060
7524
  setResponse("");
@@ -7073,144 +7537,232 @@ ${traceSummary}` : traceSummary;
7073
7537
  setTimeout(() => {
7074
7538
  scrollToBottom(true);
7075
7539
  }, 0);
7076
- console.log("AIChatPanel.continueChat - about to call ensureConversation");
7077
- ensureConversation().then((convId) => {
7078
- console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
7079
- const historyForCall = latestHistoryRef.current || {};
7080
- const messagesAndHistory = [];
7081
- Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
7082
- if (historyPrompt === promptKey) return;
7083
- const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
7084
- const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
7085
- messagesAndHistory.push({ role: "user", content: promptForHistory });
7086
- messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
7087
- });
7088
- let fullPromptToSend = promptToSend;
7089
- if (messagesAndHistory.length === 0 && promptTemplate) {
7090
- fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
7091
- }
7092
- const newController = new AbortController();
7093
- setLastController(newController);
7094
- if (onBeforeSend) {
7095
- void Promise.resolve(
7096
- onBeforeSend({
7097
- prompt: promptToSend,
7540
+ void (() => __async(void 0, null, function* () {
7541
+ var _a2, _b;
7542
+ try {
7543
+ console.log("AIChatPanel.continueChat - about to call ensureConversation");
7544
+ const convId = yield ensureConversation();
7545
+ console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
7546
+ const historyForCall = latestHistoryRef.current || {};
7547
+ const messagesAndHistory = [];
7548
+ Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
7549
+ if (historyPrompt === promptKey) return;
7550
+ const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
7551
+ const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
7552
+ messagesAndHistory.push({ role: "user", content: promptForHistory });
7553
+ messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
7554
+ });
7555
+ let fullPromptToSend = promptToSend;
7556
+ if (messagesAndHistory.length === 0 && promptTemplate) {
7557
+ fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
7558
+ }
7559
+ const newController = new AbortController();
7560
+ setLastController(newController);
7561
+ if (onBeforeSend) {
7562
+ try {
7563
+ yield Promise.resolve(
7564
+ onBeforeSend({
7565
+ prompt: promptToSend,
7566
+ conversationId: convId || null,
7567
+ agentId: agent,
7568
+ service,
7569
+ messages: messagesAndHistory
7570
+ })
7571
+ );
7572
+ } catch (error2) {
7573
+ console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
7574
+ }
7575
+ }
7576
+ const warnRatio = toBoundedRatio(DEFAULT_CONTEXT_WARN_RATIO, DEFAULT_CONTEXT_WARN_RATIO);
7577
+ const compactRatio = toBoundedRatio(DEFAULT_CONTEXT_COMPACT_RATIO, DEFAULT_CONTEXT_COMPACT_RATIO);
7578
+ const targetRatio = toBoundedRatio(DEFAULT_CONTEXT_TARGET_RATIO, DEFAULT_CONTEXT_TARGET_RATIO);
7579
+ const maxTokens = Math.max(1, Number.isFinite(Number(maxContextTokens)) ? Math.floor(Number(maxContextTokens)) : 8e3);
7580
+ const dataEntries = dataWithExtras();
7581
+ const projectedBefore = estimateTokensFromText(fullPromptToSend) + estimateTokensFromMessages(messagesAndHistory) + Math.max(Number(totalContextTokens) || 0, estimateTokensFromData(dataEntries));
7582
+ const warnThreshold = Math.floor(maxTokens * warnRatio);
7583
+ const compactThreshold = Math.floor(maxTokens * compactRatio);
7584
+ const targetThreshold = Math.floor(maxTokens * targetRatio);
7585
+ const preserveTurnsForCompaction = Number.isFinite(Number(compactionPreserveTurns)) && Number(compactionPreserveTurns) > 0 ? Math.floor(Number(compactionPreserveTurns)) : DEFAULT_COMPACTION_PRESERVE_TURNS;
7586
+ const canDropOlderTurns = buildFallbackWindowMessages(messagesAndHistory, preserveTurnsForCompaction).length < messagesAndHistory.length;
7587
+ let sendPrompt = fullPromptToSend;
7588
+ let sendMessages = messagesAndHistory;
7589
+ if (projectedBefore >= warnThreshold && projectedBefore < compactThreshold) {
7590
+ const nearLimitMessage = "Context is approaching the configured limit. A later turn may auto-compact.";
7591
+ setCompactionNotice({
7592
+ level: "warning",
7593
+ message: nearLimitMessage
7594
+ });
7595
+ }
7596
+ if (compactContext && projectedBefore >= compactThreshold && canDropOlderTurns) {
7597
+ setCompactionNotice({
7598
+ level: "info",
7599
+ message: "Automatically compacting context"
7600
+ });
7601
+ const compactionInput = {
7602
+ prompt: fullPromptToSend,
7098
7603
  conversationId: convId || null,
7099
7604
  agentId: agent,
7100
7605
  service,
7101
- messages: messagesAndHistory
7102
- })
7103
- ).catch((error2) => {
7104
- console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
7105
- });
7106
- }
7107
- send(
7108
- fullPromptToSend,
7109
- messagesAndHistory,
7110
- [
7111
- ...dataWithExtras(),
7112
- { key: "--messages", data: messagesAndHistory.length.toString() }
7113
- ],
7114
- true,
7115
- // stream
7116
- true,
7117
- // includeHistory
7118
- service,
7119
- // group_id from agent config
7120
- convId,
7121
- // Use the conversation ID from ensureConversation
7122
- newController,
7123
- void 0,
7124
- // onComplete
7125
- (errorMsg) => {
7126
- console.log("[AIChatPanel] Error callback triggered:", errorMsg);
7127
- const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
7128
- if (isAbortError) {
7129
- if (suppressAbortHistoryUpdateRef.current) {
7130
- setIsLoading(false);
7131
- releaseTurnLockIfSettled();
7132
- return;
7606
+ messages: messagesAndHistory,
7607
+ data: dataEntries,
7608
+ maxContextTokens: maxTokens,
7609
+ totalContextTokens: Number(totalContextTokens) || 0,
7610
+ warnRatio,
7611
+ compactRatio,
7612
+ targetRatio,
7613
+ preserveTurns: preserveTurnsForCompaction,
7614
+ projectedTokens: {
7615
+ projectedBefore,
7616
+ projectedAfter: projectedBefore,
7617
+ warnThreshold,
7618
+ compactThreshold,
7619
+ targetThreshold
7133
7620
  }
7134
- console.log("[AIChatPanel] Request was aborted by user");
7135
- if (promptKey) {
7136
- setHistory((prev) => {
7137
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7138
- return __spreadProps(__spreadValues({}, prev), {
7139
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7140
- content: "Response canceled",
7141
- callId: lastCallId || existingEntry.callId || ""
7142
- })
7621
+ };
7622
+ try {
7623
+ const compactResult = yield Promise.resolve(compactContext(compactionInput));
7624
+ if (compactResult && typeof compactResult === "object") {
7625
+ if (typeof compactResult.prompt === "string" && compactResult.prompt.trim()) {
7626
+ sendPrompt = compactResult.prompt;
7627
+ }
7628
+ if (Array.isArray(compactResult.messages)) {
7629
+ sendMessages = compactResult.messages;
7630
+ }
7631
+ if (compactResult.action === "compacted") {
7632
+ const usageBefore = Number((_a2 = compactResult.tokenUsage) == null ? void 0 : _a2.projectedBefore);
7633
+ const usageAfter = Number((_b = compactResult.tokenUsage) == null ? void 0 : _b.projectedAfter);
7634
+ const hasUsageNumbers = Number.isFinite(usageBefore) && Number.isFinite(usageAfter);
7635
+ const compactedMessage = hasUsageNumbers ? `Context compacted for this send (${formatTokenCount(usageBefore)} -> ${formatTokenCount(usageAfter)} tokens).` : "Older turns were compacted to keep this thread within context limits.";
7636
+ setCompactionNotice({
7637
+ level: "success",
7638
+ message: compactResult.warning || compactedMessage
7143
7639
  });
7144
- });
7640
+ } else if (compactResult.action === "fallback_window") {
7641
+ setCompactionNotice({
7642
+ level: "warning",
7643
+ message: compactResult.warning || "Compaction fallback applied. Sending only the most recent turns."
7644
+ });
7645
+ } else {
7646
+ setCompactionNotice(null);
7647
+ }
7145
7648
  }
7146
- } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
7147
- setError({
7148
- message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7149
- code: "413"
7649
+ } catch (error2) {
7650
+ console.warn("[AIChatPanel] compactContext failed, applying local fallback window", error2);
7651
+ sendMessages = buildFallbackWindowMessages(messagesAndHistory, preserveTurnsForCompaction);
7652
+ setCompactionNotice({
7653
+ level: "warning",
7654
+ message: "Compaction service unavailable. Sending a reduced recent-window context."
7150
7655
  });
7151
- if (promptKey) {
7152
- setHistory((prev) => {
7153
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7154
- return __spreadProps(__spreadValues({}, prev), {
7155
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7156
- content: `Error: ${errorMsg}`,
7157
- callId: lastCallId || existingEntry.callId || ""
7158
- })
7656
+ }
7657
+ }
7658
+ send(
7659
+ sendPrompt,
7660
+ sendMessages,
7661
+ [
7662
+ ...dataEntries,
7663
+ { key: "--messages", data: sendMessages.length.toString() }
7664
+ ],
7665
+ true,
7666
+ // stream
7667
+ true,
7668
+ // includeHistory
7669
+ service,
7670
+ // group_id from agent config
7671
+ convId,
7672
+ // Use the conversation ID from ensureConversation
7673
+ newController,
7674
+ void 0,
7675
+ // onComplete
7676
+ (errorMsg) => {
7677
+ console.log("[AIChatPanel] Error callback triggered:", errorMsg);
7678
+ const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
7679
+ if (isAbortError) {
7680
+ if (suppressAbortHistoryUpdateRef.current) {
7681
+ setIsLoading(false);
7682
+ releaseTurnLockIfSettled();
7683
+ return;
7684
+ }
7685
+ console.log("[AIChatPanel] Request was aborted by user");
7686
+ if (promptKey) {
7687
+ setHistory((prev) => {
7688
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7689
+ return __spreadProps(__spreadValues({}, prev), {
7690
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7691
+ content: "Response canceled",
7692
+ callId: lastCallId || existingEntry.callId || ""
7693
+ })
7694
+ });
7159
7695
  });
7696
+ }
7697
+ } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
7698
+ setError({
7699
+ message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
7700
+ code: "413"
7160
7701
  });
7161
- }
7162
- } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
7163
- setError({
7164
- message: "Network error. Please check your connection and try again.",
7165
- code: "NETWORK_ERROR"
7166
- });
7167
- if (promptKey) {
7168
- setHistory((prev) => {
7169
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7170
- return __spreadProps(__spreadValues({}, prev), {
7171
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7172
- content: `Error: ${errorMsg}`,
7173
- callId: lastCallId || existingEntry.callId || ""
7174
- })
7702
+ if (promptKey) {
7703
+ setHistory((prev) => {
7704
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7705
+ return __spreadProps(__spreadValues({}, prev), {
7706
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7707
+ content: `Error: ${errorMsg}`,
7708
+ callId: lastCallId || existingEntry.callId || ""
7709
+ })
7710
+ });
7175
7711
  });
7712
+ }
7713
+ } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
7714
+ setError({
7715
+ message: "Network error. Please check your connection and try again.",
7716
+ code: "NETWORK_ERROR"
7176
7717
  });
7177
- }
7178
- } else {
7179
- setError({
7180
- message: errorMsg,
7181
- code: "UNKNOWN_ERROR"
7182
- });
7183
- if (promptKey) {
7184
- setHistory((prev) => {
7185
- const existingEntry = prev[promptKey] || { content: "", callId: "" };
7186
- return __spreadProps(__spreadValues({}, prev), {
7187
- [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7188
- content: `Error: ${errorMsg}`,
7189
- callId: lastCallId || existingEntry.callId || ""
7190
- })
7718
+ if (promptKey) {
7719
+ setHistory((prev) => {
7720
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7721
+ return __spreadProps(__spreadValues({}, prev), {
7722
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7723
+ content: `Error: ${errorMsg}`,
7724
+ callId: lastCallId || existingEntry.callId || ""
7725
+ })
7726
+ });
7191
7727
  });
7728
+ }
7729
+ } else {
7730
+ setError({
7731
+ message: errorMsg,
7732
+ code: "UNKNOWN_ERROR"
7192
7733
  });
7734
+ if (promptKey) {
7735
+ setHistory((prev) => {
7736
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
7737
+ return __spreadProps(__spreadValues({}, prev), {
7738
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
7739
+ content: `Error: ${errorMsg}`,
7740
+ callId: lastCallId || existingEntry.callId || ""
7741
+ })
7742
+ });
7743
+ });
7744
+ }
7193
7745
  }
7746
+ setIsLoading(false);
7747
+ releaseTurnLockIfSettled();
7194
7748
  }
7195
- setIsLoading(false);
7196
- releaseTurnLockIfSettled();
7749
+ );
7750
+ setLastMessages(sendMessages);
7751
+ if (convId && onConversationCreated) {
7752
+ setTimeout(() => {
7753
+ onConversationCreated(convId);
7754
+ }, 100);
7197
7755
  }
7198
- );
7199
- setLastMessages(messagesAndHistory);
7200
- if (convId && onConversationCreated) {
7201
- setTimeout(() => {
7202
- onConversationCreated(convId);
7203
- }, 100);
7756
+ } catch (error2) {
7757
+ console.error("[AIChatPanel] Failed to send prompt:", error2);
7758
+ setError({
7759
+ message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7760
+ code: "UNKNOWN_ERROR"
7761
+ });
7762
+ setIsLoading(false);
7763
+ releaseTurnLockIfSettled();
7204
7764
  }
7205
- }).catch((error2) => {
7206
- console.error("[AIChatPanel] Failed to send prompt:", error2);
7207
- setError({
7208
- message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7209
- code: "UNKNOWN_ERROR"
7210
- });
7211
- setIsLoading(false);
7212
- releaseTurnLockIfSettled();
7213
- });
7765
+ }))();
7214
7766
  }, [
7215
7767
  clearFollowOnQuestionsNextPrompt,
7216
7768
  promptTemplate,
@@ -7225,6 +7777,10 @@ ${traceSummary}` : traceSummary;
7225
7777
  scrollToBottom,
7226
7778
  onConversationCreated,
7227
7779
  onBeforeSend,
7780
+ compactContext,
7781
+ compactionPreserveTurns,
7782
+ maxContextTokens,
7783
+ totalContextTokens,
7228
7784
  hasInFlightTurnWork,
7229
7785
  queuePromptForLater,
7230
7786
  releaseTurnLockIfSettled,
@@ -7244,8 +7800,15 @@ ${traceSummary}` : traceSummary;
7244
7800
  continueChat(question);
7245
7801
  }, [continueChat]);
7246
7802
  const handleStop = (0, import_react14.useCallback)(() => {
7803
+ if (pendingUserInputRequest) {
7804
+ completePendingUserInputRequest({
7805
+ status: "cancelled",
7806
+ requestId: pendingUserInputRequest.requestId,
7807
+ cancelledBy: "stop"
7808
+ });
7809
+ }
7247
7810
  stop(lastController);
7248
- }, [stop, lastController]);
7811
+ }, [completePendingUserInputRequest, lastController, pendingUserInputRequest, stop]);
7249
7812
  const handleNewConversation = (0, import_react14.useCallback)(() => {
7250
7813
  if (!newConversationConfirm) {
7251
7814
  setNewConversationConfirm(true);
@@ -7256,8 +7819,16 @@ ${traceSummary}` : traceSummary;
7256
7819
  if (!idle) {
7257
7820
  stop(lastController);
7258
7821
  }
7822
+ if (pendingUserInputRequest) {
7823
+ completePendingUserInputRequest({
7824
+ status: "cancelled",
7825
+ requestId: pendingUserInputRequest.requestId,
7826
+ cancelledBy: "reset"
7827
+ });
7828
+ }
7259
7829
  setResponse("");
7260
7830
  setHistory({});
7831
+ setCompactionNotice(null);
7261
7832
  latestHistoryRef.current = {};
7262
7833
  hasNotifiedCompletionRef.current = true;
7263
7834
  queuedPromptsRef.current = [];
@@ -7279,6 +7850,7 @@ ${traceSummary}` : traceSummary;
7279
7850
  setThinkingBlocks([]);
7280
7851
  setCurrentThinkingIndex(0);
7281
7852
  setCollapsedBlocks(/* @__PURE__ */ new Set());
7853
+ setPendingUserInputRequest(null);
7282
7854
  hasAutoCollapsedRef.current = false;
7283
7855
  prevBlockCountRef.current = 0;
7284
7856
  setJustReset(true);
@@ -7292,7 +7864,16 @@ ${traceSummary}` : traceSummary;
7292
7864
  setJustReset(false);
7293
7865
  (_a2 = responseAreaRef.current) == null ? void 0 : _a2.scrollTo({ top: 0, behavior: "smooth" });
7294
7866
  }, 100);
7295
- }, [newConversationConfirm, idle, stop, lastController, setResponse, followOnQuestions]);
7867
+ }, [
7868
+ completePendingUserInputRequest,
7869
+ followOnQuestions,
7870
+ idle,
7871
+ lastController,
7872
+ newConversationConfirm,
7873
+ pendingUserInputRequest,
7874
+ setResponse,
7875
+ stop
7876
+ ]);
7296
7877
  (0, import_react14.useEffect)(() => {
7297
7878
  if (!response || !lastKey || justReset) return;
7298
7879
  const extractedToolRequests = extractToolRequests(response);
@@ -8006,6 +8587,15 @@ ${traceSummary}` : traceSummary;
8006
8587
  },
8007
8588
  /* @__PURE__ */ import_react14.default.createElement(CloseIcon, null)
8008
8589
  )),
8590
+ compactionNotice && compactionNotice.level !== "info" && /* @__PURE__ */ import_react14.default.createElement("div", { className: `ai-chat-compaction-banner ai-chat-compaction-banner--${compactionNotice.level}` }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-compaction-banner__message" }, compactionNotice.message), /* @__PURE__ */ import_react14.default.createElement(
8591
+ "button",
8592
+ {
8593
+ className: "ai-chat-compaction-banner__close",
8594
+ onClick: () => setCompactionNotice(null),
8595
+ "aria-label": "Dismiss context notice"
8596
+ },
8597
+ /* @__PURE__ */ import_react14.default.createElement(CloseIcon, null)
8598
+ )),
8009
8599
  /* @__PURE__ */ import_react14.default.createElement(ScrollArea, { className: "ai-chat-panel__messages", ref: responseAreaRef }, initialMessage && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__content" }, markdownClass ? /* @__PURE__ */ import_react14.default.createElement("div", { className: markdownClass }, /* @__PURE__ */ import_react14.default.createElement(
8010
8600
  import_react_markdown2.default,
8011
8601
  {
@@ -8188,6 +8778,7 @@ ${traceSummary}` : traceSummary;
8188
8778
  },
8189
8779
  question
8190
8780
  ))), /* @__PURE__ */ import_react14.default.createElement("div", { ref: bottomRef })),
8781
+ (compactionNotice == null ? void 0 : compactionNotice.level) === "info" && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-compaction-inline-status", role: "status", "aria-live": "polite" }, /* @__PURE__ */ import_react14.default.createElement("span", null, compactionNotice.message)),
8191
8782
  (showSaveButton || showEmailButton || showCallToAction) && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-button-container" }, showSaveButton && /* @__PURE__ */ import_react14.default.createElement(
8192
8783
  Button,
8193
8784
  {
@@ -8327,6 +8918,8 @@ ${traceSummary}` : traceSummary;
8327
8918
  onSubmit: continueChat,
8328
8919
  onQueueSubmit: handleQueuePrompt,
8329
8920
  onStop: handleStop,
8921
+ userInputRequest: pendingUserInputRequest,
8922
+ onCompleteUserInputRequest: completePendingUserInputRequest,
8330
8923
  queuedPrompts,
8331
8924
  onClearQueuedPrompt: handleClearQueuedPrompt,
8332
8925
  agentOptions,
@@ -8340,7 +8933,8 @@ ${traceSummary}` : traceSummary;
8340
8933
  maxContextTokens,
8341
8934
  enableContextDetailView,
8342
8935
  disabledSectionIds,
8343
- onToggleSection: handleToggleSection
8936
+ onToggleSection: handleToggleSection,
8937
+ composerAgentModeControl
8344
8938
  }
8345
8939
  ),
8346
8940
  showPoweredBy && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__footer" }, mcpServers && mcpServers.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-tools-status" }, /* @__PURE__ */ import_react14.default.createElement(
@@ -8578,7 +9172,7 @@ var normalizeConversationListPayload = (payload) => {
8578
9172
  if (!title) {
8579
9173
  title = conv.summary || extractTitleFromConversation(conv);
8580
9174
  }
8581
- const resolvedConversationId = conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
9175
+ const resolvedConversationId = conv.sessionId || conv.session_id || conv.sessionKey || conv.session_key || conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
8582
9176
  return {
8583
9177
  conversationId: resolvedConversationId,
8584
9178
  title,
@@ -9067,6 +9661,8 @@ var ChatPanelWrapper = ({
9067
9661
  onToggleSection,
9068
9662
  onConversationCreated,
9069
9663
  onBeforeSend,
9664
+ compactContext,
9665
+ compactionPreserveTurns,
9070
9666
  conversationInitialPrompt,
9071
9667
  // New props from ChatPanel port
9072
9668
  cssUrl,
@@ -9088,7 +9684,8 @@ var ChatPanelWrapper = ({
9088
9684
  localToolExecutors,
9089
9685
  traceContextMode,
9090
9686
  autoApproveTools,
9091
- toolStatusLabelFormatter
9687
+ toolStatusLabelFormatter,
9688
+ composerAgentModeControl
9092
9689
  }) => {
9093
9690
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
9094
9691
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -9186,6 +9783,8 @@ var ChatPanelWrapper = ({
9186
9783
  onToggleSection,
9187
9784
  onConversationCreated: conversationCreatedCallback,
9188
9785
  onBeforeSend: beforeSendCallback,
9786
+ compactContext,
9787
+ compactionPreserveTurns,
9189
9788
  cssUrl,
9190
9789
  markdownClass,
9191
9790
  width,
@@ -9205,7 +9804,8 @@ var ChatPanelWrapper = ({
9205
9804
  localToolExecutors,
9206
9805
  traceContextMode,
9207
9806
  autoApproveTools,
9208
- toolStatusLabelFormatter
9807
+ toolStatusLabelFormatter,
9808
+ composerAgentModeControl
9209
9809
  }
9210
9810
  )
9211
9811
  );
@@ -9257,6 +9857,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9257
9857
  onAgentSwitch,
9258
9858
  onConversationChange,
9259
9859
  onBeforeSend,
9860
+ compactContext,
9861
+ compactionPreserveTurns,
9260
9862
  historyChangedCallback,
9261
9863
  responseCompleteCallback,
9262
9864
  thumbsUpClick,
@@ -9272,6 +9874,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9272
9874
  followOnQuestions = [],
9273
9875
  followOnPrompt = "",
9274
9876
  historyListLimit = 50,
9877
+ conversationSubtitleResolver,
9275
9878
  showConversationHistory = true,
9276
9879
  // New props from ChatPanel port
9277
9880
  cssUrl,
@@ -9293,7 +9896,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9293
9896
  localToolExecutors,
9294
9897
  traceContextMode = "standard",
9295
9898
  autoApproveTools,
9296
- toolStatusLabelFormatter
9899
+ toolStatusLabelFormatter,
9900
+ composerAgentModeControl
9297
9901
  }, ref) => {
9298
9902
  var _a, _b, _c, _d;
9299
9903
  (0, import_react16.useEffect)(() => {
@@ -9431,6 +10035,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9431
10035
  "This Month": false,
9432
10036
  "Older": false
9433
10037
  });
10038
+ const [recentlyFocusedConversationId, setRecentlyFocusedConversationId] = (0, import_react16.useState)(null);
9434
10039
  const [disabledContextSections, setDisabledContextSections] = (0, import_react16.useState)(/* @__PURE__ */ new Map());
9435
10040
  const {
9436
10041
  agents: agentProfiles,
@@ -9816,8 +10421,11 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9816
10421
  if (loadingConversationId === targetConversationId) {
9817
10422
  return;
9818
10423
  }
10424
+ if (currentConversationIdRef.current !== targetConversationId) {
10425
+ setCurrentConversationId(targetConversationId);
10426
+ }
9819
10427
  const existingActive = activeConversationsRef.current.get(targetConversationId);
9820
- if (existingActive && existingActive.transcriptLoaded && currentConversationIdRef.current === targetConversationId) {
10428
+ if (existingActive && existingActive.transcriptLoaded) {
9821
10429
  return;
9822
10430
  }
9823
10431
  const apiConversation = apiConversations.find(
@@ -9957,6 +10565,50 @@ var AIAgentPanel = import_react16.default.forwardRef(({
9957
10565
  }
9958
10566
  return groupConversationsByTime(filtered, true);
9959
10567
  }, [apiConversations, searchQuery, conversationFirstPrompts]);
10568
+ (0, import_react16.useEffect)(() => {
10569
+ if (!currentConversationId) {
10570
+ return;
10571
+ }
10572
+ const matchingGroup = groupedConversations.find(
10573
+ (group) => group.conversations.some((conversation2) => conversation2.conversationId === currentConversationId)
10574
+ );
10575
+ if (!matchingGroup) {
10576
+ return;
10577
+ }
10578
+ setExpandedSections((prev) => {
10579
+ if (prev[matchingGroup.label]) {
10580
+ return prev;
10581
+ }
10582
+ return __spreadProps(__spreadValues({}, prev), {
10583
+ [matchingGroup.label]: true
10584
+ });
10585
+ });
10586
+ }, [currentConversationId, groupedConversations]);
10587
+ (0, import_react16.useEffect)(() => {
10588
+ if (!currentConversationId) {
10589
+ return;
10590
+ }
10591
+ setRecentlyFocusedConversationId(currentConversationId);
10592
+ const timer = window.setTimeout(() => {
10593
+ setRecentlyFocusedConversationId((prev) => prev === currentConversationId ? null : prev);
10594
+ }, 1200);
10595
+ return () => window.clearTimeout(timer);
10596
+ }, [currentConversationId]);
10597
+ const conversationRowRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
10598
+ (0, import_react16.useEffect)(() => {
10599
+ if (!currentConversationId) {
10600
+ return;
10601
+ }
10602
+ const row = conversationRowRefs.current.get(currentConversationId);
10603
+ if (!row) {
10604
+ return;
10605
+ }
10606
+ row.scrollIntoView({
10607
+ block: "nearest",
10608
+ inline: "nearest",
10609
+ behavior: "smooth"
10610
+ });
10611
+ }, [currentConversationId, expandedSections, groupedConversations]);
9960
10612
  const effectiveCustomer = (0, import_react16.useMemo)(() => {
9961
10613
  return __spreadProps(__spreadValues({}, customer), {
9962
10614
  customer_id: customerId
@@ -10531,26 +11183,36 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10531
11183
  onClick: handleNewConversation
10532
11184
  },
10533
11185
  /* @__PURE__ */ import_react16.default.createElement(PlusIcon, null)
10534
- ))), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse History", side: "bottom" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleHistoryCollapse }, /* @__PURE__ */ import_react16.default.createElement(SidebarIcon, null))), collapsible && /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null))), /* @__PURE__ */ import_react16.default.createElement(ScrollArea, { className: "ai-agent-panel__conversations" }, activeConversationsList.length > 0 && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group ai-agent-panel__group--active" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group-label" }, /* @__PURE__ */ import_react16.default.createElement("span", null, "Active (", activeConversationsList.length, ")")), activeConversationsList.map((activeConv) => /* @__PURE__ */ import_react16.default.createElement(
10535
- "div",
10536
- {
10537
- key: activeConv.stableKey,
10538
- className: `ai-agent-panel__conversation ai-agent-panel__conversation--active-item ${currentConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--current" : ""}`,
10539
- onClick: () => {
10540
- commitConversationSelection(activeConv.conversationId, true);
10541
- }
10542
- },
10543
- /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ import_react16.default.createElement(LoadingDotIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, activeConv.title))),
10544
- /* @__PURE__ */ import_react16.default.createElement(
10545
- "button",
11186
+ ))), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse History", side: "bottom" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleHistoryCollapse }, /* @__PURE__ */ import_react16.default.createElement(SidebarIcon, null))), collapsible && /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null))), /* @__PURE__ */ import_react16.default.createElement(ScrollArea, { className: "ai-agent-panel__conversations" }, activeConversationsList.length > 0 && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group ai-agent-panel__group--active" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group-label" }, /* @__PURE__ */ import_react16.default.createElement("span", null, "Active (", activeConversationsList.length, ")")), activeConversationsList.map((activeConv) => {
11187
+ const subtitle = (conversationSubtitleResolver == null ? void 0 : conversationSubtitleResolver(activeConv.conversationId, activeConv)) || "";
11188
+ return /* @__PURE__ */ import_react16.default.createElement(
11189
+ "div",
10546
11190
  {
10547
- className: "ai-agent-panel__conversation-close",
10548
- onClick: (e) => handleCloseConversation(activeConv.conversationId, e),
10549
- title: "Close conversation"
11191
+ key: activeConv.stableKey,
11192
+ 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" : ""}`,
11193
+ ref: (node) => {
11194
+ if (node) {
11195
+ conversationRowRefs.current.set(activeConv.conversationId, node);
11196
+ } else {
11197
+ conversationRowRefs.current.delete(activeConv.conversationId);
11198
+ }
11199
+ },
11200
+ onClick: () => {
11201
+ commitConversationSelection(activeConv.conversationId, true);
11202
+ }
10550
11203
  },
10551
- /* @__PURE__ */ import_react16.default.createElement(CloseIcon2, null)
10552
- )
10553
- ))), conversationsLoading ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("span", null, "Loading conversations...")) : conversationsError ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ import_react16.default.createElement("p", null, "Error: ", conversationsError), /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "secondary", size: "sm", onClick: handleRefreshConversations }, "Retry")) : groupedConversations.length === 0 && activeConversationsList.length === 0 ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ import_react16.default.createElement(MessageIcon, null), /* @__PURE__ */ import_react16.default.createElement("p", null, "No conversations yet"), /* @__PURE__ */ import_react16.default.createElement("p", { className: "ai-agent-panel__empty-hint" }, "Start chatting to create your first conversation")) : /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, activeConversationsList.length > 0 && groupedConversations.some((g) => g.count > 0) && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group-divider" }), groupedConversations.map((group) => /* @__PURE__ */ import_react16.default.createElement("div", { key: group.label, className: "ai-agent-panel__group" }, /* @__PURE__ */ import_react16.default.createElement(
11204
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ import_react16.default.createElement(LoadingDotIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, activeConv.title)), subtitle ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null),
11205
+ /* @__PURE__ */ import_react16.default.createElement(
11206
+ "button",
11207
+ {
11208
+ className: "ai-agent-panel__conversation-close",
11209
+ onClick: (e) => handleCloseConversation(activeConv.conversationId, e),
11210
+ title: "Close conversation"
11211
+ },
11212
+ /* @__PURE__ */ import_react16.default.createElement(CloseIcon2, null)
11213
+ )
11214
+ );
11215
+ })), conversationsLoading ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("span", null, "Loading conversations...")) : conversationsError ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ import_react16.default.createElement("p", null, "Error: ", conversationsError), /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "secondary", size: "sm", onClick: handleRefreshConversations }, "Retry")) : groupedConversations.length === 0 && activeConversationsList.length === 0 ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty" }, /* @__PURE__ */ import_react16.default.createElement(MessageIcon, null), /* @__PURE__ */ import_react16.default.createElement("p", null, "No conversations yet"), /* @__PURE__ */ import_react16.default.createElement("p", { className: "ai-agent-panel__empty-hint" }, "Start chatting to create your first conversation")) : /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, activeConversationsList.length > 0 && groupedConversations.some((g) => g.count > 0) && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__group-divider" }), groupedConversations.map((group) => /* @__PURE__ */ import_react16.default.createElement("div", { key: group.label, className: "ai-agent-panel__group" }, /* @__PURE__ */ import_react16.default.createElement(
10554
11216
  "div",
10555
11217
  {
10556
11218
  className: "ai-agent-panel__group-label ai-agent-panel__group-label--clickable",
@@ -10560,14 +11222,22 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10560
11222
  /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__group-chevron" }, expandedSections[group.label] ? "\u25BC" : "\u25B6")
10561
11223
  ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv, convIndex) => {
10562
11224
  const isActive = activeConversations.has(conv.conversationId);
11225
+ const subtitle = (conversationSubtitleResolver == null ? void 0 : conversationSubtitleResolver(conv.conversationId, conv)) || "";
10563
11226
  return /* @__PURE__ */ import_react16.default.createElement(
10564
11227
  "div",
10565
11228
  {
10566
11229
  key: `${group.label}-${conv.conversationId}-${convIndex}`,
10567
- className: `ai-agent-panel__conversation ${currentConversationId === conv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${isActive ? "ai-agent-panel__conversation--in-active" : ""}`,
11230
+ 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" : ""}`,
11231
+ ref: (node) => {
11232
+ if (node) {
11233
+ conversationRowRefs.current.set(conv.conversationId, node);
11234
+ } else {
11235
+ conversationRowRefs.current.delete(conv.conversationId);
11236
+ }
11237
+ },
10568
11238
  onClick: () => handleConversationSelect(conv)
10569
11239
  },
10570
- /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title))
11240
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title), subtitle ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null)
10571
11241
  );
10572
11242
  }))))))),
10573
11243
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ import_react16.default.createElement(SparkleIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ import_react16.default.createElement(
@@ -10605,6 +11275,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10605
11275
  onToggleSection: handleContextSectionToggle,
10606
11276
  onConversationCreated: handleConversationCreated,
10607
11277
  onBeforeSend,
11278
+ compactContext,
11279
+ compactionPreserveTurns,
10608
11280
  conversationInitialPrompt: activeConv.conversationInitialPrompt,
10609
11281
  cssUrl,
10610
11282
  markdownClass,
@@ -10625,7 +11297,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10625
11297
  localToolExecutors,
10626
11298
  traceContextMode,
10627
11299
  autoApproveTools,
10628
- toolStatusLabelFormatter
11300
+ toolStatusLabelFormatter,
11301
+ composerAgentModeControl
10629
11302
  }
10630
11303
  )), loadingConversationId && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ import_react16.default.createElement(MessageIcon, null), /* @__PURE__ */ import_react16.default.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("p", null, "Loading agent..."))),
10631
11304
  /* @__PURE__ */ import_react16.default.createElement(