//#region src/policy/tool-argument-policy.d.ts /** * Progent-style declarative tool-argument policies and Rule-of-Two * capability profiles. Both are **pure, deterministic** * decision engines the tool executor consults BEFORE a call runs - the * preventive layer that turns the coarse lethal-trifecta trigger and the * inert `sandboxPolicy` advisory into an enforced default-deny. * * Progent policy (`ToolArgumentPolicy`): * - four-value vocabulary: rule effects are `allow | deny | ask | * defer` with priority `deny > defer > ask > allow` (`'forbid'` stays * accepted as a legacy alias of `'deny'`), so narrowing composes * safely and a later broad allow can never re-open a denied call, * - default-deny for sensitive tools: when `defaultDenySensitive` is on, * a tool flagged `sensitive` with no matching `allow` is blocked, * - argument predicates: rules match on tool name (exact / glob) plus an * optional pure predicate over the validated args; predicate-free * `deny` rules additionally power the advertise-time deny-by-name * check ({@link isToolDeniedByName}). * * Rule-of-Two (`buildRuleOfTwoPolicy`): a session preset declaring which * of the three lethal-trifecta legs {untrusted input, sensitive data, * external side effects} an agent may hold. Holding all three is the * dangerous configuration; the preset deterministically blocks the third * leg by compiling to a Progent policy + capability floor. * * @packageDocumentation */ /** The three legs of the lethal trifecta (Meta "Rule of Two"). */ type TrifectaLeg = 'untrusted-input' | 'sensitive-data' | 'external-side-effects'; /** Side-effect classes the policy engine reasons about. */ type PolicySideEffectClass = 'pure' | 'read-only' | 'side-effecting' | 'external-stateful'; /** Facts about a tool call the policy engine decides over. */ interface ToolCallFacts { readonly toolName: string; readonly sideEffectClass: PolicySideEffectClass; /** `true` when the tool reads/handles sensitive data (e.g. `sensitivity: 'secret'`). */ readonly sensitive?: boolean; /** * `true` when the tool is an untrusted-content SOURCE - its * trust class is one the taint engine treats as injection-bearing * (mcp-derived / web-search / skill-untrusted; see * `isUntrustedTrustClass` in `@graphorin/security/dataflow`). Powers * the Rule-of-Two `untrustedInput` leg. The engine stays pure: the * caller derives this from the tool's metadata. */ readonly untrustedSource?: boolean; /** The validated arguments (post-schema, post-repair). */ readonly args?: unknown; } /** * Four-value permission vocabulary: * * - `'allow'` - the call may run. * - `'deny'` - the call must not run (deterministic block). * - `'ask'` - the call needs a human decision BEFORE it runs; only a * surface that can durably suspend (the agent pre-screen) can honour * it - a bare executor fails it closed. * - `'defer'` - the decision is parked for ASYNCHRONOUS resolution * (messenger button, workflow awakeable) with a timeout that * auto-denies; like `'ask'`, honoured only by a suspending surface. * * Priority when several rules match: `deny > defer > ask > allow`. * * @stable */ type PermissionEffect = 'allow' | 'deny' | 'ask' | 'defer'; /** * Effect accepted on a {@link ToolArgumentRule}: the four-value * vocabulary plus `'forbid'`, the legacy spelling kept as a back-compat * alias of `'deny'` (existing policies keep working byte-for-byte). * * @stable */ type ToolRuleEffect = PermissionEffect | 'forbid'; /** A single Progent rule. `deny`/`forbid` always beats `allow` (see module doc). */ interface ToolArgumentRule { readonly effect: ToolRuleEffect; /** * Tool-name matcher: an exact name, `'*'` for any, or a trailing-`*` * prefix glob (e.g. `'fs_*'`). Matching is case-sensitive. */ readonly tool: string; /** Optional pure predicate over the call facts (args, sensitivity). */ readonly when?: (facts: ToolCallFacts) => boolean; /** Human-readable reason surfaced on a non-`allow` match. */ readonly reason?: string; } /** A compiled tool-argument policy. */ interface ToolArgumentPolicy { readonly rules: ReadonlyArray; /** * Default-deny a `sensitive` tool with no matching `allow` rule * (Progent's posture for high-risk tools). Default `false` - a policy * with no rules and this off is a no-op (allows everything). */ readonly defaultDenySensitive?: boolean; } /** Decision returned by {@link evaluateToolArgumentPolicy}. */ type ToolPolicyDecision = { readonly effect: 'allow'; } | { readonly effect: 'forbid'; readonly reason: string; }; /** * Four-value decision returned by {@link evaluatePermissionDecision}. * Non-`allow` effects always carry a reason. * * @stable */ type PermissionDecision = { readonly effect: 'allow'; } | { readonly effect: 'deny' | 'ask' | 'defer'; readonly reason: string; }; /** * Evaluate a policy against one tool call under the four-value * vocabulary. Every matching rule contributes its (normalised) * effect; the strongest wins with priority `deny > defer > ask > * allow`, so a broad late `allow` can never re-open a denied call and * an `ask`/`defer` narrows an `allow` but yields to a `deny`. When no * rule matches, `defaultDenySensitive` denies sensitive tools; anything * else is allowed. Pure + deterministic. * * @stable */ declare function evaluatePermissionDecision(policy: ToolArgumentPolicy, facts: ToolCallFacts): PermissionDecision; /** * Evaluate a policy against one tool call, projected onto the binary * legacy vocabulary. Delegates to {@link evaluatePermissionDecision} * and maps every non-`allow` effect to `'forbid'`: a consumer that * cannot ask or defer must not run the call (fail-closed). Legacy * policies contain only `allow`/`forbid` rules, for which this * is byte-identical to the original forbid-before-allow semantics. * * @stable */ declare function evaluateToolArgumentPolicy(policy: ToolArgumentPolicy, facts: ToolCallFacts): ToolPolicyDecision; /** Result of {@link isToolDeniedByName}. */ type NameDenialDecision = { readonly denied: false; } | { readonly denied: true; readonly reason: string; }; /** * Name-level deny check (deny-by-name): does a PREDICATE-FREE * `deny`/`forbid` rule match this tool name? Used at advertise time - * the per-step catalogue, `tool_search` results/promotion and the * executor's early mirror all consult it BEFORE any args exist. A rule * with a `when` predicate is call-time only (its predicate reasons over * validated args) and never participates here, so the check stays * deterministic for a given policy + name. * * @stable */ declare function isToolDeniedByName(policy: ToolArgumentPolicy, toolName: string): NameDenialDecision; /** * Capability profile for the Rule-of-Two preset. Declares which of the * three trifecta legs the agent is permitted to hold this session. The * dangerous configuration is holding all three; a well-formed profile * drops at least one. * * @stable */ interface RuleOfTwoProfile { /** May the agent ingest untrusted input (web / MCP / untrusted skills)? */ readonly untrustedInput: boolean; /** May the agent read sensitive data (secrets / PII)? */ readonly sensitiveData: boolean; /** May the agent take external side effects (write / send / deploy)? */ readonly externalSideEffects: boolean; } /** Result of compiling a Rule-of-Two profile. */ interface RuleOfTwoCompilation { /** The tool-argument policy enforcing the profile at call time. */ readonly policy: ToolArgumentPolicy; /** * The capability floor: `'read-only'` when the profile denies external * side effects (so the agent runtime's capability gate blocks writer * tools too), else `undefined`. */ readonly capability?: 'read-only'; /** The legs the profile holds - `> 2` is flagged unsafe. */ readonly heldLegs: ReadonlyArray; /** `true` when the profile holds all three legs (the dangerous case). */ readonly holdsFullTrifecta: boolean; } /** * Compile a Rule-of-Two profile into an enforceable policy. When the * profile denies external side effects, the compilation yields a * `'read-only'` capability floor AND a forbid rule over writer tools; * when it denies sensitive data, sensitive tools are default-denied. * Holding all three legs is surfaced (`holdsFullTrifecta`) so the caller * can refuse or warn - the preset never silently permits the trifecta. * * @stable */ declare function buildRuleOfTwoPolicy(profile: RuleOfTwoProfile): RuleOfTwoCompilation; //#endregion export { NameDenialDecision, PermissionDecision, PermissionEffect, PolicySideEffectClass, RuleOfTwoCompilation, RuleOfTwoProfile, ToolArgumentPolicy, ToolArgumentRule, ToolCallFacts, ToolPolicyDecision, ToolRuleEffect, TrifectaLeg, buildRuleOfTwoPolicy, evaluatePermissionDecision, evaluateToolArgumentPolicy, isToolDeniedByName }; //# sourceMappingURL=tool-argument-policy.d.ts.map