import { z } from "zod/v4"; /** * Tool effect classes (plan §10.1, §10.3). * * An effect is not metadata: it is an enforced runtime capability ceiling — * the maximum class of side effect the executing unit can cause. The * capability gate (capabilities/gate.ts) denies any operation whose * mutation class exceeds the ceiling, on both sides of the trust boundary. * Approval gating in Kairo reads the same declaration. */ export const TOOL_EFFECTS = ["read", "write", "destructive", "external"] as const; export const toolEffectSchema = z.enum(TOOL_EFFECTS); export type ToolEffect = z.infer; /** Ordered mutation classes; an effect ceiling admits its class and below. */ export const MUTATION_CLASSES = ["read", "write", "destructive"] as const; export type MutationClass = (typeof MUTATION_CLASSES)[number]; export const MUTATION_CLASS_RANK: Record = { read: 0, write: 1, destructive: 2, }; export interface EffectCeiling { /** Highest mutation class capability calls may reach. */ maxMutation: MutationClass; /** Whether outbound network access is permitted (per manifest allowlist). */ allowsNetwork: boolean; } /** * Effect → ceiling mapping, exactly as specified in plan §10.3: * `external` grants network reach with write-level workspace mutation. */ export const EFFECT_CEILINGS: Record = { read: { maxMutation: "read", allowsNetwork: false }, write: { maxMutation: "write", allowsNetwork: false }, destructive: { maxMutation: "destructive", allowsNetwork: false }, external: { maxMutation: "write", allowsNetwork: true }, }; export function mutationClassAllowedByEffect( mutation: MutationClass, effect: ToolEffect, ): boolean { const ceiling = EFFECT_CEILINGS[effect]; return MUTATION_CLASS_RANK[mutation] <= MUTATION_CLASS_RANK[ceiling.maxMutation]; }