@guidekit/core 0.1.0-beta.2 → 0.1.0-beta.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
@@ -1092,7 +1092,7 @@ var DOMScanner = class {
1092
1092
  const style = window.getComputedStyle(el);
1093
1093
  const position = style.position;
1094
1094
  const zIndex = parseInt(style.zIndex, 10) || 0;
1095
- if ((position === "fixed" || position === "absolute") && !isNaN(zIndex) && zIndex >= 1e3) {
1095
+ if ((position === "fixed" || position === "absolute") && !Number.isNaN(zIndex) && zIndex >= 1e3) {
1096
1096
  const visible = isElementVisible(el);
1097
1097
  if (!visible) return;
1098
1098
  const overlayType = this.classifyOverlay(el, style);
@@ -1120,10 +1120,10 @@ var DOMScanner = class {
1120
1120
  return "dropdown";
1121
1121
  const width = parseFloat(style.width);
1122
1122
  const height = parseFloat(style.height);
1123
- if (typeof window !== "undefined" && !isNaN(width) && !isNaN(height) && width > window.innerWidth * 0.5 && height > window.innerHeight * 0.5) {
1123
+ if (typeof window !== "undefined" && !Number.isNaN(width) && !Number.isNaN(height) && width > window.innerWidth * 0.5 && height > window.innerHeight * 0.5) {
1124
1124
  return "modal";
1125
1125
  }
1126
- if (!isNaN(width) && width < 400) return "popover";
1126
+ if (!Number.isNaN(width) && width < 400) return "popover";
1127
1127
  return null;
1128
1128
  }
1129
1129
  // -------------------------------------------------------------------------
@@ -1839,424 +1839,9 @@ function isGuideKitError(error) {
1839
1839
  return error instanceof GuideKitError;
1840
1840
  }
1841
1841
 
1842
- // src/llm/openai-adapter.ts
1843
- var DEFAULT_OPENAI_MODEL = "gpt-4o";
1844
- var DEFAULT_TIMEOUT_MS = 15e3;
1845
- var OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
1846
- function emptyUsage() {
1847
- return { prompt: 0, completion: 0, total: 0 };
1848
- }
1849
- var OpenAIAdapter = class {
1850
- apiKey;
1851
- model;
1852
- /** Tracks whether the last extractChunks call emitted a done chunk. */
1853
- lastExtractEmittedDone = false;
1854
- /**
1855
- * Token usage extracted from the most recent `parseResponse` call.
1856
- * Updated as each SSE chunk is parsed.
1857
- */
1858
- _lastUsage = emptyUsage();
1859
- constructor(config) {
1860
- this.apiKey = config.apiKey;
1861
- this.model = config.model ?? DEFAULT_OPENAI_MODEL;
1862
- }
1863
- /** Token usage from the most recent parseResponse call. */
1864
- get lastUsage() {
1865
- return this._lastUsage;
1866
- }
1867
- // -----------------------------------------------------------------------
1868
- // LLMProviderAdapter implementation
1869
- // -----------------------------------------------------------------------
1870
- /**
1871
- * Convert GuideKit tool definitions into OpenAI's `tools` format.
1872
- * Each tool is wrapped as `{ type: 'function', function: { name, description, parameters } }`.
1873
- */
1874
- formatTools(tools) {
1875
- if (tools.length === 0) return void 0;
1876
- return tools.map((tool) => ({
1877
- type: "function",
1878
- function: {
1879
- name: tool.name,
1880
- description: tool.description,
1881
- parameters: {
1882
- type: "object",
1883
- properties: { ...tool.parameters },
1884
- required: tool.required ?? []
1885
- }
1886
- }
1887
- }));
1888
- }
1889
- /**
1890
- * Convert an array of `ConversationTurn` objects into OpenAI's messages
1891
- * format with `role: 'user' | 'assistant'`.
1892
- */
1893
- formatConversation(history) {
1894
- return history.map((turn) => ({
1895
- role: turn.role,
1896
- content: turn.content
1897
- }));
1898
- }
1899
- /**
1900
- * Parse an OpenAI SSE streaming response into an async iterable of
1901
- * `TextChunk` and `ToolCall` objects.
1902
- *
1903
- * The OpenAI streaming endpoint sends each chunk as a JSON object
1904
- * prefixed by `data: `. The final line is `data: [DONE]`.
1905
- * Text content arrives in `choices[0].delta.content` and tool calls
1906
- * arrive in `choices[0].delta.tool_calls`.
1907
- *
1908
- * This method also:
1909
- * - Detects content filtering and throws `ContentFilterError`.
1910
- * - Tracks token usage (accessible via `lastUsage` after iteration).
1911
- */
1912
- async *parseResponse(stream) {
1913
- const reader = stream.getReader();
1914
- const decoder = new TextDecoder();
1915
- let buffer = "";
1916
- let doneEmitted = false;
1917
- this._lastUsage = emptyUsage();
1918
- const pendingToolCalls = /* @__PURE__ */ new Map();
1919
- try {
1920
- while (true) {
1921
- const { done, value } = await reader.read();
1922
- if (done) break;
1923
- buffer += decoder.decode(value, { stream: true });
1924
- const lines = buffer.split("\n");
1925
- buffer = lines.pop() ?? "";
1926
- for (const line of lines) {
1927
- const trimmed = line.trim();
1928
- if (!trimmed.startsWith("data:")) continue;
1929
- const jsonStr = trimmed.slice(5).trim();
1930
- if (jsonStr === "" || jsonStr === "[DONE]") {
1931
- if (jsonStr === "[DONE]") {
1932
- yield* this.flushPendingToolCalls(pendingToolCalls);
1933
- if (!doneEmitted) {
1934
- doneEmitted = true;
1935
- yield { text: "", done: true };
1936
- }
1937
- }
1938
- continue;
1939
- }
1940
- let parsed;
1941
- try {
1942
- parsed = JSON.parse(jsonStr);
1943
- } catch {
1944
- continue;
1945
- }
1946
- if (this.isContentFiltered(parsed)) {
1947
- throw new ContentFilterError({
1948
- code: ErrorCodes.CONTENT_FILTER_TRIGGERED,
1949
- message: "Response was blocked by provider content safety filter.",
1950
- provider: "openai",
1951
- suggestion: "Rephrase your question or adjust safety settings."
1952
- });
1953
- }
1954
- const chunkUsage = this.extractUsage(parsed);
1955
- if (chunkUsage) {
1956
- this._lastUsage = chunkUsage;
1957
- }
1958
- yield* this.extractChunks(parsed, pendingToolCalls, doneEmitted);
1959
- if (!doneEmitted && this.lastExtractEmittedDone) {
1960
- doneEmitted = true;
1961
- }
1962
- }
1963
- }
1964
- if (buffer.trim().startsWith("data:")) {
1965
- const jsonStr = buffer.trim().slice(5).trim();
1966
- if (jsonStr === "[DONE]") {
1967
- yield* this.flushPendingToolCalls(pendingToolCalls);
1968
- if (!doneEmitted) {
1969
- doneEmitted = true;
1970
- yield { text: "", done: true };
1971
- }
1972
- } else if (jsonStr !== "") {
1973
- try {
1974
- const parsed = JSON.parse(jsonStr);
1975
- if (this.isContentFiltered(parsed)) {
1976
- throw new ContentFilterError({
1977
- code: ErrorCodes.CONTENT_FILTER_TRIGGERED,
1978
- message: "Response was blocked by provider content safety filter.",
1979
- provider: "openai",
1980
- suggestion: "Rephrase your question or adjust safety settings."
1981
- });
1982
- }
1983
- const chunkUsage = this.extractUsage(parsed);
1984
- if (chunkUsage) {
1985
- this._lastUsage = chunkUsage;
1986
- }
1987
- yield* this.extractChunks(parsed, pendingToolCalls, doneEmitted);
1988
- if (!doneEmitted && this.lastExtractEmittedDone) {
1989
- doneEmitted = true;
1990
- }
1991
- } catch (error) {
1992
- if (error instanceof ContentFilterError) throw error;
1993
- }
1994
- }
1995
- }
1996
- yield* this.flushPendingToolCalls(pendingToolCalls);
1997
- } finally {
1998
- reader.releaseLock();
1999
- }
2000
- }
2001
- /**
2002
- * Format a tool result so it can be sent back to OpenAI as a
2003
- * `tool` role message with the `tool_call_id`.
2004
- */
2005
- formatToolResult(callId, result) {
2006
- return {
2007
- role: "tool",
2008
- tool_call_id: callId,
2009
- content: typeof result === "string" ? result : JSON.stringify(result)
2010
- };
2011
- }
2012
- // -----------------------------------------------------------------------
2013
- // Streaming request
2014
- // -----------------------------------------------------------------------
2015
- /**
2016
- * Build and execute a streaming request to the OpenAI Chat Completions API.
2017
- * Returns the raw `ReadableStream` for the response body together with
2018
- * the raw Response object.
2019
- */
2020
- async streamRequest(params) {
2021
- const contentsArray = params.contents;
2022
- const messages = [
2023
- { role: "system", content: params.systemPrompt },
2024
- ...contentsArray
2025
- ];
2026
- if (params.userMessage) {
2027
- messages.push({ role: "user", content: params.userMessage });
2028
- }
2029
- const body = {
2030
- model: this.model,
2031
- messages,
2032
- stream: true,
2033
- temperature: 0.7,
2034
- top_p: 0.95
2035
- };
2036
- if (params.tools) {
2037
- body.tools = params.tools;
2038
- }
2039
- const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2040
- const controller = new AbortController();
2041
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2042
- if (params.signal) {
2043
- params.signal.addEventListener(
2044
- "abort",
2045
- () => controller.abort(params.signal.reason),
2046
- { once: true }
2047
- );
2048
- }
2049
- let response;
2050
- try {
2051
- response = await fetch(OPENAI_CHAT_URL, {
2052
- method: "POST",
2053
- headers: {
2054
- "Content-Type": "application/json",
2055
- Authorization: `Bearer ${this.apiKey}`
2056
- },
2057
- body: JSON.stringify(body),
2058
- signal: controller.signal
2059
- });
2060
- } catch (error) {
2061
- clearTimeout(timeoutId);
2062
- if (error instanceof DOMException && error.name === "AbortError") {
2063
- if (params.signal?.aborted) {
2064
- throw error;
2065
- }
2066
- throw new TimeoutError({
2067
- code: ErrorCodes.TIMEOUT_LLM_RESPONSE,
2068
- message: `OpenAI request timed out after ${timeoutMs}ms`,
2069
- provider: "openai",
2070
- recoverable: true,
2071
- suggestion: "Try again or increase the timeout.",
2072
- operationName: "openai.chatCompletions",
2073
- timeoutMs
2074
- });
2075
- }
2076
- throw new NetworkError({
2077
- code: ErrorCodes.NETWORK_CONNECTION_LOST,
2078
- message: `Failed to connect to OpenAI API: ${error.message}`,
2079
- provider: "openai",
2080
- suggestion: "Check your network connection and try again.",
2081
- cause: error instanceof Error ? error : void 0
2082
- });
2083
- }
2084
- clearTimeout(timeoutId);
2085
- if (!response.ok) {
2086
- await this.handleHttpError(response);
2087
- }
2088
- if (!response.body) {
2089
- throw new NetworkError({
2090
- code: ErrorCodes.NETWORK_CONNECTION_LOST,
2091
- message: "OpenAI response body is null -- streaming unavailable.",
2092
- provider: "openai",
2093
- suggestion: "Retry the request."
2094
- });
2095
- }
2096
- return { stream: response.body, response };
2097
- }
2098
- // -----------------------------------------------------------------------
2099
- // Internal helpers
2100
- // -----------------------------------------------------------------------
2101
- /**
2102
- * Extract `TextChunk` and accumulate `ToolCall` data from a single parsed
2103
- * OpenAI SSE JSON object.
2104
- *
2105
- * OpenAI tool calls arrive incrementally: the first chunk for a tool call
2106
- * carries the `id` and `function.name`, while subsequent chunks append to
2107
- * `function.arguments`. We accumulate these in `pendingToolCalls` and only
2108
- * yield complete `ToolCall` objects when the finish_reason is 'tool_calls'
2109
- * or when flushed.
2110
- */
2111
- *extractChunks(parsed, pendingToolCalls, doneEmitted) {
2112
- this.lastExtractEmittedDone = false;
2113
- const choices = parsed.choices;
2114
- if (!choices || choices.length === 0) return;
2115
- for (const choice of choices) {
2116
- const delta = choice.delta;
2117
- const finishReason = choice.finish_reason;
2118
- if (delta) {
2119
- if (typeof delta.content === "string" && delta.content !== "") {
2120
- yield {
2121
- text: delta.content,
2122
- done: false
2123
- };
2124
- }
2125
- const toolCallDeltas = delta.tool_calls;
2126
- if (toolCallDeltas) {
2127
- for (const tc of toolCallDeltas) {
2128
- const existing = pendingToolCalls.get(tc.index);
2129
- if (existing) {
2130
- if (tc.function?.arguments) {
2131
- existing.argumentsJson += tc.function.arguments;
2132
- }
2133
- } else {
2134
- pendingToolCalls.set(tc.index, {
2135
- id: tc.id ?? "",
2136
- name: tc.function?.name ?? "",
2137
- argumentsJson: tc.function?.arguments ?? ""
2138
- });
2139
- }
2140
- }
2141
- }
2142
- }
2143
- if (finishReason === "tool_calls") {
2144
- yield* this.flushPendingToolCalls(pendingToolCalls);
2145
- }
2146
- if (finishReason === "stop" && !doneEmitted && !this.lastExtractEmittedDone) {
2147
- this.lastExtractEmittedDone = true;
2148
- yield { text: "", done: true };
2149
- }
2150
- }
2151
- }
2152
- /**
2153
- * Flush all accumulated pending tool calls as complete `ToolCall` objects.
2154
- */
2155
- *flushPendingToolCalls(pendingToolCalls) {
2156
- const sorted = [...pendingToolCalls.entries()].sort(
2157
- ([a], [b]) => a - b
2158
- );
2159
- for (const [, tc] of sorted) {
2160
- let args = {};
2161
- try {
2162
- args = JSON.parse(tc.argumentsJson);
2163
- } catch (_e) {
2164
- console.warn("[GuideKit:LLM] Failed to parse tool call arguments:", tc.argumentsJson);
2165
- }
2166
- yield {
2167
- id: tc.id,
2168
- name: tc.name,
2169
- arguments: args
2170
- };
2171
- }
2172
- pendingToolCalls.clear();
2173
- }
2174
- /**
2175
- * Extract token usage from a parsed OpenAI response chunk.
2176
- * Usage data typically appears in the final chunk when `stream_options`
2177
- * includes `include_usage`, or in the non-streaming response.
2178
- * Returns `null` if no usage data is present.
2179
- */
2180
- extractUsage(parsed) {
2181
- const usage = parsed.usage;
2182
- if (!usage) return null;
2183
- return {
2184
- prompt: usage.prompt_tokens ?? 0,
2185
- completion: usage.completion_tokens ?? 0,
2186
- total: usage.total_tokens ?? 0
2187
- };
2188
- }
2189
- /**
2190
- * Check whether a parsed OpenAI chunk indicates the response was
2191
- * blocked by a content filter.
2192
- *
2193
- * OpenAI signals content filtering through:
2194
- * - `choices[].finish_reason === 'content_filter'`
2195
- * - `choices[].content_filter_results` with `filtered: true`
2196
- */
2197
- isContentFiltered(parsed) {
2198
- const choices = parsed.choices;
2199
- if (!choices || choices.length === 0) return false;
2200
- return choices.some((choice) => {
2201
- if (choice.finish_reason === "content_filter") return true;
2202
- const filterResults = choice.content_filter_results;
2203
- if (filterResults) {
2204
- return Object.values(filterResults).some((r) => r.filtered === true);
2205
- }
2206
- return false;
2207
- });
2208
- }
2209
- /**
2210
- * Translate an HTTP error response from OpenAI into the appropriate
2211
- * GuideKit error class.
2212
- */
2213
- async handleHttpError(response) {
2214
- let errorBody = "";
2215
- try {
2216
- errorBody = await response.text();
2217
- } catch {
2218
- }
2219
- const status = response.status;
2220
- if (status === 401 || status === 403) {
2221
- throw new AuthenticationError({
2222
- code: ErrorCodes.AUTH_INVALID_KEY,
2223
- message: `OpenAI API authentication failed (${status}): ${errorBody}`,
2224
- provider: "openai",
2225
- suggestion: "Verify your OpenAI API key is correct and has not expired."
2226
- });
2227
- }
2228
- if (status === 429) {
2229
- const retryAfterHeader = response.headers.get("retry-after");
2230
- const retryAfterMs = retryAfterHeader ? parseInt(retryAfterHeader, 10) * 1e3 : 6e4;
2231
- throw new RateLimitError({
2232
- code: ErrorCodes.RATE_LIMIT_PROVIDER,
2233
- message: `OpenAI API rate limit exceeded (429): ${errorBody}`,
2234
- provider: "openai",
2235
- recoverable: true,
2236
- suggestion: `Rate limited by OpenAI. Retry after ${Math.ceil(retryAfterMs / 1e3)}s.`,
2237
- retryAfterMs
2238
- });
2239
- }
2240
- if (status >= 500) {
2241
- throw new NetworkError({
2242
- code: ErrorCodes.NETWORK_CONNECTION_LOST,
2243
- message: `OpenAI API server error (${status}): ${errorBody}`,
2244
- provider: "openai",
2245
- suggestion: "The OpenAI API is experiencing issues. Please try again later."
2246
- });
2247
- }
2248
- throw new NetworkError({
2249
- code: ErrorCodes.NETWORK_CONNECTION_LOST,
2250
- message: `OpenAI API request failed (${status}): ${errorBody}`,
2251
- provider: "openai",
2252
- suggestion: "Check the request parameters and try again."
2253
- });
2254
- }
2255
- };
2256
-
2257
1842
  // src/llm/index.ts
2258
1843
  var DEFAULT_GEMINI_MODEL = "gemini-2.5-flash";
2259
- var DEFAULT_TIMEOUT_MS2 = 15e3;
1844
+ var DEFAULT_TIMEOUT_MS = 15e3;
2260
1845
  var GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models";
2261
1846
  var DEFAULT_SAFETY_SETTINGS = [
2262
1847
  { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_ONLY_HIGH" },
@@ -2264,7 +1849,7 @@ var DEFAULT_SAFETY_SETTINGS = [
2264
1849
  { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_ONLY_HIGH" },
2265
1850
  { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_ONLY_HIGH" }
2266
1851
  ];
2267
- function emptyUsage2() {
1852
+ function emptyUsage() {
2268
1853
  return { prompt: 0, completion: 0, total: 0 };
2269
1854
  }
2270
1855
  var GeminiAdapter = class {
@@ -2275,7 +1860,7 @@ var GeminiAdapter = class {
2275
1860
  * Updated as each SSE chunk is parsed; the final value reflects the
2276
1861
  * cumulative usage metadata sent by Gemini (typically in the last chunk).
2277
1862
  */
2278
- _lastUsage = emptyUsage2();
1863
+ _lastUsage = emptyUsage();
2279
1864
  constructor(config) {
2280
1865
  this.apiKey = config.apiKey;
2281
1866
  this.model = config.model ?? DEFAULT_GEMINI_MODEL;
@@ -2333,7 +1918,7 @@ var GeminiAdapter = class {
2333
1918
  const reader = stream.getReader();
2334
1919
  const decoder = new TextDecoder();
2335
1920
  let buffer = "";
2336
- this._lastUsage = emptyUsage2();
1921
+ this._lastUsage = emptyUsage();
2337
1922
  try {
2338
1923
  while (true) {
2339
1924
  const { done, value } = await reader.read();
@@ -2442,7 +2027,7 @@ var GeminiAdapter = class {
2442
2027
  if (params.tools) {
2443
2028
  body.tools = params.tools;
2444
2029
  }
2445
- const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
2030
+ const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2446
2031
  const controller = new AbortController();
2447
2032
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2448
2033
  if (params.signal) {
@@ -2717,7 +2302,7 @@ var LLMOrchestrator = class {
2717
2302
  }
2718
2303
  }
2719
2304
  this.callbacks.onChunk?.({ text: "", done: true });
2720
- let usage = emptyUsage2();
2305
+ let usage = emptyUsage();
2721
2306
  if ("lastUsage" in adapter) {
2722
2307
  usage = adapter.lastUsage;
2723
2308
  }
@@ -2737,7 +2322,7 @@ var LLMOrchestrator = class {
2737
2322
  *
2738
2323
  * Custom adapters:
2739
2324
  * - Pass `{ adapter: myAdapter }` to use any `LLMProviderAdapter`.
2740
- * Example: `llm: { adapter: new OpenAIAdapter({ ... }) }`
2325
+ * Example: `llm: { adapter: myCustomAdapter }`
2741
2326
  */
2742
2327
  createAdapter(config) {
2743
2328
  if ("adapter" in config) {
@@ -9617,6 +9202,6 @@ var GuideKitCore = class {
9617
9202
  }
9618
9203
  };
9619
9204
 
9620
- export { AuthenticationError, AwarenessSystem, BrowserSupportError, ConfigurationError, ConnectionManager, ContentFilterError, ContextManager, DOMScanner, ErrorCodes, EventBus, GeminiAdapter, GuideKitCore, GuideKitError, I18n, InitializationError, LLMOrchestrator, NavigationController, NetworkError, OpenAIAdapter, PermissionError, ProactiveTriggerEngine, RateLimitError, RateLimiter, ResourceExhaustedError, ResourceManager, SingletonGuard, TimeoutError, TokenManager, ToolExecutor, VisualGuidance, VoicePipeline, WebSpeechSTT, WebSpeechTTS, createEventBus, isGuideKitError };
9205
+ export { AuthenticationError, AwarenessSystem, BrowserSupportError, ConfigurationError, ConnectionManager, ContentFilterError, ContextManager, DOMScanner, ErrorCodes, EventBus, GeminiAdapter, GuideKitCore, GuideKitError, I18n, InitializationError, LLMOrchestrator, NavigationController, NetworkError, PermissionError, ProactiveTriggerEngine, RateLimitError, RateLimiter, ResourceExhaustedError, ResourceManager, SingletonGuard, TimeoutError, TokenManager, ToolExecutor, VisualGuidance, VoicePipeline, WebSpeechSTT, WebSpeechTTS, createEventBus, isGuideKitError };
9621
9206
  //# sourceMappingURL=index.js.map
9622
9207
  //# sourceMappingURL=index.js.map