@struktoai/mirage-agents 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,135 +23,125 @@
23
23
  <img src="https://img.shields.io/npm/v/%40struktoai%2Fmirage-agents.svg?color=0C0C0C&labelColor=FAFAFA"/></a>
24
24
  </p>
25
25
 
26
- Mirage is **a Unified Virtual File System for AI Agents**: a single tree that mounts services and data sources like S3, Google Drive, Slack, Gmail, and Redis side-by-side as one filesystem.
26
+ <p align="center">
27
+ <a href="https://github.com/strukto-ai/mirage#readme"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
28
+ <a href="https://github.com/strukto-ai/mirage/blob/main/readme/README.zh-CN.md"><img alt="简体中文 README" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
29
+ <a href="https://github.com/strukto-ai/mirage/blob/main/readme/README.zh-TW.md"><img alt="繁體中文 README" src="https://img.shields.io/badge/繁體中文-d9d9d9"></a>
30
+ <a href="https://github.com/strukto-ai/mirage/blob/main/readme/README.fr.md"><img alt="README en Français" src="https://img.shields.io/badge/Français-d9d9d9"></a>
31
+ <a href="https://github.com/strukto-ai/mirage/blob/main/readme/README.vi.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
32
+ <a href="https://github.com/strukto-ai/mirage/blob/main/readme/README.ko.md"><img alt="README 한국어" src="https://img.shields.io/badge/%ED%95%9C%EA%B5%AD%EC%96%B4-d9d9d9"></a>
33
+ </p>
27
34
 
28
- AI agents reach every backend with the same handful of Unix-like tools, and pipelines compose across services as naturally as on a local disk. It's a simulated environment, agents see one filesystem underneath. Any LLM that already knows bash can use Mirage out of the box, with zero new vocabulary.
35
+ Mirage is **a Unified Virtual File System for AI Agents**: it mounts services and data sources like S3, Google Drive, Slack, Gmail, and Redis side-by-side as one filesystem. Any LLM that already knows bash can read, grep, and pipe across every backend out of the box, with zero new vocabulary.
29
36
 
30
37
  ```ts
31
38
  const ws = new Workspace({
32
- '/data': new RAMResource(),
33
- '/s3': new S3Resource({ bucket: 'logs' }),
34
- '/slack': new SlackResource({}),
35
- '/github': new GitHubResource({}),
39
+ '/data': new RAMResource(),
40
+ '/s3': new S3Resource({ bucket: 'logs' }),
41
+ '/slack': new SlackResource({ token: process.env.SLACK_BOT_TOKEN! }),
36
42
  })
37
43
 
38
- await ws.execute('grep alert /slack/general/*.json | wc -l')
39
- await ws.execute('cat /github/mirage/README.md')
44
+ await ws.execute('grep -r alert /slack/channels/general__C04QX/ | wc -l')
40
45
  await ws.execute('cp /s3/report.csv /data/local.csv')
46
+ await ws.execute('wc -l $(find /s3/data -name "*.jsonl")')
47
+
48
+ // Commands are extensible: register new commands, or override one per
49
+ // resource + filetype, e.g. `cat` on S3 Parquet renders rows as JSON.
50
+ ws.command('summarize', ...)
51
+ ws.command('cat', { resource: 's3', filetype: 'parquet' }, ...)
52
+
53
+ await ws.execute('summarize /data/local.csv')
54
+ await ws.execute('cat /s3/events/2026-05-06.parquet | jq .user')
41
55
  ```
42
56
 
43
57
  ## About
44
58
 
45
- - **One filesystem, every backend.** Every service speaks the same filesystem semantics, so agents reason about one abstraction instead of N SDKs and M MCPs, leaning on the filesystem and bash vocabulary LLMs are most fluent in.
46
- - **Multiple resources, one filesystem:** RAM, Disk, Redis, S3 / R2 / OCI / Supabase / GCS, Gmail / GDrive / GDocs / GSheets / GSlides, GitHub / Linear / Notion / Trello, Slack / Discord / Telegram / Email, MongoDB, SSH, and more, mounted side-by-side under a single root.
47
- - **Familiar bash tools across every mount.** Agents reuse the same handful of Unix-like tools instead of learning a new API per service, and pipelines compose across services as naturally as on a local disk, the exact corpus modern LLMs are most heavily trained on.
48
- - **Portable workspaces:** clone, snapshot, and version your environment. Move agent runs between machines without restarting or reconfiguring the system.
49
- - **Embed in your apps and services:** Python and TypeScript SDKs let you give your AI agents a virtual filesystem directly inside FastAPI, Express, browser apps, or any async runtime, no separate process required. Clone, snapshot, and version the workspace from inside your code.
50
- - **Works with major agent application frameworks:** OpenAI Agents SDK, Vercel AI SDK (TypeScript), LangChain, Pydantic AI, CAMEL, and OpenHands.
51
- - **Lightweight CLI + daemon:** plugs into coding agents like Claude Code and Codex so they reach every mounted resource through familiar bash, getting more useful work done per turn.
59
+ - **One interface instead of N SDKs and M MCPs.** Every service speaks the same filesystem semantics, and pipelines compose across services as naturally as on a local disk.
60
+ - **Around 50 built-in backends:** RAM, Disk, Redis, S3 / R2 / OCI / Supabase / GCS, Gmail / GDrive / GDocs / GSheets / GSlides, GitHub / Linear / Notion / Trello, Slack / Discord / Telegram / Email, MongoDB / Postgres / LanceDB, SSH, and more, mounted side-by-side under a single root.
61
+ - **Portable workspaces:** clone, snapshot, and version a workspace; agent runs move between machines without restarting or reconfiguring the system.
62
+ - **Embeddable:** the Python and TypeScript SDKs run in-process inside FastAPI, Express, browser apps, or any async runtime; no separate process required.
63
+ - **Agent integrations:** OpenAI Agents SDK, Vercel AI SDK, LangChain, Pydantic AI, CAMEL, and OpenHands via the SDKs; coding agents like Claude Code and Codex via the lightweight CLI + daemon.
52
64
 
53
65
  ## Architecture
54
66
 
55
67
  <p align="center">
56
68
  <picture>
57
69
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/strukto-ai/mirage/main/assets/mirage-arch-dark.svg">
58
- <img src="https://raw.githubusercontent.com/strukto-ai/mirage/main/assets/mirage-arch-light.svg" alt="Mirage architecture: AI Agent and Application → Mirage Bash and VFS → Dispatcher &amp; Cache → Infrastructure and Remote" width="900">
70
+ <img src="https://raw.githubusercontent.com/strukto-ai/mirage/main/assets/mirage-arch-light.svg" alt="Mirage architecture: AI Agent and Application → Mirage Bash and VFS → Dispatcher &amp; Cache → Infrastructure and Remote" width="720">
59
71
  </picture>
60
72
  </p>
61
73
 
62
74
  ## Installation
63
75
 
64
- ### Prerequisites
65
-
66
- - **Python** ≥ 3.12 for the `mirage-ai` package and the `mirage` CLI
76
+ - **Python** ≥ 3.11 for the `mirage-ai` package and the `mirage` CLI
67
77
  - **Node.js** ≥ 20 for the TypeScript SDK
68
78
  - **macOS** or **Linux** (FUSE-based mounts require platform support)
69
79
 
70
80
  ### Python
71
81
 
72
82
  ```bash
73
- uv add mirage-ai
83
+ uv add mirage-ai # installs the `mirage` library and the `mirage` CLI binary
74
84
  ```
75
85
 
76
- This installs both the `mirage` library and the `mirage` CLI binary.
77
-
78
86
  ### TypeScript
79
87
 
80
- Pick the package that matches your runtime:
81
-
82
88
  ```bash
83
89
  npm install @struktoai/mirage-node # Node.js servers and CLIs
84
90
  npm install @struktoai/mirage-browser # browser / edge runtimes
85
- npm install @struktoai/mirage-core # runtime-agnostic primitives
91
+ npm install @struktoai/mirage-agents # OpenAI / Vercel AI / LangChain / Mastra adapters
86
92
  ```
87
93
 
88
- `@struktoai/mirage-node` and `@struktoai/mirage-browser` both pull in `@struktoai/mirage-core` automatically.
94
+ Both runtime packages pull in `@struktoai/mirage-core` automatically.
89
95
 
90
96
  ### CLI
91
97
 
92
98
  ```bash
93
- curl -fsSL https://strukto.ai/install.sh | sh
94
- ```
95
-
96
- Or via your package manager of choice:
97
-
98
- ```bash
99
- brew install mirage
100
- ```
101
-
102
- ```bash
99
+ curl -fsSL https://strukto.ai/mirage/install.sh | sh
100
+ # or
101
+ npm install -g @struktoai/mirage-cli
102
+ # or
103
103
  uvx mirage-ai
104
- ```
105
-
106
- ```bash
104
+ # or
107
105
  npx @struktoai/mirage-cli
108
106
  ```
109
107
 
110
- ## Quickstart (Python)
108
+ ## Quickstart
109
+
110
+ ### Python
111
111
 
112
112
  ```python
113
113
  from mirage import Workspace
114
- from mirage.resource.gdocs import GDocsConfig, GDocsResource
115
114
  from mirage.resource.ram import RAMResource
116
115
  from mirage.resource.s3 import S3Config, S3Resource
117
- from mirage.resource.slack import SlackConfig, SlackResource
118
116
 
119
117
  ws = Workspace({
120
- "/data": RAMResource(),
121
- "/s3": S3Resource(S3Config(bucket="my-bucket")),
122
- "/slack": SlackResource(SlackConfig()),
123
- "/docs": GDocsResource(GDocsConfig()),
118
+ "/data": RAMResource(),
119
+ "/s3": S3Resource(S3Config(bucket="my-bucket")),
124
120
  })
125
121
 
126
122
  await ws.execute("cp /s3/report.csv /data/report.csv")
127
123
  await ws.execute("grep alert /s3/data/log.jsonl | wc -l")
128
124
 
129
- ws.snapshot("demo.tar")
125
+ await ws.snapshot("demo.tar")
130
126
  ```
131
127
 
132
- ## Quickstart (TypeScript)
128
+ ### TypeScript
133
129
 
134
130
  ```ts
135
- import {
136
- Workspace,
137
- RAMResource,
138
- S3Resource,
139
- SlackResource,
140
- GDocsResource,
141
- } from '@struktoai/mirage-browser'
131
+ import { Workspace, RAMResource, S3Resource } from '@struktoai/mirage-node'
142
132
 
143
133
  const ws = new Workspace({
144
134
  '/data': new RAMResource(),
145
135
  '/s3': new S3Resource({ bucket: 'my-bucket' }),
146
- '/slack': new SlackResource({}),
147
- '/docs': new GDocsResource({}),
148
136
  })
149
137
 
150
138
  await ws.execute('cp /s3/report.csv /data/report.csv')
151
139
  await ws.execute('grep alert /s3/data/log.jsonl | wc -l')
140
+
141
+ await ws.snapshot('demo.tar')
152
142
  ```
153
143
 
154
- ## Quickstart (CLI)
144
+ ### CLI
155
145
 
156
146
  ```bash
157
147
  mirage workspace create ws.yaml --id demo
@@ -163,89 +153,33 @@ mirage workspace load demo.tar --id demo-restored
163
153
 
164
154
  ## Agent Frameworks
165
155
 
166
- Mirage drops into the major agent application frameworks as a sandbox or tool layer. Your agent runs against the same mount tree it would in bash, so swapping the model or runtime never changes the surface.
167
-
168
- ### OpenAI Agents SDK (Python)
156
+ Mirage plugs into agent frameworks as a sandbox or tool layer. POSIX operations such as `read` can also be customized per resource and filetype, e.g. reading a PDF returns parsed pages instead of raw bytes.
169
157
 
170
- The `MirageSandboxClient` plugs a `Workspace` into the OpenAI Agents SDK as a sandbox: bash commands the agent runs execute against your mounts.
171
-
172
- ```python
173
- from agents import Runner
174
- from agents.run import RunConfig
175
- from agents.sandbox import SandboxAgent, SandboxRunConfig
176
-
177
- from mirage.agents.openai_agents import MirageSandboxClient
178
-
179
- client = MirageSandboxClient(ws)
180
- agent = SandboxAgent(
181
- name="Mirage Sandbox Agent",
182
- model="gpt-5.4-nano",
183
- instructions=ws.file_prompt,
184
- )
185
-
186
- result = await Runner.run(
187
- agent,
188
- "Summarize /s3/data/report.parquet into /report.txt.",
189
- run_config=RunConfig(sandbox=SandboxRunConfig(client=client)),
190
- )
191
- ```
192
-
193
- ### Vercel AI SDK (TypeScript)
194
-
195
- `mirageTools(ws)` exposes the workspace as a typed AI SDK tool set, so any model wired into the AI SDK can read and write across mounts, in Node or the browser.
196
-
197
- ```ts
198
- import { generateText } from 'ai'
199
- import { openai } from '@ai-sdk/openai'
200
- import { mirageTools } from '@struktoai/mirage-agents/vercel'
201
- import { buildSystemPrompt } from '@struktoai/mirage-agents/openai'
202
-
203
- const { text } = await generateText({
204
- model: openai('gpt-5.4-nano'),
205
- system: buildSystemPrompt({ mountInfo: { '/': 'In-memory filesystem' } }),
206
- prompt: "Use readFile to read /docs/paper.pdf, then describe what's in it.",
207
- tools: mirageTools(ws),
208
- })
209
- ```
210
-
211
- LangChain, Pydantic AI, CAMEL, OpenHands, and Mastra adapters live alongside these.
158
+ | | Integrations |
159
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
+ | Python | [OpenAI Agents SDK](https://docs.mirage.strukto.ai/python/agents/openai-agents), [LangChain](https://docs.mirage.strukto.ai/python/agents/langchain), [Pydantic AI](https://docs.mirage.strukto.ai/python/agents/pydantic-ai), [CAMEL](https://docs.mirage.strukto.ai/python/agents/camel), [OpenHands](https://docs.mirage.strukto.ai/python/agents/openhands), [Agno](https://docs.mirage.strukto.ai/python/agents/agno) |
161
+ | TypeScript | [Vercel AI SDK](https://docs.mirage.strukto.ai/typescript/agents/vercel), [OpenAI Agents SDK](https://docs.mirage.strukto.ai/typescript/agents/openai), [LangChain](https://docs.mirage.strukto.ai/typescript/agents/langchain), [Mastra](https://docs.mirage.strukto.ai/typescript/agents/mastra) |
162
+ | Coding agents | [Claude Code](https://docs.mirage.strukto.ai/python/agents/claude-code), [Codex](https://docs.mirage.strukto.ai/python/agents/codex), [OpenCode](https://docs.mirage.strukto.ai/typescript/agents/opencode), [Pi](https://docs.mirage.strukto.ai/typescript/agents/pi) |
212
163
 
213
164
  ## Cache
214
165
 
215
- Every `Workspace` ships with a **two-layer cache** so repeated work against remote backends (S3, GDrive, Slack, …) hits local state instead of the network:
166
+ Every `Workspace` has a two-layer cache so repeated work against remote backends hits local state instead of the network:
216
167
 
217
- - **Index cache.** Listings and metadata. The first directory walk hits the API; subsequent ones serve from the index until TTL expires.
218
- - **File cache.** Object bytes. The first read streams from origin; later pipelines read from cache.
219
- - **Pluggable backends.** Each layer is a store with two built-ins:
220
- - **RAM** (default): in-process, zero setup, 512 MB file cache and 10-minute index TTL. Best for single-process apps and notebooks.
221
- - **Redis**: shared across workers, processes, and machines. Best for serverless, multi-replica services, or when you want cache state to survive restarts.
168
+ - **Index cache:** listings and metadata. The first directory walk hits the API; later ones serve from the index until the TTL expires (default 10 minutes).
169
+ - **File cache:** object bytes. The first read streams from origin; later pipelines read from cache (default 512 MB).
170
+
171
+ Both layers default to in-process RAM with zero setup. A Redis store shares cache state across workers, processes, and machines:
222
172
 
223
173
  ```ts
