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