@revealui/ai 0.5.2 → 0.6.2

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/README.md CHANGED
@@ -1,3 +1,11 @@
1
+ ---
2
+ title: "@revealui/ai"
3
+ description: "AI system for RevealUI - memory, LLM, orchestration, and tools."
4
+ visibility: public
5
+ status: verified
6
+ audience: user
7
+ ---
8
+
1
9
  # @revealui/ai
2
10
 
3
11
  > **Commercial package** - requires a [RevealUI Pro license](https://revealui.com/pro). Free to install and evaluate; a license key is required for production use.
@@ -11,6 +11,13 @@
11
11
  * - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
12
12
  * - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
13
13
  * - abort(): cancels the stream
14
+ *
15
+ * CSRF: both POSTs echo the JS-readable `revealui-csrf` cookie (the signed
16
+ * double-submit token the RevealUI admin proxy issues) as an `X-CSRF-Token`
17
+ * header when the cookie is present — the api's csrfMiddleware requires it
18
+ * on session-cookie-authenticated unsafe requests. Sessionless callers
19
+ * (API-key / server-to-server) carry no session cookie and are exempt from
20
+ * the check, so omitting the header when the cookie is absent is correct.
14
21
  */
15
22
  /**
16
23
  * Mirror of `AgentStreamChunk` in
@@ -1 +1 @@
1
- {"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,13 @@
11
11
  * - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
12
12
  * - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
13
13
  * - abort(): cancels the stream
14
+ *
15
+ * CSRF: both POSTs echo the JS-readable `revealui-csrf` cookie (the signed
16
+ * double-submit token the RevealUI admin proxy issues) as an `X-CSRF-Token`
17
+ * header when the cookie is present — the api's csrfMiddleware requires it
18
+ * on session-cookie-authenticated unsafe requests. Sessionless callers
19
+ * (API-key / server-to-server) carry no session cookie and are exempt from
20
+ * the check, so omitting the header when the cookie is absent is correct.
14
21
  */
15
22
  'use client';
16
23
  import { useCallback, useRef, useState } from 'react';
@@ -22,6 +29,35 @@ const INITIAL_STATE = {
22
29
  sessionId: null,
23
30
  pendingElicitations: [],
24
31
  };
32
+ /**
33
+ * Read the JS-readable `revealui-csrf` cookie. The api's csrfMiddleware
34
+ * (`apps/server/src/middleware/csrf.ts`) requires it as an `X-CSRF-Token`
35
+ * header on any session-cookie-bearing unsafe request; the admin proxy
36
+ * issues it on page load. Returns undefined outside the browser or when
37
+ * the cookie is absent/empty. Mirrors the cookie-read in `@revealui/core`'s
38
+ * admin APIClient (`request()`).
39
+ */
40
+ function readCsrfToken() {
41
+ if (typeof document === 'undefined')
42
+ return undefined;
43
+ for (const part of document.cookie.split(';')) {
44
+ const eqIdx = part.indexOf('=');
45
+ if (eqIdx === -1)
46
+ continue;
47
+ if (part.slice(0, eqIdx).trim() === 'revealui-csrf') {
48
+ const raw = part.slice(eqIdx + 1).trim();
49
+ if (!raw)
50
+ return undefined;
51
+ try {
52
+ return decodeURIComponent(raw);
53
+ }
54
+ catch {
55
+ return raw;
56
+ }
57
+ }
58
+ }
59
+ return undefined;
60
+ }
25
61
  export function useAgentStream() {
26
62
  const [state, setState] = useState(INITIAL_STATE);
27
63
  const controllerRef = useRef(null);
@@ -39,9 +75,13 @@ export function useAgentStream() {
39
75
  controllerRef.current = controller;
40
76
  setState({ ...INITIAL_STATE, isStreaming: true });
41
77
  try {
78
+ const csrfToken = readCsrfToken();
42
79
  const response = await fetch(apiBase, {
43
80
  method: 'POST',
44
- headers: { 'Content-Type': 'application/json' },
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ ...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
84
+ },
45
85
  body: JSON.stringify(request),
46
86
  credentials: 'include',
47
87
  signal: controller.signal,
@@ -107,9 +147,13 @@ export function useAgentStream() {
107
147
  const body = { sessionId, elicitationId, action };
108
148
  if (action === 'accept' && content)
109
149
  body.content = content;
150
+ const csrfToken = readCsrfToken();
110
151
  const response = await fetch('/api/agent-stream/elicit', {
111
152
  method: 'POST',
112
- headers: { 'Content-Type': 'application/json' },
153
+ headers: {
154
+ 'Content-Type': 'application/json',
155
+ ...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
156
+ },
113
157
  body: JSON.stringify(body),
114
158
  credentials: 'include',
115
159
  });
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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"}
@@ -250,20 +250,36 @@ export class LLMClient {
250
250
  }
251
251
  // Use dedicated embed provider if one was configured
252
252
  const embedProvider = this.embedProviderOverride ?? this.provider;
253
+ // Only the primary-provider path maps to a known LLMProviderType the health
254
+ // monitor can key on. A dedicated embed override is an opaque provider, so we
255
+ // skip recording rather than attribute its latency to the primary provider.
256
+ const trackPrimary = !this.embedProviderOverride;
257
+ const callStart = Date.now();
253
258
  try {
254
259
  this.recordRequest();
255
- return await this.circuitBreaker.execute(() => embedProvider.embed(text, options));
260
+ const result = await this.circuitBreaker.execute(() => embedProvider.embed(text, options));
261
+ if (trackPrimary) {
262
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
263
+ }
264
+ return result;
256
265
  }
257
266
  catch (error) {
267
+ if (trackPrimary) {
268
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart, error instanceof Error ? error : new Error(String(error)));
269
+ }
258
270
  // Try fallback if available (only when using the primary provider path)
259
- if (!this.embedProviderOverride && this.fallbackProvider) {
271
+ if (!this.embedProviderOverride && this.fallbackProvider && this.config.fallbackProvider) {
260
272
  const fp = this.fallbackProvider;
273
+ const fallbackStart = Date.now();
261
274
  try {
262
- return this.fallbackCircuitBreaker
275
+ const fb = this.fallbackCircuitBreaker
263
276
  ? await this.fallbackCircuitBreaker.execute(() => fp.embed(text, options))
264
277
  : await fp.embed(text, options);
278
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
279
+ return fb;
265
280
  }
266
- catch {
281
+ catch (fallbackError) {
282
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart, fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
267
283
  throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
268
284
  }
269
285
  }
@@ -276,6 +292,7 @@ export class LLMClient {
276
292
  if (!this.checkRateLimit()) {
277
293
  throw new Error('Rate limit exceeded');
278
294
  }
295
+ const callStart = Date.now();
279
296
  try {
280
297
  // Circuit breaker check — streaming can't use execute() wrapper
281
298
  // so we check state and record outcomes manually
@@ -283,18 +300,45 @@ export class LLMClient {
283
300
  throw new CircuitBreakerOpenError(`llm-${this.config.provider}`);
284
301
  }
285
302
  this.recordRequest();
286
- yield* this.provider.stream(messages, options);
303
+ // Sample health latency at time-to-first-chunk, not full stream duration:
304
+ // a pull-based generator's total time includes downstream consumer speed,
305
+ // which would falsely mark a healthy provider degraded.
306
+ let sampled = false;
307
+ for await (const chunk of this.provider.stream(messages, options)) {
308
+ if (!sampled) {
309
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
310
+ sampled = true;
311
+ }
312
+ yield chunk;
313
+ }
314
+ if (!sampled) {
315
+ // Stream produced zero chunks but the call still succeeded.
316
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
317
+ }
287
318
  }
288
319
  catch (error) {
320
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart, error instanceof Error ? error : new Error(String(error)));
289
321
  // Try fallback if available
290
- if (this.fallbackProvider) {
322
+ if (this.fallbackProvider && this.config.fallbackProvider) {
323
+ const fallbackStart = Date.now();
291
324
  try {
292
325
  if (this.fallbackCircuitBreaker?.isOpen()) {
293
326
  throw new CircuitBreakerOpenError(`llm-${this.config.fallbackProvider}`);
294
327
  }
295
- yield* this.fallbackProvider.stream(messages, options);
328
+ let fallbackSampled = false;
329
+ for await (const chunk of this.fallbackProvider.stream(messages, options)) {
330
+ if (!fallbackSampled) {
331
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
332
+ fallbackSampled = true;
333
+ }
334
+ yield chunk;
335
+ }
336
+ if (!fallbackSampled) {
337
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
338
+ }
296
339
  }
297
- catch {
340
+ catch (fallbackError) {
341
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart, fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
298
342
  throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
299
343
  }
300
344
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/ai",
3
- "version": "0.5.2",
3
+ "version": "0.6.2",
4
4
  "description": "AI runtime for agent-driven products — agents, memory, LLM providers (Inference Snaps, Ollama, OpenAI-compatible), tools, and orchestration. Anthropic-SDK-free.",
5
5
  "keywords": [
6
6
  "agent",
@@ -23,22 +23,22 @@
23
23
  },
24
24
  "license": "FSL-1.1-MIT",
25
25
  "dependencies": {
26
- "lru-cache": "^11.5.0",
26
+ "lru-cache": "^11.5.1",
27
27
  "zod": "^4.4.3",
28
- "@revealui/contracts": "0.6.0",
29
- "@revealui/core": "0.9.0",
30
- "@revealui/db": "0.7.0",
28
+ "@revealui/contracts": "0.6.1",
29
+ "@revealui/core": "0.10.1",
30
+ "@revealui/db": "0.7.1",
31
31
  "@revealui/resilience": "0.2.4"
32
32
  },
33
33
  "devDependencies": {
34
- "@electric-sql/pglite": "^0.4.5",
34
+ "@electric-sql/pglite": "^0.5.1",
35
35
  "@testing-library/react": "^16.3.2",
36
- "@vitest/coverage-v8": "^4.1.5",
36
+ "@vitest/coverage-v8": "^4.1.8",
37
37
  "jsdom": "29.1.1",
38
- "react": "^19.2.6",
39
- "react-dom": "^19.2.6",
38
+ "react": "^19.2.7",
39
+ "react-dom": "^19.2.7",
40
40
  "typescript": "^6.0.3",
41
- "vitest": "^4.1.5",
41
+ "vitest": "^4.1.8",
42
42
  "@revealui/dev": "0.1.0"
43
43
  },
44
44
  "engines": {