正在載入留言。
正在確認登入狀態。
The World's First Virtual Terminal for AI Agents
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Mirage is a Virtual Terminal for AI Agents. The virtual filesystem delivers broad data context, virtualized CLIs give an agent more flexibility on tool use, dynamic runtimes save underlying infrastructure cost and are more token efficient, and fine-grained control over an agent's actions and even over what it can see gives the best security. Together these parts form one virtualized terminal, giving the best agent performance, cost efficiency and security.
Here is an example of launching Mirage inside an application:檢視完整 README ↗Mirage is a Virtual Terminal for AI Agents: one shell over data, CLIs and runtimes. Unix tools reach every service, and grep runs on each service's own search, such as Gmail's or Slack's. Mirage routes each command to the runtime that fits it, so most lines run in-process and only heavy work reaches a sandbox, which cuts infrastructure cost and tokens. The terminal itself is light: a workspace takes about 100 KB of memory and each new session about 10 KB, so one process serves many agents at once. Profiles decide what an agent can run and even what it can see, and scriptable policies refuse anything those rules cannot express.
Here is an example of launching Mirage inside an application:
ws = Workspace(
{
"/s3": (S3VFS(S3Config(bucket="my-bucket")), MountMode.WRITE),
"/slack": (SlackVFS(SlackConfig(token=slack_bot_token)), MountMode.EXEC),
"/gmail": (GmailVFS(GmailConfig(**google_credentials)), MountMode.READ),
},
# Install the Slack CLI, run by name and listed by `man` and `which` like any program
clis={"slack": (SLACK, {"token": slack_bot_token})},
# the analyst profile hides /s3/secrets and denies posting to Slack and curl
profiles={
"analyst": {
"paths": {"hide": ["/s3/secrets"]},
"commands": {"deny": ["slack send-message", "curl"]},
},
},
runtimes=[
MontyRuntime(captures=["python", "python3"]),
E2BRuntime(
captures=["python", "python3"],
config={"sandbox_id": sandbox_id, "mount_type": "ssh"},
),
],
# route the underlying computation: numpy lines run in the e2b sandbox, the rest on monty
route_policy=lambda ctx: "e2b" if "numpy" in ctx.line else None,
)
# Add an agent using the analyst profile
alice = await ws.session("alice", profile="analyst")
# one grep sweeps S3, Slack and Gmail, on each service's own search where it has one
await alice.shell("grep -rlw invoice /s3 /slack /gmail")
# run a script that lives in Slack on monty, file the report into S3
await alice.shell("python3 /slack/channels/general__C0123/2026-10-21/files/report__F0456.py > /s3/report.txt")
# numpy is not in monty, so the route policy sends this line to e2b
await alice.shell("python3 -c 'import numpy; print(numpy.arange(10).sum())'")
And what alice sees in the shell:
$ grep -rlw invoice /s3 /slack /gmail
/s3/billing/2026-10.csv
/gmail/INBOX/2026-10-21/Invoice_1042__18f3a9c2b7d1e0f4.gmail.json
$ ls /s3
billing
report.txt
$ cat /s3/secrets/aws.txt
cat: /s3/secrets/aws.txt: No such file or directory
$ slack send-message --channel general --text "report is up"
slack: Permission denied
ls, grep, find and jq.git, slack and ntn are answered by Mirage itself, so an agent drives the service with nothing installed, across different runtimes and machines, and one tool can be virtualized into two or more, each under its own name with its own credentials.allow, ask and deny govern commands and CLIs, while hide and show govern files and folders, so a hidden path is not merely unreadable but absent from the filesystem the agent sees.Everything Mirage "mounts" as one unified virtual filesystem for AI agents. Each service sits side-by-side under a single root and answers the same POSIX semantics.
| VFS | |
|---|---|
| Object Storage | |
| Files and Documents | |
| Messaging and Work | |
| Databases and Data Platforms | |
| Observability | |
| Local and Remote |
Agents reach it through the Python and TypeScript SDKs, the mirage CLI, or a real
mountpoint over FUSE and FSKit, then work it with the unix tools they already know,
like ls, grep, find and jq.
These command line tools are virtualized: Mirage answers git, slack or ntn
itself, so an agent drives the service without that program being installed on the
machine. Each one mimics the real tool, so an agent that knows the CLI needs nothing
new. Because they are virtual, the same tool can be installed more than once under
different names, each with its own credentials, so every agent gets exactly the
accounts it is given.
| CLIs | |
|---|---|
| Code | |
| Communication | |
| Work and Data |
Runtimes are virtualized the same way, and not only for coding languages. Any
command on the line can be redirected to a configured runtime, so Python might
run in-process with Monty while
another command, say kubectl, is sent to a remote machine over SSH. Which runtime
serves a given line can be decided by a
scripted runtime router.
| Runtimes | |
|---|---|
| Python | |
| JavaScript | |
| Sandboxes |
A profile decides what a session may run and what it may see. Commands are governed
by customizable allow, ask and deny rules, and paths by hide and show, so a
hidden file is not merely unreadable but absent from the filesystem the agent sees.
For anything those rules cannot express, a profile can name a policy script that runs
at the gate on every command and answers allow, deny or ask itself, though like every
rule it can only restrict and never grant. Separately, a host can register its own
policies on the policy engine, an
ordered stack the workspace consults on every command, VFS op and session write. See
the policy docs.
Credentials and authentication integrate with the stores secrets already live in, including AWS Secrets Manager, 1Password, Auth0 and dotenv, so an environment variable in Mirage can resolve straight to a credential held in one of them.
| Sources | |
|---|---|
| Built in | |
| Custom |
mirage-ai package and the mirage CLIuv add mirage-ai # installs the `mirage` library and the `mirage` CLI binary
uv add 'mirage-ai[openai]' # OpenAI Agents adapter; each agent framework is an extra
npm install @struktoai/mirage-node # Node.js servers and CLIs
npm install @struktoai/mirage-browser # browser / edge runtimes
npm install @struktoai/mirage-vercel # Vercel AI adapter; each agent framework has its own package
Both runtime packages pull in @struktoai/mirage-core automatically.
npm install -g @struktoai/mirage-cli
# or
uvx --from mirage-ai mirage
# or
npx @struktoai/mirage-cli
To get started, see the quickstarts for Python, TypeScript and the CLI.
A profile can name policy scripts for what its rules cannot express. A script
reads the command as JSON, can open the files it names, and prints a deny, an
ask or nothing. This one disables rm, and a Python script whose code
deletes files:
import json, os
ctx = json.loads(os.environ["MIRAGE_POLICY_CTX"])
def deletes(path):
try:
code = open(path).read()
except OSError:
return False
return any(call in code for call in ("os.remove", "os.unlink", "shutil.rmtree"))
if ctx["command"] == "rm" or (
ctx["command"].startswith("python") and any(deletes(p) for p in ctx["paths"])
):
print(json.dumps({"kind": "deny", "reason": "deleting files is disabled"}))
Register it in a profile, here to run on Monty before every command:
ws = Workspace(
{"/s3": (S3VFS(S3Config(bucket="my-bucket")), MountMode.WRITE)},
profiles={
"analyst": {
"policy": {
"pre_command": [{"script": ScriptSource(Path("guard.py").read_text()), "runtime": "monty"}],
},
},
},
profile="analyst",
)
$ cat /s3/clean.py
import os
os.remove("/s3/report.txt")
$ rm /s3/report.txt
rm: Permission denied
$ python3 /s3/clean.py
python3: Permission denied
See policy scripts for the other stages a script can answer at.
Most of the time an agent does not need a sandbox, only a terminal-like interface, and Mirage embeds one in an application server. Each terminal is a Mirage session with its own working directory, environment and history, and Python runs in-process on Monty, so a terminal starts with no container or VM. Here it is in Express; the FastAPI version is in the docs.
npm install @struktoai/mirage-node @pydantic/monty express
import express from 'express'
import { MontyRuntime, MountMode, RAMVFS, Workspace } from '@struktoai/mirage-node'
const ws = new Workspace(
{ '/work': new RAMVFS() },
{ mode: MountMode.EXEC, runtimes: [new MontyRuntime({ captures: ['python', 'python3'] })] },
)
const app = express()
app.use(express.json())
app.post('/sessions/:id', (req, res) => {
ws.createSession(req.params.id)
res.sendStatus(201)
})
app.post('/sessions/:id/run', async (req, res) => {
const r = await ws.shell(req.body.command, { sessionId: req.params.id })
res.json({ stdout: r.stdoutText, stderr: r.stderrText, exitCode: r.exitCode })
})
app.listen(3000, '127.0.0.1')
Thanks to everyone who has contributed to Mirage.