@copilotz/chat-adapter 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/CopilotzChat.tsx
2
- import { useMemo } from "react";
2
+ import { useMemo, useEffect as useEffect3, useState as useState3 } from "react";
3
3
  import { ChatUI, ChatUserContextProvider } from "@copilotz/chat-ui";
4
4
 
5
5
  // ../node_modules/lucide-react/dist/esm/createLucideIcon.js
@@ -99,7 +99,7 @@ var __iconNode = [
99
99
  var User = createLucideIcon("user", __iconNode);
100
100
 
101
101
  // src/useCopilotzChat.ts
102
- import { useState, useCallback, useRef, useEffect } from "react";
102
+ import { useState as useState2, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect2 } from "react";
103
103
 
104
104
  // src/copilotzService.ts
105
105
  var rawBaseValue = import.meta.env?.VITE_API_URL;
@@ -259,6 +259,7 @@ async function runCopilotzStream(options) {
259
259
  metadata,
260
260
  threadMetadata,
261
261
  toolCalls,
262
+ selectedAgent,
262
263
  onToken,
263
264
  onMessageEvent,
264
265
  onAssetEvent,
@@ -304,7 +305,7 @@ async function runCopilotzStream(options) {
304
305
  id: threadId ?? null,
305
306
  externalId: threadExternalId ?? null,
306
307
  name: threadName,
307
- participants: ["assistant"],
308
+ participants: [selectedAgent || "assistant"],
308
309
  metadata: Object.keys(restThreadMetadata).length > 0 ? restThreadMetadata : null
309
310
  } : void 0;
310
311
  const preparedAudioParts = [];
@@ -610,6 +611,110 @@ async function resolveAssetsInMessages(messages) {
610
611
  return resolved;
611
612
  }
612
613
 
614
+ // src/useUrlState.ts
615
+ import { useState, useEffect, useCallback, useRef } from "react";
616
+ var DEFAULT_PARAMS = {
617
+ thread: "thread",
618
+ agent: "agent",
619
+ prompt: "prompt"
620
+ };
621
+ var isBrowser = typeof globalThis !== "undefined" && typeof globalThis.location !== "undefined";
622
+ var getSearchParams = () => {
623
+ if (!isBrowser) return new URLSearchParams();
624
+ return new URLSearchParams(globalThis.location.search);
625
+ };
626
+ var updateUrl = (params, mode) => {
627
+ if (!isBrowser) return;
628
+ const url = new URL(globalThis.location.href);
629
+ url.search = params.toString();
630
+ if (mode === "push") {
631
+ globalThis.history.pushState({}, "", url.toString());
632
+ } else {
633
+ globalThis.history.replaceState({}, "", url.toString());
634
+ }
635
+ };
636
+ function useUrlState(config = {}) {
637
+ const {
638
+ enabled = true,
639
+ mode = "replace",
640
+ params: userParams = {},
641
+ clearPromptAfterRead = true
642
+ } = config;
643
+ const params = { ...DEFAULT_PARAMS, ...userParams };
644
+ const isReadOnly = mode === "read-only";
645
+ const updateMode = mode === "read-only" ? "replace" : mode;
646
+ const initialReadDone = useRef(false);
647
+ const promptCleared = useRef(false);
648
+ const parseUrlState = useCallback(() => {
649
+ if (!enabled || !isBrowser) {
650
+ return { threadId: null, agentId: null, prompt: null };
651
+ }
652
+ const searchParams = getSearchParams();
653
+ return {
654
+ threadId: searchParams.get(params.thread) || null,
655
+ agentId: searchParams.get(params.agent) || null,
656
+ prompt: promptCleared.current ? null : searchParams.get(params.prompt) || null
657
+ };
658
+ }, [enabled, params.thread, params.agent, params.prompt]);
659
+ const [state, setState] = useState(parseUrlState);
660
+ useEffect(() => {
661
+ if (!enabled || !isBrowser) return;
662
+ if (!initialReadDone.current) {
663
+ const initialState = parseUrlState();
664
+ setState(initialState);
665
+ initialReadDone.current = true;
666
+ if (clearPromptAfterRead && initialState.prompt && !isReadOnly) {
667
+ const searchParams = getSearchParams();
668
+ searchParams.delete(params.prompt);
669
+ updateUrl(searchParams, "replace");
670
+ promptCleared.current = true;
671
+ }
672
+ }
673
+ const handlePopState = () => {
674
+ setState(parseUrlState());
675
+ };
676
+ globalThis.addEventListener("popstate", handlePopState);
677
+ return () => globalThis.removeEventListener("popstate", handlePopState);
678
+ }, [enabled, parseUrlState, clearPromptAfterRead, params.prompt, isReadOnly]);
679
+ const setThreadId = useCallback((threadId) => {
680
+ if (!enabled || isReadOnly || !isBrowser) return;
681
+ const searchParams = getSearchParams();
682
+ if (threadId) {
683
+ searchParams.set(params.thread, threadId);
684
+ } else {
685
+ searchParams.delete(params.thread);
686
+ }
687
+ updateUrl(searchParams, updateMode);
688
+ setState((prev) => ({ ...prev, threadId }));
689
+ }, [enabled, isReadOnly, params.thread, updateMode]);
690
+ const setAgentId = useCallback((agentId) => {
691
+ if (!enabled || isReadOnly || !isBrowser) return;
692
+ const searchParams = getSearchParams();
693
+ if (agentId) {
694
+ searchParams.set(params.agent, agentId);
695
+ } else {
696
+ searchParams.delete(params.agent);
697
+ }
698
+ updateUrl(searchParams, updateMode);
699
+ setState((prev) => ({ ...prev, agentId }));
700
+ }, [enabled, isReadOnly, params.agent, updateMode]);
701
+ const clearPrompt = useCallback(() => {
702
+ if (!enabled || isReadOnly || !isBrowser) return;
703
+ const searchParams = getSearchParams();
704
+ searchParams.delete(params.prompt);
705
+ updateUrl(searchParams, "replace");
706
+ promptCleared.current = true;
707
+ setState((prev) => ({ ...prev, prompt: null }));
708
+ }, [enabled, isReadOnly, params.prompt]);
709
+ return {
710
+ state,
711
+ setThreadId,
712
+ setAgentId,
713
+ clearPrompt,
714
+ isEnabled: enabled
715
+ };
716
+ }
717
+
613
718
  // src/useCopilotzChat.ts
614
719
  var nowTs = () => Date.now();
615
720
  var generateId = () => globalThis.crypto?.randomUUID?.() ?? `id-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
@@ -667,48 +772,45 @@ var convertServerMessage = (msg) => {
667
772
  toolCalls: hasToolCalls ? mappedToolCalls : void 0
668
773
  };
669
774
  };
670
- function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onToolOutput }) {
671
- const [threads, setThreads] = useState([]);
672
- const [threadMetadataMap, setThreadMetadataMap] = useState({});
673
- const [threadExternalIdMap, setThreadExternalIdMap] = useState({});
674
- const [currentThreadId, setCurrentThreadId] = useState(null);
675
- const [currentThreadExternalId, setCurrentThreadExternalId] = useState(null);
676
- const [messages, setMessages] = useState([]);
677
- const [isStreaming, setIsStreaming] = useState(false);
678
- const [userContextSeed, setUserContextSeed] = useState(initialContext || {});
679
- const threadsRef = useRef(threads);
680
- const threadMetadataMapRef = useRef(threadMetadataMap);
681
- const threadExternalIdMapRef = useRef(threadExternalIdMap);
682
- const currentThreadIdRef = useRef(currentThreadId);
683
- const currentThreadExternalIdRef = useRef(currentThreadExternalId);
684
- const userContextSeedRef = useRef(userContextSeed);
685
- useEffect(() => {
686
- threadsRef.current = threads;
687
- }, [threads]);
688
- useEffect(() => {
689
- threadMetadataMapRef.current = threadMetadataMap;
690
- }, [threadMetadataMap]);
691
- useEffect(() => {
692
- threadExternalIdMapRef.current = threadExternalIdMap;
693
- }, [threadExternalIdMap]);
694
- useEffect(() => {
695
- currentThreadIdRef.current = currentThreadId;
696
- }, [currentThreadId]);
697
- useEffect(() => {
698
- currentThreadExternalIdRef.current = currentThreadExternalId;
699
- }, [currentThreadExternalId]);
700
- useEffect(() => {
701
- userContextSeedRef.current = userContextSeed;
702
- }, [userContextSeed]);
703
- const abortControllerRef = useRef(null);
704
- const messagesRequestRef = useRef(0);
705
- const initializationRef = useRef({ userId: null, started: false });
706
- useEffect(() => {
775
+ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onToolOutput, preferredAgentName, urlSync }) {
776
+ const {
777
+ state: urlState,
778
+ setThreadId: setUrlThreadId,
779
+ setAgentId: setUrlAgentId,
780
+ clearPrompt: clearUrlPrompt,
781
+ isEnabled: isUrlSyncEnabled
782
+ } = useUrlState(urlSync);
783
+ const [threads, setThreads] = useState2([]);
784
+ const [threadMetadataMap, setThreadMetadataMap] = useState2({});
785
+ const [threadExternalIdMap, setThreadExternalIdMap] = useState2({});
786
+ const [currentThreadId, setCurrentThreadId] = useState2(null);
787
+ const [currentThreadExternalId, setCurrentThreadExternalId] = useState2(null);
788
+ const [messages, setMessages] = useState2([]);
789
+ const [isStreaming, setIsStreaming] = useState2(false);
790
+ const [userContextSeed, setUserContextSeed] = useState2(initialContext || {});
791
+ const preferredAgentRef = useRef2(preferredAgentName ?? null);
792
+ const threadsRef = useRef2(threads);
793
+ const threadMetadataMapRef = useRef2(threadMetadataMap);
794
+ const threadExternalIdMapRef = useRef2(threadExternalIdMap);
795
+ const currentThreadIdRef = useRef2(currentThreadId);
796
+ const currentThreadExternalIdRef = useRef2(currentThreadExternalId);
797
+ const userContextSeedRef = useRef2(userContextSeed);
798
+ threadsRef.current = threads;
799
+ threadMetadataMapRef.current = threadMetadataMap;
800
+ threadExternalIdMapRef.current = threadExternalIdMap;
801
+ currentThreadIdRef.current = currentThreadId;
802
+ currentThreadExternalIdRef.current = currentThreadExternalId;
803
+ userContextSeedRef.current = userContextSeed;
804
+ preferredAgentRef.current = preferredAgentName ?? null;
805
+ const abortControllerRef = useRef2(null);
806
+ const messagesRequestRef = useRef2(0);
807
+ const initializationRef = useRef2({ userId: null, started: false });
808
+ useEffect2(() => {
707
809
  if (initialContext) {
708
810
  setUserContextSeed((prev) => ({ ...prev, ...initialContext }));
709
811
  }
710
812
  }, [initialContext]);
711
- const processToolOutput = useCallback((output) => {
813
+ const processToolOutput = useCallback2((output) => {
712
814
  if (!output) return;
713
815
  const contextPatch = {};
714
816
  if (output.userContext && typeof output.userContext === "object") {
@@ -719,7 +821,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
719
821
  }
720
822
  onToolOutput?.(output);
721
823
  }, [onToolOutput]);
722
- const handleStreamMessageEvent = useCallback((event) => {
824
+ const handleStreamMessageEvent = useCallback2((event) => {
723
825
  const payload = event?.payload;
724
826
  if (!payload) return;
725
827
  if (payload.senderType === "tool") {
@@ -776,7 +878,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
776
878
  });
777
879
  }
778
880
  }, [processToolOutput]);
779
- const updateThreadsState = useCallback((rawThreads, preferredExternalId) => {
881
+ const updateThreadsState = useCallback2((rawThreads, preferredExternalId) => {
780
882
  const metadataMap = {};
781
883
  const externalMap = {};
782
884
  const normalized = rawThreads.map((thread) => {
@@ -818,7 +920,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
818
920
  setCurrentThreadExternalId(nextThreadId ? externalMap[nextThreadId] ?? null : null);
819
921
  return nextThreadId;
820
922
  }, []);
821
- const fetchAndSetThreadsState = useCallback(async (uid, preferredExternalId) => {
923
+ const fetchAndSetThreadsState = useCallback2(async (uid, preferredExternalId) => {
822
924
  try {
823
925
  const rawThreads = await fetchThreads(uid);
824
926
  return updateThreadsState(rawThreads, preferredExternalId);
@@ -828,7 +930,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
828
930
  return null;
829
931
  }
830
932
  }, [updateThreadsState]);
831
- const loadThreadMessages = useCallback(async (threadId) => {
933
+ const loadThreadMessages = useCallback2(async (threadId) => {
832
934
  const requestId = Date.now();
833
935
  messagesRequestRef.current = requestId;
834
936
  try {
@@ -859,13 +961,13 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
859
961
  console.error(`Error loading messages for thread ${threadId}`, error);
860
962
  }
861
963
  }, [processToolOutput]);
862
- const handleSelectThread = useCallback(async (threadId) => {
964
+ const handleSelectThread = useCallback2(async (threadId) => {
863
965
  setCurrentThreadId(threadId);
864
966
  const extMap = threadExternalIdMapRef.current;
865
967
  setCurrentThreadExternalId(extMap[threadId] ?? null);
866
968
  await loadThreadMessages(threadId);
867
969
  }, [loadThreadMessages]);
868
- const handleCreateThread = useCallback((title) => {
970
+ const handleCreateThread = useCallback2((title) => {
869
971
  const id = generateId();
870
972
  const now = nowTs();
871
973
  const newThread = {
@@ -883,7 +985,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
883
985
  setCurrentThreadExternalId(id);
884
986
  setMessages([]);
885
987
  }, []);
886
- const handleRenameThread = useCallback(async (threadId, newTitle) => {
988
+ const handleRenameThread = useCallback2(async (threadId, newTitle) => {
887
989
  const trimmedTitle = newTitle.trim();
888
990
  if (!trimmedTitle) return;
889
991
  setThreads(
@@ -907,7 +1009,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
907
1009
  }
908
1010
  }
909
1011
  }, [userId, fetchAndSetThreadsState]);
910
- const handleArchiveThread = useCallback(async (threadId) => {
1012
+ const handleArchiveThread = useCallback2(async (threadId) => {
911
1013
  const thread = threadsRef.current.find((t) => t.id === threadId);
912
1014
  if (!thread) return;
913
1015
  const newArchivedStatus = !thread.isArchived;
@@ -927,7 +1029,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
927
1029
  }
928
1030
  }
929
1031
  }, [userId, fetchAndSetThreadsState]);
930
- const handleDeleteThread = useCallback(async (threadId) => {
1032
+ const handleDeleteThread = useCallback2(async (threadId) => {
931
1033
  const extMap = threadExternalIdMapRef.current;
932
1034
  const isPlaceholder = extMap[threadId] === threadId;
933
1035
  setThreads((prev) => prev.filter((t) => t.id !== threadId));
@@ -964,13 +1066,17 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
964
1066
  }
965
1067
  }
966
1068
  }, [userId, fetchAndSetThreadsState, loadThreadMessages]);
967
- const handleStop = useCallback(() => {
1069
+ const handleStop = useCallback2(() => {
968
1070
  abortControllerRef.current?.abort();
969
1071
  abortControllerRef.current = null;
970
1072
  setIsStreaming(false);
971
- setMessages((prev) => prev.map((msg) => msg.isStreaming ? { ...msg, isStreaming: false, isComplete: true } : msg));
1073
+ setMessages((prev) => {
1074
+ const hasStreaming = prev.some((msg) => msg.isStreaming);
1075
+ if (!hasStreaming) return prev;
1076
+ return prev.map((msg) => msg.isStreaming ? { ...msg, isStreaming: false, isComplete: true } : msg);
1077
+ });
972
1078
  }, []);
973
- const handleStreamAssetEvent = useCallback((payload, assistantMessageId) => {
1079
+ const handleStreamAssetEvent = useCallback2((payload, assistantMessageId) => {
974
1080
  if (!payload?.dataUrl) return;
975
1081
  const mimeType = payload.mime || "image/png";
976
1082
  const dataUrl = payload.dataUrl;
@@ -992,22 +1098,36 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
992
1098
  isComplete: true
993
1099
  } : msg));
994
1100
  }, []);
995
- const sendCopilotzMessage = useCallback(async (params) => {
1101
+ const sendCopilotzMessage = useCallback2(async (params) => {
996
1102
  let currentAssistantId = generateId();
997
1103
  params.onBeforeStart?.(currentAssistantId);
998
1104
  let hasStreamProgress = false;
999
1105
  let pendingStartNewAssistantBubble = false;
1000
- const ensureStreamingBubble = () => {
1106
+ const updateStreamingMessage = (partial, isComplete) => {
1107
+ if (partial && partial.length > 0) {
1108
+ hasStreamProgress = true;
1109
+ }
1001
1110
  setMessages((prev) => {
1002
1111
  const idx = prev.findIndex((m) => m.id === currentAssistantId);
1003
- if (idx >= 0 && prev[idx].role === "assistant" && prev[idx].isStreaming) {
1004
- return prev;
1112
+ if (idx >= 0 && prev[idx].role === "assistant") {
1113
+ const msg = prev[idx];
1114
+ if (msg.content === partial && msg.isStreaming === !isComplete && msg.isComplete === isComplete) {
1115
+ return prev;
1116
+ }
1117
+ const updated = [...prev];
1118
+ updated[idx] = { ...msg, content: partial, isStreaming: !isComplete, isComplete };
1119
+ return updated;
1005
1120
  }
1006
1121
  const last = prev[prev.length - 1];
1007
1122
  if (last && last.role === "assistant" && last.isStreaming) {
1008
1123
  currentAssistantId = last.id;
1009
1124
  pendingStartNewAssistantBubble = false;
1010
- return prev;
1125
+ if (last.content === partial && last.isStreaming === !isComplete && last.isComplete === isComplete) {
1126
+ return prev;
1127
+ }
1128
+ const updated = [...prev];
1129
+ updated[prev.length - 1] = { ...last, content: partial, isStreaming: !isComplete, isComplete };
1130
+ return updated;
1011
1131
  }
1012
1132
  if (pendingStartNewAssistantBubble || !prev.length || (prev[prev.length - 1].role !== "assistant" || !prev[prev.length - 1].isStreaming)) {
1013
1133
  const newId = generateId();
@@ -1018,25 +1138,26 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1018
1138
  {
1019
1139
  id: newId,
1020
1140
  role: "assistant",
1021
- content: "",
1141
+ content: partial,
1022
1142
  timestamp: nowTs(),
1023
- isStreaming: true,
1024
- isComplete: false
1143
+ isStreaming: !isComplete,
1144
+ isComplete
1025
1145
  }
1026
1146
  ];
1027
1147
  }
1028
1148
  return prev;
1029
1149
  });
1030
1150
  };
1031
- const updateStreamingMessage = (partial, isComplete) => {
1032
- if (partial && partial.length > 0) {
1033
- hasStreamProgress = true;
1034
- }
1035
- ensureStreamingBubble();
1036
- setMessages((prev) => prev.map((msg) => msg.id === currentAssistantId ? { ...msg, content: partial, isStreaming: !isComplete, isComplete } : msg));
1037
- };
1038
1151
  const finalizeCurrentAssistantBubble = () => {
1039
- setMessages((prev) => prev.map((msg) => msg.id === currentAssistantId ? { ...msg, isStreaming: false, isComplete: true } : msg));
1152
+ setMessages((prev) => {
1153
+ const idx = prev.findIndex((m) => m.id === currentAssistantId);
1154
+ if (idx < 0) return prev;
1155
+ const msg = prev[idx];
1156
+ if (!msg.isStreaming && msg.isComplete) return prev;
1157
+ const updated = [...prev];
1158
+ updated[idx] = { ...msg, isStreaming: false, isComplete: true };
1159
+ return updated;
1160
+ });
1040
1161
  };
1041
1162
  const curThreadId = currentThreadIdRef.current;
1042
1163
  const toServerMessageFromEvent = async (event) => {
@@ -1139,6 +1260,11 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1139
1260
  const currentThreadMetadataMap = threadMetadataMapRef.current;
1140
1261
  const messageMetadata = metadataKey ? currentThreadMetadataMap[metadataKey]?.userContext : void 0;
1141
1262
  const threadMetadata = metadataKey ? currentThreadMetadataMap[metadataKey] : void 0;
1263
+ const mergedMetadata = {
1264
+ ...messageMetadata ?? {},
1265
+ ...params.metadata ?? {}
1266
+ };
1267
+ const finalMetadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
1142
1268
  await runCopilotzStream({
1143
1269
  threadId: params.threadId ?? void 0,
1144
1270
  threadExternalId: params.threadExternalId ?? void 0,
@@ -1152,9 +1278,10 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1152
1278
  }
1153
1279
  },
1154
1280
  attachments: params.attachments,
1155
- metadata: params.metadata ?? messageMetadata,
1281
+ metadata: finalMetadata,
1156
1282
  threadMetadata: params.threadMetadata ?? threadMetadata,
1157
1283
  toolCalls: params.toolCalls,
1284
+ selectedAgent: params.agentName ?? preferredAgentRef.current ?? null,
1158
1285
  onToken: (token, isComplete) => updateStreamingMessage(token, isComplete),
1159
1286
  onMessageEvent: async (event) => {
1160
1287
  const type = event?.type || "";
@@ -1289,7 +1416,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1289
1416
  }
1290
1417
  return currentAssistantId;
1291
1418
  }, [handleStreamMessageEvent, handleStreamAssetEvent]);
1292
- const handleSendMessage = useCallback(async (content, attachments = []) => {
1419
+ const handleSendMessage = useCallback2(async (content, attachments = []) => {
1293
1420
  if (!content.trim() && attachments.length === 0) return;
1294
1421
  if (!userId) return;
1295
1422
  const timestamp = nowTs();
@@ -1349,6 +1476,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1349
1476
  userId,
1350
1477
  // userName can be anything, but let's try to find it in context or just fallback
1351
1478
  userName: userContextSeedRef.current?.profile?.full_name ?? userId,
1479
+ agentName: preferredAgentRef.current,
1352
1480
  // Include pending title for new threads
1353
1481
  threadMetadata: pendingTitle ? { name: pendingTitle } : void 0
1354
1482
  });
@@ -1365,7 +1493,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1365
1493
  } : msg));
1366
1494
  }
1367
1495
  }, [userId, fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage]);
1368
- const bootstrapConversation = useCallback(async (uid) => {
1496
+ const bootstrapConversation = useCallback2(async (uid) => {
1369
1497
  if (!bootstrap?.initialToolCalls && !bootstrap?.initialMessage) return;
1370
1498
  const bootstrapThreadExternalId = generateId();
1371
1499
  setCurrentThreadId(bootstrapThreadExternalId);
@@ -1379,6 +1507,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1379
1507
  content: bootstrap.initialMessage || "",
1380
1508
  toolCalls: bootstrap.initialToolCalls,
1381
1509
  userId: uid,
1510
+ agentName: preferredAgentRef.current,
1382
1511
  threadMetadata: {
1383
1512
  name: defaultThreadName || "Main Thread"
1384
1513
  }
@@ -1400,7 +1529,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1400
1529
  ]);
1401
1530
  }
1402
1531
  }, [fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage, bootstrap, defaultThreadName]);
1403
- const reset = useCallback(() => {
1532
+ const reset = useCallback2(() => {
1404
1533
  setThreads([]);
1405
1534
  setThreadMetadataMap({});
1406
1535
  setThreadExternalIdMap({});
@@ -1411,14 +1540,15 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1411
1540
  setIsStreaming(false);
1412
1541
  abortControllerRef.current?.abort();
1413
1542
  }, []);
1414
- useEffect(() => {
1543
+ useEffect2(() => {
1415
1544
  if (userId) {
1416
1545
  if (initializationRef.current.userId === userId && initializationRef.current.started) {
1417
1546
  return;
1418
1547
  }
1419
1548
  initializationRef.current = { userId, started: true };
1420
1549
  const init = async () => {
1421
- const preferredThreadId = await fetchAndSetThreadsState(userId, void 0);
1550
+ const urlPreferredThread = isUrlSyncEnabled ? urlState.threadId : void 0;
1551
+ const preferredThreadId = await fetchAndSetThreadsState(userId, urlPreferredThread);
1422
1552
  if (preferredThreadId) {
1423
1553
  await loadThreadMessages(preferredThreadId);
1424
1554
  } else if (bootstrap) {
@@ -1430,8 +1560,13 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1430
1560
  initializationRef.current = { userId: null, started: false };
1431
1561
  reset();
1432
1562
  }
1433
- }, [userId, fetchAndSetThreadsState, loadThreadMessages, bootstrapConversation, reset, bootstrap]);
1434
- useEffect(() => {
1563
+ }, [userId, fetchAndSetThreadsState, loadThreadMessages, bootstrapConversation, reset, bootstrap, isUrlSyncEnabled, urlState.threadId]);
1564
+ useEffect2(() => {
1565
+ if (!isUrlSyncEnabled) return;
1566
+ if (!initializationRef.current.started) return;
1567
+ setUrlThreadId(currentThreadExternalId);
1568
+ }, [currentThreadExternalId, isUrlSyncEnabled, setUrlThreadId]);
1569
+ useEffect2(() => {
1435
1570
  if (!currentThreadId) return;
1436
1571
  const metadata = threadMetadataMap[currentThreadId];
1437
1572
  if (!metadata) return;
@@ -1454,7 +1589,16 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1454
1589
  stopGeneration: handleStop,
1455
1590
  fetchAndSetThreadsState,
1456
1591
  loadThreadMessages,
1457
- reset
1592
+ reset,
1593
+ // URL state
1594
+ /** Initial prompt from URL (if urlSync enabled) - use for pre-filling input */
1595
+ initialPrompt: isUrlSyncEnabled ? urlState.prompt : null,
1596
+ /** Clear the initial prompt from URL (call after consuming it) */
1597
+ clearInitialPrompt: clearUrlPrompt,
1598
+ /** URL agent ID (if urlSync enabled) - use for agent pre-selection */
1599
+ urlAgentId: isUrlSyncEnabled ? urlState.agentId : null,
1600
+ /** Update agent ID in URL */
1601
+ setUrlAgentId
1458
1602
  };
1459
1603
  }
1460
1604
 
@@ -1476,8 +1620,14 @@ var CopilotzChat = ({
1476
1620
  onAddMemory,
1477
1621
  onUpdateMemory,
1478
1622
  onDeleteMemory,
1479
- className
1623
+ suggestions,
1624
+ agentOptions = [],
1625
+ selectedAgentId = null,
1626
+ onSelectAgent,
1627
+ className,
1628
+ urlSync
1480
1629
  }) => {
1630
+ const selectedAgent = agentOptions.find((agent) => agent.id === selectedAgentId) || null;
1481
1631
  const {
1482
1632
  messages,
1483
1633
  threads,
@@ -1490,14 +1640,44 @@ var CopilotzChat = ({
1490
1640
  renameThread,
1491
1641
  archiveThread,
1492
1642
  deleteThread: deleteThread2,
1493
- stopGeneration
1643
+ stopGeneration,
1644
+ initialPrompt,
1645
+ clearInitialPrompt,
1646
+ urlAgentId,
1647
+ setUrlAgentId
1494
1648
  } = useCopilotz({
1495
1649
  userId,
1496
1650
  initialContext,
1497
1651
  bootstrap,
1498
1652
  defaultThreadName: userConfig?.labels?.defaultThreadName,
1499
- onToolOutput
1653
+ onToolOutput,
1654
+ preferredAgentName: selectedAgent?.name ?? null,
1655
+ urlSync
1500
1656
  });
1657
+ const [promptHandled, setPromptHandled] = useState3(false);
1658
+ useEffect3(() => {
1659
+ if (urlAgentId && onSelectAgent && urlAgentId !== selectedAgentId) {
1660
+ const agentExists = agentOptions.some((a) => a.id === urlAgentId);
1661
+ if (agentExists) {
1662
+ onSelectAgent(urlAgentId);
1663
+ }
1664
+ }
1665
+ }, [urlAgentId, selectedAgentId, onSelectAgent, agentOptions]);
1666
+ useEffect3(() => {
1667
+ if (selectedAgentId && urlSync?.enabled) {
1668
+ setUrlAgentId(selectedAgentId);
1669
+ }
1670
+ }, [selectedAgentId, urlSync?.enabled, setUrlAgentId]);
1671
+ useEffect3(() => {
1672
+ if (initialPrompt && !promptHandled && urlSync?.promptBehavior === "auto-send") {
1673
+ const timer = setTimeout(() => {
1674
+ void sendMessage(initialPrompt);
1675
+ clearInitialPrompt();
1676
+ setPromptHandled(true);
1677
+ }, 500);
1678
+ return () => clearTimeout(timer);
1679
+ }
1680
+ }, [initialPrompt, promptHandled, urlSync?.promptBehavior, sendMessage, clearInitialPrompt]);
1501
1681
  const chatCallbacks = useMemo(() => ({
1502
1682
  onSendMessage: (content, attachments) => {
1503
1683
  void sendMessage(content, attachments);
@@ -1565,6 +1745,10 @@ var CopilotzChat = ({
1565
1745
  config: mergedConfig,
1566
1746
  callbacks: chatCallbacks,
1567
1747
  isGenerating: isStreaming,
1748
+ suggestions,
1749
+ agentOptions,
1750
+ selectedAgentId,
1751
+ onSelectAgent,
1568
1752
  user: {
1569
1753
  id: userId,
1570
1754
  name: effectiveUserName,
@@ -1579,7 +1763,12 @@ var CopilotzChat = ({
1579
1763
  onAddMemory,
1580
1764
  onUpdateMemory,
1581
1765
  onDeleteMemory,
1582
- className
1766
+ className,
1767
+ initialInput: initialPrompt && !promptHandled && urlSync?.promptBehavior !== "auto-send" ? initialPrompt : void 0,
1768
+ onInitialInputConsumed: () => {
1769
+ clearInitialPrompt();
1770
+ setPromptHandled(true);
1771
+ }
1583
1772
  }
1584
1773
  ) });
1585
1774
  };
@@ -1594,7 +1783,8 @@ export {
1594
1783
  resolveAssetsInMessages,
1595
1784
  runCopilotzStream,
1596
1785
  updateThread,
1597
- useCopilotz
1786
+ useCopilotz,
1787
+ useUrlState
1598
1788
  };
1599
1789
  /*! Bundled license information:
1600
1790