@revealui/ai 0.8.0 → 0.8.1

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.
Files changed (77) hide show
  1. package/README.md +25 -12
  2. package/dist/client/hooks/useAgentStream.d.ts.map +1 -1
  3. package/dist/client/hooks/useAgentStream.js +1 -29
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/inference/context-assembly.d.ts.map +1 -1
  8. package/dist/inference/context-assembly.js +1 -3
  9. package/dist/inference/overflow-compressor.d.ts.map +1 -1
  10. package/dist/inference/overflow-compressor.js +1 -3
  11. package/dist/ingestion/pipeline.d.ts.map +1 -1
  12. package/dist/ingestion/pipeline.js +1 -3
  13. package/dist/ingestion/text-splitter.d.ts.map +1 -1
  14. package/dist/ingestion/text-splitter.js +1 -3
  15. package/dist/internal/token-estimate.d.ts +6 -0
  16. package/dist/internal/token-estimate.d.ts.map +1 -0
  17. package/dist/internal/token-estimate.js +7 -0
  18. package/dist/llm/client.d.ts +4 -3
  19. package/dist/llm/client.d.ts.map +1 -1
  20. package/dist/llm/client.js +11 -4
  21. package/dist/llm/local-ai-profile.d.ts +39 -0
  22. package/dist/llm/local-ai-profile.d.ts.map +1 -0
  23. package/dist/llm/local-ai-profile.js +165 -0
  24. package/dist/llm/providers/inference-snaps.d.ts +24 -13
  25. package/dist/llm/providers/inference-snaps.d.ts.map +1 -1
  26. package/dist/llm/providers/inference-snaps.js +21 -17
  27. package/dist/llm/providers/us-origin-snaps.d.ts +79 -0
  28. package/dist/llm/providers/us-origin-snaps.d.ts.map +1 -0
  29. package/dist/llm/providers/us-origin-snaps.js +108 -0
  30. package/dist/observability/export.d.ts +78 -0
  31. package/dist/observability/export.d.ts.map +1 -0
  32. package/dist/observability/export.js +229 -0
  33. package/dist/observability/index.d.ts +19 -0
  34. package/dist/observability/index.d.ts.map +1 -0
  35. package/dist/observability/index.js +16 -0
  36. package/dist/observability/instrumentation.d.ts +68 -0
  37. package/dist/observability/instrumentation.d.ts.map +1 -0
  38. package/dist/observability/instrumentation.js +219 -0
  39. package/dist/observability/storage.d.ts +39 -0
  40. package/dist/observability/storage.d.ts.map +1 -0
  41. package/dist/observability/storage.js +184 -0
  42. package/dist/orchestration/runtime.d.ts +7 -0
  43. package/dist/orchestration/runtime.d.ts.map +1 -1
  44. package/dist/orchestration/runtime.js +9 -2
  45. package/dist/orchestration/ticket-agent.d.ts +10 -0
  46. package/dist/orchestration/ticket-agent.d.ts.map +1 -1
  47. package/dist/orchestration/ticket-agent.js +7 -1
  48. package/dist/skills/index.d.ts +4 -0
  49. package/dist/skills/index.d.ts.map +1 -1
  50. package/dist/skills/index.js +4 -0
  51. package/dist/skills/integration/agent-skill-provider.d.ts +0 -5
  52. package/dist/skills/integration/agent-skill-provider.d.ts.map +1 -1
  53. package/dist/skills/integration/agent-skill-provider.js +3 -9
  54. package/dist/tools/admin/factory.d.ts +6 -0
  55. package/dist/tools/admin/factory.d.ts.map +1 -1
  56. package/dist/tools/admin/factory.js +3 -2
  57. package/dist/tools/coding/index.d.ts +6 -0
  58. package/dist/tools/coding/index.d.ts.map +1 -1
  59. package/dist/tools/coding/index.js +7 -2
  60. package/dist/tools/governance.d.ts +37 -0
  61. package/dist/tools/governance.d.ts.map +1 -0
  62. package/dist/tools/governance.js +42 -0
  63. package/dist/tools/integrity-audit.d.ts +27 -0
  64. package/dist/tools/integrity-audit.d.ts.map +1 -0
  65. package/dist/tools/integrity-audit.js +64 -0
  66. package/dist/tools/mcp-adapter.d.ts +23 -3
  67. package/dist/tools/mcp-adapter.d.ts.map +1 -1
  68. package/dist/tools/mcp-adapter.js +189 -132
  69. package/dist/tools/mcp-sampling.d.ts +3 -3
  70. package/dist/tools/mcp-sampling.js +3 -3
  71. package/dist/tools/web/duck-duck-go.d.ts.map +1 -1
  72. package/dist/tools/web/duck-duck-go.js +1 -4
  73. package/dist/tools/web/exa.d.ts.map +1 -1
  74. package/dist/tools/web/exa.js +1 -4
  75. package/dist/tools/web/tavily.d.ts.map +1 -1
  76. package/dist/tools/web/tavily.js +1 -4
  77. package/package.json +68 -33
