UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

263 lines • 13.4 kB
import { type NetworkSetting } from '../io/network.ts'; import { type ProcessPermissions } from '../process/config.ts'; import { ScriptSource } from '../runtime/types.ts'; import { type EnvEntries, SecretRef } from '../secrets/config.ts'; import type { Limit, ProcessScope, ShowEntry, Visibility } from '../types.ts'; import { type MountMode } from '../types.ts'; import type { AdmissionRules, CommandRule, HideReason, ProfileScript, ProfileScripts, ScriptMethod } from './types.ts'; /** * `paths:` of a profile, or of one of its mount sections. `hide` entries * use the document's one grammar: an entry with `*`, `?` or `[` is a * pattern, anything else an exact path and its subtree * (`utils/hidden.classifyPaths`); every entry holds a token and is * absolute or a name pattern, wherever the block is written. A hide * entry may also be a group `{patterns: [...], reason: ...}`: the * patterns join the flat list like any other entry and the reason lands * in `reasons`, the operator-only side table (`HideReason`). * * `show` is the other half of the path axis: a mapping of path to mode * (`{"/repo/docs": "r"}`), or a plain list whose entries inherit the * mount's mode. Every entry is absolute, a subtree or an anchored * pattern, because a show anchors to a place and a name pattern names * none. An entry re-opens its subtree inside a hidden region when its * anchor is deeper than the hide's, and states the mode in force below * its anchor when it carries one; both on the one anchor-depth rule. */ export interface PathsBlock { readonly hide: readonly string[]; readonly show?: readonly ShowEntry[]; readonly reasons?: readonly HideReason[]; } /** `vars:` of a profile: names or globs over names the session reads as unset. */ export interface VarsBlock { readonly hide: readonly string[]; } /** * `commands:` at the top level of a profile. `allow` lists the command * patterns the profile allows; a line no pattern covers is refused, * `<cmd>: Permission denied` at 126, like any other rule's Deny. Shell * builtins are subjects like everything else: a list stating only `cat` * refuses `echo` and `cd`. The agent's own functions are the one exemption, safe because * every line of a body passes the gate itself. `ask` rules are admitted * only with a host approval; `deny` rules refuse with a reason. A bare * string in either is one command pattern with the default reason. * `allow` null or absent (unstated) allows everything. */ export interface CommandsBlock { readonly allow?: readonly string[] | null; readonly ask?: readonly CommandRule[]; readonly deny?: readonly CommandRule[]; } /** * `commands:` of one mount section: `ask` and `deny` only. A mount rule * applies to a line that works inside the mount (its cwd or one of its * paths lies under the root); its `paths` are absolute, like every other * path in the document, and must name something under that root. There * is no `allow` here: what a session can see is a property of the * session, and an operand cannot make a command "not found". */ export interface MountCommandsBlock { readonly ask?: readonly CommandRule[]; readonly deny?: readonly CommandRule[]; } /** * One mount's entry in a profile: what this profile may do there. Every field * is optional, and an omitted mount is not a refusal: the mount is * reachable at the mode it declares in the workspace's `mounts:`, which * a profile can only weaken (`weakerMode`), never raise. A profile that must * not touch a mount hides it, so the mount reads as nonexistent rather * than as a permission error naming something the profile cannot see. * * `commands` here carries ask and deny only: an allow list installs a * command for the whole session, and visibility is answered before any * operand exists, so it cannot be per mount. Rules written here apply to * a line that works inside this mount, by cwd or by operand, which is * what a path-scoped rule cannot express (`cd /repo && git commit` names * no path). */ export interface ProfileMount { readonly mode?: MountMode | null; readonly commands?: MountCommandsBlock | null; readonly paths?: PathsBlock | null; } /** * One profile: the whole permission document a session runs under. * * A session is created from exactly one of these, and it is the only * place permissions are written. There is no workspace-wide block and * no mount-owned block above it, so reading this object is reading * everything the profile may do; what a profile does not say, it does not * restrict. Configuration, not enforcement: the resolver compiles it * onto the session's narrowing fields and the entry points keep enforcing. * Deliberately not named a View, which per the view convention is a * handle an agent holds on one entry point, while a profile is what the * embedder uses to *define* one. Immutable by type, so two agents with * the same profile share one object and neither can bend the other's view. * * Two rules decide a line against it, and they are the whole law. A * rule naming no path is read by verb (deny before ask before allow), * wherever it is written. A rule carrying paths, and every hide, is * read by anchor depth: the deeper entry wins, ties break by verb. * * `mounts` is keyed by prefix; a bare mode string is sugar for the * section that carries only a mode. `parseProfileMounts` normalizes * every spelling and the resolver reads only the normalized form. */ export interface SessionProfile { readonly cwd?: string | null; /** * The env entries seeded into the session at creation, in the * workspace `env` grammar: a bare string is an exported literal, and a * `from` entry a managed pointer, so a credential is named here and * fetched, never written as its value. */ readonly env?: Readonly<EnvEntries> | null; readonly mounts?: ReadonlyMap<string, ProfileMount> | null; readonly paths?: PathsBlock | null; readonly vars?: VarsBlock | null; readonly commands?: CommandsBlock | null; /** * The profile's policy: for each `pre` method it answers at * (`preExecute`, `preCommand`, `preVfs`, `preSession`; `pre_execute`, * ... in the document), a list of programs, each with the runtime it * runs on, the shape a `clis` entry has. A program reads the method's * context as JSON and prints its answer: nothing for no opinion, a * deny, an ask, or at `preExecute` a route. They are asked in order and * the first deny wins. The document is optional beside it: a profile * stating only a policy hides nothing, and the policy is its whole * admission policy. */ readonly policy?: ProfilePolicy | null; readonly commandLimits?: Readonly<Record<string, Limit>> | null; /** A bare scope for both `list` and `kill`, or any of the fields. */ readonly processes?: ProcessScope | Partial<ProcessPermissions> | null; /** * Where the session's lines may connect, the workspace's `network` * grammar, held besides the workspace's own; unset leaves the * workspace's alone. */ readonly network?: NetworkSetting | null; } /** * One program of a profile's policy, as the document states it: the * program, and the runtime that runs it. * * `script` is the path form the config loader accepts and loads; code * passes the loaded ScriptSource, so a path still spelled as a string * when the workspace reads it means the config layer never saw it. * `runtime` is a name, or `{name, config}` for one that needs settings, * the shape a `runtimes` entry has. Monty when the program names none; * without `@pydantic/monty` installed, such a program fails closed with * the package's install hint. `env` is the variables the program runs * with beside `MIRAGE_POLICY_CTX`, on any runtime: a literal, or a * pointer read from its source when the program first runs. None of the * session's reach it. */ export interface ProfilePolicySpec { readonly script: ScriptSource | string; readonly runtime?: string | { readonly name: string; readonly config?: Readonly<Record<string, unknown>>; }; readonly env?: Readonly<Record<string, string | SecretRef>>; } /** A profile's policy: each `pre` method's programs, asked in order. */ export type ProfilePolicy = Readonly<Partial<Record<ScriptMethod, readonly ProfilePolicySpec[]>>>; /** * What a profile seeds a new session with; the agent's to change * afterwards, so a stored session keeps its own. */ export interface ProfileSetup { readonly env: Readonly<EnvEntries> | null; readonly cwd: string | null; } /** * What a profile lets a session do, for the policy chain to judge. * `mountModes` is the mode each mount section states (a mount absent * from the map keeps its own); `commands` the admission rules, its own * and its mount sections' in one list; `scripts` its policy programs by * method, which `ScriptPolicy` runs at that method; `processes` whose * processes the session may stop and how many it may hold. */ export interface ProfilePolicies { readonly mountModes: ReadonlyMap<string, MountMode> | null; readonly commands: AdmissionRules | null; readonly scripts: ProfileScripts; readonly commandLimits: Readonly<Record<string, Limit>> | null; readonly processes: ProcessPermissions; /** Where the session's lines may connect besides the workspace's `network`. */ readonly network: NetworkSetting | null; } /** * What an effective profile compiles to: the setup a session starts * from, what exists for it (`visibility`, which the views and the op * boundary read), and what it may do (`policies`, which the policy * chain judges). `hideReasons` are the operator's reasons for grouped * hides, never rendered to the agent; `profile` is the profile's name, * null for a document passed without one, and what the session reports * as its group. */ export interface CompiledProfile { readonly setup: ProfileSetup; readonly visibility: Visibility; readonly policies: ProfilePolicies; readonly hideReasons: readonly HideReason[]; readonly profile: string | null; } /** Validate a `paths:` block. Every entry is absolute or a name pattern. */ export declare function PathsBlock(raw: unknown, where?: string): PathsBlock; export declare function VarsBlock(raw: unknown, where?: string): VarsBlock; export declare function CommandsBlock(raw: unknown, where?: string): CommandsBlock; /** Validate a mount section's `commands:` block (`ask` and `deny` only). */ export declare function MountCommandsBlock(raw: unknown, where?: string): MountCommandsBlock; /** Validate one `mounts.<prefix>` section of a profile. */ export declare function ProfileMount(raw: unknown, root: string, where: string): ProfileMount; /** * Normalize a profile's `mounts` mapping: prefix to its settings, with a * bare mode string as sugar for a section carrying only a mode. A bare * list used to mean "only these mounts" and now means nothing at all, * so it fails loudly rather than quietly dropping the confinement it * used to carry. */ export declare function parseProfileMounts(raw: unknown, where?: string): ReadonlyMap<string, ProfileMount> | null; /** * Validate a profile's `policy` block: per method, a list of programs, * each naming its program and the runtime that runs it, monty when it * names none. * A config file spells the methods and a runtime's settings as the * document does (`pre_command`, `auto_load_from_imports`); code, with * `code` set, as the interface does (`preCommand`, `autoLoadFromImports`). */ export declare function parseProfilePolicy(raw: unknown, where: string, code?: boolean): ProfilePolicy; /** * Validate one policy program, `{script, runtime}`: a profile's, or the * workspace's `route_policy`. `code` reads a runtime's settings as the * interface spells them, as `parseProfilePolicy` does. */ export declare function parseProgram(raw: unknown, at: string, code?: boolean): ProfilePolicySpec; /** * A policy program as it runs: its source, its runtime's name and that * runtime's settings. `profile` is the profile it speaks for, empty for * the workspace's `routePolicy`. Mirrors the Python `compile_program`. * * @throws Error - the script is still a path, which only the config * loader loads. */ export declare function compileProgram(program: ProfilePolicySpec, profile: string): ProfileScript; /** Validate one profile (a `profiles.<name>` block, or an inline document). */ export declare function SessionProfile(raw: unknown, where?: string): SessionProfile; /** * A profile as the document that parses back to it: the shape a * `profiles:` entry has in YAML, which either host reads. Every block is * spelled as the document spells it, a program's runtime settings * included, and a script as its source, language and module flag. * Mirrors Python `profile_to_dict`. */ export declare function profileToDict(profile: SessionProfile): Record<string, unknown>; /** * The profile a document states, as `profileToDict` writes it: a script * written as its source comes back as the ScriptSource code passes. * Mirrors Python `profile_from_dict`. */ export declare function profileFromDict(doc: Readonly<Record<string, unknown>>): SessionProfile; //# sourceMappingURL=profile.d.ts.map