@yourgpt/copilot-sdk 0.1.1 → 1.0.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.
@@ -1841,9 +1841,890 @@ function fileToBase64(file) {
1841
1841
  });
1842
1842
  }
1843
1843
 
1844
+ // src/thread/interfaces/ThreadManagerState.ts
1845
+ var SimpleThreadManagerState = class {
1846
+ constructor() {
1847
+ this._threads = [];
1848
+ this._currentThreadId = null;
1849
+ this._currentThread = null;
1850
+ this._loadStatus = "idle";
1851
+ this._error = void 0;
1852
+ this.callbacks = /* @__PURE__ */ new Set();
1853
+ }
1854
+ // Getters
1855
+ get threads() {
1856
+ return this._threads;
1857
+ }
1858
+ get currentThreadId() {
1859
+ return this._currentThreadId;
1860
+ }
1861
+ get currentThread() {
1862
+ return this._currentThread;
1863
+ }
1864
+ get loadStatus() {
1865
+ return this._loadStatus;
1866
+ }
1867
+ get error() {
1868
+ return this._error;
1869
+ }
1870
+ // Setters with notification
1871
+ set threads(value) {
1872
+ this._threads = value;
1873
+ this.notify();
1874
+ }
1875
+ setThreads(threads) {
1876
+ this._threads = threads;
1877
+ this.notify();
1878
+ }
1879
+ setCurrentThread(thread) {
1880
+ this._currentThread = thread;
1881
+ this._currentThreadId = thread?.id ?? null;
1882
+ this.notify();
1883
+ }
1884
+ setCurrentThreadId(id) {
1885
+ this._currentThreadId = id;
1886
+ this.notify();
1887
+ }
1888
+ addThread(thread) {
1889
+ this._threads = [thread, ...this._threads];
1890
+ this.notify();
1891
+ }
1892
+ updateThread(id, updates) {
1893
+ this._threads = this._threads.map(
1894
+ (t) => t.id === id ? { ...t, ...updates } : t
1895
+ );
1896
+ if (this._currentThread?.id === id) {
1897
+ this._currentThread = { ...this._currentThread, ...updates };
1898
+ }
1899
+ this.notify();
1900
+ }
1901
+ removeThread(id) {
1902
+ this._threads = this._threads.filter((t) => t.id !== id);
1903
+ if (this._currentThreadId === id) {
1904
+ this._currentThreadId = null;
1905
+ this._currentThread = null;
1906
+ }
1907
+ this.notify();
1908
+ }
1909
+ setLoadStatus(status) {
1910
+ this._loadStatus = status;
1911
+ this.notify();
1912
+ }
1913
+ setError(error) {
1914
+ this._error = error;
1915
+ this.notify();
1916
+ }
1917
+ // Subscription
1918
+ subscribe(callback) {
1919
+ this.callbacks.add(callback);
1920
+ return () => this.callbacks.delete(callback);
1921
+ }
1922
+ // Snapshots
1923
+ getThreadsSnapshot() {
1924
+ return this._threads;
1925
+ }
1926
+ getCurrentThreadSnapshot() {
1927
+ return this._currentThread;
1928
+ }
1929
+ getLoadStatusSnapshot() {
1930
+ return this._loadStatus;
1931
+ }
1932
+ getErrorSnapshot() {
1933
+ return this._error;
1934
+ }
1935
+ notify() {
1936
+ this.callbacks.forEach((cb) => cb());
1937
+ }
1938
+ };
1939
+
1940
+ // src/thread/adapters/localStorageAdapter.ts
1941
+ var DEFAULT_STORAGE_KEY = "copilot-sdk-store";
1942
+ var STORE_VERSION = 1;
1943
+ function isLocalStorageAvailable() {
1944
+ if (typeof window === "undefined") return false;
1945
+ try {
1946
+ const testKey = "__copilot_test__";
1947
+ window.localStorage.setItem(testKey, testKey);
1948
+ window.localStorage.removeItem(testKey);
1949
+ return true;
1950
+ } catch {
1951
+ return false;
1952
+ }
1953
+ }
1954
+ function createEmptyStore() {
1955
+ return {
1956
+ version: STORE_VERSION,
1957
+ lastActiveThreadId: null,
1958
+ threads: []
1959
+ };
1960
+ }
1961
+ function serializeThread(thread) {
1962
+ return {
1963
+ id: thread.id,
1964
+ title: thread.title,
1965
+ preview: thread.preview,
1966
+ messageCount: thread.messageCount,
1967
+ createdAt: thread.createdAt.toISOString(),
1968
+ updatedAt: thread.updatedAt.toISOString(),
1969
+ sources: thread.sources || [],
1970
+ messages: thread.messages.map((m) => ({
1971
+ id: m.id,
1972
+ role: m.role,
1973
+ content: m.content,
1974
+ created_at: m.created_at instanceof Date ? m.created_at.toISOString() : m.created_at,
1975
+ tool_calls: m.tool_calls,
1976
+ tool_call_id: m.tool_call_id,
1977
+ metadata: m.metadata
1978
+ }))
1979
+ };
1980
+ }
1981
+ function deserializeThread(data) {
1982
+ return {
1983
+ id: data.id,
1984
+ title: data.title,
1985
+ preview: data.preview,
1986
+ messageCount: data.messageCount ?? data.messages?.length ?? 0,
1987
+ createdAt: new Date(data.createdAt),
1988
+ updatedAt: new Date(data.updatedAt),
1989
+ sources: data.sources || [],
1990
+ messages: (data.messages || []).map((m) => ({
1991
+ id: m.id,
1992
+ role: m.role,
1993
+ content: m.content,
1994
+ created_at: new Date(m.created_at),
1995
+ tool_calls: m.tool_calls,
1996
+ tool_call_id: m.tool_call_id,
1997
+ metadata: m.metadata
1998
+ }))
1999
+ };
2000
+ }
2001
+ function readStore(storageKey) {
2002
+ if (!isLocalStorageAvailable()) {
2003
+ return createEmptyStore();
2004
+ }
2005
+ try {
2006
+ const raw = localStorage.getItem(storageKey);
2007
+ if (!raw) {
2008
+ return createEmptyStore();
2009
+ }
2010
+ const parsed = JSON.parse(raw);
2011
+ if (!parsed || typeof parsed !== "object") {
2012
+ console.warn("[CopilotSDK] Invalid store format, resetting");
2013
+ return createEmptyStore();
2014
+ }
2015
+ if (parsed.version !== STORE_VERSION) {
2016
+ console.log(
2017
+ `[CopilotSDK] Migrating store from v${parsed.version} to v${STORE_VERSION}`
2018
+ );
2019
+ }
2020
+ return {
2021
+ version: STORE_VERSION,
2022
+ lastActiveThreadId: parsed.lastActiveThreadId ?? null,
2023
+ threads: Array.isArray(parsed.threads) ? parsed.threads : []
2024
+ };
2025
+ } catch (e) {
2026
+ console.warn("[CopilotSDK] Failed to read store, resetting:", e);
2027
+ return createEmptyStore();
2028
+ }
2029
+ }
2030
+ function writeStore(storageKey, store) {
2031
+ if (!isLocalStorageAvailable()) return;
2032
+ try {
2033
+ const serialized = JSON.stringify(store);
2034
+ localStorage.setItem(storageKey, serialized);
2035
+ } catch (e) {
2036
+ if (e instanceof DOMException && e.name === "QuotaExceededError") {
2037
+ console.error(
2038
+ "[CopilotSDK] localStorage quota exceeded. Consider clearing old threads."
2039
+ );
2040
+ } else {
2041
+ console.warn("[CopilotSDK] Failed to write store:", e);
2042
+ }
2043
+ }
2044
+ }
2045
+ function updateStore(storageKey, updater) {
2046
+ const store = readStore(storageKey);
2047
+ const updates = updater(store);
2048
+ const newStore = { ...store, ...updates };
2049
+ writeStore(storageKey, newStore);
2050
+ return newStore;
2051
+ }
2052
+ function createLocalStorageAdapter(config) {
2053
+ const storageKey = config?.storageKey ?? DEFAULT_STORAGE_KEY;
2054
+ return {
2055
+ save: async (threads) => {
2056
+ updateStore(storageKey, (store) => ({
2057
+ threads: threads.map(serializeThread)
2058
+ }));
2059
+ },
2060
+ load: async () => {
2061
+ const store = readStore(storageKey);
2062
+ return store.threads.map(deserializeThread);
2063
+ },
2064
+ clear: async () => {
2065
+ if (!isLocalStorageAvailable()) return;
2066
+ try {
2067
+ localStorage.removeItem(storageKey);
2068
+ } catch (e) {
2069
+ console.warn("[CopilotSDK] Failed to clear store:", e);
2070
+ }
2071
+ },
2072
+ getLastActiveThreadId: async () => {
2073
+ const store = readStore(storageKey);
2074
+ return store.lastActiveThreadId;
2075
+ },
2076
+ setLastActiveThreadId: async (threadId) => {
2077
+ updateStore(storageKey, () => ({
2078
+ lastActiveThreadId: threadId
2079
+ }));
2080
+ }
2081
+ };
2082
+ }
2083
+ var localStorageAdapter = createLocalStorageAdapter();
2084
+
2085
+ // src/thread/adapters/serverAdapter.ts
2086
+ function parseThread(data) {
2087
+ return {
2088
+ id: data.id,
2089
+ title: data.title,
2090
+ preview: data.preview,
2091
+ messageCount: data.messageCount,
2092
+ createdAt: new Date(data.createdAt),
2093
+ updatedAt: new Date(data.updatedAt),
2094
+ messages: (data.messages ?? []).map((m) => ({
2095
+ id: m.id,
2096
+ role: m.role,
2097
+ content: m.content,
2098
+ created_at: m.created_at ? new Date(m.created_at) : /* @__PURE__ */ new Date(),
2099
+ tool_calls: m.tool_calls,
2100
+ tool_call_id: m.tool_call_id,
2101
+ metadata: m.metadata
2102
+ })),
2103
+ sources: data.sources ?? []
2104
+ };
2105
+ }
2106
+ function serializeThread2(thread) {
2107
+ const serialized = {};
2108
+ if (thread.id !== void 0) serialized.id = thread.id;
2109
+ if (thread.title !== void 0) serialized.title = thread.title;
2110
+ if (thread.preview !== void 0) serialized.preview = thread.preview;
2111
+ if (thread.messageCount !== void 0)
2112
+ serialized.messageCount = thread.messageCount;
2113
+ if (thread.createdAt !== void 0)
2114
+ serialized.createdAt = thread.createdAt.toISOString();
2115
+ if (thread.updatedAt !== void 0)
2116
+ serialized.updatedAt = thread.updatedAt.toISOString();
2117
+ if (thread.messages !== void 0) {
2118
+ serialized.messages = thread.messages.map((m) => ({
2119
+ id: m.id,
2120
+ role: m.role,
2121
+ content: m.content,
2122
+ created_at: m.created_at instanceof Date ? m.created_at.toISOString() : m.created_at,
2123
+ tool_calls: m.tool_calls,
2124
+ tool_call_id: m.tool_call_id,
2125
+ metadata: m.metadata
2126
+ }));
2127
+ }
2128
+ if (thread.sources !== void 0) serialized.sources = thread.sources;
2129
+ return serialized;
2130
+ }
2131
+ function createServerAdapter(config) {
2132
+ const { endpoint, headers = {} } = config;
2133
+ const fetchFn = config.fetch ?? globalThis.fetch;
2134
+ const buildUrl = (path = "", params) => {
2135
+ const url = new URL(
2136
+ path ? `${endpoint}/${path}` : endpoint,
2137
+ globalThis.location?.origin ?? "http://localhost"
2138
+ );
2139
+ if (params) {
2140
+ Object.entries(params).forEach(([key, value]) => {
2141
+ url.searchParams.set(key, value);
2142
+ });
2143
+ }
2144
+ return url.toString();
2145
+ };
2146
+ const request = async (method, path = "", body, params) => {
2147
+ const url = buildUrl(path, params);
2148
+ const response = await fetchFn(url, {
2149
+ method,
2150
+ headers: {
2151
+ "Content-Type": "application/json",
2152
+ ...headers
2153
+ },
2154
+ body: body ? JSON.stringify(body) : void 0
2155
+ });
2156
+ if (!response.ok) {
2157
+ const errorText = await response.text();
2158
+ throw new Error(
2159
+ `Server adapter request failed: ${response.status} ${response.statusText} - ${errorText}`
2160
+ );
2161
+ }
2162
+ if (response.status === 204) {
2163
+ return void 0;
2164
+ }
2165
+ return response.json();
2166
+ };
2167
+ return {
2168
+ // ============================================
2169
+ // Basic Operations (required by ThreadStorageAdapter)
2170
+ // ============================================
2171
+ /**
2172
+ * Load all threads from server
2173
+ * Note: For large datasets, prefer using listThreads with pagination
2174
+ */
2175
+ load: async () => {
2176
+ const response = await request("GET");
2177
+ const threads = Array.isArray(response) ? response : response.threads;
2178
+ return threads.map(parseThread);
2179
+ },
2180
+ /**
2181
+ * Save all threads to server (batch upsert)
2182
+ * Note: This is a fallback - prefer using individual CRUD operations
2183
+ */
2184
+ save: async (threads) => {
2185
+ await request("PUT", "", {
2186
+ threads: threads.map(serializeThread2)
2187
+ });
2188
+ },
2189
+ /**
2190
+ * Clear all threads
2191
+ */
2192
+ clear: async () => {
2193
+ await request("DELETE", "");
2194
+ },
2195
+ // ============================================
2196
+ // Optimized Single-Thread Operations
2197
+ // ============================================
2198
+ /**
2199
+ * Get a single thread by ID with messages
2200
+ */
2201
+ getThread: async (id) => {
2202
+ try {
2203
+ const response = await request("GET", id);
2204
+ return parseThread(response);
2205
+ } catch (error) {
2206
+ if (error instanceof Error && error.message.includes("404")) {
2207
+ return null;
2208
+ }
2209
+ throw error;
2210
+ }
2211
+ },
2212
+ /**
2213
+ * Create a new thread
2214
+ */
2215
+ createThread: async (thread) => {
2216
+ const response = await request(
2217
+ "POST",
2218
+ "",
2219
+ serializeThread2(thread)
2220
+ );
2221
+ return parseThread(response);
2222
+ },
2223
+ /**
2224
+ * Update an existing thread
2225
+ */
2226
+ updateThread: async (id, updates) => {
2227
+ const response = await request(
2228
+ "PATCH",
2229
+ id,
2230
+ serializeThread2(updates)
2231
+ );
2232
+ return parseThread(response);
2233
+ },
2234
+ /**
2235
+ * Delete a thread by ID
2236
+ */
2237
+ deleteThread: async (id) => {
2238
+ await request("DELETE", id);
2239
+ },
2240
+ /**
2241
+ * List threads with pagination
2242
+ */
2243
+ listThreads: async (options) => {
2244
+ const params = {};
2245
+ if (options?.limit) params.limit = String(options.limit);
2246
+ if (options?.offset) params.offset = String(options.offset);
2247
+ if (options?.orderBy) params.orderBy = options.orderBy;
2248
+ if (options?.orderDir) params.orderDir = options.orderDir;
2249
+ const response = await request(
2250
+ "GET",
2251
+ "",
2252
+ void 0,
2253
+ params
2254
+ );
2255
+ return {
2256
+ threads: response.threads.map((t) => ({
2257
+ id: t.id,
2258
+ title: t.title,
2259
+ preview: t.preview,
2260
+ messageCount: t.messageCount,
2261
+ createdAt: new Date(t.createdAt),
2262
+ updatedAt: new Date(t.updatedAt)
2263
+ })),
2264
+ total: response.total,
2265
+ hasMore: response.hasMore ?? false
2266
+ };
2267
+ },
2268
+ // ============================================
2269
+ // Session Persistence
2270
+ // ============================================
2271
+ /**
2272
+ * Get the last active thread ID from localStorage
2273
+ */
2274
+ getLastActiveThreadId: async () => {
2275
+ if (typeof window === "undefined") return null;
2276
+ try {
2277
+ return localStorage.getItem("yourgpt-last-thread-id");
2278
+ } catch {
2279
+ return null;
2280
+ }
2281
+ },
2282
+ /**
2283
+ * Store the last active thread ID in localStorage
2284
+ */
2285
+ setLastActiveThreadId: async (threadId) => {
2286
+ if (typeof window === "undefined") return;
2287
+ try {
2288
+ if (threadId) {
2289
+ localStorage.setItem("yourgpt-last-thread-id", threadId);
2290
+ } else {
2291
+ localStorage.removeItem("yourgpt-last-thread-id");
2292
+ }
2293
+ } catch {
2294
+ }
2295
+ }
2296
+ };
2297
+ }
2298
+
2299
+ // src/thread/adapters/memoryAdapter.ts
2300
+ function createMemoryAdapter(initialThreads) {
2301
+ let threads = initialThreads ? [...initialThreads] : [];
2302
+ return {
2303
+ save: async (newThreads) => {
2304
+ threads = JSON.parse(JSON.stringify(newThreads));
2305
+ threads = threads.map((t) => ({
2306
+ ...t,
2307
+ createdAt: new Date(t.createdAt),
2308
+ updatedAt: new Date(t.updatedAt),
2309
+ messages: t.messages.map((m) => ({
2310
+ ...m,
2311
+ created_at: new Date(m.created_at)
2312
+ }))
2313
+ }));
2314
+ },
2315
+ load: async () => {
2316
+ return threads.map((t) => ({
2317
+ ...t,
2318
+ messages: [...t.messages],
2319
+ sources: [...t.sources]
2320
+ }));
2321
+ },
2322
+ clear: async () => {
2323
+ threads = [];
2324
+ }
2325
+ };
2326
+ }
2327
+ var noopAdapter = {
2328
+ save: async () => {
2329
+ },
2330
+ load: async () => [],
2331
+ clear: async () => {
2332
+ }
2333
+ };
2334
+
2335
+ // src/thread/ThreadManager.ts
2336
+ function generateThreadId2() {
2337
+ return `thread_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
2338
+ }
2339
+ function generateThreadTitle2(content) {
2340
+ const trimmed = content.trim();
2341
+ if (trimmed.length <= 50) return trimmed;
2342
+ return trimmed.substring(0, 47) + "...";
2343
+ }
2344
+ function generatePreview(content) {
2345
+ const trimmed = content.trim();
2346
+ if (trimmed.length <= 100) return trimmed;
2347
+ return trimmed.substring(0, 97) + "...";
2348
+ }
2349
+ var ThreadManager = class {
2350
+ constructor(config = {}, callbacks = {}) {
2351
+ // Debounce timer for auto-save
2352
+ this.saveTimer = null;
2353
+ // Full thread data cache (for localStorage adapter)
2354
+ this.threadsData = /* @__PURE__ */ new Map();
2355
+ // Initialization promise
2356
+ this.initPromise = null;
2357
+ this.state = config.state ?? new SimpleThreadManagerState();
2358
+ this.adapter = config.adapter ?? localStorageAdapter;
2359
+ this.callbacks = callbacks;
2360
+ this.saveDebounce = config.saveDebounce ?? 1e3;
2361
+ this.autoLoad = config.autoLoad ?? true;
2362
+ this.autoRestoreLastThread = config.autoRestoreLastThread ?? true;
2363
+ if (this.autoLoad) {
2364
+ this.initPromise = this.loadThreads().catch((err) => {
2365
+ console.warn("[ThreadManager] Auto-load failed:", err);
2366
+ });
2367
+ }
2368
+ }
2369
+ // ============================================
2370
+ // Getters
2371
+ // ============================================
2372
+ /** All threads (metadata) */
2373
+ get threads() {
2374
+ return this.state.threads;
2375
+ }
2376
+ /** Currently selected thread ID */
2377
+ get currentThreadId() {
2378
+ return this.state.currentThreadId;
2379
+ }
2380
+ /** Currently loaded thread (with messages) */
2381
+ get currentThread() {
2382
+ return this.state.currentThread;
2383
+ }
2384
+ /** Whether threads are currently loading */
2385
+ get isLoading() {
2386
+ return this.state.loadStatus === "loading";
2387
+ }
2388
+ /** Current load status */
2389
+ get loadStatus() {
2390
+ return this.state.loadStatus;
2391
+ }
2392
+ /** Current error */
2393
+ get error() {
2394
+ return this.state.error;
2395
+ }
2396
+ /** Whether there are pending changes waiting to be saved */
2397
+ get hasPendingChanges() {
2398
+ return this.saveTimer !== null;
2399
+ }
2400
+ // ============================================
2401
+ // Public Methods
2402
+ // ============================================
2403
+ /**
2404
+ * Load all threads from storage
2405
+ */
2406
+ async loadThreads() {
2407
+ this.state.setLoadStatus("loading");
2408
+ this.state.setError(void 0);
2409
+ try {
2410
+ const threadsData = await this.adapter.load();
2411
+ this.threadsData.clear();
2412
+ for (const thread of threadsData) {
2413
+ this.threadsData.set(thread.id, thread);
2414
+ }
2415
+ const threads = threadsData.map((t) => ({
2416
+ id: t.id,
2417
+ title: t.title,
2418
+ preview: t.preview ?? (t.messages[0]?.content ? generatePreview(
2419
+ typeof t.messages[0].content === "string" ? t.messages[0].content : ""
2420
+ ) : void 0),
2421
+ messageCount: t.messageCount ?? t.messages.length,
2422
+ createdAt: t.createdAt,
2423
+ updatedAt: t.updatedAt
2424
+ }));
2425
+ threads.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
2426
+ this.state.setThreads(threads);
2427
+ this.state.setLoadStatus("loaded");
2428
+ this.callbacks.onThreadsLoaded?.(threads);
2429
+ if (this.autoRestoreLastThread && this.adapter.getLastActiveThreadId) {
2430
+ const lastActiveId = await this.adapter.getLastActiveThreadId();
2431
+ if (lastActiveId && this.threadsData.has(lastActiveId)) {
2432
+ this.switchThread(lastActiveId).catch((err) => {
2433
+ console.warn(
2434
+ "[ThreadManager] Failed to restore last active thread:",
2435
+ err
2436
+ );
2437
+ });
2438
+ }
2439
+ }
2440
+ } catch (err) {
2441
+ const error = err instanceof Error ? err : new Error(String(err));
2442
+ this.state.setError(error);
2443
+ this.state.setLoadStatus("error");
2444
+ this.callbacks.onError?.(error);
2445
+ throw error;
2446
+ }
2447
+ }
2448
+ /**
2449
+ * Wait for initialization to complete
2450
+ */
2451
+ async waitForInit() {
2452
+ if (this.initPromise) {
2453
+ await this.initPromise;
2454
+ }
2455
+ }
2456
+ /**
2457
+ * Create a new thread
2458
+ */
2459
+ async createThread(options) {
2460
+ const now = /* @__PURE__ */ new Date();
2461
+ const id = options?.id ?? generateThreadId2();
2462
+ let title = options?.title;
2463
+ if (!title && options?.messages) {
2464
+ const firstUserMsg = options.messages.find((m) => m.role === "user");
2465
+ if (firstUserMsg?.content) {
2466
+ title = generateThreadTitle2(
2467
+ typeof firstUserMsg.content === "string" ? firstUserMsg.content : ""
2468
+ );
2469
+ }
2470
+ }
2471
+ const thread = {
2472
+ id,
2473
+ title,
2474
+ messages: options?.messages ?? [],
2475
+ sources: [],
2476
+ createdAt: now,
2477
+ updatedAt: now,
2478
+ preview: options?.messages?.[0]?.content ? generatePreview(
2479
+ typeof options.messages[0].content === "string" ? options.messages[0].content : ""
2480
+ ) : void 0,
2481
+ messageCount: options?.messages?.length ?? 0
2482
+ };
2483
+ const asyncAdapter = this.adapter;
2484
+ if (asyncAdapter.createThread) {
2485
+ try {
2486
+ const created = await asyncAdapter.createThread(thread);
2487
+ this.threadsData.set(created.id, created);
2488
+ this.state.addThread(created);
2489
+ this.state.setCurrentThread(created);
2490
+ this.callbacks.onThreadCreated?.(created);
2491
+ this.saveLastActiveThread(created.id);
2492
+ return created;
2493
+ } catch (err) {
2494
+ const error = err instanceof Error ? err : new Error(String(err));
2495
+ this.state.setError(error);
2496
+ this.callbacks.onError?.(error);
2497
+ throw error;
2498
+ }
2499
+ }
2500
+ this.threadsData.set(id, thread);
2501
+ this.state.addThread(thread);
2502
+ this.state.setCurrentThread(thread);
2503
+ this.scheduleSave();
2504
+ this.callbacks.onThreadCreated?.(thread);
2505
+ this.saveLastActiveThread(id);
2506
+ return thread;
2507
+ }
2508
+ /**
2509
+ * Switch to a different thread
2510
+ */
2511
+ async switchThread(id) {
2512
+ if (this.currentThreadId === id && this.currentThread) {
2513
+ return this.currentThread;
2514
+ }
2515
+ const asyncAdapter = this.adapter;
2516
+ if (asyncAdapter.getThread) {
2517
+ try {
2518
+ const thread2 = await asyncAdapter.getThread(id);
2519
+ this.state.setCurrentThread(thread2);
2520
+ if (thread2) {
2521
+ this.threadsData.set(id, thread2);
2522
+ this.saveLastActiveThread(id);
2523
+ }
2524
+ this.callbacks.onThreadSwitched?.(thread2);
2525
+ return thread2;
2526
+ } catch (err) {
2527
+ const error = err instanceof Error ? err : new Error(String(err));
2528
+ this.state.setError(error);
2529
+ this.callbacks.onError?.(error);
2530
+ throw error;
2531
+ }
2532
+ }
2533
+ const thread = this.threadsData.get(id) ?? null;
2534
+ this.state.setCurrentThread(thread);
2535
+ if (thread) {
2536
+ this.saveLastActiveThread(id);
2537
+ }
2538
+ this.callbacks.onThreadSwitched?.(thread);
2539
+ return thread;
2540
+ }
2541
+ /**
2542
+ * Update the current thread
2543
+ */
2544
+ async updateCurrentThread(updates) {
2545
+ if (!this.currentThread) {
2546
+ throw new Error("No thread selected");
2547
+ }
2548
+ const now = /* @__PURE__ */ new Date();
2549
+ const id = this.currentThread.id;
2550
+ const updatedThread = {
2551
+ ...this.currentThread,
2552
+ ...updates,
2553
+ updatedAt: now
2554
+ };
2555
+ if (updates.messages) {
2556
+ updatedThread.messageCount = updates.messages.length;
2557
+ if (updates.messages[0]?.content) {
2558
+ updatedThread.preview = generatePreview(
2559
+ typeof updates.messages[0].content === "string" ? updates.messages[0].content : ""
2560
+ );
2561
+ }
2562
+ if (!updatedThread.title) {
2563
+ const firstUserMsg = updates.messages.find((m) => m.role === "user");
2564
+ if (firstUserMsg?.content) {
2565
+ updatedThread.title = generateThreadTitle2(
2566
+ typeof firstUserMsg.content === "string" ? firstUserMsg.content : ""
2567
+ );
2568
+ }
2569
+ }
2570
+ }
2571
+ const asyncAdapter = this.adapter;
2572
+ if (asyncAdapter.updateThread) {
2573
+ try {
2574
+ const updated = await asyncAdapter.updateThread(id, updatedThread);
2575
+ this.threadsData.set(id, updated);
2576
+ this.state.setCurrentThread(updated);
2577
+ this.state.updateThread(id, {
2578
+ title: updated.title,
2579
+ preview: updated.preview,
2580
+ messageCount: updated.messageCount,
2581
+ updatedAt: updated.updatedAt
2582
+ });
2583
+ this.callbacks.onThreadUpdated?.(updated);
2584
+ return;
2585
+ } catch (err) {
2586
+ const error = err instanceof Error ? err : new Error(String(err));
2587
+ this.state.setError(error);
2588
+ this.callbacks.onError?.(error);
2589
+ throw error;
2590
+ }
2591
+ }
2592
+ this.threadsData.set(id, updatedThread);
2593
+ this.state.setCurrentThread(updatedThread);
2594
+ this.state.updateThread(id, {
2595
+ title: updatedThread.title,
2596
+ preview: updatedThread.preview,
2597
+ messageCount: updatedThread.messageCount,
2598
+ updatedAt: updatedThread.updatedAt
2599
+ });
2600
+ this.scheduleSave();
2601
+ this.callbacks.onThreadUpdated?.(updatedThread);
2602
+ }
2603
+ /**
2604
+ * Delete a thread
2605
+ */
2606
+ async deleteThread(id) {
2607
+ const isDeletingCurrent = this.currentThreadId === id;
2608
+ const asyncAdapter = this.adapter;
2609
+ if (asyncAdapter.deleteThread) {
2610
+ try {
2611
+ await asyncAdapter.deleteThread(id);
2612
+ this.threadsData.delete(id);
2613
+ this.state.removeThread(id);
2614
+ if (isDeletingCurrent) {
2615
+ this.state.setCurrentThread(null);
2616
+ this.saveLastActiveThread(null);
2617
+ }
2618
+ this.callbacks.onThreadDeleted?.(id);
2619
+ return;
2620
+ } catch (err) {
2621
+ const error = err instanceof Error ? err : new Error(String(err));
2622
+ this.state.setError(error);
2623
+ this.callbacks.onError?.(error);
2624
+ throw error;
2625
+ }
2626
+ }
2627
+ this.threadsData.delete(id);
2628
+ this.state.removeThread(id);
2629
+ if (isDeletingCurrent) {
2630
+ this.state.setCurrentThread(null);
2631
+ this.saveLastActiveThread(null);
2632
+ }
2633
+ this.scheduleSave();
2634
+ this.callbacks.onThreadDeleted?.(id);
2635
+ }
2636
+ /**
2637
+ * Clear the current thread selection
2638
+ */
2639
+ clearCurrentThread() {
2640
+ this.state.setCurrentThread(null);
2641
+ this.saveLastActiveThread(null);
2642
+ this.callbacks.onThreadSwitched?.(null);
2643
+ }
2644
+ /**
2645
+ * Clear all threads
2646
+ */
2647
+ async clearAllThreads() {
2648
+ try {
2649
+ await this.adapter.clear();
2650
+ this.threadsData.clear();
2651
+ this.state.setThreads([]);
2652
+ this.state.setCurrentThread(null);
2653
+ this.saveLastActiveThread(null);
2654
+ } catch (err) {
2655
+ const error = err instanceof Error ? err : new Error(String(err));
2656
+ this.state.setError(error);
2657
+ this.callbacks.onError?.(error);
2658
+ throw error;
2659
+ }
2660
+ }
2661
+ /**
2662
+ * Save changes immediately (bypass debounce)
2663
+ */
2664
+ async saveNow() {
2665
+ if (this.saveTimer) {
2666
+ clearTimeout(this.saveTimer);
2667
+ this.saveTimer = null;
2668
+ }
2669
+ try {
2670
+ const threads = Array.from(this.threadsData.values());
2671
+ await this.adapter.save(threads);
2672
+ } catch (err) {
2673
+ const error = err instanceof Error ? err : new Error(String(err));
2674
+ this.state.setError(error);
2675
+ this.callbacks.onError?.(error);
2676
+ throw error;
2677
+ }
2678
+ }
2679
+ /**
2680
+ * Dispose of the manager and save pending changes
2681
+ */
2682
+ async dispose() {
2683
+ if (this.saveTimer) {
2684
+ clearTimeout(this.saveTimer);
2685
+ this.saveTimer = null;
2686
+ await this.saveNow();
2687
+ }
2688
+ }
2689
+ // ============================================
2690
+ // Protected Methods
2691
+ // ============================================
2692
+ /**
2693
+ * Schedule a debounced save
2694
+ */
2695
+ scheduleSave() {
2696
+ if (this.saveTimer) {
2697
+ clearTimeout(this.saveTimer);
2698
+ }
2699
+ this.saveTimer = setTimeout(async () => {
2700
+ this.saveTimer = null;
2701
+ try {
2702
+ await this.saveNow();
2703
+ } catch (err) {
2704
+ console.warn("[ThreadManager] Auto-save failed:", err);
2705
+ }
2706
+ }, this.saveDebounce);
2707
+ }
2708
+ /**
2709
+ * Save the last active thread ID (for session persistence)
2710
+ */
2711
+ saveLastActiveThread(threadId) {
2712
+ if (this.adapter.setLastActiveThreadId) {
2713
+ this.adapter.setLastActiveThreadId(threadId).catch((err) => {
2714
+ console.warn("[ThreadManager] Failed to save last active thread:", err);
2715
+ });
2716
+ }
2717
+ }
2718
+ };
2719
+ function createThreadManager(config, callbacks) {
2720
+ return new ThreadManager(config, callbacks);
2721
+ }
2722
+
1844
2723
  exports.CLOUD_MAX_FILE_SIZE = CLOUD_MAX_FILE_SIZE;
1845
2724
  exports.DEFAULT_MODELS = DEFAULT_MODELS;
1846
2725
  exports.DEFAULT_YOURGPT_ENDPOINT = DEFAULT_YOURGPT_ENDPOINT;
2726
+ exports.SimpleThreadManagerState = SimpleThreadManagerState;
2727
+ exports.ThreadManager = ThreadManager;
1847
2728
  exports.actionToTool = actionToTool;
1848
2729
  exports.builtinTools = builtinTools;
1849
2730
  exports.captureCurrentLogs = captureCurrentLogs;
@@ -1855,10 +2736,14 @@ exports.createAssistantMessage = createAssistantMessage;
1855
2736
  exports.createCloudStorage = createCloudStorage;
1856
2737
  exports.createConsoleLogsTool = createConsoleLogsTool;
1857
2738
  exports.createCustomDetector = createCustomDetector;
2739
+ exports.createLocalStorageAdapter = createLocalStorageAdapter;
2740
+ exports.createMemoryAdapter = createMemoryAdapter;
1858
2741
  exports.createMessage = createMessage;
1859
2742
  exports.createNetworkRequestsTool = createNetworkRequestsTool;
1860
2743
  exports.createSSEStream = createSSEStream;
1861
2744
  exports.createScreenshotTool = createScreenshotTool;
2745
+ exports.createServerAdapter = createServerAdapter;
2746
+ exports.createThreadManager = createThreadManager;
1862
2747
  exports.createToolCall = createToolCall;
1863
2748
  exports.createToolMessage = createToolMessage;
1864
2749
  exports.createToolResult = createToolResult;
@@ -1892,7 +2777,9 @@ exports.isConsoleCaptureActive = isConsoleCaptureActive;
1892
2777
  exports.isNetworkCaptureActive = isNetworkCaptureActive;
1893
2778
  exports.isScreenshotSupported = isScreenshotSupported;
1894
2779
  exports.isToolResult = isToolResult;
2780
+ exports.localStorageAdapter = localStorageAdapter;
1895
2781
  exports.networkRequestsTool = networkRequestsTool;
2782
+ exports.noopAdapter = noopAdapter;
1896
2783
  exports.parseSSELine = parseSSELine;
1897
2784
  exports.parseStreamEvent = parseStreamEvent;
1898
2785
  exports.parseToolCallArgs = parseToolCallArgs;
@@ -1911,5 +2798,5 @@ exports.toolToAnthropicFormat = toolToAnthropicFormat;
1911
2798
  exports.toolToOpenAIFormat = toolToOpenAIFormat;
1912
2799
  exports.zodObjectToInputSchema = zodObjectToInputSchema;
1913
2800
  exports.zodToJsonSchema = zodToJsonSchema;
1914
- //# sourceMappingURL=chunk-IH7WXWX4.cjs.map
1915
- //# sourceMappingURL=chunk-IH7WXWX4.cjs.map
2801
+ //# sourceMappingURL=chunk-42YQ4ATO.cjs.map
2802
+ //# sourceMappingURL=chunk-42YQ4ATO.cjs.map