@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
263 lines • 13.4 kB
TypeScript
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