@gr33n-ai/jade-sdk-client 0.1.0 → 0.1.3

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,3 +1,7 @@
1
+ import { createContext, useMemo, useContext, useState, useRef, useCallback } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import useSWR from 'swr';
4
+
1
5
  // ../../node_modules/.pnpm/@microsoft+fetch-event-source@2.0.1/node_modules/@microsoft/fetch-event-source/lib/esm/parse.js
2
6
  async function getBytes(stream, onChunk) {
3
7
  const reader = stream.getReader();
@@ -1671,5 +1675,916 @@ function extractMedia(conversation) {
1671
1675
  mediaArray.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
1672
1676
  return mediaArray;
1673
1677
  }
1678
+ var AgentEventEmitter2 = class {
1679
+ constructor() {
1680
+ this.handlers = /* @__PURE__ */ new Map();
1681
+ }
1682
+ /**
1683
+ * Subscribe to an event.
1684
+ * @returns Unsubscribe function
1685
+ */
1686
+ on(event, handler) {
1687
+ if (!this.handlers.has(event)) {
1688
+ this.handlers.set(event, /* @__PURE__ */ new Set());
1689
+ }
1690
+ this.handlers.get(event).add(handler);
1691
+ return () => this.off(event, handler);
1692
+ }
1693
+ /**
1694
+ * Unsubscribe from an event.
1695
+ */
1696
+ off(event, handler) {
1697
+ this.handlers.get(event)?.delete(handler);
1698
+ }
1699
+ /**
1700
+ * Emit an event to all handlers.
1701
+ */
1702
+ emit(event, data) {
1703
+ this.handlers.get(event)?.forEach((handler) => {
1704
+ try {
1705
+ handler(data);
1706
+ } catch (err) {
1707
+ console.error(`[AgentEventEmitter] Error in ${event} handler:`, err);
1708
+ }
1709
+ });
1710
+ }
1711
+ /**
1712
+ * Remove all event handlers.
1713
+ */
1714
+ removeAllListeners() {
1715
+ this.handlers.clear();
1716
+ }
1717
+ };
1718
+ var AgentClientError2 = class extends Error {
1719
+ constructor(message, cause) {
1720
+ super(message);
1721
+ this.cause = cause;
1722
+ this.name = "AgentClientError";
1723
+ }
1724
+ };
1725
+ var AuthenticationError2 = class extends AgentClientError2 {
1726
+ constructor(message = "Authentication failed") {
1727
+ super(message);
1728
+ this.name = "AuthenticationError";
1729
+ }
1730
+ };
1731
+ var SessionNotFoundError2 = class extends AgentClientError2 {
1732
+ constructor(sessionId) {
1733
+ super(`Session not found: ${sessionId}`);
1734
+ this.sessionId = sessionId;
1735
+ this.name = "SessionNotFoundError";
1736
+ }
1737
+ };
1738
+ var SkillNotFoundError2 = class extends AgentClientError2 {
1739
+ constructor(skillName) {
1740
+ super(`Skill not found: ${skillName}`);
1741
+ this.skillName = skillName;
1742
+ this.name = "SkillNotFoundError";
1743
+ }
1744
+ };
1745
+ var RequestError2 = class extends AgentClientError2 {
1746
+ constructor(message, statusCode) {
1747
+ super(message);
1748
+ this.statusCode = statusCode;
1749
+ this.name = "RequestError";
1750
+ }
1751
+ };
1752
+ var FatalSSEError2 = class extends Error {
1753
+ constructor(message) {
1754
+ super(message);
1755
+ this.name = "FatalSSEError";
1756
+ }
1757
+ };
1758
+ var MAX_RETRIES2 = 5;
1759
+ var INITIAL_RETRY_DELAY2 = 1e3;
1760
+ var MAX_RETRY_DELAY2 = 1e4;
1761
+ async function createSSEStream2(options) {
1762
+ const {
1763
+ url,
1764
+ method,
1765
+ headers,
1766
+ body,
1767
+ emitter,
1768
+ signal,
1769
+ onOpen,
1770
+ onClose,
1771
+ lastEventId
1772
+ } = options;
1773
+ const processedIds = /* @__PURE__ */ new Set();
1774
+ let retryCount = 0;
1775
+ try {
1776
+ await fetchEventSource(url, {
1777
+ method,
1778
+ headers: {
1779
+ "Content-Type": "application/json",
1780
+ ...lastEventId ? { "Last-Event-ID": lastEventId } : {},
1781
+ ...headers
1782
+ },
1783
+ body: JSON.stringify(body),
1784
+ signal,
1785
+ async onopen(response) {
1786
+ if (response.ok && response.headers.get("content-type")?.includes(EventStreamContentType)) {
1787
+ onOpen?.();
1788
+ return;
1789
+ }
1790
+ if (response.status === 401 || response.status === 403) {
1791
+ throw new FatalSSEError2(`Authentication failed: ${response.status}`);
1792
+ }
1793
+ if (response.status === 404) {
1794
+ throw new FatalSSEError2("Session not found or not active");
1795
+ }
1796
+ if (response.status === 503) {
1797
+ const text = await response.text().catch(() => "Service unavailable");
1798
+ throw new FatalSSEError2(`Server unavailable: ${text}`);
1799
+ }
1800
+ if (response.status >= 400 && response.status < 500) {
1801
+ const text = await response.text().catch(() => `Error ${response.status}`);
1802
+ throw new FatalSSEError2(text);
1803
+ }
1804
+ throw new Error(`Failed to connect: ${response.status}`);
1805
+ },
1806
+ onmessage(event) {
1807
+ if (event.id && processedIds.has(event.id)) {
1808
+ return;
1809
+ }
1810
+ if (event.id) {
1811
+ processedIds.add(event.id);
1812
+ }
1813
+ if (!event.data) {
1814
+ return;
1815
+ }
1816
+ try {
1817
+ const data = JSON.parse(event.data);
1818
+ const eventType = event.event || "message";
1819
+ switch (eventType) {
1820
+ case "init":
1821
+ case "session":
1822
+ emitter.emit("session", { session_id: data.session_id });
1823
+ break;
1824
+ case "entry":
1825
+ emitter.emit("entry", { entry: data });
1826
+ break;
1827
+ case "stream_event":
1828
+ handleStreamEvent2(data, emitter);
1829
+ break;
1830
+ case "content_block_start":
1831
+ emitter.emit("content_block_start", data);
1832
+ break;
1833
+ case "content_block_delta":
1834
+ emitter.emit("content_block_delta", data);
1835
+ break;
1836
+ case "content_block_stop":
1837
+ emitter.emit("content_block_stop", data);
1838
+ break;
1839
+ case "complete":
1840
+ emitter.emit("complete", data);
1841
+ break;
1842
+ case "error":
1843
+ emitter.emit("error", { error: data.error });
1844
+ break;
1845
+ case "heartbeat":
1846
+ emitter.emit("heartbeat", {});
1847
+ break;
1848
+ default:
1849
+ break;
1850
+ }
1851
+ } catch (e) {
1852
+ console.error("[SSE] Failed to parse event:", event.data, e);
1853
+ }
1854
+ },
1855
+ onerror(err) {
1856
+ if (signal?.aborted) {
1857
+ throw err;
1858
+ }
1859
+ if (err instanceof FatalSSEError2) {
1860
+ emitter.emit("error", { error: err.message });
1861
+ throw err;
1862
+ }
1863
+ retryCount++;
1864
+ if (retryCount > MAX_RETRIES2) {
1865
+ const error = `Connection failed after ${MAX_RETRIES2} retries`;
1866
+ emitter.emit("error", { error });
1867
+ throw new FatalSSEError2(error);
1868
+ }
1869
+ const delay = Math.min(INITIAL_RETRY_DELAY2 * Math.pow(2, retryCount - 1), MAX_RETRY_DELAY2);
1870
+ console.log(`[SSE] Retry ${retryCount}/${MAX_RETRIES2} in ${delay}ms`);
1871
+ return delay;
1872
+ },
1873
+ onclose() {
1874
+ onClose?.();
1875
+ },
1876
+ // Keep connection open when tab is hidden - heartbeats keep it alive
1877
+ openWhenHidden: true
1878
+ });
1879
+ } catch (error) {
1880
+ if (error instanceof FatalSSEError2) {
1881
+ if (error.message.includes("Authentication")) {
1882
+ throw new AuthenticationError2(error.message);
1883
+ }
1884
+ throw new RequestError2(error.message, 500);
1885
+ }
1886
+ if (error instanceof Error) {
1887
+ emitter.emit("error", { error: error.message });
1888
+ }
1889
+ throw error;
1890
+ }
1891
+ }
1892
+ function handleStreamEvent2(data, emitter) {
1893
+ const streamEvent = data.event || data;
1894
+ const eventData = streamEvent;
1895
+ switch (eventData.type) {
1896
+ case "content_block_start":
1897
+ emitter.emit("content_block_start", eventData);
1898
+ break;
1899
+ case "content_block_delta":
1900
+ emitter.emit("content_block_delta", eventData);
1901
+ break;
1902
+ case "content_block_stop":
1903
+ emitter.emit("content_block_stop", eventData);
1904
+ break;
1905
+ }
1906
+ }
1907
+ var AgentClient2 = class {
1908
+ constructor(config) {
1909
+ this.activeStreams = /* @__PURE__ */ new Map();
1910
+ this.config = {
1911
+ apiVersion: "/v1",
1912
+ timeout: 3e4,
1913
+ ...config
1914
+ };
1915
+ }
1916
+ // ===========================================================================
1917
+ // Public getters
1918
+ // ===========================================================================
1919
+ /** Whether org context is configured (orgId is set) */
1920
+ get hasOrgContext() {
1921
+ return !!this.config.orgId;
1922
+ }
1923
+ // ===========================================================================
1924
+ // Private helpers
1925
+ // ===========================================================================
1926
+ get baseUrl() {
1927
+ return `${this.config.endpoint}${this.config.apiVersion}`;
1928
+ }
1929
+ async getHeaders() {
1930
+ const headers = {
1931
+ "Content-Type": "application/json"
1932
+ };
1933
+ const token = await this.config.getAuthToken();
1934
+ if (token) {
1935
+ headers["Authorization"] = `Bearer ${token}`;
1936
+ }
1937
+ return headers;
1938
+ }
1939
+ async request(path, options = {}) {
1940
+ const url = `${this.baseUrl}${path}`;
1941
+ const headers = await this.getHeaders();
1942
+ const controller = new AbortController();
1943
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
1944
+ try {
1945
+ const response = await fetch(url, {
1946
+ ...options,
1947
+ headers: { ...headers, ...options.headers || {} },
1948
+ signal: controller.signal
1949
+ });
1950
+ if (!response.ok) {
1951
+ await this.handleErrorResponse(response, path);
1952
+ }
1953
+ return response.json();
1954
+ } finally {
1955
+ clearTimeout(timeoutId);
1956
+ }
1957
+ }
1958
+ async handleErrorResponse(response, path) {
1959
+ if (response.status === 401 || response.status === 403) {
1960
+ throw new AuthenticationError2();
1961
+ }
1962
+ if (response.status === 404) {
1963
+ const text = await response.text();
1964
+ if (text.toLowerCase().includes("session")) {
1965
+ const sessionId = path.split("/").pop() || "";
1966
+ throw new SessionNotFoundError2(sessionId);
1967
+ }
1968
+ if (text.toLowerCase().includes("skill")) {
1969
+ const skillName = path.split("/").pop() || "";
1970
+ throw new SkillNotFoundError2(skillName);
1971
+ }
1972
+ }
1973
+ throw new RequestError2(`Request failed: ${response.status}`, response.status);
1974
+ }
1975
+ // ===========================================================================
1976
+ // Messages API
1977
+ // ===========================================================================
1978
+ /**
1979
+ * Send a message and stream responses.
1980
+ * @returns Object with event emitter and abort function
1981
+ */
1982
+ stream(request) {
1983
+ const emitter = new AgentEventEmitter2();
1984
+ const controller = new AbortController();
1985
+ const streamId = request.session_id || `stream-${Date.now()}`;
1986
+ this.activeStreams.set(streamId, controller);
1987
+ (async () => {
1988
+ try {
1989
+ const headers = await this.getHeaders();
1990
+ await createSSEStream2({
1991
+ url: `${this.baseUrl}/messages`,
1992
+ method: "POST",
1993
+ headers,
1994
+ body: { ...request, stream: true },
1995
+ emitter,
1996
+ signal: controller.signal
1997
+ });
1998
+ } catch (error) {
1999
+ emitter.emit("error", {
2000
+ error: error instanceof Error ? error.message : "Unknown error"
2001
+ });
2002
+ } finally {
2003
+ this.activeStreams.delete(streamId);
2004
+ }
2005
+ })();
2006
+ return {
2007
+ emitter,
2008
+ abort: () => controller.abort()
2009
+ };
2010
+ }
2011
+ // ===========================================================================
2012
+ // Sessions API
2013
+ // ===========================================================================
2014
+ /**
2015
+ * List all sessions for the authenticated user.
2016
+ */
2017
+ async listSessions() {
2018
+ return this.request("/sessions");
2019
+ }
2020
+ /**
2021
+ * Get session content in display format.
2022
+ */
2023
+ async getSession(sessionId) {
2024
+ return this.request(`/sessions/${sessionId}`);
2025
+ }
2026
+ /**
2027
+ * Get session content as raw JSONL.
2028
+ */
2029
+ async getSessionRaw(sessionId) {
2030
+ return this.request(`/sessions/${sessionId}?format=raw`);
2031
+ }
2032
+ /**
2033
+ * Check if a session is actively streaming.
2034
+ */
2035
+ async getSessionStatus(sessionId) {
2036
+ return this.request(`/sessions/${sessionId}/status`);
2037
+ }
2038
+ /**
2039
+ * Update session metadata (name).
2040
+ */
2041
+ async updateSession(sessionId, updates) {
2042
+ return this.request(`/sessions/${sessionId}`, {
2043
+ method: "PATCH",
2044
+ body: JSON.stringify(updates)
2045
+ });
2046
+ }
2047
+ /**
2048
+ * Delete a session.
2049
+ */
2050
+ async deleteSession(sessionId) {
2051
+ return this.request(`/sessions/${sessionId}`, {
2052
+ method: "DELETE"
2053
+ });
2054
+ }
2055
+ /**
2056
+ * Cancel an active session query.
2057
+ */
2058
+ async cancelSession(sessionId) {
2059
+ return this.request(`/sessions/${sessionId}/cancel`, {
2060
+ method: "POST"
2061
+ });
2062
+ }
2063
+ /**
2064
+ * Reconnect to an active streaming session.
2065
+ * @returns Object with event emitter and abort function
2066
+ */
2067
+ reconnect(sessionId, lastEventId = -1) {
2068
+ const emitter = new AgentEventEmitter2();
2069
+ const controller = new AbortController();
2070
+ (async () => {
2071
+ try {
2072
+ const headers = await this.getHeaders();
2073
+ await createSSEStream2({
2074
+ url: `${this.baseUrl}/sessions/${sessionId}/reconnect`,
2075
+ method: "POST",
2076
+ headers,
2077
+ body: { last_event_id: lastEventId },
2078
+ emitter,
2079
+ signal: controller.signal
2080
+ });
2081
+ } catch (error) {
2082
+ emitter.emit("error", {
2083
+ error: error instanceof Error ? error.message : "Unknown error"
2084
+ });
2085
+ }
2086
+ })();
2087
+ return {
2088
+ emitter,
2089
+ abort: () => controller.abort()
2090
+ };
2091
+ }
2092
+ // ===========================================================================
2093
+ // Skills API - Personal
2094
+ // ===========================================================================
2095
+ /**
2096
+ * List personal skills.
2097
+ */
2098
+ async listSkills() {
2099
+ return this.request("/skills");
2100
+ }
2101
+ /**
2102
+ * Get personal skill content.
2103
+ */
2104
+ async getSkill(name) {
2105
+ const response = await this.request(`/skills/${name}`);
2106
+ return this.decodeBase64(response.content);
2107
+ }
2108
+ /**
2109
+ * Create or update a personal skill.
2110
+ */
2111
+ async saveSkill(skill) {
2112
+ const content = this.encodeBase64(skill.content);
2113
+ return this.request("/skills", {
2114
+ method: "POST",
2115
+ body: JSON.stringify({ ...skill, content })
2116
+ });
2117
+ }
2118
+ /**
2119
+ * Delete a personal skill.
2120
+ */
2121
+ async deleteSkill(name) {
2122
+ return this.request(`/skills/${name}`, {
2123
+ method: "DELETE"
2124
+ });
2125
+ }
2126
+ // ===========================================================================
2127
+ // Skills API - Organization
2128
+ // ===========================================================================
2129
+ /**
2130
+ * List organization skills.
2131
+ */
2132
+ async listOrgSkills() {
2133
+ return this.request("/skills/org");
2134
+ }
2135
+ /**
2136
+ * Get organization skill content.
2137
+ */
2138
+ async getOrgSkill(name) {
2139
+ const response = await this.request(
2140
+ `/skills/org/${name}`
2141
+ );
2142
+ return this.decodeBase64(response.content);
2143
+ }
2144
+ /**
2145
+ * Create or update an organization skill.
2146
+ */
2147
+ async saveOrgSkill(skill) {
2148
+ const content = this.encodeBase64(skill.content);
2149
+ return this.request("/skills/org", {
2150
+ method: "POST",
2151
+ body: JSON.stringify({ ...skill, content })
2152
+ });
2153
+ }
2154
+ /**
2155
+ * Delete an organization skill.
2156
+ */
2157
+ async deleteOrgSkill(name) {
2158
+ return this.request(`/skills/org/${name}`, {
2159
+ method: "DELETE"
2160
+ });
2161
+ }
2162
+ // ===========================================================================
2163
+ // Plugins API
2164
+ // ===========================================================================
2165
+ /**
2166
+ * Get plugin manifest for client sync.
2167
+ */
2168
+ async getPluginManifest() {
2169
+ return this.request("/plugins/manifest");
2170
+ }
2171
+ /**
2172
+ * Download plugin bundle.
2173
+ */
2174
+ async getPluginBundle(skills) {
2175
+ const query = skills?.length ? `?skills=${skills.join(",")}` : "";
2176
+ return this.request(`/plugins/bundle${query}`);
2177
+ }
2178
+ // ===========================================================================
2179
+ // Utility methods
2180
+ // ===========================================================================
2181
+ encodeBase64(data) {
2182
+ if (typeof data === "string") {
2183
+ const encoder = new TextEncoder();
2184
+ data = encoder.encode(data);
2185
+ }
2186
+ return btoa(String.fromCharCode(...data));
2187
+ }
2188
+ decodeBase64(base64) {
2189
+ return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
2190
+ }
2191
+ };
2192
+ var AgentContext = createContext(null);
2193
+ function AgentProvider({ children, config }) {
2194
+ const client = useMemo(
2195
+ () => new AgentClient2(config),
2196
+ // Only recreate client if endpoint changes
2197
+ // getAuthToken is a function and should not trigger recreation
2198
+ [config.endpoint, config.apiVersion, config.timeout]
2199
+ );
2200
+ return /* @__PURE__ */ jsx(AgentContext.Provider, { value: { client }, children });
2201
+ }
2202
+ function useAgentClient() {
2203
+ const context = useContext(AgentContext);
2204
+ if (!context) {
2205
+ throw new Error("useAgentClient must be used within an AgentProvider");
2206
+ }
2207
+ return context.client;
2208
+ }
2209
+ function useAgentSession(options = {}) {
2210
+ const client = useAgentClient();
2211
+ const { initialSessionId, initialConversation, onMediaGenerated } = options;
2212
+ const [session, setSession] = useState({
2213
+ sessionId: initialSessionId,
2214
+ conversation: initialConversation ?? [],
2215
+ isStreaming: false
2216
+ });
2217
+ const abortRef = useRef(null);
2218
+ const accumulatedTextRef = useRef({});
2219
+ const setupEventHandlers = useCallback(
2220
+ (emitter) => {
2221
+ emitter.on("session", (data) => {
2222
+ setSession((prev) => ({ ...prev, sessionId: data.session_id }));
2223
+ });
2224
+ emitter.on("content_block_start", (data) => {
2225
+ const block = data.content_block;
2226
+ if (block?.type === "text") {
2227
+ accumulatedTextRef.current[data.index] = "";
2228
+ setSession((prev) => ({ ...prev, showTinkering: false }));
2229
+ } else if (block?.type === "tool_use") {
2230
+ setSession((prev) => ({
2231
+ ...prev,
2232
+ streamingToolCall: {
2233
+ tool_name: block.name || "",
2234
+ tool_use_id: block.id || "",
2235
+ block_index: data.index,
2236
+ accumulated_input: ""
2237
+ },
2238
+ showTinkering: false
2239
+ }));
2240
+ }
2241
+ });
2242
+ emitter.on("content_block_delta", (data) => {
2243
+ const delta = data.delta;
2244
+ if (delta?.type === "text_delta" && delta.text) {
2245
+ const idx = data.index ?? 0;
2246
+ accumulatedTextRef.current[idx] = (accumulatedTextRef.current[idx] || "") + delta.text;
2247
+ setSession((prev) => ({
2248
+ ...prev,
2249
+ streamingText: accumulatedTextRef.current[idx],
2250
+ streamingBlockIndex: idx,
2251
+ showTinkering: false
2252
+ }));
2253
+ } else if (delta?.type === "input_json_delta" && delta.partial_json) {
2254
+ setSession((prev) => {
2255
+ if (!prev.streamingToolCall) return prev;
2256
+ return {
2257
+ ...prev,
2258
+ streamingToolCall: {
2259
+ ...prev.streamingToolCall,
2260
+ accumulated_input: (prev.streamingToolCall.accumulated_input || "") + delta.partial_json
2261
+ }
2262
+ };
2263
+ });
2264
+ }
2265
+ });
2266
+ emitter.on("content_block_stop", (data) => {
2267
+ const idx = data.index ?? 0;
2268
+ if (accumulatedTextRef.current[idx] !== void 0) {
2269
+ delete accumulatedTextRef.current[idx];
2270
+ setSession((prev) => ({
2271
+ ...prev,
2272
+ streamingText: void 0,
2273
+ streamingBlockIndex: void 0
2274
+ }));
2275
+ }
2276
+ });
2277
+ emitter.on("entry", (data) => {
2278
+ const entry = data.entry;
2279
+ if (entry.type === "tool_result" && onMediaGenerated) {
2280
+ const media = extractMediaFromEntry(entry);
2281
+ if (media) {
2282
+ onMediaGenerated(media.urls, media.type);
2283
+ }
2284
+ }
2285
+ if (entry.type === "tool_call") {
2286
+ setSession((prev) => ({
2287
+ ...prev,
2288
+ conversation: [...prev.conversation, entry],
2289
+ streamingToolCall: void 0
2290
+ }));
2291
+ } else {
2292
+ setSession((prev) => ({
2293
+ ...prev,
2294
+ conversation: [...prev.conversation, entry]
2295
+ }));
2296
+ }
2297
+ });
2298
+ emitter.on("complete", (data) => {
2299
+ setSession((prev) => ({
2300
+ ...prev,
2301
+ sessionId: data.session_id || prev.sessionId,
2302
+ isStreaming: false,
2303
+ showTinkering: false
2304
+ }));
2305
+ abortRef.current = null;
2306
+ });
2307
+ emitter.on("error", (data) => {
2308
+ setSession((prev) => ({
2309
+ ...prev,
2310
+ conversation: [
2311
+ ...prev.conversation,
2312
+ {
2313
+ type: "assistant_text",
2314
+ text: `Error: ${data.error}`,
2315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2316
+ }
2317
+ ],
2318
+ isStreaming: false,
2319
+ showTinkering: false
2320
+ }));
2321
+ abortRef.current = null;
2322
+ });
2323
+ },
2324
+ [onMediaGenerated]
2325
+ );
2326
+ const sendMessage = useCallback(
2327
+ async (prompt, skills) => {
2328
+ if (!prompt.trim() || session.isStreaming) return;
2329
+ const userEntry = {
2330
+ type: "user_text",
2331
+ text: prompt,
2332
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2333
+ };
2334
+ setSession((prev) => ({
2335
+ ...prev,
2336
+ conversation: [...prev.conversation, userEntry],
2337
+ isStreaming: true,
2338
+ showTinkering: true
2339
+ }));
2340
+ accumulatedTextRef.current = {};
2341
+ const { emitter, abort } = client.stream({
2342
+ prompt,
2343
+ session_id: session.sessionId,
2344
+ skills
2345
+ });
2346
+ abortRef.current = abort;
2347
+ setupEventHandlers(emitter);
2348
+ },
2349
+ [client, session.sessionId, session.isStreaming, setupEventHandlers]
2350
+ );
2351
+ const cancel = useCallback(async () => {
2352
+ abortRef.current?.();
2353
+ abortRef.current = null;
2354
+ if (session.sessionId) {
2355
+ try {
2356
+ await client.cancelSession(session.sessionId);
2357
+ } catch (error) {
2358
+ console.error("[Cancel] Server-side cancel failed:", error);
2359
+ }
2360
+ }
2361
+ setSession((prev) => ({
2362
+ ...prev,
2363
+ isStreaming: false,
2364
+ showTinkering: false,
2365
+ streamingText: void 0,
2366
+ streamingToolCall: void 0
2367
+ }));
2368
+ }, [client, session.sessionId]);
2369
+ const clear = useCallback(() => {
2370
+ setSession({
2371
+ conversation: [],
2372
+ isStreaming: false
2373
+ });
2374
+ }, []);
2375
+ const setSessionId = useCallback((id) => {
2376
+ setSession((prev) => ({ ...prev, sessionId: id }));
2377
+ }, []);
2378
+ const setConversation = useCallback(
2379
+ (entries, sessionId) => {
2380
+ setSession((prev) => ({
2381
+ ...prev,
2382
+ conversation: entries,
2383
+ sessionId: sessionId ?? prev.sessionId,
2384
+ isStreaming: false,
2385
+ streamingText: void 0,
2386
+ streamingToolCall: void 0,
2387
+ showTinkering: false
2388
+ }));
2389
+ },
2390
+ []
2391
+ );
2392
+ const reconnect = useCallback(
2393
+ async (targetSessionId, freshConversation, streamingPrompt) => {
2394
+ let conversation = freshConversation ?? [];
2395
+ if (streamingPrompt) {
2396
+ conversation = [
2397
+ ...conversation,
2398
+ {
2399
+ type: "user_text",
2400
+ text: streamingPrompt,
2401
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2402
+ }
2403
+ ];
2404
+ }
2405
+ setSession((prev) => ({
2406
+ ...prev,
2407
+ sessionId: targetSessionId,
2408
+ conversation,
2409
+ isStreaming: true,
2410
+ showTinkering: true
2411
+ }));
2412
+ accumulatedTextRef.current = {};
2413
+ const { emitter, abort } = client.reconnect(targetSessionId, -1);
2414
+ abortRef.current = abort;
2415
+ setupEventHandlers(emitter);
2416
+ },
2417
+ [client, setupEventHandlers]
2418
+ );
2419
+ const loadSession = useCallback(
2420
+ async (sessionId) => {
2421
+ const response = await client.getSession(sessionId);
2422
+ return response.conversation;
2423
+ },
2424
+ [client]
2425
+ );
2426
+ return {
2427
+ sessionId: session.sessionId,
2428
+ conversation: session.conversation,
2429
+ isStreaming: session.isStreaming,
2430
+ streamingText: session.streamingText,
2431
+ streamingToolCall: session.streamingToolCall,
2432
+ showTinkering: session.showTinkering ?? false,
2433
+ sendMessage,
2434
+ cancel,
2435
+ clear,
2436
+ reconnect,
2437
+ setSessionId,
2438
+ setConversation,
2439
+ loadSession
2440
+ };
2441
+ }
2442
+ function detectMediaType(url) {
2443
+ if (/\.(mp4|mov|webm|avi|mkv|m4v)(\?|$)/i.test(url)) return "video";
2444
+ if (/\.(mp3|wav|m4a|aac|ogg)(\?|$)/i.test(url)) return "audio";
2445
+ return "image";
2446
+ }
2447
+ function extractMediaFromEntry(entry) {
2448
+ if (entry.type !== "tool_result") return null;
2449
+ let result = null;
2450
+ if (typeof entry.content === "string") {
2451
+ try {
2452
+ result = JSON.parse(entry.content);
2453
+ } catch {
2454
+ return null;
2455
+ }
2456
+ }
2457
+ if (!result) return null;
2458
+ const mediaInfo = result.mediaInfo || result.data?.mediaInfo;
2459
+ if (mediaInfo?.urls && mediaInfo.urls.length > 0) {
2460
+ const type = detectMediaType(mediaInfo.urls[0]);
2461
+ return { urls: mediaInfo.urls, type };
2462
+ }
2463
+ const cdnUrls = result.output_cdn_urls || result.data?.output_cdn_urls;
2464
+ if (cdnUrls && cdnUrls.length > 0) {
2465
+ const type = detectMediaType(cdnUrls[0]);
2466
+ return { urls: cdnUrls, type };
2467
+ }
2468
+ return null;
2469
+ }
2470
+ function useSkills() {
2471
+ const client = useAgentClient();
2472
+ const [isSaving, setIsSaving] = useState(false);
2473
+ const {
2474
+ data: skillsData,
2475
+ error: skillsError,
2476
+ mutate: mutateSkills,
2477
+ isLoading: isLoadingSkills
2478
+ } = useSWR("agent-sdk-skills", () => client.listSkills(), {
2479
+ revalidateOnFocus: false,
2480
+ dedupingInterval: 6e4
2481
+ // 1 minute
2482
+ });
2483
+ const {
2484
+ data: orgSkillsData,
2485
+ error: orgSkillsError,
2486
+ mutate: mutateOrgSkills,
2487
+ isLoading: isLoadingOrgSkills
2488
+ } = useSWR(
2489
+ client.hasOrgContext ? "agent-sdk-skills-org" : null,
2490
+ () => client.listOrgSkills(),
2491
+ {
2492
+ revalidateOnFocus: false,
2493
+ dedupingInterval: 6e4
2494
+ }
2495
+ );
2496
+ const saveSkill = useCallback(
2497
+ async (skill) => {
2498
+ setIsSaving(true);
2499
+ try {
2500
+ await client.saveSkill(skill);
2501
+ await mutateSkills();
2502
+ } finally {
2503
+ setIsSaving(false);
2504
+ }
2505
+ },
2506
+ [client, mutateSkills]
2507
+ );
2508
+ const deleteSkill = useCallback(
2509
+ async (name) => {
2510
+ await client.deleteSkill(name);
2511
+ await mutateSkills();
2512
+ },
2513
+ [client, mutateSkills]
2514
+ );
2515
+ const refresh = useCallback(async () => {
2516
+ await mutateSkills();
2517
+ }, [mutateSkills]);
2518
+ const saveOrgSkill = useCallback(
2519
+ async (skill) => {
2520
+ setIsSaving(true);
2521
+ try {
2522
+ await client.saveOrgSkill(skill);
2523
+ await mutateOrgSkills();
2524
+ } finally {
2525
+ setIsSaving(false);
2526
+ }
2527
+ },
2528
+ [client, mutateOrgSkills]
2529
+ );
2530
+ const deleteOrgSkill = useCallback(
2531
+ async (name) => {
2532
+ await client.deleteOrgSkill(name);
2533
+ await mutateOrgSkills();
2534
+ },
2535
+ [client, mutateOrgSkills]
2536
+ );
2537
+ const refreshOrg = useCallback(async () => {
2538
+ await mutateOrgSkills();
2539
+ }, [mutateOrgSkills]);
2540
+ return {
2541
+ // Personal skills
2542
+ skills: skillsData?.skills ?? [],
2543
+ isLoading: isLoadingSkills || isSaving,
2544
+ error: skillsError,
2545
+ refresh,
2546
+ saveSkill,
2547
+ deleteSkill,
2548
+ // Org skills
2549
+ orgSkills: orgSkillsData?.skills ?? [],
2550
+ isLoadingOrg: isLoadingOrgSkills,
2551
+ orgError: orgSkillsError,
2552
+ refreshOrg,
2553
+ saveOrgSkill,
2554
+ deleteOrgSkill
2555
+ };
2556
+ }
2557
+ function useJadeSession(options = {}) {
2558
+ const {
2559
+ skipSkillContext = true,
2560
+ processingOptions,
2561
+ ...agentOptions
2562
+ } = options;
2563
+ const session = useAgentSession(agentOptions);
2564
+ const processedConversation = useMemo(
2565
+ () => processConversation(session.conversation, {
2566
+ skipSkillContext,
2567
+ pairToolResults: true,
2568
+ extractMedia: true,
2569
+ ...processingOptions
2570
+ }),
2571
+ [session.conversation, skipSkillContext, processingOptions]
2572
+ );
2573
+ const media = useMemo(
2574
+ () => extractMedia(session.conversation),
2575
+ [session.conversation]
2576
+ );
2577
+ return {
2578
+ ...session,
2579
+ processedConversation,
2580
+ media
2581
+ };
2582
+ }
2583
+ function useMedia(conversation) {
2584
+ return useMemo(() => extractMedia(conversation), [conversation]);
2585
+ }
2586
+
2587
+ // src/jade/react/JadeProvider.tsx
2588
+ var JadeProvider = AgentProvider;
1674
2589
 
1675
- export { AgentClient, AgentClientError, AgentEventEmitter, AuthenticationError, RequestError, SessionNotFoundError, SkillNotFoundError, TOOL_REGISTRY, createMediaParseResult, extractMedia, extractMediaInfo, extractMediaInfoFromToolResult, getMediaTypeFromExtension, getMediaTypeFromUrl, getToolDefinition, getToolIconName, hasSuggestions, parseSuggestions, parseToolResultContent, processConversation };
2590
+ export { AgentClient, AgentClientError, AgentEventEmitter, AgentProvider, AuthenticationError, AgentClient as JadeClient, JadeProvider, RequestError, SessionNotFoundError, SkillNotFoundError, TOOL_REGISTRY, createMediaParseResult, extractMedia, extractMediaInfo, extractMediaInfoFromToolResult, getMediaTypeFromExtension, getMediaTypeFromUrl, getToolDefinition, getToolIconName, hasSuggestions, parseSuggestions, parseToolResultContent, processConversation, useAgentClient, useAgentSession, useJadeSession, useMedia, useSkills };