avantgate 1.5.0 → 1.6.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.
package/README.md CHANGED
@@ -33,13 +33,54 @@ flowchart LR
33
33
 
34
34
  ---
35
35
 
36
- ## ☁️ GateWall Platform — Enterprise AI-WAF & Corporate DLP *(Coming Soon)*
36
+ ## 🖥️ GateWall Cockpit — Self-Hosted AI-WAF & Governance Console (`gatewall/`)
37
37
 
38
- While the open-source **AvantGate SDK** provides lightweight, in-process control and PII redaction, **GateWall Platform** is the enterprise AI Application Firewall (AI-WAF) and Corporate DLP gateway engineered for regulated industries (Fintech, Banking, Legaltech, and Listed Scale-ups).
38
+ While the **AvantGate SDK** provides zero-infrastructure, in-process control inside your application runtime, the included **GateWall Cockpit** (`gatewall/`) provides a full-featured, self-hosted visual control plane, real-time audit inspector, and Human-in-the-Loop approval.
39
39
 
40
- Available as a **Managed Cloud Control Plane** or an **Out-of-Process High-Availability Sidecar/Proxy**:
40
+ ```mermaid
41
+ flowchart LR
42
+ Agent[🤖 AvantGate Agent / SDK] -- "POST /api/v1/ingest/events<br/>(Async Telemetry)" --> GateWall["🛡️ GateWall Cockpit<br/>(Next.js + SQLite)"]
43
+ GateWall -- "SSE /api/v1/realtime" --> Browser["💻 Web Dashboard<br/>(http://localhost:3000)"]
44
+ Browser -- "POST /api/v1/approvals<br/>(Approve / Reject)" --> GateWall
45
+ ```
46
+
47
+ ### 🚀 Quickstart: Run GateWall Cockpit
48
+
49
+ #### Option 1: One-Line Docker Compose (Recommended)
50
+ ```bash
51
+ docker compose up -d
52
+ # or: npm run gatewall:docker
53
+ ```
54
+ Open **`http://localhost:3000`** in your browser 🎉. Traces and audit sessions are automatically persisted.
55
+
56
+ #### Option 2: Run with Bun or Node.js
57
+ ```bash
58
+ cd gatewall
59
+ bun install # or npm install
60
+ bun run dev # or npm run dev
61
+ ```
62
+
63
+ ---
64
+
65
+ ### 🛡️ Cockpit Capabilities & Modules
66
+
67
+ | Module | Description |
68
+ |---|---|
69
+ | **🔒 Dual-Channel PII Inspector** | Visually compare the sequestered local escrow (`rawPayload`) against the redacted summary dispatched to the model (`llmSummary`). Highlights detected PII (emails, API keys, IBANs, phone numbers). |
70
+ | **🧑‍⚖️ Human-in-the-Loop (HITL) Queue** | Real-time queue intercepting high-impact tool executions (`WAITING_APPROVAL`) with an interactive dashboard to inspect parameters and click **Approve** or **Reject**. |
71
+ | **⚡ Real-Time Streaming (SSE)** | Low-latency Server-Sent Events (`/api/v1/realtime`) updating session trees, metrics, and alerts dynamically without manual page refresh. |
72
+ | **💰 FinOps & Token Tracking** | Automated token cost estimation and USD burn tracking across providers (GPT-4o, Claude 3.5 Sonnet, DeepSeek, etc.). |
73
+ | **🛡️ Loop Shield (Anti-Cycle Guard)** | Early visual detection of recursive agent loops and aberrant repetitive tool execution cycles. |
74
+ | **💾 Zero-External-DB SQLite Storage** | Zero setup overhead: persistent storage uses embedded SQLite (`gatewall/data/gatewall.db`) without requiring PostgreSQL, Redis, or ClickHouse. |
75
+
76
+ > 💡 **Agent Integration Recipes:** See [Connecting an Agent to GateWall Cockpit](docs/examples.md#12-connecting-an-agent-to-gatewall-cockpit-gatewall) in `docs/examples.md` for complete code recipes using `HttpTelemetryExporter` (`avantgate/agent`), direct HTTP ingestion, and React front-end streaming (`avantgate/client`).
77
+
78
+ ---
79
+
80
+ ### ☁️ Enterprise & Managed Cloud
41
81
 
42
- > 🚀 **Interested in private preview, enterprise VPC, or On-Premise deployment?** Contact our team at [contact@gatewall.fr](mailto:contact@gatewall.fr) for early access.
82
+ Looking for enterprise multi-tenant RBAC, SSO/SAML, managed VPC sidecars, or on-premise air-gapped compliance?
83
+ Contact our team at [contact@gatewall.fr](mailto:contact@gatewall.fr) for private previews and enterprise deployment options.
43
84
 
44
85
  ---
45
86
 
@@ -54,6 +95,7 @@ Available as a **Managed Cloud Control Plane** or an **Out-of-Process High-Avail
54
95
  - 🔀 **Zero-Downtime Multi-Model Failover**: Seamless client-side failover to fallback providers (or local zero-cost Ollama) when upstream APIs return HTTP 429/500 errors.
55
96
  - 🔧 **Self-Repairing Structured Outputs**: Strict Zod schema compliance with automated heuristic markdown/JSON repair if the model hallucinates formatting.
56
97
  - 📡 **Zero-Dependency Telemetry Bridge (`HttpTelemetryExporter`, `PlatformStorageAdapter`)**: Mirror execution audits and hierarchical tool traces asynchronously without adding heavy external dependencies.
98
+ - 🌐 **Lightweight Front-End SDK & React Hook (`avantgate/client`)**: Micro-bundle (< 1.7 KB gzipped) for browser UIs. Captures client-side security alerts (blocked API keys, prompt injection attempts), UI render metrics, and user feedback, streaming them directly to gateWall via `fetch(keepalive)` without loading backend infrastructure.
57
99
  - 📦 **100% Framework Agnostic**: Works seamlessly in Next.js, Express, Fastify, NestJS, Cloudflare Workers, AWS Lambda, or CLI scripts.
58
100
 
59
101
  ---
@@ -93,6 +135,7 @@ yarn add avantgate zod
93
135
  | `avantgate/finance` | Financial data normalizer (accounting parentheses, EU/US/UK/CH currencies & magnitudes). |
94
136
  | `avantgate/agent` | *(Preview / Experimental)* Durable step runner, Human-in-the-Loop, dual-channel PII tool isolation & storage adapters. |
95
137
  | `avantgate/workflow` | Deterministic sequential state machine, automatic reverse Saga rollback, durable HITL checkpoints & agent tool conversion. |
138
+ | `avantgate/client` | Lightweight front-end SDK (< 1.7 KB) & React hook to stream browser security alerts & metrics directly to GateWall. |
96
139
 
97
140
  ---
98
141
 
@@ -0,0 +1,32 @@
1
+ import type { BrowserTelemetryExporterConfig, ClientTelemetryEvent } from "./types";
2
+ export declare class BrowserTelemetryExporter {
3
+ private readonly publicKey;
4
+ private readonly endpoint;
5
+ private agentName?;
6
+ private runId?;
7
+ private readonly batchIntervalMs;
8
+ private readonly maxBatchSize;
9
+ private readonly fetchFn?;
10
+ private readonly onError?;
11
+ private buffer;
12
+ private timer;
13
+ private isFlushing;
14
+ private isDestroyed;
15
+ private readonly handleVisibilityChange;
16
+ private readonly handleBeforeUnload;
17
+ constructor(config: BrowserTelemetryExporterConfig);
18
+ updateContext: (context: {
19
+ runId?: string;
20
+ agentName?: string;
21
+ }) => void;
22
+ getRunId: () => string | undefined;
23
+ getAgentName: () => string | undefined;
24
+ private startTimer;
25
+ private setupBrowserListeners;
26
+ emit: (event: ClientTelemetryEvent) => void;
27
+ flush: () => Promise<void>;
28
+ private dispatchPayload;
29
+ private handleFlushError;
30
+ destroy: () => void;
31
+ }
32
+ //# sourceMappingURL=browser-exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-exporter.d.ts","sourceRoot":"","sources":["../../src/client/browser-exporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,8BAA8B,EAC9B,oBAAoB,EAErB,MAAM,SAAS,CAAC;AAMjB,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAyB;IAElD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAIrC;IAEF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAEjC;gBAEU,MAAM,EAAE,8BAA8B;IAc3C,aAAa,GAAI,SAAS;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,IAAI,CAO5E;IAEK,QAAQ,QAAO,MAAM,GAAG,SAAS,CAAe;IAEhD,YAAY,QAAO,MAAM,GAAG,SAAS,CAAmB;IAE/D,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,qBAAqB;IAStB,IAAI,GAAI,OAAO,oBAAoB,KAAG,IAAI,CAQ/C;IAEK,KAAK,QAAa,OAAO,CAAC,IAAI,CAAC,CAuBpC;IAEF,OAAO,CAAC,eAAe,CAkBrB;IAEF,OAAO,CAAC,gBAAgB,CAStB;IAEK,OAAO,QAAO,IAAI,CAavB;CACH"}
@@ -0,0 +1,37 @@
1
+ import { BrowserTelemetryExporter } from "./browser-exporter";
2
+ import type { BrowserTelemetryExporterConfig, ClientSecurityAlertEvent } from "./types";
3
+ export interface TrackSecurityAlertDetails {
4
+ inputLength: number;
5
+ snippet?: string;
6
+ matchedPatternSnippet?: string;
7
+ }
8
+ export interface TrackDataRenderedOptions {
9
+ renderedItemCount?: number;
10
+ channel?: "SOCKET" | "STREAM" | "HTTP";
11
+ }
12
+ export interface TrackFeedbackOptions {
13
+ rating: "POSITIVE" | "NEGATIVE";
14
+ tag?: "HELPFUL" | "INACCURATE" | "UNSAFE" | "OFF_TOPIC";
15
+ comment?: string;
16
+ }
17
+ /**
18
+ * Public interface for the universal (framework-agnostic) avantGate telemetry client.
19
+ */
20
+ export interface AvantGateClient {
21
+ trackSecurityAlert: (alertType: ClientSecurityAlertEvent["alertType"], details: TrackSecurityAlertDetails) => void;
22
+ trackClientDataRendered: (toolId: string, options?: TrackDataRenderedOptions) => void;
23
+ trackFeedback: (feedback: TrackFeedbackOptions) => void;
24
+ updateContext: (context: {
25
+ runId?: string;
26
+ agentName?: string;
27
+ }) => void;
28
+ flush: () => Promise<void>;
29
+ destroy: () => void;
30
+ exporter: BrowserTelemetryExporter;
31
+ }
32
+ /**
33
+ * Creates a universal, framework-agnostic avantGate client instance.
34
+ * Works seamlessly in Vite (Vanilla JS, Vue, Svelte, SolidJS), Next.js, or browser scripts.
35
+ */
36
+ export declare const createAvantGateClient: (config: BrowserTelemetryExporterConfig) => AvantGateClient;
37
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EACV,8BAA8B,EAC9B,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IAChC,GAAG,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,CAClB,SAAS,EAAE,wBAAwB,CAAC,WAAW,CAAC,EAChD,OAAO,EAAE,yBAAyB,KAC/B,IAAI,CAAC;IACV,uBAAuB,EAAE,CACvB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,KAC/B,IAAI,CAAC;IACV,aAAa,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACxD,aAAa,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACzE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,8BAA8B,KACrC,eAkDF,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { BrowserTelemetryExporter } from "./browser-exporter";
2
+ export { createAvantGateClient, type AvantGateClient, type TrackSecurityAlertDetails, type TrackDataRenderedOptions, type TrackFeedbackOptions, } from "./client";
3
+ export { useAvantGateTelemetry } from "./useAvantGateTelemetry";
4
+ export type { ClientSecurityAlertEvent, ClientDataRenderedEvent, UserFeedbackEvent, ClientTelemetryEvent, BrowserTelemetryExporterConfig, ClientTelemetryIngestPayload, } from "./types";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,wBAAwB,EACxB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,SAAS,CAAC"}
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/client/index.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ BrowserTelemetryExporter: () => BrowserTelemetryExporter,
24
+ createAvantGateClient: () => createAvantGateClient,
25
+ useAvantGateTelemetry: () => useAvantGateTelemetry
26
+ });
27
+ module.exports = __toCommonJS(client_exports);
28
+
29
+ // src/client/browser-exporter.ts
30
+ var DEFAULT_ENDPOINT = "http://localhost:3000/api/v1/ingest/events";
31
+ var DEFAULT_INTERVAL_MS = 3e3;
32
+ var DEFAULT_MAX_BATCH_SIZE = 20;
33
+ var BrowserTelemetryExporter = class {
34
+ publicKey;
35
+ endpoint;
36
+ agentName;
37
+ runId;
38
+ batchIntervalMs;
39
+ maxBatchSize;
40
+ fetchFn;
41
+ onError;
42
+ buffer = [];
43
+ timer = null;
44
+ isFlushing = false;
45
+ isDestroyed = false;
46
+ handleVisibilityChange = () => {
47
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
48
+ void this.flush();
49
+ }
50
+ };
51
+ handleBeforeUnload = () => {
52
+ void this.flush();
53
+ };
54
+ constructor(config) {
55
+ this.publicKey = config.publicKey;
56
+ this.endpoint = config.endpoint ?? DEFAULT_ENDPOINT;
57
+ this.agentName = config.agentName;
58
+ this.runId = config.runId;
59
+ this.batchIntervalMs = config.batchIntervalMs ?? DEFAULT_INTERVAL_MS;
60
+ this.maxBatchSize = config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
61
+ this.fetchFn = config.fetchFn ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : void 0);
62
+ this.onError = config.onError;
63
+ this.startTimer();
64
+ this.setupBrowserListeners();
65
+ }
66
+ updateContext = (context) => {
67
+ if (context.runId !== void 0) {
68
+ this.runId = context.runId;
69
+ }
70
+ if (context.agentName !== void 0) {
71
+ this.agentName = context.agentName;
72
+ }
73
+ };
74
+ getRunId = () => this.runId;
75
+ getAgentName = () => this.agentName;
76
+ startTimer() {
77
+ if (this.batchIntervalMs <= 0 || typeof setInterval === "undefined") {
78
+ return;
79
+ }
80
+ this.timer = setInterval(() => {
81
+ void this.flush();
82
+ }, this.batchIntervalMs);
83
+ }
84
+ setupBrowserListeners() {
85
+ if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
86
+ document.addEventListener("visibilitychange", this.handleVisibilityChange);
87
+ }
88
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
89
+ window.addEventListener("beforeunload", this.handleBeforeUnload);
90
+ }
91
+ }
92
+ emit = (event) => {
93
+ if (this.isDestroyed) {
94
+ return;
95
+ }
96
+ this.buffer.push(event);
97
+ if (this.buffer.length >= this.maxBatchSize) {
98
+ void this.flush();
99
+ }
100
+ };
101
+ flush = async () => {
102
+ if (this.isFlushing || this.buffer.length === 0 || !this.fetchFn) {
103
+ return;
104
+ }
105
+ this.isFlushing = true;
106
+ const batch = [...this.buffer];
107
+ this.buffer = [];
108
+ const payload = {
109
+ runId: this.runId,
110
+ agentName: this.agentName,
111
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
112
+ events: batch
113
+ };
114
+ try {
115
+ await this.dispatchPayload(payload);
116
+ } catch (err) {
117
+ this.handleFlushError(err, batch);
118
+ } finally {
119
+ this.isFlushing = false;
120
+ }
121
+ };
122
+ dispatchPayload = async (payload) => {
123
+ if (!this.fetchFn) {
124
+ throw new Error("No fetch implementation available in current environment");
125
+ }
126
+ const response = await this.fetchFn(this.endpoint, {
127
+ method: "POST",
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ Authorization: `Bearer ${this.publicKey}`
131
+ },
132
+ body: JSON.stringify(payload),
133
+ keepalive: true
134
+ });
135
+ if (!response.ok) {
136
+ throw new Error(`Browser telemetry ingestion failed: ${response.status} ${response.statusText}`);
137
+ }
138
+ };
139
+ handleFlushError = (err, failedBatch) => {
140
+ if (!this.isDestroyed && this.buffer.length < this.maxBatchSize * 2) {
141
+ this.buffer.unshift(...failedBatch);
142
+ }
143
+ const error = err instanceof Error ? err : new Error(String(err));
144
+ if (this.onError) {
145
+ this.onError(error);
146
+ }
147
+ };
148
+ destroy = () => {
149
+ this.isDestroyed = true;
150
+ if (this.timer) {
151
+ clearInterval(this.timer);
152
+ this.timer = null;
153
+ }
154
+ if (typeof document !== "undefined" && typeof document.removeEventListener === "function") {
155
+ document.removeEventListener("visibilitychange", this.handleVisibilityChange);
156
+ }
157
+ if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
158
+ window.removeEventListener("beforeunload", this.handleBeforeUnload);
159
+ }
160
+ void this.flush();
161
+ };
162
+ };
163
+
164
+ // src/client/client.ts
165
+ var createAvantGateClient = (config) => {
166
+ const exporter = new BrowserTelemetryExporter(config);
167
+ const trackSecurityAlert = (alertType, details) => {
168
+ const snippet = details.snippet ?? details.matchedPatternSnippet;
169
+ exporter.emit({
170
+ type: "CLIENT_SECURITY_ALERT",
171
+ alertType,
172
+ inputLength: details.inputLength,
173
+ snippet,
174
+ matchedPatternSnippet: snippet,
175
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
176
+ });
177
+ };
178
+ const trackClientDataRendered = (toolId, options) => {
179
+ exporter.emit({
180
+ type: "CLIENT_DATA_RENDERED",
181
+ toolId,
182
+ channel: options?.channel,
183
+ renderedItemCount: options?.renderedItemCount,
184
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
185
+ });
186
+ };
187
+ const trackFeedback = (feedback) => {
188
+ exporter.emit({
189
+ type: "USER_FEEDBACK",
190
+ rating: feedback.rating,
191
+ feedbackTag: feedback.tag,
192
+ userComment: feedback.comment,
193
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
194
+ });
195
+ };
196
+ return {
197
+ trackSecurityAlert,
198
+ trackClientDataRendered,
199
+ trackFeedback,
200
+ updateContext: exporter.updateContext,
201
+ flush: exporter.flush,
202
+ destroy: exporter.destroy,
203
+ exporter
204
+ };
205
+ };
206
+
207
+ // src/client/useAvantGateTelemetry.ts
208
+ var import_react = require("react");
209
+ var useAvantGateTelemetry = (config) => {
210
+ const clientRef = (0, import_react.useRef)(null);
211
+ if (!clientRef.current) {
212
+ clientRef.current = createAvantGateClient(config);
213
+ }
214
+ (0, import_react.useEffect)(() => {
215
+ clientRef.current?.updateContext({
216
+ runId: config.runId,
217
+ agentName: config.agentName
218
+ });
219
+ }, [config.runId, config.agentName]);
220
+ (0, import_react.useEffect)(() => {
221
+ return () => {
222
+ clientRef.current?.destroy();
223
+ clientRef.current = null;
224
+ };
225
+ }, []);
226
+ const trackSecurityAlert = (0, import_react.useCallback)(
227
+ (alertType, details) => {
228
+ clientRef.current?.trackSecurityAlert(alertType, details);
229
+ },
230
+ []
231
+ );
232
+ const trackClientDataRendered = (0, import_react.useCallback)(
233
+ (toolId, options) => {
234
+ clientRef.current?.trackClientDataRendered(toolId, options);
235
+ },
236
+ []
237
+ );
238
+ const trackFeedback = (0, import_react.useCallback)(
239
+ (feedback) => {
240
+ clientRef.current?.trackFeedback(feedback);
241
+ },
242
+ []
243
+ );
244
+ return (0, import_react.useMemo)(
245
+ () => ({
246
+ trackSecurityAlert,
247
+ trackClientDataRendered,
248
+ trackFeedback,
249
+ exporter: clientRef.current?.exporter
250
+ }),
251
+ [trackSecurityAlert, trackClientDataRendered, trackFeedback]
252
+ );
253
+ };
254
+ // Annotate the CommonJS export names for ESM import in node:
255
+ 0 && (module.exports = {
256
+ BrowserTelemetryExporter,
257
+ createAvantGateClient,
258
+ useAvantGateTelemetry
259
+ });
@@ -0,0 +1,230 @@
1
+ // src/client/browser-exporter.ts
2
+ var DEFAULT_ENDPOINT = "http://localhost:3000/api/v1/ingest/events";
3
+ var DEFAULT_INTERVAL_MS = 3e3;
4
+ var DEFAULT_MAX_BATCH_SIZE = 20;
5
+ var BrowserTelemetryExporter = class {
6
+ publicKey;
7
+ endpoint;
8
+ agentName;
9
+ runId;
10
+ batchIntervalMs;
11
+ maxBatchSize;
12
+ fetchFn;
13
+ onError;
14
+ buffer = [];
15
+ timer = null;
16
+ isFlushing = false;
17
+ isDestroyed = false;
18
+ handleVisibilityChange = () => {
19
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
20
+ void this.flush();
21
+ }
22
+ };
23
+ handleBeforeUnload = () => {
24
+ void this.flush();
25
+ };
26
+ constructor(config) {
27
+ this.publicKey = config.publicKey;
28
+ this.endpoint = config.endpoint ?? DEFAULT_ENDPOINT;
29
+ this.agentName = config.agentName;
30
+ this.runId = config.runId;
31
+ this.batchIntervalMs = config.batchIntervalMs ?? DEFAULT_INTERVAL_MS;
32
+ this.maxBatchSize = config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
33
+ this.fetchFn = config.fetchFn ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : void 0);
34
+ this.onError = config.onError;
35
+ this.startTimer();
36
+ this.setupBrowserListeners();
37
+ }
38
+ updateContext = (context) => {
39
+ if (context.runId !== void 0) {
40
+ this.runId = context.runId;
41
+ }
42
+ if (context.agentName !== void 0) {
43
+ this.agentName = context.agentName;
44
+ }
45
+ };
46
+ getRunId = () => this.runId;
47
+ getAgentName = () => this.agentName;
48
+ startTimer() {
49
+ if (this.batchIntervalMs <= 0 || typeof setInterval === "undefined") {
50
+ return;
51
+ }
52
+ this.timer = setInterval(() => {
53
+ void this.flush();
54
+ }, this.batchIntervalMs);
55
+ }
56
+ setupBrowserListeners() {
57
+ if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
58
+ document.addEventListener("visibilitychange", this.handleVisibilityChange);
59
+ }
60
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
61
+ window.addEventListener("beforeunload", this.handleBeforeUnload);
62
+ }
63
+ }
64
+ emit = (event) => {
65
+ if (this.isDestroyed) {
66
+ return;
67
+ }
68
+ this.buffer.push(event);
69
+ if (this.buffer.length >= this.maxBatchSize) {
70
+ void this.flush();
71
+ }
72
+ };
73
+ flush = async () => {
74
+ if (this.isFlushing || this.buffer.length === 0 || !this.fetchFn) {
75
+ return;
76
+ }
77
+ this.isFlushing = true;
78
+ const batch = [...this.buffer];
79
+ this.buffer = [];
80
+ const payload = {
81
+ runId: this.runId,
82
+ agentName: this.agentName,
83
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
84
+ events: batch
85
+ };
86
+ try {
87
+ await this.dispatchPayload(payload);
88
+ } catch (err) {
89
+ this.handleFlushError(err, batch);
90
+ } finally {
91
+ this.isFlushing = false;
92
+ }
93
+ };
94
+ dispatchPayload = async (payload) => {
95
+ if (!this.fetchFn) {
96
+ throw new Error("No fetch implementation available in current environment");
97
+ }
98
+ const response = await this.fetchFn(this.endpoint, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ Authorization: `Bearer ${this.publicKey}`
103
+ },
104
+ body: JSON.stringify(payload),
105
+ keepalive: true
106
+ });
107
+ if (!response.ok) {
108
+ throw new Error(`Browser telemetry ingestion failed: ${response.status} ${response.statusText}`);
109
+ }
110
+ };
111
+ handleFlushError = (err, failedBatch) => {
112
+ if (!this.isDestroyed && this.buffer.length < this.maxBatchSize * 2) {
113
+ this.buffer.unshift(...failedBatch);
114
+ }
115
+ const error = err instanceof Error ? err : new Error(String(err));
116
+ if (this.onError) {
117
+ this.onError(error);
118
+ }
119
+ };
120
+ destroy = () => {
121
+ this.isDestroyed = true;
122
+ if (this.timer) {
123
+ clearInterval(this.timer);
124
+ this.timer = null;
125
+ }
126
+ if (typeof document !== "undefined" && typeof document.removeEventListener === "function") {
127
+ document.removeEventListener("visibilitychange", this.handleVisibilityChange);
128
+ }
129
+ if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
130
+ window.removeEventListener("beforeunload", this.handleBeforeUnload);
131
+ }
132
+ void this.flush();
133
+ };
134
+ };
135
+
136
+ // src/client/client.ts
137
+ var createAvantGateClient = (config) => {
138
+ const exporter = new BrowserTelemetryExporter(config);
139
+ const trackSecurityAlert = (alertType, details) => {
140
+ const snippet = details.snippet ?? details.matchedPatternSnippet;
141
+ exporter.emit({
142
+ type: "CLIENT_SECURITY_ALERT",
143
+ alertType,
144
+ inputLength: details.inputLength,
145
+ snippet,
146
+ matchedPatternSnippet: snippet,
147
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
148
+ });
149
+ };
150
+ const trackClientDataRendered = (toolId, options) => {
151
+ exporter.emit({
152
+ type: "CLIENT_DATA_RENDERED",
153
+ toolId,
154
+ channel: options?.channel,
155
+ renderedItemCount: options?.renderedItemCount,
156
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
157
+ });
158
+ };
159
+ const trackFeedback = (feedback) => {
160
+ exporter.emit({
161
+ type: "USER_FEEDBACK",
162
+ rating: feedback.rating,
163
+ feedbackTag: feedback.tag,
164
+ userComment: feedback.comment,
165
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
166
+ });
167
+ };
168
+ return {
169
+ trackSecurityAlert,
170
+ trackClientDataRendered,
171
+ trackFeedback,
172
+ updateContext: exporter.updateContext,
173
+ flush: exporter.flush,
174
+ destroy: exporter.destroy,
175
+ exporter
176
+ };
177
+ };
178
+
179
+ // src/client/useAvantGateTelemetry.ts
180
+ import { useRef, useEffect, useCallback, useMemo } from "react";
181
+ var useAvantGateTelemetry = (config) => {
182
+ const clientRef = useRef(null);
183
+ if (!clientRef.current) {
184
+ clientRef.current = createAvantGateClient(config);
185
+ }
186
+ useEffect(() => {
187
+ clientRef.current?.updateContext({
188
+ runId: config.runId,
189
+ agentName: config.agentName
190
+ });
191
+ }, [config.runId, config.agentName]);
192
+ useEffect(() => {
193
+ return () => {
194
+ clientRef.current?.destroy();
195
+ clientRef.current = null;
196
+ };
197
+ }, []);
198
+ const trackSecurityAlert = useCallback(
199
+ (alertType, details) => {
200
+ clientRef.current?.trackSecurityAlert(alertType, details);
201
+ },
202
+ []
203
+ );
204
+ const trackClientDataRendered = useCallback(
205
+ (toolId, options) => {
206
+ clientRef.current?.trackClientDataRendered(toolId, options);
207
+ },
208
+ []
209
+ );
210
+ const trackFeedback = useCallback(
211
+ (feedback) => {
212
+ clientRef.current?.trackFeedback(feedback);
213
+ },
214
+ []
215
+ );
216
+ return useMemo(
217
+ () => ({
218
+ trackSecurityAlert,
219
+ trackClientDataRendered,
220
+ trackFeedback,
221
+ exporter: clientRef.current?.exporter
222
+ }),
223
+ [trackSecurityAlert, trackClientDataRendered, trackFeedback]
224
+ );
225
+ };
226
+ export {
227
+ BrowserTelemetryExporter,
228
+ createAvantGateClient,
229
+ useAvantGateTelemetry
230
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Client Security Alert Event emitted when a local pre-flight guardrail detects an anomaly
3
+ * (prompt injection attempt, hardcoded API key, unredacted PII) before network egress.
4
+ */
5
+ export interface ClientSecurityAlertEvent {
6
+ type: "CLIENT_SECURITY_ALERT";
7
+ alertType: "POTENTIAL_PROMPT_INJECTION" | "SUSPECTED_SECRET_INPUT" | "SUSPECTED_PII_INPUT";
8
+ inputLength: number;
9
+ snippet?: string;
10
+ matchedPatternSnippet?: string;
11
+ timestamp: string;
12
+ }
13
+ /**
14
+ * Client Data Rendered Event emitted when tool output or stream chunks are rendered in UI.
15
+ */
16
+ export interface ClientDataRenderedEvent {
17
+ type: "CLIENT_DATA_RENDERED";
18
+ toolId: string;
19
+ channel?: "SOCKET" | "STREAM" | "HTTP";
20
+ renderedItemCount?: number;
21
+ timestamp: string;
22
+ }
23
+ /**
24
+ * User Feedback Event emitted when a user rates or comments on an agent response.
25
+ */
26
+ export interface UserFeedbackEvent {
27
+ type: "USER_FEEDBACK";
28
+ rating: "POSITIVE" | "NEGATIVE";
29
+ feedbackTag?: "HELPFUL" | "INACCURATE" | "UNSAFE" | "OFF_TOPIC";
30
+ userComment?: string;
31
+ timestamp: string;
32
+ }
33
+ /**
34
+ * Union of all supported front-end client telemetry events.
35
+ */
36
+ export type ClientTelemetryEvent = ClientSecurityAlertEvent | ClientDataRenderedEvent | UserFeedbackEvent;
37
+ /**
38
+ * Configuration options for BrowserTelemetryExporter.
39
+ */
40
+ export interface BrowserTelemetryExporterConfig {
41
+ publicKey: string;
42
+ endpoint?: string;
43
+ agentName?: string;
44
+ runId?: string;
45
+ batchIntervalMs?: number;
46
+ maxBatchSize?: number;
47
+ fetchFn?: typeof fetch;
48
+ onError?: (error: Error) => void;
49
+ }
50
+ /**
51
+ * Ingest payload format expected by POST /api/v1/ingest/events according to DESIGN-019.
52
+ */
53
+ export interface ClientTelemetryIngestPayload {
54
+ runId?: string;
55
+ agentName?: string;
56
+ timestamp: string;
57
+ events: ClientTelemetryEvent[];
58
+ }
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,4BAA4B,GAAG,wBAAwB,GAAG,qBAAqB,CAAC;IAC3F,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IAChC,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,wBAAwB,GACxB,uBAAuB,GACvB,iBAAiB,CAAC;AAEtB;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC"}
@@ -0,0 +1,14 @@
1
+ import { type TrackSecurityAlertDetails, type TrackDataRenderedOptions, type TrackFeedbackOptions } from "./client";
2
+ import type { BrowserTelemetryExporterConfig, ClientSecurityAlertEvent } from "./types";
3
+ export type { TrackSecurityAlertDetails, TrackDataRenderedOptions, TrackFeedbackOptions, };
4
+ /**
5
+ * Lightweight React Hook for front-end telemetry and client-side security event tracking.
6
+ * Encapsulates lifecycle management, useRef persistence, useCallback and useMemo optimization.
7
+ */
8
+ export declare const useAvantGateTelemetry: (config: BrowserTelemetryExporterConfig) => {
9
+ trackSecurityAlert: (alertType: ClientSecurityAlertEvent["alertType"], details: TrackSecurityAlertDetails) => void;
10
+ trackClientDataRendered: (toolId: string, options?: TrackDataRenderedOptions) => void;
11
+ trackFeedback: (feedback: TrackFeedbackOptions) => void;
12
+ exporter: import("./browser-exporter").BrowserTelemetryExporter | undefined;
13
+ };
14
+ //# sourceMappingURL=useAvantGateTelemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAvantGateTelemetry.d.ts","sourceRoot":"","sources":["../../src/client/useAvantGateTelemetry.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,8BAA8B,EAC9B,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,GACrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,8BAA8B;oCAuB3D,wBAAwB,CAAC,WAAW,CAAC,WACvC,yBAAyB,KACjC,IAAI;sCAQG,MAAM,YACJ,wBAAwB,KACjC,IAAI;8BAOI,oBAAoB,KAAG,IAAI;;CAezC,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "avantgate",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "Zero-infrastructure, in-process AI Application Firewall (AI-WAF) & privacy control plane for TypeScript: real-time prompt guardrails, zero-egress PII redaction, agent tool isolation, anti-IDOR defense, pre-flight token budgets, and multi-model failover.",
5
- "author": "Antigravity & LexTalk Team",
5
+ "author": "Gatewall Team",
6
6
  "license": "MIT",
7
7
  "keywords": [
8
8
  "avantgate",
9
+ "gatewall",
9
10
  "ai-waf",
10
11
  "guardrails",
11
12
  "prompt-injection",
@@ -59,21 +60,41 @@
59
60
  "import": "./dist/workflow/index.mjs",
60
61
  "require": "./dist/workflow/index.js"
61
62
  },
63
+ "./client": {
64
+ "types": "./dist/client/index.d.ts",
65
+ "import": "./dist/client/index.mjs",
66
+ "require": "./dist/client/index.js"
67
+ },
62
68
  "./package.json": "./package.json"
63
69
  },
