@cadraos/sdk 0.1.11-test → 0.1.13

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.
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var fetchEventSource = require('@microsoft/fetch-event-source');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var reactDom = require('react-dom');
6
7
  var ReactMarkdown = require('react-markdown');
7
8
  var remarkGfm = require('remark-gfm');
8
9
 
@@ -588,9 +589,11 @@ var DEFAULT_STREAMING_CONFIG = {
588
589
  activityTimeoutMs: 3e4,
589
590
  pollingIntervalMs: 2e3
590
591
  };
592
+ var DEFAULT_POLLING_ENABLED = true;
591
593
  var ExecutionStreamControllerImpl = class {
592
594
  options;
593
595
  config;
596
+ pollingEnabled;
594
597
  sseClient = null;
595
598
  status = "connecting";
596
599
  reconnectAttempts = 0;
@@ -624,6 +627,7 @@ var ExecutionStreamControllerImpl = class {
624
627
  constructor(options) {
625
628
  this.options = options;
626
629
  this.config = { ...DEFAULT_STREAMING_CONFIG, ...options.config };
630
+ this.pollingEnabled = options.pollingEnabled ?? DEFAULT_POLLING_ENABLED;
627
631
  }
628
632
  /**
629
633
  * Get the last event ID (for resume support)
@@ -964,15 +968,61 @@ var ExecutionStreamControllerImpl = class {
964
968
  if (this.status === "closed") {
965
969
  return;
966
970
  }
971
+ await this.recoverState();
972
+ if (this.status === "closed") {
973
+ return;
974
+ }
967
975
  try {
968
976
  await this.connectSSE();
969
977
  } catch {
970
978
  await this.handleConnectionError(error);
971
979
  }
972
980
  } else {
973
- this.status = "fallback_polling";
974
- this.emit("connectionChange", { connected: false, usingFallback: true });
975
- this.startPolling();
981
+ if (this.pollingEnabled) {
982
+ this.status = "fallback_polling";
983
+ this.emit("connectionChange", { connected: false, usingFallback: true });
984
+ this.startPolling();
985
+ } else {
986
+ this.handleError({
987
+ error: "SSE connection failed and polling is disabled",
988
+ errorCode: "connection_failed"
989
+ });
990
+ }
991
+ }
992
+ }
993
+ /**
994
+ * Recover state by checking execution status.
995
+ *
996
+ * Called during reconnection to catch any events missed while SSE was down.
997
+ * If the execution completed while disconnected, emits completion immediately.
998
+ * Otherwise, emits any new logs that weren't seen via SSE.
999
+ */
1000
+ async recoverState() {
1001
+ try {
1002
+ const execution = await this.options.getExecution(this.options.executionId);
1003
+ if (execution.logs) {
1004
+ for (const log of execution.logs) {
1005
+ if (!this.seenEventIds.has(log.uuid)) {
1006
+ this.seenEventIds.add(log.uuid);
1007
+ this.emit("log", log);
1008
+ }
1009
+ }
1010
+ }
1011
+ if (execution.status === "completed") {
1012
+ this.stopPolling();
1013
+ this.handleComplete(execution);
1014
+ return;
1015
+ }
1016
+ if (execution.status === "failed" || execution.status === "cancelled") {
1017
+ this.stopPolling();
1018
+ this.handleError({
1019
+ error: execution.error ?? `Execution ${execution.status}`,
1020
+ errorCode: execution.status
1021
+ });
1022
+ return;
1023
+ }
1024
+ } catch (err) {
1025
+ console.warn("[SDK:StreamController] State recovery failed (non-fatal):", err);
976
1026
  }
977
1027
  }
978
1028
  /**
@@ -2824,12 +2874,54 @@ function useExecutionStream(options) {
2824
2874
  }
2825
2875
 
2826
2876
  // src/chat/hooks/useAgentChat.ts
2877
+ var __DEV__ = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
2827
2878
  var SILENT_TOOL_NAMES = /* @__PURE__ */ new Set([
2828
2879
  "check_delegations"
2829
2880
  ]);
2830
2881
  function generateId() {
2831
2882
  return `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
2832
2883
  }
2884
+ function extractImagesFromResult(result, sourceToolCallId) {
2885
+ if (!result) return [];
2886
+ let data = result;
2887
+ if (typeof result === "string") {
2888
+ try {
2889
+ data = JSON.parse(result);
2890
+ } catch {
2891
+ if (/\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(result) || result.startsWith("data:image/")) {
2892
+ return [{ url: result, sourceToolCallId }];
2893
+ }
2894
+ return [];
2895
+ }
2896
+ }
2897
+ if (typeof data !== "object" || data === null) return [];
2898
+ const obj = data;
2899
+ if (Array.isArray(obj.images)) {
2900
+ return obj.images.filter((img) => typeof img.url === "string").map((img) => ({
2901
+ url: img.url,
2902
+ alt: img.alt || img.prompt || void 0,
2903
+ filename: img.filename,
2904
+ sourceToolCallId
2905
+ }));
2906
+ }
2907
+ if (typeof obj.url === "string" && /\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(obj.url)) {
2908
+ return [{ url: obj.url, alt: obj.alt, sourceToolCallId }];
2909
+ }
2910
+ if (typeof obj.imageUrl === "string") {
2911
+ return [{ url: obj.imageUrl, alt: obj.alt, sourceToolCallId }];
2912
+ }
2913
+ return [];
2914
+ }
2915
+ function deriveImagesFromToolCalls(toolCalls) {
2916
+ if (!toolCalls) return [];
2917
+ const images = [];
2918
+ for (const tc of toolCalls) {
2919
+ if (tc.status === "completed" && tc.result) {
2920
+ images.push(...extractImagesFromResult(tc.result, tc.id));
2921
+ }
2922
+ }
2923
+ return images;
2924
+ }
2833
2925
  function historyToMessage(historyMsg) {
2834
2926
  return {
2835
2927
  id: historyMsg.uuid,
@@ -2870,7 +2962,9 @@ function useAgentChat(options) {
2870
2962
  // Deprecated, but still supported
2871
2963
  onToolCall,
2872
2964
  onToolResult,
2873
- onClarification
2965
+ onClarification,
2966
+ pollingInterval,
2967
+ pollingEnabled
2874
2968
  } = options;
2875
2969
  const contextConfig = useChatConfigOptional();
2876
2970
  const adapter = propsAdapter ?? contextConfig?.adapter;
@@ -2902,8 +2996,14 @@ function useAgentChat(options) {
2902
2996
  const streamingMessageIdRef = react.useRef(null);
2903
2997
  const pendingToolCallsRef = react.useRef(/* @__PURE__ */ new Map());
2904
2998
  const lastMessageContentRef = react.useRef("");
2999
+ const finalizedRef = react.useRef(false);
2905
3000
  const firedToolResultsRef = react.useRef(/* @__PURE__ */ new Set());
2906
3001
  const streamControllerRef = react.useRef(null);
3002
+ const pendingDelegationsRef = react.useRef(/* @__PURE__ */ new Map());
3003
+ const onDelegationRef = react.useRef(options.onDelegation);
3004
+ react.useEffect(() => {
3005
+ onDelegationRef.current = options.onDelegation;
3006
+ }, [options.onDelegation]);
2907
3007
  const updateStreamingMessage = react.useCallback((content) => {
2908
3008
  if (!streamingMessageIdRef.current) return;
2909
3009
  setMessages(
@@ -2932,25 +3032,72 @@ function useAgentChat(options) {
2932
3032
  });
2933
3033
  });
2934
3034
  }, []);
2935
- const finalizeStreamingMessage = react.useCallback((terminalStatus = "completed") => {
3035
+ const addDelegationEvent = react.useCallback((delegation) => {
3036
+ setMessages((prev) => {
3037
+ const targetId = streamingMessageIdRef.current ?? [...prev].reverse().find((m) => m.role === "assistant")?.id;
3038
+ if (!targetId) return prev;
3039
+ return prev.map((msg) => {
3040
+ if (msg.id !== targetId) return msg;
3041
+ const existing = msg.delegationEvents ?? [];
3042
+ if (delegation.type === "delegation_completed" && delegation.handoffId) {
3043
+ const updatedEvents = existing.map(
3044
+ (de) => de.handoffId === delegation.handoffId ? { ...de, ...delegation, status: delegation.status ?? "completed" } : de
3045
+ );
3046
+ const hasMatch = existing.some((de) => de.handoffId === delegation.handoffId);
3047
+ return {
3048
+ ...msg,
3049
+ delegationEvents: hasMatch ? updatedEvents : [...existing, delegation]
3050
+ };
3051
+ }
3052
+ return { ...msg, delegationEvents: [...existing, delegation] };
3053
+ });
3054
+ });
3055
+ }, []);
3056
+ const updateMessageImages = react.useCallback(() => {
3057
+ setMessages((prev) => {
3058
+ const targetId = streamingMessageIdRef.current ?? [...prev].reverse().find((m) => m.role === "assistant")?.id;
3059
+ if (!targetId) return prev;
3060
+ return prev.map((msg) => {
3061
+ if (msg.id !== targetId) return msg;
3062
+ const images = deriveImagesFromToolCalls(msg.toolCalls);
3063
+ return images.length > 0 ? { ...msg, images } : msg;
3064
+ });
3065
+ });
3066
+ }, []);
3067
+ const finalizeStreamingMessage = react.useCallback((finalOutput) => {
2936
3068
  if (!streamingMessageIdRef.current) return;
2937
- setMessages(
2938
- (prev) => prev.map((msg) => {
2939
- if (msg.id !== streamingMessageIdRef.current) return msg;
3069
+ finalizedRef.current = true;
3070
+ const streamMsgId = streamingMessageIdRef.current;
3071
+ setMessages((prev) => {
3072
+ const resolvedContent = finalOutput || streamingContentRef.current || "";
3073
+ const duplicateExists = resolvedContent && prev.some(
3074
+ (msg) => msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
3075
+ );
3076
+ if (duplicateExists) {
3077
+ return prev.filter((msg) => msg.id !== streamMsgId);
3078
+ }
3079
+ return prev.map((msg) => {
3080
+ if (msg.id !== streamMsgId) return msg;
2940
3081
  const updatedToolCalls = msg.toolCalls?.map(
2941
- (tc) => tc.status === "running" || tc.status === "pending" ? { ...tc, status: terminalStatus } : tc
3082
+ (tc) => tc.status === "running" || tc.status === "pending" ? { ...tc, status: "completed" } : tc
2942
3083
  );
2943
- return { ...msg, isStreaming: false, toolCalls: updatedToolCalls };
2944
- })
2945
- );
3084
+ return {
3085
+ ...msg,
3086
+ content: resolvedContent || msg.content,
3087
+ isStreaming: false,
3088
+ toolCalls: updatedToolCalls
3089
+ };
3090
+ });
3091
+ });
2946
3092
  streamingMessageIdRef.current = null;
2947
3093
  streamingContentRef.current = "";
2948
3094
  pendingToolCallsRef.current.clear();
2949
3095
  firedToolResultsRef.current.clear();
3096
+ pendingDelegationsRef.current.clear();
2950
3097
  }, []);
2951
3098
  const handleExecutionLog = react.useCallback(
2952
3099
  (log) => {
2953
- console.log("[useAgentChat] handleExecutionLog:", log.type, log.metadata);
3100
+ if (__DEV__) console.log("[useAgentChat] handleExecutionLog:", log.type, log.metadata);
2954
3101
  switch (log.type) {
2955
3102
  // Primary handlers for ai-saas-api worker log types
2956
3103
  case "llm_response": {
@@ -2970,11 +3117,46 @@ function useAgentChat(options) {
2970
3117
  }
2971
3118
  break;
2972
3119
  }
3120
+ case "delegation": {
3121
+ const delegationType = log.metadata?.delegationType;
3122
+ if (delegationType === "started" || delegationType === "delegation_started") {
3123
+ const delegation = {
3124
+ type: "delegation_started",
3125
+ handoffId: log.metadata?.handoffId,
3126
+ toAgent: log.metadata?.targetAgentName || log.metadata?.targetAgentUuid,
3127
+ targetAgentUuid: log.metadata?.targetAgentUuid,
3128
+ taskPreview: log.metadata?.taskPreview,
3129
+ timestamp: new Date(log.timestamp || Date.now()),
3130
+ status: "active"
3131
+ };
3132
+ if (delegation.handoffId) {
3133
+ pendingDelegationsRef.current.set(delegation.handoffId, delegation);
3134
+ }
3135
+ addDelegationEvent(delegation);
3136
+ onDelegationRef.current?.(delegation);
3137
+ } else if (delegationType === "completed" || delegationType === "delegation_completed") {
3138
+ const delegation = {
3139
+ type: "delegation_completed",
3140
+ handoffId: log.metadata?.handoffId,
3141
+ resultPreview: log.metadata?.resultPreview,
3142
+ timestamp: new Date(log.timestamp || Date.now()),
3143
+ status: log.metadata?.status === "failed" ? "failed" : "completed",
3144
+ durationMs: log.durationMs
3145
+ };
3146
+ if (delegation.handoffId) {
3147
+ pendingDelegationsRef.current.delete(delegation.handoffId);
3148
+ }
3149
+ addDelegationEvent(delegation);
3150
+ onDelegationRef.current?.(delegation);
3151
+ }
3152
+ break;
3153
+ }
2973
3154
  case "info":
2974
3155
  case "system":
2975
- console.log("[useAgentChat] Status:", log.type, log.title);
3156
+ if (__DEV__) console.log("[useAgentChat] Status:", log.type, log.title);
2976
3157
  break;
2977
3158
  case "tool_call": {
3159
+ if (finalizedRef.current) break;
2978
3160
  const toolName = log.metadata?.toolName;
2979
3161
  if (SILENT_TOOL_NAMES.has(toolName)) break;
2980
3162
  const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
@@ -3029,17 +3211,18 @@ function useAgentChat(options) {
3029
3211
  firedToolResultsRef.current.add(resultToolCallId);
3030
3212
  }
3031
3213
  onToolResultRef.current?.(resultToolName, result, durationMs);
3214
+ updateMessageImages();
3032
3215
  break;
3033
3216
  }
3034
3217
  default:
3035
- console.log("[useAgentChat] Unhandled log type:", log.type, log);
3218
+ if (__DEV__) console.log("[useAgentChat] Unhandled log type:", log.type, log);
3036
3219
  }
3037
3220
  },
3038
- [updateStreamingMessage, updateToolCall]
3221
+ [updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent]
3039
3222
  );
3040
3223
  const handleStreamLog = react.useCallback(
3041
3224
  (log) => {
3042
- console.log("[useAgentChat] handleStreamLog:", log.logType, log.metadata);
3225
+ if (__DEV__) console.log("[useAgentChat] handleStreamLog:", log.logType, log.metadata);
3043
3226
  switch (log.logType) {
3044
3227
  case "llm_response": {
3045
3228
  const llmText = log.metadata?.text || "";
@@ -3058,9 +3241,42 @@ function useAgentChat(options) {
3058
3241
  }
3059
3242
  break;
3060
3243
  }
3244
+ case "delegation_started": {
3245
+ const delegation = {
3246
+ type: "delegation_started",
3247
+ handoffId: log.metadata?.handoffId,
3248
+ toAgent: log.metadata?.targetAgentName || log.metadata?.targetAgentUuid,
3249
+ targetAgentUuid: log.metadata?.targetAgentUuid,
3250
+ taskPreview: log.metadata?.taskPreview,
3251
+ timestamp: new Date(log.timestamp || Date.now()),
3252
+ status: "active"
3253
+ };
3254
+ if (delegation.handoffId) {
3255
+ pendingDelegationsRef.current.set(delegation.handoffId, delegation);
3256
+ }
3257
+ addDelegationEvent(delegation);
3258
+ onDelegationRef.current?.(delegation);
3259
+ break;
3260
+ }
3261
+ case "delegation_completed": {
3262
+ const delegation = {
3263
+ type: "delegation_completed",
3264
+ handoffId: log.metadata?.handoffId,
3265
+ resultPreview: log.metadata?.resultPreview,
3266
+ timestamp: new Date(log.timestamp || Date.now()),
3267
+ status: log.metadata?.status === "failed" ? "failed" : "completed",
3268
+ durationMs: log.durationMs
3269
+ };
3270
+ if (delegation.handoffId) {
3271
+ pendingDelegationsRef.current.delete(delegation.handoffId);
3272
+ }
3273
+ addDelegationEvent(delegation);
3274
+ onDelegationRef.current?.(delegation);
3275
+ break;
3276
+ }
3061
3277
  case "info":
3062
3278
  case "system":
3063
- console.log("[useAgentChat] Status:", log.logType, log.message);
3279
+ if (__DEV__) console.log("[useAgentChat] Status:", log.logType, log.message);
3064
3280
  break;
3065
3281
  case "response_text":
3066
3282
  case "text_chunk": {
@@ -3070,6 +3286,7 @@ function useAgentChat(options) {
3070
3286
  break;
3071
3287
  }
3072
3288
  case "tool_call": {
3289
+ if (finalizedRef.current) break;
3073
3290
  const toolName = log.metadata?.toolName;
3074
3291
  if (SILENT_TOOL_NAMES.has(toolName)) break;
3075
3292
  const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
@@ -3124,6 +3341,7 @@ function useAgentChat(options) {
3124
3341
  firedToolResultsRef.current.add(resultToolCallId);
3125
3342
  }
3126
3343
  onToolResultRef.current?.(resultToolName, result, durationMs);
3344
+ updateMessageImages();
3127
3345
  break;
3128
3346
  }
3129
3347
  case "final_response": {
@@ -3135,10 +3353,10 @@ function useAgentChat(options) {
3135
3353
  break;
3136
3354
  }
3137
3355
  default:
3138
- console.log("[useAgentChat] Unknown log type:", log.logType, log);
3356
+ if (__DEV__) console.log("[useAgentChat] Unknown log type:", log.logType, log);
3139
3357
  }
3140
3358
  },
3141
- [updateStreamingMessage, updateToolCall]
3359
+ [updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent]
3142
3360
  );
3143
3361
  const handleClarification = react.useCallback(
3144
3362
  (data) => {
@@ -3147,17 +3365,23 @@ function useAgentChat(options) {
3147
3365
  },
3148
3366
  [onClarification]
3149
3367
  );
3150
- const handleComplete = react.useCallback(() => {
3151
- console.log("[useAgentChat] handleComplete called");
3368
+ const completedRef = react.useRef(false);
3369
+ const handleComplete = react.useCallback((finalOutput) => {
3370
+ if (completedRef.current) {
3371
+ if (__DEV__) console.log("[useAgentChat] handleComplete: skipping duplicate complete");
3372
+ return;
3373
+ }
3374
+ completedRef.current = true;
3375
+ if (__DEV__) console.log("[useAgentChat] handleComplete called");
3152
3376
  setIsLoading(false);
3153
3377
  setIsStreamControllerConnected(false);
3154
- finalizeStreamingMessage();
3378
+ finalizeStreamingMessage(finalOutput);
3155
3379
  setCurrentExecutionId(null);
3156
3380
  streamControllerRef.current = null;
3157
3381
  }, [finalizeStreamingMessage]);
3158
3382
  const handleError = react.useCallback(
3159
3383
  (errorMsg) => {
3160
- console.log("[useAgentChat] handleError called:", errorMsg);
3384
+ if (__DEV__) console.log("[useAgentChat] handleError called:", errorMsg);
3161
3385
  const chatError = {
3162
3386
  code: "EXECUTION_ERROR",
3163
3387
  message: errorMsg,
@@ -3166,7 +3390,7 @@ function useAgentChat(options) {
3166
3390
  setError(chatError);
3167
3391
  setIsLoading(false);
3168
3392
  setIsStreamControllerConnected(false);
3169
- finalizeStreamingMessage("failed");
3393
+ finalizeStreamingMessage();
3170
3394
  setCurrentExecutionId(null);
3171
3395
  streamControllerRef.current = null;
3172
3396
  onError?.(chatError);
@@ -3196,43 +3420,39 @@ function useAgentChat(options) {
3196
3420
  if (pollingExecutionIdRef.current === executionId) return;
3197
3421
  stopPolling();
3198
3422
  pollingExecutionIdRef.current = executionId;
3199
- console.log("[useAgentChat] Starting polling fallback for:", executionId);
3423
+ if (__DEV__) console.log("[useAgentChat] Starting polling fallback for:", executionId);
3200
3424
  const poll = async () => {
3201
3425
  if (pollingExecutionIdRef.current !== executionId) return;
3202
3426
  try {
3203
3427
  const execution = await adapter.getExecution(executionId);
3204
3428
  if (execution.status === "completed") {
3205
- console.log("[useAgentChat] Polling: execution completed");
3206
- if (execution.result) {
3207
- streamingContentRef.current = execution.result;
3208
- updateStreamingMessage(execution.result);
3209
- }
3429
+ if (__DEV__) console.log("[useAgentChat] Polling: execution completed");
3210
3430
  stopPolling();
3211
- handleComplete();
3431
+ handleComplete(execution.result || void 0);
3212
3432
  } else if (execution.status === "failed") {
3213
- console.log("[useAgentChat] Polling: execution failed");
3433
+ if (__DEV__) console.log("[useAgentChat] Polling: execution failed");
3214
3434
  stopPolling();
3215
3435
  handleError(execution.error || "Execution failed");
3216
3436
  }
3217
3437
  } catch (err) {
3218
- console.warn("[useAgentChat] Polling error (will retry):", err);
3438
+ if (__DEV__) console.warn("[useAgentChat] Polling error (will retry):", err);
3219
3439
  }
3220
3440
  };
3221
3441
  poll();
3222
- pollingIntervalRef.current = setInterval(poll, 2e3);
3223
- }, [adapter, stopPolling, updateStreamingMessage, handleComplete, handleError]);
3442
+ pollingIntervalRef.current = setInterval(poll, pollingInterval ?? 2e3);
3443
+ }, [adapter, stopPolling, handleComplete, handleError, pollingInterval]);
3224
3444
  react.useEffect(() => {
3225
- if (!useStreamController && isUsingFallback && currentExecutionId && isLoading) {
3226
- console.log("[useAgentChat] SSE fallback detected, activating polling");
3445
+ if (!useStreamController && isUsingFallback && currentExecutionId && isLoading && pollingEnabled !== false) {
3446
+ if (__DEV__) console.log("[useAgentChat] SSE fallback detected, activating polling");
3227
3447
  startPolling(currentExecutionId);
3228
3448
  }
3229
- }, [useStreamController, isUsingFallback, currentExecutionId, isLoading, startPolling]);
3449
+ }, [useStreamController, isUsingFallback, currentExecutionId, isLoading, startPolling, pollingEnabled]);
3230
3450
  const sseTimeoutRef = react.useRef(null);
3231
3451
  react.useEffect(() => {
3232
- if (!useStreamController && currentExecutionId && isLoading && !legacyIsConnected && !isUsingFallback) {
3452
+ if (!useStreamController && currentExecutionId && isLoading && !legacyIsConnected && !isUsingFallback && pollingEnabled !== false) {
3233
3453
  sseTimeoutRef.current = setTimeout(() => {
3234
3454
  if (!legacyIsConnected && currentExecutionId && isLoading) {
3235
- console.log("[useAgentChat] SSE connection timeout, activating polling fallback");
3455
+ if (__DEV__) console.log("[useAgentChat] SSE connection timeout, activating polling fallback");
3236
3456
  startPolling(currentExecutionId);
3237
3457
  }
3238
3458
  }, 1e4);
@@ -3243,7 +3463,7 @@ function useAgentChat(options) {
3243
3463
  sseTimeoutRef.current = null;
3244
3464
  }
3245
3465
  };
3246
- }, [useStreamController, currentExecutionId, isLoading, legacyIsConnected, isUsingFallback, startPolling]);
3466
+ }, [useStreamController, currentExecutionId, isLoading, legacyIsConnected, isUsingFallback, startPolling, pollingEnabled]);
3247
3467
  react.useEffect(() => {
3248
3468
  if (!currentExecutionId) {
3249
3469
  stopPolling();
@@ -3259,7 +3479,7 @@ function useAgentChat(options) {
3259
3479
  streamControllerRef.current.cancel();
3260
3480
  streamControllerRef.current = null;
3261
3481
  }
3262
- console.log("[useAgentChat] Creating stream controller for:", executionId);
3482
+ if (__DEV__) console.log("[useAgentChat] Creating stream controller for:", executionId);
3263
3483
  const controller = adapter.createStream(executionId, sessionId);
3264
3484
  streamControllerRef.current = controller;
3265
3485
  controller.on("log", (log) => {
@@ -3310,6 +3530,34 @@ function useAgentChat(options) {
3310
3530
  }
3311
3531
  firedToolResultsRef.current.add(toolCallId);
3312
3532
  onToolResultRef.current?.(toolName, data.result, data.durationMs);
3533
+ updateMessageImages();
3534
+ });
3535
+ controller.on("delegationStarted", (data) => {
3536
+ const delegation = {
3537
+ type: "delegation_started",
3538
+ handoffId: data.handoffId,
3539
+ toAgent: data.targetAgentName || data.targetAgentUuid,
3540
+ targetAgentUuid: data.targetAgentUuid,
3541
+ taskPreview: data.taskPreview,
3542
+ timestamp: new Date(data.timestamp),
3543
+ status: "active"
3544
+ };
3545
+ pendingDelegationsRef.current.set(data.handoffId, delegation);
3546
+ addDelegationEvent(delegation);
3547
+ onDelegationRef.current?.(delegation);
3548
+ });
3549
+ controller.on("delegationCompleted", (data) => {
3550
+ const delegation = {
3551
+ type: "delegation_completed",
3552
+ handoffId: data.handoffId,
3553
+ resultPreview: data.resultPreview,
3554
+ timestamp: new Date(data.timestamp),
3555
+ status: data.status === "failed" ? "failed" : "completed",
3556
+ durationMs: data.durationMs
3557
+ };
3558
+ pendingDelegationsRef.current.delete(data.handoffId);
3559
+ addDelegationEvent(delegation);
3560
+ onDelegationRef.current?.(delegation);
3313
3561
  });
3314
3562
  controller.on("textDelta", (data) => {
3315
3563
  streamingContentRef.current += data.delta;
@@ -3323,11 +3571,8 @@ function useAgentChat(options) {
3323
3571
  });
3324
3572
  });
3325
3573
  controller.on("complete", ({ execution }) => {
3326
- if (execution?.output && !streamingContentRef.current) {
3327
- streamingContentRef.current = execution.output;
3328
- updateStreamingMessage(execution.output);
3329
- }
3330
- handleComplete();
3574
+ const output = execution?.output || void 0;
3575
+ handleComplete(output);
3331
3576
  });
3332
3577
  controller.on("error", (errorEvent) => {
3333
3578
  handleError(errorEvent.error);
@@ -3341,7 +3586,7 @@ function useAgentChat(options) {
3341
3586
  });
3342
3587
  setIsStreamControllerConnected(true);
3343
3588
  },
3344
- [adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
3589
+ [adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent, handleClarification, handleComplete, handleError]
3345
3590
  );
3346
3591
  const sendMessage = react.useCallback(
3347
3592
  async (content, options2) => {
@@ -3371,8 +3616,10 @@ function useAgentChat(options) {
3371
3616
  setMessages((prev) => [...prev, assistantMessage]);
3372
3617
  streamingMessageIdRef.current = assistantMessageId;
3373
3618
  streamingContentRef.current = "";
3619
+ completedRef.current = false;
3620
+ finalizedRef.current = false;
3374
3621
  try {
3375
- console.log("[useAgentChat] Starting execution...", { agentId, sessionId: currentSessionId });
3622
+ if (__DEV__) console.log("[useAgentChat] Starting execution...", { agentId, sessionId: currentSessionId });
3376
3623
  const result = await adapter.startExecution({
3377
3624
  agentId,
3378
3625
  teamId,
@@ -3380,7 +3627,7 @@ function useAgentChat(options) {
3380
3627
  content: content.trim(),
3381
3628
  attachmentUuids
3382
3629
  });
3383
- console.log("[useAgentChat] Execution started:", result);
3630
+ if (__DEV__) console.log("[useAgentChat] Execution started:", result);
3384
3631
  if (!currentSessionId && result.sessionId) {
3385
3632
  internallyCreatedSessionRef.current = result.sessionId;
3386
3633
  setCurrentSessionId(result.sessionId);
@@ -3391,7 +3638,7 @@ function useAgentChat(options) {
3391
3638
  connectStreamController(result.executionId, result.sessionId || currentSessionId || void 0);
3392
3639
  setCurrentExecutionId(result.executionId);
3393
3640
  } else {
3394
- console.log("[useAgentChat] Setting executionId:", result.executionId);
3641
+ if (__DEV__) console.log("[useAgentChat] Setting executionId:", result.executionId);
3395
3642
  setCurrentExecutionId(result.executionId);
3396
3643
  }
3397
3644
  } catch (err) {
@@ -3451,6 +3698,7 @@ function useAgentChat(options) {
3451
3698
  setMessages([]);
3452
3699
  setCurrentSessionId(null);
3453
3700
  hasLoadedInitialSession.current = false;
3701
+ lastLoadedSessionRef.current = null;
3454
3702
  internallyCreatedSessionRef.current = null;
3455
3703
  }, []);
3456
3704
  const loadSession = react.useCallback(
@@ -3546,15 +3794,20 @@ function useAgentChat(options) {
3546
3794
  },
3547
3795
  [adapter, onError]
3548
3796
  );
3797
+ const lastLoadedSessionRef = react.useRef(null);
3549
3798
  react.useEffect(() => {
3550
- if (initialSessionId && !hasLoadedInitialSession.current) {
3551
- if (internallyCreatedSessionRef.current === initialSessionId) {
3552
- hasLoadedInitialSession.current = true;
3553
- return;
3554
- }
3799
+ if (!initialSessionId) return;
3800
+ if (internallyCreatedSessionRef.current === initialSessionId) {
3801
+ lastLoadedSessionRef.current = initialSessionId;
3555
3802
  hasLoadedInitialSession.current = true;
3556
- loadSession(initialSessionId);
3803
+ return;
3804
+ }
3805
+ if (lastLoadedSessionRef.current === initialSessionId && hasLoadedInitialSession.current) {
3806
+ return;
3557
3807
  }
3808
+ lastLoadedSessionRef.current = initialSessionId;
3809
+ hasLoadedInitialSession.current = true;
3810
+ loadSession(initialSessionId);
3558
3811
  }, [initialSessionId, loadSession]);
3559
3812
  react.useEffect(() => {
3560
3813
  return () => {
@@ -3570,6 +3823,16 @@ function useAgentChat(options) {
3570
3823
  }
3571
3824
  return !!currentExecutionId && legacyIsConnected;
3572
3825
  }, [useStreamController, currentExecutionId, isStreamControllerConnected, legacyIsConnected]);
3826
+ const activeDelegation = react.useMemo(() => {
3827
+ for (let i = messages.length - 1; i >= 0; i--) {
3828
+ const msg = messages[i];
3829
+ if (msg.delegationEvents) {
3830
+ const active = msg.delegationEvents.find((de) => de.status === "active");
3831
+ if (active) return active;
3832
+ }
3833
+ }
3834
+ return null;
3835
+ }, [messages]);
3573
3836
  return {
3574
3837
  messages,
3575
3838
  isLoading,
@@ -3578,6 +3841,7 @@ function useAgentChat(options) {
3578
3841
  error,
3579
3842
  sessionId: currentSessionId,
3580
3843
  clarification,
3844
+ activeDelegation,
3581
3845
  sendMessage,
3582
3846
  respondToClarification,
3583
3847
  cancel,
@@ -3769,6 +4033,24 @@ function useFileAttachments(options) {
3769
4033
  console.log("[useFileAttachments] prepareForSubmit result:", aiUuids);
3770
4034
  return aiUuids;
3771
4035
  }, [onSubmitUpload, onError]);
4036
+ const addExternalFile = react.useCallback(
4037
+ (opts) => {
4038
+ const category = opts.category || (opts.mimeType.startsWith("image/") ? "image" : "document");
4039
+ const placeholderFile = new File([], opts.filename, { type: opts.mimeType });
4040
+ const newAttachment = {
4041
+ uuid: opts.uuid,
4042
+ file: placeholderFile,
4043
+ status: "ready",
4044
+ progress: 100,
4045
+ category,
4046
+ previewUrl: opts.previewUrl,
4047
+ hasThumbnail: category === "image",
4048
+ actualSize: opts.size
4049
+ };
4050
+ setFiles((prev) => [...prev, newAttachment]);
4051
+ },
4052
+ []
4053
+ );
3772
4054
  const clear = react.useCallback(() => {
3773
4055
  for (const file of filesRef.current) {
3774
4056
  if (file.previewUrl) {
@@ -3794,6 +4076,7 @@ function useFileAttachments(options) {
3794
4076
  files,
3795
4077
  dropZoneProps,
3796
4078
  removeFile,
4079
+ addExternalFile,
3797
4080
  getReadyUuids,
3798
4081
  getReadyUrls,
3799
4082
  prepareForSubmit,
@@ -4433,18 +4716,30 @@ function ChatContainer({
4433
4716
  children,
4434
4717
  maxHeight = "600px",
4435
4718
  className,
4436
- theme = "system"
4719
+ theme = "system",
4720
+ layout = "fixed"
4437
4721
  }) {
4438
4722
  const maxHeightValue = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
4723
+ const isFill = layout === "fill";
4439
4724
  return /* @__PURE__ */ jsxRuntime.jsx(
4440
4725
  "div",
4441
4726
  {
4442
4727
  className: cn2(
4443
- "flex flex-col bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
4728
+ "cadra-chat-container flex flex-col overflow-hidden",
4729
+ // Default colors use CSS custom properties with Tailwind fallbacks.
4730
+ // Consumers can override via CSS: --chat-bg, --chat-border, --chat-border-radius
4731
+ !isFill && "rounded-lg border",
4732
+ isFill && "flex-1 min-h-0",
4444
4733
  className
4445
4734
  ),
4446
- style: { maxHeight: maxHeightValue },
4735
+ style: {
4736
+ ...!isFill ? { maxHeight: maxHeightValue } : void 0,
4737
+ // CSS custom properties for theming — consumers set these to override
4738
+ backgroundColor: "var(--chat-bg, var(--background, #fff))",
4739
+ borderColor: !isFill ? "var(--chat-border, var(--border, #e5e7eb))" : void 0
4740
+ },
4447
4741
  "data-theme": theme,
4742
+ "data-layout": layout,
4448
4743
  children
4449
4744
  }
4450
4745
  );
@@ -5131,7 +5426,7 @@ var FileDropZone = react.forwardRef(
5131
5426
  {
5132
5427
  ref: dropZoneRef,
5133
5428
  "data-dropzone": "wrapper",
5134
- className: cn4("relative w-full h-full", className),
5429
+ className: cn4("relative flex flex-col flex-1 min-h-0", className),
5135
5430
  style: { minHeight: "inherit" },
5136
5431
  children: [
5137
5432
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5416,6 +5711,380 @@ function AskUserPrompt({
5416
5711
  function cn6(...classes) {
5417
5712
  return classes.filter(Boolean).join(" ");
5418
5713
  }
5714
+ function formatDuration(ms) {
5715
+ if (ms < 1e3) return `${ms}ms`;
5716
+ return `${(ms / 1e3).toFixed(1)}s`;
5717
+ }
5718
+ function DelegationProgress({
5719
+ event,
5720
+ className
5721
+ }) {
5722
+ const statusIcon = react.useMemo(() => {
5723
+ switch (event.status) {
5724
+ case "active":
5725
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-4 w-4 text-blue-500" });
5726
+ case "completed":
5727
+ return /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-500" });
5728
+ case "failed":
5729
+ return /* @__PURE__ */ jsxRuntime.jsx(AlertCircleIcon, { className: "h-4 w-4 text-red-500" });
5730
+ default:
5731
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-4 w-4 text-gray-400" });
5732
+ }
5733
+ }, [event.status]);
5734
+ const statusColor = react.useMemo(() => {
5735
+ switch (event.status) {
5736
+ case "active":
5737
+ return "border-blue-200 dark:border-blue-800 bg-blue-50/50 dark:bg-blue-950/20";
5738
+ case "completed":
5739
+ return "border-green-200 dark:border-green-800 bg-green-50/50 dark:bg-green-950/20";
5740
+ case "failed":
5741
+ return "border-red-200 dark:border-red-800 bg-red-50/50 dark:bg-red-950/20";
5742
+ default:
5743
+ return "border-gray-200 dark:border-gray-700";
5744
+ }
5745
+ }, [event.status]);
5746
+ return /* @__PURE__ */ jsxRuntime.jsx(
5747
+ "div",
5748
+ {
5749
+ className: cn6(
5750
+ "rounded-lg border text-sm px-3 py-2",
5751
+ statusColor,
5752
+ className
5753
+ ),
5754
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5755
+ statusIcon,
5756
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
5757
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
5758
+ event.fromAgent && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5759
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-700 dark:text-gray-300 truncate", children: event.fromAgent }),
5760
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500 flex-shrink-0", children: "\u2192" })
5761
+ ] }),
5762
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100 truncate", children: event.toAgent || event.targetAgentUuid || "Specialist" })
5763
+ ] }),
5764
+ event.taskPreview && event.status === "active" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5", children: event.taskPreview }),
5765
+ event.resultPreview && event.status === "completed" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-green-600 dark:text-green-400 truncate mt-0.5", children: event.resultPreview })
5766
+ ] }),
5767
+ event.durationMs !== void 0 && event.status !== "active" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400 flex-shrink-0", children: formatDuration(event.durationMs) })
5768
+ ] })
5769
+ }
5770
+ );
5771
+ }
5772
+ function DelegationProgressList({
5773
+ events,
5774
+ className
5775
+ }) {
5776
+ if (!events || events.length === 0) return null;
5777
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn6("space-y-2", className), children: events.map((event, index) => /* @__PURE__ */ jsxRuntime.jsx(
5778
+ DelegationProgress,
5779
+ {
5780
+ event
5781
+ },
5782
+ event.handoffId || `delegation-${index}`
5783
+ )) });
5784
+ }
5785
+ function ImageLightbox({
5786
+ images,
5787
+ currentIndex,
5788
+ onClose,
5789
+ onPrev,
5790
+ onNext,
5791
+ className
5792
+ }) {
5793
+ const currentImage = images[currentIndex];
5794
+ const hasMultiple = images.length > 1;
5795
+ const [container, setContainer] = react.useState(null);
5796
+ react.useEffect(() => {
5797
+ const el = document.createElement("div");
5798
+ el.id = "cadra-lightbox-root";
5799
+ el.style.position = "fixed";
5800
+ el.style.inset = "0";
5801
+ el.style.zIndex = "999999";
5802
+ el.style.pointerEvents = "auto";
5803
+ document.body.appendChild(el);
5804
+ setContainer(el);
5805
+ return () => {
5806
+ document.body.removeChild(el);
5807
+ };
5808
+ }, []);
5809
+ const onCloseRef = react.useRef(onClose);
5810
+ const onPrevRef = react.useRef(onPrev);
5811
+ const onNextRef = react.useRef(onNext);
5812
+ onCloseRef.current = onClose;
5813
+ onPrevRef.current = onPrev;
5814
+ onNextRef.current = onNext;
5815
+ react.useEffect(() => {
5816
+ function handleKeyDown(e) {
5817
+ switch (e.key) {
5818
+ case "Escape":
5819
+ e.preventDefault();
5820
+ e.stopPropagation();
5821
+ onCloseRef.current();
5822
+ break;
5823
+ case "ArrowLeft":
5824
+ if (hasMultiple) onPrevRef.current();
5825
+ break;
5826
+ case "ArrowRight":
5827
+ if (hasMultiple) onNextRef.current();
5828
+ break;
5829
+ }
5830
+ }
5831
+ document.addEventListener("keydown", handleKeyDown, true);
5832
+ return () => document.removeEventListener("keydown", handleKeyDown, true);
5833
+ }, [hasMultiple]);
5834
+ react.useEffect(() => {
5835
+ const originalOverflow = document.body.style.overflow;
5836
+ document.body.style.overflow = "hidden";
5837
+ return () => {
5838
+ document.body.style.overflow = originalOverflow;
5839
+ };
5840
+ }, []);
5841
+ react.useEffect(() => {
5842
+ if (!container) return;
5843
+ function handleClick(e) {
5844
+ const target = e.target;
5845
+ if (target.dataset.lightboxBackdrop === "true") {
5846
+ e.preventDefault();
5847
+ e.stopPropagation();
5848
+ onCloseRef.current();
5849
+ }
5850
+ }
5851
+ container.addEventListener("click", handleClick, true);
5852
+ return () => container.removeEventListener("click", handleClick, true);
5853
+ }, [container]);
5854
+ const handleDownload = react.useCallback(async () => {
5855
+ if (!currentImage) return;
5856
+ const filename = currentImage.filename || `image-${currentIndex + 1}`;
5857
+ try {
5858
+ const response = await fetch(currentImage.url);
5859
+ const blob = await response.blob();
5860
+ const blobUrl = URL.createObjectURL(blob);
5861
+ const link = document.createElement("a");
5862
+ link.href = blobUrl;
5863
+ link.download = filename;
5864
+ document.body.appendChild(link);
5865
+ link.click();
5866
+ document.body.removeChild(link);
5867
+ URL.revokeObjectURL(blobUrl);
5868
+ } catch {
5869
+ window.open(currentImage.url, "_blank");
5870
+ }
5871
+ }, [currentImage, currentIndex]);
5872
+ const handleClose = react.useCallback(() => {
5873
+ onCloseRef.current();
5874
+ }, []);
5875
+ if (!currentImage || !container) return null;
5876
+ return reactDom.createPortal(
5877
+ /* @__PURE__ */ jsxRuntime.jsxs(
5878
+ "div",
5879
+ {
5880
+ "data-lightbox-backdrop": "true",
5881
+ className: `absolute inset-0 flex items-center justify-center bg-black/90 ${className || ""}`,
5882
+ style: { cursor: "zoom-out" },
5883
+ children: [
5884
+ /* @__PURE__ */ jsxRuntime.jsxs(
5885
+ "div",
5886
+ {
5887
+ className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4",
5888
+ style: { zIndex: 10 },
5889
+ children: [
5890
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5891
+ currentIndex + 1,
5892
+ " / ",
5893
+ images.length
5894
+ ] }) }),
5895
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5896
+ /* @__PURE__ */ jsxRuntime.jsx(
5897
+ "button",
5898
+ {
5899
+ type: "button",
5900
+ onClick: (e) => {
5901
+ e.stopPropagation();
5902
+ handleDownload();
5903
+ },
5904
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5905
+ style: { cursor: "pointer", pointerEvents: "auto" },
5906
+ "aria-label": "Download image",
5907
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" })
5908
+ }
5909
+ ),
5910
+ /* @__PURE__ */ jsxRuntime.jsx(
5911
+ "button",
5912
+ {
5913
+ type: "button",
5914
+ onClick: (e) => {
5915
+ e.stopPropagation();
5916
+ handleClose();
5917
+ },
5918
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5919
+ style: { cursor: "pointer", pointerEvents: "auto" },
5920
+ "aria-label": "Close lightbox",
5921
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
5922
+ }
5923
+ )
5924
+ ] })
5925
+ ]
5926
+ }
5927
+ ),
5928
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5929
+ "button",
5930
+ {
5931
+ type: "button",
5932
+ onClick: (e) => {
5933
+ e.stopPropagation();
5934
+ onPrev();
5935
+ },
5936
+ className: "absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5937
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5938
+ "aria-label": "Previous image",
5939
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5940
+ }
5941
+ ),
5942
+ /* @__PURE__ */ jsxRuntime.jsx(
5943
+ "img",
5944
+ {
5945
+ src: currentImage.url,
5946
+ alt: currentImage.alt || `Image ${currentIndex + 1}`,
5947
+ className: "max-w-[90vw] max-h-[85vh] object-contain select-none",
5948
+ style: { cursor: "default", pointerEvents: "auto" },
5949
+ draggable: false
5950
+ }
5951
+ ),
5952
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5953
+ "button",
5954
+ {
5955
+ type: "button",
5956
+ onClick: (e) => {
5957
+ e.stopPropagation();
5958
+ onNext();
5959
+ },
5960
+ className: "absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5961
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5962
+ "aria-label": "Next image",
5963
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-6 w-6" })
5964
+ }
5965
+ ),
5966
+ currentImage.alt && /* @__PURE__ */ jsxRuntime.jsx(
5967
+ "div",
5968
+ {
5969
+ className: "absolute bottom-0 left-0 right-0 p-4 text-center",
5970
+ style: { zIndex: 10, pointerEvents: "none" },
5971
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt })
5972
+ }
5973
+ )
5974
+ ]
5975
+ }
5976
+ ),
5977
+ container
5978
+ );
5979
+ }
5980
+ function cn7(...classes) {
5981
+ return classes.filter(Boolean).join(" ");
5982
+ }
5983
+ function ImageWithLoading({ src, alt, className, onClick }) {
5984
+ const [isLoaded, setIsLoaded] = react.useState(false);
5985
+ const [hasError, setHasError] = react.useState(false);
5986
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5987
+ "div",
5988
+ {
5989
+ className: cn7(
5990
+ "relative overflow-hidden rounded-lg",
5991
+ className
5992
+ ),
5993
+ children: [
5994
+ !isLoaded && !hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700 rounded-lg" }),
5995
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm rounded-lg bg-gray-100 dark:bg-gray-800", children: "Failed to load" }),
5996
+ /* @__PURE__ */ jsxRuntime.jsx(
5997
+ "img",
5998
+ {
5999
+ src,
6000
+ alt,
6001
+ loading: "lazy",
6002
+ className: cn7(
6003
+ "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm transition-opacity duration-200",
6004
+ isLoaded ? "opacity-100" : "opacity-0"
6005
+ ),
6006
+ onLoad: () => setIsLoaded(true),
6007
+ onError: () => setHasError(true),
6008
+ onClick,
6009
+ style: onClick ? { cursor: "pointer" } : void 0
6010
+ }
6011
+ )
6012
+ ]
6013
+ }
6014
+ );
6015
+ }
6016
+ function ImageGallery({
6017
+ images,
6018
+ onImageClick,
6019
+ className
6020
+ }) {
6021
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
6022
+ const handleImageClick = react.useCallback(
6023
+ (image, index) => {
6024
+ if (onImageClick) {
6025
+ onImageClick(image);
6026
+ } else {
6027
+ setLightboxIndex(index);
6028
+ }
6029
+ },
6030
+ [onImageClick]
6031
+ );
6032
+ const handleLightboxClose = react.useCallback(() => {
6033
+ setLightboxIndex(null);
6034
+ }, []);
6035
+ const handleLightboxPrev = react.useCallback(() => {
6036
+ setLightboxIndex(
6037
+ (prev) => prev !== null ? (prev - 1 + images.length) % images.length : null
6038
+ );
6039
+ }, [images.length]);
6040
+ const handleLightboxNext = react.useCallback(() => {
6041
+ setLightboxIndex(
6042
+ (prev) => prev !== null ? (prev + 1) % images.length : null
6043
+ );
6044
+ }, [images.length]);
6045
+ if (!images || images.length === 0) return null;
6046
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6047
+ /* @__PURE__ */ jsxRuntime.jsx(
6048
+ "div",
6049
+ {
6050
+ className: cn7(
6051
+ "grid gap-2",
6052
+ images.length === 1 ? "grid-cols-1" : images.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3",
6053
+ className
6054
+ ),
6055
+ children: images.map((image, index) => /* @__PURE__ */ jsxRuntime.jsx(
6056
+ "button",
6057
+ {
6058
+ type: "button",
6059
+ onClick: () => handleImageClick(image, index),
6060
+ className: "block rounded-lg overflow-hidden transition-transform hover:scale-[1.02] text-left",
6061
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6062
+ ImageWithLoading,
6063
+ {
6064
+ src: image.url,
6065
+ alt: image.alt || `Image ${index + 1}`
6066
+ }
6067
+ )
6068
+ },
6069
+ `${image.url}-${index}`
6070
+ ))
6071
+ }
6072
+ ),
6073
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
6074
+ ImageLightbox,
6075
+ {
6076
+ images,
6077
+ currentIndex: lightboxIndex,
6078
+ onClose: handleLightboxClose,
6079
+ onPrev: handleLightboxPrev,
6080
+ onNext: handleLightboxNext
6081
+ }
6082
+ )
6083
+ ] });
6084
+ }
6085
+ function cn8(...classes) {
6086
+ return classes.filter(Boolean).join(" ");
6087
+ }
5419
6088
  function MarkdownContent({
5420
6089
  content,
5421
6090
  className,
@@ -5435,11 +6104,11 @@ function MarkdownContent({
5435
6104
  h3: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mt-3 mb-1 first:mt-0", children }),
5436
6105
  h4: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-semibold mt-2 mb-1 first:mt-0", children }),
5437
6106
  // Paragraphs
5438
- p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 last:mb-0 leading-relaxed", children }),
6107
+ p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1.5 last:mb-0", children }),
5439
6108
  // Lists
5440
- ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside mb-2 space-y-1", children }),
5441
- ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside mb-2 space-y-1", children }),
5442
- li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "leading-relaxed", children }),
6109
+ ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside mb-1.5 last:mb-0", children }),
6110
+ ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside mb-1.5 last:mb-0", children }),
6111
+ li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "", children }),
5443
6112
  // Links
5444
6113
  a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx(
5445
6114
  "a",
@@ -5474,7 +6143,7 @@ function MarkdownContent({
5474
6143
  if (isCodeBlock) {
5475
6144
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative my-2", children: [
5476
6145
  language && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 right-0 px-2 py-1 text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-bl rounded-tr border-b border-l border-gray-200 dark:border-gray-700", children: language }),
5477
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn6("font-mono", codeClassName), ...props, children }) })
6146
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn8("font-mono", codeClassName), ...props, children }) })
5478
6147
  ] });
5479
6148
  }
5480
6149
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -5521,7 +6190,7 @@ function MarkdownContent({
5521
6190
  () => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
5522
6191
  [defaultComponents, customComponents]
5523
6192
  );
5524
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn6("prose prose-sm dark:prose-invert max-w-none", className), children: /* @__PURE__ */ jsxRuntime.jsx(
6193
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn8("max-w-none text-sm leading-relaxed", className), children: /* @__PURE__ */ jsxRuntime.jsx(
5525
6194
  ReactMarkdown__default.default,
5526
6195
  {
5527
6196
  remarkPlugins: [remarkGfm__default.default],
@@ -5531,6 +6200,58 @@ function MarkdownContent({
5531
6200
  }
5532
6201
  ) });
5533
6202
  }
6203
+ function cn9(...classes) {
6204
+ return classes.filter(Boolean).join(" ");
6205
+ }
6206
+ function ThinkingBlock({
6207
+ block,
6208
+ defaultExpanded = false,
6209
+ className
6210
+ }) {
6211
+ const [isExpanded, setIsExpanded] = react.useState(defaultExpanded);
6212
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6213
+ "div",
6214
+ {
6215
+ className: cn9(
6216
+ "rounded-lg border",
6217
+ "border-purple-200 dark:border-purple-800",
6218
+ "bg-purple-50/50 dark:bg-purple-950/20",
6219
+ className
6220
+ ),
6221
+ children: [
6222
+ /* @__PURE__ */ jsxRuntime.jsxs(
6223
+ "button",
6224
+ {
6225
+ type: "button",
6226
+ onClick: () => setIsExpanded(!isExpanded),
6227
+ className: "w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-purple-100/50 dark:hover:bg-purple-900/30 transition-colors rounded-lg",
6228
+ children: [
6229
+ isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "h-4 w-4 text-purple-400 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-4 w-4 text-purple-400 flex-shrink-0" }),
6230
+ /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "h-4 w-4 text-purple-500 flex-shrink-0" }),
6231
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-purple-700 dark:text-purple-300", children: "Thinking" })
6232
+ ]
6233
+ }
6234
+ ),
6235
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-3 border-t border-purple-200 dark:border-purple-800", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-purple-800 dark:text-purple-200 whitespace-pre-wrap font-mono leading-relaxed", children: block.content }) })
6236
+ ]
6237
+ }
6238
+ );
6239
+ }
6240
+ function ThinkingBlockList({
6241
+ blocks,
6242
+ defaultExpanded = false,
6243
+ className
6244
+ }) {
6245
+ if (!blocks || blocks.length === 0) return null;
6246
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn9("space-y-2", className), children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
6247
+ ThinkingBlock,
6248
+ {
6249
+ block,
6250
+ defaultExpanded
6251
+ },
6252
+ block.id
6253
+ )) });
6254
+ }
5534
6255
  var INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set([
5535
6256
  "update_task_list",
5536
6257
  "create_task",
@@ -5547,7 +6268,8 @@ var INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set([
5547
6268
  "check_task_status",
5548
6269
  "assign_task",
5549
6270
  "update_plan",
5550
- "check_delegations"
6271
+ "check_delegations",
6272
+ "activate_skill"
5551
6273
  ]);
5552
6274
  function isInternalTool(toolName) {
5553
6275
  if (!toolName) return false;
@@ -5561,11 +6283,20 @@ function isInternalTool(toolName) {
5561
6283
  }
5562
6284
  return false;
5563
6285
  }
5564
- function cn7(...classes) {
6286
+ function cn10(...classes) {
5565
6287
  return classes.filter(Boolean).join(" ");
5566
6288
  }
5567
6289
  function formatToolName(toolName) {
5568
6290
  if (!toolName) return toolName;
6291
+ if (toolName.includes(".")) {
6292
+ const segments = toolName.split(".");
6293
+ const uniqueSegments = segments.filter(
6294
+ (seg, idx) => idx === 0 || seg.toLowerCase().trim() !== segments[idx - 1]?.toLowerCase().trim()
6295
+ );
6296
+ const bestSegment = uniqueSegments[uniqueSegments.length - 1]?.trim() || toolName;
6297
+ if (bestSegment.includes(" ")) return bestSegment;
6298
+ return formatToolName(bestSegment);
6299
+ }
5569
6300
  const prefixesToRemove = [
5570
6301
  "skills_api_",
5571
6302
  "tools_api_",
@@ -5610,7 +6341,7 @@ function formatToolName(toolName) {
5610
6341
  }
5611
6342
  return uniqueWords.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
5612
6343
  }
5613
- function formatDuration(ms) {
6344
+ function formatDuration2(ms) {
5614
6345
  if (ms < 1e3) {
5615
6346
  return `${ms}ms`;
5616
6347
  }
@@ -5631,7 +6362,7 @@ function formatJsonPreview(data, maxLength = 100) {
5631
6362
  return String(data);
5632
6363
  }
5633
6364
  }
5634
- function extractImagesFromResult(result) {
6365
+ function extractImagesFromResult2(result) {
5635
6366
  if (!result) return [];
5636
6367
  let data = result;
5637
6368
  if (typeof result === "string") {
@@ -5670,14 +6401,23 @@ function ToolCallEntry({
5670
6401
  compact = false
5671
6402
  }) {
5672
6403
  const [isExpanded, setIsExpanded] = react.useState(false);
6404
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
5673
6405
  const formattedName = react.useMemo(
5674
6406
  () => formatToolName(toolCall.name),
5675
6407
  [toolCall.name]
5676
6408
  );
5677
6409
  const resultImages = react.useMemo(
5678
- () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult(toolCall.result) : [],
6410
+ () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult2(toolCall.result) : [],
5679
6411
  [toolCall.status, toolCall.result]
5680
6412
  );
6413
+ const lightboxImages = react.useMemo(
6414
+ () => resultImages.map((img) => ({
6415
+ url: img.url,
6416
+ alt: img.alt || img.prompt,
6417
+ filename: img.alt || img.prompt
6418
+ })),
6419
+ [resultImages]
6420
+ );
5681
6421
  const hasImages = resultImages.length > 0;
5682
6422
  const statusIcon = react.useMemo(() => {
5683
6423
  switch (toolCall.status) {
@@ -5711,14 +6451,14 @@ function ToolCallEntry({
5711
6451
  return /* @__PURE__ */ jsxRuntime.jsxs(
5712
6452
  "div",
5713
6453
  {
5714
- className: cn7(
6454
+ className: cn10(
5715
6455
  "inline-flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400",
5716
6456
  className
5717
6457
  ),
5718
6458
  children: [
5719
6459
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: statusIcon }),
5720
6460
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedName }),
5721
- toolCall.durationMs !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500", children: formatDuration(toolCall.durationMs) })
6461
+ toolCall.durationMs !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500", children: formatDuration2(toolCall.durationMs) })
5722
6462
  ]
5723
6463
  }
5724
6464
  );
@@ -5726,7 +6466,7 @@ function ToolCallEntry({
5726
6466
  return /* @__PURE__ */ jsxRuntime.jsxs(
5727
6467
  "div",
5728
6468
  {
5729
- className: cn7(
6469
+ className: cn10(
5730
6470
  "rounded-lg border text-sm",
5731
6471
  statusColor,
5732
6472
  className
@@ -5742,52 +6482,61 @@ function ToolCallEntry({
5742
6482
  isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "h-4 w-4 text-gray-400 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-4 w-4 text-gray-400 flex-shrink-0" }),
5743
6483
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: statusIcon }),
5744
6484
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100 truncate", children: formattedName }),
5745
- toolCall.status === "completed" && toolCall.durationMs !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-gray-500 dark:text-gray-400 flex-shrink-0", children: formatDuration(toolCall.durationMs) }),
6485
+ toolCall.status === "completed" && toolCall.durationMs !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-gray-500 dark:text-gray-400 flex-shrink-0", children: formatDuration2(toolCall.durationMs) }),
5746
6486
  toolCall.status === "failed" && toolCall.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-red-600 dark:text-red-400 truncate max-w-[150px]", children: toolCall.error })
5747
6487
  ]
5748
6488
  }
5749
6489
  ),
5750
- hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn7(
5751
- "grid gap-2",
5752
- resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
5753
- ), children: resultImages.map((img, idx) => {
5754
- const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
5755
- const imageElement = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5756
- /* @__PURE__ */ jsxRuntime.jsx(
5757
- "img",
5758
- {
5759
- src: img.thumbnailUrl || img.url,
5760
- alt: altText,
5761
- className: "w-full h-auto max-h-[300px] object-contain bg-gray-50 dark:bg-gray-900",
5762
- loading: "lazy"
5763
- }
5764
- ),
5765
- img.prompt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1 text-xs text-gray-500 dark:text-gray-400 truncate bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700", children: img.prompt })
5766
- ] });
5767
- if (onImageClick) {
6490
+ hasImages && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 pb-3", children: [
6491
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn10(
6492
+ "grid gap-3",
6493
+ resultImages.length === 1 ? "grid-cols-1 max-w-sm" : resultImages.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6494
+ ), children: resultImages.map((img, idx) => {
6495
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6496
+ const imageElement = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative", children: [
6497
+ /* @__PURE__ */ jsxRuntime.jsx(
6498
+ "img",
6499
+ {
6500
+ src: img.thumbnailUrl || img.url,
6501
+ alt: altText,
6502
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6503
+ loading: "lazy"
6504
+ }
6505
+ ),
6506
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6507
+ ] });
5768
6508
  return /* @__PURE__ */ jsxRuntime.jsx(
5769
6509
  "button",
5770
6510
  {
5771
6511
  type: "button",
5772
- onClick: () => onImageClick({ url: img.url, alt: altText, filename: altText }),
5773
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors cursor-pointer text-left",
6512
+ onClick: () => {
6513
+ if (onImageClick) {
6514
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6515
+ } else {
6516
+ setLightboxIndex(idx);
6517
+ }
6518
+ },
6519
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02]",
5774
6520
  children: imageElement
5775
6521
  },
5776
6522
  idx
5777
6523
  );
5778
- }
5779
- return /* @__PURE__ */ jsxRuntime.jsx(
5780
- "a",
6524
+ }) }),
6525
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
6526
+ ImageLightbox,
5781
6527
  {
5782
- href: img.url,
5783
- target: "_blank",
5784
- rel: "noopener noreferrer",
5785
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors",
5786
- children: imageElement
5787
- },
5788
- idx
5789
- );
5790
- }) }) }),
6528
+ images: lightboxImages,
6529
+ currentIndex: lightboxIndex,
6530
+ onClose: () => setLightboxIndex(null),
6531
+ onPrev: () => setLightboxIndex(
6532
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6533
+ ),
6534
+ onNext: () => setLightboxIndex(
6535
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6536
+ )
6537
+ }
6538
+ )
6539
+ ] }),
5791
6540
  isExpanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 px-3 py-2 space-y-2", children: [
5792
6541
  toolCall.args && Object.keys(toolCall.args).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5793
6542
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 mb-1", children: "Input" }),
@@ -5806,22 +6555,185 @@ function ToolCallEntry({
5806
6555
  }
5807
6556
  );
5808
6557
  }
5809
- function cn8(...classes) {
6558
+ function cn11(...classes) {
5810
6559
  return classes.filter(Boolean).join(" ");
5811
6560
  }
6561
+ function normalizeToolNameForGrouping(name) {
6562
+ const lastSegment = name.includes(".") ? name.split(".").pop() : name;
6563
+ return lastSegment.toLowerCase().replace(/[^a-z0-9]/g, "");
6564
+ }
6565
+ function groupToolCalls(toolCalls) {
6566
+ const byNormalizedName = /* @__PURE__ */ new Map();
6567
+ const nameOrder = [];
6568
+ for (const tc of toolCalls) {
6569
+ const key = normalizeToolNameForGrouping(tc.name);
6570
+ if (!byNormalizedName.has(key)) {
6571
+ byNormalizedName.set(key, []);
6572
+ nameOrder.push(key);
6573
+ }
6574
+ byNormalizedName.get(key).push(tc);
6575
+ }
6576
+ const groups = [];
6577
+ for (const key of nameOrder) {
6578
+ const calls = byNormalizedName.get(key);
6579
+ if (calls.length === 1) {
6580
+ groups.push({ type: "single", toolCall: calls[0] });
6581
+ } else {
6582
+ groups.push({ type: "group", name: calls[0].name, toolCalls: calls });
6583
+ }
6584
+ }
6585
+ return groups;
6586
+ }
6587
+ function ToolCallList({ toolCalls, toolCallVerbosity, onImageClick, renderToolCall }) {
6588
+ const visibleToolCalls = react.useMemo(() => {
6589
+ if (toolCallVerbosity !== "compact") return toolCalls;
6590
+ return toolCalls.filter((tc) => !isInternalTool(tc.name));
6591
+ }, [toolCalls, toolCallVerbosity]);
6592
+ const grouped = react.useMemo(() => groupToolCalls(visibleToolCalls), [visibleToolCalls]);
6593
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: grouped.map((group) => {
6594
+ if (group.type === "single") {
6595
+ const toolCall = group.toolCall;
6596
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
6597
+ ToolCallEntry,
6598
+ {
6599
+ toolCall,
6600
+ onImageClick
6601
+ },
6602
+ toolCall.id
6603
+ );
6604
+ if (renderToolCall) {
6605
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6606
+ }
6607
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: defaultRender() }, toolCall.id);
6608
+ }
6609
+ const completed = group.toolCalls.filter((tc) => tc.status === "completed" || tc.status === "failed");
6610
+ const running = group.toolCalls.filter((tc) => tc.status !== "completed" && tc.status !== "failed");
6611
+ const allImages = completed.flatMap(
6612
+ (tc) => tc.result ? extractImagesFromResult2(tc.result) : []
6613
+ );
6614
+ const hasImages = allImages.length > 0;
6615
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
6616
+ running.length > 0 && running[0] && /* @__PURE__ */ jsxRuntime.jsx(
6617
+ ToolCallEntry,
6618
+ {
6619
+ toolCall: {
6620
+ ...running[0],
6621
+ name: running.length > 1 ? `${running[0].name} (${running.length})` : running[0].name
6622
+ },
6623
+ onImageClick
6624
+ },
6625
+ running[0].id
6626
+ ),
6627
+ completed.length > 0 && hasImages && /* @__PURE__ */ jsxRuntime.jsx(
6628
+ GroupedImageGallery,
6629
+ {
6630
+ images: allImages,
6631
+ completedCount: completed.length,
6632
+ onImageClick
6633
+ }
6634
+ ),
6635
+ completed.length > 0 && !hasImages && completed.map((tc) => {
6636
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(ToolCallEntry, { toolCall: tc, onImageClick }, tc.id);
6637
+ if (renderToolCall) {
6638
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(tc, defaultRender) }, tc.id);
6639
+ }
6640
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: defaultRender() }, tc.id);
6641
+ })
6642
+ ] }, `group-${group.name}-${group.toolCalls[0]?.id}`);
6643
+ }) });
6644
+ }
6645
+ function GroupedImageGallery({
6646
+ images,
6647
+ completedCount,
6648
+ onImageClick
6649
+ }) {
6650
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
6651
+ const lightboxImages = react.useMemo(
6652
+ () => images.map((img) => ({
6653
+ url: img.url,
6654
+ alt: img.alt || img.prompt,
6655
+ filename: img.alt || img.prompt
6656
+ })),
6657
+ [images]
6658
+ );
6659
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
6660
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 px-1", children: [
6661
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-4 w-4 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4 text-green-500", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }) }),
6662
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6663
+ completedCount,
6664
+ " image",
6665
+ completedCount !== 1 ? "s" : "",
6666
+ " generated"
6667
+ ] })
6668
+ ] }),
6669
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn11(
6670
+ "grid gap-3",
6671
+ images.length === 1 ? "grid-cols-1 max-w-sm" : images.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6672
+ ), children: images.map((img, idx) => {
6673
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6674
+ return /* @__PURE__ */ jsxRuntime.jsx(
6675
+ "button",
6676
+ {
6677
+ type: "button",
6678
+ onClick: () => {
6679
+ if (onImageClick) {
6680
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6681
+ } else {
6682
+ setLightboxIndex(idx);
6683
+ }
6684
+ },
6685
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02] group",
6686
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6687
+ /* @__PURE__ */ jsxRuntime.jsx(
6688
+ "img",
6689
+ {
6690
+ src: img.thumbnailUrl || img.url,
6691
+ alt: altText,
6692
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6693
+ loading: "lazy"
6694
+ }
6695
+ ),
6696
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6697
+ ] })
6698
+ },
6699
+ `${img.url}-${idx}`
6700
+ );
6701
+ }) }),
6702
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
6703
+ ImageLightbox,
6704
+ {
6705
+ images: lightboxImages,
6706
+ currentIndex: lightboxIndex,
6707
+ onClose: () => setLightboxIndex(null),
6708
+ onPrev: () => setLightboxIndex(
6709
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6710
+ ),
6711
+ onNext: () => setLightboxIndex(
6712
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6713
+ )
6714
+ }
6715
+ )
6716
+ ] });
6717
+ }
5812
6718
  function ChatMessage({
5813
6719
  message,
5814
6720
  enableMarkdown = true,
5815
6721
  showToolCalls = true,
5816
- toolCallVerbosity,
6722
+ toolCallVerbosity = "compact",
5817
6723
  className,
5818
6724
  markdownComponents,
5819
6725
  markdownUrlTransform,
5820
6726
  markdownContentTransform,
5821
- onImageClick
6727
+ onImageClick,
6728
+ showThinking = true,
6729
+ showDelegation = true,
6730
+ showImages = true,
6731
+ renderToolCall,
6732
+ variant = "bubble"
5822
6733
  }) {
5823
6734
  const isUser = message.role === "user";
5824
6735
  const isAssistant = message.role === "assistant";
6736
+ const isFlat = variant === "flat";
5825
6737
  const formattedTime = react.useMemo(() => {
5826
6738
  const date = message.timestamp instanceof Date ? message.timestamp : new Date(message.timestamp);
5827
6739
  return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
@@ -5829,113 +6741,119 @@ function ChatMessage({
5829
6741
  return /* @__PURE__ */ jsxRuntime.jsxs(
5830
6742
  "div",
5831
6743
  {
5832
- className: cn8(
5833
- "flex gap-3 p-4",
5834
- isUser ? "flex-row-reverse" : "flex-row",
6744
+ className: cn11(
6745
+ "cadra-chat-message flex gap-3",
6746
+ isFlat ? "py-5 px-4" : "p-4",
6747
+ // Bubble: user messages right-aligned. Flat: always left-aligned.
6748
+ !isFlat && isUser ? "flex-row-reverse" : "flex-row",
6749
+ // Flat variant: subtle row background for user messages
6750
+ isFlat && isUser && "chat-message-user-row",
5835
6751
  className
5836
6752
  ),
6753
+ style: isFlat && isUser ? {
6754
+ backgroundColor: "var(--chat-user-row-bg, rgba(128, 128, 128, 0.08))"
6755
+ } : void 0,
6756
+ "data-role": message.role,
5837
6757
  children: [
5838
6758
  /* @__PURE__ */ jsxRuntime.jsx(
5839
6759
  "div",
5840
6760
  {
5841
- className: cn8(
5842
- "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
5843
- isUser ? "bg-blue-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
5844
- ),
6761
+ className: "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-sm",
6762
+ style: {
6763
+ backgroundColor: isUser ? "var(--chat-user-avatar-bg, #3b82f6)" : "var(--chat-assistant-avatar-bg, #e5e7eb)",
6764
+ color: isUser ? "var(--chat-user-avatar-text, #fff)" : "var(--chat-assistant-avatar-text, #4b5563)"
6765
+ },
5845
6766
  children: isUser ? /* @__PURE__ */ jsxRuntime.jsx(UserIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "h-4 w-4" })
5846
6767
  }
5847
6768
  ),
5848
6769
  /* @__PURE__ */ jsxRuntime.jsxs(
5849
6770
  "div",
5850
6771
  {
5851
- className: cn8(
5852
- "flex-1 min-w-0 space-y-2",
5853
- isUser ? "text-right" : "text-left"
6772
+ className: cn11(
6773
+ "flex-1 min-w-0",
6774
+ isFlat ? "pt-0.5" : "space-y-2",
6775
+ !isFlat && isUser ? "text-right" : "text-left"
5854
6776
  ),
5855
6777
  children: [
5856
6778
  /* @__PURE__ */ jsxRuntime.jsxs(
5857
6779
  "div",
5858
6780
  {
5859
- className: cn8(
5860
- "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400",
5861
- isUser ? "justify-end" : "justify-start"
6781
+ className: cn11(
6782
+ "flex items-center gap-2 text-xs",
6783
+ isFlat ? "mb-1.5" : "",
6784
+ !isFlat && isUser ? "justify-end" : "justify-start"
5862
6785
  ),
6786
+ style: { color: "var(--chat-meta-text, #6b7280)" },
5863
6787
  children: [
5864
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: isUser ? "You" : "Assistant" }),
6788
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--chat-label-text, inherit)" }, children: isUser ? "You" : "Assistant" }),
5865
6789
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedTime }),
5866
6790
  message.isStreaming && /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-3 w-3" })
5867
6791
  ]
5868
6792
  }
5869
6793
  ),
5870
- message.thinkingBlocks && message.thinkingBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: message.thinkingBlocks.map((block) => /* @__PURE__ */ jsxRuntime.jsxs(
5871
- "div",
6794
+ showThinking && message.thinkingBlocks && message.thinkingBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlockList, { blocks: message.thinkingBlocks }),
6795
+ showDelegation && message.delegationEvents && message.delegationEvents.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(DelegationProgressList, { events: message.delegationEvents }),
6796
+ showToolCalls && toolCallVerbosity !== "hidden" && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6797
+ ToolCallList,
5872
6798
  {
5873
- className: "bg-purple-50 dark:bg-purple-950/30 border border-purple-200 dark:border-purple-800 rounded-lg p-3 text-sm text-purple-800 dark:text-purple-200",
5874
- children: [
5875
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-purple-600 dark:text-purple-400 mb-1", children: "Thinking..." }),
5876
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: block.content })
5877
- ]
5878
- },
5879
- block.id
5880
- )) }),
5881
- showToolCalls && toolCallVerbosity !== "hidden" && message.toolCalls && message.toolCalls.length > 0 && (() => {
5882
- const verbosity = toolCallVerbosity ?? "verbose";
5883
- const filteredToolCalls = verbosity === "compact" ? message.toolCalls.filter((tc) => !isInternalTool(tc.name)) : message.toolCalls;
5884
- if (filteredToolCalls.length === 0) return null;
5885
- if (verbosity === "compact") {
5886
- const runningTools = filteredToolCalls.filter((tc) => tc.status === "pending" || tc.status === "running");
5887
- const doneTools = filteredToolCalls.filter((tc) => tc.status === "completed" || tc.status === "failed");
5888
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
5889
- doneTools.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-x-3 gap-y-1", children: doneTools.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx(
5890
- ToolCallEntry,
5891
- {
5892
- toolCall,
5893
- onImageClick,
5894
- compact: true
5895
- },
5896
- toolCall.id
5897
- )) }),
5898
- runningTools.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx(
5899
- ToolCallEntry,
5900
- {
5901
- toolCall,
5902
- onImageClick
5903
- },
5904
- toolCall.id
5905
- ))
5906
- ] });
6799
+ toolCalls: message.toolCalls,
6800
+ toolCallVerbosity,
6801
+ onImageClick,
6802
+ renderToolCall
5907
6803
  }
5908
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filteredToolCalls.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx(
5909
- ToolCallEntry,
5910
- {
5911
- toolCall,
5912
- onImageClick
5913
- },
5914
- toolCall.id
5915
- )) });
5916
- })(),
5917
- message.content && /* @__PURE__ */ jsxRuntime.jsx(
5918
- "div",
6804
+ ),
6805
+ showImages && !showToolCalls && message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6806
+ ImageGallery,
5919
6807
  {
5920
- className: cn8(
5921
- "rounded-lg p-3",
5922
- isUser ? "bg-blue-500 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"
5923
- ),
5924
- children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
5925
- MarkdownContent,
5926
- {
5927
- content: message.content,
5928
- components: markdownComponents,
5929
- urlTransform: markdownUrlTransform,
5930
- contentTransform: markdownContentTransform
5931
- }
5932
- ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: message.content })
6808
+ images: message.images,
6809
+ onImageClick: onImageClick ? (img) => onImageClick({ url: img.url, alt: img.alt, filename: img.filename }) : void 0
5933
6810
  }
5934
6811
  ),
6812
+ message.content && (isFlat ? (
6813
+ // Flat variant: no bubble, just text with theme-aware color
6814
+ /* @__PURE__ */ jsxRuntime.jsx(
6815
+ "div",
6816
+ {
6817
+ className: cn11("text-sm break-words", !(isAssistant && enableMarkdown) && "whitespace-pre-wrap"),
6818
+ style: { color: "var(--chat-content-text, inherit)" },
6819
+ children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
6820
+ MarkdownContent,
6821
+ {
6822
+ content: message.content,
6823
+ components: markdownComponents,
6824
+ urlTransform: markdownUrlTransform,
6825
+ contentTransform: markdownContentTransform
6826
+ }
6827
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: message.content })
6828
+ }
6829
+ )
6830
+ ) : (
6831
+ // Bubble variant: colored bubble backgrounds
6832
+ /* @__PURE__ */ jsxRuntime.jsx(
6833
+ "div",
6834
+ {
6835
+ className: "rounded-lg p-3",
6836
+ style: {
6837
+ backgroundColor: isUser ? "var(--chat-user-bg, #3b82f6)" : "var(--chat-assistant-bg, #f3f4f6)",
6838
+ color: isUser ? "var(--chat-user-text, #fff)" : "var(--chat-assistant-text, #111827)"
6839
+ },
6840
+ children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
6841
+ MarkdownContent,
6842
+ {
6843
+ content: message.content,
6844
+ components: markdownComponents,
6845
+ urlTransform: markdownUrlTransform,
6846
+ contentTransform: markdownContentTransform
6847
+ }
6848
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: message.content })
6849
+ }
6850
+ )
6851
+ )),
5935
6852
  message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: message.attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
5936
6853
  "div",
5937
6854
  {
5938
- className: "flex items-center gap-2 bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 text-sm",
6855
+ className: "flex items-center gap-2 rounded-lg px-3 py-2 text-sm",
6856
+ style: { backgroundColor: "var(--chat-assistant-bg, #f3f4f6)" },
5939
6857
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[150px]", children: attachment.filename })
5940
6858
  },
5941
6859
  attachment.uuid
@@ -5947,7 +6865,7 @@ function ChatMessage({
5947
6865
  }
5948
6866
  );
5949
6867
  }
5950
- function cn9(...classes) {
6868
+ function cn12(...classes) {
5951
6869
  return classes.filter(Boolean).join(" ");
5952
6870
  }
5953
6871
  function MessageList({
@@ -5965,6 +6883,12 @@ function MessageList({
5965
6883
  markdownUrlTransform,
5966
6884
  markdownContentTransform,
5967
6885
  onImageClick,
6886
+ showThinking = true,
6887
+ showDelegation = true,
6888
+ showImages = true,
6889
+ renderMessage,
6890
+ renderToolCall,
6891
+ messageVariant = "bubble",
5968
6892
  sendMessage
5969
6893
  }) {
5970
6894
  const scrollRef = react.useRef(null);
@@ -5978,7 +6902,7 @@ function MessageList({
5978
6902
  "div",
5979
6903
  {
5980
6904
  ref: scrollRef,
5981
- className: cn9(
6905
+ className: cn12(
5982
6906
  "flex-1 overflow-y-auto",
5983
6907
  className
5984
6908
  ),
@@ -5989,20 +6913,31 @@ function MessageList({
5989
6913
  ] }) }),
5990
6914
  messages.length === 0 && !isLoading && !isLoadingHistory && (renderWelcome ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: typeof renderWelcome === "function" ? renderWelcome({ sendMessage: sendMessage || (() => {
5991
6915
  }) }) : renderWelcome }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full p-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg", children: welcomeMessage }) }) })),
5992
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: messages.map((message) => /* @__PURE__ */ jsxRuntime.jsx(
5993
- ChatMessage,
5994
- {
5995
- message,
5996
- enableMarkdown,
5997
- showToolCalls,
5998
- toolCallVerbosity,
5999
- markdownComponents,
6000
- markdownUrlTransform,
6001
- markdownContentTransform,
6002
- onImageClick
6003
- },
6004
- message.id
6005
- )) }),
6916
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: messages.map((message) => {
6917
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
6918
+ ChatMessage,
6919
+ {
6920
+ message,
6921
+ enableMarkdown,
6922
+ showToolCalls,
6923
+ toolCallVerbosity,
6924
+ markdownComponents,
6925
+ markdownUrlTransform,
6926
+ markdownContentTransform,
6927
+ onImageClick,
6928
+ showThinking,
6929
+ showDelegation,
6930
+ showImages,
6931
+ renderToolCall,
6932
+ variant: messageVariant
6933
+ },
6934
+ message.id
6935
+ );
6936
+ if (renderMessage) {
6937
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderMessage(message, defaultRender) }, message.id);
6938
+ }
6939
+ return defaultRender();
6940
+ }) }),
6006
6941
  isLoading && !isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 p-4", children: [
6007
6942
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-4 w-4 text-gray-600 dark:text-gray-300" }) }),
6008
6943
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Thinking..." })
@@ -6058,6 +6993,7 @@ function AgentChat({
6058
6993
  maxHeight = "600px",
6059
6994
  className,
6060
6995
  theme = "system",
6996
+ layout = "fixed",
6061
6997
  enableAttachments = false,
6062
6998
  enableMarkdown = true,
6063
6999
  showToolCalls = true,
@@ -6065,7 +7001,21 @@ function AgentChat({
6065
7001
  markdownComponents,
6066
7002
  markdownUrlTransform,
6067
7003
  markdownContentTransform,
6068
- onImageClick
7004
+ onImageClick,
7005
+ // Extension props
7006
+ hideHeader,
7007
+ renderAboveInput,
7008
+ renderBelowMessages,
7009
+ renderMessage,
7010
+ renderToolCall,
7011
+ messageVariant = "bubble",
7012
+ showThinking = true,
7013
+ showDelegation = true,
7014
+ showImages = true,
7015
+ pollingInterval,
7016
+ pollingEnabled,
7017
+ onDelegation,
7018
+ onAttachmentsReady
6069
7019
  }) {
6070
7020
  const {
6071
7021
  messages,
@@ -6092,7 +7042,10 @@ function AgentChat({
6092
7042
  onSessionStart,
6093
7043
  onToolCall,
6094
7044
  onToolResult,
6095
- onClarification
7045
+ onClarification,
7046
+ onDelegation,
7047
+ pollingInterval,
7048
+ pollingEnabled
6096
7049
  });
6097
7050
  const effectiveVerbosity = showToolCalls === false ? "hidden" : toolCallVerbosity ?? "compact";
6098
7051
  const dropZoneRef = react.useRef(null);
@@ -6109,6 +7062,11 @@ function AgentChat({
6109
7062
  console.error("[AgentChat] Attachment error:", msg);
6110
7063
  }
6111
7064
  });
7065
+ react.useEffect(() => {
7066
+ if (enableAttachments && onAttachmentsReady) {
7067
+ onAttachmentsReady({ addExternalFile: attachments.addExternalFile });
7068
+ }
7069
+ }, [enableAttachments, onAttachmentsReady, attachments.addExternalFile]);
6112
7070
  const handleSend = react.useCallback(
6113
7071
  async (content) => {
6114
7072
  if (enableAttachments && attachments.hasReadyFiles) {
@@ -6121,51 +7079,31 @@ function AgentChat({
6121
7079
  },
6122
7080
  [enableAttachments, attachments, sendMessage]
6123
7081
  );
6124
- return /* @__PURE__ */ jsxRuntime.jsxs(ChatContainer, { maxHeight, className, theme, children: [
6125
- enableAttachments ? /* @__PURE__ */ jsxRuntime.jsx(
6126
- FileDropZone,
6127
- {
6128
- ref: dropZoneRef,
6129
- ...attachments.dropZoneProps,
6130
- disabled: isLoading || !!clarification || isLoadingHistory,
6131
- children: /* @__PURE__ */ jsxRuntime.jsx(
6132
- MessageList,
6133
- {
6134
- messages,
6135
- welcomeMessage,
6136
- renderWelcome,
6137
- isLoading,
6138
- isStreaming,
6139
- isLoadingHistory,
6140
- enableMarkdown,
6141
- showToolCalls,
6142
- toolCallVerbosity: effectiveVerbosity,
6143
- markdownComponents,
6144
- markdownUrlTransform,
6145
- markdownContentTransform,
6146
- onImageClick,
6147
- sendMessage: handleSend
6148
- }
6149
- )
6150
- }
6151
- ) : /* @__PURE__ */ jsxRuntime.jsx(
6152
- MessageList,
6153
- {
6154
- messages,
6155
- welcomeMessage,
6156
- renderWelcome,
6157
- isLoading,
6158
- isStreaming,
6159
- isLoadingHistory,
6160
- enableMarkdown,
6161
- showToolCalls,
6162
- markdownComponents,
6163
- markdownUrlTransform,
6164
- markdownContentTransform,
6165
- onImageClick,
6166
- sendMessage: handleSend
6167
- }
6168
- ),
7082
+ const messageListProps = {
7083
+ messages,
7084
+ welcomeMessage: hideHeader ? void 0 : welcomeMessage,
7085
+ renderWelcome: hideHeader ? void 0 : renderWelcome,
7086
+ isLoading,
7087
+ isStreaming,
7088
+ isLoadingHistory,
7089
+ enableMarkdown,
7090
+ showToolCalls,
7091
+ toolCallVerbosity: effectiveVerbosity,
7092
+ markdownComponents,
7093
+ markdownUrlTransform,
7094
+ markdownContentTransform,
7095
+ onImageClick,
7096
+ showThinking,
7097
+ showDelegation,
7098
+ showImages,
7099
+ renderMessage,
7100
+ renderToolCall,
7101
+ messageVariant,
7102
+ sendMessage: handleSend
7103
+ };
7104
+ const chatContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7105
+ /* @__PURE__ */ jsxRuntime.jsx(MessageList, { ...messageListProps }),
7106
+ renderBelowMessages?.(),
6169
7107
  clarification && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
6170
7108
  AskUserPrompt,
6171
7109
  {
@@ -6175,6 +7113,7 @@ function AgentChat({
6175
7113
  }
6176
7114
  ) }),
6177
7115
  error && /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, onRetry: retry, onDismiss: clearError }),
7116
+ renderAboveInput?.(),
6178
7117
  /* @__PURE__ */ jsxRuntime.jsx(
6179
7118
  ChatInput,
6180
7119
  {
@@ -6191,8 +7130,17 @@ function AgentChat({
6191
7130
  }
6192
7131
  )
6193
7132
  ] });
7133
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContainer, { maxHeight, className, theme, layout, children: enableAttachments ? /* @__PURE__ */ jsxRuntime.jsx(
7134
+ FileDropZone,
7135
+ {
7136
+ ref: dropZoneRef,
7137
+ ...attachments.dropZoneProps,
7138
+ disabled: isLoading || !!clarification || isLoadingHistory,
7139
+ children: chatContent
7140
+ }
7141
+ ) : chatContent });
6194
7142
  }
6195
- function cn10(...classes) {
7143
+ function cn13(...classes) {
6196
7144
  return classes.filter(Boolean).join(" ");
6197
7145
  }
6198
7146
  function TodoList({
@@ -6221,7 +7169,7 @@ function TodoList({
6221
7169
  return /* @__PURE__ */ jsxRuntime.jsxs(
6222
7170
  "div",
6223
7171
  {
6224
- className: cn10(
7172
+ className: cn13(
6225
7173
  "rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
6226
7174
  className
6227
7175
  ),
@@ -6229,7 +7177,7 @@ function TodoList({
6229
7177
  /* @__PURE__ */ jsxRuntime.jsxs(
6230
7178
  "div",
6231
7179
  {
6232
- className: cn10(
7180
+ className: cn13(
6233
7181
  "flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50",
6234
7182
  collapsible && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
6235
7183
  ),
@@ -6260,7 +7208,7 @@ function TodoList({
6260
7208
  !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: todos.map((todo, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
6261
7209
  "li",
6262
7210
  {
6263
- className: cn10(
7211
+ className: cn13(
6264
7212
  "px-3 py-2 flex items-start gap-2 text-sm",
6265
7213
  todo.status === "in_progress" && "bg-blue-50/50 dark:bg-blue-950/20",
6266
7214
  todo.status === "completed" && "opacity-60"
@@ -6270,7 +7218,7 @@ function TodoList({
6270
7218
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
6271
7219
  "p",
6272
7220
  {
6273
- className: cn10(
7221
+ className: cn13(
6274
7222
  "text-gray-700 dark:text-gray-300",
6275
7223
  todo.status === "completed" && "line-through"
6276
7224
  ),
@@ -6287,39 +7235,6 @@ function TodoList({
6287
7235
  );
6288
7236
  }
6289
7237
 
6290
- // src/chat/index.ts
6291
- var DEPRECATION_WARNING = `
6292
- [@cadraos/sdk/chat] DEPRECATION WARNING
6293
-
6294
- This module is deprecated and will be removed in a future version.
6295
- Please migrate to @cadraos/react for:
6296
- - Simpler setup with CadraCopilot
6297
- - Better security (header-based SSE auth)
6298
- - Scoped CSS that won't conflict with your app
6299
-
6300
- Migration steps:
6301
- 1. npm install @cadraos/react
6302
- 2. import '@cadraos/react/styles.css'
6303
- 3. Replace <AgentChat> with <CadraCopilot> or <CadraChat>
6304
-
6305
- See: https://github.com/cadraos/cadra-sdk/blob/main/packages/react/MIGRATION.md
6306
- `;
6307
- var hasWarned = false;
6308
- function showDeprecationWarning() {
6309
- if (hasWarned) return;
6310
- hasWarned = true;
6311
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
6312
- return;
6313
- }
6314
- if (typeof window === "undefined") {
6315
- return;
6316
- }
6317
- console.warn(DEPRECATION_WARNING);
6318
- }
6319
- if (typeof window !== "undefined") {
6320
- setTimeout(showDeprecationWarning, 0);
6321
- }
6322
-
6323
7238
  exports.AgentChat = AgentChat;
6324
7239
  exports.AlertCircleIcon = AlertCircleIcon;
6325
7240
  exports.AskUserPrompt = AskUserPrompt;
@@ -6338,6 +7253,8 @@ exports.ClockIcon = ClockIcon;
6338
7253
  exports.CodeIcon = CodeIcon;
6339
7254
  exports.CopyIcon = CopyIcon;
6340
7255
  exports.DatabaseIcon = DatabaseIcon;
7256
+ exports.DelegationProgress = DelegationProgress;
7257
+ exports.DelegationProgressList = DelegationProgressList;
6341
7258
  exports.DownloadIcon = DownloadIcon;
6342
7259
  exports.ExternalLinkIcon = ExternalLinkIcon;
6343
7260
  exports.FileDropZone = FileDropZone;
@@ -6345,7 +7262,9 @@ exports.FileIcon = FileIcon;
6345
7262
  exports.FileTextIcon = FileTextIcon;
6346
7263
  exports.HelpCircleIcon = HelpCircleIcon;
6347
7264
  exports.INTERNAL_TOOL_NAMES = INTERNAL_TOOL_NAMES;
7265
+ exports.ImageGallery = ImageGallery;
6348
7266
  exports.ImageIcon = ImageIcon;
7267
+ exports.ImageLightbox = ImageLightbox;
6349
7268
  exports.ImagesIcon = ImagesIcon;
6350
7269
  exports.ListTodoIcon = ListTodoIcon;
6351
7270
  exports.LoadingSpinner = LoadingSpinner;
@@ -6358,6 +7277,8 @@ exports.PaperclipIcon = PaperclipIcon;
6358
7277
  exports.SendIcon = SendIcon;
6359
7278
  exports.SparklesIcon = SparklesIcon;
6360
7279
  exports.StopCircleIcon = StopCircleIcon;
7280
+ exports.ThinkingBlock = ThinkingBlock;
7281
+ exports.ThinkingBlockList = ThinkingBlockList;
6361
7282
  exports.TodoList = TodoList;
6362
7283
  exports.ToolCallEntry = ToolCallEntry;
6363
7284
  exports.UserIcon = UserIcon;