UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

404 lines • 17.7 kB
import { VersionHistory } from '../../cache/versions.ts'; import { type SharedInput } from '../../io/async_line_iterator.ts'; import type { NetworkSetting } from '../../io/network.ts'; import type { AdmissionRules, Decision, HideReason, ProfileScripts } from '../../policy/types.ts'; import { type ProcessPermissions } from '../../process/config.ts'; import { type EnvEntries } from '../../secrets/config.ts'; import type { ShellArray } from '../../shell/array.ts'; import { Terminal, type JobOutput } from '../../shell/console/index.ts'; import type { Descriptor, StreamOwner } from '../../shell/descriptors.ts'; import type { JobTable, JobWaits } from '../../shell/job_table/index.ts'; import type { AliasExpansion } from '../../shell/types.ts'; import type { ShellVar } from '../../shell/variable.ts'; import type { Limit, MountMode } from '../../types.ts'; import { type Visibility } from '../../types.ts'; import { type IdSource } from '../../utils/ids.ts'; import type { StatusWriter } from '../types.ts'; import { type FunctionSite } from './functions.ts'; import { commandsToDict, type DecisionJSON, type ScriptJSON } from './serialize.ts'; /** * Read one entry of a session record, ignoring anything inherited from * `Object.prototype`. Shell names are script-controlled, so a plain * `record[name]` lookup would hand back `Object.prototype` (or one of * its methods) for a name like `__proto__` or `toString`; Python's dicts * have no such shadow, so this guard is the TypeScript side only. */ export declare function sessionEntry<T>(record: Record<string, T>, name: string): T | undefined; /** * Write one entry of a session record as an own property. A plain * `record[name] = value` on the name `__proto__` runs the inherited * setter instead: it silently drops a string value and rewrites the * record's prototype for an array one. */ export declare function setSessionEntry<T>(record: Record<string, T>, name: string, value: T): void; /** * A copy of `record` with a null prototype. Session records (env, * functions, arrays) hold script-controlled names, so they must not * inherit from `Object.prototype`: on a plain object, reading a name * like `toString` hands back an inherited function and assigning * `__proto__` runs the inherited setter instead of storing the value. * With no prototype, every name is an ordinary key. Python's dicts * need no equivalent. */ export declare function ownRecord<T>(record?: Record<string, T>): Record<string, T>; export interface SessionInit { sessionId: string; cwd?: string; logicalCwd?: string | undefined; vars?: Record<string, ShellVar>; createdAt?: number; functions?: Record<string, string>; readonlyFunctions?: Set<string>; exportedFunctions?: Set<string>; lastExitCode?: number; positionalArgs?: string[]; scriptName?: string | null; shellOptions?: Record<string, boolean>; /** * Per-mount mode caps for this session. `null` (the default) means * no restriction: every mount in the workspace is reachable at its own * mode. When provided, a mount absent from the map is invisible * (dispatch / handle_command / Files reject it with a capability error) * and a present mount is narrowed to the weaker of its own mode and * the session's mode. The workspace always implicitly grants its own * infrastructure mounts (implicit scratch root, observer, /dev). */ mountModes?: ReadonlyMap<string, MountMode> | null; /** * What exists for this session: the profile's hides, shows, hidden * variables, process scope and allow list, compiled once. The views * and the op boundary read it; fork carries it, toJSON serializes. * A field left out hides nothing, as `DEFAULT_VISIBILITY` says. */ visibility?: Partial<Visibility>; /** * The operator's reasons for grouped hides: never rendered to the * agent (a reason on ENOENT would confirm the path exists), * persisted so the host's read-back entry points survive a restart. */ hideReasons?: readonly HideReason[]; /** * The session's own command tier (`profiles.<n>.commands` tightened * by the inline document): allow patterns, ask and deny rules. A * durable restriction like visibility, so it persists. */ commands?: AdmissionRules | null; /** * The profile's policy programs by method, run by ScriptPolicy at * that method. A durable restriction like commands, so it persists. */ scripts?: ProfileScripts; /** * The name of the profile the session runs under, null for an * unrestricted session. What an owner-rendering command prints as the * group. Stamped by the profile like script, so it persists. */ profile?: string | null; commandLimits?: Readonly<Record<string, Limit>>; /** * Where the session's lines may connect besides the workspace's * network, from its profile. A durable restriction like commands. */ network?: NetworkSetting | null; processes?: ProcessPermissions; processId?: number | null; shellPid?: number | null; processDepth?: number; shellNesting?: number; /** * The host's standing answers to asked lines (design 3.9): session * state like functions and cwd, persisted, read and written through * the manager by id so a fork shares them, never another session's. */ decisions?: readonly Decision[]; generation?: number; lastBgJobId?: number | null; } /** * Variable records for a plain name/value map. The one conversion from * the shape an embedder speaks (a process environment) to the shape the * session stores. * * Every seeded name is exported, because a process environment is by * definition the exported set: these are the names the embedder means a * child runtime to inherit, and `envSnapshot` hands on only what carries * the attribute. Seeding them plain would leave them visible to `$X` and * invisible to every runtime, which is not what an embedder passing an * env record is asking for. */ export declare function varsFromEnv(env: Record<string, string>): Record<string, ShellVar>; /** * Variable records for a stored session's two halves, the restore side * of `toJSON`. `env` carries every scalar and `attrs` the letter cluster * for the names that have one, so a name in `attrs` alone is bash's * declared-but-unset third state (`export Z`) and restores with no value. * * Not `varsFromEnv`: that one reads a bare record as a *process* * environment and exports all of it, which is right for an embedder * handing over an env record and wrong here, where the attributes were * recorded. Restoring through it promoted every plain `X=hello` to an * exported one on the first reload. */ export declare function varsFromDict(env: Record<string, string>, attrs: Record<string, string>): Record<string, ShellVar>; /** * Variable records for a workspace env block. * * The declaration side of the env plane: a bare string is the literal * short form (exported, like `varsFromEnv`), a mapping is coerced * through `EnvVar`, and a managed entry becomes bash's third * state -- exported, unset -- carrying the pointer as `ManagedRef`. * After this translation the session vars are the only truth the fill * step reads. */ export declare function varsFromEntries(entries: EnvEntries): Record<string, ShellVar>; /** The wire shape `varsToFields` writes and `varsFromFields` reads. */ export interface VarFields { env?: Record<string, string>; var_attrs?: Record<string, string>; managed?: Record<string, { from: string; ref: string; key: string; fetch?: string; }>; } /** * The stored shape of a bare variable table. * * The three keys a stored session writes (`toJSON`): `env` holds the * plain scalars, `var_attrs` the letter clusters, `managed` the * pointers -- and a managed name serializes as its pointer, never its * value, the same rule the session codec states. This exists for the * workspace env template, a variable table with no session around it, * so a snapshot or copy can carry the declaration. */ export declare function varsToFields(table: Record<string, ShellVar>): VarFields; /** * The variable table a `varsToFields` payload restores. * * `varsFromDict` reads the two plain halves; each managed name then * restores declared-but-unfetched, its value forced back to null so a * payload that smuggles one in is discarded rather than trusted -- * exactly how `fromJSON` restores a stored session's vars. */ export declare function varsFromFields(data: VarFields): Record<string, ShellVar>; export declare class SessionState { versions: VersionHistory; sessionId: string; cwd: string; logicalCwd: string | undefined; vars: Record<string, ShellVar>; createdAt: number; functions: Record<string, string>; readonlyFunctions: Set<string>; exportedFunctions: Set<string>; lastExitCode: number; pipeStatus: readonly number[]; functionNames: readonly string[] | null; pipeStatusPending: readonly number[] | null; statusWriter: StatusWriter | null; ids: IdSource; randomState: number | null; randomSeed: string | null; randomLast: number; positionalArgs: string[]; scriptName: string | null; exitTrap: string | null; exitTrapInherited: boolean; errTrap: string | null; returnTrap: string | null; errTrapHidden: boolean; returnTrapHidden: boolean; errTrapRunning: boolean; returnTrapRunning: boolean; trapStatus: number | null; tty: Terminal; jobOutput: JobOutput | null; jobWaits: JobWaits | null; jobTable: JobTable | null; shellOptions: Record<string, boolean>; errexitImmune: boolean; errexitIgnored: boolean; errexitExiting: boolean; localVars: Map<string, ShellVar | null> | null; getoptsPos: number; getoptsOptind: number | null; shopts: Record<string, boolean>; aliases: Record<string, string>; aliasMarks: Map<string, Map<string, string | null>>; expandAliasesMarks: Map<string, boolean>; aliasExpansion: AliasExpansion | null; aliasView: Readonly<Record<string, string>> | null; parseSeq: number; parseCurrent: number; parseRow: number; functionSites: Map<string, FunctionSite>; readonly terminal: StreamOwner; lineOpen: boolean; umask: number; descriptors: Map<number, Descriptor>; execStdout: string | null; execStdoutAppend: boolean; execStdoutInput: SharedInput | null; execStderr: string | null; execStderrAppend: boolean; execStderrInput: SharedInput | null; execStdin: SharedInput | null; execStdinUnreadable: boolean; execStdinIdentity: string | null; borrowed: SharedInput[]; localFrames: Map<string, ShellVar | null>[]; reached: [string, Map<string, ShellVar | null>, ShellVar | null][]; localRandom: (string | null)[]; mountModes: ReadonlyMap<string, MountMode> | null; visibility: Visibility; hideReasons: readonly HideReason[]; commands: AdmissionRules | null; scripts: ProfileScripts; profile: string | null; commandLimits: Readonly<Record<string, Limit>>; network: NetworkSetting | null; terminalOutput: boolean; processes: ProcessPermissions; processId: number | null; shellPid: number | null; processDepth: number; shellNesting: number; decisions: readonly Decision[]; generation: number; lastBgJobId: number | null; constructor(init: SessionInit); /** * Return a copy of this session with `overrides` applied. Mutable * containers (env, functions, readonlyVars, arrays, positionalArgs) * are shallow-copied so mutations on the fork do not leak back into * the source. Every field — including capability fields like * `mountModes` — is propagated, so callers cannot accidentally * forget one when adding new fields. * * A caller that moves the fork with `cwd` supplies a physical path * with no typed spelling behind it, so the source's logical name is * dropped rather than left describing where the fork is not — the same * reasoning as `shell_dirs.setCwd`. Deciding it here rather than at * each call site is what keeps `execute({cwd})` from reporting the * persistent session's old directory from `pwd`. `??` cannot express * this, since the value being chosen is `undefined`. */ fork(overrides?: Partial<SessionInit>): SessionState; /** * A child shell of this session: a fork that reads on from here. `fork` * copies what a session keeps; a child shell (a command substitution, a * subshell) also inherits the reader's position, the aliases being * expanded and the local frames, and reseeds `$RANDOM` on its first draw * instead of replaying this session's seed. Mirrors Python. */ subshell(): SessionState; /** * Start `$RANDOM` on a new sequence at its next draw, rather than seed it * from the value the variable holds now. */ private drawAfresh; /** * A new shell started from this session, as a nested `bash` is. bash runs * a nested shell as a program of its own, which inherits the working * directory, the umask, the open files and the environment: the exported * variables, as plain exported strings (no array, no other attribute), and * the functions `export -f` marked. The rest starts as a fresh shell's * does: the other variables and functions, the aliases, the `set` and * `shopt` options, `$?`, `$!`, `$RANDOM`'s sequence, `getopts`'s place, * the call stack, any test its caller is in (`if bash -ec 'false; ...'` * still ends at `false`) and the startup variables, which bash never reads * from its environment: IFS is dropped and `STARTUP_VALUES` restart. A * managed variable not yet fetched crosses as its pointer, which the * nested shell fetches through. Mirrors Python. */ newShell(): SessionState; /** Remove a function with its definition site and export mark. */ removeFunction(name: string): void; /** * What `$0` expands to. Null is the shell itself; a nested `bash`/`sh` * sets it to the script it is running, or to the name given after * `-c`. An empty name is a name, so it is not folded into the default: * GNU `bash -c 'echo "[$0]"' ""` prints `[]`. */ get argv0(): string; /** * The scalar variables, by name. * * A frozen read-only projection of `vars`, not a container: a writer * goes through `SessionView.set` (or `seedVar` when seeding a session * before it is narrowed), so a `pre_session` policy sees every write. * `state.ts` has always documented that rule; freezing the projection * is what stops it being walked around by assigning into storage, and * an assignment into a plain object would land in a throwaway and * vanish -- silent loss is the exact failure this store removes. */ get env(): Readonly<Record<string, string>>; /** The indexed arrays, by name. Read-only, like `env`. */ get arrays(): Readonly<Record<string, ShellArray>>; /** The associative arrays, by name. Read-only, like `env`. */ get assocs(): Readonly<Record<string, Record<string, string>>>; /** The names `readonly` has marked. Read-only, like `env`. */ get readonlyVars(): ReadonlySet<string>; openInputs(): SharedInput[]; /** Close the readers among `inputs` that this shell no longer names and did not inherit. */ releaseInputs(inputs: SharedInput[]): Promise<void>; /** * Close the readers this child shell opened, as it ends; the ones it * inherited stay with the shell that opened them. Mirrors Python's * close_inputs. */ closeInputs(): Promise<void>; /** * The durable-field payload the session table and snapshots persist. * Keys are snake_case, byte-identical to Python's `SessionState.to_dict`, * so both languages can share one store (a py daemon creates the * session, a node kernel tier binds it). */ toJSON(): Record<string, unknown>; toDict(): Record<string, unknown>; static fromDict(data: { session_id: string; cwd?: string; env?: Record<string, string>; var_attrs?: Record<string, string>; managed?: Record<string, { from: string; ref: string; key: string; fetch?: string; }> | null; created_at?: number; mount_modes?: Record<string, MountMode> | null; hidden_paths?: { paths?: string[]; patterns?: string[]; } | null; shown_paths?: { entries?: { path: string; mode?: MountMode; }[]; } | null; hide_reasons?: { patterns?: string[]; reason?: string; }[] | null; hidden_vars?: { names?: string[]; patterns?: string[]; } | null; commands?: ReturnType<typeof commandsToDict> | null; scripts?: Record<string, ScriptJSON[]> | null; profile?: string | null; command_limits?: unknown; network?: NetworkSetting | null; processes?: ProcessPermissions; decisions?: DecisionJSON[] | null; generation?: number; functions?: Record<string, string>; readonly_functions?: string[]; exported_functions?: string[]; }): SessionState; } //# sourceMappingURL=session.d.ts.map