@cadraos/sdk 0.1.11-test → 0.1.12

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.
@@ -588,9 +588,11 @@ var DEFAULT_STREAMING_CONFIG = {
588
588
  activityTimeoutMs: 3e4,
589
589
  pollingIntervalMs: 2e3
590
590
  };
591
+ var DEFAULT_POLLING_ENABLED = true;
591
592
  var ExecutionStreamControllerImpl = class {
592
593
  options;
593
594
  config;
595
+ pollingEnabled;
594
596
  sseClient = null;
595
597
  status = "connecting";
596
598
  reconnectAttempts = 0;
@@ -624,6 +626,7 @@ var ExecutionStreamControllerImpl = class {
624
626
  constructor(options) {
625
627
  this.options = options;
626
628
  this.config = { ...DEFAULT_STREAMING_CONFIG, ...options.config };
629
+ this.pollingEnabled = options.pollingEnabled ?? DEFAULT_POLLING_ENABLED;
627
630
  }
628
631
  /**
629
632
  * Get the last event ID (for resume support)
@@ -964,15 +967,61 @@ var ExecutionStreamControllerImpl = class {
964
967
  if (this.status === "closed") {
965
968
  return;
966
969
  }
970
+ await this.recoverState();
971
+ if (this.status === "closed") {
972
+ return;
973
+ }
967
974
  try {
968
975
  await this.connectSSE();
969
976
  } catch {
970
977
  await this.handleConnectionError(error);
971
978
  }
972
979
  } else {
973
- this.status = "fallback_polling";
974
- this.emit("connectionChange", { connected: false, usingFallback: true });
975
- this.startPolling();
980
+ if (this.pollingEnabled) {
981
+ this.status = "fallback_polling";
982
+ this.emit("connectionChange", { connected: false, usingFallback: true });
983
+ this.startPolling();
984
+ } else {
985
+ this.handleError({
986
+ error: "SSE connection failed and polling is disabled",
987
+ errorCode: "connection_failed"
988
+ });
989
+ }
990
+ }
991
+ }
992
+ /**
993
+ * Recover state by checking execution status.
994
+ *
995
+ * Called during reconnection to catch any events missed while SSE was down.
996
+ * If the execution completed while disconnected, emits completion immediately.
997
+ * Otherwise, emits any new logs that weren't seen via SSE.
998
+ */
999
+ async recoverState() {
1000
+ try {
1001
+ const execution = await this.options.getExecution(this.options.executionId);
1002
+ if (execution.logs) {
1003
+ for (const log of execution.logs) {
1004
+ if (!this.seenEventIds.has(log.uuid)) {
1005
+ this.seenEventIds.add(log.uuid);
1006
+ this.emit("log", log);
1007
+ }
1008
+ }
1009
+ }
1010
+ if (execution.status === "completed") {
1011
+ this.stopPolling();
1012
+ this.handleComplete(execution);
1013
+ return;
1014
+ }
1015
+ if (execution.status === "failed" || execution.status === "cancelled") {
1016
+ this.stopPolling();
1017
+ this.handleError({
1018
+ error: execution.error ?? `Execution ${execution.status}`,
1019
+ errorCode: execution.status
1020
+ });
1021
+ return;
1022
+ }
1023
+ } catch (err) {
1024
+ console.warn("[SDK:StreamController] State recovery failed (non-fatal):", err);
976
1025
  }
977
1026
  }
978
1027
  /**
@@ -2824,12 +2873,54 @@ function useExecutionStream(options) {
2824
2873
  }
2825
2874
 
2826
2875
  // src/chat/hooks/useAgentChat.ts
2876
+ var __DEV__ = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
2827
2877
  var SILENT_TOOL_NAMES = /* @__PURE__ */ new Set([
2828
2878
  "check_delegations"
2829
2879
  ]);
2830
2880
  function generateId() {
2831
2881
  return `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
2832
2882
  }
2883
+ function extractImagesFromResult(result, sourceToolCallId) {
2884
+ if (!result) return [];
2885
+ let data = result;
2886
+ if (typeof result === "string") {
2887
+ try {
2888
+ data = JSON.parse(result);
2889
+ } catch {
2890
+ if (/\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(result) || result.startsWith("data:image/")) {
2891
+ return [{ url: result, sourceToolCallId }];
2892
+ }
2893
+ return [];
2894
+ }
2895
+ }
2896
+ if (typeof data !== "object" || data === null) return [];
2897
+ const obj = data;
2898
+ if (Array.isArray(obj.images)) {
2899
+ return obj.images.filter((img) => typeof img.url === "string").map((img) => ({
2900
+ url: img.url,
2901
+ alt: img.alt || img.prompt || void 0,
2902
+ filename: img.filename,
2903
+ sourceToolCallId
2904
+ }));
2905
+ }
2906
+ if (typeof obj.url === "string" && /\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(obj.url)) {
2907
+ return [{ url: obj.url, alt: obj.alt, sourceToolCallId }];
2908
+ }
2909
+ if (typeof obj.imageUrl === "string") {
2910
+ return [{ url: obj.imageUrl, alt: obj.alt, sourceToolCallId }];
2911
+ }
2912
+ return [];
2913
+ }
2914
+ function deriveImagesFromToolCalls(toolCalls) {
2915
+ if (!toolCalls) return [];
2916
+ const images = [];
2917
+ for (const tc of toolCalls) {
2918
+ if (tc.status === "completed" && tc.result) {
2919
+ images.push(...extractImagesFromResult(tc.result, tc.id));
2920
+ }
2921
+ }
2922
+ return images;
2923
+ }
2833
2924
  function historyToMessage(historyMsg) {
2834
2925
  return {
2835
2926
  id: historyMsg.uuid,
@@ -2870,7 +2961,9 @@ function useAgentChat(options) {
2870
2961
  // Deprecated, but still supported
2871
2962
  onToolCall,
2872
2963
  onToolResult,
2873
- onClarification
2964
+ onClarification,
2965
+ pollingInterval,
2966
+ pollingEnabled
2874
2967
  } = options;
2875
2968
  const contextConfig = useChatConfigOptional();
2876
2969
  const adapter = propsAdapter ?? contextConfig?.adapter;
@@ -2902,8 +2995,14 @@ function useAgentChat(options) {
2902
2995
  const streamingMessageIdRef = react.useRef(null);
2903
2996
  const pendingToolCallsRef = react.useRef(/* @__PURE__ */ new Map());
2904
2997
  const lastMessageContentRef = react.useRef("");
2998
+ const finalizedRef = react.useRef(false);
2905
2999
  const firedToolResultsRef = react.useRef(/* @__PURE__ */ new Set());
2906
3000
  const streamControllerRef = react.useRef(null);
3001
+ const pendingDelegationsRef = react.useRef(/* @__PURE__ */ new Map());
3002
+ const onDelegationRef = react.useRef(options.onDelegation);
3003
+ react.useEffect(() => {
3004
+ onDelegationRef.current = options.onDelegation;
3005
+ }, [options.onDelegation]);
2907
3006
  const updateStreamingMessage = react.useCallback((content) => {
2908
3007
  if (!streamingMessageIdRef.current) return;
2909
3008
  setMessages(
@@ -2932,25 +3031,72 @@ function useAgentChat(options) {
2932
3031
  });
2933
3032
  });
2934
3033
  }, []);
2935
- const finalizeStreamingMessage = react.useCallback((terminalStatus = "completed") => {
3034
+ const addDelegationEvent = react.useCallback((delegation) => {
3035
+ setMessages((prev) => {
3036
+ const targetId = streamingMessageIdRef.current ?? [...prev].reverse().find((m) => m.role === "assistant")?.id;
3037
+ if (!targetId) return prev;
3038
+ return prev.map((msg) => {
3039
+ if (msg.id !== targetId) return msg;
3040
+ const existing = msg.delegationEvents ?? [];
3041
+ if (delegation.type === "delegation_completed" && delegation.handoffId) {
3042
+ const updatedEvents = existing.map(
3043
+ (de) => de.handoffId === delegation.handoffId ? { ...de, ...delegation, status: delegation.status ?? "completed" } : de
3044
+ );
3045
+ const hasMatch = existing.some((de) => de.handoffId === delegation.handoffId);
3046
+ return {
3047
+ ...msg,
3048
+ delegationEvents: hasMatch ? updatedEvents : [...existing, delegation]
3049
+ };
3050
+ }
3051
+ return { ...msg, delegationEvents: [...existing, delegation] };
3052
+ });
3053
+ });
3054
+ }, []);
3055
+ const updateMessageImages = react.useCallback(() => {
3056
+ setMessages((prev) => {
3057
+ const targetId = streamingMessageIdRef.current ?? [...prev].reverse().find((m) => m.role === "assistant")?.id;
3058
+ if (!targetId) return prev;
3059
+ return prev.map((msg) => {
3060
+ if (msg.id !== targetId) return msg;
3061
+ const images = deriveImagesFromToolCalls(msg.toolCalls);
3062
+ return images.length > 0 ? { ...msg, images } : msg;
3063
+ });
3064
+ });
3065
+ }, []);
3066
+ const finalizeStreamingMessage = react.useCallback((finalOutput) => {
2936
3067
  if (!streamingMessageIdRef.current) return;
2937
- setMessages(
2938
- (prev) => prev.map((msg) => {
2939
- if (msg.id !== streamingMessageIdRef.current) return msg;
3068
+ finalizedRef.current = true;
3069
+ const streamMsgId = streamingMessageIdRef.current;
3070
+ setMessages((prev) => {
3071
+ const resolvedContent = finalOutput || streamingContentRef.current || "";
3072
+ const duplicateExists = resolvedContent && prev.some(
3073
+ (msg) => msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
3074
+ );
3075
+ if (duplicateExists) {
3076
+ return prev.filter((msg) => msg.id !== streamMsgId);
3077
+ }
3078
+ return prev.map((msg) => {
3079
+ if (msg.id !== streamMsgId) return msg;
2940
3080
  const updatedToolCalls = msg.toolCalls?.map(
2941
- (tc) => tc.status === "running" || tc.status === "pending" ? { ...tc, status: terminalStatus } : tc
3081
+ (tc) => tc.status === "running" || tc.status === "pending" ? { ...tc, status: "completed" } : tc
2942
3082
  );
2943
- return { ...msg, isStreaming: false, toolCalls: updatedToolCalls };
2944
- })
2945
- );
3083
+ return {
3084
+ ...msg,
3085
+ content: resolvedContent || msg.content,
3086
+ isStreaming: false,
3087
+ toolCalls: updatedToolCalls
3088
+ };
3089
+ });
3090
+ });
2946
3091
  streamingMessageIdRef.current = null;
2947
3092
  streamingContentRef.current = "";
2948
3093
  pendingToolCallsRef.current.clear();
2949
3094
  firedToolResultsRef.current.clear();
3095
+ pendingDelegationsRef.current.clear();
2950
3096
  }, []);
2951
3097
  const handleExecutionLog = react.useCallback(
2952
3098
  (log) => {
2953
- console.log("[useAgentChat] handleExecutionLog:", log.type, log.metadata);
3099
+ if (__DEV__) console.log("[useAgentChat] handleExecutionLog:", log.type, log.metadata);
2954
3100
  switch (log.type) {
2955
3101
  // Primary handlers for ai-saas-api worker log types
2956
3102
  case "llm_response": {
@@ -2970,11 +3116,46 @@ function useAgentChat(options) {
2970
3116
  }
2971
3117
  break;
2972
3118
  }
3119
+ case "delegation": {
3120
+ const delegationType = log.metadata?.delegationType;
3121
+ if (delegationType === "started" || delegationType === "delegation_started") {
3122
+ const delegation = {
3123
+ type: "delegation_started",
3124
+ handoffId: log.metadata?.handoffId,
3125
+ toAgent: log.metadata?.targetAgentName || log.metadata?.targetAgentUuid,
3126
+ targetAgentUuid: log.metadata?.targetAgentUuid,
3127
+ taskPreview: log.metadata?.taskPreview,
3128
+ timestamp: new Date(log.timestamp || Date.now()),
3129
+ status: "active"
3130
+ };
3131
+ if (delegation.handoffId) {
3132
+ pendingDelegationsRef.current.set(delegation.handoffId, delegation);
3133
+ }
3134
+ addDelegationEvent(delegation);
3135
+ onDelegationRef.current?.(delegation);
3136
+ } else if (delegationType === "completed" || delegationType === "delegation_completed") {
3137
+ const delegation = {
3138
+ type: "delegation_completed",
3139
+ handoffId: log.metadata?.handoffId,
3140
+ resultPreview: log.metadata?.resultPreview,
3141
+ timestamp: new Date(log.timestamp || Date.now()),
3142
+ status: log.metadata?.status === "failed" ? "failed" : "completed",
3143
+ durationMs: log.durationMs
3144
+ };
3145
+ if (delegation.handoffId) {
3146
+ pendingDelegationsRef.current.delete(delegation.handoffId);
3147
+ }
3148
+ addDelegationEvent(delegation);
3149
+ onDelegationRef.current?.(delegation);
3150
+ }
3151
+ break;
3152
+ }
2973
3153
  case "info":
2974
3154
  case "system":
2975
- console.log("[useAgentChat] Status:", log.type, log.title);
3155
+ if (__DEV__) console.log("[useAgentChat] Status:", log.type, log.title);
2976
3156
  break;
2977
3157
  case "tool_call": {
3158
+ if (finalizedRef.current) break;
2978
3159
  const toolName = log.metadata?.toolName;
2979
3160
  if (SILENT_TOOL_NAMES.has(toolName)) break;
2980
3161
  const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
@@ -3029,17 +3210,18 @@ function useAgentChat(options) {
3029
3210
  firedToolResultsRef.current.add(resultToolCallId);
3030
3211
  }
3031
3212
  onToolResultRef.current?.(resultToolName, result, durationMs);
3213
+ updateMessageImages();
3032
3214
  break;
3033
3215
  }
3034
3216
  default:
3035
- console.log("[useAgentChat] Unhandled log type:", log.type, log);
3217
+ if (__DEV__) console.log("[useAgentChat] Unhandled log type:", log.type, log);
3036
3218
  }
3037
3219
  },
3038
- [updateStreamingMessage, updateToolCall]
3220
+ [updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent]
3039
3221
  );
3040
3222
  const handleStreamLog = react.useCallback(
3041
3223
  (log) => {
3042
- console.log("[useAgentChat] handleStreamLog:", log.logType, log.metadata);
3224
+ if (__DEV__) console.log("[useAgentChat] handleStreamLog:", log.logType, log.metadata);
3043
3225
  switch (log.logType) {
3044
3226
  case "llm_response": {
3045
3227
  const llmText = log.metadata?.text || "";
@@ -3058,9 +3240,42 @@ function useAgentChat(options) {
3058
3240
  }
3059
3241
  break;
3060
3242
  }
3243
+ case "delegation_started": {
3244
+ const delegation = {
3245
+ type: "delegation_started",
3246
+ handoffId: log.metadata?.handoffId,
3247
+ toAgent: log.metadata?.targetAgentName || log.metadata?.targetAgentUuid,
3248
+ targetAgentUuid: log.metadata?.targetAgentUuid,
3249
+ taskPreview: log.metadata?.taskPreview,
3250
+ timestamp: new Date(log.timestamp || Date.now()),
3251
+ status: "active"
3252
+ };
3253
+ if (delegation.handoffId) {
3254
+ pendingDelegationsRef.current.set(delegation.handoffId, delegation);
3255
+ }
3256
+ addDelegationEvent(delegation);
3257
+ onDelegationRef.current?.(delegation);
3258
+ break;
3259
+ }
3260
+ case "delegation_completed": {
3261
+ const delegation = {
3262
+ type: "delegation_completed",
3263
+ handoffId: log.metadata?.handoffId,
3264
+ resultPreview: log.metadata?.resultPreview,
3265
+ timestamp: new Date(log.timestamp || Date.now()),
3266
+ status: log.metadata?.status === "failed" ? "failed" : "completed",
3267
+ durationMs: log.durationMs
3268
+ };
3269
+ if (delegation.handoffId) {
3270
+ pendingDelegationsRef.current.delete(delegation.handoffId);
3271
+ }
3272
+ addDelegationEvent(delegation);
3273
+ onDelegationRef.current?.(delegation);
3274
+ break;
3275
+ }
3061
3276
  case "info":
3062
3277
  case "system":
3063
- console.log("[useAgentChat] Status:", log.logType, log.message);
3278
+ if (__DEV__) console.log("[useAgentChat] Status:", log.logType, log.message);
3064
3279
  break;
3065
3280
  case "response_text":
3066
3281
  case "text_chunk": {
@@ -3070,6 +3285,7 @@ function useAgentChat(options) {
3070
3285
  break;
3071
3286
  }
3072
3287
  case "tool_call": {
3288
+ if (finalizedRef.current) break;
3073
3289
  const toolName = log.metadata?.toolName;
3074
3290
  if (SILENT_TOOL_NAMES.has(toolName)) break;
3075
3291
  const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
@@ -3124,6 +3340,7 @@ function useAgentChat(options) {
3124
3340
  firedToolResultsRef.current.add(resultToolCallId);
3125
3341
  }
3126
3342
  onToolResultRef.current?.(resultToolName, result, durationMs);
3343
+ updateMessageImages();
3127
3344
  break;
3128
3345
  }
3129
3346
  case "final_response": {
@@ -3135,10 +3352,10 @@ function useAgentChat(options) {
3135
3352
  break;
3136
3353
  }
3137
3354
  default:
3138
- console.log("[useAgentChat] Unknown log type:", log.logType, log);
3355
+ if (__DEV__) console.log("[useAgentChat] Unknown log type:", log.logType, log);
3139
3356
  }
3140
3357
  },
3141
- [updateStreamingMessage, updateToolCall]
3358
+ [updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent]
3142
3359
  );
3143
3360
  const handleClarification = react.useCallback(
3144
3361
  (data) => {
@@ -3147,17 +3364,23 @@ function useAgentChat(options) {
3147
3364
  },
3148
3365
  [onClarification]
3149
3366
  );
3150
- const handleComplete = react.useCallback(() => {
3151
- console.log("[useAgentChat] handleComplete called");
3367
+ const completedRef = react.useRef(false);
3368
+ const handleComplete = react.useCallback((finalOutput) => {
3369
+ if (completedRef.current) {
3370
+ if (__DEV__) console.log("[useAgentChat] handleComplete: skipping duplicate complete");
3371
+ return;
3372
+ }
3373
+ completedRef.current = true;
3374
+ if (__DEV__) console.log("[useAgentChat] handleComplete called");
3152
3375
  setIsLoading(false);
3153
3376
  setIsStreamControllerConnected(false);
3154
- finalizeStreamingMessage();
3377
+ finalizeStreamingMessage(finalOutput);
3155
3378
  setCurrentExecutionId(null);
3156
3379
  streamControllerRef.current = null;
3157
3380
  }, [finalizeStreamingMessage]);
3158
3381
  const handleError = react.useCallback(
3159
3382
  (errorMsg) => {
3160
- console.log("[useAgentChat] handleError called:", errorMsg);
3383
+ if (__DEV__) console.log("[useAgentChat] handleError called:", errorMsg);
3161
3384
  const chatError = {
3162
3385
  code: "EXECUTION_ERROR",
3163
3386
  message: errorMsg,
@@ -3166,7 +3389,7 @@ function useAgentChat(options) {
3166
3389
  setError(chatError);
3167
3390
  setIsLoading(false);
3168
3391
  setIsStreamControllerConnected(false);
3169
- finalizeStreamingMessage("failed");
3392
+ finalizeStreamingMessage();
3170
3393
  setCurrentExecutionId(null);
3171
3394
  streamControllerRef.current = null;
3172
3395
  onError?.(chatError);
@@ -3196,43 +3419,39 @@ function useAgentChat(options) {
3196
3419
  if (pollingExecutionIdRef.current === executionId) return;
3197
3420
  stopPolling();
3198
3421
  pollingExecutionIdRef.current = executionId;
3199
- console.log("[useAgentChat] Starting polling fallback for:", executionId);
3422
+ if (__DEV__) console.log("[useAgentChat] Starting polling fallback for:", executionId);
3200
3423
  const poll = async () => {
3201
3424
  if (pollingExecutionIdRef.current !== executionId) return;
3202
3425
  try {
3203
3426
  const execution = await adapter.getExecution(executionId);
3204
3427
  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
- }
3428
+ if (__DEV__) console.log("[useAgentChat] Polling: execution completed");
3210
3429
  stopPolling();
3211
- handleComplete();
3430
+ handleComplete(execution.result || void 0);
3212
3431
  } else if (execution.status === "failed") {
3213
- console.log("[useAgentChat] Polling: execution failed");
3432
+ if (__DEV__) console.log("[useAgentChat] Polling: execution failed");
3214
3433
  stopPolling();
3215
3434
  handleError(execution.error || "Execution failed");
3216
3435
  }
3217
3436
  } catch (err) {
3218
- console.warn("[useAgentChat] Polling error (will retry):", err);
3437
+ if (__DEV__) console.warn("[useAgentChat] Polling error (will retry):", err);
3219
3438
  }
3220
3439
  };
3221
3440
  poll();
3222
- pollingIntervalRef.current = setInterval(poll, 2e3);
3223
- }, [adapter, stopPolling, updateStreamingMessage, handleComplete, handleError]);
3441
+ pollingIntervalRef.current = setInterval(poll, pollingInterval ?? 2e3);
3442
+ }, [adapter, stopPolling, handleComplete, handleError, pollingInterval]);
3224
3443
  react.useEffect(() => {
3225
- if (!useStreamController && isUsingFallback && currentExecutionId && isLoading) {
3226
- console.log("[useAgentChat] SSE fallback detected, activating polling");
3444
+ if (!useStreamController && isUsingFallback && currentExecutionId && isLoading && pollingEnabled !== false) {
3445
+ if (__DEV__) console.log("[useAgentChat] SSE fallback detected, activating polling");
3227
3446
  startPolling(currentExecutionId);
3228
3447
  }
3229
- }, [useStreamController, isUsingFallback, currentExecutionId, isLoading, startPolling]);
3448
+ }, [useStreamController, isUsingFallback, currentExecutionId, isLoading, startPolling, pollingEnabled]);
3230
3449
  const sseTimeoutRef = react.useRef(null);
3231
3450
  react.useEffect(() => {
3232
- if (!useStreamController && currentExecutionId && isLoading && !legacyIsConnected && !isUsingFallback) {
3451
+ if (!useStreamController && currentExecutionId && isLoading && !legacyIsConnected && !isUsingFallback && pollingEnabled !== false) {
3233
3452
  sseTimeoutRef.current = setTimeout(() => {
3234
3453
  if (!legacyIsConnected && currentExecutionId && isLoading) {
3235
- console.log("[useAgentChat] SSE connection timeout, activating polling fallback");
3454
+ if (__DEV__) console.log("[useAgentChat] SSE connection timeout, activating polling fallback");
3236
3455
  startPolling(currentExecutionId);
3237
3456
  }
3238
3457
  }, 1e4);
@@ -3243,7 +3462,7 @@ function useAgentChat(options) {
3243
3462
  sseTimeoutRef.current = null;
3244
3463
  }
3245
3464
  };
3246
- }, [useStreamController, currentExecutionId, isLoading, legacyIsConnected, isUsingFallback, startPolling]);
3465
+ }, [useStreamController, currentExecutionId, isLoading, legacyIsConnected, isUsingFallback, startPolling, pollingEnabled]);
3247
3466
  react.useEffect(() => {
3248
3467
  if (!currentExecutionId) {
3249
3468
  stopPolling();
@@ -3259,7 +3478,7 @@ function useAgentChat(options) {
3259
3478
  streamControllerRef.current.cancel();
3260
3479
  streamControllerRef.current = null;
3261
3480
  }
3262
- console.log("[useAgentChat] Creating stream controller for:", executionId);
3481
+ if (__DEV__) console.log("[useAgentChat] Creating stream controller for:", executionId);
3263
3482
  const controller = adapter.createStream(executionId, sessionId);
3264
3483
  streamControllerRef.current = controller;
3265
3484
  controller.on("log", (log) => {
@@ -3310,6 +3529,34 @@ function useAgentChat(options) {
3310
3529
  }
3311
3530
  firedToolResultsRef.current.add(toolCallId);
3312
3531
  onToolResultRef.current?.(toolName, data.result, data.durationMs);
3532
+ updateMessageImages();
3533
+ });
3534
+ controller.on("delegationStarted", (data) => {
3535
+ const delegation = {
3536
+ type: "delegation_started",
3537
+ handoffId: data.handoffId,
3538
+ toAgent: data.targetAgentName || data.targetAgentUuid,
3539
+ targetAgentUuid: data.targetAgentUuid,
3540
+ taskPreview: data.taskPreview,
3541
+ timestamp: new Date(data.timestamp),
3542
+ status: "active"
3543
+ };
3544
+ pendingDelegationsRef.current.set(data.handoffId, delegation);
3545
+ addDelegationEvent(delegation);
3546
+ onDelegationRef.current?.(delegation);
3547
+ });
3548
+ controller.on("delegationCompleted", (data) => {
3549
+ const delegation = {
3550
+ type: "delegation_completed",
3551
+ handoffId: data.handoffId,
3552
+ resultPreview: data.resultPreview,
3553
+ timestamp: new Date(data.timestamp),
3554
+ status: data.status === "failed" ? "failed" : "completed",
3555
+ durationMs: data.durationMs
3556
+ };
3557
+ pendingDelegationsRef.current.delete(data.handoffId);
3558
+ addDelegationEvent(delegation);
3559
+ onDelegationRef.current?.(delegation);
3313
3560
  });
3314
3561
  controller.on("textDelta", (data) => {
3315
3562
  streamingContentRef.current += data.delta;
@@ -3323,11 +3570,8 @@ function useAgentChat(options) {
3323
3570
  });
3324
3571
  });
3325
3572
  controller.on("complete", ({ execution }) => {
3326
- if (execution?.output && !streamingContentRef.current) {
3327
- streamingContentRef.current = execution.output;
3328
- updateStreamingMessage(execution.output);
3329
- }
3330
- handleComplete();
3573
+ const output = execution?.output || void 0;
3574
+ handleComplete(output);
3331
3575
  });
3332
3576
  controller.on("error", (errorEvent) => {
3333
3577
  handleError(errorEvent.error);
@@ -3341,7 +3585,7 @@ function useAgentChat(options) {
3341
3585
  });
3342
3586
  setIsStreamControllerConnected(true);
3343
3587
  },
3344
- [adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
3588
+ [adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, updateMessageImages, addDelegationEvent, handleClarification, handleComplete, handleError]
3345
3589
  );
3346
3590
  const sendMessage = react.useCallback(
3347
3591
  async (content, options2) => {
@@ -3371,8 +3615,10 @@ function useAgentChat(options) {
3371
3615
  setMessages((prev) => [...prev, assistantMessage]);
3372
3616
  streamingMessageIdRef.current = assistantMessageId;
3373
3617
  streamingContentRef.current = "";
3618
+ completedRef.current = false;
3619
+ finalizedRef.current = false;
3374
3620
  try {
3375
- console.log("[useAgentChat] Starting execution...", { agentId, sessionId: currentSessionId });
3621
+ if (__DEV__) console.log("[useAgentChat] Starting execution...", { agentId, sessionId: currentSessionId });
3376
3622
  const result = await adapter.startExecution({
3377
3623
  agentId,
3378
3624
  teamId,
@@ -3380,7 +3626,7 @@ function useAgentChat(options) {
3380
3626
  content: content.trim(),
3381
3627
  attachmentUuids
3382
3628
  });
3383
- console.log("[useAgentChat] Execution started:", result);
3629
+ if (__DEV__) console.log("[useAgentChat] Execution started:", result);
3384
3630
  if (!currentSessionId && result.sessionId) {
3385
3631
  internallyCreatedSessionRef.current = result.sessionId;
3386
3632
  setCurrentSessionId(result.sessionId);
@@ -3391,7 +3637,7 @@ function useAgentChat(options) {
3391
3637
  connectStreamController(result.executionId, result.sessionId || currentSessionId || void 0);
3392
3638
  setCurrentExecutionId(result.executionId);
3393
3639
  } else {
3394
- console.log("[useAgentChat] Setting executionId:", result.executionId);
3640
+ if (__DEV__) console.log("[useAgentChat] Setting executionId:", result.executionId);
3395
3641
  setCurrentExecutionId(result.executionId);
3396
3642
  }
3397
3643
  } catch (err) {
@@ -3451,6 +3697,7 @@ function useAgentChat(options) {
3451
3697
  setMessages([]);
3452
3698
  setCurrentSessionId(null);
3453
3699
  hasLoadedInitialSession.current = false;
3700
+ lastLoadedSessionRef.current = null;
3454
3701
  internallyCreatedSessionRef.current = null;
3455
3702
  }, []);
3456
3703
  const loadSession = react.useCallback(
@@ -3546,15 +3793,20 @@ function useAgentChat(options) {
3546
3793
  },
3547
3794
  [adapter, onError]
3548
3795
  );
3796
+ const lastLoadedSessionRef = react.useRef(null);
3549
3797
  react.useEffect(() => {
3550
- if (initialSessionId && !hasLoadedInitialSession.current) {
3551
- if (internallyCreatedSessionRef.current === initialSessionId) {
3552
- hasLoadedInitialSession.current = true;
3553
- return;
3554
- }
3798
+ if (!initialSessionId) return;
3799
+ if (internallyCreatedSessionRef.current === initialSessionId) {
3800
+ lastLoadedSessionRef.current = initialSessionId;
3555
3801
  hasLoadedInitialSession.current = true;
3556
- loadSession(initialSessionId);
3802
+ return;
3803
+ }
3804
+ if (lastLoadedSessionRef.current === initialSessionId && hasLoadedInitialSession.current) {
3805
+ return;
3557
3806
  }
3807
+ lastLoadedSessionRef.current = initialSessionId;
3808
+ hasLoadedInitialSession.current = true;
3809
+ loadSession(initialSessionId);
3558
3810
  }, [initialSessionId, loadSession]);
3559
3811
  react.useEffect(() => {
3560
3812
  return () => {
@@ -3570,6 +3822,16 @@ function useAgentChat(options) {
3570
3822
  }
3571
3823
  return !!currentExecutionId && legacyIsConnected;
3572
3824
  }, [useStreamController, currentExecutionId, isStreamControllerConnected, legacyIsConnected]);
3825
+ const activeDelegation = react.useMemo(() => {
3826
+ for (let i = messages.length - 1; i >= 0; i--) {
3827
+ const msg = messages[i];
3828
+ if (msg.delegationEvents) {
3829
+ const active = msg.delegationEvents.find((de) => de.status === "active");
3830
+ if (active) return active;
3831
+ }
3832
+ }
3833
+ return null;
3834
+ }, [messages]);
3573
3835
  return {
3574
3836
  messages,
3575
3837
  isLoading,
@@ -3578,6 +3840,7 @@ function useAgentChat(options) {
3578
3840
  error,
3579
3841
  sessionId: currentSessionId,
3580
3842
  clarification,
3843
+ activeDelegation,
3581
3844
  sendMessage,
3582
3845
  respondToClarification,
3583
3846
  cancel,
@@ -4433,18 +4696,30 @@ function ChatContainer({
4433
4696
  children,
4434
4697
  maxHeight = "600px",
4435
4698
  className,
4436
- theme = "system"
4699
+ theme = "system",
4700
+ layout = "fixed"
4437
4701
  }) {
4438
4702
  const maxHeightValue = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
4703
+ const isFill = layout === "fill";
4439
4704
  return /* @__PURE__ */ jsxRuntime.jsx(
4440
4705
  "div",
4441
4706
  {
4442
4707
  className: cn2(
4443
- "flex flex-col bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
4708
+ "cadra-chat-container flex flex-col overflow-hidden",
4709
+ // Default colors use CSS custom properties with Tailwind fallbacks.
4710
+ // Consumers can override via CSS: --chat-bg, --chat-border, --chat-border-radius
4711
+ !isFill && "rounded-lg border",
4712
+ isFill && "flex-1 min-h-0",
4444
4713
  className
4445
4714
  ),
4446
- style: { maxHeight: maxHeightValue },
4715
+ style: {
4716
+ ...!isFill ? { maxHeight: maxHeightValue } : void 0,
4717
+ // CSS custom properties for theming — consumers set these to override
4718
+ backgroundColor: "var(--chat-bg, var(--background, #fff))",
4719
+ borderColor: !isFill ? "var(--chat-border, var(--border, #e5e7eb))" : void 0
4720
+ },
4447
4721
  "data-theme": theme,
4722
+ "data-layout": layout,
4448
4723
  children
4449
4724
  }
4450
4725
  );
@@ -5131,7 +5406,7 @@ var FileDropZone = react.forwardRef(
5131
5406
  {
5132
5407
  ref: dropZoneRef,
5133
5408
  "data-dropzone": "wrapper",
5134
- className: cn4("relative w-full h-full", className),
5409
+ className: cn4("relative flex flex-col flex-1 min-h-0", className),
5135
5410
  style: { minHeight: "inherit" },
5136
5411
  children: [
5137
5412
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5416,6 +5691,312 @@ function AskUserPrompt({
5416
5691
  function cn6(...classes) {
5417
5692
  return classes.filter(Boolean).join(" ");
5418
5693
  }
5694
+ function formatDuration(ms) {
5695
+ if (ms < 1e3) return `${ms}ms`;
5696
+ return `${(ms / 1e3).toFixed(1)}s`;
5697
+ }
5698
+ function DelegationProgress({
5699
+ event,
5700
+ className
5701
+ }) {
5702
+ const statusIcon = react.useMemo(() => {
5703
+ switch (event.status) {
5704
+ case "active":
5705
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-4 w-4 text-blue-500" });
5706
+ case "completed":
5707
+ return /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-500" });
5708
+ case "failed":
5709
+ return /* @__PURE__ */ jsxRuntime.jsx(AlertCircleIcon, { className: "h-4 w-4 text-red-500" });
5710
+ default:
5711
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-4 w-4 text-gray-400" });
5712
+ }
5713
+ }, [event.status]);
5714
+ const statusColor = react.useMemo(() => {
5715
+ switch (event.status) {
5716
+ case "active":
5717
+ return "border-blue-200 dark:border-blue-800 bg-blue-50/50 dark:bg-blue-950/20";
5718
+ case "completed":
5719
+ return "border-green-200 dark:border-green-800 bg-green-50/50 dark:bg-green-950/20";
5720
+ case "failed":
5721
+ return "border-red-200 dark:border-red-800 bg-red-50/50 dark:bg-red-950/20";
5722
+ default:
5723
+ return "border-gray-200 dark:border-gray-700";
5724
+ }
5725
+ }, [event.status]);
5726
+ return /* @__PURE__ */ jsxRuntime.jsx(
5727
+ "div",
5728
+ {
5729
+ className: cn6(
5730
+ "rounded-lg border text-sm px-3 py-2",
5731
+ statusColor,
5732
+ className
5733
+ ),
5734
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5735
+ statusIcon,
5736
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
5737
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
5738
+ event.fromAgent && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5739
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-700 dark:text-gray-300 truncate", children: event.fromAgent }),
5740
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500 flex-shrink-0", children: "\u2192" })
5741
+ ] }),
5742
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100 truncate", children: event.toAgent || event.targetAgentUuid || "Specialist" })
5743
+ ] }),
5744
+ 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 }),
5745
+ 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 })
5746
+ ] }),
5747
+ 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) })
5748
+ ] })
5749
+ }
5750
+ );
5751
+ }
5752
+ function DelegationProgressList({
5753
+ events,
5754
+ className
5755
+ }) {
5756
+ if (!events || events.length === 0) return null;
5757
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn6("space-y-2", className), children: events.map((event, index) => /* @__PURE__ */ jsxRuntime.jsx(
5758
+ DelegationProgress,
5759
+ {
5760
+ event
5761
+ },
5762
+ event.handoffId || `delegation-${index}`
5763
+ )) });
5764
+ }
5765
+ function ImageLightbox({
5766
+ images,
5767
+ currentIndex,
5768
+ onClose,
5769
+ onPrev,
5770
+ onNext,
5771
+ className
5772
+ }) {
5773
+ const currentImage = images[currentIndex];
5774
+ const hasMultiple = images.length > 1;
5775
+ react.useEffect(() => {
5776
+ function handleKeyDown(e) {
5777
+ switch (e.key) {
5778
+ case "Escape":
5779
+ onClose();
5780
+ break;
5781
+ case "ArrowLeft":
5782
+ if (hasMultiple) onPrev();
5783
+ break;
5784
+ case "ArrowRight":
5785
+ if (hasMultiple) onNext();
5786
+ break;
5787
+ }
5788
+ }
5789
+ document.addEventListener("keydown", handleKeyDown);
5790
+ return () => document.removeEventListener("keydown", handleKeyDown);
5791
+ }, [onClose, onPrev, onNext, hasMultiple]);
5792
+ react.useEffect(() => {
5793
+ const originalOverflow = document.body.style.overflow;
5794
+ document.body.style.overflow = "hidden";
5795
+ return () => {
5796
+ document.body.style.overflow = originalOverflow;
5797
+ };
5798
+ }, []);
5799
+ const handleDownload = react.useCallback(() => {
5800
+ if (!currentImage) return;
5801
+ const link = document.createElement("a");
5802
+ link.href = currentImage.url;
5803
+ link.download = currentImage.filename || `image-${currentIndex + 1}`;
5804
+ link.target = "_blank";
5805
+ link.rel = "noopener noreferrer";
5806
+ document.body.appendChild(link);
5807
+ link.click();
5808
+ document.body.removeChild(link);
5809
+ }, [currentImage, currentIndex]);
5810
+ if (!currentImage) return null;
5811
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5812
+ "div",
5813
+ {
5814
+ className: `fixed inset-0 z-50 flex items-center justify-center bg-black/80 ${className || ""}`,
5815
+ onClick: onClose,
5816
+ children: [
5817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4 z-10", children: [
5818
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5819
+ currentIndex + 1,
5820
+ " / ",
5821
+ images.length
5822
+ ] }) }),
5823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5824
+ /* @__PURE__ */ jsxRuntime.jsx(
5825
+ "button",
5826
+ {
5827
+ type: "button",
5828
+ onClick: (e) => {
5829
+ e.stopPropagation();
5830
+ handleDownload();
5831
+ },
5832
+ className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5833
+ "aria-label": "Download image",
5834
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" })
5835
+ }
5836
+ ),
5837
+ /* @__PURE__ */ jsxRuntime.jsx(
5838
+ "button",
5839
+ {
5840
+ type: "button",
5841
+ onClick: (e) => {
5842
+ e.stopPropagation();
5843
+ onClose();
5844
+ },
5845
+ className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5846
+ "aria-label": "Close lightbox",
5847
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
5848
+ }
5849
+ )
5850
+ ] })
5851
+ ] }),
5852
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5853
+ "button",
5854
+ {
5855
+ type: "button",
5856
+ onClick: (e) => {
5857
+ e.stopPropagation();
5858
+ onPrev();
5859
+ },
5860
+ className: "absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors z-10",
5861
+ "aria-label": "Previous image",
5862
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5863
+ }
5864
+ ),
5865
+ /* @__PURE__ */ jsxRuntime.jsx(
5866
+ "img",
5867
+ {
5868
+ src: currentImage.url,
5869
+ alt: currentImage.alt || `Image ${currentIndex + 1}`,
5870
+ className: "max-w-[90vw] max-h-[85vh] object-contain",
5871
+ onClick: (e) => e.stopPropagation()
5872
+ }
5873
+ ),
5874
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5875
+ "button",
5876
+ {
5877
+ type: "button",
5878
+ onClick: (e) => {
5879
+ e.stopPropagation();
5880
+ onNext();
5881
+ },
5882
+ className: "absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors z-10",
5883
+ "aria-label": "Next image",
5884
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-6 w-6" })
5885
+ }
5886
+ ),
5887
+ currentImage.alt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 text-center z-10", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt }) })
5888
+ ]
5889
+ }
5890
+ );
5891
+ }
5892
+ function cn7(...classes) {
5893
+ return classes.filter(Boolean).join(" ");
5894
+ }
5895
+ function ImageWithLoading({ src, alt, className, onClick }) {
5896
+ const [isLoaded, setIsLoaded] = react.useState(false);
5897
+ const [hasError, setHasError] = react.useState(false);
5898
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5899
+ "div",
5900
+ {
5901
+ className: cn7(
5902
+ "relative overflow-hidden bg-gray-100 dark:bg-gray-800",
5903
+ className
5904
+ ),
5905
+ children: [
5906
+ !isLoaded && !hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700" }),
5907
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm", children: "Failed to load" }),
5908
+ /* @__PURE__ */ jsxRuntime.jsx(
5909
+ "img",
5910
+ {
5911
+ src,
5912
+ alt,
5913
+ loading: "lazy",
5914
+ className: cn7(
5915
+ "w-full h-auto max-h-[300px] object-contain transition-opacity duration-200",
5916
+ isLoaded ? "opacity-100" : "opacity-0"
5917
+ ),
5918
+ onLoad: () => setIsLoaded(true),
5919
+ onError: () => setHasError(true),
5920
+ onClick,
5921
+ style: onClick ? { cursor: "pointer" } : void 0
5922
+ }
5923
+ )
5924
+ ]
5925
+ }
5926
+ );
5927
+ }
5928
+ function ImageGallery({
5929
+ images,
5930
+ onImageClick,
5931
+ className
5932
+ }) {
5933
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
5934
+ const handleImageClick = react.useCallback(
5935
+ (image, index) => {
5936
+ if (onImageClick) {
5937
+ onImageClick(image);
5938
+ } else {
5939
+ setLightboxIndex(index);
5940
+ }
5941
+ },
5942
+ [onImageClick]
5943
+ );
5944
+ const handleLightboxClose = react.useCallback(() => {
5945
+ setLightboxIndex(null);
5946
+ }, []);
5947
+ const handleLightboxPrev = react.useCallback(() => {
5948
+ setLightboxIndex(
5949
+ (prev) => prev !== null ? (prev - 1 + images.length) % images.length : null
5950
+ );
5951
+ }, [images.length]);
5952
+ const handleLightboxNext = react.useCallback(() => {
5953
+ setLightboxIndex(
5954
+ (prev) => prev !== null ? (prev + 1) % images.length : null
5955
+ );
5956
+ }, [images.length]);
5957
+ if (!images || images.length === 0) return null;
5958
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5959
+ /* @__PURE__ */ jsxRuntime.jsx(
5960
+ "div",
5961
+ {
5962
+ className: cn7(
5963
+ "grid gap-2",
5964
+ images.length === 1 ? "grid-cols-1" : images.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3",
5965
+ className
5966
+ ),
5967
+ children: images.map((image, index) => /* @__PURE__ */ jsxRuntime.jsx(
5968
+ "button",
5969
+ {
5970
+ type: "button",
5971
+ onClick: () => handleImageClick(image, index),
5972
+ 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 text-left",
5973
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5974
+ ImageWithLoading,
5975
+ {
5976
+ src: image.url,
5977
+ alt: image.alt || `Image ${index + 1}`
5978
+ }
5979
+ )
5980
+ },
5981
+ `${image.url}-${index}`
5982
+ ))
5983
+ }
5984
+ ),
5985
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
5986
+ ImageLightbox,
5987
+ {
5988
+ images,
5989
+ currentIndex: lightboxIndex,
5990
+ onClose: handleLightboxClose,
5991
+ onPrev: handleLightboxPrev,
5992
+ onNext: handleLightboxNext
5993
+ }
5994
+ )
5995
+ ] });
5996
+ }
5997
+ function cn8(...classes) {
5998
+ return classes.filter(Boolean).join(" ");
5999
+ }
5419
6000
  function MarkdownContent({
5420
6001
  content,
5421
6002
  className,
@@ -5435,11 +6016,11 @@ function MarkdownContent({
5435
6016
  h3: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mt-3 mb-1 first:mt-0", children }),
5436
6017
  h4: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-semibold mt-2 mb-1 first:mt-0", children }),
5437
6018
  // Paragraphs
5438
- p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 last:mb-0 leading-relaxed", children }),
6019
+ p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1.5 last:mb-0", children }),
5439
6020
  // 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 }),
6021
+ ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside mb-1.5 last:mb-0", children }),
6022
+ ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside mb-1.5 last:mb-0", children }),
6023
+ li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "", children }),
5443
6024
  // Links
5444
6025
  a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx(
5445
6026
  "a",
@@ -5474,7 +6055,7 @@ function MarkdownContent({
5474
6055
  if (isCodeBlock) {
5475
6056
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative my-2", children: [
5476
6057
  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 }) })
6058
+ /* @__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
6059
  ] });
5479
6060
  }
5480
6061
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -5521,7 +6102,7 @@ function MarkdownContent({
5521
6102
  () => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
5522
6103
  [defaultComponents, customComponents]
5523
6104
  );
5524
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn6("prose prose-sm dark:prose-invert max-w-none", className), children: /* @__PURE__ */ jsxRuntime.jsx(
6105
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn8("max-w-none text-sm leading-relaxed", className), children: /* @__PURE__ */ jsxRuntime.jsx(
5525
6106
  ReactMarkdown__default.default,
5526
6107
  {
5527
6108
  remarkPlugins: [remarkGfm__default.default],
@@ -5531,6 +6112,58 @@ function MarkdownContent({
5531
6112
  }
5532
6113
  ) });
5533
6114
  }
6115
+ function cn9(...classes) {
6116
+ return classes.filter(Boolean).join(" ");
6117
+ }
6118
+ function ThinkingBlock({
6119
+ block,
6120
+ defaultExpanded = false,
6121
+ className
6122
+ }) {
6123
+ const [isExpanded, setIsExpanded] = react.useState(defaultExpanded);
6124
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6125
+ "div",
6126
+ {
6127
+ className: cn9(
6128
+ "rounded-lg border",
6129
+ "border-purple-200 dark:border-purple-800",
6130
+ "bg-purple-50/50 dark:bg-purple-950/20",
6131
+ className
6132
+ ),
6133
+ children: [
6134
+ /* @__PURE__ */ jsxRuntime.jsxs(
6135
+ "button",
6136
+ {
6137
+ type: "button",
6138
+ onClick: () => setIsExpanded(!isExpanded),
6139
+ 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",
6140
+ children: [
6141
+ 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" }),
6142
+ /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "h-4 w-4 text-purple-500 flex-shrink-0" }),
6143
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-purple-700 dark:text-purple-300", children: "Thinking" })
6144
+ ]
6145
+ }
6146
+ ),
6147
+ 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 }) })
6148
+ ]
6149
+ }
6150
+ );
6151
+ }
6152
+ function ThinkingBlockList({
6153
+ blocks,
6154
+ defaultExpanded = false,
6155
+ className
6156
+ }) {
6157
+ if (!blocks || blocks.length === 0) return null;
6158
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn9("space-y-2", className), children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
6159
+ ThinkingBlock,
6160
+ {
6161
+ block,
6162
+ defaultExpanded
6163
+ },
6164
+ block.id
6165
+ )) });
6166
+ }
5534
6167
  var INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set([
5535
6168
  "update_task_list",
5536
6169
  "create_task",
@@ -5561,7 +6194,7 @@ function isInternalTool(toolName) {
5561
6194
  }
5562
6195
  return false;
5563
6196
  }
5564
- function cn7(...classes) {
6197
+ function cn10(...classes) {
5565
6198
  return classes.filter(Boolean).join(" ");
5566
6199
  }
5567
6200
  function formatToolName(toolName) {
@@ -5610,7 +6243,7 @@ function formatToolName(toolName) {
5610
6243
  }
5611
6244
  return uniqueWords.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
5612
6245
  }
5613
- function formatDuration(ms) {
6246
+ function formatDuration2(ms) {
5614
6247
  if (ms < 1e3) {
5615
6248
  return `${ms}ms`;
5616
6249
  }
@@ -5631,7 +6264,7 @@ function formatJsonPreview(data, maxLength = 100) {
5631
6264
  return String(data);
5632
6265
  }
5633
6266
  }
5634
- function extractImagesFromResult(result) {
6267
+ function extractImagesFromResult2(result) {
5635
6268
  if (!result) return [];
5636
6269
  let data = result;
5637
6270
  if (typeof result === "string") {
@@ -5675,7 +6308,7 @@ function ToolCallEntry({
5675
6308
  [toolCall.name]
5676
6309
  );
5677
6310
  const resultImages = react.useMemo(
5678
- () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult(toolCall.result) : [],
6311
+ () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult2(toolCall.result) : [],
5679
6312
  [toolCall.status, toolCall.result]
5680
6313
  );
5681
6314
  const hasImages = resultImages.length > 0;
@@ -5711,14 +6344,14 @@ function ToolCallEntry({
5711
6344
  return /* @__PURE__ */ jsxRuntime.jsxs(
5712
6345
  "div",
5713
6346
  {
5714
- className: cn7(
6347
+ className: cn10(
5715
6348
  "inline-flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400",
5716
6349
  className
5717
6350
  ),
5718
6351
  children: [
5719
6352
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: statusIcon }),
5720
6353
  /* @__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) })
6354
+ toolCall.durationMs !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500", children: formatDuration2(toolCall.durationMs) })
5722
6355
  ]
5723
6356
  }
5724
6357
  );
@@ -5726,7 +6359,7 @@ function ToolCallEntry({
5726
6359
  return /* @__PURE__ */ jsxRuntime.jsxs(
5727
6360
  "div",
5728
6361
  {
5729
- className: cn7(
6362
+ className: cn10(
5730
6363
  "rounded-lg border text-sm",
5731
6364
  statusColor,
5732
6365
  className
@@ -5742,12 +6375,12 @@ function ToolCallEntry({
5742
6375
  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
6376
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: statusIcon }),
5744
6377
  /* @__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) }),
6378
+ 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
6379
  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
6380
  ]
5748
6381
  }
5749
6382
  ),
5750
- hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn7(
6383
+ hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn10(
5751
6384
  "grid gap-2",
5752
6385
  resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
5753
6386
  ), children: resultImages.map((img, idx) => {
@@ -5806,22 +6439,28 @@ function ToolCallEntry({
5806
6439
  }
5807
6440
  );
5808
6441
  }
5809
- function cn8(...classes) {
6442
+ function cn11(...classes) {
5810
6443
  return classes.filter(Boolean).join(" ");
5811
6444
  }
5812
6445
  function ChatMessage({
5813
6446
  message,
5814
6447
  enableMarkdown = true,
5815
6448
  showToolCalls = true,
5816
- toolCallVerbosity,
6449
+ toolCallVerbosity: _toolCallVerbosity,
5817
6450
  className,
5818
6451
  markdownComponents,
5819
6452
  markdownUrlTransform,
5820
6453
  markdownContentTransform,
5821
- onImageClick
6454
+ onImageClick,
6455
+ showThinking = true,
6456
+ showDelegation = true,
6457
+ showImages = true,
6458
+ renderToolCall,
6459
+ variant = "bubble"
5822
6460
  }) {
5823
6461
  const isUser = message.role === "user";
5824
6462
  const isAssistant = message.role === "assistant";
6463
+ const isFlat = variant === "flat";
5825
6464
  const formattedTime = react.useMemo(() => {
5826
6465
  const date = message.timestamp instanceof Date ? message.timestamp : new Date(message.timestamp);
5827
6466
  return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
@@ -5829,113 +6468,124 @@ function ChatMessage({
5829
6468
  return /* @__PURE__ */ jsxRuntime.jsxs(
5830
6469
  "div",
5831
6470
  {
5832
- className: cn8(
5833
- "flex gap-3 p-4",
5834
- isUser ? "flex-row-reverse" : "flex-row",
6471
+ className: cn11(
6472
+ "cadra-chat-message flex gap-3",
6473
+ isFlat ? "py-5 px-4" : "p-4",
6474
+ // Bubble: user messages right-aligned. Flat: always left-aligned.
6475
+ !isFlat && isUser ? "flex-row-reverse" : "flex-row",
6476
+ // Flat variant: subtle row background for user messages
6477
+ isFlat && isUser && "chat-message-user-row",
5835
6478
  className
5836
6479
  ),
6480
+ style: isFlat && isUser ? {
6481
+ backgroundColor: "var(--chat-user-row-bg, rgba(128, 128, 128, 0.08))"
6482
+ } : void 0,
6483
+ "data-role": message.role,
5837
6484
  children: [
5838
6485
  /* @__PURE__ */ jsxRuntime.jsx(
5839
6486
  "div",
5840
6487
  {
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
- ),
6488
+ className: "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-sm",
6489
+ style: {
6490
+ backgroundColor: isUser ? "var(--chat-user-avatar-bg, #3b82f6)" : "var(--chat-assistant-avatar-bg, #e5e7eb)",
6491
+ color: isUser ? "var(--chat-user-avatar-text, #fff)" : "var(--chat-assistant-avatar-text, #4b5563)"
6492
+ },
5845
6493
  children: isUser ? /* @__PURE__ */ jsxRuntime.jsx(UserIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "h-4 w-4" })
5846
6494
  }
5847
6495
  ),
5848
6496
  /* @__PURE__ */ jsxRuntime.jsxs(
5849
6497
  "div",
5850
6498
  {
5851
- className: cn8(
5852
- "flex-1 min-w-0 space-y-2",
5853
- isUser ? "text-right" : "text-left"
6499
+ className: cn11(
6500
+ "flex-1 min-w-0",
6501
+ isFlat ? "pt-0.5" : "space-y-2",
6502
+ !isFlat && isUser ? "text-right" : "text-left"
5854
6503
  ),
5855
6504
  children: [
5856
6505
  /* @__PURE__ */ jsxRuntime.jsxs(
5857
6506
  "div",
5858
6507
  {
5859
- className: cn8(
5860
- "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400",
5861
- isUser ? "justify-end" : "justify-start"
6508
+ className: cn11(
6509
+ "flex items-center gap-2 text-xs",
6510
+ isFlat ? "mb-1.5" : "",
6511
+ !isFlat && isUser ? "justify-end" : "justify-start"
5862
6512
  ),
6513
+ style: { color: "var(--chat-meta-text, #6b7280)" },
5863
6514
  children: [
5864
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: isUser ? "You" : "Assistant" }),
6515
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--chat-label-text, inherit)" }, children: isUser ? "You" : "Assistant" }),
5865
6516
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedTime }),
5866
6517
  message.isStreaming && /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-3 w-3" })
5867
6518
  ]
5868
6519
  }
5869
6520
  ),
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",
5872
- {
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
- ] });
5907
- }
5908
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filteredToolCalls.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx(
6521
+ showThinking && message.thinkingBlocks && message.thinkingBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlockList, { blocks: message.thinkingBlocks }),
6522
+ showDelegation && message.delegationEvents && message.delegationEvents.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(DelegationProgressList, { events: message.delegationEvents }),
6523
+ showToolCalls && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: message.toolCalls.map((toolCall) => {
6524
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
5909
6525
  ToolCallEntry,
5910
6526
  {
5911
6527
  toolCall,
5912
6528
  onImageClick
5913
6529
  },
5914
6530
  toolCall.id
5915
- )) });
5916
- })(),
5917
- message.content && /* @__PURE__ */ jsxRuntime.jsx(
5918
- "div",
6531
+ );
6532
+ if (renderToolCall) {
6533
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6534
+ }
6535
+ return defaultRender();
6536
+ }) }),
6537
+ showImages && message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6538
+ ImageGallery,
5919
6539
  {
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 })
6540
+ images: message.images,
6541
+ onImageClick: onImageClick ? (img) => onImageClick({ url: img.url, alt: img.alt, filename: img.filename }) : void 0
5933
6542
  }
5934
6543
  ),
6544
+ message.content && (isFlat ? (
6545
+ // Flat variant: no bubble, just text with theme-aware color
6546
+ /* @__PURE__ */ jsxRuntime.jsx(
6547
+ "div",
6548
+ {
6549
+ className: cn11("text-sm break-words", !(isAssistant && enableMarkdown) && "whitespace-pre-wrap"),
6550
+ style: { color: "var(--chat-content-text, inherit)" },
6551
+ children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
6552
+ MarkdownContent,
6553
+ {
6554
+ content: message.content,
6555
+ components: markdownComponents,
6556
+ urlTransform: markdownUrlTransform,
6557
+ contentTransform: markdownContentTransform
6558
+ }
6559
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: message.content })
6560
+ }
6561
+ )
6562
+ ) : (
6563
+ // Bubble variant: colored bubble backgrounds
6564
+ /* @__PURE__ */ jsxRuntime.jsx(
6565
+ "div",
6566
+ {
6567
+ className: "rounded-lg p-3",
6568
+ style: {
6569
+ backgroundColor: isUser ? "var(--chat-user-bg, #3b82f6)" : "var(--chat-assistant-bg, #f3f4f6)",
6570
+ color: isUser ? "var(--chat-user-text, #fff)" : "var(--chat-assistant-text, #111827)"
6571
+ },
6572
+ children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
6573
+ MarkdownContent,
6574
+ {
6575
+ content: message.content,
6576
+ components: markdownComponents,
6577
+ urlTransform: markdownUrlTransform,
6578
+ contentTransform: markdownContentTransform
6579
+ }
6580
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: message.content })
6581
+ }
6582
+ )
6583
+ )),
5935
6584
  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
6585
  "div",
5937
6586
  {
5938
- className: "flex items-center gap-2 bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 text-sm",
6587
+ className: "flex items-center gap-2 rounded-lg px-3 py-2 text-sm",
6588
+ style: { backgroundColor: "var(--chat-assistant-bg, #f3f4f6)" },
5939
6589
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[150px]", children: attachment.filename })
5940
6590
  },
5941
6591
  attachment.uuid
@@ -5947,7 +6597,7 @@ function ChatMessage({
5947
6597
  }
5948
6598
  );
5949
6599
  }
5950
- function cn9(...classes) {
6600
+ function cn12(...classes) {
5951
6601
  return classes.filter(Boolean).join(" ");
5952
6602
  }
5953
6603
  function MessageList({
@@ -5965,6 +6615,12 @@ function MessageList({
5965
6615
  markdownUrlTransform,
5966
6616
  markdownContentTransform,
5967
6617
  onImageClick,
6618
+ showThinking = true,
6619
+ showDelegation = true,
6620
+ showImages = true,
6621
+ renderMessage,
6622
+ renderToolCall,
6623
+ messageVariant = "bubble",
5968
6624
  sendMessage
5969
6625
  }) {
5970
6626
  const scrollRef = react.useRef(null);
@@ -5978,7 +6634,7 @@ function MessageList({
5978
6634
  "div",
5979
6635
  {
5980
6636
  ref: scrollRef,
5981
- className: cn9(
6637
+ className: cn12(
5982
6638
  "flex-1 overflow-y-auto",
5983
6639
  className
5984
6640
  ),
@@ -5989,20 +6645,31 @@ function MessageList({
5989
6645
  ] }) }),
5990
6646
  messages.length === 0 && !isLoading && !isLoadingHistory && (renderWelcome ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: typeof renderWelcome === "function" ? renderWelcome({ sendMessage: sendMessage || (() => {
5991
6647
  }) }) : 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
- )) }),
6648
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: messages.map((message) => {
6649
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
6650
+ ChatMessage,
6651
+ {
6652
+ message,
6653
+ enableMarkdown,
6654
+ showToolCalls,
6655
+ toolCallVerbosity,
6656
+ markdownComponents,
6657
+ markdownUrlTransform,
6658
+ markdownContentTransform,
6659
+ onImageClick,
6660
+ showThinking,
6661
+ showDelegation,
6662
+ showImages,
6663
+ renderToolCall,
6664
+ variant: messageVariant
6665
+ },
6666
+ message.id
6667
+ );
6668
+ if (renderMessage) {
6669
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderMessage(message, defaultRender) }, message.id);
6670
+ }
6671
+ return defaultRender();
6672
+ }) }),
6006
6673
  isLoading && !isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 p-4", children: [
6007
6674
  /* @__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
6675
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Thinking..." })
@@ -6058,6 +6725,7 @@ function AgentChat({
6058
6725
  maxHeight = "600px",
6059
6726
  className,
6060
6727
  theme = "system",
6728
+ layout = "fixed",
6061
6729
  enableAttachments = false,
6062
6730
  enableMarkdown = true,
6063
6731
  showToolCalls = true,
@@ -6065,7 +6733,20 @@ function AgentChat({
6065
6733
  markdownComponents,
6066
6734
  markdownUrlTransform,
6067
6735
  markdownContentTransform,
6068
- onImageClick
6736
+ onImageClick,
6737
+ // Extension props
6738
+ hideHeader,
6739
+ renderAboveInput,
6740
+ renderBelowMessages,
6741
+ renderMessage,
6742
+ renderToolCall,
6743
+ messageVariant = "bubble",
6744
+ showThinking = true,
6745
+ showDelegation = true,
6746
+ showImages = true,
6747
+ pollingInterval,
6748
+ pollingEnabled,
6749
+ onDelegation
6069
6750
  }) {
6070
6751
  const {
6071
6752
  messages,
@@ -6092,7 +6773,10 @@ function AgentChat({
6092
6773
  onSessionStart,
6093
6774
  onToolCall,
6094
6775
  onToolResult,
6095
- onClarification
6776
+ onClarification,
6777
+ onDelegation,
6778
+ pollingInterval,
6779
+ pollingEnabled
6096
6780
  });
6097
6781
  const effectiveVerbosity = showToolCalls === false ? "hidden" : toolCallVerbosity ?? "compact";
6098
6782
  const dropZoneRef = react.useRef(null);
@@ -6121,51 +6805,31 @@ function AgentChat({
6121
6805
  },
6122
6806
  [enableAttachments, attachments, sendMessage]
6123
6807
  );
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
- ),
6808
+ const messageListProps = {
6809
+ messages,
6810
+ welcomeMessage: hideHeader ? void 0 : welcomeMessage,
6811
+ renderWelcome: hideHeader ? void 0 : renderWelcome,
6812
+ isLoading,
6813
+ isStreaming,
6814
+ isLoadingHistory,
6815
+ enableMarkdown,
6816
+ showToolCalls,
6817
+ toolCallVerbosity: effectiveVerbosity,
6818
+ markdownComponents,
6819
+ markdownUrlTransform,
6820
+ markdownContentTransform,
6821
+ onImageClick,
6822
+ showThinking,
6823
+ showDelegation,
6824
+ showImages,
6825
+ renderMessage,
6826
+ renderToolCall,
6827
+ messageVariant,
6828
+ sendMessage: handleSend
6829
+ };
6830
+ const chatContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6831
+ /* @__PURE__ */ jsxRuntime.jsx(MessageList, { ...messageListProps }),
6832
+ renderBelowMessages?.(),
6169
6833
  clarification && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
6170
6834
  AskUserPrompt,
6171
6835
  {
@@ -6175,6 +6839,7 @@ function AgentChat({
6175
6839
  }
6176
6840
  ) }),
6177
6841
  error && /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, onRetry: retry, onDismiss: clearError }),
6842
+ renderAboveInput?.(),
6178
6843
  /* @__PURE__ */ jsxRuntime.jsx(
6179
6844
  ChatInput,
6180
6845
  {
@@ -6191,8 +6856,17 @@ function AgentChat({
6191
6856
  }
6192
6857
  )
6193
6858
  ] });
6859
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContainer, { maxHeight, className, theme, layout, children: enableAttachments ? /* @__PURE__ */ jsxRuntime.jsx(
6860
+ FileDropZone,
6861
+ {
6862
+ ref: dropZoneRef,
6863
+ ...attachments.dropZoneProps,
6864
+ disabled: isLoading || !!clarification || isLoadingHistory,
6865
+ children: chatContent
6866
+ }
6867
+ ) : chatContent });
6194
6868
  }
6195
- function cn10(...classes) {
6869
+ function cn13(...classes) {
6196
6870
  return classes.filter(Boolean).join(" ");
6197
6871
  }
6198
6872
  function TodoList({
@@ -6221,7 +6895,7 @@ function TodoList({
6221
6895
  return /* @__PURE__ */ jsxRuntime.jsxs(
6222
6896
  "div",
6223
6897
  {
6224
- className: cn10(
6898
+ className: cn13(
6225
6899
  "rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
6226
6900
  className
6227
6901
  ),
@@ -6229,7 +6903,7 @@ function TodoList({
6229
6903
  /* @__PURE__ */ jsxRuntime.jsxs(
6230
6904
  "div",
6231
6905
  {
6232
- className: cn10(
6906
+ className: cn13(
6233
6907
  "flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50",
6234
6908
  collapsible && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
6235
6909
  ),
@@ -6260,7 +6934,7 @@ function TodoList({
6260
6934
  !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: todos.map((todo, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
6261
6935
  "li",
6262
6936
  {
6263
- className: cn10(
6937
+ className: cn13(
6264
6938
  "px-3 py-2 flex items-start gap-2 text-sm",
6265
6939
  todo.status === "in_progress" && "bg-blue-50/50 dark:bg-blue-950/20",
6266
6940
  todo.status === "completed" && "opacity-60"
@@ -6270,7 +6944,7 @@ function TodoList({
6270
6944
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
6271
6945
  "p",
6272
6946
  {
6273
- className: cn10(
6947
+ className: cn13(
6274
6948
  "text-gray-700 dark:text-gray-300",
6275
6949
  todo.status === "completed" && "line-through"
6276
6950
  ),
@@ -6287,39 +6961,6 @@ function TodoList({
6287
6961
  );
6288
6962
  }
6289
6963
 
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
6964
  exports.AgentChat = AgentChat;
6324
6965
  exports.AlertCircleIcon = AlertCircleIcon;
6325
6966
  exports.AskUserPrompt = AskUserPrompt;
@@ -6338,6 +6979,8 @@ exports.ClockIcon = ClockIcon;
6338
6979
  exports.CodeIcon = CodeIcon;
6339
6980
  exports.CopyIcon = CopyIcon;
6340
6981
  exports.DatabaseIcon = DatabaseIcon;
6982
+ exports.DelegationProgress = DelegationProgress;
6983
+ exports.DelegationProgressList = DelegationProgressList;
6341
6984
  exports.DownloadIcon = DownloadIcon;
6342
6985
  exports.ExternalLinkIcon = ExternalLinkIcon;
6343
6986
  exports.FileDropZone = FileDropZone;
@@ -6345,7 +6988,9 @@ exports.FileIcon = FileIcon;
6345
6988
  exports.FileTextIcon = FileTextIcon;
6346
6989
  exports.HelpCircleIcon = HelpCircleIcon;
6347
6990
  exports.INTERNAL_TOOL_NAMES = INTERNAL_TOOL_NAMES;
6991
+ exports.ImageGallery = ImageGallery;
6348
6992
  exports.ImageIcon = ImageIcon;
6993
+ exports.ImageLightbox = ImageLightbox;
6349
6994
  exports.ImagesIcon = ImagesIcon;
6350
6995
  exports.ListTodoIcon = ListTodoIcon;
6351
6996
  exports.LoadingSpinner = LoadingSpinner;
@@ -6358,6 +7003,8 @@ exports.PaperclipIcon = PaperclipIcon;
6358
7003
  exports.SendIcon = SendIcon;
6359
7004
  exports.SparklesIcon = SparklesIcon;
6360
7005
  exports.StopCircleIcon = StopCircleIcon;
7006
+ exports.ThinkingBlock = ThinkingBlock;
7007
+ exports.ThinkingBlockList = ThinkingBlockList;
6361
7008
  exports.TodoList = TodoList;
6362
7009
  exports.ToolCallEntry = ToolCallEntry;
6363
7010
  exports.UserIcon = UserIcon;