citadel0 1.0.0 → 1.0.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/dist/index.d.ts CHANGED
@@ -1,20 +1,5 @@
1
1
  type Decision = 'ALLOW' | 'BLOCK' | 'REVIEW';
2
2
  type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
3
- type PolicyEffect = 'ALLOW' | 'BLOCK' | 'REVIEW';
4
- interface SecurityContext {
5
- tenantId: string;
6
- agentId: string;
7
- sessionId: string;
8
- environment: 'production' | 'staging' | 'development';
9
- clientIp?: string;
10
- }
11
- interface ActionPayload {
12
- context: SecurityContext;
13
- action: string;
14
- params: Record<string, unknown>;
15
- timestamp: number;
16
- metadata?: Record<string, unknown>;
17
- }
18
3
  type PolicyConditionOperator = 'EQUALS' | 'NOT_EQUALS' | 'GREATER_THAN' | 'LESS_THAN' | 'IN' | 'NOT_IN' | 'CONTAINS' | 'REGEX_MATCH';
19
4
  interface PolicyRule {
20
5
  field: string;
@@ -23,20 +8,28 @@ interface PolicyRule {
23
8
  }
24
9
  interface SecurityPolicy {
25
10
  id: string;
26
- tenantId: string;
27
11
  name: string;
28
- description?: string;
29
12
  actionPattern: string;
30
- rules: PolicyRule[];
31
- effect: PolicyEffect;
13
+ effect: Decision;
32
14
  priority: number;
33
15
  isActive: boolean;
16
+ rules: PolicyRule[];
17
+ }
18
+ interface ActionPayload {
19
+ action: string;
20
+ params: Record<string, unknown>;
21
+ context?: {
22
+ tenantId?: string;
23
+ sessionId?: string;
24
+ environment?: string;
25
+ };
26
+ metadata?: Record<string, unknown>;
34
27
  }
35
28
  interface PolicyEvaluationTrace {
36
29
  policyId: string;
37
30
  policyName: string;
38
31
  matched: boolean;
39
- effect: PolicyEffect;
32
+ effect: Decision;
40
33
  }
41
34
  interface SecurityVerdict {
42
35
  verdictId: string;
@@ -44,108 +37,58 @@ interface SecurityVerdict {
44
37
  reason: string;
45
38
  riskScore: number;
46
39
  riskLevel: RiskLevel;
47
- evaluationPipeline: 'DETERMINISTIC' | 'AI_GUARD' | 'HYBRID';
48
40
  matchedPolicies: PolicyEvaluationTrace[];
49
41
  executedAt: number;
50
42
  latencyMs: number;
51
43
  }
52
- interface AuditLogEntry {
53
- id: string;
54
- tenantId: string;
55
- agentId: string;
56
- sessionId: string;
44
+ interface TelemetryTrace {
45
+ traceId: string;
57
46
  action: string;
58
47
  params: Record<string, unknown>;
59
48
  verdict: SecurityVerdict;
60
- reviewedBy?: string;
61
- reviewComment?: string;
62
- createdAt: number;
49
+ sessionId?: string;
50
+ environment: string;
51
+ timestamp: number;
63
52
  }
64
53
 
65
- interface CitadelClientConfig {
54
+ declare function evaluatePolicies(payload: ActionPayload, sortedPolicies: SecurityPolicy[]): SecurityVerdict;
55
+
56
+ interface Citadel_0_Config {
66
57
  baseUrl: string;
67
58
  agentToken: string;
68
59
  environment?: 'production' | 'staging' | 'development';
69
60
  defaultSessionId?: string;
61
+ policies?: SecurityPolicy[];
62
+ flushIntervalMs?: number;
63
+ batchSize?: number;
70
64
  }
71
65
  interface CheckOptions {
72
66
  sessionId?: string;
73
67
  environment?: 'production' | 'staging' | 'development';
74
68
  metadata?: Record<string, unknown>;
75
69
  }
76
- interface CheckResult {
77
- verdict: SecurityVerdict;
78
- auditLogId: string;
79
- }
80
- interface ReviewStatus {
81
- auditLogId: string;
82
- decision: 'ALLOW' | 'BLOCK' | 'REVIEW';
83
- reviewedBy?: string;
84
- reviewComment?: string;
85
- action: string;
86
- params: unknown;
87
- riskScore: number;
88
- riskLevel: string;
89
- createdAt: number;
90
- }
91
- interface GuardWithReviewOptions extends CheckOptions {
92
- timeoutMs?: number;
93
- pollIntervalMs?: number;
94
- onReviewPending?: (auditLogId: string) => void;
95
- }
96
- declare class CitadelClient {
70
+ declare class Citadel_0 {
97
71
  private readonly baseUrl;
98
72
  private readonly agentToken;
99
- private readonly environment?;
73
+ private readonly environment;
100
74
  private readonly defaultSessionId?;
101
- constructor(config: CitadelClientConfig);
102
- check(action: string, params?: Record<string, unknown>, options?: CheckOptions): Promise<CheckResult>;
103
- isAllowed(action: string, params?: Record<string, unknown>, options?: CheckOptions): Promise<boolean>;
104
- guard<T>(action: string, params: Record<string, unknown>, callback: () => Promise<T> | T, options?: CheckOptions): Promise<T>;
105
- getReviewStatus(auditLogId: string): Promise<ReviewStatus>;
106
- pollReview(auditLogId: string, options?: {
107
- timeoutMs?: number;
108
- intervalMs?: number;
109
- }): Promise<ReviewStatus>;
110
- guardWithReview<T>(action: string, params: Record<string, unknown>, callback: () => Promise<T> | T, options?: GuardWithReviewOptions): Promise<T>;
75
+ private policies;
76
+ private telemetryQueue;
77
+ private readonly batchSize;
78
+ private flushTimer;
79
+ constructor(config: Citadel_0_Config);
80
+ setPolicies(policies: SecurityPolicy[]): void;
81
+ syncPolicies(): Promise<void>;
82
+ check(action: string, params?: Record<string, unknown>, options?: CheckOptions): SecurityVerdict;
83
+ isAllowed(action: string, params?: Record<string, unknown>, options?: CheckOptions): boolean;
84
+ guard<T>(action: string, params: Record<string, unknown>, callback: () => Promise<T> | T, options?: CheckOptions): Promise<T> | T;
85
+ flush(): Promise<void>;
86
+ destroy(): void;
111
87
  }
112
88
 
113
- interface GuardedToolDefinition<TArgs = any, TResult = any> {
114
- name: string;
115
- description?: string;
116
- execute: (args: TArgs) => Promise<TResult> | TResult;
117
- options?: CheckOptions;
118
- }
119
- declare function wrapTool<TArgs extends Record<string, unknown>, TResult>(client: CitadelClient, tool: GuardedToolDefinition<TArgs, TResult>): {
120
- execute: (args: TArgs) => Promise<TResult>;
121
- name: string;
122
- description?: string;
123
- options?: CheckOptions;
124
- };
125
- declare function wrapToolWithReview<TArgs extends Record<string, unknown>, TResult>(client: CitadelClient, tool: GuardedToolDefinition<TArgs, TResult>, reviewOptions?: {
126
- timeoutMs?: number;
127
- pollIntervalMs?: number;
128
- onReviewPending?: (auditLogId: string) => void;
129
- }): {
130
- execute: (args: TArgs) => Promise<TResult>;
131
- name: string;
132
- description?: string;
133
- options?: CheckOptions;
134
- };
135
-
136
- interface ThreatDetection {
137
- type: 'PROMPT_INJECTION' | 'JAILBREAK_ATTEMPT' | 'CREDENTIAL_LEAK';
138
- confidence: number;
139
- reason: string;
140
- matchedPattern: string;
89
+ interface GuardedToolOptions extends CheckOptions {
90
+ actionName?: string;
141
91
  }
142
- interface SemanticScanResult {
143
- flagged: boolean;
144
- threats: ThreatDetection[];
145
- maxRiskScore: number;
146
- }
147
- declare function scanPayloadSemantics(params: Record<string, unknown>): SemanticScanResult;
148
-
149
- declare function evaluatePolicies(payload: ActionPayload, policies: SecurityPolicy[]): SecurityVerdict;
92
+ declare function guardTool<TArgs extends Record<string, unknown>, TResult>(client: Citadel_0, action: string, fn: (args: TArgs) => Promise<TResult> | TResult, options?: CheckOptions): (args: TArgs) => Promise<TResult>;
150
93
 
151
- export { type ActionPayload, type AuditLogEntry, type CheckOptions, type CheckResult, CitadelClient, type CitadelClientConfig, type Decision, type GuardWithReviewOptions, type GuardedToolDefinition, type PolicyConditionOperator, type PolicyEffect, type PolicyEvaluationTrace, type PolicyRule, type ReviewStatus, type RiskLevel, type SecurityContext, type SecurityPolicy, type SecurityVerdict, type SemanticScanResult, type ThreatDetection, evaluatePolicies, scanPayloadSemantics, wrapTool, wrapToolWithReview };
94
+ export { type ActionPayload, type CheckOptions, Citadel_0, type Citadel_0_Config, type Decision, type GuardedToolOptions, type PolicyConditionOperator, type PolicyEvaluationTrace, type PolicyRule, type RiskLevel, type SecurityPolicy, type SecurityVerdict, type TelemetryTrace, evaluatePolicies, guardTool };
package/dist/index.js CHANGED
@@ -1,226 +1,13 @@
1
- // src/sdk/client.ts
2
- var CitadelClient = class {
3
- baseUrl;
4
- agentToken;
5
- environment;
6
- defaultSessionId;
7
- constructor(config) {
8
- this.baseUrl = config.baseUrl.replace(/\/+$/, "");
9
- this.agentToken = config.agentToken;
10
- this.environment = config.environment;
11
- this.defaultSessionId = config.defaultSessionId;
12
- }
13
- async check(action, params = {}, options = {}) {
14
- const url = `${this.baseUrl}/api/v1/check`;
15
- const sessionId = options.sessionId ?? this.defaultSessionId ?? `sess_${Math.random().toString(36).slice(2, 11)}`;
16
- const response = await fetch(url, {
17
- method: "POST",
18
- headers: {
19
- "Content-Type": "application/json",
20
- Authorization: `Bearer ${this.agentToken}`
21
- },
22
- body: JSON.stringify({
23
- action,
24
- params,
25
- sessionId,
26
- environment: options.environment ?? this.environment,
27
- metadata: options.metadata
28
- })
29
- });
30
- if (!response.ok) {
31
- const errorBody = await response.json().catch(() => ({}));
32
- const errorMessage = typeof errorBody.error === "string" ? errorBody.error : `Citadel gateway error: ${response.status} ${response.statusText}`;
33
- throw new Error(errorMessage);
34
- }
35
- return await response.json();
36
- }
37
- async isAllowed(action, params = {}, options = {}) {
38
- const result = await this.check(action, params, options);
39
- return result.verdict.decision === "ALLOW";
40
- }
41
- async guard(action, params, callback, options = {}) {
42
- const result = await this.check(action, params, options);
43
- if (result.verdict.decision !== "ALLOW") {
44
- throw new Error(
45
- `Action '${action}' blocked by Citadel: ${result.verdict.reason}`
46
- );
47
- }
48
- return await callback();
49
- }
50
- async getReviewStatus(auditLogId) {
51
- const url = `${this.baseUrl}/api/v1/review/status?auditLogId=${encodeURIComponent(
52
- auditLogId
53
- )}`;
54
- const response = await fetch(url);
55
- if (!response.ok) {
56
- const errorBody = await response.json().catch(() => ({}));
57
- const errorMessage = typeof errorBody.error === "string" ? errorBody.error : `Review status error: ${response.status}`;
58
- throw new Error(errorMessage);
59
- }
60
- return await response.json();
61
- }
62
- async pollReview(auditLogId, options = {}) {
63
- const timeoutMs = options.timeoutMs ?? 6e4;
64
- const intervalMs = options.intervalMs ?? 2e3;
65
- const deadline = Date.now() + timeoutMs;
66
- while (Date.now() < deadline) {
67
- const status = await this.getReviewStatus(auditLogId);
68
- if (status.decision !== "REVIEW") {
69
- return status;
70
- }
71
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
72
- }
73
- throw new Error(
74
- `Review timed out after ${timeoutMs}ms for auditLogId: ${auditLogId}`
75
- );
76
- }
77
- async guardWithReview(action, params, callback, options = {}) {
78
- const result = await this.check(action, params, options);
79
- if (result.verdict.decision === "ALLOW") {
80
- return await callback();
81
- }
82
- if (result.verdict.decision === "BLOCK") {
83
- throw new Error(
84
- `Action '${action}' blocked by Citadel: ${result.verdict.reason}`
85
- );
86
- }
87
- if (options.onReviewPending) {
88
- options.onReviewPending(result.auditLogId);
89
- }
90
- const reviewed = await this.pollReview(result.auditLogId, {
91
- timeoutMs: options.timeoutMs,
92
- intervalMs: options.pollIntervalMs
93
- });
94
- if (reviewed.decision === "ALLOW") {
95
- return await callback();
96
- }
97
- throw new Error(
98
- `Action '${action}' rejected during human review: ${reviewed.reviewComment ?? "Rejected"}`
99
- );
100
- }
101
- };
102
-
103
- // src/sdk/tools.ts
104
- function wrapTool(client, tool) {
105
- return {
106
- ...tool,
107
- execute: async (args) => {
108
- return await client.guard(
109
- tool.name,
110
- args,
111
- () => tool.execute(args),
112
- tool.options
113
- );
114
- }
115
- };
116
- }
117
- function wrapToolWithReview(client, tool, reviewOptions) {
118
- return {
119
- ...tool,
120
- execute: async (args) => {
121
- return await client.guardWithReview(
122
- tool.name,
123
- args,
124
- () => tool.execute(args),
125
- {
126
- ...tool.options,
127
- ...reviewOptions
128
- }
129
- );
130
- }
131
- };
132
- }
133
-
134
- // src/engine/semantic.ts
135
- var INJECTION_PATTERNS = [
136
- {
137
- regex: /(?:ignore|disregard|forget|bypass|override)\s+(?:all\s+)?(?:previous|prior|existing|above)\s+(?:instructions|rules|guidelines|directions|constraints)/i,
138
- description: "Direct prompt instruction override",
139
- score: 95
140
- },
141
- {
142
- regex: /(?:you\s+are\s+now|act\s+as|roleplay\s+as)\s+(?:an?\s+)?(?:unfiltered|unrestricted|jailbroken|evil|dan|developer\s+mode)/i,
143
- description: "Adversarial persona / DAN jailbreak attempt",
144
- score: 90
145
- },
146
- {
147
- regex: /(?:reveal|output|print|display|leak)\s+(?:the\s+)?(?:system\s+prompt|initial\s+prompt|core\s+instructions|developer\s+instructions)/i,
148
- description: "System prompt extraction attempt",
149
- score: 85
150
- },
151
- {
152
- regex: /(?:\[INST\]|\[\/INST\]|<<SYS>>|<\/SYS>|<\|im_start\|>|<\|im_end\|>)/i,
153
- description: "Model token delimiter injection",
154
- score: 90
155
- }
156
- ];
157
- var CREDENTIAL_PATTERNS = [
158
- {
159
- regex: /(?:sk-[a-zA-Z0-9-_]{20,}|ghp_[a-zA-Z0-9]{36}|gho_[a-zA-Z0-9]{36})/i,
160
- description: "Exposed API token (OpenAI / GitHub)",
161
- score: 95
162
- },
163
- {
164
- regex: /AKIA[0-9A-Z]{16}/,
165
- description: "AWS Access Key ID",
166
- score: 90
167
- },
168
- {
169
- regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
170
- description: "Exposed Private Key block",
171
- score: 99
172
- }
173
- ];
174
- function extractStrings(obj) {
175
- const strings = [];
176
- function recurse(value) {
177
- if (typeof value === "string") {
178
- strings.push(value);
179
- } else if (Array.isArray(value)) {
180
- for (const item of value) recurse(item);
181
- } else if (value !== null && typeof value === "object") {
182
- for (const val of Object.values(value)) {
183
- recurse(val);
184
- }
185
- }
186
- }
187
- recurse(obj);
188
- return strings;
189
- }
190
- function scanPayloadSemantics(params) {
191
- const stringsToScan = extractStrings(params);
192
- const threats = [];
193
- for (const text of stringsToScan) {
194
- for (const rule of INJECTION_PATTERNS) {
195
- if (rule.regex.test(text)) {
196
- threats.push({
197
- type: "PROMPT_INJECTION",
198
- confidence: rule.score / 100,
199
- reason: rule.description,
200
- matchedPattern: rule.regex.source
201
- });
202
- }
203
- }
204
- for (const rule of CREDENTIAL_PATTERNS) {
205
- if (rule.regex.test(text)) {
206
- threats.push({
207
- type: "CREDENTIAL_LEAK",
208
- confidence: rule.score / 100,
209
- reason: rule.description,
210
- matchedPattern: rule.regex.source
211
- });
212
- }
213
- }
214
- }
215
- const maxScore = threats.length > 0 ? Math.max(...threats.map((t) => t.confidence * 100)) : 0;
216
- return {
217
- flagged: threats.length > 0,
218
- threats,
219
- maxRiskScore: Math.round(maxScore)
220
- };
221
- }
222
-
223
1
  // src/engine/match.ts
2
+ var regexCache = /* @__PURE__ */ new Map();
3
+ function getCompiledRegex(pattern) {
4
+ let re = regexCache.get(pattern);
5
+ if (!re) {
6
+ re = new RegExp(pattern);
7
+ regexCache.set(pattern, re);
8
+ }
9
+ return re;
10
+ }
224
11
  function extractField(obj, path) {
225
12
  const parts = path.split(".");
226
13
  let current = obj;
@@ -262,7 +49,7 @@ function evaluateCondition(actual, operator, target) {
262
49
  return false;
263
50
  }
264
51
  try {
265
- return new RegExp(target).test(actual);
52
+ return getCompiledRegex(target).test(actual);
266
53
  } catch {
267
54
  return false;
268
55
  }
@@ -275,8 +62,7 @@ function matchesActionPattern(pattern, action) {
275
62
  return true;
276
63
  }
277
64
  if (pattern.endsWith("*")) {
278
- const prefix = pattern.slice(0, -1);
279
- return action.startsWith(prefix);
65
+ return action.startsWith(pattern.slice(0, -1));
280
66
  }
281
67
  return false;
282
68
  }
@@ -284,37 +70,35 @@ function evaluateRule(payload, rule) {
284
70
  const targetObject = {
285
71
  action: payload.action,
286
72
  params: payload.params,
287
- context: payload.context,
288
- timestamp: payload.timestamp,
73
+ context: payload.context ?? {},
289
74
  metadata: payload.metadata ?? {}
290
75
  };
291
76
  const actualValue = extractField(targetObject, rule.field);
292
77
  return evaluateCondition(actualValue, rule.operator, rule.value);
293
78
  }
294
- function calculateRisk(decision, matchedCount, semanticScore = 0) {
79
+ function calculateRisk(decision, matchedCount) {
295
80
  if (decision === "BLOCK") {
296
- return { riskScore: Math.max(95, semanticScore), riskLevel: "CRITICAL" };
81
+ return { riskScore: 95, riskLevel: "CRITICAL" };
297
82
  }
298
83
  if (decision === "REVIEW") {
299
- return { riskScore: Math.max(65, semanticScore), riskLevel: "HIGH" };
84
+ return { riskScore: 65, riskLevel: "HIGH" };
300
85
  }
301
86
  if (matchedCount > 0) {
302
87
  return { riskScore: 25, riskLevel: "MEDIUM" };
303
88
  }
304
89
  return { riskScore: 5, riskLevel: "LOW" };
305
90
  }
306
- function evaluatePolicies(payload, policies) {
307
- const startTime = Date.now();
91
+ function evaluatePolicies(payload, sortedPolicies) {
92
+ const startTime = performance.now();
308
93
  const traces = [];
309
- const activePolicies = policies.filter((p) => p.isActive && p.tenantId === payload.context.tenantId).sort((a, b) => b.priority - a.priority);
310
94
  let finalDecision = "ALLOW";
311
- let primaryReason = "No blocking or review policies triggered";
312
- let pipeline = "DETERMINISTIC";
313
- for (const policy of activePolicies) {
314
- if (!matchesActionPattern(policy.actionPattern, payload.action)) {
95
+ let primaryReason = "All security policies satisfied";
96
+ for (let i = 0; i < sortedPolicies.length; i++) {
97
+ const policy = sortedPolicies[i];
98
+ if (!policy.isActive || !matchesActionPattern(policy.actionPattern, payload.action)) {
315
99
  continue;
316
100
  }
317
- const rulesMatched = policy.rules.length > 0 && policy.rules.every((rule) => evaluateRule(payload, rule));
101
+ const rulesMatched = policy.rules.length === 0 || policy.rules.every((rule) => evaluateRule(payload, rule));
318
102
  if (rulesMatched) {
319
103
  traces.push({
320
104
  policyId: policy.id,
@@ -333,35 +117,138 @@ function evaluatePolicies(payload, policies) {
333
117
  }
334
118
  }
335
119
  }
336
- let semanticScore = 0;
337
- if (finalDecision !== "BLOCK") {
338
- const semanticResult = scanPayloadSemantics(payload.params);
339
- if (semanticResult.flagged) {
340
- pipeline = traces.length > 0 ? "HYBRID" : "AI_GUARD";
341
- semanticScore = semanticResult.maxRiskScore;
342
- finalDecision = "BLOCK";
343
- const threatReasons = semanticResult.threats.map((t) => t.reason).join(", ");
344
- primaryReason = `AI Guard blocked threat: ${threatReasons}`;
345
- }
346
- }
347
- const { riskScore, riskLevel } = calculateRisk(finalDecision, traces.length, semanticScore);
348
- const latencyMs = Date.now() - startTime;
120
+ const { riskScore, riskLevel } = calculateRisk(finalDecision, traces.length);
121
+ const latencyMs = Number((performance.now() - startTime).toFixed(3));
349
122
  return {
350
123
  verdictId: `vrd_${Math.random().toString(36).slice(2, 11)}`,
351
124
  decision: finalDecision,
352
125
  reason: primaryReason,
353
126
  riskScore,
354
127
  riskLevel,
355
- evaluationPipeline: pipeline,
356
128
  matchedPolicies: traces,
357
- executedAt: startTime,
129
+ executedAt: Date.now(),
358
130
  latencyMs
359
131
  };
360
132
  }
133
+
134
+ // src/sdk/client.ts
135
+ var Citadel_0 = class {
136
+ baseUrl;
137
+ agentToken;
138
+ environment;
139
+ defaultSessionId;
140
+ policies = [];
141
+ telemetryQueue = [];
142
+ batchSize;
143
+ flushTimer = null;
144
+ constructor(config) {
145
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
146
+ this.agentToken = config.agentToken;
147
+ this.environment = config.environment ?? "production";
148
+ this.defaultSessionId = config.defaultSessionId;
149
+ this.batchSize = config.batchSize ?? 50;
150
+ if (config.policies) {
151
+ this.setPolicies(config.policies);
152
+ }
153
+ const interval = config.flushIntervalMs ?? 3e3;
154
+ this.flushTimer = setInterval(() => {
155
+ void this.flush();
156
+ }, interval);
157
+ if (typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
158
+ this.flushTimer.unref();
159
+ }
160
+ }
161
+ setPolicies(policies) {
162
+ this.policies = [...policies].sort((a, b) => b.priority - a.priority);
163
+ }
164
+ async syncPolicies() {
165
+ const res = await fetch(`${this.baseUrl}/api/v1/policies`, {
166
+ headers: { Authorization: `Bearer ${this.agentToken}` }
167
+ });
168
+ if (res.ok) {
169
+ const data = await res.json();
170
+ const list = Array.isArray(data) ? data : data?.policies ?? [];
171
+ this.setPolicies(list);
172
+ }
173
+ }
174
+ check(action, params = {}, options = {}) {
175
+ const payload = {
176
+ action,
177
+ params,
178
+ context: {
179
+ sessionId: options.sessionId ?? this.defaultSessionId,
180
+ environment: options.environment ?? this.environment
181
+ },
182
+ metadata: options.metadata
183
+ };
184
+ const verdict = evaluatePolicies(payload, this.policies);
185
+ this.telemetryQueue.push({
186
+ traceId: verdict.verdictId,
187
+ action,
188
+ params,
189
+ verdict,
190
+ sessionId: payload.context?.sessionId,
191
+ environment: payload.context?.environment ?? this.environment,
192
+ timestamp: verdict.executedAt
193
+ });
194
+ if (this.telemetryQueue.length >= this.batchSize) {
195
+ void this.flush();
196
+ }
197
+ return verdict;
198
+ }
199
+ isAllowed(action, params = {}, options = {}) {
200
+ return this.check(action, params, options).decision === "ALLOW";
201
+ }
202
+ guard(action, params, callback, options = {}) {
203
+ const verdict = this.check(action, params, options);
204
+ if (verdict.decision === "BLOCK") {
205
+ throw new Error(`Action '${action}' blocked by Citadel 0: ${verdict.reason}`);
206
+ }
207
+ if (verdict.decision === "REVIEW") {
208
+ throw new Error(`Action '${action}' halted: requires human review`);
209
+ }
210
+ return callback();
211
+ }
212
+ async flush() {
213
+ if (this.telemetryQueue.length === 0) {
214
+ return;
215
+ }
216
+ const batch = this.telemetryQueue.splice(0, this.batchSize);
217
+ try {
218
+ await fetch(`${this.baseUrl}/api/v1/telemetry`, {
219
+ method: "POST",
220
+ headers: {
221
+ "Content-Type": "application/json",
222
+ Authorization: `Bearer ${this.agentToken}`
223
+ },
224
+ body: JSON.stringify({ traces: batch })
225
+ });
226
+ } catch {
227
+ this.telemetryQueue.unshift(...batch);
228
+ }
229
+ }
230
+ destroy() {
231
+ if (this.flushTimer) {
232
+ clearInterval(this.flushTimer);
233
+ this.flushTimer = null;
234
+ }
235
+ void this.flush();
236
+ }
237
+ };
238
+
239
+ // src/sdk/tools.ts
240
+ function guardTool(client, action, fn, options) {
241
+ return async (args) => {
242
+ return client.guard(
243
+ action,
244
+ args,
245
+ () => fn(args),
246
+ options
247
+ );
248
+ };
249
+ }
361
250
  export {
362
- CitadelClient,
251
+ Citadel_0,
363
252
  evaluatePolicies,
364
- scanPayloadSemantics,
365
- wrapTool,
366
- wrapToolWithReview
253
+ guardTool
367
254
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citadel0",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Programmatic Security Gateway & Policy Engine for AI Agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Moulick Bose
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.