@datafog/fogclaw 0.1.6 → 0.2.0
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/CHANGELOG.md +31 -0
- package/README.md +39 -0
- package/dist/extract.d.ts +28 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +91 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/message-sending-handler.d.ts +40 -0
- package/dist/message-sending-handler.d.ts.map +1 -0
- package/dist/message-sending-handler.js +50 -0
- package/dist/message-sending-handler.js.map +1 -0
- package/dist/tool-result-handler.d.ts +36 -0
- package/dist/tool-result-handler.d.ts.map +1 -0
- package/dist/tool-result-handler.js +91 -0
- package/dist/tool-result-handler.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/docs/OBSERVABILITY.md +22 -15
- package/docs/SECURITY.md +6 -4
- package/docs/plans/active/2026-02-17-feat-tool-result-pii-scanning-plan.md +293 -0
- package/docs/specs/2026-02-17-feat-outbound-message-pii-scanning-spec.md +93 -0
- package/docs/specs/2026-02-17-feat-tool-result-pii-scanning-spec.md +122 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/extract.ts +98 -0
- package/src/index.ts +13 -4
- package/src/message-sending-handler.ts +87 -0
- package/src/tool-result-handler.ts +133 -0
- package/src/types.ts +4 -0
- package/tests/extract.test.ts +185 -0
- package/tests/message-sending-handler.test.ts +244 -0
- package/tests/plugin-smoke.test.ts +109 -2
- package/tests/tool-result-handler.test.ts +329 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Tool result PII scanning** — new `tool_result_persist` hook scans file reads, API responses, and web fetches for PII before they enter the session transcript. Uses regex engine (sync) for <1ms latency on the hot path.
|
|
8
|
+
- **Outbound message PII scanning** — new `message_sending` hook provides a last-chance gate that catches PII in agent replies before delivery to Telegram, Slack, Discord, and other external channels. Uses full Scanner (regex + GLiNER).
|
|
9
|
+
- **Policy allowlist** — whitelist exact strings, regex patterns, or per-entity-type values to skip enforcement on known-safe content (e.g., `noreply@example.com`).
|
|
10
|
+
- **Per-entity confidence thresholds** — tune GLiNER detection sensitivity per label (e.g., require 0.7 for PERSON, 0.85 for ORGANIZATION).
|
|
11
|
+
- **Audit trail logging** — structured audit log entries with `source` field (`guardrail`, `tool_result`, `outbound`) when `auditEnabled: true`. Logs entity counts and labels without raw PII.
|
|
12
|
+
- **Policy preview tool** — `fogclaw_preview` shows which entities would be blocked, warned, or redacted without changing runtime behavior.
|
|
13
|
+
- **Scanning architecture documentation** — README now includes a comparison matrix showing engine trade-offs across all three hooks.
|
|
14
|
+
- **Control UI hints** — `openclaw.plugin.json` includes `uiHints` for policy configuration in OpenClaw's Control UI.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `resolveAction` extracted to shared `types.ts` module (was duplicated across files).
|
|
19
|
+
- README updated with three-layer scanning overview and defense-in-depth flow diagram.
|
|
20
|
+
|
|
21
|
+
## 0.1.6
|
|
22
|
+
|
|
23
|
+
Initial scoped release as `@datafog/fogclaw`.
|
|
24
|
+
|
|
25
|
+
- Dual-engine PII detection (regex + GLiNER via ONNX)
|
|
26
|
+
- `before_agent_start` hook for automatic prompt guardrail
|
|
27
|
+
- `fogclaw_scan` and `fogclaw_redact` tools
|
|
28
|
+
- Configurable per-entity actions (`redact`, `block`, `warn`)
|
|
29
|
+
- Multiple redaction strategies (`token`, `mask`, `hash`)
|
|
30
|
+
- Custom entity types via GLiNER zero-shot NER
|
|
31
|
+
- Graceful degradation to regex-only when GLiNER unavailable
|
package/README.md
CHANGED
|
@@ -6,7 +6,10 @@ FogClaw uses a dual-engine approach: battle-tested regex patterns for structured
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
+
- **Three-layer scanning** — inbound prompts, tool results, and outbound messages are all scanned for PII before they cross trust boundaries
|
|
9
10
|
- **Automatic guardrail** — intercepts messages before they reach the LLM via OpenClaw's `before_agent_start` hook
|
|
11
|
+
- **Tool result scanning** — redacts PII in file reads, API responses, and web fetches before they enter the session transcript (`tool_result_persist`)
|
|
12
|
+
- **Outbound message scanning** — last-chance gate that catches PII in agent replies before delivery to external channels (`message_sending`)
|
|
10
13
|
- **On-demand tools** — `fogclaw_scan`, `fogclaw_preview`, and `fogclaw_redact`
|
|
11
14
|
- **Dual detection engine** — regex for structured PII (<1ms), GLiNER for zero-shot NER (~50-200ms)
|
|
12
15
|
- **Custom entity types** — define any entity label (e.g., "project codename", "competitor name") and GLiNER detects them with zero training
|
|
@@ -141,6 +144,42 @@ Incoming message
|
|
|
141
144
|
Apply action per entity type (redact / block / warn)
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
## Scanning Architecture
|
|
148
|
+
|
|
149
|
+
FogClaw hooks into three points in the OpenClaw message lifecycle. Each hook uses the detection engine best suited to its runtime constraints:
|
|
150
|
+
|
|
151
|
+
| Hook | Direction | Engine | Latency | Async | Entity Coverage |
|
|
152
|
+
|------|-----------|--------|---------|-------|-----------------|
|
|
153
|
+
| `before_agent_start` | Inbound (user prompt) | Regex + GLiNER | ~50-200ms | Yes | Full — structured PII + names, orgs, custom entities |
|
|
154
|
+
| `tool_result_persist` | Internal (tool results) | Regex only | <1ms | No (sync) | Structured PII — emails, SSNs, phones, credit cards, IPs |
|
|
155
|
+
| `message_sending` | Outbound (agent reply) | Regex + GLiNER | ~50-200ms | Yes | Full — structured PII + names, orgs, custom entities |
|
|
156
|
+
|
|
157
|
+
**Why regex-only for tool results?** OpenClaw's `tool_result_persist` hook requires synchronous handlers — async returns are rejected. GLiNER inference runs a synchronous ONNX native call that blocks the event loop for 100-500ms per invocation, which would degrade gateway responsiveness (delayed heartbeats, WebSocket pings, HTTP responses). Regex covers the high-confidence structured patterns most common in tool output (credentials in file reads, contact info in API responses). Person names and organization names are caught on the async inbound and outbound paths, providing defense-in-depth without hot-path latency.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
User prompt ──► before_agent_start (regex + GLiNER)
|
|
161
|
+
│
|
|
162
|
+
▼
|
|
163
|
+
Agent + LLM
|
|
164
|
+
│
|
|
165
|
+
┌─────────┼─────────┐
|
|
166
|
+
▼ ▼ ▼
|
|
167
|
+
Tool call Tool call Tool call
|
|
168
|
+
│ │ │
|
|
169
|
+
▼ ▼ ▼
|
|
170
|
+
tool_result_persist (regex only, sync)
|
|
171
|
+
│
|
|
172
|
+
▼
|
|
173
|
+
Agent reply
|
|
174
|
+
│
|
|
175
|
+
▼
|
|
176
|
+
message_sending (regex + GLiNER)
|
|
177
|
+
│
|
|
178
|
+
▼
|
|
179
|
+
External channel
|
|
180
|
+
(Telegram, Slack, etc.)
|
|
181
|
+
```
|
|
182
|
+
|
|
144
183
|
## Detected Entity Types
|
|
145
184
|
|
|
146
185
|
### Regex Engine (structured PII)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for extracting text from AgentMessage tool result payloads
|
|
3
|
+
* and replacing text content after redaction.
|
|
4
|
+
*
|
|
5
|
+
* AgentMessage shapes handled:
|
|
6
|
+
* - Plain string
|
|
7
|
+
* - Object with `content: string`
|
|
8
|
+
* - Object with `content: [{ type: "text", text: "..." }, ...]`
|
|
9
|
+
*
|
|
10
|
+
* When multiple text blocks exist in a content array, they are joined
|
|
11
|
+
* with a null byte separator (\0) so entity offsets stay valid across
|
|
12
|
+
* the concatenated string. replaceText splits on the same separator
|
|
13
|
+
* to map redacted text back to individual blocks.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Extract all text content from an AgentMessage tool result payload.
|
|
17
|
+
* Returns an empty string if no text content is found.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractText(message: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* Replace text content in an AgentMessage tool result payload with
|
|
22
|
+
* the redacted version. Returns a shallow copy; does not mutate.
|
|
23
|
+
*
|
|
24
|
+
* If the message shape is not recognized or has no text, returns
|
|
25
|
+
* the original message unchanged.
|
|
26
|
+
*/
|
|
27
|
+
export declare function replaceText(message: unknown, redactedText: string): unknown;
|
|
28
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CA4BpD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAqC3E"}
|
package/dist/extract.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for extracting text from AgentMessage tool result payloads
|
|
3
|
+
* and replacing text content after redaction.
|
|
4
|
+
*
|
|
5
|
+
* AgentMessage shapes handled:
|
|
6
|
+
* - Plain string
|
|
7
|
+
* - Object with `content: string`
|
|
8
|
+
* - Object with `content: [{ type: "text", text: "..." }, ...]`
|
|
9
|
+
*
|
|
10
|
+
* When multiple text blocks exist in a content array, they are joined
|
|
11
|
+
* with a null byte separator (\0) so entity offsets stay valid across
|
|
12
|
+
* the concatenated string. replaceText splits on the same separator
|
|
13
|
+
* to map redacted text back to individual blocks.
|
|
14
|
+
*/
|
|
15
|
+
// Separator between text segments from content block arrays.
|
|
16
|
+
// Null byte won't appear in regex PII patterns or normal text content.
|
|
17
|
+
const SEGMENT_SEP = "\0";
|
|
18
|
+
/**
|
|
19
|
+
* Extract all text content from an AgentMessage tool result payload.
|
|
20
|
+
* Returns an empty string if no text content is found.
|
|
21
|
+
*/
|
|
22
|
+
export function extractText(message) {
|
|
23
|
+
if (message == null)
|
|
24
|
+
return "";
|
|
25
|
+
if (typeof message === "string")
|
|
26
|
+
return message;
|
|
27
|
+
if (typeof message !== "object")
|
|
28
|
+
return "";
|
|
29
|
+
const msg = message;
|
|
30
|
+
const content = msg.content;
|
|
31
|
+
if (content == null)
|
|
32
|
+
return "";
|
|
33
|
+
if (typeof content === "string")
|
|
34
|
+
return content;
|
|
35
|
+
if (Array.isArray(content)) {
|
|
36
|
+
const textParts = [];
|
|
37
|
+
for (const block of content) {
|
|
38
|
+
if (block != null &&
|
|
39
|
+
typeof block === "object" &&
|
|
40
|
+
block.type === "text" &&
|
|
41
|
+
typeof block.text === "string") {
|
|
42
|
+
textParts.push(block.text);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (textParts.length === 0)
|
|
46
|
+
return "";
|
|
47
|
+
return textParts.join(SEGMENT_SEP);
|
|
48
|
+
}
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Replace text content in an AgentMessage tool result payload with
|
|
53
|
+
* the redacted version. Returns a shallow copy; does not mutate.
|
|
54
|
+
*
|
|
55
|
+
* If the message shape is not recognized or has no text, returns
|
|
56
|
+
* the original message unchanged.
|
|
57
|
+
*/
|
|
58
|
+
export function replaceText(message, redactedText) {
|
|
59
|
+
if (message == null)
|
|
60
|
+
return message;
|
|
61
|
+
if (typeof message === "string")
|
|
62
|
+
return redactedText;
|
|
63
|
+
if (typeof message !== "object")
|
|
64
|
+
return message;
|
|
65
|
+
const msg = message;
|
|
66
|
+
const content = msg.content;
|
|
67
|
+
if (content == null)
|
|
68
|
+
return message;
|
|
69
|
+
if (typeof content === "string") {
|
|
70
|
+
return { ...msg, content: redactedText };
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(content)) {
|
|
73
|
+
const segments = redactedText.split(SEGMENT_SEP);
|
|
74
|
+
let segmentIndex = 0;
|
|
75
|
+
const newContent = content.map((block) => {
|
|
76
|
+
if (block != null &&
|
|
77
|
+
typeof block === "object" &&
|
|
78
|
+
block.type === "text" &&
|
|
79
|
+
typeof block.text === "string" &&
|
|
80
|
+
segmentIndex < segments.length) {
|
|
81
|
+
const replaced = { ...block, text: segments[segmentIndex] };
|
|
82
|
+
segmentIndex++;
|
|
83
|
+
return replaced;
|
|
84
|
+
}
|
|
85
|
+
return block;
|
|
86
|
+
});
|
|
87
|
+
return { ...msg, content: newContent };
|
|
88
|
+
}
|
|
89
|
+
return message;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,6DAA6D;AAC7D,uEAAuE;AACvE,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAE3C,MAAM,GAAG,GAAG,OAAkC,CAAC;IAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAE5B,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAEhD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IACE,KAAK,IAAI,IAAI;gBACb,OAAO,KAAK,KAAK,QAAQ;gBACxB,KAAiC,CAAC,IAAI,KAAK,MAAM;gBAClD,OAAQ,KAAiC,CAAC,IAAI,KAAK,QAAQ,EAC3D,CAAC;gBACD,SAAS,CAAC,IAAI,CAAE,KAAiC,CAAC,IAAc,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,YAAoB;IAChE,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC;IACpC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC;IACrD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAEhD,MAAM,GAAG,GAAG,OAAkC,CAAC;IAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAE5B,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC;IAEpC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IAC3C,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACvC,IACE,KAAK,IAAI,IAAI;gBACb,OAAO,KAAK,KAAK,QAAQ;gBACxB,KAAiC,CAAC,IAAI,KAAK,MAAM;gBAClD,OAAQ,KAAiC,CAAC,IAAI,KAAK,QAAQ;gBAC3D,YAAY,GAAG,QAAQ,CAAC,MAAM,EAC9B,CAAC;gBACD,MAAM,QAAQ,GAAG,EAAE,GAAI,KAAiC,EAAE,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBACzF,YAAY,EAAE,CAAC;gBACf,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACzC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAqEpB;;;;;;;;GAQG;AACH,QAAA,MAAM,OAAO;;;kBAIG,GAAG;CA8QlB,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Scanner } from "./scanner.js";
|
|
2
2
|
import { redact } from "./redactor.js";
|
|
3
3
|
import { loadConfig } from "./config.js";
|
|
4
|
+
import { RegexEngine } from "./engines/regex.js";
|
|
5
|
+
import { createToolResultHandler } from "./tool-result-handler.js";
|
|
6
|
+
import { createMessageSendingHandler } from "./message-sending-handler.js";
|
|
7
|
+
import { resolveAction } from "./types.js";
|
|
4
8
|
export { Scanner } from "./scanner.js";
|
|
5
9
|
export { redact } from "./redactor.js";
|
|
6
10
|
export { loadConfig, DEFAULT_CONFIG } from "./config.js";
|
|
7
|
-
function resolveAction(entity, config) {
|
|
8
|
-
return config.entityActions[entity.label] ?? config.guardrail_mode;
|
|
9
|
-
}
|
|
10
11
|
function buildGuardrailPlan(entities, config) {
|
|
11
12
|
const blocked = [];
|
|
12
13
|
const warned = [];
|
|
@@ -107,6 +108,13 @@ const fogclaw = {
|
|
|
107
108
|
return { prependContext: contextParts.join("\n\n") };
|
|
108
109
|
}
|
|
109
110
|
});
|
|
111
|
+
// --- HOOK: Scan tool results for PII before persistence ---
|
|
112
|
+
const toolResultRegex = new RegexEngine();
|
|
113
|
+
const toolResultHandler = createToolResultHandler(config, toolResultRegex, api.logger);
|
|
114
|
+
api.on("tool_result_persist", toolResultHandler);
|
|
115
|
+
// --- HOOK: Scan outbound messages for PII before delivery ---
|
|
116
|
+
const messageSendingHandler = createMessageSendingHandler(config, scanner, api.logger);
|
|
117
|
+
api.on("message_sending", messageSendingHandler);
|
|
110
118
|
// --- TOOL: On-demand scan ---
|
|
111
119
|
api.registerTool({
|
|
112
120
|
name: "fogclaw_scan",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAU3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAUzD,SAAS,kBAAkB,CAAC,QAAkB,EAAE,MAAqB;IACnE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;aACxC,IAAI,MAAM,KAAK,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;;YAC3C,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,IAA2C;IAWhE,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM;QACtE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;QAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;QAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;QAC9B,MAAM,EAAE;YACN,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjE,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/D,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;SACpE;KACF,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,IAA2C,EAAE,MAAqB;IAC/F,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClF,YAAY,CAAC,IAAI,CACf,oFAAoF,KAAK,KAAK;YAC5F,6FAA6F,CAChG,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,YAAY,CAAC,IAAI,CACf,kDAAkD,KAAK,qBAAqB,CAC7E,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,YAAY,CAAC,IAAI,CACf,sBAAsB,IAAI,CAAC,QAAQ,CAAC,MAAM,6BAA6B,MAAM,CAAC,cAAc,eAAe,MAAM,IAAI,CACtH,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,GAAG;IACd,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,SAAS;IAEf,QAAQ,CAAC,GAAQ;QACf,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;QAErC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACzD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QACpC,iEAAiE;QACjE,iDAAiD;QACjD,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YAC1C,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,4CAA4C,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,+CAA+C;QAC/C,GAAG,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE;YAChD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO;gBAAE,OAAO;YAErB,MAAM,MAAM,GAAe,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAEzC,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACzD,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAEzD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;gBACpC,GAAG,CAAC,MAAM,EAAE,IAAI,CACd,kCAAkC,IAAI,CAAC,SAAS,CAAC;oBAC/C,aAAa,EAAE,OAAO,CAAC,KAAK;oBAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO;oBACrC,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;oBACnC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;iBACxC,CAAC,EAAE,CACL,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,cAAc,GAAiB,MAAM,CACzC,OAAO,EACP,IAAI,CAAC,QAAQ,EACb,MAAM,CAAC,cAAc,CACtB,CAAC;gBACF,YAAY,CAAC,IAAI,CACf,8EAA8E,cAAc,CAAC,aAAa,EAAE,CAC7G,CAAC;YACJ,CAAC;YAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,6DAA6D;QAC7D,MAAM,eAAe,GAAG,IAAI,WAAW,EAAE,CAAC;QAC1C,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACvF,GAAG,CAAC,EAAE,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;QAEjD,+DAA+D;QAC/D,MAAM,qBAAqB,GAAG,2BAA2B,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACvF,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;QAEjD,+BAA+B;QAC/B,GAAG,CAAC,YAAY,CACd;YACE,IAAI,EAAE,cAAc;YACpB,EAAE,EAAE,cAAc;YAClB,WAAW,EACT,gHAAgH;YAClH,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,2BAA2B;qBACzC;oBACD,aAAa,EAAE;wBACb,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EACT,kGAAkG;qBACrG;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB;YACD,OAAO,EAAE,KAAK,EAAE,EACd,IAAI,EACJ,aAAa,GAId,EAAE,EAAE;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBACvD,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gCACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;gCACzB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;gCAC7B,OAAO,EACL,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;oCACxB,CAAC,CAAC,SAAS,MAAM,CAAC,QAAQ,CAAC,MAAM,cAAc,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oCACvH,CAAC,CAAC,sBAAsB;6BAC7B,EACD,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;SACF,CACF,CAAC;QAEF,+BAA+B;QAC/B,GAAG,CAAC,YAAY,CACd;YACE,IAAI,EAAE,iBAAiB;YACvB,EAAE,EAAE,iBAAiB;YACrB,WAAW,EACT,0HAA0H;YAC5H,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,4CAA4C;qBAC1D;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,iHAAiH;wBACnH,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;qBAChC;oBACD,aAAa,EAAE;wBACb,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EAAE,kDAAkD;qBAChE;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB;YACD,OAAO,EAAE,KAAK,EAAE,EACd,IAAI,EACJ,QAAQ,EACR,aAAa,GAKd,EAAE,EAAE;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBACvD,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBACzD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,QAAQ,GAAG,MAAM,CACrB,IAAI,EACJ,IAAI,CAAC,QAAQ,EACb,QAAQ,IAAI,MAAM,CAAC,cAAc,CAClC,CAAC;gBAEF,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gCACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;gCACzB,aAAa,EAAE,OAAO,CAAC,KAAK;gCAC5B,UAAU,EAAE;oCACV,OAAO,EAAE;wCACP,KAAK,EAAE,OAAO,CAAC,OAAO;wCACtB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO;qCAC/B;oCACD,MAAM,EAAE;wCACN,KAAK,EAAE,OAAO,CAAC,MAAM;wCACrB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;qCAC9B;oCACD,QAAQ,EAAE;wCACR,KAAK,EAAE,OAAO,CAAC,QAAQ;wCACvB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;qCAChC;iCACF;gCACD,YAAY,EAAE,QAAQ,CAAC,aAAa;gCACpC,iBAAiB,EAAE,QAAQ,IAAI,MAAM,CAAC,cAAc;gCACpD,OAAO,EAAE,QAAQ,CAAC,OAAO;6BAC1B,EACD,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;SACF,CACF,CAAC;QAEF,iCAAiC;QACjC,GAAG,CAAC,YAAY,CACd;YACE,IAAI,EAAE,gBAAgB;YACtB,EAAE,EAAE,gBAAgB;YACpB,WAAW,EACT,+FAA+F;YACjG,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,yBAAyB;qBACvC;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,uFAAuF;wBACzF,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;qBAChC;oBACD,aAAa,EAAE;wBACb,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EAAE,kDAAkD;qBAChE;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB;YACD,OAAO,EAAE,KAAK,EAAE,EACd,IAAI,EACJ,QAAQ,EACR,aAAa,GAKd,EAAE,EAAE;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBACvD,MAAM,QAAQ,GAAG,MAAM,CACrB,IAAI,EACJ,MAAM,CAAC,QAAQ,EACf,QAAQ,IAAI,MAAM,CAAC,cAAc,CAClC,CAAC;gBACF,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gCACE,aAAa,EAAE,QAAQ,CAAC,aAAa;gCACrC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;gCACtC,OAAO,EAAE,QAAQ,CAAC,OAAO;6BAC1B,EACD,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;SACF,CACF,CAAC;QAEF,GAAG,CAAC,MAAM,EAAE,IAAI,CACd,4CAA4C,MAAM,CAAC,cAAc,YAAY,MAAM,CAAC,KAAK,sBAAsB,MAAM,CAAC,eAAe,CAAC,MAAM,YAAY,MAAM,CAAC,YAAY,EAAE,CAC9K,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async message_sending hook handler for FogClaw.
|
|
3
|
+
*
|
|
4
|
+
* Scans outbound message text for PII using the full Scanner
|
|
5
|
+
* (regex + GLiNER), redacts detected entities, and returns
|
|
6
|
+
* modified content. Never cancels message delivery.
|
|
7
|
+
*
|
|
8
|
+
* Note: message_sending is defined in OpenClaw but not yet invoked
|
|
9
|
+
* upstream. This handler activates automatically when wired.
|
|
10
|
+
*/
|
|
11
|
+
import type { Scanner } from "./scanner.js";
|
|
12
|
+
import type { FogClawConfig } from "./types.js";
|
|
13
|
+
interface Logger {
|
|
14
|
+
info(msg: string): void;
|
|
15
|
+
warn(msg: string): void;
|
|
16
|
+
}
|
|
17
|
+
export interface MessageSendingEvent {
|
|
18
|
+
to: string;
|
|
19
|
+
content: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface MessageSendingContext {
|
|
23
|
+
channelId: string;
|
|
24
|
+
accountId?: string;
|
|
25
|
+
conversationId?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MessageSendingResult {
|
|
28
|
+
content?: string;
|
|
29
|
+
cancel?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create an async message_sending hook handler.
|
|
33
|
+
*
|
|
34
|
+
* Uses the full Scanner (regex + GLiNER) since this hook supports
|
|
35
|
+
* async handlers. All guardrail modes produce span-level redaction;
|
|
36
|
+
* cancel is never returned.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createMessageSendingHandler(config: FogClawConfig, scanner: Scanner, logger?: Logger): (event: MessageSendingEvent, ctx: MessageSendingContext) => Promise<MessageSendingResult | void>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=message-sending-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-sending-handler.d.ts","sourceRoot":"","sources":["../src/message-sending-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EAAU,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,MAAM,GACd,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,qBAAqB,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAqClG"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async message_sending hook handler for FogClaw.
|
|
3
|
+
*
|
|
4
|
+
* Scans outbound message text for PII using the full Scanner
|
|
5
|
+
* (regex + GLiNER), redacts detected entities, and returns
|
|
6
|
+
* modified content. Never cancels message delivery.
|
|
7
|
+
*
|
|
8
|
+
* Note: message_sending is defined in OpenClaw but not yet invoked
|
|
9
|
+
* upstream. This handler activates automatically when wired.
|
|
10
|
+
*/
|
|
11
|
+
import { redact } from "./redactor.js";
|
|
12
|
+
import { resolveAction } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Create an async message_sending hook handler.
|
|
15
|
+
*
|
|
16
|
+
* Uses the full Scanner (regex + GLiNER) since this hook supports
|
|
17
|
+
* async handlers. All guardrail modes produce span-level redaction;
|
|
18
|
+
* cancel is never returned.
|
|
19
|
+
*/
|
|
20
|
+
export function createMessageSendingHandler(config, scanner, logger) {
|
|
21
|
+
return async (event, _ctx) => {
|
|
22
|
+
const text = event.content;
|
|
23
|
+
if (!text)
|
|
24
|
+
return;
|
|
25
|
+
const result = await scanner.scan(text);
|
|
26
|
+
if (result.entities.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
// All modes produce span-level redaction for outbound messages.
|
|
29
|
+
const actionableEntities = result.entities.filter((entity) => {
|
|
30
|
+
const action = resolveAction(entity, config);
|
|
31
|
+
return action === "redact" || action === "block" || action === "warn";
|
|
32
|
+
});
|
|
33
|
+
if (actionableEntities.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
const redacted = redact(text, actionableEntities, config.redactStrategy);
|
|
36
|
+
// Audit logging
|
|
37
|
+
if (config.auditEnabled && logger) {
|
|
38
|
+
const labels = [...new Set(actionableEntities.map((e) => e.label))];
|
|
39
|
+
logger.info(`[FOGCLAW AUDIT] outbound_scan ${JSON.stringify({
|
|
40
|
+
totalEntities: actionableEntities.length,
|
|
41
|
+
labels,
|
|
42
|
+
channelId: _ctx.channelId ?? null,
|
|
43
|
+
source: "outbound",
|
|
44
|
+
})}`);
|
|
45
|
+
}
|
|
46
|
+
// Never cancel — always deliver the redacted version.
|
|
47
|
+
return { content: redacted.redacted_text };
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=message-sending-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-sending-handler.js","sourceRoot":"","sources":["../src/message-sending-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyB3C;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAAqB,EACrB,OAAgB,EAChB,MAAe;IAEf,OAAO,KAAK,EACV,KAA0B,EAC1B,IAA2B,EACW,EAAE;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC;QAC3B,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEzC,gEAAgE;QAChE,MAAM,kBAAkB,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;YAC3D,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC7C,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;QAEzE,gBAAgB;QAChB,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpE,MAAM,CAAC,IAAI,CACT,iCAAiC,IAAI,CAAC,SAAS,CAAC;gBAC9C,aAAa,EAAE,kBAAkB,CAAC,MAAM;gBACxC,MAAM;gBACN,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;gBACjC,MAAM,EAAE,UAAU;aACnB,CAAC,EAAE,CACL,CAAC;QACJ,CAAC;QAED,sDAAsD;QACtD,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous tool_result_persist hook handler for FogClaw.
|
|
3
|
+
*
|
|
4
|
+
* Scans tool result text for PII using the regex engine (synchronous),
|
|
5
|
+
* redacts detected entities, and returns the transformed message.
|
|
6
|
+
* GLiNER is not used here because tool_result_persist is synchronous-only.
|
|
7
|
+
*/
|
|
8
|
+
import { RegexEngine } from "./engines/regex.js";
|
|
9
|
+
import type { FogClawConfig } from "./types.js";
|
|
10
|
+
interface Logger {
|
|
11
|
+
info(msg: string): void;
|
|
12
|
+
warn(msg: string): void;
|
|
13
|
+
}
|
|
14
|
+
export interface ToolResultPersistEvent {
|
|
15
|
+
toolName?: string;
|
|
16
|
+
toolCallId?: string;
|
|
17
|
+
message: unknown;
|
|
18
|
+
isSynthetic?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ToolResultPersistContext {
|
|
21
|
+
agentId?: string;
|
|
22
|
+
sessionKey?: string;
|
|
23
|
+
toolName?: string;
|
|
24
|
+
toolCallId?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a synchronous tool_result_persist hook handler.
|
|
28
|
+
*
|
|
29
|
+
* The returned function must NOT return a Promise — OpenClaw rejects
|
|
30
|
+
* async tool_result_persist handlers.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createToolResultHandler(config: FogClawConfig, regexEngine: RegexEngine, logger?: Logger): (event: ToolResultPersistEvent, ctx: ToolResultPersistContext) => {
|
|
33
|
+
message: unknown;
|
|
34
|
+
} | void;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=tool-result-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-result-handler.d.ts","sourceRoot":"","sources":["../src/tool-result-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,OAAO,KAAK,EAAU,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA6CD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,CAAC,KAAK,EAAE,sBAAsB,EAAE,GAAG,EAAE,wBAAwB,KAAK;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA8C/F"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous tool_result_persist hook handler for FogClaw.
|
|
3
|
+
*
|
|
4
|
+
* Scans tool result text for PII using the regex engine (synchronous),
|
|
5
|
+
* redacts detected entities, and returns the transformed message.
|
|
6
|
+
* GLiNER is not used here because tool_result_persist is synchronous-only.
|
|
7
|
+
*/
|
|
8
|
+
import { redact } from "./redactor.js";
|
|
9
|
+
import { extractText, replaceText } from "./extract.js";
|
|
10
|
+
import { canonicalType, resolveAction } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Build an allowlist filter from config. Replicates Scanner.filterByPolicy
|
|
13
|
+
* and Scanner.shouldAllowlistEntity logic synchronously.
|
|
14
|
+
*/
|
|
15
|
+
function buildAllowlistFilter(config) {
|
|
16
|
+
const globalValues = new Set(config.allowlist.values.map((v) => v.trim().toLowerCase()));
|
|
17
|
+
const globalPatterns = config.allowlist.patterns
|
|
18
|
+
.filter((p) => p.length > 0)
|
|
19
|
+
.map((p) => new RegExp(p, "i"));
|
|
20
|
+
const entityValues = new Map();
|
|
21
|
+
for (const [entityType, values] of Object.entries(config.allowlist.entities)) {
|
|
22
|
+
const canonical = canonicalType(entityType);
|
|
23
|
+
const set = new Set(values
|
|
24
|
+
.map((v) => v.trim().toLowerCase())
|
|
25
|
+
.filter((v) => v.length > 0));
|
|
26
|
+
entityValues.set(canonical, set);
|
|
27
|
+
}
|
|
28
|
+
// Short-circuit: if no allowlist entries, keep everything
|
|
29
|
+
if (globalValues.size === 0 && globalPatterns.length === 0 && entityValues.size === 0) {
|
|
30
|
+
return () => true;
|
|
31
|
+
}
|
|
32
|
+
// Return true if entity should be KEPT (not allowlisted)
|
|
33
|
+
return (entity) => {
|
|
34
|
+
const normalizedText = entity.text.trim().toLowerCase();
|
|
35
|
+
if (globalValues.has(normalizedText))
|
|
36
|
+
return false;
|
|
37
|
+
if (globalPatterns.some((pattern) => pattern.test(entity.text)))
|
|
38
|
+
return false;
|
|
39
|
+
const perEntity = entityValues.get(entity.label);
|
|
40
|
+
if (perEntity && perEntity.has(normalizedText))
|
|
41
|
+
return false;
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a synchronous tool_result_persist hook handler.
|
|
47
|
+
*
|
|
48
|
+
* The returned function must NOT return a Promise — OpenClaw rejects
|
|
49
|
+
* async tool_result_persist handlers.
|
|
50
|
+
*/
|
|
51
|
+
export function createToolResultHandler(config, regexEngine, logger) {
|
|
52
|
+
const shouldKeep = buildAllowlistFilter(config);
|
|
53
|
+
return (event, _ctx) => {
|
|
54
|
+
const text = extractText(event.message);
|
|
55
|
+
if (!text)
|
|
56
|
+
return;
|
|
57
|
+
// Scan with regex engine (synchronous)
|
|
58
|
+
let entities = regexEngine.scan(text);
|
|
59
|
+
if (entities.length === 0)
|
|
60
|
+
return;
|
|
61
|
+
// Apply allowlist filtering
|
|
62
|
+
entities = entities.filter(shouldKeep);
|
|
63
|
+
if (entities.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
// All guardrail modes produce span-level redaction in tool results.
|
|
66
|
+
// Determine which entities are actionable (all of them — block/warn/redact
|
|
67
|
+
// all produce redaction at the tool result level).
|
|
68
|
+
const actionableEntities = entities.filter((entity) => {
|
|
69
|
+
const action = resolveAction(entity, config);
|
|
70
|
+
return action === "redact" || action === "block" || action === "warn";
|
|
71
|
+
});
|
|
72
|
+
if (actionableEntities.length === 0)
|
|
73
|
+
return;
|
|
74
|
+
// Redact
|
|
75
|
+
const result = redact(text, actionableEntities, config.redactStrategy);
|
|
76
|
+
// Replace text in the message
|
|
77
|
+
const modifiedMessage = replaceText(event.message, result.redacted_text);
|
|
78
|
+
// Audit logging
|
|
79
|
+
if (config.auditEnabled && logger) {
|
|
80
|
+
const labels = [...new Set(actionableEntities.map((e) => e.label))];
|
|
81
|
+
logger.info(`[FOGCLAW AUDIT] tool_result_scan ${JSON.stringify({
|
|
82
|
+
totalEntities: actionableEntities.length,
|
|
83
|
+
labels,
|
|
84
|
+
toolName: event.toolName ?? null,
|
|
85
|
+
source: "tool_result",
|
|
86
|
+
})}`);
|
|
87
|
+
}
|
|
88
|
+
return { message: modifiedMessage };
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=tool-result-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-result-handler.js","sourceRoot":"","sources":["../src/tool-result-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -44,4 +44,5 @@ export interface GuardrailPlan {
|
|
|
44
44
|
}
|
|
45
45
|
export declare const CANONICAL_TYPE_MAP: Record<string, string>;
|
|
46
46
|
export declare function canonicalType(entityType: string): string;
|
|
47
|
+
export declare function resolveAction(entity: Entity, config: FogClawConfig): GuardrailAction;
|
|
47
48
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IACzC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,eAAe,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,0BAA0B,EAAE,0BAA0B,CAAC;IACvD,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAYrD,CAAC;AAEF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGxD"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IACzC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,eAAe,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,0BAA0B,EAAE,0BAA0B,CAAC;IACvD,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAYrD,CAAC;AAEF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,eAAe,CAEpF"}
|
package/dist/types.js
CHANGED
|
@@ -15,4 +15,7 @@ export function canonicalType(entityType) {
|
|
|
15
15
|
const normalized = entityType.toUpperCase().trim();
|
|
16
16
|
return CANONICAL_TYPE_MAP[normalized] ?? normalized;
|
|
17
17
|
}
|
|
18
|
+
export function resolveAction(entity, config) {
|
|
19
|
+
return config.entityActions[entity.label] ?? config.guardrail_mode;
|
|
20
|
+
}
|
|
18
21
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAqDA,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,OAAO;IACrB,sBAAsB,EAAE,KAAK;IAC7B,kBAAkB,EAAE,aAAa;IACjC,aAAa,EAAE,MAAM;CACtB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACnD,OAAO,kBAAkB,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;AACtD,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAqDA,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,OAAO;IACrB,sBAAsB,EAAE,KAAK;IAC7B,kBAAkB,EAAE,aAAa;IACjC,aAAa,EAAE,MAAM;CACtB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACnD,OAAO,kBAAkB,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,MAAqB;IACjE,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,cAAc,CAAC;AACrE,CAAC"}
|
package/docs/OBSERVABILITY.md
CHANGED
|
@@ -4,22 +4,29 @@ use_when: "Documenting logging, metrics, tracing, and health check conventions f
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Logging Strategy
|
|
7
|
-
- Prefer structured logs with consistent fields (service, env, request_id/trace_id, user_id when safe).
|
|
8
|
-
- Never log secrets; be deliberate about PII.
|
|
9
|
-
- Log at boundaries and on errors; avoid noisy per-loop logging in hot paths.
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
- Track the golden signals: latency, traffic, errors, saturation.
|
|
13
|
-
- Prefer histograms for latency; keep label cardinality low.
|
|
8
|
+
FogClaw uses `api.logger` provided by OpenClaw during plugin registration. Three log levels are used:
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
10
|
+
- `info` — Audit log entries for PII detections, plugin lifecycle events (registration, config load).
|
|
11
|
+
- `warn` — GLiNER model initialization failures, degraded mode notifications, scan errors that fall back gracefully.
|
|
12
|
+
- `error` — Configuration validation failures, unrecoverable errors.
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
- Health checks are fast and deterministic; readiness reflects dependency availability when needed.
|
|
21
|
-
- Document expected status codes and what "unhealthy" means operationally.
|
|
14
|
+
Never log raw PII values. Audit entries include entity counts and type labels only.
|
|
22
15
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
## Audit Log Format
|
|
17
|
+
|
|
18
|
+
When `auditEnabled: true`, FogClaw emits structured JSON audit entries on each scan that detects entities:
|
|
19
|
+
|
|
20
|
+
[FOGCLAW AUDIT] guardrail_scan {"totalEntities":2,"blocked":1,"warned":0,"redacted":1,"blockedLabels":["SSN"],"warnedLabels":[],"redactedLabels":["EMAIL"],"source":"prompt"}
|
|
21
|
+
|
|
22
|
+
The `source` field distinguishes scan surfaces: `"prompt"` for `before_agent_start`, `"tool_result"` for `tool_result_persist`.
|
|
23
|
+
|
|
24
|
+
## Health Signals
|
|
25
|
+
|
|
26
|
+
- **Plugin registration:** `[fogclaw] Plugin registered` log line at startup confirms the plugin loaded and configured successfully.
|
|
27
|
+
- **GLiNER availability:** Logged at startup. If the ONNX model fails to download or load, FogClaw logs a warning and operates in regex-only mode.
|
|
28
|
+
- **Scan activity:** Audit entries indicate active scanning. Absence of audit entries when PII is known to be present may indicate misconfiguration, a disabled plugin, or a gap in hook coverage.
|
|
29
|
+
|
|
30
|
+
## Metrics and Traces
|
|
31
|
+
|
|
32
|
+
FogClaw does not emit standalone metrics or traces. It operates within OpenClaw's process and relies on the host's observability infrastructure. Audit log entries serve as the primary observability signal.
|