@@ -4,35 +4,45 @@
4
4
  * Local inference via Canonical's inference-snaps OpenAI-compatible API.
5
5
  * No API key required. Zero cost, fully offline, hardware-optimized.
6
6
  *
7
- * Supported models (snaps), per Canonical's official catalog at
8
- * https://documentation.ubuntu.com/inference-snaps/reference/snaps/ :
9
- * gemma3 - general LLM + vision (text/image in, text out)
10
- * deepseek-r1 - reasoning LLM (chat completions)
11
- * qwen-vl - vision-language model (image + text)
12
- * nemotron-3-nano - general LLM (reasoning + non-reasoning, text only)
13
- * nemotron-3-nano-omni - multimodal (text/image/video/audio in, text out)
7
+ * Product hardline: US-origin snap models only (see us-origin-snaps.ts).
8
+ * Allowlisted snaps (install these):
9
+ * nemotron-3-nano - NVIDIA general + tools (default)
10
+ * nemotron-3-nano-omni - NVIDIA multimodal
11
+ * gemma3 / gemma4 - Google general + vision + tools
12
+ *
13
+ * Canonical also publishes non-US snaps (deepseek-r1, qwen-*, glm-*). Those
14
+ * are rejected at construction unless REVEALUI_ALLOW_NON_US_MODELS=1.
14
15
  *
15
16
  * Install a model:
16
- * sudo snap install gemma3
17
- * gemma3 set http.port=9090 # optional: change port (default varies)
18
- * gemma3 status # shows base URL and available models
17
+ * sudo snap install nemotron-3-nano
18
+ * nemotron-3-nano set http.port=9090 # optional: change port
19
+ * nemotron-3-nano status # shows base URL and available models
19
20
  *
20
21
  * Set env vars:
21
22
  * INFERENCE_SNAPS_BASE_URL=http://localhost:9090/v1
22
- * LLM_MODEL=gemma3 # must match the snap name / model ID
23
- * LLM_EMBED_MODEL=gemma3 # optional: model for embeddings
23
+ * LLM_MODEL=nemotron-3-nano # must match an allowlisted snap model ID
24
+ * LLM_EMBED_MODEL=nemotron-3-nano
24
25
  *
25
26
  * Docs: https://documentation.ubuntu.com/inference-snaps
26
27
  */
27
28
  import type { Embedding, LLMChatOptions, LLMChunk, LLMEmbedOptions, LLMProvider, LLMProviderConfig, LLMResponse, LLMStreamOptions, Message, ReasonerCapabilities } from './base.js';
29
+ import { DEFAULT_US_ORIGIN_INFERENCE_SNAP } from './us-origin-snaps.js';
28
30
  export interface InferenceSnapsProviderConfig extends Omit<LLMProviderConfig, 'apiKey'> {
29
31
  apiKey?: string;
30
32
  /** Base URL of the inference-snaps service, e.g. http://localhost:9090/v1 */
31
33
  baseURL: string;
32
- /** Chat/vision model name - must match the snap's model ID (e.g. 'gemma3', 'deepseek-r1') */
34
+ /**
35
+ * Chat/vision model name — must match an allowlisted snap model ID
36
+ * (e.g. 'nemotron-3-nano', 'gemma4'). See US_ORIGIN_INFERENCE_SNAP_IDS.
37
+ */
33
38
  model?: string;
34
39
  /** Embedding model name. Defaults to the chat model when omitted. */
35
40
  embedModel?: string;
41
+ /**
42
+ * Bypass US-origin allowlist for this instance (operator/tests only).
43
+ * Prefer the process env REVEALUI_ALLOW_NON_US_MODELS=1 when needed.
44
+ */
45
+ allowNonUsModels?: boolean;
36
46
  }
