@cdot65/prisma-airs 0.2.5 → 0.3.0-alpha.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/index.ts CHANGED
@@ -6,29 +6,41 @@
6
6
  *
7
7
  * Provides:
8
8
  * - Gateway RPC method: prisma-airs.scan
9
- * - Agent tool: prisma_airs_scan
10
- * - Bootstrap hook: prisma-airs-guard (reminds agent about scanning)
9
+ * - Agent tool: prisma_airs_scan (always registered)
10
+ * - Probabilistic tools: prisma_airs_scan_prompt, prisma_airs_scan_response, prisma_airs_check_tool_safety
11
+ * - Bootstrap hook: prisma-airs-guard (mode-aware reminder)
12
+ * - Deterministic hooks: audit, context, outbound, tools (conditional)
11
13
  */
12
14
 
13
15
  import { scan, isConfigured, ScanRequest } from "./src/scanner";
14
- import guardHandler from "./hooks/prisma-airs-guard/handler";
16
+ import { resolveAllModes, type RawPluginConfig, type ResolvedModes } from "./src/config";
17
+ import { buildReminder } from "./hooks/prisma-airs-guard/handler";
15
18
  import auditHandler from "./hooks/prisma-airs-audit/handler";
16
19
  import contextHandler from "./hooks/prisma-airs-context/handler";
17
20
  import outboundHandler from "./hooks/prisma-airs-outbound/handler";
18
21
  import toolsHandler from "./hooks/prisma-airs-tools/handler";
22
+ import {
23
+ maskSensitiveData,
24
+ shouldMaskOnly,
25
+ buildBlockMessage,
26
+ } from "./hooks/prisma-airs-outbound/handler";
27
+ import { shouldBlockTool, DEFAULT_HIGH_RISK_TOOLS } from "./hooks/prisma-airs-tools/handler";
28
+ import { getCachedScanResult, cacheScanResult, hashMessage } from "./src/scan-cache";
19
29
 
20
30
  // Plugin config interface
21
- interface PrismaAirsConfig {
31
+ interface PrismaAirsConfig extends RawPluginConfig {
22
32
  profile_name?: string;
23
33
  app_name?: string;
24
34
  api_key?: string;
25
- reminder_enabled?: boolean;
35
+ high_risk_tools?: string[];
36
+ dlp_mask_only?: boolean;
26
37
  }
27
38
 
28
39
  // Tool parameter schema
29
40
  interface ToolParameterProperty {
30
41
  type: string;
31
42
  description: string;
43
+ items?: { type: string };
32
44
  }
33
45
 
34
46
  interface ToolParameters {
@@ -73,7 +85,7 @@ interface PluginApi {
73
85
  name: string;
74
86
  description: string;
75
87
  parameters: ToolParameters;
76
- execute: (_id: string, params: ScanRequest) => Promise<ToolResult>;
88
+ execute: (_id: string, params: Record<string, unknown>) => Promise<ToolResult>;
77
89
  }) => void;
78
90
  registerCli: (setup: (ctx: { program: unknown }) => void, opts: { commands: string[] }) => void;
79
91
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -100,70 +112,306 @@ function buildScanRequest(params: ScanRequest | undefined, config: PrismaAirsCon
100
112
  };
101
113
  }
102
114
 
115
+ /**
116
+ * Build a text tool result
117
+ */
118
+ function textResult(data: unknown): ToolResult {
119
+ return {
120
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
121
+ };
122
+ }
123
+
103
124
  // Register the plugin
