@yourgpt/copilot-sdk 2.1.5-alpha.5 → 2.1.5-alpha.6

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkKGYDGK3U_cjs = require('./chunk-KGYDGK3U.cjs');
3
+ var chunkVION33GW_cjs = require('./chunk-VION33GW.cjs');
4
4
  var chunkJGPDQDY4_cjs = require('./chunk-JGPDQDY4.cjs');
5
5
  var chunkBJYA5NDL_cjs = require('./chunk-BJYA5NDL.cjs');
6
6
  var React2 = require('react');
@@ -1485,6 +1485,8 @@ var ChatContextOptimizer = class {
1485
1485
  var AbstractChat = class {
1486
1486
  constructor(init) {
1487
1487
  this.lastContextUsage = null;
1488
+ this.sessionInitPromise = null;
1489
+ this.sessionStatus = "idle";
1488
1490
  // Event handlers
1489
1491
  this.eventHandlers = /* @__PURE__ */ new Map();
1490
1492
  // Current streaming state
@@ -1513,7 +1515,8 @@ var AbstractChat = class {
1513
1515
  body: init.body,
1514
1516
  threadId: init.threadId,
1515
1517
  debug: init.debug,
1516
- optimization: init.optimization
1518
+ optimization: init.optimization,
1519
+ yourgptConfig: init.yourgptConfig
1517
1520
  };
1518
1521
  this.state = init.state ?? new SimpleChatState();
1519
1522
  this.transport = init.transport ?? new HttpTransport({
@@ -1524,6 +1527,7 @@ var AbstractChat = class {
1524
1527
  });
1525
1528
  this.callbacks = init.callbacks ?? {};
1526
1529
  this.optimizer = new ChatContextOptimizer(init.optimization);
1530
+ this.onCreateSession = init.onCreateSession;
1527
1531
  if (init.initialMessages?.length) {
1528
1532
  this.state.setMessages(init.initialMessages);
1529
1533
  }
@@ -1602,6 +1606,23 @@ var AbstractChat = class {
1602
1606
  }
1603
1607
  this.callbacks.onMessagesChange?.(this._allMessages());
1604
1608
  this.callbacks.onStatusChange?.("submitted");
1609
+ const needsSession = !this.config.threadId && (!!this.config.yourgptConfig || !!this.onCreateSession);
1610
+ if (needsSession) {
1611
+ if (!this.sessionInitPromise) {
1612
+ this.setSessionStatus("creating");
1613
+ this.sessionInitPromise = (this.onCreateSession ? Promise.resolve(this.onCreateSession()) : this._defaultCreateSession()).then((id) => {
1614
+ this.callbacks.onThreadChange?.(id);
1615
+ this.setSessionStatus("ready");
1616
+ return id;
1617
+ }).catch((err) => {
1618
+ this.setSessionStatus("error");
1619
+ this.sessionInitPromise = null;
1620
+ throw err;
1621
+ });
1622
+ }
1623
+ this.config.threadId = await this.sessionInitPromise;
1624
+ this.debug("sendMessage", { threadId: this.config.threadId });
1625
+ }
1605
1626
  await Promise.resolve();
1606
1627
  await this.processRequest({ preCreatedMessageId });
1607
1628
  return true;
@@ -1941,6 +1962,42 @@ var AbstractChat = class {
1941
1962
  setContext(context) {
1942
1963
  this.dynamicContext = context;
1943
1964
  }
1965
+ /**
1966
+ * Switch to a different thread (or start a new one).
1967
+ *
1968
+ * - Pass the session/thread ID from persistence → used as-is, no session creation call.
1969
+ * - Pass null → threadId cleared, new session created on first sendMessage.
1970
+ *
1971
+ * The session ID IS the thread ID: whatever is stored in persistence is passed here directly.
1972
+ */
1973
+ setActiveThread(id) {
1974
+ this.config.threadId = id ?? void 0;
1975
+ this.sessionInitPromise = null;
1976
+ this.setSessionStatus(id ? "ready" : "idle");
1977
+ }
1978
+ /**
1979
+ * Force a new session to be created on the next sendMessage.
1980
+ * Call this when the current session has expired or credits are exhausted.
1981
+ * After calling this, the next sendMessage will invoke onCreateSession/yourgptConfig
1982
+ * again and onThreadChange will fire with the new session ID.
1983
+ */
1984
+ renewSession() {
1985
+ this.config.threadId = void 0;
1986
+ this.sessionInitPromise = null;
1987
+ this.setSessionStatus("idle");
1988
+ }
1989
+ setSessionStatus(status) {
1990
+ if (this.sessionStatus !== status) {
1991
+ this.sessionStatus = status;
1992
+ this.callbacks.onSessionStatusChange?.(status);
1993
+ }
1994
+ }
1995
+ /**
1996
+ * Get the current session creation status.
1997
+ */
1998
+ getSessionStatus() {
1999
+ return this.sessionStatus;
2000
+ }
1944
2001
  /**
1945
2002
  * Set system prompt dynamically
1946
2003
  * This allows updating the system prompt after initialization
@@ -1978,9 +2035,98 @@ var AbstractChat = class {
1978
2035
  this.transport.setBody(body);
1979
2036
  }
1980
2037
  }
2038
+ /**
2039
+ * Default session creation via yourgptConfig.
2040
+ * Only called when yourgptConfig is set and no onCreateSession is provided.
2041
+ * The returned session_uid IS the thread ID going forward.
2042
+ */
2043
+ async _defaultCreateSession() {
2044
+ if (!this.config.yourgptConfig) {
2045
+ throw new Error(
2046
+ "[copilot-sdk] _defaultCreateSession called without yourgptConfig"
2047
+ );
2048
+ }
2049
+ const {
2050
+ apiKey,
2051
+ widgetUid,
2052
+ endpoint = "https://api.yourgpt.ai"
2053
+ } = this.config.yourgptConfig;
2054
+ const base = endpoint.replace(/\/$/, "");
2055
+ const controller = new AbortController();
2056
+ const timeout = setTimeout(() => controller.abort(), 1e4);
2057
+ try {
2058
+ const res = await fetch(`${base}/chatbot/v1/copilot-sdk/createSession`, {
2059
+ method: "POST",
2060
+ headers: {
2061
+ "Content-Type": "application/json",
2062
+ "api-key": apiKey
2063
+ },
2064
+ body: JSON.stringify({ widget_uid: widgetUid }),
2065
+ signal: controller.signal
2066
+ });
2067
+ const data = await res.json();
2068
+ const d = data?.data ?? data;
2069
+ const id = d?.session_uid ?? d?.id;
2070
+ if (!id)
2071
+ throw new Error(
2072
+ "[copilot-sdk] createSession failed: " + JSON.stringify(data)
2073
+ );
2074
+ return String(id);
2075
+ } catch (err) {
2076
+ if (err?.name === "AbortError") {
2077
+ throw new Error(
2078
+ "[copilot-sdk] createSession timed out after 10s \u2014 check your endpoint/network"
2079
+ );
2080
+ }
2081
+ throw err;
2082
+ } finally {
2083
+ clearTimeout(timeout);
2084
+ }
2085
+ }
1981
2086
  /**
1982
2087
  * Build the request payload
1983
2088
  */
2089
+ /** Inline text-file attachments into message content for the LLM */
2090
+ inlineTextAttachments(messages) {
2091
+ const textMimeTypes = /* @__PURE__ */ new Set([
2092
+ "text/csv",
2093
+ "text/plain",
2094
+ "text/markdown",
2095
+ "text/x-markdown",
2096
+ "application/json",
2097
+ "application/csv"
2098
+ ]);
2099
+ const textExts = /* @__PURE__ */ new Set(["csv", "txt", "md", "json"]);
2100
+ return messages.map((msg) => {
2101
+ if (msg.role !== "user" || !msg.attachments?.length) return msg;
2102
+ const textFiles = [];
2103
+ const binaryFiles = [];
2104
+ for (const att of msg.attachments) {
2105
+ const ext = att.filename?.toLowerCase().split(".").pop();
2106
+ const isText = textMimeTypes.has(att.mimeType) || ext && textExts.has(ext);
2107
+ if (isText && att.data && !att.url) {
2108
+ textFiles.push(att);
2109
+ } else {
2110
+ binaryFiles.push(att);
2111
+ }
2112
+ }
2113
+ if (textFiles.length === 0) return msg;
2114
+ const fileParts = textFiles.map((f) => {
2115
+ const ext = f.filename?.split(".").pop()?.toLowerCase() || "txt";
2116
+ return `
2117
+
2118
+ --- ${f.filename || "file"} ---
2119
+ \`\`\`${ext}
2120
+ ${f.data}
2121
+ \`\`\``;
2122
+ });
2123
+ return {
2124
+ ...msg,
2125
+ content: (msg.content || "") + fileParts.join(""),
2126
+ attachments: binaryFiles.length > 0 ? binaryFiles : void 0
2127
+ };
2128
+ });
2129
+ }
1984
2130
  buildRequest() {
1985
2131
  const systemPrompt = this.dynamicContext ? `${this.config.systemPrompt || ""}
1986
2132
 
@@ -1989,8 +2135,9 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
1989
2135
  const rawMessages = this.requestMessageTransform ? this.requestMessageTransform(
1990
2136
  this.state.messages
1991
2137
  ) : this.state.messages;
2138
+ const processedMessages = this.inlineTextAttachments(rawMessages);
1992
2139
  const optimized = this.optimizer.prepare({
1993
- messages: rawMessages,
2140
+ messages: processedMessages,
1994
2141
  tools: this.config.tools,
1995
2142
  systemPrompt
1996
2143
  });
@@ -2253,6 +2400,15 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
2253
2400
  toolCallsEmitted,
2254
2401
  chunkCount
2255
2402
  });
2403
+ if (chunk.type === "done" && chunk.threadId) {
2404
+ const serverThreadId = chunk.threadId;
2405
+ if (!this.config.threadId || this.config.threadId !== serverThreadId) {
2406
+ this.config.threadId = serverThreadId;
2407
+ this.sessionInitPromise = null;
2408
+ this.setSessionStatus("ready");
2409
+ this.callbacks.onThreadChange?.(serverThreadId);
2410
+ }
2411
+ }
2256
2412
  if (chunk.type === "done" && chunk.messages?.length) {
2257
2413
  this.debug("processDoneMessages", {
2258
2414
  count: chunk.messages.length,
@@ -2414,6 +2570,12 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
2414
2570
  * Handle JSON (non-streaming) response
2415
2571
  */
2416
2572
  handleJsonResponse(response) {
2573
+ if (response.threadId && (!this.config.threadId || this.config.threadId !== response.threadId)) {
2574
+ this.config.threadId = response.threadId;
2575
+ this.sessionInitPromise = null;
2576
+ this.setSessionStatus("ready");
2577
+ this.callbacks.onThreadChange?.(response.threadId);
2578
+ }
2417
2579
  const toolCallHiddenMap = /* @__PURE__ */ new Map();
2418
2580
  if (response.toolCalls) {
2419
2581
  for (const tc of response.toolCalls) {
@@ -2478,7 +2640,7 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
2478
2640
  }
2479
2641
  get log() {
2480
2642
  if (!this._log) {
2481
- this._log = chunkKGYDGK3U_cjs.createLogger("streaming", () => this.config.debug ?? false);
2643
+ this._log = chunkVION33GW_cjs.createLogger("streaming", () => this.config.debug ?? false);
2482
2644
  }
2483
2645
  return this._log;
2484
2646
  }
@@ -3337,6 +3499,8 @@ var ChatWithTools = class {
3337
3499
  body: config.body,
3338
3500
  optimization: config.optimization,
3339
3501
  threadId: config.threadId,
3502
+ onCreateSession: config.onCreateSession,
3503
+ yourgptConfig: config.yourgptConfig,
3340
3504
  debug: config.debug,
3341
3505
  initialMessages: config.initialMessages,
3342
3506
  state: config.state,
@@ -3351,6 +3515,8 @@ var ChatWithTools = class {
3351
3515
  onToolCalls: callbacks.onToolCalls,
3352
3516
  onFinish: callbacks.onFinish,
3353
3517
  onContextUsageChange: callbacks.onContextUsageChange,
3518
+ onThreadChange: callbacks.onThreadChange,
3519
+ onSessionStatusChange: callbacks.onSessionStatusChange,
3354
3520
  // Server-side tool callbacks - track in agentLoop for UI display
3355
3521
  // IMPORTANT: Only track tools that are NOT registered client-side
3356
3522
  // Client-side tools are tracked via executeToolCalls() path
@@ -3590,6 +3756,26 @@ var ChatWithTools = class {
3590
3756
  setContext(context) {
3591
3757
  this.chat.setContext(context);
3592
3758
  }
3759
+ /**
3760
+ * Switch to a different thread (or start a new one).
3761
+ * Pass the session/thread ID from persistence to reuse it, or null to start fresh.
3762
+ */
3763
+ setActiveThread(id) {
3764
+ this.chat.setActiveThread(id);
3765
+ }
3766
+ /**
3767
+ * Force a new session on the next sendMessage.
3768
+ * Call this when the current session has expired or credits are exhausted.
3769
+ */
3770
+ renewSession() {
3771
+ this.chat.renewSession();
3772
+ }
3773
+ /**
3774
+ * Current session creation status.
3775
+ */
3776
+ getSessionStatus() {
3777
+ return this.chat.getSessionStatus();
3778
+ }
3593
3779
  /**
3594
3780
  * Set system prompt dynamically
3595
3781
  */
@@ -3702,7 +3888,7 @@ var ChatWithTools = class {
3702
3888
  // Private
3703
3889
  // ============================================
3704
3890
  debug(message, ...args) {
3705
- chunkKGYDGK3U_cjs.createLogger("tools", () => this.config.debug ?? false)(
3891
+ chunkVION33GW_cjs.createLogger("tools", () => this.config.debug ?? false)(
3706
3892
  message,
3707
3893
  args.length === 1 ? args[0] : args.length > 1 ? args : void 0
3708
3894
  );
@@ -4461,9 +4647,6 @@ ${lines}` : lines;
4461
4647
  }
4462
4648
 
4463
4649
  // src/react/message-history/session-persistence.ts
4464
- var IDB_DB_NAME = "copilot-sdk";
4465
- var IDB_STORE = "sessions";
4466
- var IDB_VERSION = 1;
4467
4650
  function saveCompactionState(storageKey, state) {
4468
4651
  try {
4469
4652
  localStorage.setItem(
@@ -4490,64 +4673,34 @@ function clearCompactionState(storageKey) {
4490
4673
  } catch {
4491
4674
  }
4492
4675
  }
4493
- function openDB() {
4494
- return new Promise((resolve, reject) => {
4495
- const req = indexedDB.open(IDB_DB_NAME, IDB_VERSION);
4496
- req.onupgradeneeded = () => {
4497
- req.result.createObjectStore(IDB_STORE, { keyPath: "sessionId" });
4498
- };
4499
- req.onsuccess = () => resolve(req.result);
4500
- req.onerror = () => reject(req.error);
4501
- });
4502
- }
4503
- async function saveDisplayMessages(storageKey, messages) {
4676
+ function saveDisplayMessages(storageKey, messages) {
4504
4677
  try {
4505
- const db = await openDB();
4506
- const tx = db.transaction(IDB_STORE, "readwrite");
4507
- tx.objectStore(IDB_STORE).put({
4508
- sessionId: storageKey,
4509
- messages,
4510
- savedAt: Date.now()
4511
- });
4512
- await new Promise((res, rej) => {
4513
- tx.oncomplete = () => res();
4514
- tx.onerror = () => rej(tx.error);
4515
- });
4516
- db.close();
4678
+ localStorage.setItem(
4679
+ `${storageKey}-messages`,
4680
+ JSON.stringify({ messages, savedAt: Date.now() })
4681
+ );
4517
4682
  } catch {
4518
4683
  }
4519
4684
  }
4520
- async function loadDisplayMessages(storageKey) {
4685
+ function loadDisplayMessages(storageKey) {
4521
4686
  try {
4522
- const db = await openDB();
4523
- const tx = db.transaction(IDB_STORE, "readonly");
4524
- const req = tx.objectStore(IDB_STORE).get(storageKey);
4525
- const result = await new Promise((res, rej) => {
4526
- req.onsuccess = () => res(req.result);
4527
- req.onerror = () => rej(req.error);
4528
- });
4529
- db.close();
4530
- return result?.messages ?? null;
4687
+ const raw = localStorage.getItem(`${storageKey}-messages`);
4688
+ if (!raw) return null;
4689
+ const parsed = JSON.parse(raw);
4690
+ return parsed?.messages ?? null;
4531
4691
  } catch {
4532
4692
  return null;
4533
4693
  }
4534
4694
  }
4535
- async function clearDisplayMessages(storageKey) {
4695
+ function clearDisplayMessages(storageKey) {
4536
4696
  try {
4537
- const db = await openDB();
4538
- const tx = db.transaction(IDB_STORE, "readwrite");
4539
- tx.objectStore(IDB_STORE).delete(storageKey);
4540
- await new Promise((res, rej) => {
4541
- tx.oncomplete = () => res();
4542
- tx.onerror = () => rej(tx.error);
4543
- });
4544
- db.close();
4697
+ localStorage.removeItem(`${storageKey}-messages`);
4545
4698
  } catch {
4546
4699
  }
4547
4700
  }
4548
- async function clearSession(storageKey) {
4701
+ function clearSession(storageKey) {
4549
4702
  clearCompactionState(storageKey);
4550
- await clearDisplayMessages(storageKey);
4703
+ clearDisplayMessages(storageKey);
4551
4704
  }
4552
4705
 
4553
4706
  // src/react/message-history/useMessageHistory.ts
@@ -4590,9 +4743,8 @@ function useMessageHistory(options = {}) {
4590
4743
  React2.useEffect(() => {
4591
4744
  if (!config.persistSession || restoredRef.current) return;
4592
4745
  restoredRef.current = true;
4593
- loadDisplayMessages(storageKey).then((saved) => {
4594
- if (saved?.length && messages.length === 0) ;
4595
- });
4746
+ const saved = loadDisplayMessages(storageKey);
4747
+ if (saved?.length && messages.length === 0) ;
4596
4748
  }, [config.persistSession, storageKey, messages.length]);
4597
4749
  React2.useEffect(() => {
4598
4750
  if (!config.persistSession || displayMessages.length === 0) return;
@@ -4746,10 +4898,10 @@ function useMessageHistory(options = {}) {
4746
4898
  const clearWorkingMemory = React2.useCallback(() => {
4747
4899
  setCompactionState((prev) => ({ ...prev, workingMemory: [] }));
4748
4900
  }, []);
4749
- const resetSession = React2.useCallback(async () => {
4901
+ const resetSession = React2.useCallback(() => {
4750
4902
  setCompactionState(DEFAULT_COMPACTION_STATE);
4751
4903
  if (config.persistSession) {
4752
- await clearSession(storageKey);
4904
+ clearSession(storageKey);
4753
4905
  }
4754
4906
  }, [config.persistSession, storageKey]);
4755
4907
  return {
@@ -4851,7 +5003,7 @@ function useTool(config, dependencies = []) {
4851
5003
  configRef.current = config;
4852
5004
  const inputSchema = React2.useMemo(() => {
4853
5005
  if (isZodSchema(config.inputSchema)) {
4854
- return chunkKGYDGK3U_cjs.zodToJsonSchema(config.inputSchema);
5006
+ return chunkVION33GW_cjs.zodToJsonSchema(config.inputSchema);
4855
5007
  }
4856
5008
  return config.inputSchema;
4857
5009
  }, [config.inputSchema]);
@@ -5213,26 +5365,34 @@ function useCopilot() {
5213
5365
  }
5214
5366
  return context;
5215
5367
  }
5216
- function CopilotProvider({
5217
- children,
5218
- runtimeUrl,
5219
- systemPrompt,
5220
- tools: toolsConfig,
5221
- threadId,
5222
- initialMessages,
5223
- onMessagesChange,
5224
- onError,
5225
- streaming,
5226
- headers,
5227
- body,
5228
- debug = false,
5229
- maxIterations,
5230
- maxIterationsMessage,
5231
- mcpServers,
5232
- optimization,
5233
- messageHistory,
5234
- skills
5235
- }) {
5368
+ function CopilotProvider(props) {
5369
+ const {
5370
+ children,
5371
+ runtimeUrl,
5372
+ systemPrompt,
5373
+ tools: toolsConfig,
5374
+ threadId,
5375
+ onCreateSession,
5376
+ onThreadChange,
5377
+ yourgptConfig,
5378
+ initialMessages,
5379
+ onMessagesChange,
5380
+ onError,
5381
+ streaming,
5382
+ headers,
5383
+ body,
5384
+ debug = false,
5385
+ maxIterations,
5386
+ maxIterationsMessage,
5387
+ mcpServers,
5388
+ optimization,
5389
+ messageHistory,
5390
+ skills
5391
+ } = props;
5392
+ const isThreadIdControlled = Object.prototype.hasOwnProperty.call(
5393
+ props,
5394
+ "threadId"
5395
+ );
5236
5396
  const streamListenersRef = React2.useRef(/* @__PURE__ */ new Set());
5237
5397
  const subscribeToStreamEvents = React2.useCallback(
5238
5398
  (handler) => {
@@ -5244,7 +5404,7 @@ function CopilotProvider({
5244
5404
  const messageMetaStoreRef = React2.useRef(new MessageMetaStore());
5245
5405
  const debugLog = React2.useCallback(
5246
5406
  (action, data) => {
5247
- chunkKGYDGK3U_cjs.createLogger("provider", () => debug ?? false)(action, data);
5407
+ chunkVION33GW_cjs.createLogger("provider", () => debug ?? false)(action, data);
5248
5408
  },
5249
5409
  [debug]
5250
5410
  );
@@ -5256,31 +5416,29 @@ function CopilotProvider({
5256
5416
  }
5257
5417
  }, [toolsConfig]);
5258
5418
  const [toolExecutions, setToolExecutions] = React2.useState([]);
5419
+ const [sessionStatus, setSessionStatus] = React2.useState(() => threadId ? "ready" : "idle");
5259
5420
  const [agentIteration, setAgentIteration] = React2.useState(0);
5421
+ const [actualThreadId, setActualThreadId] = React2.useState(
5422
+ threadId
5423
+ );
5424
+ const lastControlledThreadIdRef = React2.useRef({
5425
+ controlled: isThreadIdControlled,
5426
+ value: threadId
5427
+ });
5260
5428
  const chatRef = React2.useRef(null);
5261
5429
  if (chatRef.current !== null && chatRef.current.disposed) {
5262
5430
  chatRef.current.revive();
5263
5431
  debugLog("Revived disposed instance (React StrictMode)");
5264
5432
  }
5265
5433
  if (chatRef.current === null) {
5266
- const uiInitialMessages = initialMessages?.map(
5267
- (m) => ({
5268
- id: m.id,
5269
- role: m.role,
5270
- content: m.content ?? "",
5271
- createdAt: m.created_at ?? /* @__PURE__ */ new Date(),
5272
- attachments: m.metadata?.attachments,
5273
- toolCalls: m.tool_calls,
5274
- toolCallId: m.tool_call_id,
5275
- parentId: m.parent_id,
5276
- childrenIds: m.children_ids
5277
- })
5278
- );
5434
+ const uiInitialMessages = initialMessages;
5279
5435
  chatRef.current = new ReactChatWithTools(
5280
5436
  {
5281
5437
  runtimeUrl,
5282
5438
  systemPrompt,
5283
5439
  threadId,
5440
+ onCreateSession,
5441
+ yourgptConfig,
5284
5442
  initialMessages: uiInitialMessages,
5285
5443
  streaming,
5286
5444
  headers,
@@ -5305,6 +5463,15 @@ function CopilotProvider({
5305
5463
  onError: (error2) => {
5306
5464
  if (error2) onError?.(error2);
5307
5465
  },
5466
+ onThreadChange: (id) => {
5467
+ debugLog("Thread/session ID assigned:", id);
5468
+ setActualThreadId(id);
5469
+ onThreadChange?.(id);
5470
+ },
5471
+ onSessionStatusChange: (status2) => {
5472
+ debugLog("Session status:", status2);
5473
+ setSessionStatus(status2);
5474
+ },
5308
5475
  onStreamChunk: (chunk) => {
5309
5476
  if (streamListenersRef.current.size > 0) {
5310
5477
  for (const handler of streamListenersRef.current) {
@@ -5339,6 +5506,25 @@ function CopilotProvider({
5339
5506
  debugLog("URL config updated from prop");
5340
5507
  }
5341
5508
  }, [runtimeUrl, debugLog]);
5509
+ React2.useEffect(() => {
5510
+ const prev = lastControlledThreadIdRef.current;
5511
+ const controlChanged = prev.controlled !== isThreadIdControlled;
5512
+ const valueChanged = prev.value !== threadId;
5513
+ if (!controlChanged && !valueChanged) {
5514
+ return;
5515
+ }
5516
+ lastControlledThreadIdRef.current = {
5517
+ controlled: isThreadIdControlled,
5518
+ value: threadId
5519
+ };
5520
+ if (!isThreadIdControlled) {
5521
+ return;
5522
+ }
5523
+ chatRef.current?.setActiveThread(threadId ?? null);
5524
+ setActualThreadId(threadId);
5525
+ setSessionStatus(threadId ? "ready" : "idle");
5526
+ debugLog("Thread/session synced from prop", { threadId });
5527
+ }, [debugLog, isThreadIdControlled, threadId]);
5342
5528
  const EMPTY_MESSAGES = React2.useRef([]);
5343
5529
  const getMessagesSnapshot = React2.useCallback(() => chatRef.current.messages, []);
5344
5530
  const getServerMessagesSnapshot = React2.useCallback(
@@ -5364,6 +5550,15 @@ function CopilotProvider({
5364
5550
  );
5365
5551
  const error = errorFromChat ?? null;
5366
5552
  const isLoading = status === "streaming" || status === "submitted";
5553
+ const setActiveThread = React2.useCallback((id) => {
5554
+ chatRef.current?.setActiveThread(id);
5555
+ setActualThreadId(id ?? void 0);
5556
+ }, []);
5557
+ const renewSession = React2.useCallback(() => {
5558
+ chatRef.current?.renewSession();
5559
+ setActualThreadId(void 0);
5560
+ setSessionStatus("idle");
5561
+ }, []);
5367
5562
  const registerTool = React2.useCallback((tool) => {
5368
5563
  chatRef.current?.registerTool(tool);
5369
5564
  }, []);
@@ -5570,7 +5765,10 @@ function CopilotProvider({
5570
5765
  // Skills
5571
5766
  setInlineSkills,
5572
5767
  // Config
5573
- threadId,
5768
+ threadId: actualThreadId,
5769
+ setActiveThread,
5770
+ renewSession,
5771
+ sessionStatus,
5574
5772
  runtimeUrl,
5575
5773
  toolsConfig,
5576
5774
  // Headless primitives
@@ -5609,7 +5807,10 @@ function CopilotProvider({
5609
5807
  contextUsage,
5610
5808
  setSystemPrompt,
5611
5809
  setInlineSkills,
5612
- threadId,
5810
+ actualThreadId,
5811
+ setActiveThread,
5812
+ renewSession,
5813
+ sessionStatus,
5613
5814
  runtimeUrl,
5614
5815
  toolsConfig
5615
5816
  ]
@@ -5741,5 +5942,5 @@ exports.useMessageHistoryContext = useMessageHistoryContext;
5741
5942
  exports.useSkillContext = useSkillContext;
5742
5943
  exports.useTool = useTool;
5743
5944
  exports.useTools = useTools;
5744
- //# sourceMappingURL=chunk-LHLVTGIP.cjs.map
5745
- //# sourceMappingURL=chunk-LHLVTGIP.cjs.map
5945
+ //# sourceMappingURL=chunk-2QLF7XM7.cjs.map
5946
+ //# sourceMappingURL=chunk-2QLF7XM7.cjs.map