@datafog/fogclaw 0.1.4 → 0.1.6

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.
@@ -9,12 +9,14 @@ function createApi() {
9
9
  return {
10
10
  pluginConfig: {
11
11
  model: "invalid:/not/real/model",
12
+ auditEnabled: true,
12
13
  },
13
14
  hooks,
14
15
  tools,
15
16
  logger: {
16
17
  info: vi.fn(),
17
18
  warn: vi.fn(),
19
+ error: vi.fn(),
18
20
  },
19
21
  on: vi.fn((event: string, handler: (event: any) => Promise<any>) => {
20
22
  hooks.push({ event, handler });
@@ -46,14 +48,18 @@ describe("FogClaw OpenClaw plugin contract (integration path)", () => {
46
48
 
47
49
  expect(typeof plugin.register).toBe("function");
48
50
  expect(api.on).toHaveBeenCalledWith("before_agent_start", expect.any(Function));
49
- expect(api.registerTool).toHaveBeenCalledTimes(2);
51
+ expect(api.registerTool).toHaveBeenCalledTimes(3);
50
52
 
51
53
  const scanTool = api.tools.find((tool: any) => tool.id === "fogclaw_scan");
54
+ const previewTool = api.tools.find((tool: any) => tool.id === "fogclaw_preview");
52
55
  const redactTool = api.tools.find((tool: any) => tool.id === "fogclaw_redact");
53
56
 
54
57
  expect(scanTool).toBeDefined();
58
+ expect(previewTool).toBeDefined();
55
59
  expect(redactTool).toBeDefined();
60
+
56
61
  expect(scanTool.schema.required).toContain("text");
62
+ expect(previewTool.schema.required).toContain("text");
57
63
  expect(redactTool.schema.required).toContain("text");
58
64
  });
59
65
 
@@ -96,6 +102,29 @@ describe("FogClaw OpenClaw plugin contract (integration path)", () => {
96
102
  expect(redactParsed.redacted_text).not.toContain("john@example.com");
97
103
  });
98
104
 
105
+ it("supports preview output with action plan and redacted text", async () => {
106
+ const api = createApi();
107
+
108
+ plugin.register(api);
109
+
110
+ const previewTool = api.tools.find((tool: any) => tool.id === "fogclaw_preview");
111
+
112
+ const previewOutput = await previewTool.handler({
113
+ text: "Email me at john@example.com about Acme Corp tomorrow.",
114
+ });
115
+
116
+ const parsed = JSON.parse(previewOutput.content[0].text);
117
+ expect(parsed.totalEntities).toBeGreaterThan(0);
118
+ expect(parsed.actionPlan).toEqual(
119
+ expect.objectContaining({
120
+ blocked: expect.objectContaining({ count: expect.any(Number) }),
121
+ warned: expect.objectContaining({ count: expect.any(Number) }),
122
+ redacted: expect.objectContaining({ count: expect.any(Number) }),
123
+ }),
124
+ );
125
+ expect(typeof parsed.redactedText).toBe("string");
126
+ });
127
+
99
128
  it("passes custom_labels through tool path in real execution", async () => {
100
129
  const api = createApi();
101
130
 
@@ -1,4 +1,12 @@
1
- import { beforeAll, beforeEach, afterAll, describe, it, expect, vi } from "vitest";
1
+ import {
2
+ beforeAll,
3
+ beforeEach,
4
+ afterAll,
5
+ describe,
6
+ it,
7
+ expect,
8
+ vi,
9
+ } from "vitest";
2
10
  import fs from "node:fs/promises";
3
11
  import os from "node:os";
4
12
  import path from "node:path";
@@ -186,6 +194,58 @@ describe("Scanner", () => {
186
194
  expect(email!.source).toBe("regex");
187
195
  });
188
196
 
197
+ it("applies per-entity confidence threshold overrides", async () => {
198
+ const strictScanner = new Scanner(
199
+ makeConfig({
200
+ entityConfidenceThresholds: {
201
+ PERSON: 0.98,
202
+ },
203
+ }),
204
+ );
205
+ await strictScanner.initialize();
206
+
207
+ const result = await strictScanner.scan("My name is John Smith.");
208
+ expect(result.entities.find((e) => e.label === "PERSON")).toBeUndefined();
209
+ });
210
+
211
+ it("supports allowlist exact matches across global and per-entity rules", async () => {
212
+ const allowlistScanner = new Scanner(
213
+ makeConfig({
214
+ allowlist: {
215
+ values: ["john@example.com"],
216
+ patterns: ["^internal-"],
217
+ entities: {
218
+ PERSON: ["john smith"],
219
+ },
220
+ },
221
+ }),
222
+ );
223
+ await allowlistScanner.initialize();
224
+
225
+ const result = await allowlistScanner.scan(
226
+ "John Smith can be reached at john@example.com.",
227
+ );
228
+
229
+ expect(result.entities.find((e) => e.label === "EMAIL")).toBeUndefined();
230
+ expect(result.entities.find((e) => e.label === "PERSON")).toBeUndefined();
231
+ });
232
+
233
+ it("applies allowlist regex patterns", async () => {
234
+ const allowlistScanner = new Scanner(
235
+ makeConfig({
236
+ allowlist: {
237
+ values: [],
238
+ patterns: ["test@example\\.com"],
239
+ entities: {},
240
+ },
241
+ }),
242
+ );
243
+ await allowlistScanner.initialize();
244
+
245
+ const result = await allowlistScanner.scan("This is test@example.com for redaction.");
246
+ expect(result.entities.find((e) => e.label === "EMAIL")).toBeUndefined();
247
+ });
248
+
189
249
  it("deduplicates overlapping spans keeping higher confidence", async () => {
190
250
  // Scan text that might produce overlapping entities
191
251
  // The dedup logic should keep higher confidence when spans overlap