70
+ "peerDependencies": {
71
+ "react": ">=18.0.0"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "react": {
75
+ "optional": true
76
+ }
77
+ },
64
78
  "files": [
65
79
  "dist",
66
80
  "README.md",
67
81
  "LICENSE"
68
82
  ],
69
83
  "scripts": {
70
- "build": "tsup src/index.ts src/finance/index.ts src/agent/index.ts src/workflow/index.ts --format cjs --format esm --clean && tsc --emitDeclarationOnly --outDir dist",
71
- "test": "tsx tests/avantgate.test.ts && tsx tests/preflight-budget.test.ts && tsx tests/financial-normalizer.test.ts && tsx tests/prompt-builder.test.ts && tsx tests/pii-extended.test.ts && npm run test:agent && npm run test:workflow",
84
+ "build": "tsup src/index.ts src/finance/index.ts src/agent/index.ts src/workflow/index.ts src/client/index.ts --format cjs --format esm --clean && tsc --emitDeclarationOnly --outDir dist",
85
+ "test": "tsx tests/avantgate.test.ts && tsx tests/preflight-budget.test.ts && tsx tests/financial-normalizer.test.ts && tsx tests/prompt-builder.test.ts && tsx tests/pii-extended.test.ts && npm run test:agent && npm run test:workflow && npm run test:client",
72
86
  "test:finance": "tsx tests/financial-normalizer.test.ts",
73
87
  "test:prompts": "tsx tests/prompt-builder.test.ts",
74
88
  "test:pii": "tsx tests/pii-extended.test.ts",
75
89
  "test:agent": "tsx tests/agent/step-runner.test.ts && tsx tests/agent/isolated-tool.test.ts && tsx tests/agent/dto-pattern.test.ts && tsx tests/agent/tool-patterns.test.ts && tsx tests/agent/storage-adapters.test.ts && tsx tests/agent/tool-aliasing.test.ts && tsx tests/agent/tool-chaining.test.ts && tsx tests/agent/tool-storage.test.ts && tsx tests/agent/telemetry-exporter.test.ts && tsx tests/agent/platform-adapter.test.ts && tsx tests/agent/tool-governance.test.ts && tsx tests/agent/tool-impact.test.ts",
76
90
  "test:workflow": "tsx tests/workflow/workflow-saga.test.ts && tsx tests/workflow/workflow-abort.test.ts",
91
+ "test:client": "tsx tests/client/browser-exporter.test.ts",
92
+ "test:demo": "tsx tests/agent/prospect-ai-demo-scenario.test.ts",
93
+ "gatewall": "npm --prefix gatewall run dev",
94
+ "gatewall:dev": "npm --prefix gatewall run dev",
95
+ "gatewall:build": "npm --prefix gatewall run build",
96
+ "gatewall:start": "npm --prefix gatewall run start",
97
+ "gatewall:docker": "docker compose up -d",
77
98
  "lint": "tsc --noEmit",
78
99
  "prepublishOnly": "npm run build && npm test"
79
100
  },
@@ -81,11 +102,21 @@
81
102
  "zod": "^3.23.0 || ^4.0.0"
82
103
  },
83
104
  "devDependencies": {
105
+ "@types/react": "^18.3.18",
106
+ "react": "^18.3.1",
84
107
  "tsup": "^8.3.5",
85
108
  "tsx": "^4.19.0",
86
109
  "typescript": "^5.4.0"
87
110
  },
88
111
  "publishConfig": {
89
112
  "access": "public"
113
+ },
114
+ "repository": {
115
+ "type": "git",
116
+ "url": "git+https://github.com/thienban/avantGate.git"
117
+ },
118
+ "homepage": "https://github.com/thienban/avantGate#readme",
119
+ "bugs": {
120
+ "url": "https://github.com/thienban/avantGate/issues"
90
121
  }
91
122
  }