@cdot65/prisma-airs 0.3.0-alpha.0 → 0.3.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/README.md +0 -2
- package/hooks/prisma-airs-audit/HOOK.md +1 -1
- package/hooks/prisma-airs-audit/handler.ts +1 -2
- package/hooks/prisma-airs-context/HOOK.md +1 -1
- package/hooks/prisma-airs-context/handler.ts +1 -2
- package/hooks/prisma-airs-guard/HOOK.md +1 -1
- package/hooks/prisma-airs-guard/handler.test.ts +2 -23
- package/hooks/prisma-airs-guard/handler.ts +1 -3
- package/hooks/prisma-airs-outbound/HOOK.md +1 -1
- package/hooks/prisma-airs-outbound/handler.test.ts +0 -24
- package/hooks/prisma-airs-outbound/handler.ts +1 -2
- package/hooks/prisma-airs-tools/HOOK.md +1 -1
- package/hooks/prisma-airs-tools/handler.ts +1 -2
- package/index.ts +3 -3
- package/openclaw.plugin.json +1 -51
- package/package.json +1 -1
- package/src/config.test.ts +13 -62
- package/src/config.ts +8 -30
package/README.md
CHANGED
|
@@ -122,8 +122,6 @@ Each security feature supports three modes:
|
|
|
122
122
|
- `prisma_airs_scan_response` — replaces outbound scanning
|
|
123
123
|
- `prisma_airs_check_tool_safety` — replaces tool gating
|
|
124
124
|
|
|
125
|
-
**Backward compatibility**: Old boolean flags (`audit_enabled`, `context_injection_enabled`, etc.) still work. `true` maps to `deterministic`, `false` maps to `off`. New `*_mode` fields take precedence.
|
|
126
|
-
|
|
127
125
|
**`fail_closed` constraint**: When `fail_closed=true` (default), all features must be `deterministic` or `off`. Probabilistic mode is rejected because the model might skip scanning.
|
|
128
126
|
|
|
129
127
|
## Usage
|
|
@@ -42,6 +42,6 @@ This hook runs asynchronously on every inbound message. It:
|
|
|
42
42
|
|
|
43
43
|
Controlled by plugin config:
|
|
44
44
|
|
|
45
|
-
- `
|
|
45
|
+
- `audit_mode`: Scanning mode (default: `deterministic`). Options: `deterministic` / `probabilistic` / `off`
|
|
46
46
|
- `profile_name`: AIRS profile to use for scanning
|
|
47
47
|
- `app_name`: Application name for scan metadata
|
|
@@ -41,7 +41,6 @@ interface PluginConfig {
|
|
|
41
41
|
entries?: {
|
|
42
42
|
"prisma-airs"?: {
|
|
43
43
|
config?: {
|
|
44
|
-
audit_enabled?: boolean;
|
|
45
44
|
profile_name?: string;
|
|
46
45
|
app_name?: string;
|
|
47
46
|
api_key?: string;
|
|
@@ -64,7 +63,7 @@ function getPluginConfig(ctx: HookContext & { cfg?: PluginConfig }): {
|
|
|
64
63
|
} {
|
|
65
64
|
const cfg = ctx.cfg?.plugins?.entries?.["prisma-airs"]?.config;
|
|
66
65
|
return {
|
|
67
|
-
enabled:
|
|
66
|
+
enabled: true,
|
|
68
67
|
profileName: cfg?.profile_name ?? "default",
|
|
69
68
|
appName: cfg?.app_name ?? "openclaw",
|
|
70
69
|
apiKey: cfg?.api_key ?? "",
|
|
@@ -37,5 +37,5 @@ The hook provides category-specific instructions to the agent:
|
|
|
37
37
|
|
|
38
38
|
## Configuration
|
|
39
39
|
|
|
40
|
-
- `
|
|
40
|
+
- `context_injection_mode`: Scanning mode (default: `deterministic`). Options: `deterministic` / `probabilistic` / `off`
|
|
41
41
|
- `fail_closed`: Block on scan failure (default: true)
|
|
@@ -47,7 +47,6 @@ interface PluginConfig {
|
|
|
47
47
|
entries?: {
|
|
48
48
|
"prisma-airs"?: {
|
|
49
49
|
config?: {
|
|
50
|
-
context_injection_enabled?: boolean;
|
|
51
50
|
profile_name?: string;
|
|
52
51
|
app_name?: string;
|
|
53
52
|
api_key?: string;
|
|
@@ -145,7 +144,7 @@ function getPluginConfig(ctx: HookContext): {
|
|
|
145
144
|
} {
|
|
146
145
|
const cfg = ctx.cfg?.plugins?.entries?.["prisma-airs"]?.config;
|
|
147
146
|
return {
|
|
148
|
-
enabled:
|
|
147
|
+
enabled: true,
|
|
149
148
|
profileName: cfg?.profile_name ?? "default",
|
|
150
149
|
appName: cfg?.app_name ?? "openclaw",
|
|
151
150
|
apiKey: cfg?.api_key ?? "",
|
|
@@ -60,27 +60,6 @@ describe("prisma-airs-guard hook", () => {
|
|
|
60
60
|
expect(files[1].path).toBe("SECURITY.md");
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("does not inject when reminder_enabled is false", async () => {
|
|
64
|
-
const event: TestEvent = {
|
|
65
|
-
type: "agent",
|
|
66
|
-
action: "bootstrap",
|
|
67
|
-
context: {
|
|
68
|
-
bootstrapFiles: [],
|
|
69
|
-
cfg: {
|
|
70
|
-
plugins: {
|
|
71
|
-
entries: {
|
|
72
|
-
"prisma-airs": { config: { reminder_enabled: false } },
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
await handler(event);
|
|
80
|
-
|
|
81
|
-
expect(event.context!.bootstrapFiles).toHaveLength(0);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
63
|
it("ignores non-bootstrap events", async () => {
|
|
85
64
|
const event: TestEvent = {
|
|
86
65
|
type: "agent",
|
|
@@ -158,7 +137,7 @@ describe("prisma-airs-guard hook", () => {
|
|
|
158
137
|
expect(event.context!.bootstrapFiles).toHaveLength(0);
|
|
159
138
|
});
|
|
160
139
|
|
|
161
|
-
it("
|
|
140
|
+
it("injects when reminder_mode is on", async () => {
|
|
162
141
|
const event: TestEvent = {
|
|
163
142
|
type: "agent",
|
|
164
143
|
action: "bootstrap",
|
|
@@ -168,7 +147,7 @@ describe("prisma-airs-guard hook", () => {
|
|
|
168
147
|
plugins: {
|
|
169
148
|
entries: {
|
|
170
149
|
"prisma-airs": {
|
|
171
|
-
config: { reminder_mode: "on"
|
|
150
|
+
config: { reminder_mode: "on" },
|
|
172
151
|
},
|
|
173
152
|
},
|
|
174
153
|
},
|
|
@@ -161,11 +161,9 @@ const handler: HookHandler = async (event: HookEvent) => {
|
|
|
161
161
|
const pluginSettings = prismaConfig?.config as Record<string, unknown> | undefined;
|
|
162
162
|
|
|
163
163
|
// Check if reminder is enabled (default true)
|
|
164
|
-
// Support both new reminder_mode and deprecated reminder_enabled
|
|
165
164
|
const reminderMode = pluginSettings?.reminder_mode as string | undefined;
|
|
166
|
-
const reminderEnabled = pluginSettings?.reminder_enabled as boolean | undefined;
|
|
167
165
|
|
|
168
|
-
if (reminderMode === "off"
|
|
166
|
+
if (reminderMode === "off") {
|
|
169
167
|
return;
|
|
170
168
|
}
|
|
171
169
|
|
|
@@ -38,6 +38,6 @@ Masked patterns include:
|
|
|
38
38
|
|
|
39
39
|
## Configuration
|
|
40
40
|
|
|
41
|
-
- `
|
|
41
|
+
- `outbound_mode`: Scanning mode (default: `deterministic`). Options: `deterministic` / `probabilistic` / `off`
|
|
42
42
|
- `fail_closed`: Block on scan failure (default: true)
|
|
43
43
|
- `dlp_mask_only`: Mask DLP instead of blocking (default: true)
|
|
@@ -61,7 +61,6 @@ describe("prisma-airs-outbound handler", () => {
|
|
|
61
61
|
entries: {
|
|
62
62
|
"prisma-airs": {
|
|
63
63
|
config: {
|
|
64
|
-
outbound_scanning_enabled: true,
|
|
65
64
|
profile_name: "default",
|
|
66
65
|
app_name: "test-app",
|
|
67
66
|
api_key: "test-api-key",
|
|
@@ -299,29 +298,6 @@ describe("prisma-airs-outbound handler", () => {
|
|
|
299
298
|
});
|
|
300
299
|
});
|
|
301
300
|
|
|
302
|
-
describe("disabled scanning", () => {
|
|
303
|
-
it("should skip scanning when disabled", async () => {
|
|
304
|
-
const ctxDisabled = {
|
|
305
|
-
...baseCtx,
|
|
306
|
-
cfg: {
|
|
307
|
-
plugins: {
|
|
308
|
-
entries: {
|
|
309
|
-
"prisma-airs": {
|
|
310
|
-
config: {
|
|
311
|
-
outbound_scanning_enabled: false,
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
},
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
const result = await handler(baseEvent, ctxDisabled);
|
|
320
|
-
expect(result).toBeUndefined();
|
|
321
|
-
expect(mockScan).not.toHaveBeenCalled();
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
301
|
describe("empty content", () => {
|
|
326
302
|
it("should skip empty content", async () => {
|
|
327
303
|
const emptyEvent = { ...baseEvent, content: "" };
|
|
@@ -40,7 +40,6 @@ interface PluginConfig {
|
|
|
40
40
|
entries?: {
|
|
41
41
|
"prisma-airs"?: {
|
|
42
42
|
config?: {
|
|
43
|
-
outbound_scanning_enabled?: boolean;
|
|
44
43
|
profile_name?: string;
|
|
45
44
|
app_name?: string;
|
|
46
45
|
api_key?: string;
|
|
@@ -129,7 +128,7 @@ function getPluginConfig(ctx: HookContext): {
|
|
|
129
128
|
} {
|
|
130
129
|
const cfg = ctx.cfg?.plugins?.entries?.["prisma-airs"]?.config;
|
|
131
130
|
return {
|
|
132
|
-
enabled:
|
|
131
|
+
enabled: true,
|
|
133
132
|
profileName: cfg?.profile_name ?? "default",
|
|
134
133
|
appName: cfg?.app_name ?? "openclaw",
|
|
135
134
|
apiKey: cfg?.api_key ?? "",
|
|
@@ -36,5 +36,5 @@ These tools are blocked on ANY detected threat:
|
|
|
36
36
|
|
|
37
37
|
## Configuration
|
|
38
38
|
|
|
39
|
-
- `
|
|
39
|
+
- `tool_gating_mode`: Scanning mode (default: `deterministic`). Options: `deterministic` / `probabilistic` / `off`
|
|
40
40
|
- `high_risk_tools`: List of tools to block on any threat
|
|
@@ -30,7 +30,6 @@ interface PluginConfig {
|
|
|
30
30
|
entries?: {
|
|
31
31
|
"prisma-airs"?: {
|
|
32
32
|
config?: {
|
|
33
|
-
tool_gating_enabled?: boolean;
|
|
34
33
|
high_risk_tools?: string[];
|
|
35
34
|
api_key?: string;
|
|
36
35
|
};
|
|
@@ -149,7 +148,7 @@ function getPluginConfig(ctx: HookContext): {
|
|
|
149
148
|
} {
|
|
150
149
|
const cfg = ctx.cfg?.plugins?.entries?.["prisma-airs"]?.config;
|
|
151
150
|
return {
|
|
152
|
-
enabled:
|
|
151
|
+
enabled: true,
|
|
153
152
|
highRiskTools: cfg?.high_risk_tools ?? DEFAULT_HIGH_RISK_TOOLS,
|
|
154
153
|
};
|
|
155
154
|
}
|
package/index.ts
CHANGED
|
@@ -419,7 +419,7 @@ export default function register(api: PluginApi): void {
|
|
|
419
419
|
const hasApiKey = isConfigured(cfg.api_key);
|
|
420
420
|
respond(true, {
|
|
421
421
|
plugin: "prisma-airs",
|
|
422
|
-
version: "0.3.0
|
|
422
|
+
version: "0.3.0",
|
|
423
423
|
modes,
|
|
424
424
|
config: {
|
|
425
425
|
profile_name: cfg.profile_name ?? "default",
|
|
@@ -511,7 +511,7 @@ export default function register(api: PluginApi): void {
|
|
|
511
511
|
const hasKey = isConfigured(cfg.api_key);
|
|
512
512
|
console.log("Prisma AIRS Plugin Status");
|
|
513
513
|
console.log("-------------------------");
|
|
514
|
-
console.log(`Version: 0.3.0
|
|
514
|
+
console.log(`Version: 0.3.0`);
|
|
515
515
|
console.log(`Profile: ${cfg.profile_name ?? "default"}`);
|
|
516
516
|
console.log(`App Name: ${cfg.app_name ?? "openclaw"}`);
|
|
517
517
|
console.log(`Modes:`);
|
|
@@ -567,7 +567,7 @@ export default function register(api: PluginApi): void {
|
|
|
567
567
|
// Export plugin metadata for discovery
|
|
568
568
|
export const id = "prisma-airs";
|
|
569
569
|
export const name = "Prisma AIRS Security";
|
|
570
|
-
export const version = "0.3.0
|
|
570
|
+
export const version = "0.3.0";
|
|
571
571
|
|
|
572
572
|
// Re-export scanner types and functions
|
|
573
573
|
export { scan, isConfigured } from "./src/scanner";
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "prisma-airs",
|
|
3
3
|
"name": "Prisma AIRS Security",
|
|
4
4
|
"description": "AI Runtime Security - full AIRS detection suite with audit logging, context injection, outbound blocking, and tool gating",
|
|
5
|
-
"version": "0.3.0
|
|
5
|
+
"version": "0.3.0",
|
|
6
6
|
"entrypoint": "index.ts",
|
|
7
7
|
"hooks": [
|
|
8
8
|
"hooks/prisma-airs-guard",
|
|
@@ -55,36 +55,6 @@
|
|
|
55
55
|
"default": "deterministic",
|
|
56
56
|
"description": "Tool gating mode: deterministic (hook, always gate), probabilistic (tool, model decides), or off"
|
|
57
57
|
},
|
|
58
|
-
"reminder_enabled": {
|
|
59
|
-
"type": "boolean",
|
|
60
|
-
"default": true,
|
|
61
|
-
"description": "DEPRECATED: Use reminder_mode instead. Inject security scanning reminder on agent bootstrap",
|
|
62
|
-
"deprecated": true
|
|
63
|
-
},
|
|
64
|
-
"audit_enabled": {
|
|
65
|
-
"type": "boolean",
|
|
66
|
-
"default": true,
|
|
67
|
-
"description": "DEPRECATED: Use audit_mode instead. Enable audit logging of all inbound messages",
|
|
68
|
-
"deprecated": true
|
|
69
|
-
},
|
|
70
|
-
"context_injection_enabled": {
|
|
71
|
-
"type": "boolean",
|
|
72
|
-
"default": true,
|
|
73
|
-
"description": "DEPRECATED: Use context_injection_mode instead. Inject security warnings into agent context",
|
|
74
|
-
"deprecated": true
|
|
75
|
-
},
|
|
76
|
-
"outbound_scanning_enabled": {
|
|
77
|
-
"type": "boolean",
|
|
78
|
-
"default": true,
|
|
79
|
-
"description": "DEPRECATED: Use outbound_mode instead. Enable scanning and blocking of outbound responses",
|
|
80
|
-
"deprecated": true
|
|
81
|
-
},
|
|
82
|
-
"tool_gating_enabled": {
|
|
83
|
-
"type": "boolean",
|
|
84
|
-
"default": true,
|
|
85
|
-
"description": "DEPRECATED: Use tool_gating_mode instead. Block dangerous tools when threats are detected",
|
|
86
|
-
"deprecated": true
|
|
87
|
-
},
|
|
88
58
|
"fail_closed": {
|
|
89
59
|
"type": "boolean",
|
|
90
60
|
"default": true,
|
|
@@ -152,26 +122,6 @@
|
|
|
152
122
|
"label": "Tool Gating Mode",
|
|
153
123
|
"description": "deterministic: always gate tool calls via hook; probabilistic: model checks tool safety via tool; off: disabled"
|
|
154
124
|
},
|
|
155
|
-
"reminder_enabled": {
|
|
156
|
-
"label": "Enable Bootstrap Reminder (deprecated)",
|
|
157
|
-
"description": "DEPRECATED: Use reminder_mode instead"
|
|
158
|
-
},
|
|
159
|
-
"audit_enabled": {
|
|
160
|
-
"label": "Enable Audit Logging (deprecated)",
|
|
161
|
-
"description": "DEPRECATED: Use audit_mode instead"
|
|
162
|
-
},
|
|
163
|
-
"context_injection_enabled": {
|
|
164
|
-
"label": "Enable Context Injection (deprecated)",
|
|
165
|
-
"description": "DEPRECATED: Use context_injection_mode instead"
|
|
166
|
-
},
|
|
167
|
-
"outbound_scanning_enabled": {
|
|
168
|
-
"label": "Enable Outbound Scanning (deprecated)",
|
|
169
|
-
"description": "DEPRECATED: Use outbound_mode instead"
|
|
170
|
-
},
|
|
171
|
-
"tool_gating_enabled": {
|
|
172
|
-
"label": "Enable Tool Gating (deprecated)",
|
|
173
|
-
"description": "DEPRECATED: Use tool_gating_mode instead"
|
|
174
|
-
},
|
|
175
125
|
"fail_closed": {
|
|
176
126
|
"label": "Fail Closed",
|
|
177
127
|
"description": "Block on scan failure (recommended for security)"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdot65/prisma-airs",
|
|
3
|
-
"version": "0.3.0
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Prisma AIRS (AI Runtime Security) plugin for OpenClaw - Full security suite with audit logging, context injection, outbound blocking, and tool gating",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
package/src/config.test.ts
CHANGED
|
@@ -6,59 +6,37 @@ import { describe, it, expect } from "vitest";
|
|
|
6
6
|
import { resolveMode, resolveReminderMode, resolveAllModes } from "./config";
|
|
7
7
|
|
|
8
8
|
describe("resolveMode", () => {
|
|
9
|
-
it("returns default when
|
|
10
|
-
expect(resolveMode(undefined
|
|
9
|
+
it("returns default when undefined", () => {
|
|
10
|
+
expect(resolveMode(undefined)).toBe("deterministic");
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it("returns custom default", () => {
|
|
14
|
-
expect(resolveMode(undefined,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("mode string takes precedence over boolean", () => {
|
|
18
|
-
expect(resolveMode("probabilistic", true)).toBe("probabilistic");
|
|
19
|
-
expect(resolveMode("off", true)).toBe("off");
|
|
20
|
-
expect(resolveMode("deterministic", false)).toBe("deterministic");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("falls back to boolean when mode undefined", () => {
|
|
24
|
-
expect(resolveMode(undefined, true)).toBe("deterministic");
|
|
25
|
-
expect(resolveMode(undefined, false)).toBe("off");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("ignores invalid mode string and falls back to boolean", () => {
|
|
29
|
-
expect(resolveMode("invalid", true)).toBe("deterministic");
|
|
30
|
-
expect(resolveMode("invalid", false)).toBe("off");
|
|
14
|
+
expect(resolveMode(undefined, "off")).toBe("off");
|
|
31
15
|
});
|
|
32
16
|
|
|
33
17
|
it("ignores invalid mode string and falls back to default", () => {
|
|
34
|
-
expect(resolveMode("invalid"
|
|
18
|
+
expect(resolveMode("invalid")).toBe("deterministic");
|
|
35
19
|
});
|
|
36
20
|
|
|
37
21
|
it("accepts all valid mode values", () => {
|
|
38
|
-
expect(resolveMode("deterministic"
|
|
39
|
-
expect(resolveMode("probabilistic"
|
|
40
|
-
expect(resolveMode("off"
|
|
22
|
+
expect(resolveMode("deterministic")).toBe("deterministic");
|
|
23
|
+
expect(resolveMode("probabilistic")).toBe("probabilistic");
|
|
24
|
+
expect(resolveMode("off")).toBe("off");
|
|
41
25
|
});
|
|
42
26
|
});
|
|
43
27
|
|
|
44
28
|
describe("resolveReminderMode", () => {
|
|
45
|
-
it("returns default when
|
|
46
|
-
expect(resolveReminderMode(undefined
|
|
29
|
+
it("returns default when undefined", () => {
|
|
30
|
+
expect(resolveReminderMode(undefined)).toBe("on");
|
|
47
31
|
});
|
|
48
32
|
|
|
49
|
-
it("
|
|
50
|
-
expect(resolveReminderMode("off"
|
|
51
|
-
expect(resolveReminderMode("on"
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("falls back to boolean", () => {
|
|
55
|
-
expect(resolveReminderMode(undefined, true)).toBe("on");
|
|
56
|
-
expect(resolveReminderMode(undefined, false)).toBe("off");
|
|
33
|
+
it("resolves valid mode values", () => {
|
|
34
|
+
expect(resolveReminderMode("off")).toBe("off");
|
|
35
|
+
expect(resolveReminderMode("on")).toBe("on");
|
|
57
36
|
});
|
|
58
37
|
|
|
59
38
|
it("ignores invalid mode string", () => {
|
|
60
|
-
expect(resolveReminderMode("invalid"
|
|
61
|
-
expect(resolveReminderMode("invalid", undefined)).toBe("on");
|
|
39
|
+
expect(resolveReminderMode("invalid")).toBe("on");
|
|
62
40
|
});
|
|
63
41
|
});
|
|
64
42
|
|
|
@@ -92,33 +70,6 @@ describe("resolveAllModes", () => {
|
|
|
92
70
|
});
|
|
93
71
|
});
|
|
94
72
|
|
|
95
|
-
it("resolves deprecated booleans", () => {
|
|
96
|
-
const modes = resolveAllModes({
|
|
97
|
-
reminder_enabled: false,
|
|
98
|
-
audit_enabled: false,
|
|
99
|
-
context_injection_enabled: true,
|
|
100
|
-
outbound_scanning_enabled: false,
|
|
101
|
-
tool_gating_enabled: true,
|
|
102
|
-
fail_closed: false,
|
|
103
|
-
});
|
|
104
|
-
expect(modes).toEqual({
|
|
105
|
-
reminder: "off",
|
|
106
|
-
audit: "off",
|
|
107
|
-
context: "deterministic",
|
|
108
|
-
outbound: "off",
|
|
109
|
-
toolGating: "deterministic",
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("new mode takes precedence over deprecated boolean", () => {
|
|
114
|
-
const modes = resolveAllModes({
|
|
115
|
-
audit_mode: "probabilistic",
|
|
116
|
-
audit_enabled: false, // would be "off", but mode overrides
|
|
117
|
-
fail_closed: false,
|
|
118
|
-
});
|
|
119
|
-
expect(modes.audit).toBe("probabilistic");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
73
|
it("throws when fail_closed=true with probabilistic audit", () => {
|
|
123
74
|
expect(() =>
|
|
124
75
|
resolveAllModes({
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration mode resolution for Prisma AIRS plugin.
|
|
3
3
|
*
|
|
4
|
-
* Maps
|
|
4
|
+
* Maps tri-state mode enums to resolved modes.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export type FeatureMode = "deterministic" | "probabilistic" | "off";
|
|
@@ -17,19 +17,11 @@ export interface ResolvedModes {
|
|
|
17
17
|
|
|
18
18
|
/** Raw plugin config (from openclaw.plugin.json) */
|
|
19
19
|
export interface RawPluginConfig {
|
|
20
|
-
// New mode fields
|
|
21
20
|
reminder_mode?: string;
|
|
22
21
|
audit_mode?: string;
|
|
23
22
|
context_injection_mode?: string;
|
|
24
23
|
outbound_mode?: string;
|
|
25
24
|
tool_gating_mode?: string;
|
|
26
|
-
// Deprecated boolean fields
|
|
27
|
-
reminder_enabled?: boolean;
|
|
28
|
-
audit_enabled?: boolean;
|
|
29
|
-
context_injection_enabled?: boolean;
|
|
30
|
-
outbound_scanning_enabled?: boolean;
|
|
31
|
-
tool_gating_enabled?: boolean;
|
|
32
|
-
// Other config
|
|
33
25
|
fail_closed?: boolean;
|
|
34
26
|
[key: string]: unknown;
|
|
35
27
|
}
|
|
@@ -38,36 +30,26 @@ const VALID_FEATURE_MODES: FeatureMode[] = ["deterministic", "probabilistic", "o
|
|
|
38
30
|
const VALID_REMINDER_MODES: ReminderMode[] = ["on", "off"];
|
|
39
31
|
|
|
40
32
|
/**
|
|
41
|
-
* Resolve a single feature mode from
|
|
42
|
-
* New mode field takes precedence when both are set.
|
|
33
|
+
* Resolve a single feature mode from mode string.
|
|
43
34
|
*/
|
|
44
35
|
export function resolveMode(
|
|
45
36
|
modeValue: string | undefined,
|
|
46
|
-
enabledValue: boolean | undefined,
|
|
47
37
|
defaultMode: FeatureMode = "deterministic"
|
|
48
38
|
): FeatureMode {
|
|
49
|
-
// New mode field takes precedence
|
|
50
39
|
if (modeValue !== undefined) {
|
|
51
40
|
if (VALID_FEATURE_MODES.includes(modeValue as FeatureMode)) {
|
|
52
41
|
return modeValue as FeatureMode;
|
|
53
42
|
}
|
|
54
|
-
// Invalid value → fall through to boolean/default
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Deprecated boolean fallback
|
|
58
|
-
if (enabledValue !== undefined) {
|
|
59
|
-
return enabledValue ? "deterministic" : "off";
|
|
60
43
|
}
|
|
61
44
|
|
|
62
45
|
return defaultMode;
|
|
63
46
|
}
|
|
64
47
|
|
|
65
48
|
/**
|
|
66
|
-
* Resolve reminder mode from
|
|
49
|
+
* Resolve reminder mode from mode string.
|
|
67
50
|
*/
|
|
68
51
|
export function resolveReminderMode(
|
|
69
52
|
modeValue: string | undefined,
|
|
70
|
-
enabledValue: boolean | undefined,
|
|
71
53
|
defaultMode: ReminderMode = "on"
|
|
72
54
|
): ReminderMode {
|
|
73
55
|
if (modeValue !== undefined) {
|
|
@@ -76,10 +58,6 @@ export function resolveReminderMode(
|
|
|
76
58
|
}
|
|
77
59
|
}
|
|
78
60
|
|
|
79
|
-
if (enabledValue !== undefined) {
|
|
80
|
-
return enabledValue ? "on" : "off";
|
|
81
|
-
}
|
|
82
|
-
|
|
83
61
|
return defaultMode;
|
|
84
62
|
}
|
|
85
63
|
|
|
@@ -89,11 +67,11 @@ export function resolveReminderMode(
|
|
|
89
67
|
*/
|
|
90
68
|
export function resolveAllModes(config: RawPluginConfig): ResolvedModes {
|
|
91
69
|
const modes: ResolvedModes = {
|
|
92
|
-
reminder: resolveReminderMode(config.reminder_mode
|
|
93
|
-
audit: resolveMode(config.audit_mode
|
|
94
|
-
context: resolveMode(config.context_injection_mode
|
|
95
|
-
outbound: resolveMode(config.outbound_mode
|
|
96
|
-
toolGating: resolveMode(config.tool_gating_mode
|
|
70
|
+
reminder: resolveReminderMode(config.reminder_mode),
|
|
71
|
+
audit: resolveMode(config.audit_mode),
|
|
72
|
+
context: resolveMode(config.context_injection_mode),
|
|
73
|
+
outbound: resolveMode(config.outbound_mode),
|
|
74
|
+
toolGating: resolveMode(config.tool_gating_mode),
|
|
97
75
|
};
|
|
98
76
|
|
|
99
77
|
// Validate: fail_closed + probabilistic is not allowed
|