224
- import { RedisFileCacheStore, RedisIndexCacheStore, Workspace } from 'mirage/node'
174
+ import { RedisFileCacheStore, S3Resource, Workspace } from '@struktoai/mirage-node'
225
175
 
226
176
  const ws = new Workspace(
227
177
  { '/s3': new S3Resource({ bucket: 'my-bucket' }) },
228
178
  {
229
- cache: new RedisFileCacheStore({ url: 'redis://localhost:6379/0', limit: '8GB' }),
230
- index: new RedisIndexCacheStore({ url: 'redis://localhost:6379/0', ttl: 600 }),
179
+ cache: new RedisFileCacheStore({ url: 'redis://localhost:6379/0', cacheLimit: '8GB' }),
180
+ index: { type: 'redis', url: 'redis://localhost:6379/0', ttl: 600 },
231
181
  },
232
182
  )
233
183
  ```
234
184
 
235
- ```ts
236
- import { S3Resource, Workspace } from 'mirage/node'
237
-
238
- const ws = new Workspace({ '/s3': new S3Resource({ bucket: 'my-bucket' }) })
239
-
240
- // 1. Index miss → S3 LIST. Listing stored in index cache.
241
- await ws.execute('ls /s3/data/')
242
-
243
- // 2. Index hit → 0 network calls.
244
- await ws.execute('find /s3/data/ -name "*.jsonl"')
245
-
246
- // 3. File miss → S3 GET. Bytes stored in file cache.
247
- await ws.execute('cat /s3/data/log.jsonl | wc -l')
248
-
249
- // 4. File hit → 0 network calls.
250
- await ws.execute('grep alert /s3/data/log.jsonl')
251
- ```
185
+ See the [cache docs](https://docs.mirage.strukto.ai/home/cache) for the full miss/hit lifecycle.
@@ -46,6 +46,7 @@ function ioToFileInfos(io) {
46
46
  }
47
47
 
48
48
  // src/langchain/backend.ts
49
+ import { gnuDirname } from "@struktoai/mirage-core";
49
50
  var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
50
51
  "txt",
51
52
  "md",
@@ -89,15 +90,9 @@ function shellQuote(s) {
89
90
  if (/^[A-Za-z0-9_./-]+$/.test(s)) return s;
90
91
  return `'${s.replaceAll(`'`, `'\\''`)}'`;
91
92
  }
92
- function parentOf(path) {
93
- const trimmed = path.replace(/\/+$/, "");
94
- const idx = trimmed.lastIndexOf("/");
95
- if (idx <= 0) return "/";
96
- return trimmed.slice(0, idx);
97
- }
98
93
  async function ensureParent(ws, path) {
99
- const parent = parentOf(path);
100
- if (parent === "/" || parent === "") return;
94
+ const parent = gnuDirname(path);
95
+ if (parent === "/" || parent === "" || parent === ".") return;
101
96
  if (await ws.fs.exists(parent)) return;
102
97
  await ensureParent(ws, parent);
103
98
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/langchain/convert.ts","../../src/langchain/backend.ts","../../src/langchain/messages.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { ExecuteResponse, FileInfo, GrepMatch } from 'deepagents'\n\ninterface IOLike {\n stdoutText: string\n stderrText: string\n exitCode: number | null\n}\n\nexport function ioToExecuteResponse(io: IOLike): ExecuteResponse {\n const stdout = io.stdoutText\n const stderr = io.stderrText\n let output = stdout\n if (stderr.length > 0) {\n output = stdout.length > 0 ? `${stdout}\\n${stderr}` : stderr\n }\n return { output, exitCode: io.exitCode, truncated: false }\n}\n\nexport function ioToGrepMatches(io: IOLike): GrepMatch[] {\n const stdout = io.stdoutText.trim()\n if (stdout.length === 0) return []\n const matches: GrepMatch[] = []\n for (const line of stdout.split('\\n')) {\n const firstColon = line.indexOf(':')\n if (firstColon < 0) continue\n const secondColon = line.indexOf(':', firstColon + 1)\n if (secondColon < 0) continue\n const path = line.slice(0, firstColon)\n const lineNumStr = line.slice(firstColon + 1, secondColon)\n const text = line.slice(secondColon + 1)\n const lineNum = Number.parseInt(lineNumStr, 10)\n if (Number.isNaN(lineNum)) continue\n matches.push({ path, line: lineNum, text })\n }\n return matches\n}\n\nexport function ioToFileInfos(io: IOLike): FileInfo[] {\n const stdout = io.stdoutText.trim()\n if (stdout.length === 0) return []\n const infos: FileInfo[] = []\n for (const raw of stdout.split('\\n')) {\n const entry = raw.trim()\n if (entry.length === 0) continue\n const isDir = entry.endsWith('/')\n infos.push({ path: isDir ? entry.slice(0, -1) : entry, is_dir: isDir })\n }\n return infos\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-node'\nimport type {\n EditResult,\n ExecuteResponse,\n FileData,\n FileDownloadResponse,\n FileInfo,\n FileUploadResponse,\n GlobResult,\n GrepResult,\n LsResult,\n ReadRawResult,\n ReadResult,\n SandboxBackendProtocolV2 as SandboxBackendProtocol,\n WriteResult,\n} from 'deepagents'\nimport { ioToExecuteResponse, ioToFileInfos, ioToGrepMatches } from './convert.ts'\n\nconst TEXT_EXTENSIONS = new Set([\n 'txt',\n 'md',\n 'json',\n 'jsonl',\n 'yaml',\n 'yml',\n 'csv',\n 'tsv',\n 'xml',\n 'html',\n 'htm',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'fish',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nfunction shellQuote(s: string): string {\n if (/^[A-Za-z0-9_./-]+$/.test(s)) return s\n return `'${s.replaceAll(`'`, `'\\\\''`)}'`\n}\n\nfunction parentOf(path: string): string {\n const trimmed = path.replace(/\\/+$/, '')\n const idx = trimmed.lastIndexOf('/')\n if (idx <= 0) return '/'\n return trimmed.slice(0, idx)\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = parentOf(path)\n if (parent === '/' || parent === '') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n if (dot < 0) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nconst MIRAGE_RENDERED_AS_TEXT = new Set(['parquet', 'h5', 'hdf5', 'feather'])\n\nfunction mimeFor(path: string): string {\n const ext = extOf(path)\n if (ext === 'json' || ext === 'jsonl') return 'application/json'\n if (ext === 'csv') return 'text/csv'\n if (ext === 'html' || ext === 'htm') return 'text/html'\n if (ext === 'md') return 'text/markdown'\n if (TEXT_EXTENSIONS.has(ext)) return 'text/plain'\n if (MIRAGE_RENDERED_AS_TEXT.has(ext)) return 'text/plain'\n if (ext === 'png') return 'image/png'\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n if (ext === 'pdf') return 'application/pdf'\n return 'application/octet-stream'\n}\n\nfunction isTextMime(mimeType: string): boolean {\n return mimeType.startsWith('text/') || mimeType === 'application/json'\n}\n\nconst ANTHROPIC_BINARY_MIMES = new Set([\n 'application/pdf',\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n])\n\nexport interface LangchainWorkspaceOptions {\n sandboxId?: string\n}\n\nexport class LangchainWorkspace implements SandboxBackendProtocol {\n readonly id: string\n private readonly ws: Workspace\n\n constructor(workspace: Workspace, options: LangchainWorkspaceOptions = {}) {\n this.ws = workspace\n this.id = options.sandboxId ?? 'mirage'\n }\n\n async execute(command: string): Promise<ExecuteResponse> {\n const io = await this.ws.execute(command)\n return ioToExecuteResponse(io)\n }\n\n async ls(path: string): Promise<LsResult> {\n let entries: string[]\n try {\n entries = await this.ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: FileInfo[] = []\n for (const entry of entries) {\n const isDir = await this.ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n }\n\n async read(filePath: string, offset = 0, limit = 500): Promise<ReadResult> {\n const mimeType = mimeFor(filePath)\n let bytes: Uint8Array\n try {\n bytes = await this.ws.fs.readFile(filePath)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n if (!isTextMime(mimeType)) {\n if (ANTHROPIC_BINARY_MIMES.has(mimeType)) {\n return { content: bytes, mimeType }\n }\n return {\n error: `Binary file '${filePath}' (${mimeType}, ${String(bytes.length)} bytes). Use shell commands (head, file, wc, od) via execute() to inspect.`,\n }\n }\n const text = new TextDecoder('utf-8').decode(bytes)\n const lines = text.split('\\n')\n if (offset >= lines.length) {\n return {\n error: `Line offset ${String(offset)} exceeds file length (${String(lines.length)} lines)`,\n }\n }\n const end = Math.min(offset + limit, lines.length)\n return { content: lines.slice(offset, end).join('\\n'), mimeType }\n }\n\n async readRaw(filePath: string): Promise<ReadRawResult> {\n let stat: Awaited<ReturnType<Workspace['fs']['stat']>>\n let bytes: Uint8Array\n try {\n stat = await this.ws.fs.stat(filePath)\n bytes = await this.ws.fs.readFile(filePath)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const mimeType = mimeFor(filePath)\n const content: string | Uint8Array = isTextMime(mimeType)\n ? new TextDecoder('utf-8').decode(bytes)\n : bytes\n const modified = stat.modified ?? new Date().toISOString()\n const data: FileData = {\n content,\n mimeType,\n created_at: modified,\n modified_at: modified,\n }\n return { data }\n }\n\n async write(filePath: string, content: string): Promise<WriteResult> {\n if (await this.ws.fs.exists(filePath)) {\n return { error: `Error: file '${filePath}' already exists` }\n }\n await ensureParent(this.ws, filePath)\n await this.ws.fs.writeFile(filePath, content)\n return { path: filePath }\n }\n\n async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll = false,\n ): Promise<EditResult> {\n let current: string\n try {\n current = await this.ws.fs.readFileText(filePath)\n } catch {\n return { error: `Error: file '${filePath}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && !replaceAll) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next = replaceAll\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await this.ws.fs.writeFile(filePath, next)\n return { path: filePath, occurrences: replaceAll ? count : 1 }\n }\n\n async grep(pattern: string, path?: string | null, glob?: string | null): Promise<GrepResult> {\n const parts: string[] = ['grep', '-rn']\n if (glob !== undefined && glob !== null && glob.length > 0) {\n parts.push('--include', shellQuote(glob))\n }\n parts.push(shellQuote(pattern))\n parts.push(shellQuote(path ?? '/'))\n const io = await this.ws.execute(parts.join(' '))\n return { matches: ioToGrepMatches(io) }\n }\n\n async glob(pattern: string, path = '/'): Promise<GlobResult> {\n const name = pattern.includes('/') ? (pattern.split('/').pop() ?? pattern) : pattern\n const io = await this.ws.execute(`find ${shellQuote(path)} -name ${shellQuote(name)}`)\n return { files: ioToFileInfos(io) }\n }\n\n async uploadFiles(\n files: readonly (readonly [string, Uint8Array])[],\n ): Promise<FileUploadResponse[]> {\n const results: FileUploadResponse[] = []\n for (const [path, data] of files) {\n await ensureParent(this.ws, path)\n await this.ws.fs.writeFile(path, data)\n results.push({ path, error: null })\n }\n return results\n }\n\n async downloadFiles(paths: readonly string[]): Promise<FileDownloadResponse[]> {\n const results: FileDownloadResponse[] = []\n for (const path of paths) {\n try {\n const content = await this.ws.fs.readFile(path)\n results.push({ path, content, error: null })\n } catch {\n results.push({ path, content: null, error: 'file_not_found' })\n }\n }\n return results\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\ninterface MessageLike {\n content?: unknown\n}\n\ninterface ContentBlock {\n type?: string\n text?: string\n}\n\nfunction isTextBlock(value: unknown): value is { type: 'text'; text: string } {\n if (typeof value !== 'object' || value === null) return false\n const block = value as ContentBlock\n return block.type === 'text' && typeof block.text === 'string'\n}\n\nexport function extractText(messages: readonly MessageLike[]): string[] {\n const texts: string[] = []\n for (const msg of messages) {\n if (!('content' in msg) || msg.content === undefined || msg.content === null) continue\n const content = msg.content\n if (typeof content === 'string') {\n if (content.trim().length > 0) texts.push(content)\n continue\n }\n if (Array.isArray(content)) {\n for (const block of content) {\n if (isTextBlock(block) && block.text.trim().length > 0) {\n texts.push(block.text)\n }\n }\n }\n }\n return texts\n}\n"],"mappings":";;;;;;AAsBO,SAAS,oBAAoB,IAA6B;AAC/D,QAAM,SAAS,GAAG;AAClB,QAAM,SAAS,GAAG;AAClB,MAAI,SAAS;AACb,MAAI,OAAO,SAAS,GAAG;AACrB,aAAS,OAAO,SAAS,IAAI,GAAG,MAAM;AAAA,EAAK,MAAM,KAAK;AAAA,EACxD;AACA,SAAO,EAAE,QAAQ,UAAU,GAAG,UAAU,WAAW,MAAM;AAC3D;AAEO,SAAS,gBAAgB,IAAyB;AACvD,QAAM,SAAS,GAAG,WAAW,KAAK;AAClC,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,QAAM,UAAuB,CAAC;AAC9B,aAAW,QAAQ,OAAO,MAAM,IAAI,GAAG;AACrC,UAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,QAAI,aAAa,EAAG;AACpB,UAAM,cAAc,KAAK,QAAQ,KAAK,aAAa,CAAC;AACpD,QAAI,cAAc,EAAG;AACrB,UAAM,OAAO,KAAK,MAAM,GAAG,UAAU;AACrC,UAAM,aAAa,KAAK,MAAM,aAAa,GAAG,WAAW;AACzD,UAAM,OAAO,KAAK,MAAM,cAAc,CAAC;AACvC,UAAM,UAAU,OAAO,SAAS,YAAY,EAAE;AAC9C,QAAI,OAAO,MAAM,OAAO,EAAG;AAC3B,YAAQ,KAAK,EAAE,MAAM,MAAM,SAAS,KAAK,CAAC;AAAA,EAC5C;AACA,SAAO;AACT;AAEO,SAAS,cAAc,IAAwB;AACpD,QAAM,SAAS,GAAG,WAAW,KAAK;AAClC,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,QAAM,QAAoB,CAAC;AAC3B,aAAW,OAAO,OAAO,MAAM,IAAI,GAAG;AACpC,UAAM,QAAQ,IAAI,KAAK;AACvB,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,QAAQ,MAAM,SAAS,GAAG;AAChC,UAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,OAAO,QAAQ,MAAM,CAAC;AAAA,EACxE;AACA,SAAO;AACT;;;AC9BA,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,WAAW,GAAmB;AACrC,MAAI,qBAAqB,KAAK,CAAC,EAAG,QAAO;AACzC,SAAO,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;AACvC;AAEA,SAAS,SAAS,MAAsB;AACtC,QAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE;AACvC,QAAM,MAAM,QAAQ,YAAY,GAAG;AACnC,MAAI,OAAO,EAAG,QAAO;AACrB,SAAO,QAAQ,MAAM,GAAG,GAAG;AAC7B;AAEA,eAAe,aAAa,IAAe,MAA6B;AACtE,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,WAAW,OAAO,WAAW,GAAI;AACrC,MAAI,MAAM,GAAG,GAAG,OAAO,MAAM,EAAG;AAChC,QAAM,aAAa,IAAI,MAAM;AAC7B,MAAI;AACF,UAAM,GAAG,GAAG,MAAM,MAAM;AAAA,EAC1B,SAAS,KAAK;AACZ,QAAI,CAAE,MAAM,GAAG,GAAG,OAAO,MAAM,EAAI,OAAM;AAAA,EAC3C;AACF;AAEA,SAAS,MAAM,MAAsB;AACnC,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,KAAK,MAAM,MAAM,CAAC,EAAE,YAAY;AACzC;AAEA,IAAM,0BAA0B,oBAAI,IAAI,CAAC,WAAW,MAAM,QAAQ,SAAS,CAAC;AAE5E,SAAS,QAAQ,MAAsB;AACrC,QAAM,MAAM,MAAM,IAAI;AACtB,MAAI,QAAQ,UAAU,QAAQ,QAAS,QAAO;AAC9C,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,UAAU,QAAQ,MAAO,QAAO;AAC5C,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,gBAAgB,IAAI,GAAG,EAAG,QAAO;AACrC,MAAI,wBAAwB,IAAI,GAAG,EAAG,QAAO;AAC7C,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,SAAS,QAAQ,OAAQ,QAAO;AAC5C,MAAI,QAAQ,MAAO,QAAO;AAC1B,SAAO;AACT;AAEA,SAAS,WAAW,UAA2B;AAC7C,SAAO,SAAS,WAAW,OAAO,KAAK,aAAa;AACtD;AAEA,IAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,qBAAN,MAA2D;AAAA,EACvD;AAAA,EACQ;AAAA,EAEjB,YAAY,WAAsB,UAAqC,CAAC,GAAG;AACzE,SAAK,KAAK;AACV,SAAK,KAAK,QAAQ,aAAa;AAAA,EACjC;AAAA,EAEA,MAAM,QAAQ,SAA2C;AACvD,UAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,OAAO;AACxC,WAAO,oBAAoB,EAAE;AAAA,EAC/B;AAAA,EAEA,MAAM,GAAG,MAAiC;AACxC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,GAAG,GAAG,QAAQ,IAAI;AAAA,IACzC,SAAS,KAAK;AACZ,aAAO,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IACnE;AACA,UAAM,QAAoB,CAAC;AAC3B,eAAW,SAAS,SAAS;AAC3B,YAAM,QAAQ,MAAM,KAAK,GAAG,GAAG,MAAM,KAAK;AAC1C,YAAM,KAAK,EAAE,MAAM,OAAO,QAAQ,MAAM,CAAC;AAAA,IAC3C;AACA,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,UAAkB,SAAS,GAAG,QAAQ,KAA0B;AACzE,UAAM,WAAW,QAAQ,QAAQ;AACjC,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,KAAK,GAAG,GAAG,SAAS,QAAQ;AAAA,IAC5C,SAAS,KAAK;AACZ,aAAO,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IACnE;AACA,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,UAAI,uBAAuB,IAAI,QAAQ,GAAG;AACxC,eAAO,EAAE,SAAS,OAAO,SAAS;AAAA,MACpC;AACA,aAAO;AAAA,QACL,OAAO,gBAAgB,QAAQ,MAAM,QAAQ,KAAK,OAAO,MAAM,MAAM,CAAC;AAAA,MACxE;AAAA,IACF;AACA,UAAM,OAAO,IAAI,YAAY,OAAO,EAAE,OAAO,KAAK;AAClD,UAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAI,UAAU,MAAM,QAAQ;AAC1B,aAAO;AAAA,QACL,OAAO,eAAe,OAAO,MAAM,CAAC,yBAAyB,OAAO,MAAM,MAAM,CAAC;AAAA,MACnF;AAAA,IACF;AACA,UAAM,MAAM,KAAK,IAAI,SAAS,OAAO,MAAM,MAAM;AACjD,WAAO,EAAE,SAAS,MAAM,MAAM,QAAQ,GAAG,EAAE,KAAK,IAAI,GAAG,SAAS;AAAA,EAClE;AAAA,EAEA,MAAM,QAAQ,UAA0C;AACtD,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,GAAG,GAAG,KAAK,QAAQ;AACrC,cAAQ,MAAM,KAAK,GAAG,GAAG,SAAS,QAAQ;AAAA,IAC5C,SAAS,KAAK;AACZ,aAAO,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IACnE;AACA,UAAM,WAAW,QAAQ,QAAQ;AACjC,UAAM,UAA+B,WAAW,QAAQ,IACpD,IAAI,YAAY,OAAO,EAAE,OAAO,KAAK,IACrC;AACJ,UAAM,WAAW,KAAK,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzD,UAAM,OAAiB;AAAA,MACrB;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,aAAa;AAAA,IACf;AACA,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,MAAM,UAAkB,SAAuC;AACnE,QAAI,MAAM,KAAK,GAAG,GAAG,OAAO,QAAQ,GAAG;AACrC,aAAO,EAAE,OAAO,gBAAgB,QAAQ,mBAAmB;AAAA,IAC7D;AACA,UAAM,aAAa,KAAK,IAAI,QAAQ;AACpC,UAAM,KAAK,GAAG,GAAG,UAAU,UAAU,OAAO;AAC5C,WAAO,EAAE,MAAM,SAAS;AAAA,EAC1B;AAAA,EAEA,MAAM,KACJ,UACA,WACA,WACA,aAAa,OACQ;AACrB,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,GAAG,GAAG,aAAa,QAAQ;AAAA,IAClD,QAAQ;AACN,aAAO,EAAE,OAAO,gBAAgB,QAAQ,cAAc;AAAA,IACxD;AACA,UAAM,QAAQ,QAAQ,MAAM,SAAS,EAAE,SAAS;AAChD,QAAI,UAAU,GAAG;AACf,aAAO,EAAE,OAAO,qCAAqC,SAAS,IAAI;AAAA,IACpE;AACA,QAAI,QAAQ,KAAK,CAAC,YAAY;AAC5B,aAAO;AAAA,QACL,OAAO,kBAAkB,SAAS,aAAa,OAAO,KAAK,CAAC;AAAA,MAC9D;AAAA,IACF;AACA,UAAM,OAAO,aACT,QAAQ,MAAM,SAAS,EAAE,KAAK,SAAS,IACvC,QAAQ,QAAQ,WAAW,SAAS;AACxC,UAAM,KAAK,GAAG,GAAG,UAAU,UAAU,IAAI;AACzC,WAAO,EAAE,MAAM,UAAU,aAAa,aAAa,QAAQ,EAAE;AAAA,EAC/D;AAAA,EAEA,MAAM,KAAK,SAAiB,MAAsB,MAA2C;AAC3F,UAAM,QAAkB,CAAC,QAAQ,KAAK;AACtC,QAAI,SAAS,UAAa,SAAS,QAAQ,KAAK,SAAS,GAAG;AAC1D,YAAM,KAAK,aAAa,WAAW,IAAI,CAAC;AAAA,IAC1C;AACA,UAAM,KAAK,WAAW,OAAO,CAAC;AAC9B,UAAM,KAAK,WAAW,QAAQ,GAAG,CAAC;AAClC,UAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,MAAM,KAAK,GAAG,CAAC;AAChD,WAAO,EAAE,SAAS,gBAAgB,EAAE,EAAE;AAAA,EACxC;AAAA,EAEA,MAAM,KAAK,SAAiB,OAAO,KAA0B;AAC3D,UAAM,OAAO,QAAQ,SAAS,GAAG,IAAK,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK,UAAW;AAC7E,UAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,QAAQ,WAAW,IAAI,CAAC,UAAU,WAAW,IAAI,CAAC,EAAE;AACrF,WAAO,EAAE,OAAO,cAAc,EAAE,EAAE;AAAA,EACpC;AAAA,EAEA,MAAM,YACJ,OAC+B;AAC/B,UAAM,UAAgC,CAAC;AACvC,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO;AAChC,YAAM,aAAa,KAAK,IAAI,IAAI;AAChC,YAAM,KAAK,GAAG,GAAG,UAAU,MAAM,IAAI;AACrC,cAAQ,KAAK,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,OAA2D;AAC7E,UAAM,UAAkC,CAAC;AACzC,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,GAAG,GAAG,SAAS,IAAI;AAC9C,gBAAQ,KAAK,EAAE,MAAM,SAAS,OAAO,KAAK,CAAC;AAAA,MAC7C,QAAQ;AACN,gBAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC/D;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AC5QA,SAAS,YAAY,OAAyD;AAC5E,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,QAAQ;AACd,SAAO,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS;AACxD;AAEO,SAAS,YAAY,UAA4C;AACtE,QAAM,QAAkB,CAAC;AACzB,aAAW,OAAO,UAAU;AAC1B,QAAI,EAAE,aAAa,QAAQ,IAAI,YAAY,UAAa,IAAI,YAAY,KAAM;AAC9E,UAAM,UAAU,IAAI;AACpB,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI,QAAQ,KAAK,EAAE,SAAS,EAAG,OAAM,KAAK,OAAO;AACjD;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,iBAAW,SAAS,SAAS;AAC3B,YAAI,YAAY,KAAK,KAAK,MAAM,KAAK,KAAK,EAAE,SAAS,GAAG;AACtD,gBAAM,KAAK,MAAM,IAAI;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/langchain/convert.ts","../../src/langchain/backend.ts","../../src/langchain/messages.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { ExecuteResponse, FileInfo, GrepMatch } from 'deepagents'\n\ninterface IOLike {\n stdoutText: string\n stderrText: string\n exitCode: number | null\n}\n\nexport function ioToExecuteResponse(io: IOLike): ExecuteResponse {\n const stdout = io.stdoutText\n const stderr = io.stderrText\n let output = stdout\n if (stderr.length > 0) {\n output = stdout.length > 0 ? `${stdout}\\n${stderr}` : stderr\n }\n return { output, exitCode: io.exitCode, truncated: false }\n}\n\nexport function ioToGrepMatches(io: IOLike): GrepMatch[] {\n const stdout = io.stdoutText.trim()\n if (stdout.length === 0) return []\n const matches: GrepMatch[] = []\n for (const line of stdout.split('\\n')) {\n const firstColon = line.indexOf(':')\n if (firstColon < 0) continue\n const secondColon = line.indexOf(':', firstColon + 1)\n if (secondColon < 0) continue\n const path = line.slice(0, firstColon)\n const lineNumStr = line.slice(firstColon + 1, secondColon)\n const text = line.slice(secondColon + 1)\n const lineNum = Number.parseInt(lineNumStr, 10)\n if (Number.isNaN(lineNum)) continue\n matches.push({ path, line: lineNum, text })\n }\n return matches\n}\n\nexport function ioToFileInfos(io: IOLike): FileInfo[] {\n const stdout = io.stdoutText.trim()\n if (stdout.length === 0) return []\n const infos: FileInfo[] = []\n for (const raw of stdout.split('\\n')) {\n const entry = raw.trim()\n if (entry.length === 0) continue\n const isDir = entry.endsWith('/')\n infos.push({ path: isDir ? entry.slice(0, -1) : entry, is_dir: isDir })\n }\n return infos\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-node'\nimport type {\n EditResult,\n ExecuteResponse,\n FileData,\n FileDownloadResponse,\n FileInfo,\n FileUploadResponse,\n GlobResult,\n GrepResult,\n LsResult,\n ReadRawResult,\n ReadResult,\n SandboxBackendProtocolV2 as SandboxBackendProtocol,\n WriteResult,\n} from 'deepagents'\nimport { ioToExecuteResponse, ioToFileInfos, ioToGrepMatches } from './convert.ts'\nimport { gnuDirname } from '@struktoai/mirage-core'\n\nconst TEXT_EXTENSIONS = new Set([\n 'txt',\n 'md',\n 'json',\n 'jsonl',\n 'yaml',\n 'yml',\n 'csv',\n 'tsv',\n 'xml',\n 'html',\n 'htm',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'fish',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nfunction shellQuote(s: string): string {\n if (/^[A-Za-z0-9_./-]+$/.test(s)) return s\n return `'${s.replaceAll(`'`, `'\\\\''`)}'`\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n if (dot < 0) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nconst MIRAGE_RENDERED_AS_TEXT = new Set(['parquet', 'h5', 'hdf5', 'feather'])\n\nfunction mimeFor(path: string): string {\n const ext = extOf(path)\n if (ext === 'json' || ext === 'jsonl') return 'application/json'\n if (ext === 'csv') return 'text/csv'\n if (ext === 'html' || ext === 'htm') return 'text/html'\n if (ext === 'md') return 'text/markdown'\n if (TEXT_EXTENSIONS.has(ext)) return 'text/plain'\n if (MIRAGE_RENDERED_AS_TEXT.has(ext)) return 'text/plain'\n if (ext === 'png') return 'image/png'\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n if (ext === 'pdf') return 'application/pdf'\n return 'application/octet-stream'\n}\n\nfunction isTextMime(mimeType: string): boolean {\n return mimeType.startsWith('text/') || mimeType === 'application/json'\n}\n\nconst ANTHROPIC_BINARY_MIMES = new Set([\n 'application/pdf',\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n])\n\nexport interface LangchainWorkspaceOptions {\n sandboxId?: string\n}\n\nexport class LangchainWorkspace implements SandboxBackendProtocol {\n readonly id: string\n private readonly ws: Workspace\n\n constructor(workspace: Workspace, options: LangchainWorkspaceOptions = {}) {\n this.ws = workspace\n this.id = options.sandboxId ?? 'mirage'\n }\n\n async execute(command: string): Promise<ExecuteResponse> {\n const io = await this.ws.execute(command)\n return ioToExecuteResponse(io)\n }\n\n async ls(path: string): Promise<LsResult> {\n let entries: string[]\n try {\n entries = await this.ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: FileInfo[] = []\n for (const entry of entries) {\n const isDir = await this.ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n }\n\n async read(filePath: string, offset = 0, limit = 500): Promise<ReadResult> {\n const mimeType = mimeFor(filePath)\n let bytes: Uint8Array\n try {\n bytes = await this.ws.fs.readFile(filePath)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n if (!isTextMime(mimeType)) {\n if (ANTHROPIC_BINARY_MIMES.has(mimeType)) {\n return { content: bytes, mimeType }\n }\n return {\n error: `Binary file '${filePath}' (${mimeType}, ${String(bytes.length)} bytes). Use shell commands (head, file, wc, od) via execute() to inspect.`,\n }\n }\n const text = new TextDecoder('utf-8').decode(bytes)\n const lines = text.split('\\n')\n if (offset >= lines.length) {\n return {\n error: `Line offset ${String(offset)} exceeds file length (${String(lines.length)} lines)`,\n }\n }\n const end = Math.min(offset + limit, lines.length)\n return { content: lines.slice(offset, end).join('\\n'), mimeType }\n }\n\n async readRaw(filePath: string): Promise<ReadRawResult> {\n let stat: Awaited<ReturnType<Workspace['fs']['stat']>>\n let bytes: Uint8Array\n try {\n stat = await this.ws.fs.stat(filePath)\n bytes = await this.ws.fs.readFile(filePath)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const mimeType = mimeFor(filePath)\n const content: string | Uint8Array = isTextMime(mimeType)\n ? new TextDecoder('utf-8').decode(bytes)\n : bytes\n const modified = stat.modified ?? new Date().toISOString()\n const data: FileData = {\n content,\n mimeType,\n created_at: modified,\n modified_at: modified,\n }\n return { data }\n }\n\n async write(filePath: string, content: string): Promise<WriteResult> {\n if (await this.ws.fs.exists(filePath)) {\n return { error: `Error: file '${filePath}' already exists` }\n }\n await ensureParent(this.ws, filePath)\n await this.ws.fs.writeFile(filePath, content)\n return { path: filePath }\n }\n\n async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll = false,\n ): Promise<EditResult> {\n let current: string\n try {\n current = await this.ws.fs.readFileText(filePath)\n } catch {\n return { error: `Error: file '${filePath}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && !replaceAll) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next = replaceAll\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await this.ws.fs.writeFile(filePath, next)\n return { path: filePath, occurrences: replaceAll ? count : 1 }\n }\n\n async grep(pattern: string, path?: string | null, glob?: string | null): Promise<GrepResult> {\n const parts: string[] = ['grep', '-rn']\n if (glob !== undefined && glob !== null && glob.length > 0) {\n parts.push('--include', shellQuote(glob))\n }\n parts.push(shellQuote(pattern))\n parts.push(shellQuote(path ?? '/'))\n const io = await this.ws.execute(parts.join(' '))\n return { matches: ioToGrepMatches(io) }\n }\n\n async glob(pattern: string, path = '/'): Promise<GlobResult> {\n const name = pattern.includes('/') ? (pattern.split('/').pop() ?? pattern) : pattern\n const io = await this.ws.execute(`find ${shellQuote(path)} -name ${shellQuote(name)}`)\n return { files: ioToFileInfos(io) }\n }\n\n async uploadFiles(\n files: readonly (readonly [string, Uint8Array])[],\n ): Promise<FileUploadResponse[]> {\n const results: FileUploadResponse[] = []\n for (const [path, data] of files) {\n await ensureParent(this.ws, path)\n await this.ws.fs.writeFile(path, data)\n results.push({ path, error: null })\n }\n return results\n }\n\n async downloadFiles(paths: readonly string[]): Promise<FileDownloadResponse[]> {\n const results: FileDownloadResponse[] = []\n for (const path of paths) {\n try {\n const content = await this.ws.fs.readFile(path)\n results.push({ path, content, error: null })\n } catch {\n results.push({ path, content: null, error: 'file_not_found' })\n }\n }\n return results\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\ninterface MessageLike {\n content?: unknown\n}\n\ninterface ContentBlock {\n type?: string\n text?: string\n}\n\nfunction isTextBlock(value: unknown): value is { type: 'text'; text: string } {\n if (typeof value !== 'object' || value === null) return false\n const block = value as ContentBlock\n return block.type === 'text' && typeof block.text === 'string'\n}\n\nexport function extractText(messages: readonly MessageLike[]): string[] {\n const texts: string[] = []\n for (const msg of messages) {\n if (!('content' in msg) || msg.content === undefined || msg.content === null) continue\n const content = msg.content\n if (typeof content === 'string') {\n if (content.trim().length > 0) texts.push(content)\n continue\n }\n if (Array.isArray(content)) {\n for (const block of content) {\n if (isTextBlock(block) && block.text.trim().length > 0) {\n texts.push(block.text)\n }\n }\n }\n }\n return texts\n}\n"],"mappings":"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","names":[]}
@@ -1,15 +1,10 @@
1
1
  // src/mastra/index.ts
2
2
  import { createTool } from "@mastra/core/tools";
3
3
  import { z } from "zod";
4
- function parentOf(path) {
5
- const trimmed = path.replace(/\/+$/, "");
6
- const idx = trimmed.lastIndexOf("/");
7
- if (idx <= 0) return "/";
8
- return trimmed.slice(0, idx);
9
- }
4
+ import { gnuDirname } from "@struktoai/mirage-core";
10
5
  async function ensureParent(ws, path) {
11
- const parent = parentOf(path);
12
- if (parent === "/" || parent === "") return;
6
+ const parent = gnuDirname(path);
7
+ if (parent === "/" || parent === "" || parent === ".") return;
13
8
  if (await ws.fs.exists(parent)) return;
14
9
  await ensureParent(ws, parent);
15
10
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mastra/index.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport { createTool } from '@mastra/core/tools'\nimport { z } from 'zod'\n\nfunction parentOf(path: string): string {\n const trimmed = path.replace(/\\/+$/, '')\n const idx = trimmed.lastIndexOf('/')\n if (idx <= 0) return '/'\n return trimmed.slice(0, idx)\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = parentOf(path)\n if (parent === '/' || parent === '') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nexport function mirageTools(ws: Workspace) {\n return {\n execute: createTool({\n id: 'mirage-execute',\n description:\n 'Execute a shell command in the Mirage workspace and return stdout, stderr, and exitCode.',\n inputSchema: z.object({\n command: z.string().describe('The shell command to execute.'),\n }),\n outputSchema: z.object({\n stdout: z.string(),\n stderr: z.string(),\n exitCode: z.number(),\n }),\n execute: async (inputData) => {\n const { command } = inputData as { command: string }\n const io = await ws.execute(command)\n return {\n stdout: io.stdoutText,\n stderr: io.stderrText,\n exitCode: io.exitCode,\n }\n },\n }),\n\n readFile: createTool({\n id: 'mirage-read-file',\n description: 'Read the contents of a file from the Mirage workspace as UTF-8 text.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n }),\n outputSchema: z.object({\n content: z.string().optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path } = inputData as { path: string }\n try {\n const content = await ws.fs.readFileText(path)\n return { content }\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n },\n }),\n\n writeFile: createTool({\n id: 'mirage-write-file',\n description:\n 'Write content to a file in the Mirage workspace. Creates missing parent directories.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n content: z.string().describe('UTF-8 text content to write.'),\n }),\n outputSchema: z.object({ path: z.string() }),\n execute: async (inputData) => {\n const { path, content } = inputData as { path: string; content: string }\n await ensureParent(ws, path)\n await ws.fs.writeFile(path, content)\n return { path }\n },\n }),\n\n editFile: createTool({\n id: 'mirage-edit-file',\n description:\n 'Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path of the file to edit.'),\n oldString: z.string().describe('The exact string to replace.'),\n newString: z.string().describe('The replacement string.'),\n replaceAll: z\n .boolean()\n .optional()\n .describe('Replace every occurrence rather than requiring a unique match.'),\n }),\n outputSchema: z.object({\n path: z.string().optional(),\n occurrences: z.number().optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path, oldString, newString, replaceAll } = inputData as {\n path: string\n oldString: string\n newString: string\n replaceAll?: boolean\n }\n let current: string\n try {\n current = await ws.fs.readFileText(path)\n } catch {\n return { error: `Error: file '${path}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && replaceAll !== true) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next =\n replaceAll === true\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await ws.fs.writeFile(path, next)\n return { path, occurrences: replaceAll === true ? count : 1 }\n },\n }),\n\n ls: createTool({\n id: 'mirage-ls',\n description: 'List entries of a directory in the Mirage workspace.',\n inputSchema: z.object({\n path: z.string().describe('Absolute directory path inside the workspace.'),\n }),\n outputSchema: z.object({\n files: z.array(z.object({ path: z.string(), is_dir: z.boolean() })).optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path } = inputData as { path: string }\n let entries: string[]\n try {\n entries = await ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: { path: string; is_dir: boolean }[] = []\n for (const entry of entries) {\n const isDir = await ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n },\n }),\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/mastra/index.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport { createTool } from '@mastra/core/tools'\nimport { z } from 'zod'\nimport { gnuDirname } from '@struktoai/mirage-core'\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nexport function mirageTools(ws: Workspace) {\n return {\n execute: createTool({\n id: 'mirage-execute',\n description:\n 'Execute a shell command in the Mirage workspace and return stdout, stderr, and exitCode.',\n inputSchema: z.object({\n command: z.string().describe('The shell command to execute.'),\n }),\n outputSchema: z.object({\n stdout: z.string(),\n stderr: z.string(),\n exitCode: z.number(),\n }),\n execute: async (inputData) => {\n const { command } = inputData as { command: string }\n const io = await ws.execute(command)\n return {\n stdout: io.stdoutText,\n stderr: io.stderrText,\n exitCode: io.exitCode,\n }\n },\n }),\n\n readFile: createTool({\n id: 'mirage-read-file',\n description: 'Read the contents of a file from the Mirage workspace as UTF-8 text.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n }),\n outputSchema: z.object({\n content: z.string().optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path } = inputData as { path: string }\n try {\n const content = await ws.fs.readFileText(path)\n return { content }\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n },\n }),\n\n writeFile: createTool({\n id: 'mirage-write-file',\n description:\n 'Write content to a file in the Mirage workspace. Creates missing parent directories.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n content: z.string().describe('UTF-8 text content to write.'),\n }),\n outputSchema: z.object({ path: z.string() }),\n execute: async (inputData) => {\n const { path, content } = inputData as { path: string; content: string }\n await ensureParent(ws, path)\n await ws.fs.writeFile(path, content)\n return { path }\n },\n }),\n\n editFile: createTool({\n id: 'mirage-edit-file',\n description:\n 'Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path of the file to edit.'),\n oldString: z.string().describe('The exact string to replace.'),\n newString: z.string().describe('The replacement string.'),\n replaceAll: z\n .boolean()\n .optional()\n .describe('Replace every occurrence rather than requiring a unique match.'),\n }),\n outputSchema: z.object({\n path: z.string().optional(),\n occurrences: z.number().optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path, oldString, newString, replaceAll } = inputData as {\n path: string\n oldString: string\n newString: string\n replaceAll?: boolean\n }\n let current: string\n try {\n current = await ws.fs.readFileText(path)\n } catch {\n return { error: `Error: file '${path}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && replaceAll !== true) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next =\n replaceAll === true\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await ws.fs.writeFile(path, next)\n return { path, occurrences: replaceAll === true ? count : 1 }\n },\n }),\n\n ls: createTool({\n id: 'mirage-ls',\n description: 'List entries of a directory in the Mirage workspace.',\n inputSchema: z.object({\n path: z.string().describe('Absolute directory path inside the workspace.'),\n }),\n outputSchema: z.object({\n files: z.array(z.object({ path: z.string(), is_dir: z.boolean() })).optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n const { path } = inputData as { path: string }\n let entries: string[]\n try {\n entries = await ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: { path: string; is_dir: boolean }[] = []\n for (const entry of entries) {\n const isDir = await ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n },\n }),\n }\n}\n"],"mappings":"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","names":[]}
@@ -5,15 +5,10 @@ import {
5
5
 
6
6
  // src/openai/editor.ts
7
7
  import { applyDiff } from "@openai/agents";
8
- function parentOf(path) {
9
- const trimmed = path.replace(/\/+$/, "");
10
- const idx = trimmed.lastIndexOf("/");
11
- if (idx <= 0) return "/";
12
- return trimmed.slice(0, idx);
13
- }
8
+ import { gnuDirname } from "@struktoai/mirage-core";
14
9
  async function ensureParent(ws, path) {
15
- const parent = parentOf(path);
16
- if (parent === "/" || parent === "") return;
10
+ const parent = gnuDirname(path);
11
+ if (parent === "/" || parent === "" || parent === ".") return;
17
12
  if (await ws.fs.exists(parent)) return;
18
13
  await ensureParent(ws, parent);
19
14
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/openai/editor.ts","../../src/openai/shell.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport { applyDiff } from '@openai/agents'\nimport type { ApplyPatchOperation, ApplyPatchResult, Editor } from '@openai/agents'\n\nfunction parentOf(path: string): string {\n const trimmed = path.replace(/\\/+$/, '')\n const idx = trimmed.lastIndexOf('/')\n if (idx <= 0) return '/'\n return trimmed.slice(0, idx)\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = parentOf(path)\n if (parent === '/' || parent === '') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n // Tolerate mkdir race: another caller may have created the dir between exists() and mkdir().\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nexport class MirageEditor implements Editor {\n constructor(private readonly ws: Workspace) {}\n\n async createFile(\n op: Extract<ApplyPatchOperation, { type: 'create_file' }>,\n ): Promise<ApplyPatchResult> {\n await ensureParent(this.ws, op.path)\n const content = applyDiff('', op.diff, 'create')\n await this.ws.fs.writeFile(op.path, content)\n return { status: 'completed' }\n }\n\n async updateFile(\n op: Extract<ApplyPatchOperation, { type: 'update_file' }>,\n ): Promise<ApplyPatchResult> {\n let current: string\n try {\n current = await this.ws.fs.readFileText(op.path)\n } catch {\n return { status: 'failed', output: `File not found: ${op.path}` }\n }\n const next = applyDiff(current, op.diff)\n await this.ws.fs.writeFile(op.path, next)\n return { status: 'completed' }\n }\n\n async deleteFile(\n op: Extract<ApplyPatchOperation, { type: 'delete_file' }>,\n ): Promise<ApplyPatchResult> {\n if (!(await this.ws.fs.exists(op.path))) {\n return { status: 'failed', output: `File not found: ${op.path}` }\n }\n await this.ws.fs.unlink(op.path)\n return { status: 'completed' }\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport type { Shell, ShellAction, ShellResult, ShellOutputResult } from '@openai/agents'\n\nexport class MirageShell implements Shell {\n constructor(private readonly ws: Workspace) {}\n\n async run(action: ShellAction): Promise<ShellResult> {\n const output: ShellOutputResult[] = []\n for (const cmd of action.commands) {\n const io = await this.ws.execute(cmd)\n output.push({\n stdout: io.stdoutText,\n stderr: io.stderrText,\n outcome: { type: 'exit', exitCode: io.exitCode },\n })\n }\n return { output }\n }\n}\n"],"mappings":";;;;;;AAeA,SAAS,iBAAiB;AAG1B,SAAS,SAAS,MAAsB;AACtC,QAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE;AACvC,QAAM,MAAM,QAAQ,YAAY,GAAG;AACnC,MAAI,OAAO,EAAG,QAAO;AACrB,SAAO,QAAQ,MAAM,GAAG,GAAG;AAC7B;AAEA,eAAe,aAAa,IAAe,MAA6B;AACtE,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,WAAW,OAAO,WAAW,GAAI;AACrC,MAAI,MAAM,GAAG,GAAG,OAAO,MAAM,EAAG;AAChC,QAAM,aAAa,IAAI,MAAM;AAC7B,MAAI;AACF,UAAM,GAAG,GAAG,MAAM,MAAM;AAAA,EAC1B,SAAS,KAAK;AAEZ,QAAI,CAAE,MAAM,GAAG,GAAG,OAAO,MAAM,EAAI,OAAM;AAAA,EAC3C;AACF;AAEO,IAAM,eAAN,MAAqC;AAAA,EAC1C,YAA6B,IAAe;AAAf;AAAA,EAAgB;AAAA,EAAhB;AAAA,EAE7B,MAAM,WACJ,IAC2B;AAC3B,UAAM,aAAa,KAAK,IAAI,GAAG,IAAI;AACnC,UAAM,UAAU,UAAU,IAAI,GAAG,MAAM,QAAQ;AAC/C,UAAM,KAAK,GAAG,GAAG,UAAU,GAAG,MAAM,OAAO;AAC3C,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAAA,EAEA,MAAM,WACJ,IAC2B;AAC3B,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,GAAG,GAAG,aAAa,GAAG,IAAI;AAAA,IACjD,QAAQ;AACN,aAAO,EAAE,QAAQ,UAAU,QAAQ,mBAAmB,GAAG,IAAI,GAAG;AAAA,IAClE;AACA,UAAM,OAAO,UAAU,SAAS,GAAG,IAAI;AACvC,UAAM,KAAK,GAAG,GAAG,UAAU,GAAG,MAAM,IAAI;AACxC,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAAA,EAEA,MAAM,WACJ,IAC2B;AAC3B,QAAI,CAAE,MAAM,KAAK,GAAG,GAAG,OAAO,GAAG,IAAI,GAAI;AACvC,aAAO,EAAE,QAAQ,UAAU,QAAQ,mBAAmB,GAAG,IAAI,GAAG;AAAA,IAClE;AACA,UAAM,KAAK,GAAG,GAAG,OAAO,GAAG,IAAI;AAC/B,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AACF;;;ACxDO,IAAM,cAAN,MAAmC;AAAA,EACxC,YAA6B,IAAe;AAAf;AAAA,EAAgB;AAAA,EAAhB;AAAA,EAE7B,MAAM,IAAI,QAA2C;AACnD,UAAM,SAA8B,CAAC;AACrC,eAAW,OAAO,OAAO,UAAU;AACjC,YAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,GAAG;AACpC,aAAO,KAAK;AAAA,QACV,QAAQ,GAAG;AAAA,QACX,QAAQ,GAAG;AAAA,QACX,SAAS,EAAE,MAAM,QAAQ,UAAU,GAAG,SAAS;AAAA,MACjD,CAAC;AAAA,IACH;AACA,WAAO,EAAE,OAAO;AAAA,EAClB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/openai/editor.ts","../../src/openai/shell.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport { applyDiff } from '@openai/agents'\nimport type { ApplyPatchOperation, ApplyPatchResult, Editor } from '@openai/agents'\nimport { gnuDirname } from '@struktoai/mirage-core'\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n // Tolerate mkdir race: another caller may have created the dir between exists() and mkdir().\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nexport class MirageEditor implements Editor {\n constructor(private readonly ws: Workspace) {}\n\n async createFile(\n op: Extract<ApplyPatchOperation, { type: 'create_file' }>,\n ): Promise<ApplyPatchResult> {\n await ensureParent(this.ws, op.path)\n const content = applyDiff('', op.diff, 'create')\n await this.ws.fs.writeFile(op.path, content)\n return { status: 'completed' }\n }\n\n async updateFile(\n op: Extract<ApplyPatchOperation, { type: 'update_file' }>,\n ): Promise<ApplyPatchResult> {\n let current: string\n try {\n current = await this.ws.fs.readFileText(op.path)\n } catch {\n return { status: 'failed', output: `File not found: ${op.path}` }\n }\n const next = applyDiff(current, op.diff)\n await this.ws.fs.writeFile(op.path, next)\n return { status: 'completed' }\n }\n\n async deleteFile(\n op: Extract<ApplyPatchOperation, { type: 'delete_file' }>,\n ): Promise<ApplyPatchResult> {\n if (!(await this.ws.fs.exists(op.path))) {\n return { status: 'failed', output: `File not found: ${op.path}` }\n }\n await this.ws.fs.unlink(op.path)\n return { status: 'completed' }\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport type { Shell, ShellAction, ShellResult, ShellOutputResult } from '@openai/agents'\n\nexport class MirageShell implements Shell {\n constructor(private readonly ws: Workspace) {}\n\n async run(action: ShellAction): Promise<ShellResult> {\n const output: ShellOutputResult[] = []\n for (const cmd of action.commands) {\n const io = await this.ws.execute(cmd)\n output.push({\n stdout: io.stdoutText,\n stderr: io.stderrText,\n outcome: { type: 'exit', exitCode: io.exitCode },\n })\n }\n return { output }\n }\n}\n"],"mappings":";;;;;;AAeA,SAAS,iBAAiB;AAE1B,SAAS,kBAAkB;AAE3B,eAAe,aAAa,IAAe,MAA6B;AACtE,QAAM,SAAS,WAAW,IAAI;AAC9B,MAAI,WAAW,OAAO,WAAW,MAAM,WAAW,IAAK;AACvD,MAAI,MAAM,GAAG,GAAG,OAAO,MAAM,EAAG;AAChC,QAAM,aAAa,IAAI,MAAM;AAC7B,MAAI;AACF,UAAM,GAAG,GAAG,MAAM,MAAM;AAAA,EAC1B,SAAS,KAAK;AAEZ,QAAI,CAAE,MAAM,GAAG,GAAG,OAAO,MAAM,EAAI,OAAM;AAAA,EAC3C;AACF;AAEO,IAAM,eAAN,MAAqC;AAAA,EAC1C,YAA6B,IAAe;AAAf;AAAA,EAAgB;AAAA,EAAhB;AAAA,EAE7B,MAAM,WACJ,IAC2B;AAC3B,UAAM,aAAa,KAAK,IAAI,GAAG,IAAI;AACnC,UAAM,UAAU,UAAU,IAAI,GAAG,MAAM,QAAQ;AAC/C,UAAM,KAAK,GAAG,GAAG,UAAU,GAAG,MAAM,OAAO;AAC3C,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAAA,EAEA,MAAM,WACJ,IAC2B;AAC3B,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,GAAG,GAAG,aAAa,GAAG,IAAI;AAAA,IACjD,QAAQ;AACN,aAAO,EAAE,QAAQ,UAAU,QAAQ,mBAAmB,GAAG,IAAI,GAAG;AAAA,IAClE;AACA,UAAM,OAAO,UAAU,SAAS,GAAG,IAAI;AACvC,UAAM,KAAK,GAAG,GAAG,UAAU,GAAG,MAAM,IAAI;AACxC,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAAA,EAEA,MAAM,WACJ,IAC2B;AAC3B,QAAI,CAAE,MAAM,KAAK,GAAG,GAAG,OAAO,GAAG,IAAI,GAAI;AACvC,aAAO,EAAE,QAAQ,UAAU,QAAQ,mBAAmB,GAAG,IAAI,GAAG;AAAA,IAClE;AACA,UAAM,KAAK,GAAG,GAAG,OAAO,GAAG,IAAI;AAC/B,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AACF;;;AClDO,IAAM,cAAN,MAAmC;AAAA,EACxC,YAA6B,IAAe;AAAf;AAAA,EAAgB;AAAA,EAAhB;AAAA,EAE7B,MAAM,IAAI,QAA2C;AACnD,UAAM,SAA8B,CAAC;AACrC,eAAW,OAAO,OAAO,UAAU;AACjC,YAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,GAAG;AACpC,aAAO,KAAK;AAAA,QACV,QAAQ,GAAG;AAAA,QACX,QAAQ,GAAG;AAAA,QACX,SAAS,EAAE,MAAM,QAAQ,UAAU,GAAG,SAAS;AAAA,MACjD,CAAC;AAAA,IACH;AACA,WAAO,EAAE,OAAO;AAAA,EAClB;AACF;","names":[]}
@@ -0,0 +1,24 @@
1
+ import { Workspace } from '@struktoai/mirage-node';
2
+ import { z } from 'zod';
3
+
4
+ interface ToolContext {
5
+ sessionID: string;
6
+ messageID: string;
7
+ agent: string;
8
+ abort: AbortSignal;
9
+ }
10
+ type WsResolver = (ctx: ToolContext) => Workspace | Promise<Workspace>;
11
+ type WsLike = Workspace | WsResolver;
12
+ interface ToolDefinition<Args extends z.ZodRawShape = z.ZodRawShape> {
13
+ description: string;
14
+ args: Args;
15
+ execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string>;
16
+ }
17
+ declare function mirageTools(ws: WsLike): Record<string, ToolDefinition>;
18
+ interface Hooks {
19
+ tool?: Record<string, ToolDefinition>;
20
+ }
21
+ type PluginFn = (input: unknown) => Promise<Hooks>;
22
+ declare function miragePlugin(ws: WsLike): PluginFn;
23
+
24
+ export { type ToolContext, type WsLike, type WsResolver, miragePlugin, mirageTools };
@@ -0,0 +1,206 @@
1
+ // src/opencode/index.ts
2
+ import { z } from "zod";
3
+ import { gnuDirname } from "@struktoai/mirage-core";
4
+ function tool(input) {
5
+ return input;
6
+ }
7
+ function isResolver(ws) {
8
+ return typeof ws === "function";
9
+ }
10
+ async function resolveWs(ws, ctx) {
11
+ return isResolver(ws) ? ws(ctx) : ws;
12
+ }
13
+ async function ensureParent(ws, path) {
14
+ const parent = gnuDirname(path);
15
+ if (parent === "/" || parent === "" || parent === ".") return;
16
+ if (await ws.fs.exists(parent)) return;
17
+ await ensureParent(ws, parent);
18
+ try {
19
+ await ws.fs.mkdir(parent);
20
+ } catch (err) {
21
+ if (!await ws.fs.exists(parent)) throw err;
22
+ }
23
+ }
24
+ var TEXT_EXTS = /* @__PURE__ */ new Set([
25
+ "txt",
26
+ "md",
27
+ "json",
28
+ "jsonl",
29
+ "yaml",
30
+ "yml",
31
+ "csv",
32
+ "tsv",
33
+ "xml",
34
+ "html",
35
+ "htm",
36
+ "js",
37
+ "mjs",
38
+ "cjs",
39
+ "ts",
40
+ "tsx",
41
+ "jsx",
42
+ "py",
43
+ "rb",
44
+ "rs",
45
+ "go",
46
+ "java",
47
+ "c",
48
+ "cpp",
49
+ "h",
50
+ "hpp",
51
+ "sh",
52
+ "bash",
53
+ "zsh",
54
+ "sql",
55
+ "log",
56
+ "env",
57
+ "ini",
58
+ "toml",
59
+ "conf",
60
+ "cfg"
61
+ ]);
62
+ function extOf(path) {
63
+ const dot = path.lastIndexOf(".");
64
+ if (dot < 0) return "";
65
+ return path.slice(dot + 1).toLowerCase();
66
+ }
67
+ function isLikelyText(path) {
68
+ return TEXT_EXTS.has(extOf(path));
69
+ }
70
+ function errMsg(err) {
71
+ return err instanceof Error ? err.message : String(err);
72
+ }
73
+ function mirageTools(ws) {
74
+ const read = tool({
75
+ description: "Read a file. Returns UTF-8 text for source/data files; for binary files returns a metadata stub. Use the bash tool to inspect binaries.",
76
+ args: {
77
+ filePath: z.string().describe("Absolute path of the file to read.")
78
+ },
79
+ execute: async ({ filePath }, ctx) => {
80
+ const w = await resolveWs(ws, ctx);
81
+ let bytes;
82
+ try {
83
+ bytes = await w.fs.readFile(filePath);
84
+ } catch (err) {
85
+ return `Error: ${errMsg(err)}`;
86
+ }
87
+ if (isLikelyText(filePath)) {
88
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
89
+ }
90
+ return `Binary file ${filePath} (${String(bytes.length)} bytes). Use the bash tool with head/file/wc/od to inspect.`;
91
+ }
92
+ });
93
+ const write = tool({
94
+ description: "Write content to a file. Creates missing parent directories.",
95
+ args: {
96
+ filePath: z.string().describe("Absolute path of the file to write."),
97
+ content: z.string().describe("UTF-8 text content to write.")
98
+ },
99
+ execute: async ({ filePath, content }, ctx) => {
100
+ const w = await resolveWs(ws, ctx);
101
+ await ensureParent(w, filePath);
102
+ await w.fs.writeFile(filePath, content);
103
+ return `Wrote ${String(content.length)} bytes to ${filePath}`;
104
+ }
105
+ });
106
+ const edit = tool({
107
+ description: "Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.",
108
+ args: {
109
+ filePath: z.string().describe("Absolute path of the file to edit."),
110
+ oldString: z.string().describe("The exact string to replace."),
111
+ newString: z.string().describe("The replacement string."),
112
+ replaceAll: z.boolean().optional().describe("Replace every occurrence rather than requiring a unique match.")
113
+ },
114
+ execute: async ({ filePath, oldString, newString, replaceAll }, ctx) => {
115
+ const w = await resolveWs(ws, ctx);
116
+ let current;
117
+ try {
118
+ current = await w.fs.readFileText(filePath);
119
+ } catch {
120
+ return `Error: file '${filePath}' not found`;
121
+ }
122
+ const count = current.split(oldString).length - 1;
123
+ if (count === 0) {
124
+ return `Error: string not found in file: '${oldString}'`;
125
+ }
126
+ if (count > 1 && replaceAll !== true) {
127
+ return `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`;
128
+ }
129
+ const next = replaceAll === true ? current.split(oldString).join(newString) : current.replace(oldString, newString);
130
+ await w.fs.writeFile(filePath, next);
131
+ const occurrences = replaceAll === true ? count : 1;
132
+ return `Edited ${filePath} (${String(occurrences)} occurrence${occurrences === 1 ? "" : "s"})`;
133
+ }
134
+ });
135
+ const ls = tool({
136
+ description: "List entries of a directory.",
137
+ args: {
138
+ path: z.string().describe("Absolute directory path.")
139
+ },
140
+ execute: async ({ path }, ctx) => {
141
+ const w = await resolveWs(ws, ctx);
142
+ let entries;
143
+ try {
144
+ entries = await w.fs.readdir(path);
145
+ } catch (err) {
146
+ return `Error: ${errMsg(err)}`;
147
+ }
148
+ const lines = [];
149
+ for (const entry of entries) {
150
+ const isDir = await w.fs.isDir(entry);
151
+ lines.push(isDir ? `${entry}/` : entry);
152
+ }
153
+ return lines.join("\n");
154
+ }
155
+ });
156
+ const bash = tool({
157
+ description: "Execute a shell command and return stdout, stderr, and exit code.",
158
+ args: {
159
+ command: z.string().describe("The shell command to execute.")
160
+ },
161
+ execute: async ({ command }, ctx) => {
162
+ const w = await resolveWs(ws, ctx);
163
+ const io = await w.execute(command);
164
+ const parts = [];
165
+ if (io.stdoutText.length > 0) parts.push(io.stdoutText);
166
+ if (io.stderrText.length > 0) parts.push(io.stderrText);
167
+ return parts.join("\n").trim();
168
+ }
169
+ });
170
+ const glob = tool({
171
+ description: "Find files matching a name pattern.",
172
+ args: {
173
+ pattern: z.string().describe('Filename pattern (e.g. "*.ts").'),
174
+ path: z.string().optional().describe("Directory to search under. Defaults to /.")
175
+ },
176
+ execute: async ({ pattern, path }, ctx) => {
177
+ const w = await resolveWs(ws, ctx);
178
+ const root = path ?? "/";
179
+ const io = await w.execute(`find ${root} -name '${pattern.replace(/'/g, "'\\''")}'`);
180
+ return io.stdoutText.trim();
181
+ }
182
+ });
183
+ const grep = tool({
184
+ description: "Search for a regex pattern in files.",
185
+ args: {
186
+ pattern: z.string().describe("Pattern to search for."),
187
+ path: z.string().optional().describe("Directory or file to search under. Defaults to /.")
188
+ },
189
+ execute: async ({ pattern, path }, ctx) => {
190
+ const w = await resolveWs(ws, ctx);
191
+ const root = path ?? "/";
192
+ const escaped = pattern.replace(/'/g, "'\\''");
193
+ const io = await w.execute(`grep -rn '${escaped}' ${root}`);
194
+ return io.stdoutText.trim();
195
+ }
196
+ });
197
+ return { read, write, edit, ls, bash, glob, grep };
198
+ }
199
+ function miragePlugin(ws) {
200
+ return () => Promise.resolve({ tool: mirageTools(ws) });
201
+ }
202
+ export {
203
+ miragePlugin,
204
+ mirageTools
205
+ };
206
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/opencode/index.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-node'\nimport { z } from 'zod'\nimport { gnuDirname } from '@struktoai/mirage-core'\n\nexport interface ToolContext {\n sessionID: string\n messageID: string\n agent: string\n abort: AbortSignal\n}\n\nexport type WsResolver = (ctx: ToolContext) => Workspace | Promise<Workspace>\nexport type WsLike = Workspace | WsResolver\n\ninterface ToolDefinition<Args extends z.ZodRawShape = z.ZodRawShape> {\n description: string\n args: Args\n execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string>\n}\n\nfunction tool<Args extends z.ZodRawShape>(input: ToolDefinition<Args>): ToolDefinition<Args> {\n return input\n}\n\nfunction isResolver(ws: WsLike): ws is WsResolver {\n return typeof ws === 'function'\n}\n\nasync function resolveWs(ws: WsLike, ctx: ToolContext): Promise<Workspace> {\n return isResolver(ws) ? ws(ctx) : ws\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nconst TEXT_EXTS = new Set([\n 'txt',\n 'md',\n 'json',\n 'jsonl',\n 'yaml',\n 'yml',\n 'csv',\n 'tsv',\n 'xml',\n 'html',\n 'htm',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n if (dot < 0) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nfunction isLikelyText(path: string): boolean {\n return TEXT_EXTS.has(extOf(path))\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nexport function mirageTools(ws: WsLike): Record<string, ToolDefinition> {\n const read = tool({\n description:\n 'Read a file. Returns UTF-8 text for source/data files; for binary files returns a metadata stub. Use the bash tool to inspect binaries.',\n args: {\n filePath: z.string().describe('Absolute path of the file to read.'),\n },\n execute: async ({ filePath }, ctx) => {\n const w = await resolveWs(ws, ctx)\n let bytes: Uint8Array\n try {\n bytes = await w.fs.readFile(filePath)\n } catch (err) {\n return `Error: ${errMsg(err)}`\n }\n if (isLikelyText(filePath)) {\n return new TextDecoder('utf-8', { fatal: false }).decode(bytes)\n }\n return `Binary file ${filePath} (${String(bytes.length)} bytes). Use the bash tool with head/file/wc/od to inspect.`\n },\n })\n\n const write = tool({\n description: 'Write content to a file. Creates missing parent directories.',\n args: {\n filePath: z.string().describe('Absolute path of the file to write.'),\n content: z.string().describe('UTF-8 text content to write.'),\n },\n execute: async ({ filePath, content }, ctx) => {\n const w = await resolveWs(ws, ctx)\n await ensureParent(w, filePath)\n await w.fs.writeFile(filePath, content)\n return `Wrote ${String(content.length)} bytes to ${filePath}`\n },\n })\n\n const edit = tool({\n description:\n 'Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.',\n args: {\n filePath: z.string().describe('Absolute path of the file to edit.'),\n oldString: z.string().describe('The exact string to replace.'),\n newString: z.string().describe('The replacement string.'),\n replaceAll: z\n .boolean()\n .optional()\n .describe('Replace every occurrence rather than requiring a unique match.'),\n },\n execute: async ({ filePath, oldString, newString, replaceAll }, ctx) => {\n const w = await resolveWs(ws, ctx)\n let current: string\n try {\n current = await w.fs.readFileText(filePath)\n } catch {\n return `Error: file '${filePath}' not found`\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return `Error: string not found in file: '${oldString}'`\n }\n if (count > 1 && replaceAll !== true) {\n return `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`\n }\n const next =\n replaceAll === true\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await w.fs.writeFile(filePath, next)\n const occurrences = replaceAll === true ? count : 1\n return `Edited ${filePath} (${String(occurrences)} occurrence${occurrences === 1 ? '' : 's'})`\n },\n })\n\n const ls = tool({\n description: 'List entries of a directory.',\n args: {\n path: z.string().describe('Absolute directory path.'),\n },\n execute: async ({ path }, ctx) => {\n const w = await resolveWs(ws, ctx)\n let entries: string[]\n try {\n entries = await w.fs.readdir(path)\n } catch (err) {\n return `Error: ${errMsg(err)}`\n }\n const lines: string[] = []\n for (const entry of entries) {\n const isDir = await w.fs.isDir(entry)\n lines.push(isDir ? `${entry}/` : entry)\n }\n return lines.join('\\n')\n },\n })\n\n const bash = tool({\n description: 'Execute a shell command and return stdout, stderr, and exit code.',\n args: {\n command: z.string().describe('The shell command to execute.'),\n },\n execute: async ({ command }, ctx) => {\n const w = await resolveWs(ws, ctx)\n const io = await w.execute(command)\n const parts: string[] = []\n if (io.stdoutText.length > 0) parts.push(io.stdoutText)\n if (io.stderrText.length > 0) parts.push(io.stderrText)\n return parts.join('\\n').trim()\n },\n })\n\n const glob = tool({\n description: 'Find files matching a name pattern.',\n args: {\n pattern: z.string().describe('Filename pattern (e.g. \"*.ts\").'),\n path: z.string().optional().describe('Directory to search under. Defaults to /.'),\n },\n execute: async ({ pattern, path }, ctx) => {\n const w = await resolveWs(ws, ctx)\n const root = path ?? '/'\n const io = await w.execute(`find ${root} -name '${pattern.replace(/'/g, \"'\\\\''\")}'`)\n return io.stdoutText.trim()\n },\n })\n\n const grep = tool({\n description: 'Search for a regex pattern in files.',\n args: {\n pattern: z.string().describe('Pattern to search for.'),\n path: z.string().optional().describe('Directory or file to search under. Defaults to /.'),\n },\n execute: async ({ pattern, path }, ctx) => {\n const w = await resolveWs(ws, ctx)\n const root = path ?? '/'\n const escaped = pattern.replace(/'/g, \"'\\\\''\")\n const io = await w.execute(`grep -rn '${escaped}' ${root}`)\n return io.stdoutText.trim()\n },\n })\n\n return { read, write, edit, ls, bash, glob, grep }\n}\n\ninterface Hooks {\n tool?: Record<string, ToolDefinition>\n}\n\ntype PluginFn = (input: unknown) => Promise<Hooks>\n\nexport function miragePlugin(ws: WsLike): PluginFn {\n return () => Promise.resolve({ tool: mirageTools(ws) })\n}\n"],"mappings":"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","names":[]}
package/dist/pi/index.js CHANGED
@@ -16,8 +16,9 @@ import {
16
16
 
17
17
  // src/pi/operations.ts
18
18
  import picomatch from "picomatch";
19
+ import { rstripSlash } from "@struktoai/mirage-core";
19
20
  async function ensureParent(ws, dir) {
20
- const norm = dir.replace(/\/+$/, "") || "/";
21
+ const norm = rstripSlash(dir) || "/";
21
22
  if (norm === "/" || await ws.fs.exists(norm)) return;
22
23
  const parent = norm.substring(0, norm.lastIndexOf("/")) || "/";
23
24
  await ensureParent(ws, parent);
@@ -90,7 +91,7 @@ function mirageOperations(ws) {
90
91
  glob: async (pattern, cwd, options) => {
91
92
  const matcher = picomatch(pattern, { dot: false });
92
93
  const ignoreMatchers = options.ignore.map((p) => picomatch(p, { dot: false }));
93
- const root = cwd.replace(/\/+$/, "") || "/";
94
+ const root = rstripSlash(cwd) || "/";
94
95
  const cwdPrefix = root === "/" ? "/" : `${root}/`;
95
96
  const results = [];
96
97
  await walkDirectory(
@@ -112,7 +113,7 @@ function mirageOperations(ws) {
112
113
  },
113
114
  readdir: async (absolutePath) => {
114
115
  const entries = await ws.fs.readdir(absolutePath);
115
- const prefix = absolutePath === "/" ? "/" : `${absolutePath.replace(/\/+$/, "")}/`;
116
+ const prefix = absolutePath === "/" ? "/" : `${rstripSlash(absolutePath)}/`;
116
117
  return entries.map((e) => e.startsWith(prefix) ? e.slice(prefix.length) : e);
117
118
  }
118
119
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pi/extension.ts","../../src/pi/operations.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport {\n createBashToolDefinition,\n createEditToolDefinition,\n createFindToolDefinition,\n createGrepToolDefinition,\n createLsToolDefinition,\n createReadToolDefinition,\n createWriteToolDefinition,\n type ExtensionFactory,\n} from '@mariozechner/pi-coding-agent'\nimport { mirageOperations } from './operations.ts'\n\nexport interface MirageExtensionOptions {\n cwd?: string\n}\n\nexport function mirageExtension(\n ws: Workspace,\n opts: MirageExtensionOptions = {},\n): ExtensionFactory {\n const cwd = opts.cwd ?? '/'\n const ops = mirageOperations(ws)\n return (pi) => {\n pi.registerTool(createReadToolDefinition(cwd, { operations: ops.read }))\n pi.registerTool(createWriteToolDefinition(cwd, { operations: ops.write }))\n pi.registerTool(createEditToolDefinition(cwd, { operations: ops.edit }))\n pi.registerTool(createBashToolDefinition(cwd, { operations: ops.bash }))\n pi.registerTool(createGrepToolDefinition(cwd, { operations: ops.grep }))\n pi.registerTool(createFindToolDefinition(cwd, { operations: ops.find }))\n pi.registerTool(createLsToolDefinition(cwd, { operations: ops.ls }))\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport type {\n BashOperations,\n EditOperations,\n FindOperations,\n GrepOperations,\n LsOperations,\n ReadOperations,\n WriteOperations,\n} from '@mariozechner/pi-coding-agent'\nimport picomatch from 'picomatch'\n\nexport interface MirageOperationsBundle {\n read: ReadOperations\n write: WriteOperations\n edit: EditOperations\n bash: BashOperations\n grep: GrepOperations\n find: FindOperations\n ls: LsOperations\n}\n\nasync function ensureParent(ws: Workspace, dir: string): Promise<void> {\n const norm = dir.replace(/\\/+$/, '') || '/'\n if (norm === '/' || (await ws.fs.exists(norm))) return\n const parent = norm.substring(0, norm.lastIndexOf('/')) || '/'\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(norm)\n } catch (err) {\n if (await ws.fs.isDir(norm)) return\n throw err\n }\n}\n\ninterface WalkOptions {\n ignoreMatchers: ((path: string) => boolean)[]\n limit: number\n}\n\nasync function walkDirectory(\n ws: Workspace,\n dir: string,\n cwdPrefix: string,\n matcher: (relativePath: string) => boolean,\n opts: WalkOptions,\n results: string[],\n): Promise<void> {\n if (results.length >= opts.limit) return\n let entries: string[]\n try {\n entries = await ws.fs.readdir(dir)\n } catch {\n return\n }\n for (const full of entries) {\n if (results.length >= opts.limit) return\n const rel = full.startsWith(cwdPrefix) ? full.slice(cwdPrefix.length) : full\n if (opts.ignoreMatchers.some((m) => m(rel))) continue\n const isDir = await ws.fs.isDir(full)\n if (matcher(rel)) results.push(full)\n if (isDir) await walkDirectory(ws, full, cwdPrefix, matcher, opts, results)\n }\n}\n\nexport function mirageOperations(ws: Workspace): MirageOperationsBundle {\n const read: ReadOperations = {\n readFile: async (absolutePath: string) => {\n const bytes = await ws.fs.readFile(absolutePath, { raw: true })\n return Buffer.from(bytes)\n },\n access: async (absolutePath: string) => {\n await ws.fs.stat(absolutePath)\n },\n }\n\n const write: WriteOperations = {\n writeFile: async (absolutePath: string, content: string) => {\n await ws.fs.writeFile(absolutePath, content)\n },\n mkdir: async (dir: string) => {\n await ensureParent(ws, dir)\n if (!(await ws.fs.exists(dir))) {\n await ws.fs.mkdir(dir)\n }\n },\n }\n\n const edit: EditOperations = {\n readFile: read.readFile,\n writeFile: write.writeFile,\n access: read.access,\n }\n\n const bash: BashOperations = {\n exec: async (command, _cwd, options) => {\n const result = await ws.execute(command)\n const combined = result.stdoutText + result.stderrText\n if (combined.length > 0) {\n options.onData(Buffer.from(combined))\n }\n return { exitCode: result.exitCode }\n },\n }\n\n const grep: GrepOperations = {\n isDirectory: async (absolutePath: string) => ws.fs.isDir(absolutePath),\n readFile: async (absolutePath: string) => ws.fs.readFileText(absolutePath),\n }\n\n const find: FindOperations = {\n exists: async (absolutePath: string) => ws.fs.exists(absolutePath),\n glob: async (pattern, cwd, options) => {\n const matcher = picomatch(pattern, { dot: false })\n const ignoreMatchers = options.ignore.map((p) => picomatch(p, { dot: false }))\n const root = cwd.replace(/\\/+$/, '') || '/'\n const cwdPrefix = root === '/' ? '/' : `${root}/`\n const results: string[] = []\n await walkDirectory(\n ws,\n root,\n cwdPrefix,\n matcher,\n { ignoreMatchers, limit: options.limit },\n results,\n )\n return results\n },\n }\n\n const ls: LsOperations = {\n exists: async (absolutePath: string) => ws.fs.exists(absolutePath),\n stat: async (absolutePath: string) => {\n const isDir = await ws.fs.isDir(absolutePath)\n return { isDirectory: () => isDir }\n },\n readdir: async (absolutePath: string) => {\n const entries = await ws.fs.readdir(absolutePath)\n const prefix = absolutePath === '/' ? '/' : `${absolutePath.replace(/\\/+$/, '')}/`\n return entries.map((e) => (e.startsWith(prefix) ? e.slice(prefix.length) : e))\n },\n }\n\n return { read, write, edit, bash, grep, find, ls }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/pi/extension.ts","../../src/pi/operations.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport {\n createBashToolDefinition,\n createEditToolDefinition,\n createFindToolDefinition,\n createGrepToolDefinition,\n createLsToolDefinition,\n createReadToolDefinition,\n createWriteToolDefinition,\n type ExtensionFactory,\n} from '@mariozechner/pi-coding-agent'\nimport { mirageOperations } from './operations.ts'\n\nexport interface MirageExtensionOptions {\n cwd?: string\n}\n\nexport function mirageExtension(\n ws: Workspace,\n opts: MirageExtensionOptions = {},\n): ExtensionFactory {\n const cwd = opts.cwd ?? '/'\n const ops = mirageOperations(ws)\n return (pi) => {\n pi.registerTool(createReadToolDefinition(cwd, { operations: ops.read }))\n pi.registerTool(createWriteToolDefinition(cwd, { operations: ops.write }))\n pi.registerTool(createEditToolDefinition(cwd, { operations: ops.edit }))\n pi.registerTool(createBashToolDefinition(cwd, { operations: ops.bash }))\n pi.registerTool(createGrepToolDefinition(cwd, { operations: ops.grep }))\n pi.registerTool(createFindToolDefinition(cwd, { operations: ops.find }))\n pi.registerTool(createLsToolDefinition(cwd, { operations: ops.ls }))\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { Workspace } from '@struktoai/mirage-core'\nimport type {\n BashOperations,\n EditOperations,\n FindOperations,\n GrepOperations,\n LsOperations,\n ReadOperations,\n WriteOperations,\n} from '@mariozechner/pi-coding-agent'\nimport picomatch from 'picomatch'\nimport { rstripSlash } from '@struktoai/mirage-core'\n\nexport interface MirageOperationsBundle {\n read: ReadOperations\n write: WriteOperations\n edit: EditOperations\n bash: BashOperations\n grep: GrepOperations\n find: FindOperations\n ls: LsOperations\n}\n\nasync function ensureParent(ws: Workspace, dir: string): Promise<void> {\n const norm = rstripSlash(dir) || '/'\n if (norm === '/' || (await ws.fs.exists(norm))) return\n const parent = norm.substring(0, norm.lastIndexOf('/')) || '/'\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(norm)\n } catch (err) {\n if (await ws.fs.isDir(norm)) return\n throw err\n }\n}\n\ninterface WalkOptions {\n ignoreMatchers: ((path: string) => boolean)[]\n limit: number\n}\n\nasync function walkDirectory(\n ws: Workspace,\n dir: string,\n cwdPrefix: string,\n matcher: (relativePath: string) => boolean,\n opts: WalkOptions,\n results: string[],\n): Promise<void> {\n if (results.length >= opts.limit) return\n let entries: string[]\n try {\n entries = await ws.fs.readdir(dir)\n } catch {\n return\n }\n for (const full of entries) {\n if (results.length >= opts.limit) return\n const rel = full.startsWith(cwdPrefix) ? full.slice(cwdPrefix.length) : full\n if (opts.ignoreMatchers.some((m) => m(rel))) continue\n const isDir = await ws.fs.isDir(full)\n if (matcher(rel)) results.push(full)\n if (isDir) await walkDirectory(ws, full, cwdPrefix, matcher, opts, results)\n }\n}\n\nexport function mirageOperations(ws: Workspace): MirageOperationsBundle {\n const read: ReadOperations = {\n readFile: async (absolutePath: string) => {\n const bytes = await ws.fs.readFile(absolutePath, { raw: true })\n return Buffer.from(bytes)\n },\n access: async (absolutePath: string) => {\n await ws.fs.stat(absolutePath)\n },\n }\n\n const write: WriteOperations = {\n writeFile: async (absolutePath: string, content: string) => {\n await ws.fs.writeFile(absolutePath, content)\n },\n mkdir: async (dir: string) => {\n await ensureParent(ws, dir)\n if (!(await ws.fs.exists(dir))) {\n await ws.fs.mkdir(dir)\n }\n },\n }\n\n const edit: EditOperations = {\n readFile: read.readFile,\n writeFile: write.writeFile,\n access: read.access,\n }\n\n const bash: BashOperations = {\n exec: async (command, _cwd, options) => {\n const result = await ws.execute(command)\n const combined = result.stdoutText + result.stderrText\n if (combined.length > 0) {\n options.onData(Buffer.from(combined))\n }\n return { exitCode: result.exitCode }\n },\n }\n\n const grep: GrepOperations = {\n isDirectory: async (absolutePath: string) => ws.fs.isDir(absolutePath),\n readFile: async (absolutePath: string) => ws.fs.readFileText(absolutePath),\n }\n\n const find: FindOperations = {\n exists: async (absolutePath: string) => ws.fs.exists(absolutePath),\n glob: async (pattern, cwd, options) => {\n const matcher = picomatch(pattern, { dot: false })\n const ignoreMatchers = options.ignore.map((p) => picomatch(p, { dot: false }))\n const root = rstripSlash(cwd) || '/'\n const cwdPrefix = root === '/' ? '/' : `${root}/`\n const results: string[] = []\n await walkDirectory(\n ws,\n root,\n cwdPrefix,\n matcher,\n { ignoreMatchers, limit: options.limit },\n results,\n )\n return results\n },\n }\n\n const ls: LsOperations = {\n exists: async (absolutePath: string) => ws.fs.exists(absolutePath),\n stat: async (absolutePath: string) => {\n const isDir = await ws.fs.isDir(absolutePath)\n return { isDirectory: () => isDir }\n },\n readdir: async (absolutePath: string) => {\n const entries = await ws.fs.readdir(absolutePath)\n const prefix = absolutePath === '/' ? '/' : `${rstripSlash(absolutePath)}/`\n return entries.map((e) => (e.startsWith(prefix) ? e.slice(prefix.length) : e))\n },\n }\n\n return { read, write, edit, bash, grep, find, ls }\n}\n"],"mappings":"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","names":[]}
@@ -1,16 +1,10 @@
1
1
  // src/vercel/index.ts
2
- import { encodeBase64 } from "@struktoai/mirage-core";
2
+ import { encodeBase64, gnuDirname } from "@struktoai/mirage-core";
3
3
  import { tool } from "ai";
4
4
  import { z } from "zod";
5
- function parentOf(path) {
6
- const trimmed = path.replace(/\/+$/, "");
7
- const idx = trimmed.lastIndexOf("/");
8
- if (idx <= 0) return "/";
9
- return trimmed.slice(0, idx);
10
- }
11
5
  async function ensureParent(ws, path) {
12
- const parent = parentOf(path);
13
- if (parent === "/" || parent === "") return;
6
+ const parent = gnuDirname(path);
7
+ if (parent === "/" || parent === "" || parent === ".") return;
14
8
  if (await ws.fs.exists(parent)) return;
15
9
  await ensureParent(ws, parent);
16
10
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vercel/index.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport { encodeBase64 } from '@struktoai/mirage-core'\nimport type { Workspace } from '@struktoai/mirage-node'\nimport { tool } from 'ai'\nimport { z } from 'zod'\n\nfunction parentOf(path: string): string {\n const trimmed = path.replace(/\\/+$/, '')\n const idx = trimmed.lastIndexOf('/')\n if (idx <= 0) return '/'\n return trimmed.slice(0, idx)\n}\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = parentOf(path)\n if (parent === '/' || parent === '') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nconst TEXT_EXTS = new Set([\n 'txt',\n 'md',\n 'json',\n 'jsonl',\n 'yaml',\n 'yml',\n 'csv',\n 'tsv',\n 'xml',\n 'html',\n 'htm',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nconst PRESENTABLE_BINARY = new Set([\n 'application/pdf',\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n])\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n if (dot < 0) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nfunction mimeFor(path: string): string {\n const ext = extOf(path)\n if (ext === 'json' || ext === 'jsonl') return 'application/json'\n if (ext === 'csv') return 'text/csv'\n if (ext === 'html' || ext === 'htm') return 'text/html'\n if (ext === 'md') return 'text/markdown'\n if (TEXT_EXTS.has(ext)) return 'text/plain'\n if (ext === 'png') return 'image/png'\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n if (ext === 'gif') return 'image/gif'\n if (ext === 'webp') return 'image/webp'\n if (ext === 'pdf') return 'application/pdf'\n return 'application/octet-stream'\n}\n\nfunction isTextMime(mime: string): boolean {\n return mime.startsWith('text/') || mime === 'application/json'\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n return encodeBase64(bytes)\n}\n\ntype ReadFileResult =\n | { kind: 'text'; path: string; mimeType: string; content: string; bytes: number }\n | { kind: 'media'; path: string; mimeType: string; base64: string; bytes: number }\n | { kind: 'binary'; path: string; mimeType: string; bytes: number; note: string }\n | { error: string }\n\nfunction isError(r: ReadFileResult): r is { error: string } {\n return 'error' in r\n}\n\nexport function mirageTools(ws: Workspace) {\n return {\n execute: tool({\n description:\n 'Execute a shell command in the Mirage workspace and return stdout, stderr, and exitCode.',\n inputSchema: z.object({\n command: z.string().describe('The shell command to execute.'),\n }),\n execute: async ({ command }) => {\n const io = await ws.execute(command)\n return {\n stdout: io.stdoutText,\n stderr: io.stderrText,\n exitCode: io.exitCode,\n }\n },\n }),\n\n readFile: tool({\n description:\n 'Read a file from the Mirage workspace. Text files (utf-8 source/data) come back as text; PDFs and images (png/jpeg/gif/webp) come back as base64 media that is forwarded to the model as a multimodal attachment; other binaries return a metadata stub.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n }),\n execute: async ({ path }): Promise<ReadFileResult> => {\n let bytes: Uint8Array\n try {\n bytes = await ws.fs.readFile(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const mimeType = mimeFor(path)\n if (isTextMime(mimeType)) {\n return {\n kind: 'text',\n path,\n mimeType,\n content: new TextDecoder('utf-8', { fatal: false }).decode(bytes),\n bytes: bytes.length,\n }\n }\n if (PRESENTABLE_BINARY.has(mimeType)) {\n return {\n kind: 'media',\n path,\n mimeType,\n base64: bytesToBase64(bytes),\n bytes: bytes.length,\n }\n }\n return {\n kind: 'binary',\n path,\n mimeType,\n bytes: bytes.length,\n note: `Binary file ${path} (${mimeType}, ${String(bytes.length)} bytes). Use the execute tool with shell commands (head, file, wc, od) to inspect.`,\n }\n },\n toModelOutput: ({ output }) => {\n const out: ReadFileResult = output\n if (isError(out)) return { type: 'error-text', value: out.error }\n if (out.kind === 'text') return { type: 'text', value: out.content }\n if (out.kind === 'media') {\n return {\n type: 'content',\n value: [\n {\n type: 'text',\n text: `[${out.path}] ${out.mimeType} (${String(out.bytes)} bytes)`,\n },\n { type: 'media', data: out.base64, mediaType: out.mimeType },\n ],\n }\n }\n return { type: 'text', value: out.note }\n },\n }),\n\n writeFile: tool({\n description:\n 'Write content to a file in the Mirage workspace. Creates missing parent directories.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n content: z.string().describe('UTF-8 text content to write.'),\n }),\n execute: async ({ path, content }) => {\n await ensureParent(ws, path)\n await ws.fs.writeFile(path, content)\n return { path }\n },\n }),\n\n editFile: tool({\n description:\n 'Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path of the file to edit.'),\n oldString: z.string().describe('The exact string to replace.'),\n newString: z.string().describe('The replacement string.'),\n replaceAll: z\n .boolean()\n .optional()\n .describe('Replace every occurrence rather than requiring a unique match.'),\n }),\n execute: async ({ path, oldString, newString, replaceAll }) => {\n let current: string\n try {\n current = await ws.fs.readFileText(path)\n } catch {\n return { error: `Error: file '${path}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && replaceAll !== true) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next =\n replaceAll === true\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await ws.fs.writeFile(path, next)\n return { path, occurrences: replaceAll === true ? count : 1 }\n },\n }),\n\n ls: tool({\n description: 'List entries of a directory in the Mirage workspace.',\n inputSchema: z.object({\n path: z.string().describe('Absolute directory path inside the workspace.'),\n }),\n execute: async ({ path }) => {\n let entries: string[]\n try {\n entries = await ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: { path: string; is_dir: boolean }[] = []\n for (const entry of entries) {\n const isDir = await ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n },\n }),\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/vercel/index.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport { encodeBase64, gnuDirname } from '@struktoai/mirage-core'\nimport type { Workspace } from '@struktoai/mirage-node'\nimport { tool } from 'ai'\nimport { z } from 'zod'\n\nasync function ensureParent(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParent(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nconst TEXT_EXTS = new Set([\n 'txt',\n 'md',\n 'json',\n 'jsonl',\n 'yaml',\n 'yml',\n 'csv',\n 'tsv',\n 'xml',\n 'html',\n 'htm',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nconst PRESENTABLE_BINARY = new Set([\n 'application/pdf',\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n])\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n if (dot < 0) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nfunction mimeFor(path: string): string {\n const ext = extOf(path)\n if (ext === 'json' || ext === 'jsonl') return 'application/json'\n if (ext === 'csv') return 'text/csv'\n if (ext === 'html' || ext === 'htm') return 'text/html'\n if (ext === 'md') return 'text/markdown'\n if (TEXT_EXTS.has(ext)) return 'text/plain'\n if (ext === 'png') return 'image/png'\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n if (ext === 'gif') return 'image/gif'\n if (ext === 'webp') return 'image/webp'\n if (ext === 'pdf') return 'application/pdf'\n return 'application/octet-stream'\n}\n\nfunction isTextMime(mime: string): boolean {\n return mime.startsWith('text/') || mime === 'application/json'\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n return encodeBase64(bytes)\n}\n\ntype ReadFileResult =\n | { kind: 'text'; path: string; mimeType: string; content: string; bytes: number }\n | { kind: 'media'; path: string; mimeType: string; base64: string; bytes: number }\n | { kind: 'binary'; path: string; mimeType: string; bytes: number; note: string }\n | { error: string }\n\nfunction isError(r: ReadFileResult): r is { error: string } {\n return 'error' in r\n}\n\nexport function mirageTools(ws: Workspace) {\n return {\n execute: tool({\n description:\n 'Execute a shell command in the Mirage workspace and return stdout, stderr, and exitCode.',\n inputSchema: z.object({\n command: z.string().describe('The shell command to execute.'),\n }),\n execute: async ({ command }) => {\n const io = await ws.execute(command)\n return {\n stdout: io.stdoutText,\n stderr: io.stderrText,\n exitCode: io.exitCode,\n }\n },\n }),\n\n readFile: tool({\n description:\n 'Read a file from the Mirage workspace. Text files (utf-8 source/data) come back as text; PDFs and images (png/jpeg/gif/webp) come back as base64 media that is forwarded to the model as a multimodal attachment; other binaries return a metadata stub.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n }),\n execute: async ({ path }): Promise<ReadFileResult> => {\n let bytes: Uint8Array\n try {\n bytes = await ws.fs.readFile(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const mimeType = mimeFor(path)\n if (isTextMime(mimeType)) {\n return {\n kind: 'text',\n path,\n mimeType,\n content: new TextDecoder('utf-8', { fatal: false }).decode(bytes),\n bytes: bytes.length,\n }\n }\n if (PRESENTABLE_BINARY.has(mimeType)) {\n return {\n kind: 'media',\n path,\n mimeType,\n base64: bytesToBase64(bytes),\n bytes: bytes.length,\n }\n }\n return {\n kind: 'binary',\n path,\n mimeType,\n bytes: bytes.length,\n note: `Binary file ${path} (${mimeType}, ${String(bytes.length)} bytes). Use the execute tool with shell commands (head, file, wc, od) to inspect.`,\n }\n },\n toModelOutput: ({ output }) => {\n const out: ReadFileResult = output\n if (isError(out)) return { type: 'error-text', value: out.error }\n if (out.kind === 'text') return { type: 'text', value: out.content }\n if (out.kind === 'media') {\n return {\n type: 'content',\n value: [\n {\n type: 'text',\n text: `[${out.path}] ${out.mimeType} (${String(out.bytes)} bytes)`,\n },\n { type: 'media', data: out.base64, mediaType: out.mimeType },\n ],\n }\n }\n return { type: 'text', value: out.note }\n },\n }),\n\n writeFile: tool({\n description:\n 'Write content to a file in the Mirage workspace. Creates missing parent directories.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path inside the workspace.'),\n content: z.string().describe('UTF-8 text content to write.'),\n }),\n execute: async ({ path, content }) => {\n await ensureParent(ws, path)\n await ws.fs.writeFile(path, content)\n return { path }\n },\n }),\n\n editFile: tool({\n description:\n 'Replace a string inside an existing file. Errors if the string appears more than once unless replaceAll is true.',\n inputSchema: z.object({\n path: z.string().describe('Absolute path of the file to edit.'),\n oldString: z.string().describe('The exact string to replace.'),\n newString: z.string().describe('The replacement string.'),\n replaceAll: z\n .boolean()\n .optional()\n .describe('Replace every occurrence rather than requiring a unique match.'),\n }),\n execute: async ({ path, oldString, newString, replaceAll }) => {\n let current: string\n try {\n current = await ws.fs.readFileText(path)\n } catch {\n return { error: `Error: file '${path}' not found` }\n }\n const count = current.split(oldString).length - 1\n if (count === 0) {\n return { error: `Error: string not found in file: '${oldString}'` }\n }\n if (count > 1 && replaceAll !== true) {\n return {\n error: `Error: string '${oldString}' appears ${String(count)} times. Use replaceAll=true`,\n }\n }\n const next =\n replaceAll === true\n ? current.split(oldString).join(newString)\n : current.replace(oldString, newString)\n await ws.fs.writeFile(path, next)\n return { path, occurrences: replaceAll === true ? count : 1 }\n },\n }),\n\n ls: tool({\n description: 'List entries of a directory in the Mirage workspace.',\n inputSchema: z.object({\n path: z.string().describe('Absolute directory path inside the workspace.'),\n }),\n execute: async ({ path }) => {\n let entries: string[]\n try {\n entries = await ws.fs.readdir(path)\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) }\n }\n const files: { path: string; is_dir: boolean }[] = []\n for (const entry of entries) {\n const isDir = await ws.fs.isDir(entry)\n files.push({ path: entry, is_dir: isDir })\n }\n return { files }\n },\n }),\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@struktoai/mirage-agents",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "A Unified Virtual Filesystem For AI Agents",
5
5
  "keywords": [
6
6
  "bash",
@@ -57,6 +57,10 @@
57
57
  "./mastra": {
58
58
  "types": "./dist/mastra/index.d.ts",
59
59
  "import": "./dist/mastra/index.js"
60
+ },
61
+ "./opencode": {
62
+ "types": "./dist/opencode/index.d.ts",
63
+ "import": "./dist/opencode/index.js"
60
64
  }
61
65
  },
62
66
  "files": [
@@ -64,7 +68,7 @@
64
68
  ],
65
69
  "dependencies": {
66
70
  "picomatch": "^4.0.4",
67
- "@struktoai/mirage-core": "0.0.1"
71
+ "@struktoai/mirage-core": "0.0.2"
68
72
  },
69
73
  "peerDependencies": {
70
74
  "@mariozechner/pi-ai": "^0.70.0",
@@ -85,9 +89,9 @@
85
89
  "deepagents": "^1.9.0",
86
90
  "tsup": "^8.5.0",
87
91
  "typescript": "^6.0.0",
88
- "vitest": "^3.0.0",
92
+ "vitest": "^3.2.6",
89
93
  "zod": "^4.0.0",
90
- "@struktoai/mirage-node": "0.0.1"
94
+ "@struktoai/mirage-node": "0.0.2"
91
95
  },
92
96
  "scripts": {
93
97
  "build": "tsup",