104
125
  export default function register(api: PluginApi): void {
105
126
  const config = getPluginConfig(api);
127
+
128
+ // Resolve modes (may throw on invalid fail_closed + probabilistic combo)
129
+ let modes: ResolvedModes;
130
+ try {
131
+ modes = resolveAllModes(config);
132
+ } catch (err) {
133
+ api.logger.error(
134
+ `Prisma AIRS config error: ${err instanceof Error ? err.message : String(err)}`
135
+ );
136
+ throw err;
137
+ }
138
+
106
139
  api.logger.info(
107
- `Prisma AIRS plugin loaded (reminder_enabled=${config.reminder_enabled ?? true})`
140
+ `Prisma AIRS plugin loaded (audit=${modes.audit}, context=${modes.context}, outbound=${modes.outbound}, toolGating=${modes.toolGating}, reminder=${modes.reminder})`
108
141
  );
109
142
 
110
- // Register lifecycle hooks via api.on()
111
- // Each adapter injects api.config as cfg for handler compatibility.
112
-
113
- // Guard: inject security scanning reminder at agent bootstrap
114
- api.on(
115
- "before_agent_start",
116
- async () => {
117
- const files: { path: string; content: string; source?: string }[] = [];
118
- await guardHandler({
119
- type: "agent",
120
- action: "bootstrap",
121
- context: { bootstrapFiles: files, cfg: api.config },
122
- });
123
- if (files.length > 0) {
124
- return { systemPrompt: files.map((f) => f.content).join("\n\n") };
125
- }
126
- return undefined;
127
- },
128
- { priority: 100 }
129
- );
143
+ // ── DETERMINISTIC HOOKS ──────────────────────────────────────────────
144
+
145
+ // Guard: inject mode-aware security reminder at agent bootstrap
146
+ if (modes.reminder === "on") {
147
+ api.on(
148
+ "before_agent_start",
149
+ async () => {
150
+ const reminderText = buildReminder(modes);
151
+ return { systemPrompt: reminderText };
152
+ },
153
+ { priority: 100 }
154
+ );
155
+ }
130
156
 
131
157
  // Audit: fire-and-forget inbound message scan logging
132
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
- api.on("message_received", async (event: any, ctx: any) => {
134
- await auditHandler(event, { ...ctx, cfg: api.config });
135
- });
158
+ if (modes.audit === "deterministic") {
159
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
160
+ api.on("message_received", async (event: any, ctx: any) => {
161
+ await auditHandler(event, { ...ctx, cfg: api.config });
162
+ });
163
+ }
136
164
 
137
165
  // Context: inject security warnings before agent processes message
138
- api.on(
139
- "before_agent_start",
140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
- async (event: any, ctx: any) => {
142
- return await contextHandler(
143
- {
144
- sessionKey: ctx.sessionKey,
145
- message: { content: event.prompt },
146
- messages: event.messages,
147
- },
148
- { ...ctx, cfg: api.config }
149
- );
150
- },
151
- { priority: 50 }
152
- );
166
+ if (modes.context === "deterministic") {
167
+ api.on(
168
+ "before_agent_start",
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
+ async (event: any, ctx: any) => {
171
+ return await contextHandler(
172
+ {
173
+ sessionKey: ctx.sessionKey,
174
+ message: { content: event.prompt },
175
+ messages: event.messages,
176
+ },
177
+ { ...ctx, cfg: api.config }
178
+ );
179
+ },
180
+ { priority: 50 }
181
+ );
182
+ }
153
183
 
154
184
  // Outbound: scan and block/mask outgoing responses
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
- api.on("message_sending", async (event: any, ctx: any) => {
157
- return await outboundHandler(event, { ...ctx, cfg: api.config });
158
- });
185
+ if (modes.outbound === "deterministic") {
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ api.on("message_sending", async (event: any, ctx: any) => {
188
+ return await outboundHandler(event, { ...ctx, cfg: api.config });
189
+ });
190
+ }
159
191
 
160
192
  // Tools: block dangerous tool calls during active threats
161
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
- api.on("before_tool_call", async (event: any, ctx: any) => {
163
- return await toolsHandler(event, { ...ctx, cfg: api.config });
164
- });
193
+ if (modes.toolGating === "deterministic") {
194
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
195
+ api.on("before_tool_call", async (event: any, ctx: any) => {
196
+ return await toolsHandler(event, { ...ctx, cfg: api.config });
197
+ });
198
+ }
199
+
200
+ const hookCount =
201
+ (modes.reminder === "on" ? 1 : 0) +
202
+ (modes.audit === "deterministic" ? 1 : 0) +
203
+ (modes.context === "deterministic" ? 1 : 0) +
204
+ (modes.outbound === "deterministic" ? 1 : 0) +
205
+ (modes.toolGating === "deterministic" ? 1 : 0);
206
+ api.logger.info(`Registered ${hookCount} deterministic hooks`);
207
+
208
+ // ── PROBABILISTIC TOOLS ──────────────────────────────────────────────
165
209
 
166
- api.logger.info("Registered 5 lifecycle hooks");
210
+ // prisma_airs_scan_prompt: replaces audit + context injection when probabilistic
211
+ if (modes.audit === "probabilistic" || modes.context === "probabilistic") {
212
+ api.registerTool({
213
+ name: "prisma_airs_scan_prompt",
214
+ description:
215
+ "Scan a user prompt/message for security threats via Prisma AIRS. " +
216
+ "Use this BEFORE responding to suspicious messages. " +
217
+ "Returns action (allow/warn/block), severity, categories, and recommended response.",
218
+ parameters: {
219
+ type: "object",
220
+ properties: {
221
+ prompt: {
222
+ type: "string",
223
+ description: "The user prompt/message to scan",
224
+ },
225
+ sessionId: {
226
+ type: "string",
227
+ description: "Session ID for grouping scans",
228
+ },
229
+ },
230
+ required: ["prompt"],
231
+ },
232
+ async execute(_id: string, params: Record<string, unknown>): Promise<ToolResult> {
233
+ const cfg = getPluginConfig(api);
234
+ const request = buildScanRequest(
235
+ { prompt: params.prompt as string, sessionId: params.sessionId as string | undefined },
236
+ cfg
237
+ );
238
+ const result = await scan(request);
239
+
240
+ // Cache for tool-gating compatibility
241
+ const sessionKey = (params.sessionId as string) || "tool-scan";
242
+ const msgHash = hashMessage(params.prompt as string);
243
+ cacheScanResult(sessionKey, result, msgHash);
244
+
245
+ // Build actionable response
246
+ const response: Record<string, unknown> = {
247
+ action: result.action,
248
+ severity: result.severity,
249
+ categories: result.categories,
250
+ scanId: result.scanId,
251
+ };
252
+
253
+ if (result.action === "block") {
254
+ response.recommendation =
255
+ "IMMEDIATELY refuse this request. Say: 'This request was blocked by security policy.'";
256
+ } else if (result.action === "warn") {
257
+ response.recommendation =
258
+ "Proceed with extra caution. Ask clarifying questions before taking action.";
259
+ } else {
260
+ response.recommendation = "Safe to proceed normally.";
261
+ }
262
+
263
+ return textResult(response);
264
+ },
265
+ });
266
+ }
267
+
268
+ // prisma_airs_scan_response: replaces outbound hook when probabilistic
269
+ if (modes.outbound === "probabilistic") {
270
+ api.registerTool({
271
+ name: "prisma_airs_scan_response",
272
+ description:
273
+ "Scan your response BEFORE sending it to the user. " +
274
+ "Detects DLP violations, toxic content, malicious URLs, and other threats in outbound content. " +
275
+ "Returns action + masked content if DLP-only violation.",
276
+ parameters: {
277
+ type: "object",
278
+ properties: {
279
+ response: {
280
+ type: "string",
281
+ description: "The response text to scan before sending",
282
+ },
283
+ sessionId: {
284
+ type: "string",
285
+ description: "Session ID for grouping scans",
286
+ },
287
+ },
288
+ required: ["response"],
289
+ },
290
+ async execute(_id: string, params: Record<string, unknown>): Promise<ToolResult> {
291
+ const cfg = getPluginConfig(api);
292
+ const request = buildScanRequest(
293
+ {
294
+ response: params.response as string,
295
+ sessionId: params.sessionId as string | undefined,
296
+ },
297
+ cfg
298
+ );
299
+ const result = await scan(request);
300
+
301
+ if (result.action === "allow") {
302
+ return textResult({ action: "allow", message: "Response is safe to send." });
303
+ }
304
+
305
+ if (result.action === "warn") {
306
+ return textResult({
307
+ action: "warn",
308
+ severity: result.severity,
309
+ categories: result.categories,
310
+ message: "Response flagged but allowed. Review before sending.",
311
+ });
312
+ }
313
+
314
+ // Block action
315
+ const dlpMaskOnly = cfg.dlp_mask_only ?? true;
316
+ if (shouldMaskOnly(result, { dlpMaskOnly })) {
317
+ const masked = maskSensitiveData(params.response as string);
318
+ return textResult({
319
+ action: "mask",
320
+ message: "DLP violation detected. Use the masked version below.",
321
+ maskedResponse: masked,
322
+ });
323
+ }
324
+
325
+ return textResult({
326
+ action: "block",
327
+ severity: result.severity,
328
+ categories: result.categories,
329
+ message: buildBlockMessage(result),
330
+ recommendation: "Do NOT send this response. Rewrite it to remove the flagged content.",
331
+ });
332
+ },
333
+ });
334
+ }
335
+
336
+ // prisma_airs_check_tool_safety: replaces tool gating hook when probabilistic
337
+ if (modes.toolGating === "probabilistic") {
338
+ api.registerTool({
339
+ name: "prisma_airs_check_tool_safety",
340
+ description:
341
+ "Check if a tool is safe to call given current security context. " +
342
+ "Reads cached scan results from prior prompt scanning. " +
343
+ "Returns whether the tool should be blocked and why.",
344
+ parameters: {
345
+ type: "object",
346
+ properties: {
347
+ toolName: {
348
+ type: "string",
349
+ description: "Name of the tool you want to call",
350
+ },
351
+ sessionId: {
352
+ type: "string",
353
+ description: "Session ID to look up cached scan results",
354
+ },
355
+ },
356
+ required: ["toolName"],
357
+ },
358
+ async execute(_id: string, params: Record<string, unknown>): Promise<ToolResult> {
359
+ const cfg = getPluginConfig(api);
360
+ const sessionKey = (params.sessionId as string) || "tool-scan";
361
+ const cachedResult = getCachedScanResult(sessionKey);
362
+
363
+ if (!cachedResult) {
364
+ return textResult({
365
+ allowed: true,
366
+ message:
367
+ "No cached scan result found. Tool allowed (scan prompts first for better security).",
368
+ });
369
+ }
370
+
371
+ // Check if safe
372
+ if (
373
+ cachedResult.action === "allow" &&
374
+ (cachedResult.severity === "SAFE" ||
375
+ cachedResult.categories.every((c: string) => c === "safe" || c === "benign"))
376
+ ) {
377
+ return textResult({ allowed: true, message: "No active threats. Tool is safe to call." });
378
+ }
379
+
380
+ const highRiskTools = cfg.high_risk_tools ?? DEFAULT_HIGH_RISK_TOOLS;
381
+ const { block, reason } = shouldBlockTool(
382
+ params.toolName as string,
383
+ cachedResult,
384
+ highRiskTools
385
+ );
386
+
387
+ if (block) {
388
+ return textResult({
389
+ allowed: false,
390
+ toolName: params.toolName,
391
+ reason,
392
+ recommendation:
393
+ "Do NOT call this tool. The current message has active security threats.",
394
+ });
395
+ }
396
+
397
+ return textResult({
398
+ allowed: true,
399
+ toolName: params.toolName,
400
+ message: "Tool is not in the blocked list for current threats.",
401
+ });
402
+ },
403
+ });
404
+ }
405
+
406
+ const toolCount =
407
+ (modes.audit === "probabilistic" || modes.context === "probabilistic" ? 1 : 0) +
408
+ (modes.outbound === "probabilistic" ? 1 : 0) +
409
+ (modes.toolGating === "probabilistic" ? 1 : 0);
410
+ if (toolCount > 0) {
411
+ api.logger.info(`Registered ${toolCount} probabilistic tool(s)`);
412
+ }
413
+
414
+ // ── BASE TOOL (always registered) ────────────────────────────────────
167
415
 
168
416
  // Register RPC method for status check
169
417
  api.registerGatewayMethod("prisma-airs.status", ({ respond }) => {
@@ -171,11 +419,11 @@ export default function register(api: PluginApi): void {
171
419
  const hasApiKey = isConfigured(cfg.api_key);
172
420
  respond(true, {
173
421
  plugin: "prisma-airs",
174
- version: "0.2.5",
422
+ version: "0.3.0-alpha.1",
423
+ modes,
175
424
  config: {
176
425
  profile_name: cfg.profile_name ?? "default",
177
426
  app_name: cfg.app_name ?? "openclaw",
178
- reminder_enabled: cfg.reminder_enabled ?? true,
179
427
  },
180
428
  api_key_set: hasApiKey,
181
429
  status: hasApiKey ? "ready" : "missing_api_key",
@@ -209,7 +457,7 @@ export default function register(api: PluginApi): void {
209
457
  })();
210
458
  });
211
459
 
212
- // Register agent tool for scanning
460
+ // Register agent tool for scanning (always available as manual escape hatch)
213
461
  api.registerTool({
214
462
  name: "prisma_airs_scan",
215
463
  description:
@@ -239,20 +487,12 @@ export default function register(api: PluginApi): void {
239
487
  },
240
488
  required: ["prompt"],
241
489
  },
242
- async execute(_id: string, params: ScanRequest): Promise<ToolResult> {
490
+ async execute(_id: string, params: Record<string, unknown>): Promise<ToolResult> {
243
491
  const cfg = getPluginConfig(api);
244
- const request = buildScanRequest(params, cfg);
492
+ const request = buildScanRequest(params as ScanRequest, cfg);
245
493
  const result = await scan(request);
246
494
 
247
- // Return in OpenClaw tool result format (v2026.2.1+)
248
- return {
249
- content: [
250
- {
251
- type: "text",
252
- text: JSON.stringify(result, null, 2),
253
- },
254
- ],
255
- };
495
+ return textResult(result);
256
496
  },
257
497
  });
258
498
 
@@ -271,10 +511,15 @@ export default function register(api: PluginApi): void {
271
511
  const hasKey = isConfigured(cfg.api_key);
272
512
  console.log("Prisma AIRS Plugin Status");
273
513
  console.log("-------------------------");
274
- console.log(`Version: 0.2.5`);
514
+ console.log(`Version: 0.3.0-alpha.1`);
275
515
  console.log(`Profile: ${cfg.profile_name ?? "default"}`);
276
516
  console.log(`App Name: ${cfg.app_name ?? "openclaw"}`);
277
- console.log(`Reminder: ${cfg.reminder_enabled ?? true}`);
517
+ console.log(`Modes:`);
518
+ console.log(` Reminder: ${modes.reminder}`);
519
+ console.log(` Audit: ${modes.audit}`);
520
+ console.log(` Context: ${modes.context}`);
521
+ console.log(` Outbound: ${modes.outbound}`);
522
+ console.log(` Tool Gating: ${modes.toolGating}`);
278
523
  console.log(`API Key: ${hasKey ? "configured" : "MISSING"}`);
279
524
  if (!hasKey) {
280
525
  console.log("\nSet API key in plugin config");
@@ -322,8 +567,12 @@ export default function register(api: PluginApi): void {
322
567
  // Export plugin metadata for discovery
323
568
  export const id = "prisma-airs";
324
569
  export const name = "Prisma AIRS Security";
325
- export const version = "0.2.5";
570
+ export const version = "0.3.0-alpha.1";
326
571
 
327
572
  // Re-export scanner types and functions
328
573
  export { scan, isConfigured } from "./src/scanner";
329
574
  export type { ScanRequest, ScanResult } from "./src/scanner";
575
+
576
+ // Re-export config types
577
+ export { resolveAllModes, resolveMode, resolveReminderMode } from "./src/config";
578
+ export type { FeatureMode, ReminderMode, ResolvedModes, RawPluginConfig } from "./src/config";
@@ -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.2.5",
5
+ "version": "0.3.0-alpha.1",
6
6
  "entrypoint": "index.ts",
7
7
  "hooks": [
8
8
  "hooks/prisma-airs-guard",
@@ -25,30 +25,35 @@
25
25
  "default": "openclaw",
26
26
  "description": "Application name for scan metadata"
27
27
  },
28
- "reminder_enabled": {
29
- "type": "boolean",
30
- "default": true,
28
+ "reminder_mode": {
29
+ "type": "string",
30
+ "enum": ["on", "off"],
31
+ "default": "on",
31
32
  "description": "Inject security scanning reminder on agent bootstrap"
32
33
  },
33
- "audit_enabled": {
34
- "type": "boolean",
35
- "default": true,
36
- "description": "Enable audit logging of all inbound messages"
34
+ "audit_mode": {
35
+ "type": "string",
36
+ "enum": ["deterministic", "probabilistic", "off"],
37
+ "default": "deterministic",
38
+ "description": "Audit logging mode: deterministic (hook, always scan), probabilistic (tool, model decides), or off"
37
39
  },
38
- "context_injection_enabled": {
39
- "type": "boolean",
40
- "default": true,
41
- "description": "Inject security warnings into agent context"
40
+ "context_injection_mode": {
41
+ "type": "string",
42
+ "enum": ["deterministic", "probabilistic", "off"],
43
+ "default": "deterministic",
44
+ "description": "Context injection mode: deterministic (hook, always inject), probabilistic (tool, model decides), or off"
42
45
  },
43
- "outbound_scanning_enabled": {
44
- "type": "boolean",
45
- "default": true,
46
- "description": "Enable scanning and blocking of outbound responses"
46
+ "outbound_mode": {
47
+ "type": "string",
48
+ "enum": ["deterministic", "probabilistic", "off"],
49
+ "default": "deterministic",
50
+ "description": "Outbound scanning mode: deterministic (hook, always scan), probabilistic (tool, model decides), or off"
47
51
  },
48
- "tool_gating_enabled": {
49
- "type": "boolean",
50
- "default": true,
51
- "description": "Block dangerous tools when threats are detected"
52
+ "tool_gating_mode": {
53
+ "type": "string",
54
+ "enum": ["deterministic", "probabilistic", "off"],
55
+ "default": "deterministic",
56
+ "description": "Tool gating mode: deterministic (hook, always gate), probabilistic (tool, model decides), or off"
52
57
  },
53
58
  "fail_closed": {
54
59
  "type": "boolean",
@@ -97,24 +102,25 @@
97
102
  "label": "Application Name",
98
103
  "placeholder": "openclaw"
99
104
  },
100
- "reminder_enabled": {
101
- "label": "Enable Bootstrap Reminder"
105
+ "reminder_mode": {
106
+ "label": "Reminder Mode",
107
+ "description": "on: inject scanning reminder at agent bootstrap; off: no reminder"
102
108
  },
103
- "audit_enabled": {
104
- "label": "Enable Audit Logging",
105
- "description": "Log all inbound messages with scan results"
109
+ "audit_mode": {
110
+ "label": "Audit Mode",
111
+ "description": "deterministic: scan every inbound message via hook; probabilistic: model decides when to scan via tool; off: disabled"
106
112
  },
107
- "context_injection_enabled": {
108
- "label": "Enable Context Injection",
109
- "description": "Inject security warnings into agent context"
113
+ "context_injection_mode": {
114
+ "label": "Context Injection Mode",
115
+ "description": "deterministic: always inject security context via hook; probabilistic: model decides when to scan via tool; off: disabled"
110
116
  },
111
- "outbound_scanning_enabled": {
112
- "label": "Enable Outbound Scanning",
113
- "description": "Scan and block/mask outbound responses"
117
+ "outbound_mode": {
118
+ "label": "Outbound Scanning Mode",
119
+ "description": "deterministic: scan every outbound response via hook; probabilistic: model decides when to scan via tool; off: disabled"
114
120
  },
115
- "tool_gating_enabled": {
116
- "label": "Enable Tool Gating",
117
- "description": "Block dangerous tools during active threats"
121
+ "tool_gating_mode": {
122
+ "label": "Tool Gating Mode",
123
+ "description": "deterministic: always gate tool calls via hook; probabilistic: model checks tool safety via tool; off: disabled"
118
124
  },
119
125
  "fail_closed": {
120
126
  "label": "Fail Closed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdot65/prisma-airs",
3
- "version": "0.2.5",
3
+ "version": "0.3.0-alpha.1",
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",