37
47
  export declare class InferenceSnapsProvider implements LLMProvider {
38
48
  private inner;
@@ -44,4 +54,5 @@ export declare class InferenceSnapsProvider implements LLMProvider {
44
54
  stream(messages: Message[], options?: LLMStreamOptions): AsyncIterable<LLMChunk>;
45
55
  embed(text: string | string[], _options?: LLMEmbedOptions): Promise<Embedding | Embedding[]>;
46
56
  }
57
+ export { DEFAULT_US_ORIGIN_INFERENCE_SNAP };
47
58
  //# sourceMappingURL=inference-snaps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inference-snaps.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/inference-snaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,oBAAoB,EACrB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,sBAAuB,YAAW,WAAW;IACxD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,4BAA4B;IAahD,YAAY,IAAI,oBAAoB;IAgBpC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAIzE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC;IAI1E,KAAK,CACT,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EACvB,QAAQ,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;CAsBpC"}
1
+ {"version":3,"file":"inference-snaps.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/inference-snaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,oBAAoB,EACrB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAEL,gCAAgC,EACjC,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,qBAAa,sBAAuB,YAAW,WAAW;IACxD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,4BAA4B;IAchD,YAAY,IAAI,oBAAoB;IAgBpC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAIzE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC;IAI1E,KAAK,CACT,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EACvB,QAAQ,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;CAsBpC;AAED,OAAO,EAAE,gCAAgC,EAAE,CAAC"}
@@ -4,46 +4,49 @@
4
4
  * Local inference via Canonical's inference-snaps OpenAI-compatible API.
5
5
  * No API key required. Zero cost, fully offline, hardware-optimized.
6
6
  *
7
- * Supported models (snaps), per Canonical's official catalog at
8
- * https://documentation.ubuntu.com/inference-snaps/reference/snaps/ :
9
- * gemma3 - general LLM + vision (text/image in, text out)
10
- * deepseek-r1 - reasoning LLM (chat completions)
11
- * qwen-vl - vision-language model (image + text)
12
- * nemotron-3-nano - general LLM (reasoning + non-reasoning, text only)
13
- * nemotron-3-nano-omni - multimodal (text/image/video/audio in, text out)
7
+ * Product hardline: US-origin snap models only (see us-origin-snaps.ts).
8
+ * Allowlisted snaps (install these):
9
+ * nemotron-3-nano - NVIDIA general + tools (default)
10
+ * nemotron-3-nano-omni - NVIDIA multimodal
11
+ * gemma3 / gemma4 - Google general + vision + tools
12
+ *
13
+ * Canonical also publishes non-US snaps (deepseek-r1, qwen-*, glm-*). Those
14
+ * are rejected at construction unless REVEALUI_ALLOW_NON_US_MODELS=1.
14
15
  *
15
16
  * Install a model:
16
- * sudo snap install gemma3
17
- * gemma3 set http.port=9090 # optional: change port (default varies)
18
- * gemma3 status # shows base URL and available models
17
+ * sudo snap install nemotron-3-nano
18
+ * nemotron-3-nano set http.port=9090 # optional: change port
19
+ * nemotron-3-nano status # shows base URL and available models
19
20
  *
20
21
  * Set env vars:
21
22
  * INFERENCE_SNAPS_BASE_URL=http://localhost:9090/v1
22
- * LLM_MODEL=gemma3 # must match the snap name / model ID
23
- * LLM_EMBED_MODEL=gemma3 # optional: model for embeddings
23
+ * LLM_MODEL=nemotron-3-nano # must match an allowlisted snap model ID
24
+ * LLM_EMBED_MODEL=nemotron-3-nano
24
25
  *
25
26
  * Docs: https://documentation.ubuntu.com/inference-snaps
26
27
  */
27
28
  import { OpenAICompatProvider } from './openai-compat.js';
29
+ import { assertUsOriginInferenceSnap, DEFAULT_US_ORIGIN_INFERENCE_SNAP, } from './us-origin-snaps.js';
28
30
  export class InferenceSnapsProvider {
29
31
  inner;
30
32
  embedModel;
31
33
  baseURL;
32
34
  constructor(config) {
33
35
  this.baseURL = config.baseURL;
34
- // Use the same model for embeddings unless explicitly overridden
35
- this.embedModel = config.embedModel ?? config.model ?? 'gemma3';
36
+ const assertOpts = { allowNonUs: config.allowNonUsModels === true };
37
+ const model = assertUsOriginInferenceSnap(config.model, assertOpts);
38
+ this.embedModel = assertUsOriginInferenceSnap(config.embedModel ?? model, assertOpts);
36
39
  this.inner = new OpenAICompatProvider({
37
40
  ...config,
38
41
  // inference-snaps ignores the API key; OpenAI client requires a non-empty value
39
42
  apiKey: config.apiKey ?? 'inference-snaps',
40
43
  baseURL: config.baseURL,
41
- model: config.model ?? 'gemma3',
44
+ model,
42
45
  });
43
46
  }
44
47
  capabilities() {
45
- // Profile for the default model (gemma3, text-only). Per-model capability
46
- // negotiation (e.g. qwen-vl vision, deepseek-r1 reasoningEffort) is a follow-up.
48
+ // Profile for the default US-origin model (text + tools). Per-model capability
49
+ // negotiation (e.g. nemotron-3-nano-omni vision) is a follow-up.
47
50
  return {
48
51
  providerTag: 'inference-snaps',
49
52
  tools: true,
@@ -80,3 +83,4 @@ export class InferenceSnapsProvider {
80
83
  return Array.isArray(text) ? embeddings : embeddings[0];
81
84
  }
82
85
  }
86
+ export { DEFAULT_US_ORIGIN_INFERENCE_SNAP };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * US-origin Inference Snap allowlist (product hardline).
3
+ *
4
+ * Canonical's snap catalog includes PRC-origin models (DeepSeek, Qwen, GLM).
5
+ * RevealUI product local inference is **US-origin weights only**, fail-closed.
6
+ *
7
+ * Definition: open weights published by an organization whose ultimate parent
8
+ * is incorporated in the United States. Packaging (Canonical snaps) is not origin.
9
+ *
10
+ * Allowlist today (Canonical catalog, 2026-07):
11
+ * - nemotron-3-nano / nemotron-3-nano-omni — NVIDIA
12
+ * - gemma3 / gemma4 — Google (Alphabet)
13
+ *
14
+ * Escape (operator-only, never seed, always loud in the error path):
15
+ * REVEALUI_ALLOW_NON_US_MODELS=1
16
+ *
17
+ * Companion ADR: .jv docs/decisions/2026-07-24-us-origin-inference-snaps.md
18
+ */
19
+ /**
20
+ * Product install/list catalog (snap name + operator-facing description).
21
+ * SSOT for harnesses InferenceService + Studio install UIs.
22
+ * Order: product default first (nemotron for capable hosts; gemma preferred on low-RAM via profile daily).
23
+ */
24
+ export declare const PRODUCT_INFERENCE_SNAP_CATALOG: readonly [{
25
+ readonly id: "nemotron-3-nano";
26
+ readonly description: "NVIDIA (US) — general + tools; product default on capable hosts";
27
+ }, {
28
+ readonly id: "nemotron-3-nano-omni";
29
+ readonly description: "NVIDIA (US) — multimodal (text/image/video/audio in)";
30
+ }, {
31
+ readonly id: "gemma4";
32
+ readonly description: "Google (US) — general + vision + tools";
33
+ }, {
34
+ readonly id: "gemma3";
35
+ readonly description: "Google (US) — general + vision; prefer 270m on low-RAM hosts";
36
+ }];
37
+ export type UsOriginInferenceSnapId = (typeof PRODUCT_INFERENCE_SNAP_CATALOG)[number]['id'];
38
+ /** Snap model IDs permitted for product Inference Snaps usage. */
39
+ export declare const US_ORIGIN_INFERENCE_SNAP_IDS: readonly UsOriginInferenceSnapId[];
40
+ /** Default chat/embed model when none is specified (env factory / provider). */
41
+ export declare const DEFAULT_US_ORIGIN_INFERENCE_SNAP: UsOriginInferenceSnapId;
42
+ /** Preferred snap on constrained hosts (profile `snaps` tier). */
43
+ export declare const DEFAULT_LOW_RAM_INFERENCE_SNAP: UsOriginInferenceSnapId;
44
+ /** Preferred Ollama model for profile `daily` tier (fits ~4GB WSL). */
45
+ export declare const DEFAULT_DAILY_OLLAMA_MODEL = "gemma3:1b";
46
+ /** Operator escape env var. Never set in customer seed or CI green paths. */
47
+ export declare const NON_US_MODELS_ESCAPE_ENV = "REVEALUI_ALLOW_NON_US_MODELS";
48
+ export declare class NonUsOriginInferenceSnapError extends Error {
49
+ readonly code: "NON_US_ORIGIN_INFERENCE_SNAP";
50
+ readonly modelId: string;
51
+ constructor(modelId: string);
52
+ }
53
+ /** Normalize a snap model id for allowlist comparison. */
54
+ export declare function normalizeInferenceSnapModelId(modelId: string): string;
55
+ /**
56
+ * True when the model id is on the US-origin product allowlist.
57
+ *
58
+ * Accepts exact snap ids (`nemotron-3-nano`) and engine-served variants that
59
+ * Canonical publishes under the same snap (e.g.
60
+ * `nemotron-3-nano-30b-a3b-q4-k-m`, `gemma4:e2b` style suffixes with `-` or `:`).
61
+ * Match longest snap id first so `nemotron-3-nano-omni` wins over `nemotron-3-nano`.
62
+ */
63
+ export declare function isUsOriginInferenceSnap(modelId: string): boolean;
64
+ /** True when the operator escape hatch is enabled. */
65
+ export declare function isNonUsModelsEscapeEnabled(env?: NodeJS.ProcessEnv): boolean;
66
+ export interface AssertUsOriginInferenceSnapOptions {
67
+ /** Force-allow non-US models (tests / explicit callers). Overrides env when true. */
68
+ allowNonUs?: boolean;
69
+ /** Env source for the escape hatch (defaults to process.env). */
70
+ env?: NodeJS.ProcessEnv;
71
+ }
72
+ /**
73
+ * Resolve and enforce the US-origin allowlist.
74
+ *
75
+ * @returns the model id to use (default when empty/undefined)
76
+ * @throws {NonUsOriginInferenceSnapError} when model is off-allowlist and escape is off
77
+ */
78
+ export declare function assertUsOriginInferenceSnap(modelId: string | undefined, options?: AssertUsOriginInferenceSnapOptions): string;
79
+ //# sourceMappingURL=us-origin-snaps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"us-origin-snaps.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/us-origin-snaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;EAiBjC,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAE5F,kEAAkE;AAClE,eAAO,MAAM,4BAA4B,EAAE,SAAS,uBAAuB,EAClB,CAAC;AAE1D,gFAAgF;AAChF,eAAO,MAAM,gCAAgC,EAAE,uBAA2C,CAAC;AAE3F,kEAAkE;AAClE,eAAO,MAAM,8BAA8B,EAAE,uBAAkC,CAAC;AAEhF,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,cAAc,CAAC;AAEtD,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB,iCAAiC,CAAC;AAEvE,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,EAAG,8BAA8B,CAAU;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAW5B;AAED,0DAA0D;AAC1D,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQhE;AAED,sDAAsD;AACtD,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAGxF;AAED,MAAM,WAAW,kCAAkC;IACjD,qFAAqF;IACrF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,CAAC,EAAE,kCAAkC,GAC3C,MAAM,CAcR"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * US-origin Inference Snap allowlist (product hardline).
3
+ *
4
+ * Canonical's snap catalog includes PRC-origin models (DeepSeek, Qwen, GLM).
5
+ * RevealUI product local inference is **US-origin weights only**, fail-closed.
6
+ *
7
+ * Definition: open weights published by an organization whose ultimate parent
8
+ * is incorporated in the United States. Packaging (Canonical snaps) is not origin.
9
+ *
10
+ * Allowlist today (Canonical catalog, 2026-07):
11
+ * - nemotron-3-nano / nemotron-3-nano-omni — NVIDIA
12
+ * - gemma3 / gemma4 — Google (Alphabet)
13
+ *
14
+ * Escape (operator-only, never seed, always loud in the error path):
15
+ * REVEALUI_ALLOW_NON_US_MODELS=1
16
+ *
17
+ * Companion ADR: .jv docs/decisions/2026-07-24-us-origin-inference-snaps.md
18
+ */
19
+ /**
20
+ * Product install/list catalog (snap name + operator-facing description).
21
+ * SSOT for harnesses InferenceService + Studio install UIs.
22
+ * Order: product default first (nemotron for capable hosts; gemma preferred on low-RAM via profile daily).
23
+ */
24
+ export const PRODUCT_INFERENCE_SNAP_CATALOG = [
25
+ {
26
+ id: 'nemotron-3-nano',
27
+ description: 'NVIDIA (US) — general + tools; product default on capable hosts',
28
+ },
29
+ {
30
+ id: 'nemotron-3-nano-omni',
31
+ description: 'NVIDIA (US) — multimodal (text/image/video/audio in)',
32
+ },
33
+ {
34
+ id: 'gemma4',
35
+ description: 'Google (US) — general + vision + tools',
36
+ },
37
+ {
38
+ id: 'gemma3',
39
+ description: 'Google (US) — general + vision; prefer 270m on low-RAM hosts',
40
+ },
41
+ ];
42
+ /** Snap model IDs permitted for product Inference Snaps usage. */
43
+ export const US_ORIGIN_INFERENCE_SNAP_IDS = PRODUCT_INFERENCE_SNAP_CATALOG.map((entry) => entry.id);
44
+ /** Default chat/embed model when none is specified (env factory / provider). */
45
+ export const DEFAULT_US_ORIGIN_INFERENCE_SNAP = 'nemotron-3-nano';
46
+ /** Preferred snap on constrained hosts (profile `snaps` tier). */
47
+ export const DEFAULT_LOW_RAM_INFERENCE_SNAP = 'gemma3';
48
+ /** Preferred Ollama model for profile `daily` tier (fits ~4GB WSL). */
49
+ export const DEFAULT_DAILY_OLLAMA_MODEL = 'gemma3:1b';
50
+ /** Operator escape env var. Never set in customer seed or CI green paths. */
51
+ export const NON_US_MODELS_ESCAPE_ENV = 'REVEALUI_ALLOW_NON_US_MODELS';
52
+ export class NonUsOriginInferenceSnapError extends Error {
53
+ code = 'NON_US_ORIGIN_INFERENCE_SNAP';
54
+ modelId;
55
+ constructor(modelId) {
56
+ super(`Inference Snap model "${modelId}" is not on the US-origin allowlist ` +
57
+ `(${US_ORIGIN_INFERENCE_SNAP_IDS.join(', ')}). ` +
58
+ 'Product local inference is US-origin snaps only. ' +
59
+ `Install: sudo snap install ${DEFAULT_US_ORIGIN_INFERENCE_SNAP}. ` +
60
+ `Operator escape (audited, never seed): ${NON_US_MODELS_ESCAPE_ENV}=1`);
61
+ this.name = 'NonUsOriginInferenceSnapError';
62
+ this.modelId = modelId;
63
+ }
64
+ }
65
+ /** Normalize a snap model id for allowlist comparison. */
66
+ export function normalizeInferenceSnapModelId(modelId) {
67
+ return modelId.trim().toLowerCase();
68
+ }
69
+ /**
70
+ * True when the model id is on the US-origin product allowlist.
71
+ *
72
+ * Accepts exact snap ids (`nemotron-3-nano`) and engine-served variants that
73
+ * Canonical publishes under the same snap (e.g.
74
+ * `nemotron-3-nano-30b-a3b-q4-k-m`, `gemma4:e2b` style suffixes with `-` or `:`).
75
+ * Match longest snap id first so `nemotron-3-nano-omni` wins over `nemotron-3-nano`.
76
+ */
77
+ export function isUsOriginInferenceSnap(modelId) {
78
+ const id = normalizeInferenceSnapModelId(modelId);
79
+ const snaps = [...US_ORIGIN_INFERENCE_SNAP_IDS].sort((a, b) => b.length - a.length);
80
+ for (const snap of snaps) {
81
+ if (id === snap)
82
+ return true;
83
+ if (id.startsWith(`${snap}-`) || id.startsWith(`${snap}:`))
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+ /** True when the operator escape hatch is enabled. */
89
+ export function isNonUsModelsEscapeEnabled(env = process.env) {
90
+ const value = env[NON_US_MODELS_ESCAPE_ENV];
91
+ return value === '1' || value === 'true';
92
+ }
93
+ /**
94
+ * Resolve and enforce the US-origin allowlist.
95
+ *
96
+ * @returns the model id to use (default when empty/undefined)
97
+ * @throws {NonUsOriginInferenceSnapError} when model is off-allowlist and escape is off
98
+ */
99
+ export function assertUsOriginInferenceSnap(modelId, options) {
100
+ const resolved = modelId !== undefined && modelId.trim() !== ''
101
+ ? modelId.trim()
102
+ : DEFAULT_US_ORIGIN_INFERENCE_SNAP;
103
+ const allowNonUs = options?.allowNonUs === true || isNonUsModelsEscapeEnabled(options?.env ?? process.env);
104
+ if (!(allowNonUs || isUsOriginInferenceSnap(resolved))) {
105
+ throw new NonUsOriginInferenceSnapError(resolved);
106
+ }
107
+ return resolved;
108
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @revealui/ai - Export Utilities
3
+ *
4
+ * Export agent events to various formats (JSON, CSV, NDJSON).
5
+ */
6
+ import type { AgentEventLogger } from './logger.js';
7
+ import type { AnyAgentEvent, EventFilter } from './types.js';
8
+ /**
9
+ * Export events to JSON format
10
+ */
11
+ export declare function exportToJSON(events: AnyAgentEvent[], options?: {
12
+ pretty?: boolean;
13
+ metadata?: Record<string, unknown>;
14
+ }): string;
15
+ /**
16
+ * Export events to CSV format
17
+ */
18
+ export declare function exportToCSV(events: AnyAgentEvent[]): string;
19
+ /**
20
+ * Export events to NDJSON format (newline-delimited JSON)
21
+ */
22
+ export declare function exportToNDJSON(events: AnyAgentEvent[]): string;
23
+ /**
24
+ * Export events to a file (Node.js)
25
+ */
26
+ export declare function exportToFile(events: AnyAgentEvent[], filePath: string, format?: 'json' | 'csv' | 'ndjson'): Promise<void>;
27
+ /**
28
+ * Create a downloadable file blob (Browser)
29
+ */
30
+ export declare function createDownloadableBlob(events: AnyAgentEvent[], format?: 'json' | 'csv' | 'ndjson'): Blob;
31
+ /**
32
+ * Trigger download in browser
33
+ */
34
+ export declare function downloadEvents(events: AnyAgentEvent[], filename: string, format?: 'json' | 'csv' | 'ndjson'): void;
35
+ /**
36
+ * Export helper class with logger integration
37
+ */
38
+ export declare class EventExporter {
39
+ private logger;
40
+ constructor(logger: AgentEventLogger);
41
+ /**
42
+ * Export all events
43
+ */
44
+ exportAll(format?: 'json' | 'csv' | 'ndjson'): string;
45
+ /**
46
+ * Export filtered events
47
+ */
48
+ exportFiltered(filter: EventFilter, format?: 'json' | 'csv' | 'ndjson'): string;
49
+ /**
50
+ * Export to file (Node.js)
51
+ */
52
+ exportToFile(filePath: string, format?: 'json' | 'csv' | 'ndjson', filter?: EventFilter): Promise<void>;
53
+ /**
54
+ * Download in browser
55
+ */
56
+ download(filename: string, format?: 'json' | 'csv' | 'ndjson', filter?: EventFilter): void;
57
+ /**
58
+ * Export by time range
59
+ */
60
+ exportTimeRange(start: Date, end: Date, format?: 'json' | 'csv' | 'ndjson'): string;
61
+ /**
62
+ * Export by agent
63
+ */
64
+ exportByAgent(agentId: string, format?: 'json' | 'csv' | 'ndjson'): string;
65
+ /**
66
+ * Export by session
67
+ */
68
+ exportBySession(sessionId: string, format?: 'json' | 'csv' | 'ndjson'): string;
69
+ /**
70
+ * Export errors only
71
+ */
72
+ exportErrors(format?: 'json' | 'csv' | 'ndjson'): string;
73
+ /**
74
+ * Export LLM calls only
75
+ */
76
+ exportLLMCalls(format?: 'json' | 'csv' | 'ndjson'): string;
77
+ }
78
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/observability/export.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7D;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACA,MAAM,CAaR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAyD3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GACzC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EAAE,EACvB,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GACzC,IAAI,CAoBN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GACzC,IAAI,CAgBN;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,EAAE,gBAAgB;IAIpC;;OAEG;IACH,SAAS,CAAC,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAa7D;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAgBvF;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,EAC1C,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC;IAKhB;;OAEG;IACH,QAAQ,CACN,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,EAC1C,MAAM,CAAC,EAAE,WAAW,GACnB,IAAI;IAKP;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAU3F;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAIlF;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAItF;;OAEG;IACH,YAAY,CAAC,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;IAIhE;;OAEG;IACH,cAAc,CAAC,MAAM,GAAE,MAAM,GAAG,KAAK,GAAG,QAAiB,GAAG,MAAM;CAGnE"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @revealui/ai - Export Utilities
3
+ *
4
+ * Export agent events to various formats (JSON, CSV, NDJSON).
5
+ */
6
+ /**
7
+ * Export events to JSON format
8
+ */
9
+ export function exportToJSON(events, options) {
10
+ const data = options?.metadata
11
+ ? {
12
+ metadata: {
13
+ exportedAt: new Date().toISOString(),
14
+ eventCount: events.length,
15
+ ...options.metadata,
16
+ },
17
+ events,
18
+ }
19
+ : events;
20
+ return options?.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
21
+ }
22
+ /**
23
+ * Export events to CSV format
24
+ */
25
+ export function exportToCSV(events) {
26
+ if (events.length === 0) {
27
+ return '';
28
+ }
29
+ // Define common columns
30
+ const columns = [
31
+ 'timestamp',
32
+ 'eventType',
33
+ 'agentId',
34
+ 'sessionId',
35
+ 'taskId',
36
+ // Decision-specific
37
+ 'reasoning',
38
+ 'chosenTool',
39
+ 'confidence',
40
+ // Tool call-specific
41
+ 'toolName',
42
+ 'success',
43
+ 'durationMs',
44
+ 'errorMessage',
45
+ // LLM call-specific
46
+ 'provider',
47
+ 'model',
48
+ 'promptTokens',
49
+ 'completionTokens',
50
+ 'cost',
51
+ 'cacheHit',
52
+ // Error-specific
53
+ 'message',
54
+ 'recoverable',
55
+ 'errorCode',
56
+ ];
57
+ // Create CSV header
58
+ const header = columns.join(',');
59
+ // Create CSV rows
60
+ const rows = events.map((event) => {
61
+ const row = [];
62
+ for (const column of columns) {
63
+ const value = event[column];
64
+ if (value === undefined || value === null) {
65
+ row.push('');
66
+ }
67
+ else if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) {
68
+ // Escape strings with commas or quotes
69
+ row.push(`"${value.replace(/"/g, '""')}"`);
70
+ }
71
+ else {
72
+ row.push(String(value));
73
+ }
74
+ }
75
+ return row.join(',');
76
+ });
77
+ return [header, ...rows].join('\n');
78
+ }
79
+ /**
80
+ * Export events to NDJSON format (newline-delimited JSON)
81
+ */
82
+ export function exportToNDJSON(events) {
83
+ return events.map((event) => JSON.stringify(event)).join('\n');
84
+ }
85
+ /**
86
+ * Export events to a file (Node.js)
87
+ */
88
+ export async function exportToFile(events, filePath, format = 'json') {
89
+ const fs = await import('node:fs/promises');
90
+ let content;
91
+ switch (format) {
92
+ case 'json':
93
+ content = exportToJSON(events, { pretty: true });
94
+ break;
95
+ case 'csv':
96
+ content = exportToCSV(events);
97
+ break;
98
+ case 'ndjson':
99
+ content = exportToNDJSON(events);
100
+ break;
101
+ }
102
+ await fs.writeFile(filePath, content, 'utf-8');
103
+ }
104
+ /**
105
+ * Create a downloadable file blob (Browser)
106
+ */
107
+ export function createDownloadableBlob(events, format = 'json') {
108
+ let content;
109
+ let mimeType;
110
+ switch (format) {
111
+ case 'json':
112
+ content = exportToJSON(events, { pretty: true });
113
+ mimeType = 'application/json';
114
+ break;
115
+ case 'csv':
116
+ content = exportToCSV(events);
117
+ mimeType = 'text/csv';
118
+ break;
119
+ case 'ndjson':
120
+ content = exportToNDJSON(events);
121
+ mimeType = 'application/x-ndjson';
122
+ break;
123
+ }
124
+ return new Blob([content], { type: mimeType });
125
+ }
126
+ /**
127
+ * Trigger download in browser
128
+ */
129
+ export function downloadEvents(events, filename, format = 'json') {
130
+ if (typeof window === 'undefined') {
131
+ throw new Error('downloadEvents can only be used in browser environments');
132
+ }
133
+ const blob = createDownloadableBlob(events, format);
134
+ const url = URL.createObjectURL(blob);
135
+ const a = document.createElement('a');
136
+ a.href = url;
137
+ a.download = filename;
138
+ document.body.appendChild(a);
139
+ a.click();
140
+ document.body.removeChild(a);
141
+ URL.revokeObjectURL(url);
142
+ }
143
+ /**
144
+ * Export helper class with logger integration
145
+ */
146
+ export class EventExporter {
147
+ logger;
148
+ constructor(logger) {
149
+ this.logger = logger;
150
+ }
151
+ /**
152
+ * Export all events
153
+ */
154
+ exportAll(format = 'json') {
155
+ const events = this.logger.getEvents();
156
+ switch (format) {
157
+ case 'json':
158
+ return exportToJSON(events, { pretty: true });
159
+ case 'csv':
160
+ return exportToCSV(events);
161
+ case 'ndjson':
162
+ return exportToNDJSON(events);
163
+ }
164
+ }
165
+ /**
166
+ * Export filtered events
167
+ */
168
+ exportFiltered(filter, format = 'json') {
169
+ const events = this.logger.getEvents(filter);
170
+ switch (format) {
171
+ case 'json':
172
+ return exportToJSON(events, {
173
+ pretty: true,
174
+ metadata: { filter },
175
+ });
176
+ case 'csv':
177
+ return exportToCSV(events);
178
+ case 'ndjson':
179
+ return exportToNDJSON(events);
180
+ }
181
+ }
182
+ /**
183
+ * Export to file (Node.js)
184
+ */
185
+ async exportToFile(filePath, format = 'json', filter) {
186
+ const events = filter ? this.logger.getEvents(filter) : this.logger.getEvents();
187
+ await exportToFile(events, filePath, format);
188
+ }
189
+ /**
190
+ * Download in browser
191
+ */
192
+ download(filename, format = 'json', filter) {
193
+ const events = filter ? this.logger.getEvents(filter) : this.logger.getEvents();
194
+ downloadEvents(events, filename, format);
195
+ }
196
+ /**
197
+ * Export by time range
198
+ */
199
+ exportTimeRange(start, end, format = 'json') {
200
+ return this.exportFiltered({
201
+ startTime: start.getTime(),
202
+ endTime: end.getTime(),
203
+ }, format);
204
+ }
205
+ /**
206
+ * Export by agent
207
+ */
208
+ exportByAgent(agentId, format = 'json') {
209
+ return this.exportFiltered({ agentId }, format);
210
+ }
211
+ /**
212
+ * Export by session
213
+ */
214
+ exportBySession(sessionId, format = 'json') {
215
+ return this.exportFiltered({ sessionId }, format);
216
+ }
217
+ /**
218
+ * Export errors only
219
+ */
220
+ exportErrors(format = 'json') {
221
+ return this.exportFiltered({ eventType: 'error' }, format);
222
+ }
223
+ /**
224
+ * Export LLM calls only
225
+ */
226
+ exportLLMCalls(format = 'json') {
227
+ return this.exportFiltered({ eventType: 'llm_call' }, format);
228
+ }
229
+ }