assistant-cloud 0.1.31 → 0.1.32
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAPI.js","names":[],"sources":["../src/AssistantCloudAPI.ts"],"sourcesContent":["import {\n type AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nexport type AssistantCloudTelemetryConfig = {\n enabled?: boolean;\n /**\n * Called before each telemetry report is sent.\n * Return a modified report to enrich it (e.g. add `model_id`),\n * or return `null` to skip the report.\n */\n beforeReport?: (\n report: AssistantCloudRunReport,\n ) => AssistantCloudRunReport | null;\n};\n\nexport type AssistantCloudConfig = (\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n baseUrl?: string;\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n }\n) & {\n /**\n * Client-side run telemetry reporting. Default: `true`.\n *\n * When enabled, the SDK automatically reports run metadata (status, step\n * count, tool calls, and token usage) to Assistant Cloud after each\n * assistant message is saved. No message content is sent.\n *\n * - `true` / `undefined` — enabled with defaults\n * - `false` — disabled\n * - `{ beforeReport }` — enabled with a hook to enrich or filter reports\n */\n telemetry?: boolean | AssistantCloudTelemetryConfig;\n};\n\nclass CloudAPIError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"APIError\";\n }\n}\n\ntype MakeRequestOptions = {\n method?: \"POST\" | \"PUT\" | \"DELETE\" | undefined;\n headers?: Record<string, string> | undefined;\n query?: Record<string, string | number | boolean> | undefined;\n body?: object | undefined;\n};\n\nexport class AssistantCloudAPI {\n public _auth: AssistantCloudAuthStrategy;\n public _baseUrl;\n\n constructor(config: AssistantCloudConfig) {\n if (\"authToken\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);\n } else if (\"apiKey\" in config) {\n this._baseUrl = (\n config.baseUrl ?? \"https://backend.assistant-api.com\"\n ).replace(/\\/$/, \"\");\n this._auth = new AssistantCloudAPIKeyAuthStrategy(\n config.apiKey,\n config.userId,\n config.workspaceId,\n );\n } else if (\"anonymous\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);\n } else {\n throw new Error(\n \"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration\",\n );\n }\n }\n\n public async initializeAuth() {\n return !!this._auth.getAuthHeaders();\n }\n\n public async makeRawRequest(\n endpoint: string,\n options: MakeRequestOptions = {},\n ) {\n const authHeaders = await this._auth.getAuthHeaders();\n if (!authHeaders) throw new Error(\"Authorization failed\");\n\n const headers = {\n ...authHeaders,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n\n const queryParams = new URLSearchParams();\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value === false) continue;\n if (value === true) {\n queryParams.set(key, \"true\");\n } else {\n queryParams.set(key, value.toString());\n }\n }\n }\n\n const url = new URL(`${this._baseUrl}/v1${endpoint}`);\n url.search = queryParams.toString();\n\n const response = await fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body ? JSON.stringify(options.body) : null,\n });\n\n this._auth.readAuthHeaders(response.headers);\n\n if (!response.ok) {\n const text = await response.text();\n try {\n const body = JSON.parse(text);\n throw new CloudAPIError(body.message);\n } catch (error) {\n if (error instanceof CloudAPIError) throw error;\n throw new Error(\n `Request failed with status ${response.status}, ${text}`,\n );\n }\n }\n\n return response;\n }\n\n public async makeRequest(endpoint: string, options: MakeRequestOptions = {}) {\n const response = await this.makeRawRequest(endpoint, options);\n return response.json();\n }\n}\n"],"mappings":";;AAkDA,IAAM,gBAAN,cAA4B,MAAM;CAChC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AASA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CAEA,YAAY,QAA8B;EACxC,IAAI,eAAe,QAAQ;GACzB,KAAK,WAAW,OAAO;GACvB,KAAK,QAAQ,IAAI,8BAA8B,OAAO,SAAS;EACjE,OAAO,IAAI,YAAY,QAAQ;GAC7B,KAAK,YACH,OAAO,WAAW,
|
|
1
|
+
{"version":3,"file":"AssistantCloudAPI.js","names":[],"sources":["../src/AssistantCloudAPI.ts"],"sourcesContent":["import {\n type AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nexport type AssistantCloudTelemetryConfig = {\n enabled?: boolean;\n /**\n * Called before each telemetry report is sent.\n * Return a modified report to enrich it (e.g. add `model_id`),\n * or return `null` to skip the report.\n */\n beforeReport?: (\n report: AssistantCloudRunReport,\n ) => AssistantCloudRunReport | null;\n};\n\nexport type AssistantCloudConfig = (\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n baseUrl?: string;\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n }\n) & {\n /**\n * Client-side run telemetry reporting. Default: `true`.\n *\n * When enabled, the SDK automatically reports run metadata (status, step\n * count, tool calls, and token usage) to Assistant Cloud after each\n * assistant message is saved. No message content is sent.\n *\n * - `true` / `undefined` — enabled with defaults\n * - `false` — disabled\n * - `{ beforeReport }` — enabled with a hook to enrich or filter reports\n */\n telemetry?: boolean | AssistantCloudTelemetryConfig;\n};\n\nclass CloudAPIError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"APIError\";\n }\n}\n\ntype MakeRequestOptions = {\n method?: \"POST\" | \"PUT\" | \"DELETE\" | undefined;\n headers?: Record<string, string> | undefined;\n query?: Record<string, string | number | boolean> | undefined;\n body?: object | undefined;\n};\n\nexport class AssistantCloudAPI {\n public _auth: AssistantCloudAuthStrategy;\n public _baseUrl;\n\n constructor(config: AssistantCloudConfig) {\n if (\"authToken\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);\n } else if (\"apiKey\" in config) {\n this._baseUrl = (\n config.baseUrl ?? \"https://backend.assistant-api.com\"\n ).replace(/\\/$/, \"\");\n this._auth = new AssistantCloudAPIKeyAuthStrategy(\n config.apiKey,\n config.userId,\n config.workspaceId,\n );\n } else if (\"anonymous\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);\n } else {\n throw new Error(\n \"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration\",\n );\n }\n }\n\n public async initializeAuth() {\n return !!this._auth.getAuthHeaders();\n }\n\n public async makeRawRequest(\n endpoint: string,\n options: MakeRequestOptions = {},\n ) {\n const authHeaders = await this._auth.getAuthHeaders();\n if (!authHeaders) throw new Error(\"Authorization failed\");\n\n const headers = {\n ...authHeaders,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n\n const queryParams = new URLSearchParams();\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value === false) continue;\n if (value === true) {\n queryParams.set(key, \"true\");\n } else {\n queryParams.set(key, value.toString());\n }\n }\n }\n\n const url = new URL(`${this._baseUrl}/v1${endpoint}`);\n url.search = queryParams.toString();\n\n const response = await fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body ? JSON.stringify(options.body) : null,\n });\n\n this._auth.readAuthHeaders(response.headers);\n\n if (!response.ok) {\n const text = await response.text();\n try {\n const body = JSON.parse(text);\n throw new CloudAPIError(body.message);\n } catch (error) {\n if (error instanceof CloudAPIError) throw error;\n throw new Error(\n `Request failed with status ${response.status}, ${text}`,\n );\n }\n }\n\n return response;\n }\n\n public async makeRequest(endpoint: string, options: MakeRequestOptions = {}) {\n const response = await this.makeRawRequest(endpoint, options);\n return response.json();\n }\n}\n"],"mappings":";;AAkDA,IAAM,gBAAN,cAA4B,MAAM;CAChC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AASA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CAEA,YAAY,QAA8B;EACxC,IAAI,eAAe,QAAQ;GACzB,KAAK,WAAW,OAAO;GACvB,KAAK,QAAQ,IAAI,8BAA8B,OAAO,SAAS;EACjE,OAAO,IAAI,YAAY,QAAQ;GAC7B,KAAK,YACH,OAAO,WAAW,oCAAA,CAClB,QAAQ,OAAO,EAAE;GACnB,KAAK,QAAQ,IAAI,iCACf,OAAO,QACP,OAAO,QACP,OAAO,WACT;EACF,OAAO,IAAI,eAAe,QAAQ;GAChC,KAAK,WAAW,OAAO;GACvB,KAAK,QAAQ,IAAI,oCAAoC,OAAO,OAAO;EACrE,OACE,MAAM,IAAI,MACR,mFACF;CAEJ;CAEA,MAAa,iBAAiB;EAC5B,OAAO,CAAC,CAAC,KAAK,MAAM,eAAe;CACrC;CAEA,MAAa,eACX,UACA,UAA8B,CAAC,GAC/B;EACA,MAAM,cAAc,MAAM,KAAK,MAAM,eAAe;EACpD,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,sBAAsB;EAExD,MAAM,UAAU;GACd,GAAG;GACH,GAAG,QAAQ;GACX,gBAAgB;EAClB;EAEA,MAAM,cAAc,IAAI,gBAAgB;EACxC,IAAI,QAAQ,OACV,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,KAAK,GAAG;GACxD,IAAI,UAAU,OAAO;GACrB,IAAI,UAAU,MACZ,YAAY,IAAI,KAAK,MAAM;QAE3B,YAAY,IAAI,KAAK,MAAM,SAAS,CAAC;EAEzC;EAGF,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,SAAS,KAAK,UAAU;EACpD,IAAI,SAAS,YAAY,SAAS;EAElC,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ,QAAQ,UAAU;GAC1B;GACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;EACtD,CAAC;EAED,KAAK,MAAM,gBAAgB,SAAS,OAAO;EAE3C,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,OAAO,MAAM,SAAS,KAAK;GACjC,IAAI;IAEF,MAAM,IAAI,cADG,KAAK,MAAM,IACG,CAAC,CAAC,OAAO;GACtC,SAAS,OAAO;IACd,IAAI,iBAAiB,eAAe,MAAM;IAC1C,MAAM,IAAI,MACR,8BAA8B,SAAS,OAAO,IAAI,MACpD;GACF;EACF;EAEA,OAAO;CACT;CAEA,MAAa,YAAY,UAAkB,UAA8B,CAAC,GAAG;EAE3E,QAAO,MADgB,KAAK,eAAe,UAAU,OAAO,EAAA,CAC5C,KAAK;CACvB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAuthStrategy.js","names":["#authTokenCallback","#apiKey","#userId","#workspaceId"],"sources":["../src/AssistantCloudAuthStrategy.ts"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(`Unable to determine the token expiry: ${error}`);\n }\n};\n\nexport class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n // Fetch a new token via the callback\n const newToken = await this.#authTokenCallback();\n if (!newToken) return false;\n\n this.cachedToken = newToken;\n this.tokenExpiry = getJwtExpiry(newToken);\n\n return { Authorization: `Bearer ${newToken}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nexport class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {\n const currentTime = Date.now();\n const storedRefreshTokenJson = localStorage.getItem(\n AUI_REFRESH_TOKEN_NAME,\n );\n const storedRefreshToken = storedRefreshTokenJson\n ? (JSON.parse(storedRefreshTokenJson) as {\n token: string;\n expires_at: string;\n })\n : undefined;\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: storedRefreshToken.token }),\n },\n );\n\n if (response.ok) {\n const data = await response.json();\n const { access_token, refresh_token } = data;\n if (refresh_token) {\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n }\n return access_token;\n }\n } else {\n localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {\n method: \"POST\",\n });\n\n if (!response.ok) return null;\n\n const data = await response.json();\n const { access_token, refresh_token } = data;\n\n if (!access_token || !refresh_token) return null;\n\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n return access_token;\n });\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":";AAMA,MAAM,gBAAgB,QAAwB;CAC5C,IAAI;EAEF,MAAM,WADQ,IAAI,MAAM,GACH,
|
|
1
|
+
{"version":3,"file":"AssistantCloudAuthStrategy.js","names":["#authTokenCallback","#apiKey","#userId","#workspaceId"],"sources":["../src/AssistantCloudAuthStrategy.ts"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(`Unable to determine the token expiry: ${error}`);\n }\n};\n\nexport class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n // Fetch a new token via the callback\n const newToken = await this.#authTokenCallback();\n if (!newToken) return false;\n\n this.cachedToken = newToken;\n this.tokenExpiry = getJwtExpiry(newToken);\n\n return { Authorization: `Bearer ${newToken}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nexport class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {\n const currentTime = Date.now();\n const storedRefreshTokenJson = localStorage.getItem(\n AUI_REFRESH_TOKEN_NAME,\n );\n const storedRefreshToken = storedRefreshTokenJson\n ? (JSON.parse(storedRefreshTokenJson) as {\n token: string;\n expires_at: string;\n })\n : undefined;\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: storedRefreshToken.token }),\n },\n );\n\n if (response.ok) {\n const data = await response.json();\n const { access_token, refresh_token } = data;\n if (refresh_token) {\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n }\n return access_token;\n }\n } else {\n localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {\n method: \"POST\",\n });\n\n if (!response.ok) return null;\n\n const data = await response.json();\n const { access_token, refresh_token } = data;\n\n if (!access_token || !refresh_token) return null;\n\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n return access_token;\n });\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudMessagePersistence.js","names":[],"sources":["../src/CloudMessagePersistence.ts"],"sourcesContent":["import type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport type { AssistantCloud } from \"./AssistantCloud\";\n\n/**\n * Shared persistence logic for cloud message storage.\n *\n * Handles ID mapping (local → remote) and parent_id chaining for both:\n * - AssistantCloudThreadHistoryAdapter (assistant-ui runtime)\n * - useCloudChat (standalone AI SDK hook)\n *\n * The promise-based ID resolution handles concurrent appends — if message B's\n * parent is message A, and A is still being created, we await A's promise\n * to get its remote ID before creating B.\n */\nexport class CloudMessagePersistence {\n private idMapping: Record<string, string | Promise<string>> = {};\n\n constructor(private cloud: AssistantCloud) {}\n\n /**\n * Persist a message to the cloud.\n *\n * @param threadId - Remote thread ID\n * @param messageId - Local message ID (used for tracking)\n * @param parentId - Local parent message ID (or null for first message)\n * @param format - Message format (e.g., \"aui/v0\", \"ai-sdk/v6\")\n * @param content - Message content (format-specific)\n */\n async append(\n threadId: string,\n messageId: string,\n parentId: string | null,\n format: string,\n content: ReadonlyJSONObject,\n ): Promise<void> {\n // Resolve parent's remote ID if it exists (may be a promise if concurrent)\n const resolvedParentId = parentId\n ? ((await this.idMapping[parentId]) ?? parentId)\n : null;\n\n const task = this.cloud.threads.messages\n .create(threadId, {\n parent_id: resolvedParentId,\n format,\n content,\n })\n .then(({ message_id }) => {\n this.idMapping[messageId] = message_id;\n return message_id;\n })\n .catch((err) => {\n // Only delete if we're still the active task (avoids clobbering a retry)\n if (this.idMapping[messageId] === task) {\n delete this.idMapping[messageId];\n }\n throw err;\n });\n\n // Store the promise immediately so concurrent appends can await it\n this.idMapping[messageId] = task;\n return task.then(() => {});\n }\n\n /**\n * Update an already-persisted message in the cloud.\n */\n async update(\n threadId: string,\n messageId: string,\n _format: string,\n content: ReadonlyJSONObject,\n ): Promise<void> {\n const remoteId = await this.getRemoteId(messageId);\n if (!remoteId) return; // not persisted yet, skip\n await this.cloud.threads.messages.update(threadId, remoteId, { content });\n }\n\n /**\n * Check if a message has been persisted (or is currently being persisted).\n */\n isPersisted(messageId: string): boolean {\n return messageId in this.idMapping;\n }\n\n /**\n * Get the remote ID for a local message ID (resolved).\n * Returns undefined if not persisted.\n */\n async getRemoteId(messageId: string): Promise<string | undefined> {\n const entry = this.idMapping[messageId];\n if (!entry) return undefined;\n return entry;\n }\n\n /**\n * Load messages from the cloud and populate the ID mapping.\n *\n * The ID mapping is populated so that `isPersisted()` returns true for\n * loaded messages, preventing re-persistence of already-stored messages.\n *\n * @param threadId - Remote thread ID\n * @param format - Optional format filter\n * @returns Array of cloud messages\n */\n async load(threadId: string, format?: string) {\n const { messages } = await this.cloud.threads.messages.list(\n threadId,\n format ? { format } : undefined,\n );\n // Populate ID mapping so isPersisted() recognizes loaded messages\n for (const m of messages) {\n this.idMapping[m.id] = m.id;\n }\n return messages;\n }\n\n /**\n * Reset the ID mapping (call when switching threads).\n */\n reset() {\n this.idMapping = {};\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,IAAa,0BAAb,MAAqC;CAGf;CAFpB,YAA8D,CAAC;CAE/D,YAAY,OAA+B;EAAvB,KAAA,QAAA;CAAwB;;;;;;;;;;CAW5C,MAAM,OACJ,UACA,WACA,UACA,QACA,SACe;EAEf,MAAM,mBAAmB,WACnB,MAAM,KAAK,UAAU,aAAc,WACrC;EAEJ,MAAM,OAAO,KAAK,MAAM,QAAQ,SAC7B,OAAO,UAAU;GAChB,WAAW;GACX;GACA;EACF,CAAC,
|
|
1
|
+
{"version":3,"file":"CloudMessagePersistence.js","names":[],"sources":["../src/CloudMessagePersistence.ts"],"sourcesContent":["import type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport type { AssistantCloud } from \"./AssistantCloud\";\n\n/**\n * Shared persistence logic for cloud message storage.\n *\n * Handles ID mapping (local → remote) and parent_id chaining for both:\n * - AssistantCloudThreadHistoryAdapter (assistant-ui runtime)\n * - useCloudChat (standalone AI SDK hook)\n *\n * The promise-based ID resolution handles concurrent appends — if message B's\n * parent is message A, and A is still being created, we await A's promise\n * to get its remote ID before creating B.\n */\nexport class CloudMessagePersistence {\n private idMapping: Record<string, string | Promise<string>> = {};\n\n constructor(private cloud: AssistantCloud) {}\n\n /**\n * Persist a message to the cloud.\n *\n * @param threadId - Remote thread ID\n * @param messageId - Local message ID (used for tracking)\n * @param parentId - Local parent message ID (or null for first message)\n * @param format - Message format (e.g., \"aui/v0\", \"ai-sdk/v6\")\n * @param content - Message content (format-specific)\n */\n async append(\n threadId: string,\n messageId: string,\n parentId: string | null,\n format: string,\n content: ReadonlyJSONObject,\n ): Promise<void> {\n // Resolve parent's remote ID if it exists (may be a promise if concurrent)\n const resolvedParentId = parentId\n ? ((await this.idMapping[parentId]) ?? parentId)\n : null;\n\n const task = this.cloud.threads.messages\n .create(threadId, {\n parent_id: resolvedParentId,\n format,\n content,\n })\n .then(({ message_id }) => {\n this.idMapping[messageId] = message_id;\n return message_id;\n })\n .catch((err) => {\n // Only delete if we're still the active task (avoids clobbering a retry)\n if (this.idMapping[messageId] === task) {\n delete this.idMapping[messageId];\n }\n throw err;\n });\n\n // Store the promise immediately so concurrent appends can await it\n this.idMapping[messageId] = task;\n return task.then(() => {});\n }\n\n /**\n * Update an already-persisted message in the cloud.\n */\n async update(\n threadId: string,\n messageId: string,\n _format: string,\n content: ReadonlyJSONObject,\n ): Promise<void> {\n const remoteId = await this.getRemoteId(messageId);\n if (!remoteId) return; // not persisted yet, skip\n await this.cloud.threads.messages.update(threadId, remoteId, { content });\n }\n\n /**\n * Check if a message has been persisted (or is currently being persisted).\n */\n isPersisted(messageId: string): boolean {\n return messageId in this.idMapping;\n }\n\n /**\n * Get the remote ID for a local message ID (resolved).\n * Returns undefined if not persisted.\n */\n async getRemoteId(messageId: string): Promise<string | undefined> {\n const entry = this.idMapping[messageId];\n if (!entry) return undefined;\n return entry;\n }\n\n /**\n * Load messages from the cloud and populate the ID mapping.\n *\n * The ID mapping is populated so that `isPersisted()` returns true for\n * loaded messages, preventing re-persistence of already-stored messages.\n *\n * @param threadId - Remote thread ID\n * @param format - Optional format filter\n * @returns Array of cloud messages\n */\n async load(threadId: string, format?: string) {\n const { messages } = await this.cloud.threads.messages.list(\n threadId,\n format ? { format } : undefined,\n );\n // Populate ID mapping so isPersisted() recognizes loaded messages\n for (const m of messages) {\n this.idMapping[m.id] = m.id;\n }\n return messages;\n }\n\n /**\n * Reset the ID mapping (call when switching threads).\n */\n reset() {\n this.idMapping = {};\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,IAAa,0BAAb,MAAqC;CAGf;CAFpB,YAA8D,CAAC;CAE/D,YAAY,OAA+B;EAAvB,KAAA,QAAA;CAAwB;;;;;;;;;;CAW5C,MAAM,OACJ,UACA,WACA,UACA,QACA,SACe;EAEf,MAAM,mBAAmB,WACnB,MAAM,KAAK,UAAU,aAAc,WACrC;EAEJ,MAAM,OAAO,KAAK,MAAM,QAAQ,SAC7B,OAAO,UAAU;GAChB,WAAW;GACX;GACA;EACF,CAAC,CAAC,CACD,MAAM,EAAE,iBAAiB;GACxB,KAAK,UAAU,aAAa;GAC5B,OAAO;EACT,CAAC,CAAC,CACD,OAAO,QAAQ;GAEd,IAAI,KAAK,UAAU,eAAe,MAChC,OAAO,KAAK,UAAU;GAExB,MAAM;EACR,CAAC;EAGH,KAAK,UAAU,aAAa;EAC5B,OAAO,KAAK,WAAW,CAAC,CAAC;CAC3B;;;;CAKA,MAAM,OACJ,UACA,WACA,SACA,SACe;EACf,MAAM,WAAW,MAAM,KAAK,YAAY,SAAS;EACjD,IAAI,CAAC,UAAU;EACf,MAAM,KAAK,MAAM,QAAQ,SAAS,OAAO,UAAU,UAAU,EAAE,QAAQ,CAAC;CAC1E;;;;CAKA,YAAY,WAA4B;EACtC,OAAO,aAAa,KAAK;CAC3B;;;;;CAMA,MAAM,YAAY,WAAgD;EAChE,MAAM,QAAQ,KAAK,UAAU;EAC7B,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,OAAO;CACT;;;;;;;;;;;CAYA,MAAM,KAAK,UAAkB,QAAiB;EAC5C,MAAM,EAAE,aAAa,MAAM,KAAK,MAAM,QAAQ,SAAS,KACrD,UACA,SAAS,EAAE,OAAO,IAAI,KAAA,CACxB;EAEA,KAAK,MAAM,KAAK,UACd,KAAK,UAAU,EAAE,MAAM,EAAE;EAE3B,OAAO;CACT;;;;CAKA,QAAQ;EACN,KAAK,YAAY,CAAC;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormattedCloudPersistence.js","names":[],"sources":["../src/FormattedCloudPersistence.ts"],"sourcesContent":["import type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/**\n * Format adapter shape — structurally identical to the MessageFormatAdapter\n * in @assistant-ui/react, but defined here to avoid cross-package type moves.\n * TypeScript's structural typing ensures these are interchangeable.\n */\nexport type MessageFormatAdapter<TMessage, TStorageFormat> = {\n format: string;\n encode(item: { parentId: string | null; message: TMessage }): TStorageFormat;\n decode(stored: {\n id: string;\n parent_id: string | null;\n format: string;\n content: TStorageFormat;\n }): { parentId: string | null; message: TMessage };\n getId(message: TMessage): string;\n};\n\n/**\n * Wraps a CloudMessagePersistence instance with format-aware encode/decode.\n *\n * This centralizes the pattern used by both:\n * - useCloudChat (standalone AI SDK hook)\n * - AssistantCloudThreadHistoryAdapter.withFormat() (assistant-ui runtime)\n *\n * The persistence parameter is typed structurally (not by class) so callers\n * don't need to import CloudMessagePersistence directly.\n */\nexport const createFormattedPersistence = <TMessage, TStorageFormat>(\n persistence: {\n append: (\n threadId: string,\n messageId: string,\n parentId: string | null,\n format: string,\n content: ReadonlyJSONObject,\n ) => Promise<void>;\n load: (threadId: string, format?: string) => Promise<any[]>;\n isPersisted: (messageId: string) => boolean;\n update?: (\n threadId: string,\n messageId: string,\n format: string,\n content: ReadonlyJSONObject,\n ) => Promise<void>;\n },\n adapter: MessageFormatAdapter<TMessage, TStorageFormat>,\n) => ({\n append: async (\n threadId: string,\n item: { parentId: string | null; message: TMessage },\n ): Promise<void> => {\n const messageId = adapter.getId(item.message);\n const encoded = adapter.encode(item);\n return persistence.append(\n threadId,\n messageId,\n item.parentId,\n adapter.format,\n encoded as ReadonlyJSONObject,\n );\n },\n update: persistence.update\n ? async (\n threadId: string,\n item: { parentId: string | null; message: TMessage },\n messageId: string,\n ): Promise<void> => {\n const encoded = adapter.encode(item);\n return persistence.update!(\n threadId,\n messageId,\n adapter.format,\n encoded as ReadonlyJSONObject,\n );\n }\n : undefined,\n load: async (threadId: string) => {\n const messages = await persistence.load(threadId, adapter.format);\n return {\n messages: messages\n .filter((m) => m.format === adapter.format)\n .map((m) =>\n adapter.decode({\n id: m.id,\n parent_id: m.parent_id,\n format: m.format,\n content: m.content as TStorageFormat,\n }),\n )\n .reverse(),\n };\n },\n isPersisted: (messageId: string) => persistence.isPersisted(messageId),\n});\n"],"mappings":";;;;;;;;;;;AA6BA,MAAa,8BACX,aAiBA,aACI;CACJ,QAAQ,OACN,UACA,SACkB;EAClB,MAAM,YAAY,QAAQ,MAAM,KAAK,OAAO;EAC5C,MAAM,UAAU,QAAQ,OAAO,IAAI;EACnC,OAAO,YAAY,OACjB,UACA,WACA,KAAK,UACL,QAAQ,QACR,OACF;CACF;CACA,QAAQ,YAAY,SAChB,OACE,UACA,MACA,cACkB;EAClB,MAAM,UAAU,QAAQ,OAAO,IAAI;EACnC,OAAO,YAAY,OACjB,UACA,WACA,QAAQ,QACR,OACF;CACF,IACA,KAAA;CACJ,MAAM,OAAO,aAAqB;EAEhC,OAAO,EACL,WAAU,MAFW,YAAY,KAAK,UAAU,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"FormattedCloudPersistence.js","names":[],"sources":["../src/FormattedCloudPersistence.ts"],"sourcesContent":["import type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/**\n * Format adapter shape — structurally identical to the MessageFormatAdapter\n * in @assistant-ui/react, but defined here to avoid cross-package type moves.\n * TypeScript's structural typing ensures these are interchangeable.\n */\nexport type MessageFormatAdapter<TMessage, TStorageFormat> = {\n format: string;\n encode(item: { parentId: string | null; message: TMessage }): TStorageFormat;\n decode(stored: {\n id: string;\n parent_id: string | null;\n format: string;\n content: TStorageFormat;\n }): { parentId: string | null; message: TMessage };\n getId(message: TMessage): string;\n};\n\n/**\n * Wraps a CloudMessagePersistence instance with format-aware encode/decode.\n *\n * This centralizes the pattern used by both:\n * - useCloudChat (standalone AI SDK hook)\n * - AssistantCloudThreadHistoryAdapter.withFormat() (assistant-ui runtime)\n *\n * The persistence parameter is typed structurally (not by class) so callers\n * don't need to import CloudMessagePersistence directly.\n */\nexport const createFormattedPersistence = <TMessage, TStorageFormat>(\n persistence: {\n append: (\n threadId: string,\n messageId: string,\n parentId: string | null,\n format: string,\n content: ReadonlyJSONObject,\n ) => Promise<void>;\n load: (threadId: string, format?: string) => Promise<any[]>;\n isPersisted: (messageId: string) => boolean;\n update?: (\n threadId: string,\n messageId: string,\n format: string,\n content: ReadonlyJSONObject,\n ) => Promise<void>;\n },\n adapter: MessageFormatAdapter<TMessage, TStorageFormat>,\n) => ({\n append: async (\n threadId: string,\n item: { parentId: string | null; message: TMessage },\n ): Promise<void> => {\n const messageId = adapter.getId(item.message);\n const encoded = adapter.encode(item);\n return persistence.append(\n threadId,\n messageId,\n item.parentId,\n adapter.format,\n encoded as ReadonlyJSONObject,\n );\n },\n update: persistence.update\n ? async (\n threadId: string,\n item: { parentId: string | null; message: TMessage },\n messageId: string,\n ): Promise<void> => {\n const encoded = adapter.encode(item);\n return persistence.update!(\n threadId,\n messageId,\n adapter.format,\n encoded as ReadonlyJSONObject,\n );\n }\n : undefined,\n load: async (threadId: string) => {\n const messages = await persistence.load(threadId, adapter.format);\n return {\n messages: messages\n .filter((m) => m.format === adapter.format)\n .map((m) =>\n adapter.decode({\n id: m.id,\n parent_id: m.parent_id,\n format: m.format,\n content: m.content as TStorageFormat,\n }),\n )\n .reverse(),\n };\n },\n isPersisted: (messageId: string) => persistence.isPersisted(messageId),\n});\n"],"mappings":";;;;;;;;;;;AA6BA,MAAa,8BACX,aAiBA,aACI;CACJ,QAAQ,OACN,UACA,SACkB;EAClB,MAAM,YAAY,QAAQ,MAAM,KAAK,OAAO;EAC5C,MAAM,UAAU,QAAQ,OAAO,IAAI;EACnC,OAAO,YAAY,OACjB,UACA,WACA,KAAK,UACL,QAAQ,QACR,OACF;CACF;CACA,QAAQ,YAAY,SAChB,OACE,UACA,MACA,cACkB;EAClB,MAAM,UAAU,QAAQ,OAAO,IAAI;EACnC,OAAO,YAAY,OACjB,UACA,WACA,QAAQ,QACR,OACF;CACF,IACA,KAAA;CACJ,MAAM,OAAO,aAAqB;EAEhC,OAAO,EACL,WAAU,MAFW,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAA,CAG3D,QAAQ,MAAM,EAAE,WAAW,QAAQ,MAAM,CAAC,CAC1C,KAAK,MACJ,QAAQ,OAAO;GACb,IAAI,EAAE;GACN,WAAW,EAAE;GACb,QAAQ,EAAE;GACV,SAAS,EAAE;EACb,CAAC,CACH,CAAC,CACA,QAAQ,EACb;CACF;CACA,cAAc,cAAsB,YAAY,YAAY,SAAS;AACvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentMcpSampling.js","names":[],"sources":["../src/instrumentMcpSampling.ts"],"sourcesContent":["/**\n * MCP sampling instrumentation utility.\n *\n * Wraps an MCP client's sampling handler to capture nested LLM calls\n * (sampling/createMessage requests) made during tool execution.\n * The captured data can be reported as child generation spans.\n */\n\nexport type SamplingCallData = {\n model_id?: string;\n input_tokens?: number;\n output_tokens?: number;\n reasoning_tokens?: number;\n cached_input_tokens?: number;\n duration_ms?: number;\n};\n\nexport type McpSamplingHandler = (\n request: McpSamplingRequest,\n) => Promise<McpSamplingResponse>;\n\nexport type McpSamplingRequest = {\n method: \"sampling/createMessage\";\n params: {\n messages: unknown[];\n modelPreferences?: { hints?: { name?: string }[] };\n maxTokens?: number;\n [key: string]: unknown;\n };\n};\n\nexport type McpSamplingResponse = {\n model?: string;\n content: unknown;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n promptTokens?: number;\n completionTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n };\n [key: string]: unknown;\n};\n\n/**\n * Wraps an MCP sampling handler to intercept and measure sampling calls.\n *\n * @param handler - The original sampling handler from the MCP client\n * @param onSamplingCall - Callback invoked with metrics for each sampling call\n * @returns A wrapped handler that transparently captures sampling metrics\n *\n * @example\n * ```ts\n * const samplingCalls: SamplingCallData[] = [];\n * const wrapped = wrapSamplingHandler(\n * originalHandler,\n * (data) => samplingCalls.push(data),\n * );\n * // Use `wrapped` as the MCP client's sampling handler\n * // After tool execution, `samplingCalls` contains metrics for all nested LLM calls\n * ```\n */\nexport function wrapSamplingHandler(\n handler: McpSamplingHandler,\n onSamplingCall: (data: SamplingCallData) => void,\n): McpSamplingHandler {\n return async (request) => {\n const startTime = Date.now();\n const response = await handler(request);\n const durationMs = Date.now() - startTime;\n\n const modelId =\n response.model ?? request.params.modelPreferences?.hints?.[0]?.name;\n\n const inputTokens =\n response.usage?.inputTokens ?? response.usage?.promptTokens;\n const outputTokens =\n response.usage?.outputTokens ?? response.usage?.completionTokens;\n const reasoningTokens = response.usage?.reasoningTokens;\n const cachedInputTokens = response.usage?.cachedInputTokens;\n\n onSamplingCall({\n ...(modelId ? { model_id: modelId } : undefined),\n ...(inputTokens != null ? { input_tokens: inputTokens } : undefined),\n ...(outputTokens != null ? { output_tokens: outputTokens } : undefined),\n ...(reasoningTokens != null\n ? { reasoning_tokens: reasoningTokens }\n : undefined),\n ...(cachedInputTokens != null\n ? { cached_input_tokens: cachedInputTokens }\n : undefined),\n duration_ms: durationMs,\n });\n\n return response;\n };\n}\n\n/**\n * Creates a collector that accumulates sampling call data during tool execution.\n * Use with `wrapSamplingHandler` to capture all sampling calls for a tool invocation.\n *\n * @example\n * ```ts\n * const collector = createSamplingCollector();\n * const wrappedHandler = wrapSamplingHandler(handler, collector.collect);\n * // ... execute MCP tool ...\n * const calls = collector.getCalls(); // SamplingCallData[]\n * ```\n */\nexport function createSamplingCollector() {\n const calls: SamplingCallData[] = [];\n return {\n collect: (data: SamplingCallData) => calls.push(data),\n getCalls: () => [...calls],\n reset: () => {\n calls.length = 0;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,oBACd,SACA,gBACoB;CACpB,OAAO,OAAO,YAAY;EACxB,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,WAAW,MAAM,QAAQ,OAAO;EACtC,MAAM,aAAa,KAAK,IAAI,IAAI;EAEhC,MAAM,UACJ,SAAS,SAAS,QAAQ,OAAO,kBAAkB,QAAQ,
|
|
1
|
+
{"version":3,"file":"instrumentMcpSampling.js","names":[],"sources":["../src/instrumentMcpSampling.ts"],"sourcesContent":["/**\n * MCP sampling instrumentation utility.\n *\n * Wraps an MCP client's sampling handler to capture nested LLM calls\n * (sampling/createMessage requests) made during tool execution.\n * The captured data can be reported as child generation spans.\n */\n\nexport type SamplingCallData = {\n model_id?: string;\n input_tokens?: number;\n output_tokens?: number;\n reasoning_tokens?: number;\n cached_input_tokens?: number;\n duration_ms?: number;\n};\n\nexport type McpSamplingHandler = (\n request: McpSamplingRequest,\n) => Promise<McpSamplingResponse>;\n\nexport type McpSamplingRequest = {\n method: \"sampling/createMessage\";\n params: {\n messages: unknown[];\n modelPreferences?: { hints?: { name?: string }[] };\n maxTokens?: number;\n [key: string]: unknown;\n };\n};\n\nexport type McpSamplingResponse = {\n model?: string;\n content: unknown;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n promptTokens?: number;\n completionTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n };\n [key: string]: unknown;\n};\n\n/**\n * Wraps an MCP sampling handler to intercept and measure sampling calls.\n *\n * @param handler - The original sampling handler from the MCP client\n * @param onSamplingCall - Callback invoked with metrics for each sampling call\n * @returns A wrapped handler that transparently captures sampling metrics\n *\n * @example\n * ```ts\n * const samplingCalls: SamplingCallData[] = [];\n * const wrapped = wrapSamplingHandler(\n * originalHandler,\n * (data) => samplingCalls.push(data),\n * );\n * // Use `wrapped` as the MCP client's sampling handler\n * // After tool execution, `samplingCalls` contains metrics for all nested LLM calls\n * ```\n */\nexport function wrapSamplingHandler(\n handler: McpSamplingHandler,\n onSamplingCall: (data: SamplingCallData) => void,\n): McpSamplingHandler {\n return async (request) => {\n const startTime = Date.now();\n const response = await handler(request);\n const durationMs = Date.now() - startTime;\n\n const modelId =\n response.model ?? request.params.modelPreferences?.hints?.[0]?.name;\n\n const inputTokens =\n response.usage?.inputTokens ?? response.usage?.promptTokens;\n const outputTokens =\n response.usage?.outputTokens ?? response.usage?.completionTokens;\n const reasoningTokens = response.usage?.reasoningTokens;\n const cachedInputTokens = response.usage?.cachedInputTokens;\n\n onSamplingCall({\n ...(modelId ? { model_id: modelId } : undefined),\n ...(inputTokens != null ? { input_tokens: inputTokens } : undefined),\n ...(outputTokens != null ? { output_tokens: outputTokens } : undefined),\n ...(reasoningTokens != null\n ? { reasoning_tokens: reasoningTokens }\n : undefined),\n ...(cachedInputTokens != null\n ? { cached_input_tokens: cachedInputTokens }\n : undefined),\n duration_ms: durationMs,\n });\n\n return response;\n };\n}\n\n/**\n * Creates a collector that accumulates sampling call data during tool execution.\n * Use with `wrapSamplingHandler` to capture all sampling calls for a tool invocation.\n *\n * @example\n * ```ts\n * const collector = createSamplingCollector();\n * const wrappedHandler = wrapSamplingHandler(handler, collector.collect);\n * // ... execute MCP tool ...\n * const calls = collector.getCalls(); // SamplingCallData[]\n * ```\n */\nexport function createSamplingCollector() {\n const calls: SamplingCallData[] = [];\n return {\n collect: (data: SamplingCallData) => calls.push(data),\n getCalls: () => [...calls],\n reset: () => {\n calls.length = 0;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,oBACd,SACA,gBACoB;CACpB,OAAO,OAAO,YAAY;EACxB,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,WAAW,MAAM,QAAQ,OAAO;EACtC,MAAM,aAAa,KAAK,IAAI,IAAI;EAEhC,MAAM,UACJ,SAAS,SAAS,QAAQ,OAAO,kBAAkB,QAAQ,EAAE,EAAE;EAEjE,MAAM,cACJ,SAAS,OAAO,eAAe,SAAS,OAAO;EACjD,MAAM,eACJ,SAAS,OAAO,gBAAgB,SAAS,OAAO;EAClD,MAAM,kBAAkB,SAAS,OAAO;EACxC,MAAM,oBAAoB,SAAS,OAAO;EAE1C,eAAe;GACb,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,KAAA;GACtC,GAAI,eAAe,OAAO,EAAE,cAAc,YAAY,IAAI,KAAA;GAC1D,GAAI,gBAAgB,OAAO,EAAE,eAAe,aAAa,IAAI,KAAA;GAC7D,GAAI,mBAAmB,OACnB,EAAE,kBAAkB,gBAAgB,IACpC,KAAA;GACJ,GAAI,qBAAqB,OACrB,EAAE,qBAAqB,kBAAkB,IACzC,KAAA;GACJ,aAAa;EACf,CAAC;EAED,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,0BAA0B;CACxC,MAAM,QAA4B,CAAC;CACnC,OAAO;EACL,UAAU,SAA2B,MAAM,KAAK,IAAI;EACpD,gBAAgB,CAAC,GAAG,KAAK;EACzB,aAAa;GACX,MAAM,SAAS;EACjB;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-cloud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Cloud integration for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"assistant-stream": "^0.3.
|
|
30
|
+
"assistant-stream": "^0.3.21"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^25.9.
|
|
34
|
-
"vitest": "^4.1.
|
|
35
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
33
|
+
"@types/node": "^25.9.2",
|
|
34
|
+
"vitest": "^4.1.8",
|
|
35
|
+
"@assistant-ui/x-buildutils": "0.0.12"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public",
|