/** * Action-policy evaluation ENGINE — pure, self-contained, no I/O, no imports. * * This file is the single source of truth for HOW a tool call is matched * against org Action Policies. It runs in TWO places: * * - backend/src/modules/policies/action-policy-engine.ts (server: check-tool-call) * - sdks/cli/src/actionPolicyEngine.ts (CLI: OFFLINE enforcement) * * The CLI copy lets fleet machines keep enforcing the org's real policies when * the control plane is unreachable: the runtime bundle ships the policy rules * (hash-versioned, so edits bust the cache within one poll) and hooks / the * MCP gateway evaluate locally with EXACTLY these semantics. * * THE TWO COPIES MUST STAY BYTE-IDENTICAL. A CI drift-guard test * (backend/src/__tests__/action-policy-engine-sync.test.ts) fails the build if * they diverge. Change this file? Copy it to the other location verbatim. */ export type ActionPolicyVerdict = 'allow' | 'block' | 'require_approval' | 'log'; export interface ActionPolicyConstraint { field: string; operator: 'contains' | 'not_contains' | 'equals' | 'not_equals' | 'gt' | 'lt' | 'matches'; value: string; } export interface ActionPolicyRule { operations: string[]; verdict: ActionPolicyVerdict; constraints?: ActionPolicyConstraint[]; } export interface ActionPolicyTargeting { developerNames?: string[]; machineNames?: string[]; } /** The minimal policy shape the engine needs — structurally satisfied by the backend's ActionPolicy. */ export interface EngineActionPolicy { id: string; name: string; scope?: string; /** Rollout stage — `monitor` policies log would-be verdicts without enforcing. Default: enforce. */ stage?: string; /** First-class developer/machine targeting (empty = applies to everyone). */ appliesTo?: ActionPolicyTargeting; resourceType?: string; rules: ActionPolicyRule[]; enabled?: boolean; } /** A monitor-stage policy match: what WOULD have happened, recorded but not enforced. */ export interface ActionPolicyMonitorMatch { policyId?: string; policyName?: string; verdict: ActionPolicyVerdict; matchedRule?: string; } export interface EngineCheckResult { allowed: boolean; verdict: ActionPolicyVerdict; policyId?: string; policyName?: string; resourceType?: string; matchedRule?: string; reason?: string; /** Monitor-stage policies that matched (would-block / would-approve) without enforcing. */ monitorMatches?: ActionPolicyMonitorMatch[]; /** Org-scoped policies skipped because the call carried no developerName (for caller-side logging). */ skippedOrgPolicies?: string[]; } /** * Derive the set of canonical operations a tool represents from its NAME and declared inventory * actions. This is the "declared operations" model: the tool's identity/declaration defines what * operation a bare call performs, NOT a substring match against the raw tool name. Used only for * bare invocations (see evaluateActionPolicies). */ export declare function deriveToolOperations(toolName: string, declaredActions?: string[]): string[]; export declare function toolCapabilities(value: string): Set; /** * Check a tool call against action policies. * Returns the most restrictive matching verdict (pure — callers do their own logging). */ export declare function evaluateActionPolicies(policies: EngineActionPolicy[], toolName: string, operation: string, context?: Record, declaredOperations?: string[]): EngineCheckResult; /** * Infer operation and context from a tool call. * Handles common patterns: SQL operations, file paths, HTTP methods, etc. */ export declare function inferToolContext(toolName: string, args: Record): { operation: string; context: Record; }; /** Static inventory match — same operation rules as runtime, without constraint context. */ export declare function inventoryToolMatchesActionPolicy(toolName: string, toolActions: string[] | undefined, policies: EngineActionPolicy[]): boolean;