@mandatedev/agent 0.1.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.
@@ -0,0 +1,141 @@
1
+ // ============================================================
2
+ // MANDATE — LangChain / LangGraph Hook
3
+ // Uses before_tool_call callback for full semantic enforcement.
4
+ // Compatible with LangChain.js and LangGraph.
5
+ // ============================================================
6
+
7
+ import type {
8
+ AgentIntent,
9
+ MandateConfig,
10
+ EvaluationResult,
11
+ } from '../types';
12
+ import type { JSPolicyEvaluator } from '../evaluator/js-evaluator';
13
+ import type { AuditBuffer } from '../buffer';
14
+ import type { DegradationManager } from '../degradation';
15
+
16
+ interface LangChainToolCall {
17
+ name: string;
18
+ args: Record<string, unknown>;
19
+ id?: string;
20
+ }
21
+
22
+ export class MandateLangChainHook {
23
+ private evaluator: JSPolicyEvaluator;
24
+ private buffer: AuditBuffer;
25
+ private degradation: DegradationManager;
26
+ private config: MandateConfig;
27
+ private sessionId: string;
28
+ private stepCounter: number = 0;
29
+
30
+ constructor(
31
+ config: MandateConfig,
32
+ evaluator: JSPolicyEvaluator,
33
+ buffer: AuditBuffer,
34
+ degradation: DegradationManager
35
+ ) {
36
+ this.config = config;
37
+ this.evaluator = evaluator;
38
+ this.buffer = buffer;
39
+ this.degradation = degradation;
40
+ this.sessionId = `sess_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
41
+ }
42
+
43
+ // Returns a before_tool_call callback for LangChain
44
+ // Usage: agent.beforeToolCall = mandateHook.getBeforeToolCallHook()
45
+ getBeforeToolCallHook(): (
46
+ toolCall: LangChainToolCall
47
+ ) => Promise<LangChainToolCall | null> {
48
+ return async (
49
+ toolCall: LangChainToolCall
50
+ ): Promise<LangChainToolCall | null> => {
51
+ const step = this.stepCounter++;
52
+ const intent = this.buildIntent(toolCall, step);
53
+ const result = this.evaluator.evaluate(intent);
54
+
55
+ this.logToBuffer(intent, result);
56
+
57
+ if (result.decision === 'ALLOW') {
58
+ return toolCall;
59
+ }
60
+
61
+ if (result.decision === 'THROTTLE') {
62
+ await this.delay(2000);
63
+ this.config.onAlert?.(intent, result.anomalyScore ?? 0);
64
+ return toolCall;
65
+ }
66
+
67
+ // DENY or ESCALATE — block the tool call
68
+ this.config.onViolation?.(intent, result);
69
+ return null;
70
+ };
71
+ }
72
+
73
+ // Direct interception for manual use
74
+ async interceptToolCall(
75
+ toolCall: LangChainToolCall
76
+ ): Promise<{ allowed: boolean; result: EvaluationResult }> {
77
+ const step = this.stepCounter++;
78
+ const intent = this.buildIntent(toolCall, step);
79
+ const result = this.evaluator.evaluate(intent);
80
+
81
+ this.logToBuffer(intent, result);
82
+
83
+ if (result.decision === 'DENY' || result.decision === 'ESCALATE') {
84
+ this.config.onViolation?.(intent, result);
85
+ return { allowed: false, result };
86
+ }
87
+
88
+ if (result.decision === 'THROTTLE') {
89
+ await this.delay(2000);
90
+ this.config.onAlert?.(intent, result.anomalyScore ?? 0);
91
+ }
92
+
93
+ return { allowed: true, result };
94
+ }
95
+
96
+ // ============================================================
97
+ // PRIVATE
98
+ // ============================================================
99
+
100
+ private buildIntent(
101
+ toolCall: LangChainToolCall,
102
+ step: number
103
+ ): AgentIntent {
104
+ return {
105
+ toolName: toolCall.name,
106
+ args: toolCall.args,
107
+ context: {
108
+ agentId: this.config.agentId,
109
+ sessionId: this.sessionId,
110
+ environment: this.config.environment,
111
+ stepInChain: step,
112
+ framework: 'langchain',
113
+ timestamp: Date.now(),
114
+ },
115
+ };
116
+ }
117
+
118
+ private logToBuffer(intent: AgentIntent, result: EvaluationResult): void {
119
+ if (this.config.auditLevel === 'off') return;
120
+
121
+ this.buffer.append({
122
+ timestamp: Date.now(),
123
+ source: 'REALTIME',
124
+ agentId: this.config.agentId,
125
+ orgId: this.config.orgId,
126
+ policyHash: this.config.policy.policyHash,
127
+ degradationTier: this.degradation.getCurrentTier(),
128
+ toolName: intent.toolName,
129
+ toolArgs: this.config.auditLevel === 'full' ? intent.args : {},
130
+ intentContext: intent.context,
131
+ anomalyScore: result.anomalyScore ?? 0,
132
+ policyDecision: result.decision,
133
+ responseLevel: 1,
134
+ evalLatencyMs: result.latencyMs,
135
+ });
136
+ }
137
+
138
+ private delay(ms: number): Promise<void> {
139
+ return new Promise((resolve) => setTimeout(resolve, ms));
140
+ }
141
+ }
@@ -0,0 +1,142 @@
1
+ // ============================================================
2
+ // MANDATE — OpenAI Assistants API + Function Calling Hook
3
+ // Intercepts function_call declarations before execution.
4
+ // Full semantic enforcement on structured tool-call intent.
5
+ // ============================================================
6
+
7
+ import type {
8
+ AgentIntent,
9
+ MandateConfig,
10
+ EvaluationResult,
11
+ } from '../types';
12
+ import type { JSPolicyEvaluator } from '../evaluator/js-evaluator';
13
+ import type { AuditBuffer } from '../buffer';
14
+ import type { DegradationManager } from '../degradation';
15
+
16
+ interface OpenAIFunctionCall {
17
+ name: string;
18
+ arguments: string;
19
+ }
20
+
21
+ interface OpenAIToolCall {
22
+ id: string;
23
+ type: 'function';
24
+ function: OpenAIFunctionCall;
25
+ }
26
+
27
+ interface InterceptResult {
28
+ allowed: OpenAIToolCall[];
29
+ blocked: Array<{ toolCall: OpenAIToolCall; result: EvaluationResult }>;
30
+ }
31
+
32
+ export class MandateOpenAIHook {
33
+ private evaluator: JSPolicyEvaluator;
34
+ private buffer: AuditBuffer;
35
+ private degradation: DegradationManager;
36
+ private config: MandateConfig;
37
+ private sessionId: string;
38
+
39
+ constructor(
40
+ config: MandateConfig,
41
+ evaluator: JSPolicyEvaluator,
42
+ buffer: AuditBuffer,
43
+ degradation: DegradationManager
44
+ ) {
45
+ this.config = config;
46
+ this.evaluator = evaluator;
47
+ this.buffer = buffer;
48
+ this.degradation = degradation;
49
+ this.sessionId = this.generateSessionId();
50
+ }
51
+
52
+ // Intercept OpenAI tool calls before execution
53
+ async interceptToolCalls(
54
+ toolCalls: OpenAIToolCall[],
55
+ stepInChain: number = 0
56
+ ): Promise<InterceptResult> {
57
+ const allowed: OpenAIToolCall[] = [];
58
+ const blocked: Array<{
59
+ toolCall: OpenAIToolCall;
60
+ result: EvaluationResult;
61
+ }> = [];
62
+
63
+ for (const toolCall of toolCalls) {
64
+ const intent = this.buildIntent(toolCall, stepInChain);
65
+ const result = this.evaluator.evaluate(intent);
66
+
67
+ this.logToBuffer(intent, result);
68
+
69
+ if (result.decision === 'ALLOW') {
70
+ allowed.push(toolCall);
71
+ } else if (result.decision === 'THROTTLE') {
72
+ await this.delay(2000);
73
+ this.config.onAlert?.(intent, result.anomalyScore ?? 0);
74
+ allowed.push(toolCall);
75
+ } else {
76
+ blocked.push({ toolCall, result });
77
+ this.config.onViolation?.(intent, result);
78
+ }
79
+ }
80
+
81
+ return { allowed, blocked };
82
+ }
83
+
84
+ // ============================================================
85
+ // PRIVATE
86
+ // ============================================================
87
+
88
+ private buildIntent(
89
+ toolCall: OpenAIToolCall,
90
+ stepInChain: number
91
+ ): AgentIntent {
92
+ let args: Record<string, unknown> = {};
93
+ try {
94
+ const parsed: unknown = JSON.parse(toolCall.function.arguments);
95
+ if (typeof parsed === 'object' && parsed !== null) {
96
+ args = parsed as Record<string, unknown>;
97
+ }
98
+ } catch {
99
+ args = { raw: toolCall.function.arguments };
100
+ }
101
+ return {
102
+ toolName: toolCall.function.name,
103
+ args,
104
+ context: {
105
+ agentId: this.config.agentId,
106
+ sessionId: this.sessionId,
107
+ environment: this.config.environment,
108
+ stepInChain,
109
+ framework: 'openai-assistants',
110
+ timestamp: Date.now(),
111
+ },
112
+ };
113
+ }
114
+
115
+ private logToBuffer(intent: AgentIntent, result: EvaluationResult): void {
116
+ if (this.config.auditLevel === 'off') return;
117
+
118
+ this.buffer.append({
119
+ timestamp: Date.now(),
120
+ source: 'REALTIME',
121
+ agentId: this.config.agentId,
122
+ orgId: this.config.orgId,
123
+ policyHash: this.config.policy.policyHash,
124
+ degradationTier: this.degradation.getCurrentTier(),
125
+ toolName: intent.toolName,
126
+ toolArgs: this.config.auditLevel === 'full' ? intent.args : {},
127
+ intentContext: intent.context,
128
+ anomalyScore: result.anomalyScore ?? 0,
129
+ policyDecision: result.decision,
130
+ responseLevel: 1,
131
+ evalLatencyMs: result.latencyMs,
132
+ });
133
+ }
134
+
135
+ private generateSessionId(): string {
136
+ return `sess_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
137
+ }
138
+
139
+ private delay(ms: number): Promise<void> {
140
+ return new Promise((resolve) => setTimeout(resolve, ms));
141
+ }
142
+ }
package/src/index.ts ADDED
@@ -0,0 +1,175 @@
1
+ // ============================================================
2
+ // @mandate/agent — Trust Engine for the Agent Economy
3
+ // The Mandate Agent SDK main entry point
4
+ // Every developer imports from here
5
+ // ============================================================
6
+
7
+ import { JSPolicyEvaluator } from './evaluator/js-evaluator';
8
+ import { AuditBuffer } from './buffer';
9
+ import { DegradationManager } from './degradation';
10
+ import { MandateOpenAIHook } from './hooks/openai';
11
+ import { MandateLangChainHook } from './hooks/langchain';
12
+ import { MandateAnthropicHook } from './hooks/anthropic';
13
+
14
+ import type {
15
+ MandateConfig,
16
+ AgentIntent,
17
+ EvaluationResult,
18
+ AuditEvent,
19
+ DegradationTier,
20
+ MandatePolicy,
21
+ } from './types';
22
+
23
+ // Re-export all types for developer use
24
+ export type {
25
+ MandateConfig,
26
+ MandatePolicy,
27
+ AgentIntent,
28
+ EvaluationResult,
29
+ AuditEvent,
30
+ DegradationTier,
31
+ AgentFramework,
32
+ AgentEnvironment,
33
+ AgentRiskTier,
34
+ PolicyDecision,
35
+ PolicyRule,
36
+ PolicyCondition,
37
+ ResponseLevel,
38
+ } from './types';
39
+
40
+ // ============================================================
41
+ // MANDATE AGENT — main class
42
+ // ============================================================
43
+
44
+ export class MandateAgent {
45
+ private config: MandateConfig;
46
+ private evaluator: JSPolicyEvaluator;
47
+ private buffer: AuditBuffer;
48
+ private degradation: DegradationManager;
49
+
50
+ // Framework hooks — use the one matching your agent framework
51
+ public readonly openai: MandateOpenAIHook;
52
+ public readonly langchain: MandateLangChainHook;
53
+ public readonly anthropic: MandateAnthropicHook;
54
+
55
+ constructor(config: MandateConfig) {
56
+ this.config = config;
57
+
58
+ // Initialize core components
59
+ this.evaluator = new JSPolicyEvaluator(config.policy);
60
+
61
+ this.buffer = new AuditBuffer({
62
+ maxSize: 10000,
63
+ flushCallback: async (events) => {
64
+ await this.sendToControlPlane(events);
65
+ },
66
+ });
67
+
68
+ this.degradation = new DegradationManager({
69
+ gracePeriodMs: config.policy.localAutonomy.gracePeriodMs,
70
+ riskTier: config.riskTier ?? 'STANDARD',
71
+ onTierChange: (from, to) => {
72
+ config.onDegradation?.(from, to);
73
+ console.warn(`[Mandate] Degradation: ${from} → ${to}`);
74
+ },
75
+ });
76
+
77
+ // Initialize framework-specific hooks
78
+ this.openai = new MandateOpenAIHook(
79
+ config,
80
+ this.evaluator,
81
+ this.buffer,
82
+ this.degradation
83
+ );
84
+
85
+ this.langchain = new MandateLangChainHook(
86
+ config,
87
+ this.evaluator,
88
+ this.buffer,
89
+ this.degradation
90
+ );
91
+
92
+ this.anthropic = new MandateAnthropicHook(
93
+ config,
94
+ this.evaluator,
95
+ this.buffer,
96
+ this.degradation
97
+ );
98
+
99
+ console.log(
100
+ `[Mandate] Agent "${config.agentId}" initialized | ` +
101
+ `env: ${config.environment} | ` +
102
+ `framework: ${config.framework} | ` +
103
+ `policy: ${config.policy.policyHash}`
104
+ );
105
+ }
106
+
107
+ // Direct evaluation for custom frameworks
108
+ evaluate(intent: AgentIntent): EvaluationResult {
109
+ return this.evaluator.evaluate(intent);
110
+ }
111
+
112
+ // Flush the audit buffer manually
113
+ async flushAuditBuffer(): Promise<AuditEvent[]> {
114
+ return this.buffer.flush();
115
+ }
116
+
117
+ // Verify the cryptographic integrity of the audit chain
118
+ verifyAuditChain(): { valid: boolean; corruptedAt?: number } {
119
+ return this.buffer.verify();
120
+ }
121
+
122
+ // Get current degradation tier
123
+ getDegradationTier(): DegradationTier {
124
+ return this.degradation.getCurrentTier();
125
+ }
126
+
127
+ // Get current audit buffer size
128
+ getBufferSize(): number {
129
+ return this.buffer.size();
130
+ }
131
+
132
+ // Update policy — hot reload, no restart required
133
+ updatePolicy(policy: MandatePolicy): void {
134
+ this.evaluator.updatePolicy(policy);
135
+ console.log(`[Mandate] Policy updated: ${policy.policyHash}`);
136
+ }
137
+
138
+ // ============================================================
139
+ // PRIVATE
140
+ // ============================================================
141
+
142
+ private async sendToControlPlane(events: AuditEvent[]): Promise<void> {
143
+ if (!this.config.controlPlaneUrl || !this.config.apiKey) return;
144
+
145
+ try {
146
+ const response = await fetch(
147
+ `${this.config.controlPlaneUrl}/v1/events`,
148
+ {
149
+ method: 'POST',
150
+ headers: {
151
+ 'Content-Type': 'application/json',
152
+ Authorization: `Bearer ${this.config.apiKey}`,
153
+ },
154
+ body: JSON.stringify({ events }),
155
+ }
156
+ );
157
+
158
+ if (!response.ok) {
159
+ throw new Error(`Control plane responded with ${response.status}`);
160
+ }
161
+
162
+ this.degradation.onControlPlaneReconnected();
163
+ } catch {
164
+ this.degradation.onControlPlaneUnreachable();
165
+ }
166
+ }
167
+ }
168
+
169
+ // ============================================================
170
+ // CONVENIENCE FACTORY — three lines of code to wrap any agent
171
+ // ============================================================
172
+
173
+ export function createMandateAgent(config: MandateConfig): MandateAgent {
174
+ return new MandateAgent(config);
175
+ }
package/src/types.ts ADDED
@@ -0,0 +1,162 @@
1
+ // ============================================================
2
+ // MANDATE CORE TYPES
3
+ // Every component in the SDK depends on these definitions
4
+ // ============================================================
5
+
6
+ // The framework the agent is built on
7
+ export type AgentFramework =
8
+ | 'openai-assistants'
9
+ | 'langchain'
10
+ | 'autogen'
11
+ | 'crewai'
12
+ | 'anthropic'
13
+ | 'custom';
14
+
15
+ // Environment the agent runs in
16
+ export type AgentEnvironment = 'production' | 'staging' | 'development';
17
+
18
+ // Risk level assigned to this agent
19
+ export type AgentRiskTier = 'LOW' | 'STANDARD' | 'HIGH' | 'CRITICAL';
20
+
21
+ // Degradation tiers — V3 five-tier state machine
22
+ export type DegradationTier =
23
+ | 'NOMINAL' // Full real-time enforcement, live audit streaming
24
+ | 'DEGRADED' // Local cache active, audit buffered, agent at full capacity
25
+ | 'ISOLATED' // Control plane unreachable, local enforcement only
26
+ | 'GRACE_STD' // 30min grace elapsed, standard risk agent continues
27
+ | 'GRACE_HIGH'; // 30min grace elapsed, high risk agent → PAUSE
28
+
29
+ // Policy decision returned by the evaluator
30
+ export type PolicyDecision = 'ALLOW' | 'DENY' | 'ESCALATE' | 'THROTTLE';
31
+
32
+ // Kill switch response levels
33
+ export type ResponseLevel = 1 | 2 | 3 | 4 | 5;
34
+ // 1=ALERT 2=THROTTLE 3=PAUSE 4=SCOPED_KILL 5=FULL_KILL
35
+
36
+ // ============================================================
37
+ // INTENT — what Mandate intercepts from agent frameworks
38
+ // ============================================================
39
+
40
+ export interface IntentContext {
41
+ agentId: string;
42
+ sessionId: string;
43
+ environment: AgentEnvironment;
44
+ stepInChain: number;
45
+ framework: AgentFramework;
46
+ timestamp: number;
47
+ }
48
+
49
+ export interface AgentIntent {
50
+ toolName: string;
51
+ args: Record<string, unknown>;
52
+ context: IntentContext;
53
+ }
54
+
55
+ // ============================================================
56
+ // EVALUATION RESULT
57
+ // ============================================================
58
+
59
+ export interface EvaluationResult {
60
+ decision: PolicyDecision;
61
+ reason: string;
62
+ latencyMs: number;
63
+ ruleMatched?: string;
64
+ anomalyScore?: number;
65
+ responseLevel?: ResponseLevel;
66
+ }
67
+
68
+ // ============================================================
69
+ // POLICY — JSON representation (MPL compiles to this format)
70
+ // ============================================================
71
+
72
+ export type ConditionOperator =
73
+ | 'eq' | 'neq'
74
+ | 'lt' | 'lte'
75
+ | 'gt' | 'gte'
76
+ | 'in' | 'nin'
77
+ | 'startsWith'
78
+ | 'endsWith'
79
+ | 'contains';
80
+
81
+ export interface PolicyCondition {
82
+ field: string; // dot-notation: "intent.args.amount"
83
+ operator: ConditionOperator;
84
+ value: unknown;
85
+ }
86
+
87
+ export interface PolicyRule {
88
+ tool: string; // supports wildcards: "read_*", "*", "process_payment"
89
+ conditions?: PolicyCondition[];
90
+ }
91
+
92
+ export interface PolicyIdentity {
93
+ org: string;
94
+ env: AgentEnvironment;
95
+ framework?: string;
96
+ }
97
+
98
+ export interface AnomalyThresholds {
99
+ alert: number; // default 0.60 — fires ALERT
100
+ throttle: number; // default 0.80 — fires THROTTLE
101
+ }
102
+
103
+ export interface LocalAutonomyConfig {
104
+ gracePeriodMs: number; // default 1800000 (30 minutes)
105
+ postGrace: 'PAUSE' | 'STOP';
106
+ }
107
+
108
+ export interface MandatePolicy {
109
+ agentId: string;
110
+ version: string;
111
+ policyHash: string;
112
+ identity: PolicyIdentity;
113
+ allow: PolicyRule[];
114
+ deny: PolicyRule[];
115
+ anomalyThresholds: AnomalyThresholds;
116
+ localAutonomy: LocalAutonomyConfig;
117
+ }
118
+
119
+ // ============================================================
120
+ // AUDIT EVENT — written to the hash-chain buffer
121
+ // ============================================================
122
+
123
+ export interface AuditEvent {
124
+ eventId: string;
125
+ prevHash: string;
126
+ eventHash: string;
127
+ timestamp: number;
128
+ source: 'REALTIME' | 'BUFFER_SYNC';
129
+ agentId: string;
130
+ orgId: string;
131
+ policyHash: string;
132
+ degradationTier: DegradationTier;
133
+ toolName: string;
134
+ toolArgs: Record<string, unknown>;
135
+ intentContext: IntentContext;
136
+ anomalyScore: number;
137
+ blastRadiusEst?: string;
138
+ policyDecision: PolicyDecision;
139
+ responseLevel: ResponseLevel;
140
+ evalLatencyMs: number;
141
+ tokensUsed?: number;
142
+ costUsd?: number;
143
+ }
144
+
145
+ // ============================================================
146
+ // MANDATE CONFIGURATION
147
+ // ============================================================
148
+
149
+ export interface MandateConfig {
150
+ agentId: string;
151
+ orgId: string;
152
+ apiKey?: string;
153
+ controlPlaneUrl?: string;
154
+ policy: MandatePolicy;
155
+ framework: AgentFramework;
156
+ environment: AgentEnvironment;
157
+ auditLevel: 'full' | 'minimal' | 'off';
158
+ riskTier?: AgentRiskTier;
159
+ onViolation?: (intent: AgentIntent, result: EvaluationResult) => void;
160
+ onAlert?: (intent: AgentIntent, score: number) => void;
161
+ onDegradation?: (from: DegradationTier, to: DegradationTier) => void;
162
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2020", "DOM"],
7
+ "strict": true,
8
+ "exactOptionalPropertyTypes": true,
9
+ "noUncheckedIndexedAccess": true,
10
+ "noImplicitReturns": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "outDir": "./dist",
16
+ "rootDir": "./src",
17
+ "skipLibCheck": true
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }