The Coline App SDK: apps, tools, file types and surfaces that run inside Coline workspaces, and public websites with Coline Sites. TypeScript, with an in-memory test workspace. MIT.
| * |
| 6 | * An effect is not metadata: it is an enforced runtime capability ceiling — |
| 7 | * the maximum class of side effect the executing unit can cause. The |
| 8 | * capability gate (capabilities/gate.ts) denies any operation whose |
| 9 | * mutation class exceeds the ceiling, on both sides of the trust boundary. |
| 10 | * Approval gating in Kairo reads the same declaration. |
| 11 | */ |
| 12 |
| 13 | export const TOOL_EFFECTS = ["read", "write", "destructive", "external"] as const; |
| 14 | export const toolEffectSchema = z.enum(TOOL_EFFECTS); |
| 15 | export type ToolEffect = z.infer<typeof toolEffectSchema>; |
| 16 |
| 17 | /** Ordered mutation classes; an effect ceiling admits its class and below. */ |
| 18 | export const MUTATION_CLASSES = ["read", "write", "destructive"] as const; |
| 19 | export type MutationClass = (typeof MUTATION_CLASSES)[number]; |
| 20 |
| 21 | export const MUTATION_CLASS_RANK: Record<MutationClass, number> = { |
| 22 | read: 0, |
| 23 | write: 1, |
| 24 | destructive: 2, |
| 25 | }; |
| 26 |
| 27 | export interface EffectCeiling { |
| 28 | /** Highest mutation class capability calls may reach. */ |
| 29 | maxMutation: MutationClass; |
| 30 | /** Whether outbound network access is permitted (per manifest allowlist). */ |
| 31 | allowsNetwork: boolean; |
| 32 | } |
| 33 |
| 34 | /** |
| 35 | * Effect → ceiling mapping, exactly as specified in plan §10.3: |
| 36 | * `external` grants network reach with write-level workspace mutation. |
| 37 | */ |
| 38 | export const EFFECT_CEILINGS: Record<ToolEffect, EffectCeiling> = { |
| 39 | read: { maxMutation: "read", allowsNetwork: false }, |
| 40 | write: { maxMutation: "write", allowsNetwork: false }, |
| 41 | destructive: { maxMutation: "destructive", allowsNetwork: false }, |
| 42 | external: { maxMutation: "write", allowsNetwork: true }, |
| 43 | }; |
| 44 |
| 45 | export function mutationClassAllowedByEffect( |
| 46 | mutation: MutationClass, |
| 47 | effect: ToolEffect, |
| 48 | ): boolean { |
| 49 | const ceiling = EFFECT_CEILINGS[effect]; |
| 50 | return MUTATION_CLASS_RANK[mutation] <= MUTATION_CLASS_RANK[ceiling.maxMutation]; |
| 51 | } |
| 52 |