@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
152 lines • 7.52 kB
TypeScript
import { type Files } from '../files.ts';
import type { Decisions } from '../../policy/decisions.ts';
import type { MountEntry } from '../mount/mount.ts';
import type { SessionState } from '../session/session.ts';
import type { ExecuteResult, SessionExecuteOptions } from '../workspace/types.ts';
import { FileVersionTracker } from './file_version.ts';
import { type WorkspaceMediaRead } from './read_file.ts';
export interface ToolResult {
[key: string]: unknown;
content: {
type: 'text';
text: string;
}[];
isError?: boolean;
}
/** What a tool table acts through: one session's entry points (`Session`). */
export interface SessionLike {
readonly sessionId: string;
readonly state: SessionState;
readonly decisions: Decisions;
readonly vfs: Files;
mounts(): readonly MountEntry[];
shell(command: string, options?: SessionExecuteOptions): Promise<ExecuteResult>;
glob(pattern: string): Promise<string[]>;
loaded(): Promise<void>;
reads(): Promise<FileVersionTracker>;
}
/** What an adapter takes to pick a session's tool table. */
export interface MirageToolOperationsOptions {
staleWriteProtection?: boolean;
/**
* The session the tools act as, with its cwd, environment and mount
* grants; the workspace's default session when absent.
*/
sessionId?: string;
}
export declare function ensureParents(vfs: Files, path: string): Promise<void>;
/** Every tool, in the order the entry points list them. */
export declare const TOOL_NAMES: readonly ["shell", "read", "write", "edit", "ls", "grep", "glob"];
/**
* Whether a session may write anywhere: a mount it can see whose mode,
* narrowed by the profile or opened by a show entry below it, reaches
* write. `/dev` is left out: its null sink takes a write from anyone and
* stores nothing. Mirrors Python's `writes`.
*/
export declare function writes(session: SessionState, mounts: readonly MountEntry[]): boolean;
/**
* The agent tools for one session, independent of any agent framework.
* `session.tools` is the session's own table. Every guarded table of a
* session shares the session's read history, so a read through one
* guards a write through another. Build one directly only to turn the
* guard off.
*/
export declare class MirageToolOperations {
private readonly session;
private readonly own;
/**
* @param session The session the tools act as, with its cwd,
* environment and mount grants.
* @param staleWriteProtection False lets an agent overwrite a file that
* changed since it read it.
*/
constructor(session: SessionLike, staleWriteProtection?: boolean);
/**
* The read history this call uses: the session's, which every guarded
* table of the session shares, or this table's own when the guard is
* off. A call keeps the one it started with, so a restore during the
* call cannot mix two histories.
*/
private versions;
/**
* The tools this session can use, in the order the entry points list them.
* Read off the session's profile, so no entry point offers a tool every call
* of which would be refused: `shell` needs a command the allow list
* installs, `ls` and `grep` run those commands and need them, and
* `write` and `edit` need somewhere the session may write. `read` and
* `glob` are always offered; what they cannot reach answers as the
* error it is. A session not loaded yet (a stored one before its first
* call) is offered every tool, since its profile is not known here; the
* policies still judge each call, and `offered` loads it first. Mirrors
* Python's `names`.
*/
names(): readonly string[];
/**
* The tools this session can use, its sessions loaded first, so a stored
* session answers with its own profile: what an async entry point lists and
* calls by. Mirrors Python's `offered`.
*/
offered(): Promise<readonly string[]>;
private lineOptions;
/** Run a line in the session's shell; `signal` aborts it, as in-app. */
shell(command: string, signal?: AbortSignal): Promise<ToolResult>;
/** Read a file as line-numbered text; a relative path is taken under the session's cwd, spelled as given. */
read(given: string, offset?: number, limit?: number): Promise<ToolResult>;
/**
* Read a file for an entry point that hands media to the model, in one fetch:
* an image or a PDF comes back as media and counts as seen in full;
* anything else is the `read` answer for the same bytes. Either way
* the file is stamped for a later edit.
*/
readMedia(given: string, offset?: number, limit?: number): Promise<ToolResult | WorkspaceMediaRead>;
private readFailure;
private numbered;
/**
* Write a file; an existing one must have been read in full first. A
* new file is created with its missing parents. An existing one is
* overwritten only when the agent was shown all of it and it did not
* change since, so a write never clobbers text the agent has not seen. A
* relative path is taken under the session's cwd, spelled as given.
*/
write(given: string, content: string): Promise<ToolResult>;
/** Replace a string in an existing file; a relative path is taken under the session's cwd, spelled as given. */
edit(given: string, oldString: string, newString: string, replaceAll?: boolean): Promise<ToolResult>;
ls(path: string, signal?: AbortSignal): Promise<ToolResult>;
/**
* Search recursively for a pattern, as `grep -rn` does. Each option is
* the GNU grep flag of the same name, and the line runs in the
* session's shell, so the search is the shell's own: the same policy,
* push-down and history as typing it. grep exits 1 when nothing
* matched, an empty answer rather than a failure, so only an exit above
* 1 (a bad regex, an unreadable path) is a tool error.
*/
grep(pattern: string, path: string, options?: {
ignoreCase?: boolean | undefined;
fixedStrings?: boolean | undefined;
include?: string | undefined;
context?: number | undefined;
filesWithMatches?: boolean | undefined;
count?: boolean | undefined;
maxCount?: number | undefined;
}, signal?: AbortSignal): Promise<ToolResult>;
/**
* Find files, not directories, whose path matches a pattern. The
* pattern is expanded by `Session.glob`, the shell's own resolver:
* `**` matches any number of directories, and a relative pattern is
* matched under `path`. A symlink to a file counts; a dangling one does
* not, nor does a match the workspace refuses to stat, since nothing
* says what it is. Any other failure propagates rather than pass for a
* short list.
*/
glob(pattern: string, path?: string): Promise<ToolResult>;
/**
* Run one tool by name with its JSON input. The one entry every entry point
* shares: MCP, the HTTP routes, the CLI and the agent adapters hand a
* tool's name and its input, as the tool's `*_INPUT` schema reads it,
* to this method, so each tool answers the same way through each of
* them. Throws for a name no tool has, or one the session's profile does
* not offer (`offered`). Mirrors Python's `call`.
*/
call(name: string, args: Readonly<Record<string, unknown>>, signal?: AbortSignal): Promise<ToolResult>;
}
//# sourceMappingURL=tool_operations.d.ts.map