{"version":3,"file":"workspace-SsEU6Si6.cjs","names":["posixPath","PermissionError","MastraBase","RegisteredLogger","FilesystemNotReadyError","resolveToBasePath","nodePath","expandTilde","isEnoentError","PermissionError","WorkspaceReadOnlyError","fs","IsDirectoryError","FileNotFoundError","NotDirectoryError","DirectoryNotFoundError","StaleFileError","isEexistError","FileExistsError","fsConstants","DirectoryNotEmptyError","fsExists","fsStat","path","nodePath","nodePath","fs","Readable","Writable","StringDecoder","MastraBase","RegisteredLogger","path","expandTilde","fs","#tokenizeOptions","#documents","#docCount","#invertedIndex","#documentFrequency","#updateAvgDocLength","#avgDocLength","#computeIDF","#computeTermScore","#tokenizeOptions","#bm25Index","#vectorConfig","#lazyVectorIndex","#indexedIds","#pendingVectorDocs","#vectorIndexBuilt","#indexVector","#determineSearchMode","#searchBM25","#searchVector","#searchHybrid","#embedOne","#embedAll","#vectorIndexReady","#flushVectorBatch","#ensureVectorIndex","#dedupePendingVectorDocsLastWins","#adjustLineRange","#normalizeBM25Scores","#tree","#blobStore","#versionCreatedAt","#directories","#computeDirectories","#normalizePath","#sources","#fallback","#fallbackSkills","#maxVersionCreatedAt","#normalizePath","#routePath","joinPath","createTool","z","WorkspaceError","LocalSkillSource","WorkspaceSkillsImpl","SearchNotAvailableError","path","resolvePathPattern","pMapSkip","RequestContext","WorkspaceNotAvailableError","FilesystemNotAvailableError","SandboxNotAvailableError","createTool","z","WorkspaceReadOnlyError","FileNotFoundError","createTool","z","WorkspaceReadOnlyError","createTool","z","WorkspaceReadOnlyError","z","SandboxFeatureNotSupportedError","createTool","createTool","z","FileNotFoundError","createTool","z","SandboxFeatureNotSupportedError","createTool","z","isGlobPattern","extractGlobBase","createGlobMatcher","isTextFile","createTool","z","createTool","z","SandboxFeatureNotSupportedError","createGlobMatcher","createTool","z","fs","createTool","z","createTool","z","WorkspaceReadOnlyError","createTool","z","z","createTool","createTool","z","WorkspaceReadOnlyError","RequestContext","FileReadRequiredError","FileNotFoundError","z"],"sources":["../src/workspace/lifecycle.ts","../src/workspace/filesystem/composite-filesystem.ts","../src/workspace/filesystem/mastra-filesystem.ts","../src/workspace/utils.ts","../src/workspace/filesystem/local-filesystem.ts","../src/workspace/filesystem/file-read-tracker.ts","../src/workspace/filesystem/file-write-lock.ts","../src/workspace/lsp/language.ts","../src/workspace/lsp/client.ts","../src/workspace/lsp/servers.ts","../src/workspace/lsp/manager.ts","../src/workspace/sandbox/errors.ts","../src/workspace/sandbox/execa.ts","../src/workspace/sandbox/process-manager/process-handle.ts","../src/workspace/sandbox/process-manager/process-manager.ts","../src/workspace/sandbox/local-process-manager.ts","../src/workspace/sandbox/mounts/types.ts","../src/workspace/sandbox/mount-manager.ts","../src/workspace/sandbox/utils.ts","../src/workspace/sandbox/mastra-sandbox.ts","../src/workspace/sandbox/native-sandbox/detect.ts","../src/workspace/sandbox/native-sandbox/seatbelt.ts","../src/workspace/sandbox/native-sandbox/bubblewrap.ts","../src/workspace/sandbox/native-sandbox/wrapper.ts","../src/workspace/sandbox/local-sandbox.ts","../src/workspace/line-utils.ts","../src/workspace/search/bm25.ts","../src/workspace/search/search-engine.ts","../src/workspace/skills/versioned-skill-source.ts","../src/workspace/skills/composite-versioned-skill-source.ts","../src/workspace/skills/publish.ts","../src/workspace/tools/tracing.ts","../src/workspace/skills/tools.ts","../src/workspace/workspace.ts","../src/workspace/sandbox/sandbox.ts","../src/workspace/constants/index.ts","../src/workspace/tools/helpers.ts","../src/workspace/tools/ast-edit.ts","../src/workspace/tools/delete-file.ts","../src/workspace/tools/edit-file.ts","../src/browser/cli-handler.ts","../src/workspace/tools/output-helpers.ts","../src/workspace/tools/execute-command.ts","../src/workspace/tools/file-stat.ts","../src/workspace/tools/get-process-output.ts","../src/workspace/gitignore.ts","../src/workspace/tools/grep.ts","../src/workspace/tools/index-content.ts","../src/workspace/tools/kill-process.ts","../src/workspace/tools/tree-formatter.ts","../src/workspace/tools/list-files.ts","../src/workspace/tools/lsp-inspect.ts","../src/workspace/tools/mkdir.ts","../src/workspace/tools/read-file.ts","../src/workspace/tools/search.ts","../src/workspace/tools/write-file.ts","../src/workspace/tools/tools.ts"],"sourcesContent":["/**\n * Workspace Lifecycle Interfaces\n *\n * Defines lifecycle contracts for workspace providers (filesystem, sandbox).\n * The base `Lifecycle` holds shared members while `FilesystemLifecycle` and\n * `SandboxLifecycle` add the methods each provider kind actually uses.\n */\n\n// =============================================================================\n// Base Lifecycle Interface\n// =============================================================================\n\n/**\n * Shared lifecycle base for workspace providers.\n *\n * Contains status tracking, destroy, readiness check, and info retrieval.\n * Provider-specific lifecycle methods live in the extended interfaces:\n * - {@link FilesystemLifecycle} adds `init()`\n * - {@link SandboxLifecycle} adds `start()` / `stop()`\n *\n * @typeParam TInfo - The type returned by getInfo() (e.g., FilesystemInfo, SandboxInfo)\n */\nexport interface Lifecycle<TInfo = unknown> {\n  /** Current status */\n  status: ProviderStatus;\n\n  /** Error message when status is 'error' */\n  error?: string;\n\n  /**\n   * Clean up all resources.\n   *\n   * Called when the workspace is being permanently shut down.\n   * Use for operations like:\n   * - Terminating cloud instances\n   * - Closing all connections\n   * - Cleaning up temporary files\n   */\n  destroy?(): void | Promise<void>;\n\n  /** @deprecated Use `status === 'running'` instead. */\n  isReady?(): boolean | Promise<boolean>;\n\n  /**\n   * Get status and metadata.\n   *\n   * Returns information about the current state of the provider.\n   */\n  getInfo?(): TInfo | Promise<TInfo>;\n}\n\n// =============================================================================\n// Filesystem Lifecycle\n// =============================================================================\n\n/**\n * Lifecycle interface for filesystem providers (two-phase: init → destroy).\n *\n * @typeParam TInfo - The type returned by getInfo()\n */\nexport interface FilesystemLifecycle<TInfo = unknown> extends Lifecycle<TInfo> {\n  /**\n   * One-time setup operations.\n   *\n   * Called once when the workspace is first initialized.\n   * Use for operations like:\n   * - Creating base directories\n   * - Setting up database tables\n   * - Provisioning cloud resources\n   * - Installing dependencies\n   */\n  init?(): void | Promise<void>;\n}\n\n// =============================================================================\n// Sandbox Lifecycle\n// =============================================================================\n\n/**\n * Lifecycle interface for sandbox providers (three-phase: start → stop → destroy).\n *\n * @typeParam TInfo - The type returned by getInfo()\n */\nexport interface SandboxLifecycle<TInfo = unknown> extends Lifecycle<TInfo> {\n  /**\n   * Begin active operation.\n   *\n   * Called to transition from initialized to running state.\n   * Use for operations like:\n   * - Establishing connection pools\n   * - Spinning up cloud instances\n   * - Starting background processes\n   * - Warming up caches\n   */\n  start?(): void | Promise<void>;\n\n  /**\n   * Pause operation, keeping state for potential restart.\n   *\n   * Called to temporarily stop without full cleanup.\n   * Use for operations like:\n   * - Closing connections (but keeping config)\n   * - Pausing cloud instances\n   * - Flushing buffers\n   */\n  stop?(): void | Promise<void>;\n}\n\n// =============================================================================\n// Status Types\n// =============================================================================\n\n/**\n * Common status values for stateful providers.\n *\n * Not all providers need status tracking - local/stateless providers\n * may not use this. But providers with connection pools or cloud\n * instances can use these states.\n */\nexport type ProviderStatus =\n  | 'pending' // Created but not initialized\n  | 'initializing' // Running init()\n  | 'ready' // Initialized, waiting to start (or stateless and ready)\n  | 'starting' // Running start()\n  | 'running' // Active and accepting requests\n  | 'stopping' // Running stop()\n  | 'stopped' // Stopped but can restart\n  | 'destroying' // Running destroy()\n  | 'destroyed' // Fully cleaned up\n  | 'error'; // Something went wrong\n\n// =============================================================================\n// Lifecycle Helper\n// =============================================================================\n\n/**\n * Provider that may have lifecycle methods.\n * Used by `callLifecycle` to dispatch to the correct method.\n */\ninterface LifecycleProvider {\n  _init?(): void | Promise<void>;\n  _start?(): void | Promise<void>;\n  _stop?(): void | Promise<void>;\n  _destroy?(): void | Promise<void>;\n  init?(): void | Promise<void>;\n  start?(): void | Promise<void>;\n  stop?(): void | Promise<void>;\n  destroy?(): void | Promise<void>;\n}\n\n/**\n * Call a lifecycle method on a provider, preferring the `_`-prefixed wrapper\n * (which adds status tracking & race-condition safety) when available,\n * falling back to the plain method for interface-only implementations.\n *\n * @example\n * ```typescript\n * await callLifecycle(sandbox, 'start');   // calls sandbox._start() ?? sandbox.start()\n * await callLifecycle(filesystem, 'init'); // calls filesystem._init() ?? filesystem.init()\n * ```\n */\nexport async function callLifecycle(\n  provider: LifecycleProvider,\n  method: 'init' | 'start' | 'stop' | 'destroy',\n): Promise<void> {\n  const wrapped = `_${method}` as const;\n  const wrappedFn = provider[wrapped];\n  if (typeof wrappedFn === 'function') {\n    await wrappedFn.call(provider);\n  } else {\n    const plainFn = provider[method];\n    if (typeof plainFn === 'function') {\n      await plainFn.call(provider);\n    }\n  }\n}\n","/**\n * CompositeFilesystem - Routes operations to mounted filesystems based on path.\n *\n * Creates a unified filesystem view by combining multiple filesystems at different\n * mount points. Useful for composing local storage, S3, and other backends.\n *\n * @example\n * ```typescript\n * const cfs = new CompositeFilesystem({\n *   mounts: {\n *     '/local': new LocalFilesystem({ basePath: './data' }),\n *     '/s3': new S3Filesystem({ bucket: 'my-bucket', ... }),\n *   }\n * });\n *\n * // readdir('/') returns ['local', 's3']\n * // readFile('/local/file.txt') reads from LocalFilesystem\n * // readFile('/s3/data.json') reads from S3Filesystem\n * ```\n */\n\nimport posixPath from 'node:path/posix';\n\nimport type { RequestContext } from '../../request-context';\nimport { PermissionError } from '../errors';\nimport { callLifecycle } from '../lifecycle';\nimport type { ProviderStatus } from '../lifecycle';\nimport type {\n  WorkspaceFilesystem,\n  FileContent,\n  FileEntry,\n  FileStat,\n  FilesystemInfo,\n  ReadOptions,\n  WriteOptions,\n  ListOptions,\n  CopyOptions,\n  RemoveOptions,\n} from './filesystem';\n\n/**\n * Configuration for CompositeFilesystem.\n */\nexport interface CompositeFilesystemConfig<\n  TMounts extends Record<string, WorkspaceFilesystem> = Record<string, WorkspaceFilesystem>,\n> {\n  /** Map of mount paths to filesystem instances */\n  mounts: TMounts;\n}\n\ninterface ResolvedMount {\n  fs: WorkspaceFilesystem;\n  fsPath: string;\n  mountPath: string;\n}\n\n/**\n * CompositeFilesystem implementation.\n *\n * Routes file operations to the appropriate underlying filesystem based on path.\n * Supports cross-mount operations (copy/move between different filesystems).\n *\n * The generic parameter preserves the concrete types of mounted filesystems,\n * enabling typed access via `mounts.get()`.\n *\n * @example\n * ```typescript\n * const cfs = new CompositeFilesystem({\n *   mounts: {\n *     '/local': new LocalFilesystem({ basePath: './data' }),\n *     '/s3': new S3Filesystem({ bucket: 'my-bucket' }),\n *   },\n * });\n *\n * cfs.mounts.get('/local') // LocalFilesystem\n * cfs.mounts.get('/s3')    // S3Filesystem\n * ```\n */\nexport class CompositeFilesystem<\n  TMounts extends Record<string, WorkspaceFilesystem> = Record<string, WorkspaceFilesystem>,\n> implements WorkspaceFilesystem {\n  readonly id: string;\n  readonly name = 'CompositeFilesystem';\n  readonly provider = 'composite';\n\n  readonly readOnly?: boolean;\n  status: ProviderStatus = 'ready';\n\n  private readonly _mounts: Map<string, WorkspaceFilesystem>;\n\n  constructor(config: CompositeFilesystemConfig<TMounts>) {\n    this.id = `cfs-${Date.now().toString(36)}`;\n    this._mounts = new Map();\n\n    for (const [path, fs] of Object.entries(config.mounts)) {\n      const normalized = this.normalizePath(path);\n      this._mounts.set(normalized, fs);\n    }\n\n    if (this._mounts.size === 0) {\n      throw new Error('CompositeFilesystem requires at least one mount');\n    }\n\n    // Composite is read-only when every mount is read-only\n    this.readOnly = [...this._mounts.values()].every(fs => fs.readOnly) || undefined;\n\n    // Validate no nested mount paths (e.g., /data and /data/sub)\n    const mountPaths = [...this._mounts.keys()];\n    for (const a of mountPaths) {\n      for (const b of mountPaths) {\n        if (a !== b && b.startsWith(a + '/')) {\n          throw new Error(`Nested mount paths are not supported: \"${b}\" is nested under \"${a}\"`);\n        }\n      }\n    }\n  }\n\n  /**\n   * Get all mount paths.\n   */\n  get mountPaths(): string[] {\n    return Array.from(this._mounts.keys());\n  }\n\n  /**\n   * Get the mounts map.\n   * Returns a typed map where `get()` preserves the concrete filesystem type per mount path.\n   */\n  get mounts(): ReadonlyMountMap<TMounts> {\n    return this._mounts as unknown as ReadonlyMountMap<TMounts>;\n  }\n\n  /**\n   * Get status and metadata for this composite filesystem.\n   * Includes info from each mounted filesystem in `metadata.mounts`.\n   */\n  async getInfo(): Promise<FilesystemInfo> {\n    const mounts: Record<string, FilesystemInfo | null> = {};\n    for (const [mountPath, fs] of this._mounts) {\n      mounts[mountPath] = (await fs.getInfo?.()) ?? null;\n    }\n\n    return {\n      id: this.id,\n      name: this.name,\n      provider: this.provider,\n      status: this.status,\n      readOnly: this.readOnly,\n      metadata: { mounts },\n    };\n  }\n\n  /**\n   * Get the underlying filesystem for a given path.\n   * Returns undefined if the path doesn't resolve to any mount.\n   */\n  getFilesystemForPath(path: string): WorkspaceFilesystem | undefined {\n    const resolved = this.resolveMount(path);\n    return resolved?.fs;\n  }\n\n  /**\n   * Get the mount path for a given path.\n   * Returns undefined if the path doesn't resolve to any mount.\n   */\n  getMountPathForPath(path: string): string | undefined {\n    const resolved = this.resolveMount(path);\n    return resolved?.mountPath;\n  }\n\n  /**\n   * Resolve a workspace-relative path to an absolute disk path.\n   * Strips the mount prefix and delegates to the underlying filesystem.\n   */\n  resolveAbsolutePath(path: string): string | undefined {\n    const r = this.resolveMount(path);\n    if (!r) return undefined;\n    return r.fs.resolveAbsolutePath?.(r.fsPath);\n  }\n\n  private normalizePath(path: string): string {\n    if (!path || path === '/' || path === '.') return '/';\n    // posix.normalize resolves dot segments (./foo → foo, a/../b → b)\n    let n = posixPath.normalize(path);\n    if (n === '.') return '/';\n    if (!n.startsWith('/')) n = `/${n}`;\n    if (n.length > 1 && n.endsWith('/')) n = n.slice(0, -1);\n    return n;\n  }\n\n  private resolveMount(path: string): ResolvedMount | null {\n    const normalized = this.normalizePath(path);\n    let best: { mountPath: string; fs: WorkspaceFilesystem } | null = null;\n\n    for (const [mountPath, fs] of this._mounts) {\n      if (normalized === mountPath || normalized.startsWith(mountPath + '/')) {\n        if (!best || mountPath.length > best.mountPath.length) {\n          best = { mountPath, fs };\n        }\n      }\n    }\n\n    if (!best) return null;\n\n    let fsPath = normalized.slice(best.mountPath.length);\n    // Strip the leading slash so the path is relative to the mounted filesystem's basePath\n    if (fsPath === '/') fsPath = '';\n    else if (fsPath.startsWith('/')) fsPath = fsPath.slice(1);\n\n    return { fs: best.fs, fsPath, mountPath: best.mountPath };\n  }\n\n  private getVirtualEntries(path: string): FileEntry[] | null {\n    const normalized = this.normalizePath(path);\n    if (this.resolveMount(normalized)) return null;\n\n    const entriesMap = new Map<string, FileEntry>();\n    for (const [mountPath, fs] of this._mounts.entries()) {\n      const isUnder = normalized === '/' ? mountPath.startsWith('/') : mountPath.startsWith(normalized + '/');\n\n      if (isUnder) {\n        const remaining = normalized === '/' ? mountPath.slice(1) : mountPath.slice(normalized.length + 1);\n        const next = remaining.split('/')[0];\n        if (next && !entriesMap.has(next)) {\n          // Check if this is a direct mount point (e.g., listing '/' and mount is '/s3')\n          const isDirectMount = remaining === next;\n          const entry: FileEntry = { name: next, type: 'directory' as const };\n\n          // If it's a direct mount point, include filesystem metadata\n          if (isDirectMount) {\n            entry.mount = {\n              provider: fs.provider,\n              icon: fs.icon,\n              displayName: fs.displayName,\n              description: fs.description,\n              status: fs.status,\n              error: fs.error,\n            };\n          }\n\n          entriesMap.set(next, entry);\n        }\n      }\n    }\n\n    return entriesMap.size > 0 ? Array.from(entriesMap.values()) : null;\n  }\n\n  private isVirtualPath(path: string): boolean {\n    const normalized = this.normalizePath(path);\n    if (normalized === '/' && !this._mounts.has('/')) return true;\n    for (const mountPath of this._mounts.keys()) {\n      if (mountPath.startsWith(normalized + '/')) return true;\n    }\n    return false;\n  }\n\n  /**\n   * Assert that a filesystem is writable (not read-only).\n   * @throws {PermissionError} if the filesystem is read-only\n   */\n  private assertWritable(fs: WorkspaceFilesystem, path: string, operation: string): void {\n    if (fs.readOnly) {\n      throw new PermissionError(path, `${operation} (filesystem is read-only)`);\n    }\n  }\n\n  // ===========================================================================\n  // WorkspaceFilesystem Implementation\n  // ===========================================================================\n\n  async init(): Promise<void> {\n    this.status = 'initializing';\n    for (const [mountPath, fs] of this._mounts.entries()) {\n      try {\n        await callLifecycle(fs, 'init');\n      } catch (e) {\n        // Individual mount failed - it will have status='error'\n        // Log but continue with other mounts\n        const message = e instanceof Error ? e.message : String(e);\n        console.warn(`[CompositeFilesystem] Mount \"${mountPath}\" failed to initialize: ${message}`);\n      }\n    }\n    // CompositeFilesystem is ready even if some mounts failed\n    // Operations on errored mounts will be handled by the underlying filesystem\n    this.status = 'ready';\n  }\n\n  async destroy(): Promise<void> {\n    this.status = 'destroying';\n    const errors: Error[] = [];\n    for (const fs of this._mounts.values()) {\n      try {\n        await callLifecycle(fs, 'destroy');\n      } catch (e) {\n        errors.push(e instanceof Error ? e : new Error(String(e)));\n      }\n    }\n    if (errors.length > 0) {\n      this.status = 'error';\n      throw new AggregateError(errors, 'Some filesystems failed to destroy');\n    }\n    this.status = 'destroyed';\n  }\n\n  async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    return r.fs.readFile(r.fsPath, options);\n  }\n\n  async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    this.assertWritable(r.fs, path, 'writeFile');\n    return r.fs.writeFile(r.fsPath, content, options);\n  }\n\n  async appendFile(path: string, content: FileContent): Promise<void> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    this.assertWritable(r.fs, path, 'appendFile');\n    return r.fs.appendFile(r.fsPath, content);\n  }\n\n  async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    this.assertWritable(r.fs, path, 'deleteFile');\n    return r.fs.deleteFile(r.fsPath, options);\n  }\n\n  async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n    const srcR = this.resolveMount(src);\n    const destR = this.resolveMount(dest);\n    if (!srcR) throw new Error(`No mount for source: ${src}`);\n    if (!destR) throw new Error(`No mount for dest: ${dest}`);\n    this.assertWritable(destR.fs, dest, 'copyFile');\n\n    // Same mount - delegate\n    if (srcR.mountPath === destR.mountPath) {\n      return srcR.fs.copyFile(srcR.fsPath, destR.fsPath, options);\n    }\n\n    // Cross-mount copy - read then write\n    const content = await srcR.fs.readFile(srcR.fsPath);\n    await destR.fs.writeFile(destR.fsPath, content, { overwrite: options?.overwrite });\n  }\n\n  async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n    const srcR = this.resolveMount(src);\n    const destR = this.resolveMount(dest);\n    if (!srcR) throw new Error(`No mount for source: ${src}`);\n    if (!destR) throw new Error(`No mount for dest: ${dest}`);\n    this.assertWritable(destR.fs, dest, 'moveFile');\n    this.assertWritable(srcR.fs, src, 'moveFile'); // Source must be writable for delete\n\n    // Same mount - delegate\n    if (srcR.mountPath === destR.mountPath) {\n      return srcR.fs.moveFile(srcR.fsPath, destR.fsPath, options);\n    }\n\n    // Cross-mount move - copy then delete\n    await this.copyFile(src, dest, options);\n    await srcR.fs.deleteFile(srcR.fsPath);\n  }\n\n  async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n    const virtual = this.getVirtualEntries(path);\n    if (virtual) return virtual;\n\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    return r.fs.readdir(r.fsPath, options);\n  }\n\n  async mkdir(path: string, options?: { recursive?: boolean }): Promise<void> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    this.assertWritable(r.fs, path, 'mkdir');\n    return r.fs.mkdir(r.fsPath, options);\n  }\n\n  async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n    this.assertWritable(r.fs, path, 'rmdir');\n    return r.fs.rmdir(r.fsPath, options);\n  }\n\n  async exists(path: string): Promise<boolean> {\n    if (this.isVirtualPath(path)) return true;\n    const r = this.resolveMount(path);\n    if (!r) return false;\n    // Mount point root always exists (even if errored)\n    if (r.fsPath === '') return true;\n    return r.fs.exists(r.fsPath);\n  }\n\n  async stat(path: string): Promise<FileStat> {\n    const normalized = this.normalizePath(path);\n\n    if (this.isVirtualPath(path)) {\n      const parts = normalized.split('/').filter(Boolean);\n      const now = new Date();\n      return {\n        name: parts[parts.length - 1] || '',\n        path: normalized,\n        type: 'directory',\n        size: 0,\n        createdAt: now,\n        modifiedAt: now,\n      };\n    }\n\n    const r = this.resolveMount(path);\n    if (!r) throw new Error(`No mount for path: ${path}`);\n\n    // Mount point root always returns directory stat (even if errored)\n    if (r.fsPath === '') {\n      const parts = normalized.split('/').filter(Boolean);\n      const now = new Date();\n      return {\n        name: parts[parts.length - 1] || '',\n        path: normalized,\n        type: 'directory',\n        size: 0,\n        createdAt: now,\n        modifiedAt: now,\n      };\n    }\n\n    return r.fs.stat(r.fsPath);\n  }\n\n  async isFile(path: string): Promise<boolean> {\n    if (this.isVirtualPath(path)) return false;\n    const r = this.resolveMount(path);\n    if (!r) return false;\n    try {\n      const stat = await r.fs.stat(r.fsPath);\n      return stat.type === 'file';\n    } catch {\n      return false;\n    }\n  }\n\n  async isDirectory(path: string): Promise<boolean> {\n    if (this.isVirtualPath(path)) return true;\n    const r = this.resolveMount(path);\n    if (!r) return false;\n    // Mount point root is always a directory (even if errored)\n    if (r.fsPath === '') return true;\n    try {\n      const stat = await r.fs.stat(r.fsPath);\n      return stat.type === 'directory';\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * Get instructions describing the mounted filesystems.\n   * Used by agents to understand available storage locations.\n   */\n  getInstructions(_opts?: { requestContext?: RequestContext }): string {\n    const mountDescriptions = Array.from(this._mounts.entries())\n      .map(([mountPath, fs]) => {\n        const name = fs.displayName || fs.provider;\n        const access = fs.readOnly ? '(read-only)' : '(read-write)';\n        return `- ${mountPath}: ${name} ${access}`;\n      })\n      .join('\\n');\n\n    return `Filesystem mount points:\\n${mountDescriptions}`;\n  }\n}\n\n/**\n * Distributive mapped type that produces a union of correlated `[key, value]` tuples.\n *\n * For `{ '/local': LocalFilesystem, '/s3': S3Filesystem }` this yields:\n * `['/local', LocalFilesystem] | ['/s3', S3Filesystem]`\n *\n * This enables discriminated-union narrowing when iterating entries without destructuring:\n * ```typescript\n * for (const entry of mounts.entries()) {\n *   if (entry[0] === '/local') {\n *     entry[1] // LocalFilesystem\n *   }\n * }\n * ```\n */\nexport type MountMapEntry<TMounts extends Record<string, WorkspaceFilesystem>> = {\n  [K in string & keyof TMounts]: [K, TMounts[K]];\n}[string & keyof TMounts];\n\n/**\n * A read-only view of mounted filesystems with typed per-key access.\n *\n * Unlike `ReadonlyMap<string, WorkspaceFilesystem>`, this preserves the\n * concrete filesystem type for each mount path via an overloaded `get()`.\n *\n * Iteration methods return correlated `[key, value]` tuples ({@link MountMapEntry})\n * so that checking `entry[0]` narrows `entry[1]` to the concrete filesystem type.\n *\n * @example\n * ```typescript\n * const mounts = cfs.mounts;\n * mounts.get('/local') // LocalFilesystem\n * mounts.get('/s3')    // S3Filesystem\n * ```\n */\nexport interface ReadonlyMountMap<TMounts extends Record<string, WorkspaceFilesystem>> {\n  /** Get a mounted filesystem by path. Returns the concrete type for known mount paths. */\n  get<K extends string & keyof TMounts>(key: K): TMounts[K];\n  get(key: string): WorkspaceFilesystem | undefined;\n\n  has(key: string): boolean;\n  readonly size: number;\n\n  keys(): IterableIterator<string & keyof TMounts>;\n  values(): IterableIterator<TMounts[keyof TMounts & string]>;\n  entries(): IterableIterator<MountMapEntry<TMounts>>;\n  forEach(\n    callbackfn: (\n      value: TMounts[keyof TMounts & string],\n      key: string & keyof TMounts,\n      map: ReadonlyMountMap<TMounts>,\n    ) => void,\n  ): void;\n  [Symbol.iterator](): IterableIterator<MountMapEntry<TMounts>>;\n}\n","/**\n * MastraFilesystem Base Class\n *\n * Abstract base class for filesystem providers that want automatic logger integration\n * and lifecycle management.\n *\n * Extends MastraBase to receive the Mastra logger when registered with a Mastra instance.\n *\n * ## Lifecycle Management\n *\n * The base class provides race-condition-safe lifecycle wrappers:\n * - `_init()` - Handles concurrent calls, status management\n * - `_destroy()` - Handles concurrent calls and status management\n *\n * Subclasses override the plain `init()` and `destroy()` methods to provide\n * their implementation. Callers use the `_`-prefixed wrappers (or `callLifecycle()`)\n * which add status tracking and race-condition safety.\n *\n * External providers can extend this class to get logger support, or implement\n * the WorkspaceFilesystem interface directly if they don't need logging.\n */\n\nimport { MastraBase } from '../../base';\nimport { RegisteredLogger } from '../../logger/constants';\nimport { FilesystemNotReadyError } from '../errors';\nimport type { ProviderStatus } from '../lifecycle';\nimport type {\n  WorkspaceFilesystem,\n  FileContent,\n  FileStat,\n  FileEntry,\n  ReadOptions,\n  WriteOptions,\n  ListOptions,\n  RemoveOptions,\n  CopyOptions,\n} from './filesystem';\n\n/**\n * Lifecycle hook that fires during filesystem state transitions.\n * Receives the filesystem instance so users can inspect state, log, etc.\n */\nexport type FilesystemLifecycleHook = (args: { filesystem: WorkspaceFilesystem }) => void | Promise<void>;\n\n/**\n * Options for the MastraFilesystem base class constructor.\n * Providers extend this to add their own options while inheriting lifecycle hooks.\n */\nexport interface MastraFilesystemOptions {\n  /** Called after the filesystem reaches 'ready' status */\n  onInit?: FilesystemLifecycleHook;\n  /** Called before the filesystem is destroyed */\n  onDestroy?: FilesystemLifecycleHook;\n}\n\n/**\n * Abstract base class for filesystem providers with logger support and lifecycle management.\n *\n * Providers that extend this class automatically receive the Mastra logger\n * when the filesystem is used with a Mastra instance.\n *\n * @example\n * ```typescript\n * class MyCustomFilesystem extends MastraFilesystem {\n *   readonly id = 'my-fs';\n *   readonly name = 'MyCustomFilesystem';\n *   readonly provider = 'custom';\n *   status: ProviderStatus = 'pending';\n *\n *   constructor() {\n *     super({ name: 'MyCustomFilesystem' });\n *   }\n *\n *   // Override init() to provide initialization logic\n *   async init(): Promise<void> {\n *     // Your initialization logic here\n *   }\n *\n *   async readFile(path: string): Promise<string | Buffer> {\n *     await this.ensureReady();\n *     this.logger.debug('Reading file', { path });\n *     // Implementation...\n *   }\n *   // ... implement other WorkspaceFilesystem methods\n * }\n * ```\n */\nexport abstract class MastraFilesystem extends MastraBase implements WorkspaceFilesystem {\n  /** Unique identifier for this filesystem instance */\n  abstract readonly id: string;\n\n  /** Human-readable name (e.g., 'LocalFilesystem', 'AgentFS') */\n  abstract readonly name: string;\n\n  /** Provider type identifier */\n  abstract readonly provider: string;\n\n  /** Current status of the filesystem */\n  abstract status: ProviderStatus;\n\n  /** Error message when status is 'error' */\n  error?: string;\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle Promise Tracking (prevents race conditions)\n  // ---------------------------------------------------------------------------\n\n  /** Promise for _init() to prevent race conditions from concurrent calls */\n  private _initPromise?: Promise<void>;\n\n  /** Promise for _destroy() to prevent race conditions from concurrent calls */\n  private _destroyPromise?: Promise<void>;\n\n  /** Lifecycle callbacks */\n  private readonly _onInit?: FilesystemLifecycleHook;\n  private readonly _onDestroy?: FilesystemLifecycleHook;\n\n  constructor(options: { name: string } & MastraFilesystemOptions) {\n    super({ name: options.name, component: RegisteredLogger.WORKSPACE });\n\n    this._onInit = options.onInit;\n    this._onDestroy = options.onDestroy;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle Wrappers (race-condition-safe)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Initialize the filesystem (wrapper with status management and race-condition safety).\n   *\n   * This method is race-condition-safe - concurrent calls will return the same promise.\n   * Handles status management automatically.\n   *\n   * Subclasses override `init()` to provide their initialization logic.\n   */\n  async _init(): Promise<void> {\n    // Already ready\n    // Note: intentionally allows re-init after destroy() for reconnect scenarios\n    if (this.status === 'ready') {\n      return;\n    }\n\n    // Wait for any in-progress destroy to complete before (re-)initializing\n    if (this._destroyPromise) {\n      try {\n        await this._destroyPromise;\n      } catch {\n        // Ignore destroy errors — we're re-initializing anyway\n      }\n    }\n\n    // Init already in progress - return existing promise\n    if (this._initPromise) {\n      return this._initPromise;\n    }\n\n    // Create and store the init promise\n    this._initPromise = this._executeInit();\n\n    try {\n      await this._initPromise;\n    } finally {\n      this._initPromise = undefined;\n    }\n  }\n\n  /**\n   * Internal init execution - handles status.\n   */\n  private async _executeInit(): Promise<void> {\n    this.status = 'initializing';\n    this.error = undefined;\n\n    try {\n      await this.init();\n      this.status = 'ready';\n\n      // Fire onInit callback after filesystem is ready — treat failure as non-fatal\n      // so that a bad callback doesn't kill an otherwise healthy filesystem\n      try {\n        await this._onInit?.({ filesystem: this });\n      } catch (error) {\n        this.logger.warn('onInit callback failed', { error });\n      }\n    } catch (error) {\n      this.status = 'error';\n      this.error = error instanceof Error ? error.message : String(error);\n      this.logger.error('Failed to initialize filesystem', { error, id: this.id });\n      throw error;\n    }\n  }\n\n  /**\n   * Override this method to implement filesystem initialization logic.\n   *\n   * Called by `_init()` after status is set to 'initializing'.\n   * Status will be set to 'ready' on success, 'error' on failure.\n   *\n   * @example\n   * ```typescript\n   * async init(): Promise<void> {\n   *   this._client = new StorageClient({ ... });\n   *   await this._client.connect();\n   * }\n   * ```\n   */\n  async init(): Promise<void> {\n    // Default no-op - subclasses override\n  }\n\n  /**\n   * Ensure the filesystem is ready.\n   *\n   * Calls `_init()` if status is not 'ready'. Useful for lazy initialization\n   * where operations should automatically initialize the filesystem if needed.\n   *\n   * @throws {FilesystemNotReadyError} if the filesystem fails to reach 'ready' status\n   *\n   * @example\n   * ```typescript\n   * async readFile(path: string): Promise<string | Buffer> {\n   *   await this.ensureReady();\n   *   // Now safe to use the filesystem\n   * }\n   * ```\n   */\n  protected async ensureReady(): Promise<void> {\n    if (this.status !== 'ready') {\n      await this._init();\n    }\n    if (this.status !== 'ready') {\n      throw new FilesystemNotReadyError(this.id);\n    }\n  }\n\n  /**\n   * Destroy the filesystem and clean up all resources (wrapper with status management).\n   *\n   * This method is race-condition-safe - concurrent calls will return the same promise.\n   * Handles status management.\n   *\n   * Subclasses override `destroy()` to provide their destroy logic.\n   */\n  async _destroy(): Promise<void> {\n    // Already destroyed\n    if (this.status === 'destroyed') {\n      return;\n    }\n\n    // Never initialized — nothing to tear down\n    if (this.status === 'pending') {\n      this.status = 'destroyed';\n      return;\n    }\n\n    // Destroy already in progress - return existing promise\n    if (this._destroyPromise) {\n      return this._destroyPromise;\n    }\n\n    // Create and store the destroy promise\n    this._destroyPromise = this._executeDestroy();\n\n    try {\n      await this._destroyPromise;\n    } finally {\n      this._destroyPromise = undefined;\n    }\n  }\n\n  /**\n   * Internal destroy execution - handles status.\n   */\n  private async _executeDestroy(): Promise<void> {\n    // Wait for any in-progress init to complete before destroying\n    if (this._initPromise) {\n      try {\n        await this._initPromise;\n      } catch {\n        // Ignore init errors — we're destroying anyway\n      }\n    }\n    this.status = 'destroying';\n\n    try {\n      // Fire onDestroy callback before destroying\n      await this._onDestroy?.({ filesystem: this });\n\n      await this.destroy();\n      this.status = 'destroyed';\n    } catch (error) {\n      this.status = 'error';\n      this.logger.error('Failed to destroy filesystem', { error, id: this.id });\n      throw error;\n    }\n  }\n\n  /**\n   * Override this method to implement filesystem destroy logic.\n   *\n   * Called by `_destroy()` after status is set to 'destroying'.\n   * Status will be set to 'destroyed' on success, 'error' on failure.\n   */\n  async destroy(): Promise<void> {\n    // Default no-op - subclasses override\n  }\n\n  // ---------------------------------------------------------------------------\n  // Abstract methods - implementations must provide these\n  // ---------------------------------------------------------------------------\n\n  abstract readFile(path: string, options?: ReadOptions): Promise<string | Buffer>;\n  abstract writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void>;\n  abstract appendFile(path: string, content: FileContent): Promise<void>;\n  abstract deleteFile(path: string, options?: RemoveOptions): Promise<void>;\n  abstract copyFile(src: string, dest: string, options?: CopyOptions): Promise<void>;\n  abstract moveFile(src: string, dest: string, options?: CopyOptions): Promise<void>;\n  abstract mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;\n  abstract rmdir(path: string, options?: RemoveOptions): Promise<void>;\n  abstract readdir(path: string, options?: ListOptions): Promise<FileEntry[]>;\n  abstract exists(path: string): Promise<boolean>;\n  abstract stat(path: string): Promise<FileStat>;\n}\n","import type { RequestContext } from '../request-context';\nimport type { InstructionsOption } from './types';\n\n/**\n * Resolve an instructions override against default instructions.\n *\n * - `undefined` → return default\n * - `string` → return the string as-is\n * - `function` → call with { defaultInstructions, requestContext }\n */\nexport function resolveInstructions(\n  override: InstructionsOption | undefined,\n  getDefault: () => string,\n  requestContext?: RequestContext,\n): string {\n  if (typeof override === 'string') return override;\n  const defaultInstructions = getDefault();\n  if (override === undefined) return defaultInstructions;\n  return override({ defaultInstructions, requestContext });\n}\n","/**\n * Local Filesystem Provider\n *\n * A filesystem implementation backed by a folder on the local disk.\n * This is the default filesystem for development and local agents.\n */\n\nimport { constants as fsConstants, realpathSync } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as nodePath from 'node:path';\nimport type { RequestContext } from '../../request-context';\nimport {\n  FileNotFoundError,\n  DirectoryNotFoundError,\n  FileExistsError,\n  IsDirectoryError,\n  NotDirectoryError,\n  DirectoryNotEmptyError,\n  PermissionError,\n  StaleFileError,\n  WorkspaceReadOnlyError,\n} from '../errors';\nimport type { ProviderStatus } from '../lifecycle';\nimport type { InstructionsOption } from '../types';\nimport { resolveInstructions } from '../utils';\nimport type {\n  FilesystemInfo,\n  FileContent,\n  FileStat,\n  FileEntry,\n  ReadOptions,\n  WriteOptions,\n  ListOptions,\n  RemoveOptions,\n  CopyOptions,\n} from './filesystem';\nimport { expandTilde, fsExists, fsStat, isEnoentError, isEexistError, resolveToBasePath } from './fs-utils';\nimport { MastraFilesystem } from './mastra-filesystem';\nimport type { MastraFilesystemOptions } from './mastra-filesystem';\nimport type { FilesystemMountConfig } from './mount';\n\n/**\n * Local filesystem provider configuration.\n */\nexport interface LocalFilesystemOptions extends MastraFilesystemOptions {\n  /** Unique identifier for this filesystem instance */\n  id?: string;\n  /** Base directory path on disk */\n  basePath: string;\n  /**\n   * When true, all file operations are restricted to stay within basePath.\n   * Prevents path traversal attacks and symlink escapes.\n   *\n   * - `contained: true` (default) — File access is restricted to basePath\n   *   (and any allowedPaths). Paths that escape these boundaries throw a\n   *   PermissionError.\n   * - `contained: false` — No access restrictions. Any path on the host\n   *   filesystem is accessible.\n   *\n   * Set to `false` when the filesystem needs to access paths outside basePath,\n   * such as global skills directories or user home directories.\n   *\n   * @default true\n   */\n  contained?: boolean;\n  /**\n   * When true, all write operations to this filesystem are blocked.\n   * Read operations are still allowed.\n   * @default false\n   */\n  readOnly?: boolean;\n  /**\n   * Additional directories the agent can access outside of `basePath`.\n   *\n   * Relative paths resolve against `basePath`.\n   * Absolute and tilde paths are used as-is.\n   *\n   * @example\n   * ```typescript\n   * new LocalFilesystem({\n   *   basePath: './workspace',\n   *   contained: true,\n   *   allowedPaths: ['../skills', '~/.claude/skills'],\n   * })\n   * ```\n   */\n  allowedPaths?: string[];\n  /**\n   * Custom instructions that override the default instructions\n   * returned by `getInstructions()`.\n   *\n   * - `string` — Fully replaces the default instructions.\n   *   Pass an empty string to suppress instructions entirely.\n   * - `(opts) => string` — Receives the default instructions and\n   *   optional request context so you can extend or customise per-request.\n   */\n  instructions?: InstructionsOption;\n}\n\n/**\n * Mount configuration for local filesystems.\n *\n * When a `LocalFilesystem` is used as a mount in a Workspace with `LocalSandbox`,\n * the sandbox creates a symlink from `<workingDir>/<mountPath>` → `basePath`.\n * No FUSE tools are needed for local mounts.\n *\n * **Note:** When mounted with `contained: false`, the agent can access any\n * path on the host filesystem through this mount. Workspace logs a warning\n * at construction time if this combination is detected.\n */\nexport interface LocalMountConfig extends FilesystemMountConfig {\n  type: 'local';\n  basePath: string;\n}\n\n/**\n * Local filesystem implementation.\n *\n * Stores files in a folder on the user's machine.\n * This is the recommended filesystem for development and persistent local storage.\n *\n * @example\n * ```typescript\n * import { Workspace, LocalFilesystem } from '@mastra/core';\n *\n * const workspace = new Workspace({\n *   filesystem: new LocalFilesystem({ basePath: './my-workspace' }),\n * });\n *\n * await workspace.init();\n * await workspace.writeFile('hello.txt', 'Hello World!');\n * ```\n */\nexport class LocalFilesystem extends MastraFilesystem {\n  readonly id: string;\n  readonly name = 'LocalFilesystem';\n  readonly provider = 'local';\n  readonly readOnly?: boolean;\n\n  status: ProviderStatus = 'pending';\n\n  private readonly _basePath: string;\n  private readonly _contained: boolean;\n  private _allowedPaths: string[];\n  private readonly _instructionsOverride?: InstructionsOption;\n\n  /**\n   * The absolute base path on disk where files are stored.\n   * Useful for understanding how workspace paths map to disk paths.\n   */\n  get basePath(): string {\n    return this._basePath;\n  }\n\n  /**\n   * Whether file operations are restricted to stay within basePath.\n   *\n   * When `true` (default), relative paths resolve against basePath and\n   * absolute paths are kept as-is. Any resolved path that falls outside\n   * basePath (and allowedPaths) throws a PermissionError. When `false`,\n   * no containment check is applied.\n   *\n   * **Note:** When used as a CompositeFilesystem mount with `contained: false`,\n   * the agent can access any path on the host filesystem through this mount.\n   */\n  get contained(): boolean {\n    return this._contained;\n  }\n\n  /**\n   * Current set of resolved allowed paths.\n   * These paths are permitted beyond basePath when containment is enabled.\n   */\n  get allowedPaths(): readonly string[] {\n    return this._allowedPaths;\n  }\n\n  /**\n   * Update allowed paths. Accepts a direct array or an updater callback\n   * receiving the current paths (React setState pattern).\n   *\n   * @example\n   * ```typescript\n   * // Set directly\n   * fs.setAllowedPaths(['../shared-data']);\n   *\n   * // Update with callback\n   * fs.setAllowedPaths(prev => [...prev, '~/.claude/skills']);\n   * ```\n   */\n  setAllowedPaths(pathsOrUpdater: string[] | ((current: readonly string[]) => string[])): void {\n    const newPaths = typeof pathsOrUpdater === 'function' ? pathsOrUpdater(this._allowedPaths) : pathsOrUpdater;\n    this._allowedPaths = newPaths.map(p => resolveToBasePath(this._basePath, p));\n  }\n\n  constructor(options: LocalFilesystemOptions) {\n    super({ ...options, name: 'LocalFilesystem' });\n    this.id = options.id ?? this.generateId();\n    this._basePath = nodePath.resolve(expandTilde(options.basePath));\n    this._contained = options.contained ?? true;\n    this.readOnly = options.readOnly;\n    this._allowedPaths = (options.allowedPaths ?? []).map(p => resolveToBasePath(this._basePath, p));\n    this._instructionsOverride = options.instructions;\n  }\n\n  /**\n   * Return mount config for sandbox integration.\n   * LocalSandbox uses this to create a symlink from the mount path to basePath.\n   */\n  getMountConfig(): LocalMountConfig {\n    return { type: 'local', basePath: this._basePath };\n  }\n\n  private generateId(): string {\n    return `local-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n  }\n\n  /**\n   * Check if an absolute path falls within basePath or any allowed path.\n   */\n  private _isWithinRoot(absolutePath: string, root: string): boolean {\n    const relative = nodePath.relative(root, absolutePath);\n    return !relative.startsWith('..') && !nodePath.isAbsolute(relative);\n  }\n\n  private _resolvePathForContainment(absolutePath: string): string | undefined {\n    let currentPath = absolutePath;\n\n    while (true) {\n      try {\n        const realPath = realpathSync(currentPath);\n        if (currentPath === absolutePath) {\n          return realPath;\n        }\n\n        const remainder = nodePath.relative(currentPath, absolutePath);\n        return nodePath.join(realPath, remainder);\n      } catch (error: unknown) {\n        if (!isEnoentError(error)) return undefined;\n      }\n\n      const parentPath = nodePath.dirname(currentPath);\n      if (parentPath === currentPath) {\n        return undefined;\n      }\n      currentPath = parentPath;\n    }\n  }\n\n  private _isWithinAnyRoot(absolutePath: string): boolean {\n    const roots = [this._basePath, ...this._allowedPaths];\n    if (roots.some(root => this._isWithinRoot(absolutePath, root))) {\n      return true;\n    }\n\n    const resolvedPath = this._resolvePathForContainment(absolutePath);\n    if (!resolvedPath) {\n      return false;\n    }\n\n    return roots.some(root => {\n      const resolvedRoot = this._resolvePathForContainment(root);\n      return resolvedRoot ? this._isWithinRoot(resolvedPath, resolvedRoot) : false;\n    });\n  }\n\n  private toBuffer(content: FileContent): Buffer {\n    if (Buffer.isBuffer(content)) return content;\n    if (content instanceof Uint8Array) return Buffer.from(content);\n    return Buffer.from(content, 'utf-8');\n  }\n\n  private resolvePath(inputPath: string): string {\n    const absolutePath = resolveToBasePath(this._basePath, inputPath);\n\n    if (this._contained) {\n      if (!this._isWithinAnyRoot(absolutePath)) {\n        throw new PermissionError(inputPath, this._accessOperationHint(inputPath));\n      }\n    }\n\n    return absolutePath;\n  }\n\n  /**\n   * Build the operation string for a containment-violation `PermissionError`.\n   *\n   * When the caller passed an absolute path, suggest a concrete relative form\n   * only when that suffix names an existing entry under the workspace (e.g.\n   * `/src/app.ts` → `src/app.ts` if `<basePath>/src` exists). Otherwise emit a\n   * soft hint that doesn't lie about specific paths — agents that mistake `/`\n   * for the workspace root learn the workspace is sandboxed without us\n   * inventing a fictitious in-workspace location for `/etc/passwd`.\n   */\n  private _accessOperationHint(inputPath: string): string {\n    if (!nodePath.isAbsolute(inputPath)) return 'access';\n\n    const stripped = inputPath.replace(/^[/\\\\]+/, '');\n    if (!stripped) return 'access';\n\n    // If the first segment exists under basePath, the LLM almost certainly\n    // meant a workspace-relative path. Suggest the exact form. Reject any\n    // segment that would escape basePath (`.`, `..`) — suggesting those would\n    // just produce another containment failure on the next turn.\n    const firstSegment = stripped.split(/[/\\\\]/, 1)[0];\n    if (firstSegment && firstSegment !== '.' && firstSegment !== '..') {\n      try {\n        if (realpathSync(nodePath.join(this._basePath, firstSegment))) {\n          return `access (path is outside the workspace; use a relative path like \"${stripped}\")`;\n        }\n      } catch {\n        // Fall through to the soft hint\n      }\n    }\n\n    return 'access (path is outside the workspace; use a path relative to the workspace root, without a leading \"/\")';\n  }\n\n  /**\n   * Resolve a workspace-relative path to an absolute disk path.\n   * Uses the same resolution logic as internal file operations.\n   * Returns `undefined` if the path violates containment.\n   */\n  resolveAbsolutePath(inputPath: string): string | undefined {\n    try {\n      return this.resolvePath(inputPath);\n    } catch {\n      // PermissionError from containment check — path is not resolvable\n      return undefined;\n    }\n  }\n\n  private toRelativePath(absolutePath: string): string {\n    return nodePath.relative(this._basePath, absolutePath).replace(/\\\\/g, '/');\n  }\n\n  private assertWritable(operation: string): void {\n    if (this.readOnly) {\n      throw new WorkspaceReadOnlyError(operation);\n    }\n  }\n\n  /**\n   * Verify that the resolved path doesn't escape basePath via symlinks.\n   * Uses realpath to resolve symlinks and check the actual target.\n   */\n  private async assertPathContained(absolutePath: string): Promise<void> {\n    if (!this._contained) return;\n\n    if (this._allowedPaths.some(root => this._isWithinRoot(absolutePath, root))) {\n      return;\n    }\n\n    // Resolve symlinks for the target path. If it doesn't exist,\n    // there are no symlinks to escape through — nothing to check.\n    let targetReal: string;\n    try {\n      targetReal = await fs.realpath(absolutePath);\n    } catch (error: unknown) {\n      if (isEnoentError(error)) return; // path doesn't exist yet — safe\n      throw error;\n    }\n\n    // Resolve real paths for roots, skipping any that don't exist\n    const roots = [this._basePath, ...this._allowedPaths];\n    const rootReals: string[] = [];\n    for (const root of roots) {\n      try {\n        rootReals.push(await fs.realpath(root));\n      } catch (error: unknown) {\n        if (isEnoentError(error)) continue;\n        throw error;\n      }\n    }\n\n    const isWithinRoot = rootReals.some(\n      rootReal => targetReal === rootReal || targetReal.startsWith(rootReal + nodePath.sep),\n    );\n\n    if (!isWithinRoot) {\n      throw new PermissionError(absolutePath, 'access');\n    }\n  }\n\n  async readFile(inputPath: string, options?: ReadOptions): Promise<string | Buffer> {\n    this.logger.debug('Reading file', { path: inputPath, encoding: options?.encoding });\n    await this.ensureReady();\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    try {\n      const stats = await fs.stat(absolutePath);\n      if (stats.isDirectory()) {\n        throw new IsDirectoryError(inputPath);\n      }\n\n      if (options?.encoding) {\n        return await fs.readFile(absolutePath, { encoding: options.encoding });\n      }\n      return await fs.readFile(absolutePath);\n    } catch (error: unknown) {\n      if (error instanceof IsDirectoryError) throw error;\n      if (isEnoentError(error)) {\n        throw new FileNotFoundError(inputPath);\n      }\n      throw error;\n    }\n  }\n\n  async writeFile(inputPath: string, content: FileContent, options?: WriteOptions): Promise<void> {\n    const contentSize = Buffer.isBuffer(content) ? content.length : content.length;\n    this.logger.debug('Writing file', { path: inputPath, size: contentSize, recursive: options?.recursive });\n    await this.ensureReady();\n    this.assertWritable('writeFile');\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    // When recursive is explicitly false, verify parent directory exists\n    if (options?.recursive === false) {\n      const dir = nodePath.dirname(absolutePath);\n      const parentPath = nodePath.dirname(inputPath);\n      try {\n        const stat = await fs.stat(dir);\n        if (!stat.isDirectory()) {\n          throw new NotDirectoryError(parentPath);\n        }\n      } catch (error: unknown) {\n        if (error instanceof NotDirectoryError) throw error;\n        if (isEnoentError(error)) {\n          throw new DirectoryNotFoundError(parentPath);\n        }\n        throw error;\n      }\n    }\n\n    if (options?.recursive !== false) {\n      const dir = nodePath.dirname(absolutePath);\n      await fs.mkdir(dir, { recursive: true });\n    }\n\n    // Optimistic concurrency: reject if file was modified since caller last read it\n    if (options?.expectedMtime) {\n      try {\n        const currentStat = await fs.stat(absolutePath);\n        // Compare via Date objects — Node's stats.mtime applies internal\n        // rounding that can diverge from Math.floor(stats.mtimeMs).\n        if (currentStat.mtime.getTime() !== options.expectedMtime.getTime()) {\n          throw new StaleFileError(inputPath, options.expectedMtime, currentStat.mtime);\n        }\n      } catch (error: unknown) {\n        if (error instanceof StaleFileError) throw error;\n        // File doesn't exist yet — no conflict possible, proceed with write\n        if (!isEnoentError(error)) throw error;\n      }\n    }\n\n    // Use 'wx' flag for atomic overwrite check (avoids TOCTOU race)\n    const writeFlag = options?.overwrite === false ? 'wx' : 'w';\n    try {\n      await fs.writeFile(absolutePath, this.toBuffer(content), { flag: writeFlag });\n    } catch (error: unknown) {\n      if (options?.overwrite === false && isEexistError(error)) {\n        throw new FileExistsError(inputPath);\n      }\n      throw error;\n    }\n  }\n\n  async appendFile(inputPath: string, content: FileContent): Promise<void> {\n    const contentSize = Buffer.isBuffer(content) ? content.length : content.length;\n    this.logger.debug('Appending to file', { path: inputPath, size: contentSize });\n    await this.ensureReady();\n    this.assertWritable('appendFile');\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n    const dir = nodePath.dirname(absolutePath);\n    await fs.mkdir(dir, { recursive: true });\n    await fs.appendFile(absolutePath, this.toBuffer(content));\n  }\n\n  async deleteFile(inputPath: string, options?: RemoveOptions): Promise<void> {\n    this.logger.debug('Deleting file', { path: inputPath, force: options?.force });\n    await this.ensureReady();\n    this.assertWritable('deleteFile');\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    try {\n      const stats = await fs.stat(absolutePath);\n      if (stats.isDirectory()) {\n        throw new IsDirectoryError(inputPath);\n      }\n      await fs.unlink(absolutePath);\n    } catch (error: unknown) {\n      if (error instanceof IsDirectoryError) throw error;\n      if (isEnoentError(error)) {\n        if (!options?.force) {\n          throw new FileNotFoundError(inputPath);\n        }\n      } else {\n        throw error;\n      }\n    }\n  }\n\n  async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n    this.logger.debug('Copying file', { src, dest, recursive: options?.recursive });\n    await this.ensureReady();\n    this.assertWritable('copyFile');\n    const srcPath = this.resolvePath(src);\n    const destPath = this.resolvePath(dest);\n    await this.assertPathContained(srcPath);\n    await this.assertPathContained(destPath);\n\n    try {\n      const stats = await fs.stat(srcPath);\n      if (stats.isDirectory()) {\n        if (!options?.recursive) {\n          throw new IsDirectoryError(src);\n        }\n        await this.copyDirectory(srcPath, destPath, options);\n      } else {\n        await fs.mkdir(nodePath.dirname(destPath), { recursive: true });\n        // Use COPYFILE_EXCL for atomic overwrite check (avoids TOCTOU race)\n        const copyFlags = options?.overwrite === false ? fsConstants.COPYFILE_EXCL : 0;\n        try {\n          await fs.copyFile(srcPath, destPath, copyFlags);\n        } catch (error: unknown) {\n          if (options?.overwrite === false && isEexistError(error)) {\n            throw new FileExistsError(dest);\n          }\n          throw error;\n        }\n      }\n    } catch (error: unknown) {\n      if (error instanceof IsDirectoryError || error instanceof FileExistsError) throw error;\n      if (isEnoentError(error)) {\n        throw new FileNotFoundError(src);\n      }\n      throw error;\n    }\n  }\n\n  private async copyDirectory(src: string, dest: string, options?: CopyOptions): Promise<void> {\n    await this.ensureReady();\n    await fs.mkdir(dest, { recursive: true });\n    const entries = await fs.readdir(src, { withFileTypes: true });\n\n    for (const entry of entries) {\n      const srcEntry = nodePath.join(src, entry.name);\n      const destEntry = nodePath.join(dest, entry.name);\n\n      // Verify entries don't escape sandbox via symlink\n      await this.assertPathContained(srcEntry);\n      await this.assertPathContained(destEntry);\n\n      if (entry.isDirectory()) {\n        await this.copyDirectory(srcEntry, destEntry, options);\n      } else {\n        // Use COPYFILE_EXCL for atomic overwrite check (avoids TOCTOU race)\n        const copyFlags = options?.overwrite === false ? fsConstants.COPYFILE_EXCL : 0;\n        try {\n          await fs.copyFile(srcEntry, destEntry, copyFlags);\n        } catch (error: unknown) {\n          if (options?.overwrite === false && isEexistError(error)) {\n            // Skip existing files when overwrite is false\n            continue;\n          }\n          throw error;\n        }\n      }\n    }\n  }\n\n  async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n    this.logger.debug('Moving file', { src, dest, overwrite: options?.overwrite });\n    await this.ensureReady();\n    this.assertWritable('moveFile');\n    const srcPath = this.resolvePath(src);\n    const destPath = this.resolvePath(dest);\n    await this.assertPathContained(srcPath);\n    await this.assertPathContained(destPath);\n\n    try {\n      await fs.mkdir(nodePath.dirname(destPath), { recursive: true });\n\n      // When overwrite: false, use copy+delete to avoid TOCTOU race condition.\n      // copyFile uses COPYFILE_EXCL which atomically checks and writes.\n      if (options?.overwrite === false) {\n        await this.copyFile(src, dest, { ...options, overwrite: false });\n        await fs.rm(srcPath, { recursive: true, force: true });\n        return;\n      }\n\n      try {\n        await fs.rename(srcPath, destPath);\n      } catch (error: unknown) {\n        // Only fall back to copy+delete for cross-device moves (EXDEV)\n        const code = (error as NodeJS.ErrnoException).code;\n        if (code !== 'EXDEV') {\n          throw error;\n        }\n        await this.copyFile(src, dest, options);\n        await fs.rm(srcPath, { recursive: true, force: true });\n      }\n    } catch (error: unknown) {\n      if (error instanceof FileExistsError) throw error;\n      if (isEnoentError(error)) {\n        throw new FileNotFoundError(src);\n      }\n      throw error;\n    }\n  }\n\n  async mkdir(inputPath: string, options?: { recursive?: boolean }): Promise<void> {\n    this.logger.debug('Creating directory', { path: inputPath, recursive: options?.recursive });\n    await this.ensureReady();\n    this.assertWritable('mkdir');\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    try {\n      await fs.mkdir(absolutePath, { recursive: options?.recursive ?? true });\n    } catch (error: unknown) {\n      if (isEexistError(error)) {\n        const stats = await fs.stat(absolutePath);\n        if (!stats.isDirectory()) {\n          throw new FileExistsError(inputPath);\n        }\n      } else if (isEnoentError(error)) {\n        // Parent directory doesn't exist (only happens when recursive: false)\n        const parentPath = nodePath.dirname(inputPath);\n        throw new DirectoryNotFoundError(parentPath);\n      } else {\n        throw error;\n      }\n    }\n  }\n\n  async rmdir(inputPath: string, options?: RemoveOptions): Promise<void> {\n    this.logger.debug('Removing directory', { path: inputPath, recursive: options?.recursive, force: options?.force });\n    await this.ensureReady();\n    this.assertWritable('rmdir');\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    try {\n      const stats = await fs.stat(absolutePath);\n      if (!stats.isDirectory()) {\n        throw new NotDirectoryError(inputPath);\n      }\n\n      if (options?.recursive) {\n        await fs.rm(absolutePath, { recursive: true, force: options?.force ?? false });\n      } else {\n        const entries = await fs.readdir(absolutePath);\n        if (entries.length > 0) {\n          throw new DirectoryNotEmptyError(inputPath);\n        }\n        await fs.rmdir(absolutePath);\n      }\n    } catch (error: unknown) {\n      if (error instanceof NotDirectoryError || error instanceof DirectoryNotEmptyError) {\n        throw error;\n      }\n      if (isEnoentError(error)) {\n        if (!options?.force) {\n          throw new DirectoryNotFoundError(inputPath);\n        }\n      } else {\n        throw error;\n      }\n    }\n  }\n\n  async readdir(inputPath: string, options?: ListOptions): Promise<FileEntry[]> {\n    this.logger.debug('Reading directory', { path: inputPath, recursive: options?.recursive });\n    await this.ensureReady();\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    try {\n      const stats = await fs.stat(absolutePath);\n      if (!stats.isDirectory()) {\n        throw new NotDirectoryError(inputPath);\n      }\n\n      const entries = await fs.readdir(absolutePath, { withFileTypes: true });\n      const result: FileEntry[] = [];\n\n      for (const entry of entries) {\n        const entryPath = nodePath.join(absolutePath, entry.name);\n\n        if (options?.extension) {\n          const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];\n          if (entry.isFile()) {\n            const ext = nodePath.extname(entry.name);\n            if (!extensions.some(e => e === ext || e === ext.slice(1))) {\n              continue;\n            }\n          }\n        }\n\n        // Check if entry is a symlink\n        const isSymlink = entry.isSymbolicLink();\n        let symlinkTarget: string | undefined;\n        let resolvedType: 'file' | 'directory' = 'file';\n\n        if (isSymlink) {\n          try {\n            // Get the symlink target path\n            symlinkTarget = await fs.readlink(entryPath);\n            // Determine the type of the target (follow the symlink)\n            const targetStat = await fs.stat(entryPath);\n            resolvedType = targetStat.isDirectory() ? 'directory' : 'file';\n          } catch {\n            // If we can't read the symlink target or it's broken, treat as file\n            resolvedType = 'file';\n          }\n        } else {\n          resolvedType = entry.isDirectory() ? 'directory' : 'file';\n        }\n\n        const fileEntry: FileEntry = {\n          name: entry.name,\n          type: resolvedType,\n          isSymlink: isSymlink || undefined,\n          symlinkTarget,\n        };\n\n        if (resolvedType === 'file' && !isSymlink) {\n          try {\n            const stat = await fs.stat(entryPath);\n            fileEntry.size = stat.size;\n          } catch {\n            // Ignore\n          }\n        }\n\n        result.push(fileEntry);\n\n        // Only recurse into directories (follow symlinks to directories)\n        if (options?.recursive && resolvedType === 'directory') {\n          // Default to 100 to prevent stack overflow on deeply nested structures\n          const depth = options.maxDepth ?? 100;\n          if (depth > 0) {\n            const subEntries = await this.readdir(this.toRelativePath(entryPath), { ...options, maxDepth: depth - 1 });\n            result.push(\n              ...subEntries.map(e => ({\n                ...e,\n                name: `${entry.name}/${e.name}`,\n              })),\n            );\n          }\n        }\n      }\n\n      return result;\n    } catch (error: unknown) {\n      if (error instanceof NotDirectoryError) throw error;\n      if (isEnoentError(error)) {\n        throw new DirectoryNotFoundError(inputPath);\n      }\n      throw error;\n    }\n  }\n\n  async exists(inputPath: string): Promise<boolean> {\n    await this.ensureReady();\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n    return fsExists(absolutePath);\n  }\n\n  async stat(inputPath: string): Promise<FileStat> {\n    await this.ensureReady();\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n    const result = await fsStat(absolutePath, inputPath);\n    return {\n      ...result,\n      path: this.toRelativePath(absolutePath),\n    };\n  }\n\n  async realpath(inputPath: string): Promise<string> {\n    await this.ensureReady();\n    const absolutePath = this.resolvePath(inputPath);\n    await this.assertPathContained(absolutePath);\n\n    const canonicalPath = await fs.realpath(absolutePath);\n    return this.toRelativePath(canonicalPath);\n  }\n\n  /**\n   * Initialize the local filesystem by creating the base directory.\n   * Status management is handled by the base class.\n   */\n  async init(): Promise<void> {\n    this.logger.debug('Initializing filesystem', { basePath: this._basePath });\n    await fs.mkdir(this._basePath, { recursive: true });\n    this.logger.debug('Filesystem initialized', { basePath: this._basePath });\n  }\n\n  /**\n   * Clean up the local filesystem.\n   * LocalFilesystem doesn't delete files on destroy by default.\n   * Status management is handled by the base class.\n   */\n  async destroy(): Promise<void> {\n    // LocalFilesystem doesn't delete files on destroy\n  }\n\n  getInfo(): FilesystemInfo<{ basePath: string; contained: boolean; allowedPaths?: string[] }> {\n    return {\n      id: this.id,\n      name: this.name,\n      provider: this.provider,\n      readOnly: this.readOnly,\n      status: this.status,\n      error: this.error,\n      metadata: {\n        basePath: this.basePath,\n        contained: this._contained,\n        ...(this._allowedPaths.length > 0 && { allowedPaths: [...this._allowedPaths] }),\n      },\n    };\n  }\n\n  getInstructions(opts?: { requestContext?: RequestContext<any> }): string {\n    return resolveInstructions(this._instructionsOverride, () => this._getDefaultInstructions(), opts?.requestContext);\n  }\n\n  private _getDefaultInstructions(): string {\n    const parts = [`Local filesystem at \"${this.basePath}\". Relative paths resolve from this directory.`];\n\n    if (this._contained) {\n      if (this._allowedPaths.length > 0) {\n        parts.push(\n          `File access is restricted to this directory and the following allowed paths: ${this._allowedPaths.join(', ')}.`,\n        );\n      } else {\n        parts.push('File access is restricted to this directory.');\n      }\n    } else {\n      parts.push('Containment is disabled, so any path on the host filesystem is accessible.');\n    }\n\n    return parts.join(' ');\n  }\n}\n","import * as nodePath from 'node:path';\n\n/**\n * File Read Tracker\n *\n * Tracks when files were last read by the workspace.\n * Used to enforce \"read before write\" semantics.\n */\n\n/**\n * Record of when a file was read.\n */\nexport interface FileReadRecord {\n  /** The file path that was read */\n  path: string;\n  /** When the file was read */\n  readAt: Date;\n  /** The file's modification time when it was read */\n  modifiedAtRead: Date;\n}\n\n/**\n * Interface for tracking file reads.\n */\nexport interface FileReadTracker {\n  /** Record that a file was read */\n  recordRead(path: string, modifiedAt: Date): void;\n\n  /** Get the last read record for a path */\n  getReadRecord(path: string): FileReadRecord | undefined;\n\n  /**\n   * Check if file needs re-reading.\n   * Returns needsReRead: true if file was never read or was modified since last read.\n   */\n  needsReRead(path: string, currentModifiedAt: Date): { needsReRead: boolean; reason?: string };\n\n  /** Clear read record (typically after a successful write) */\n  clearReadRecord(path: string): void;\n\n  /** Clear all records */\n  clear(): void;\n}\n\n/**\n * In-memory implementation of FileReadTracker.\n */\nexport class InMemoryFileReadTracker implements FileReadTracker {\n  private records = new Map<string, FileReadRecord>();\n\n  recordRead(path: string, modifiedAt: Date): void {\n    const normalizedPath = this.normalizePath(path);\n    this.records.set(normalizedPath, {\n      path: normalizedPath,\n      readAt: new Date(),\n      modifiedAtRead: modifiedAt,\n    });\n  }\n\n  getReadRecord(path: string): FileReadRecord | undefined {\n    return this.records.get(this.normalizePath(path));\n  }\n\n  needsReRead(path: string, currentModifiedAt: Date): { needsReRead: boolean; reason?: string } {\n    const record = this.getReadRecord(path);\n\n    if (!record) {\n      return {\n        needsReRead: true,\n        reason: `File \"${path}\" has not been read. You must read a file before writing to it.`,\n      };\n    }\n\n    // Compare timestamps - if current modification time is newer than when we read it\n    if (currentModifiedAt.getTime() > record.modifiedAtRead.getTime()) {\n      return {\n        needsReRead: true,\n        reason: `File \"${path}\" was modified since last read (read at: ${record.modifiedAtRead.toISOString()}, current: ${currentModifiedAt.toISOString()}). Please re-read the file to get the latest contents.`,\n      };\n    }\n\n    return { needsReRead: false };\n  }\n\n  clearReadRecord(path: string): void {\n    this.records.delete(this.normalizePath(path));\n  }\n\n  clear(): void {\n    this.records.clear();\n  }\n\n  private normalizePath(pathStr: string): string {\n    // Normalize path: unify separators, resolve dot segments, remove trailing slash\n    const normalized = nodePath.posix.normalize(pathStr.replace(/\\\\/g, '/'));\n    return normalized.replace(/\\/$/, '') || '/';\n  }\n}\n","import * as nodePath from 'node:path';\n\n/**\n * File Write Lock\n *\n * Per-file promise queue that serializes write operations to the same path.\n * Prevents read-modify-write race conditions when multiple tool calls\n * target the same file concurrently.\n */\n\n/** Options for constructing a FileWriteLock. */\nexport interface FileWriteLockOptions {\n  /** Maximum time (ms) a single lock-holder may run before being rejected. Default: 30 000. */\n  timeoutMs?: number;\n}\n\n/**\n * Interface for per-file write locking.\n */\nexport interface FileWriteLock {\n  /** Execute `fn` while holding an exclusive lock on `filePath`. */\n  withLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;\n\n  /** Number of paths that currently have queued operations. */\n  get size(): number;\n}\n\n/**\n * In-memory implementation of FileWriteLock using per-path promise queues.\n *\n * Adapted from mastracode's `withWriteLock` pattern.\n */\nexport class InMemoryFileWriteLock implements FileWriteLock {\n  private queues = new Map<string, Promise<void>>();\n  private readonly timeoutMs: number;\n\n  constructor(opts?: FileWriteLockOptions) {\n    this.timeoutMs = opts?.timeoutMs ?? 30_000;\n  }\n\n  get size(): number {\n    return this.queues.size;\n  }\n\n  withLock<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n    const key = this.normalizePath(filePath);\n\n    // Get the current queue for this file (or a resolved promise if none)\n    const currentQueue = this.queues.get(key) ?? Promise.resolve();\n\n    // Create a deferred promise for our result\n    let resolve!: (value: T) => void;\n    let reject!: (error: unknown) => void;\n    const resultPromise = new Promise<T>((res, rej) => {\n      resolve = res;\n      reject = rej;\n    });\n\n    // Chain our operation onto the queue\n    const queuePromise = currentQueue\n      .catch(() => {}) // Ignore errors from previous operations\n      .then(async () => {\n        let timeoutId: ReturnType<typeof setTimeout> | undefined;\n        try {\n          const result = await Promise.race([\n            fn(),\n            new Promise<never>((_, rej) => {\n              timeoutId = setTimeout(\n                () => rej(new Error(`write-lock timeout on \"${key}\" after ${this.timeoutMs}ms`)),\n                this.timeoutMs,\n              );\n            }),\n          ]);\n          clearTimeout(timeoutId);\n          resolve(result);\n        } catch (error) {\n          clearTimeout(timeoutId);\n          reject(error);\n        }\n      });\n\n    // Update the queue\n    this.queues.set(key, queuePromise);\n\n    // Clean up when our operation completes\n    void queuePromise.finally(() => {\n      // Only delete if we're still the last in queue\n      if (this.queues.get(key) === queuePromise) {\n        this.queues.delete(key);\n      }\n    });\n\n    return resultPromise;\n  }\n\n  private normalizePath(pathStr: string): string {\n    // Normalize path: unify separators, resolve dot segments, remove trailing slash.\n    //\n    // Known limitations:\n    // - Case-sensitive comparison: on case-insensitive filesystems (macOS HFS+,\n    //   Windows NTFS) \"Foo.txt\" and \"foo.txt\" produce different lock keys.\n    //   Acceptable because workspace tool calls echo paths back consistently.\n    // - No base-directory resolution: \"foo.txt\" and \"/workspace/foo.txt\" are\n    //   distinct keys. Workspace tools pass paths relative to the workspace root,\n    //   so this doesn't arise in practice.\n    // Collapse leading //+ before normalize (POSIX preserves leading //)\n    const normalized = nodePath.posix.normalize(pathStr.replace(/\\\\/g, '/').replace(/^\\/\\/+/, '/'));\n    return normalized.replace(/\\/+$/, '') || '/';\n  }\n}\n","/**\n * Language Detection\n *\n * Maps file extensions to LSP language identifiers.\n * Browser-safe — no Node.js dependencies.\n */\n\n/**\n * Maps file extensions (including the dot) to LSP language identifiers.\n */\nexport const LANGUAGE_EXTENSIONS: Record<string, string> = {\n  // TypeScript/JavaScript\n  '.ts': 'typescript',\n  '.tsx': 'typescriptreact',\n  '.js': 'javascript',\n  '.jsx': 'javascriptreact',\n  '.mjs': 'javascript',\n  '.cjs': 'javascript',\n\n  // Python\n  '.py': 'python',\n  '.pyi': 'python',\n\n  // Go\n  '.go': 'go',\n\n  // Rust\n  '.rs': 'rust',\n\n  // C/C++\n  '.c': 'c',\n  '.cpp': 'cpp',\n  '.cc': 'cpp',\n  '.cxx': 'cpp',\n  '.h': 'c',\n  '.hpp': 'cpp',\n\n  // Java\n  '.java': 'java',\n\n  // JSON\n  '.json': 'json',\n  '.jsonc': 'jsonc',\n\n  // YAML\n  '.yaml': 'yaml',\n  '.yml': 'yaml',\n\n  // Markdown\n  '.md': 'markdown',\n\n  // HTML/CSS\n  '.html': 'html',\n  '.css': 'css',\n  '.scss': 'scss',\n  '.sass': 'sass',\n  '.less': 'less',\n};\n\n/**\n * Get the LSP language ID for a file path based on its extension.\n * Returns undefined if the extension is not recognized.\n *\n * When `customExtensions` is provided, it is checked first, allowing\n * custom servers to register new file extensions or override built-in mappings.\n */\nexport function getLanguageId(filePath: string, customExtensions?: Record<string, string>): string | undefined {\n  const dotIndex = filePath.lastIndexOf('.');\n  if (dotIndex === -1) return undefined;\n  const ext = filePath.substring(dotIndex);\n  return customExtensions?.[ext] ?? LANGUAGE_EXTENSIONS[ext];\n}\n","/**\n * LSP Client\n *\n * JSON-RPC client wrapper for communicating with language servers.\n * Uses dynamic imports for vscode-jsonrpc and vscode-languageserver-protocol\n * to keep them as optional dependencies.\n *\n * Spawns LSP servers via a SandboxProcessManager, so it works with any\n * sandbox backend (local, E2B, etc.) that has a process manager.\n */\n\nimport { createRequire } from 'node:module';\nimport { pathToFileURL, fileURLToPath } from 'node:url';\n\nimport type { ProcessHandle, SandboxProcessManager } from '../sandbox/process-manager';\nimport type { LSPServerDef } from './types';\n\n// =============================================================================\n// Dynamic Import\n// =============================================================================\n\n/** Cached module references — undefined means not yet checked, null means unavailable */\nlet jsonrpcModule:\n  | {\n      StreamMessageReader: any;\n      StreamMessageWriter: any;\n      createMessageConnection: any;\n    }\n  | null\n  | undefined;\nlet lspProtocolModule:\n  | {\n      TextDocumentIdentifier: any;\n      Position: any;\n    }\n  | null\n  | undefined;\n\n/**\n * Check if vscode-jsonrpc is available without importing it.\n * Synchronous check — safe to call at registration time.\n */\nexport function isLSPAvailable(): boolean {\n  if (jsonrpcModule !== undefined) {\n    return jsonrpcModule !== null;\n  }\n\n  try {\n    const req = createRequire(import.meta.url);\n    req.resolve('vscode-jsonrpc/node');\n    req.resolve('vscode-languageserver-protocol');\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Load vscode-jsonrpc and vscode-languageserver-protocol.\n * Returns null if not available. Caches result after first call.\n */\nexport async function loadLSPDeps(): Promise<{\n  StreamMessageReader: any;\n  StreamMessageWriter: any;\n  createMessageConnection: any;\n  TextDocumentIdentifier: any;\n  Position: any;\n} | null> {\n  if (jsonrpcModule !== undefined && lspProtocolModule !== undefined) {\n    if (jsonrpcModule === null || lspProtocolModule === null) return null;\n    return { ...jsonrpcModule, ...lspProtocolModule };\n  }\n\n  try {\n    const req = createRequire(import.meta.url);\n    const jsonrpc = req('vscode-jsonrpc/node');\n    const protocol = req('vscode-languageserver-protocol');\n    jsonrpcModule = {\n      StreamMessageReader: jsonrpc.StreamMessageReader,\n      StreamMessageWriter: jsonrpc.StreamMessageWriter,\n      createMessageConnection: jsonrpc.createMessageConnection,\n    };\n    lspProtocolModule = {\n      TextDocumentIdentifier: protocol.TextDocumentIdentifier,\n      Position: protocol.Position,\n    };\n    return { ...jsonrpcModule, ...lspProtocolModule };\n  } catch {\n    jsonrpcModule = null;\n    lspProtocolModule = null;\n    return null;\n  }\n}\n\n// =============================================================================\n// URI Helpers\n// =============================================================================\n\n/** Convert a filesystem path to a properly encoded file:// URI. */\nfunction toFileUri(fsPath: string): string {\n  return pathToFileURL(fsPath).toString();\n}\n\n/**\n * Normalize a file:// URI to a canonical fs-path-based key for diagnostics\n * map storage/lookup. On Windows, different LSP servers emit different\n * canonical forms for the same path (e.g. `file:///C:/...` vs\n * `file:///c%3A/...`), so we convert back to an OS path and compare those\n * instead of comparing URI strings directly.\n */\nexport function diagnosticsKey(uriOrPath: string): string {\n  let fsPath: string;\n  try {\n    fsPath = uriOrPath.startsWith('file:') ? fileURLToPath(uriOrPath) : uriOrPath;\n  } catch {\n    return uriOrPath;\n  }\n  // Normalize Windows drive-letter paths so they compare equal regardless of\n  // drive-letter casing or whether fileURLToPath produced a leading slash\n  // (e.g. '/C:/Users/...' vs 'C:/Users/...' vs 'c:\\\\Users\\\\...'), independent\n  // of the OS this code happens to run on.\n  const driveMatch = fsPath.match(/^[\\\\/]?([a-zA-Z]):([\\\\/].*)$/);\n  if (driveMatch) {\n    return `${driveMatch[1]!.toLowerCase()}:${driveMatch[2]}`;\n  }\n  return fsPath;\n}\n\n// =============================================================================\n// Timeout Helper\n// =============================================================================\n\nasync function withTimeout<T>(promise: Promise<T>, ms: number, errorMessage: string): Promise<T> {\n  let timer: ReturnType<typeof setTimeout>;\n  return Promise.race([\n    promise,\n    new Promise<T>((_, reject) => {\n      timer = setTimeout(() => reject(new Error(errorMessage)), ms);\n    }),\n  ]).finally(() => clearTimeout(timer!));\n}\n\n// =============================================================================\n// LSP Client\n// =============================================================================\n\n/**\n * Wraps a JSON-RPC connection to a single LSP server process.\n * Uses a SandboxProcessManager to spawn the server process.\n */\nexport class LSPClient {\n  private connection: any = null;\n  private handle: ProcessHandle | null = null;\n  private serverDef: LSPServerDef;\n  private workspaceRoot: string;\n  private processManager: SandboxProcessManager;\n  private diagnostics: Map<string, any[]> = new Map();\n  private initializationOptions: Record<string, unknown> | null = null;\n  private supportsPullDiagnostics: boolean = false;\n\n  constructor(serverDef: LSPServerDef, workspaceRoot: string, processManager: SandboxProcessManager) {\n    this.serverDef = serverDef;\n    this.workspaceRoot = workspaceRoot;\n    this.processManager = processManager;\n  }\n\n  /** Whether the underlying server process is still running. */\n  get isAlive(): boolean {\n    return this.handle !== null && this.handle.exitCode === undefined;\n  }\n\n  /** Name of the LSP server. */\n  get serverName(): string {\n    return this.serverDef.name;\n  }\n\n  /**\n   * Initialize the LSP connection — spawns the server and performs the handshake.\n   */\n  async initialize(initTimeout: number = 10000): Promise<void> {\n    const deps = await loadLSPDeps();\n    if (!deps) {\n      throw new Error('LSP dependencies (vscode-jsonrpc) are not available');\n    }\n    const { StreamMessageReader, StreamMessageWriter, createMessageConnection } = deps;\n\n    const command = this.serverDef.command(this.workspaceRoot);\n    if (!command) {\n      throw new Error('Failed to resolve LSP server command');\n    }\n    this.handle = await this.processManager.spawn(command, { cwd: this.workspaceRoot });\n\n    const initializationOptions = this.serverDef.initialization?.(this.workspaceRoot);\n\n    const reader = new StreamMessageReader(this.handle.reader);\n    const writer = new StreamMessageWriter(this.handle.writer);\n    // vscode-jsonrpc's Connection.sendRequest re-throws stream write errors inside\n    // an `async` promise executor. That throw rejects the executor's own implicit\n    // promise (which nothing awaits) instead of the request promise, so when the\n    // server process dies mid-write (e.g. EPIPE), it surfaces as an unhandled\n    // rejection and crashes the host process. Swallow write rejections here: the\n    // error still reaches the connection's error handler via the writer's\n    // handleError, and every request we send is wrapped in a timeout, so callers\n    // get a clean timeout error instead of a fatal crash.\n    const originalWrite = writer.write.bind(writer);\n    writer.write = (msg: unknown) => originalWrite(msg).catch(() => {});\n    this.connection = createMessageConnection(reader, writer);\n\n    // Silently ignore stream destroyed errors during shutdown\n    this.connection.onError(() => {});\n\n    // Listen for published diagnostics\n    this.connection.onNotification('textDocument/publishDiagnostics', (params: any) => {\n      this.diagnostics.set(diagnosticsKey(params.uri), params.diagnostics);\n    });\n\n    this.connection.listen();\n\n    // Build initialize params\n    const initParams: any = {\n      processId: process.pid,\n      rootUri: toFileUri(this.workspaceRoot),\n      workspaceFolders: [\n        {\n          name: 'workspace',\n          uri: toFileUri(this.workspaceRoot),\n        },\n      ],\n      capabilities: {\n        window: { workDoneProgress: true },\n        workspace: { configuration: true },\n        textDocument: {\n          publishDiagnostics: {\n            relatedInformation: true,\n            tagSupport: { valueSet: [1, 2] },\n            versionSupport: false,\n          },\n          synchronization: {\n            didOpen: true,\n            didChange: true,\n            dynamicRegistration: false,\n            willSave: false,\n            willSaveWaitUntil: false,\n            didSave: false,\n          },\n          completion: {\n            dynamicRegistration: false,\n            completionItem: {\n              snippetSupport: false,\n              commitCharactersSupport: false,\n              documentationFormat: ['markdown', 'plaintext'],\n              deprecatedSupport: false,\n              preselectSupport: false,\n            },\n          },\n          definition: { dynamicRegistration: false, linkSupport: true },\n          typeDefinition: { dynamicRegistration: false, linkSupport: true },\n          implementation: { dynamicRegistration: false, linkSupport: true },\n          references: { dynamicRegistration: false },\n          documentHighlight: { dynamicRegistration: false },\n          documentSymbol: { dynamicRegistration: false, hierarchicalDocumentSymbolSupport: true },\n          codeAction: {\n            dynamicRegistration: false,\n            codeActionLiteralSupport: {\n              codeActionKind: {\n                valueSet: [\n                  'quickfix',\n                  'refactor',\n                  'refactor.extract',\n                  'refactor.inline',\n                  'refactor.rewrite',\n                  'source',\n                  'source.organizeImports',\n                ],\n              },\n            },\n          },\n          hover: { dynamicRegistration: false, contentFormat: ['markdown', 'plaintext'] },\n        },\n      },\n    };\n\n    if (initializationOptions) {\n      initParams.initializationOptions = initializationOptions;\n      this.initializationOptions = initializationOptions;\n    }\n\n    // Handle workspace/configuration requests\n    this.connection.onRequest('workspace/configuration', (params: any) => {\n      return params.items?.map(() => ({})) || [];\n    });\n\n    // Handle window/workDoneProgress/create requests\n    this.connection.onRequest('window/workDoneProgress/create', () => null);\n\n    // Handle client/registerCapability requests (TS 7+ sends these during init)\n    this.connection.onRequest('client/registerCapability', () => null);\n\n    let initTimer: ReturnType<typeof setTimeout>;\n    const initResult: any = await Promise.race([\n      this.connection.sendRequest('initialize', initParams),\n      new Promise((_, reject) => {\n        initTimer = setTimeout(() => reject(new Error('LSP initialize request timed out')), initTimeout);\n      }),\n    ]).finally(() => clearTimeout(initTimer!));\n\n    // Detect pull diagnostics support (TS 7+ native LSP uses this instead of push)\n    if (initResult?.capabilities?.diagnosticProvider) {\n      this.supportsPullDiagnostics = true;\n    }\n\n    // Send initialized notification\n    this.connection.sendNotification('initialized', {});\n\n    // Send workspace/didChangeConfiguration\n    this.connection.sendNotification('workspace/didChangeConfiguration', {\n      settings: this.initializationOptions ?? {},\n    });\n  }\n\n  /**\n   * Notify the server that a document has been opened.\n   */\n  notifyOpen(filePath: string, content: string, languageId: string): void {\n    if (!this.connection) return;\n    const uri = toFileUri(filePath);\n    this.diagnostics.delete(diagnosticsKey(uri));\n    this.connection.sendNotification('textDocument/didOpen', {\n      textDocument: { uri, languageId, version: 0, text: content },\n    });\n  }\n\n  /**\n   * Notify the server that a document has changed.\n   */\n  notifyChange(filePath: string, content: string, version: number): void {\n    if (!this.connection) return;\n    this.connection.sendNotification('textDocument/didChange', {\n      textDocument: { uri: toFileUri(filePath), version },\n      contentChanges: [{ text: content }],\n    });\n  }\n\n  /**\n   * Wait for diagnostics to arrive for a file.\n   *\n   * For servers that support pull diagnostics (e.g. TypeScript 7+ native LSP),\n   * sends a `textDocument/diagnostic` request directly instead of waiting for\n   * push notifications.\n   *\n   * For push-based servers (e.g. TypeScript ≤6 via typescript-language-server),\n   * returns as soon as diagnostics are available. To avoid returning a premature\n   * empty array (servers may publish `[]` first while still analysing), empty\n   * results trigger a short settle window: polling continues for up to `settleMs`\n   * (default 500ms) to see if non-empty diagnostics arrive. Non-empty results\n   * are returned immediately.\n   */\n  async waitForDiagnostics(\n    filePath: string,\n    timeoutMs: number = 5000,\n    waitForChange: boolean = false,\n    settleMs: number = 500,\n  ): Promise<any[]> {\n    if (!this.connection) return [];\n\n    // Pull diagnostics: request directly from the server\n    if (this.supportsPullDiagnostics) {\n      try {\n        const result: any = await withTimeout(\n          this.connection.sendRequest('textDocument/diagnostic', {\n            textDocument: { uri: toFileUri(filePath) },\n          }),\n          timeoutMs,\n          'Pull diagnostics request timed out',\n        );\n        const items = result?.items ?? [];\n        // Mirror into the diagnostics map so later lookups see the same\n        // data regardless of whether the server pushes or pulls.\n        this.diagnostics.set(diagnosticsKey(toFileUri(filePath)), items);\n        return items;\n      } catch {\n        return [];\n      }\n    }\n\n    // Push diagnostics: poll the diagnostics map populated by publishDiagnostics\n    const uri = diagnosticsKey(toFileUri(filePath));\n    const startTime = Date.now();\n    const initialDiagnostics = this.diagnostics.get(uri);\n    let emptyReceivedAt: number | undefined;\n\n    while (Date.now() - startTime < timeoutMs) {\n      const currentDiagnostics = this.diagnostics.get(uri);\n\n      if (waitForChange) {\n        // Compare by reference — the notification handler sets a new array each time\n        if (currentDiagnostics !== undefined && currentDiagnostics !== initialDiagnostics) {\n          return currentDiagnostics;\n        }\n      } else {\n        if (currentDiagnostics !== undefined) {\n          // Non-empty — the server has real results, return immediately\n          if (currentDiagnostics.length > 0) return currentDiagnostics;\n          // Empty — start a settle window. The server may have published a\n          // clearing notification before the real analysis results arrive.\n          if (emptyReceivedAt === undefined) emptyReceivedAt = Date.now();\n          if (Date.now() - emptyReceivedAt >= settleMs) return currentDiagnostics;\n        }\n      }\n\n      await new Promise(resolve => setTimeout(resolve, 100));\n    }\n\n    return waitForChange ? initialDiagnostics || [] : this.diagnostics.get(uri) || [];\n  }\n\n  /**\n   * Notify the server that a document was closed.\n   */\n  notifyClose(filePath: string): void {\n    if (!this.connection) return;\n    const uri = toFileUri(filePath);\n    this.diagnostics.delete(diagnosticsKey(uri));\n    this.connection.sendNotification('textDocument/didClose', {\n      textDocument: { uri },\n    });\n  }\n\n  /**\n   * Query hover information at a position.\n   */\n  async queryHover(uri: string, position: { line: number; character: number }, timeoutMs: number = 5000): Promise<any> {\n    if (!this.connection) return null;\n    return withTimeout(\n      this.connection.sendRequest('textDocument/hover', { textDocument: { uri }, position }),\n      timeoutMs,\n      'Hover request timed out',\n    );\n  }\n\n  /**\n   * Query definition(s) at a position.\n   */\n  async queryDefinition(\n    uri: string,\n    position: { line: number; character: number },\n    timeoutMs: number = 5000,\n  ): Promise<any[]> {\n    if (!this.connection) return [];\n    const result = await withTimeout(\n      this.connection.sendRequest('textDocument/definition', { textDocument: { uri }, position }),\n      timeoutMs,\n      'Definition request timed out',\n    );\n    if (!result) return [];\n    return Array.isArray(result) ? result : (result as any).uri ? [result] : [];\n  }\n\n  /**\n   * Query type definition(s) at a position.\n   */\n  async queryTypeDefinition(\n    uri: string,\n    position: { line: number; character: number },\n    timeoutMs: number = 5000,\n  ): Promise<any[]> {\n    if (!this.connection) return [];\n    const result = await withTimeout(\n      this.connection.sendRequest('textDocument/typeDefinition', { textDocument: { uri }, position }),\n      timeoutMs,\n      'Type definition request timed out',\n    );\n    if (!result) return [];\n    return Array.isArray(result) ? result : (result as any).uri ? [result] : [];\n  }\n\n  /**\n   * Query implementation(s) at a position.\n   */\n  async queryImplementation(\n    uri: string,\n    position: { line: number; character: number },\n    timeoutMs: number = 5000,\n  ): Promise<any[]> {\n    if (!this.connection) return [];\n    const result = await withTimeout(\n      this.connection.sendRequest('textDocument/implementation', { textDocument: { uri }, position }),\n      timeoutMs,\n      'Implementation request timed out',\n    );\n    if (!result) return [];\n    return Array.isArray(result) ? result : (result as any).uri ? [result] : [];\n  }\n\n  /**\n   * Shutdown the connection and kill the process.\n   */\n  async shutdown(): Promise<void> {\n    if (this.connection) {\n      try {\n        if (this.handle && this.handle.exitCode === undefined) {\n          let shutdownTimer: ReturnType<typeof setTimeout>;\n          await Promise.race([\n            this.connection.sendRequest('shutdown'),\n            new Promise((_, reject) => {\n              shutdownTimer = setTimeout(() => reject(new Error('Shutdown request timed out')), 1000);\n            }),\n          ]).finally(() => clearTimeout(shutdownTimer!));\n          this.connection.sendNotification('exit');\n        }\n      } catch {\n        // Ignore shutdown errors\n      }\n      try {\n        this.connection.dispose();\n      } catch {\n        // Ignore dispose errors\n      }\n      this.connection = null;\n    }\n\n    if (this.handle) {\n      try {\n        await this.handle.kill();\n      } catch {\n        // Ignore kill errors\n      }\n      this.handle = null;\n    }\n\n    this.diagnostics = new Map();\n  }\n}\n","/**\n * Built-in LSP Server Definitions\n *\n * Defines how to locate language servers and build command strings for supported languages.\n * Server definitions are pure data — they don't spawn processes themselves.\n * The LSPClient uses a SandboxProcessManager to spawn from these command strings.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, parse } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { getLanguageId } from './language';\nimport type { CustomLSPServer, LSPConfig, LSPServerDef } from './types';\n\n/** Check if a binary exists on PATH. */\nfunction whichSync(binary: string): boolean {\n  try {\n    const cmd = process.platform === 'win32' ? 'where' : 'which';\n    execFileSync(cmd, [binary], { stdio: 'ignore' });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Try to resolve a module from the given directory, then fall back to process.cwd().\n * Returns the createRequire instance that succeeded, or null.\n */\nfunction resolveRequire(root: string, moduleId: string): { require: NodeRequire; resolved: string } | null {\n  // Try from root first\n  try {\n    const req = createRequire(pathToFileURL(join(root, 'package.json')));\n    return { require: req, resolved: req.resolve(moduleId) };\n  } catch {\n    // fall through\n  }\n  // Try from cwd as fallback\n  try {\n    const req = createRequire(pathToFileURL(join(process.cwd(), 'package.json')));\n    return { require: req, resolved: req.resolve(moduleId) };\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Extend resolveRequire to also search additional directories after root and cwd.\n * Each entry in searchPaths should be a directory whose node_modules contains the module.\n */\nfunction resolveRequireFromPaths(\n  root: string,\n  moduleId: string,\n  searchPaths?: string[],\n): { require: NodeRequire; resolved: string } | null {\n  const fromBase = resolveRequire(root, moduleId);\n  if (fromBase) return fromBase;\n\n  for (const searchPath of searchPaths ?? []) {\n    try {\n      const req = createRequire(pathToFileURL(join(searchPath, 'package.json')));\n      return { require: req, resolved: req.resolve(moduleId) };\n    } catch {\n      // try next\n    }\n  }\n\n  return null;\n}\n\n/** Find a binary in node_modules/.bin, searching root, cwd, then any searchPaths. */\nfunction resolveNodeBin(root: string, binary: string, searchPaths?: string[]): string | undefined {\n  const local = join(root, 'node_modules', '.bin', binary);\n  const cwd = join(process.cwd(), 'node_modules', '.bin', binary);\n  if (existsSync(local)) return local;\n  if (existsSync(cwd)) return cwd;\n  for (const dir of searchPaths ?? []) {\n    const p = join(dir, 'node_modules', '.bin', binary);\n    if (existsSync(p)) return p;\n  }\n  return undefined;\n}\n\n/**\n * Walk up from a starting directory looking for any of the given markers.\n * Returns the first directory that contains a marker, or null.\n */\nexport function walkUp(startDir: string, markers: string[]): string | null {\n  let current = startDir;\n  const fsRoot = parse(current).root;\n\n  while (true) {\n    for (const marker of markers) {\n      if (existsSync(join(current, marker))) {\n        return current;\n      }\n    }\n    if (current === fsRoot) break;\n    const parent = dirname(current);\n    if (parent === current) break;\n    current = parent;\n  }\n\n  return null;\n}\n\n/**\n * Async version of walkUp that uses a filesystem's exists() method.\n * Works with any filesystem (local, S3, GCS, composite) that implements exists().\n */\nexport async function walkUpAsync(\n  startDir: string,\n  markers: string[],\n  fs: { exists(path: string): Promise<boolean> },\n): Promise<string | null> {\n  let current = startDir;\n  const fsRoot = parse(current).root;\n\n  while (true) {\n    for (const marker of markers) {\n      if (await fs.exists(join(current, marker))) {\n        return current;\n      }\n    }\n    if (current === fsRoot) break;\n    const parent = dirname(current);\n    if (parent === current) break;\n    current = parent;\n  }\n\n  return null;\n}\n\n/** Default markers used to find a project root when no server-specific markers are available. */\nconst DEFAULT_MARKERS = [\n  'tsconfig.json',\n  'package.json',\n  'pyproject.toml',\n  'go.mod',\n  'Cargo.toml',\n  'composer.json',\n  '.git',\n];\n\n/**\n * Find a project root by walking up from a starting directory.\n * Uses default markers (tsconfig.json, package.json, go.mod, etc.).\n * Used by Workspace to resolve the default LSP root at construction time.\n */\nexport function findProjectRoot(startDir: string): string | null {\n  return walkUp(startDir, DEFAULT_MARKERS);\n}\n\n/**\n * Async version of findProjectRoot that uses a filesystem's exists() method.\n * Works with any filesystem (local, S3, GCS, composite) that implements exists().\n */\nexport async function findProjectRootAsync(\n  startDir: string,\n  fs: { exists(path: string): Promise<boolean> },\n): Promise<string | null> {\n  return walkUpAsync(startDir, DEFAULT_MARKERS, fs);\n}\n\n/**\n * Build an extension → language ID map from custom server definitions.\n * Each extension is mapped to the first language ID of the server that declares it.\n *\n * When multiple servers declare the same extension, the last server wins\n * (iteration order of `Object.values`). A warning is emitted on collision\n * so the user can spot misconfiguration.\n */\nexport function buildCustomExtensions(servers?: Record<string, CustomLSPServer>): Record<string, string> {\n  if (!servers) return {};\n  const extensions: Record<string, string> = {};\n  for (const server of Object.values(servers)) {\n    const languageId = server.languageIds[0];\n    if (!languageId) continue;\n    for (const ext of server.extensions) {\n      const existing = extensions[ext];\n      if (existing && existing !== languageId) {\n        console.warn(\n          `[LSP] Extension \"${ext}\" is claimed by language \"${existing}\" and \"${languageId}\" (server \"${server.id}\") — using \"${languageId}\"`,\n        );\n      }\n      extensions[ext] = languageId;\n    }\n  }\n  return extensions;\n}\n\n/**\n * Convert a public custom server config to an internal server definition.\n */\nfunction toServerDef(custom: CustomLSPServer): LSPServerDef {\n  return {\n    id: custom.id,\n    name: custom.name,\n    languageIds: custom.languageIds,\n    markers: custom.markers,\n    command: () => custom.command,\n    initialization: custom.initializationOptions ? () => custom.initializationOptions! : undefined,\n  };\n}\n\n/**\n * Build a set of server definitions that incorporate LSP config overrides.\n *\n * Resolution order per server:\n *  1. `config.binaryOverrides[id]` — explicit binary command override\n *  2. Project `node_modules/.bin/` binary\n *  3. `process.cwd()` `node_modules/.bin/` binary\n *  4. `config.searchPaths` `node_modules/.bin/` binary lookup\n *  5. Global PATH lookup (system-installed binaries)\n *  6. `config.packageRunner` — package runner fallback (off by default)\n *\n * `config.searchPaths` also extends TypeScript module resolution\n * (used to locate typescript/lib/tsserver.js for TS ≤6, and tsc for TS 7+).\n *\n * When `config.servers` is provided, custom servers are merged after built-in\n * definitions. Custom servers with the same ID as a built-in will replace it.\n */\nexport function buildServerDefs(config?: LSPConfig): Record<string, LSPServerDef> {\n  const { binaryOverrides, searchPaths, packageRunner } = config ?? {};\n\n  const builtins: Record<string, LSPServerDef> = {\n    typescript: {\n      id: 'typescript',\n      name: 'TypeScript Language Server',\n      languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n      markers: ['tsconfig.json', 'package.json'],\n      command: (root: string): string | undefined => {\n        if (binaryOverrides?.typescript) return binaryOverrides.typescript;\n\n        const hasTsServer = !!resolveRequireFromPaths(root, 'typescript/lib/tsserver.js', searchPaths);\n\n        if (hasTsServer) {\n          // TS ≤6: use the typescript-language-server wrapper around tsserver.js\n          const bin = resolveNodeBin(root, 'typescript-language-server', searchPaths);\n          if (bin) return `${bin} --stdio`;\n          if (whichSync('typescript-language-server')) return 'typescript-language-server --stdio';\n          if (packageRunner) return `${packageRunner} typescript-language-server --stdio`;\n          return undefined;\n        }\n\n        // TS 7+: tsserver.js no longer exists; the native tsc speaks LSP directly.\n        // Guard on the installed typescript version so we never pass --lsp to an\n        // older tsc that happens to be on PATH when typescript isn't installed.\n        const pkg = resolveRequireFromPaths(root, 'typescript/package.json', searchPaths);\n        if (!pkg) return undefined;\n        const manifest = pkg.require(pkg.resolved) as { version?: string; bin?: string | Record<string, string> };\n        if (!(parseInt(manifest.version ?? '', 10) >= 7)) return undefined;\n        // Run the validated package's own bin entry directly instead of a\n        // node_modules/.bin shim or PATH lookup — a shim found elsewhere is not\n        // guaranteed to point at the install whose version we just checked.\n        // This also covers hoisted/monorepo and pnpm layouts, where the usable\n        // shim and the resolved package live in different directories.\n        const binRel = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.tsc;\n        if (!binRel) return undefined;\n        const tscEntry = join(dirname(pkg.resolved), binRel);\n        if (!existsSync(tscEntry)) return undefined;\n        return `node ${tscEntry} --lsp --stdio`;\n      },\n      initialization: (root: string) => {\n        // Only TS ≤6 needs the tsserver.path hint; TS 7+ native LSP doesn't use it\n        const ts = resolveRequireFromPaths(root, 'typescript/lib/tsserver.js', searchPaths);\n        if (!ts) return undefined;\n        return { tsserver: { path: ts.resolved, logVerbosity: 'off' } };\n      },\n    },\n\n    eslint: {\n      id: 'eslint',\n      name: 'ESLint Language Server',\n      languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n      markers: [\n        'package.json',\n        '.eslintrc.js',\n        '.eslintrc.json',\n        '.eslintrc.yml',\n        '.eslintrc.yaml',\n        'eslint.config.js',\n        'eslint.config.mjs',\n        'eslint.config.ts',\n      ],\n      command: (root: string): string | undefined => {\n        if (binaryOverrides?.eslint) return binaryOverrides.eslint;\n        const bin = resolveNodeBin(root, 'vscode-eslint-language-server', searchPaths);\n        if (bin) return `${bin} --stdio`;\n        if (whichSync('vscode-eslint-language-server')) return 'vscode-eslint-language-server --stdio';\n        if (packageRunner) return `${packageRunner} vscode-eslint-language-server --stdio`;\n        return undefined;\n      },\n    },\n\n    python: {\n      id: 'python',\n      name: 'Python Language Server (Pyright)',\n      languageIds: ['python'],\n      markers: ['pyproject.toml', 'setup.py', 'requirements.txt', 'setup.cfg'],\n      command: (root: string): string | undefined => {\n        if (binaryOverrides?.python) return binaryOverrides.python;\n        const bin = resolveNodeBin(root, 'pyright-langserver', searchPaths);\n        if (bin) return `${bin} --stdio`;\n        if (whichSync('pyright-langserver')) return 'pyright-langserver --stdio';\n        if (packageRunner) return `${packageRunner} pyright-langserver --stdio`;\n        return undefined;\n      },\n    },\n\n    go: {\n      id: 'go',\n      name: 'Go Language Server (gopls)',\n      languageIds: ['go'],\n      markers: ['go.mod'],\n      command: (): string | undefined => {\n        if (binaryOverrides?.go) return binaryOverrides.go;\n        return whichSync('gopls') ? 'gopls serve' : undefined;\n      },\n    },\n\n    rust: {\n      id: 'rust',\n      name: 'Rust Language Server (rust-analyzer)',\n      languageIds: ['rust'],\n      markers: ['Cargo.toml'],\n      command: (): string | undefined => {\n        if (binaryOverrides?.rust) return binaryOverrides.rust;\n        return whichSync('rust-analyzer') ? 'rust-analyzer --stdio' : undefined;\n      },\n    },\n  };\n\n  if (config?.servers) {\n    for (const custom of Object.values(config.servers)) {\n      builtins[custom.id] = toServerDef(custom);\n    }\n  }\n\n  return builtins;\n}\n\n/**\n * Built-in LSP server definitions with no config overrides.\n * Use `buildServerDefs(config)` when you need binaryOverrides, searchPaths, or packageRunner.\n */\nexport const BUILTIN_SERVERS: Record<string, LSPServerDef> = buildServerDefs();\n\n/**\n * Get all server definitions that can handle the given file.\n * Filters by language ID match only — the manager resolves the root and checks command availability.\n * Pass `defs` to use config-aware server definitions from `buildServerDefs()`.\n * Pass `customExtensions` to recognize file extensions registered by custom servers.\n */\nexport function getServersForFile(\n  filePath: string,\n  disabledServers?: string[],\n  defs?: Record<string, LSPServerDef>,\n  customExtensions?: Record<string, string>,\n): LSPServerDef[] {\n  const languageId = getLanguageId(filePath, customExtensions);\n  if (!languageId) return [];\n\n  const disabled = new Set(disabledServers ?? []);\n  const servers = defs ?? BUILTIN_SERVERS;\n\n  return Object.values(servers).filter(server => !disabled.has(server.id) && server.languageIds.includes(languageId));\n}\n","/**\n * LSP Manager\n *\n * Per-workspace manager that owns LSP server clients.\n * NOT a singleton — each Workspace instance creates its own LSPManager.\n *\n * Resolves the project root per-file by walking up from the file's directory\n * using language-specific markers defined on each server (e.g. tsconfig.json\n * for TypeScript, go.mod for Go). Falls back to the default root when\n * walkup finds nothing.\n */\n\nimport path from 'node:path';\n\nimport type { SandboxProcessManager } from '../sandbox/process-manager';\nimport { LSPClient } from './client';\nimport { getLanguageId } from './language';\nimport { buildCustomExtensions, buildServerDefs, getServersForFile, walkUp, walkUpAsync } from './servers';\nimport type { DiagnosticSeverity, LSPConfig, LSPDiagnostic, LSPServerDef } from './types';\n\n/** Map LSP DiagnosticSeverity (numeric) to our string severity */\nfunction mapSeverity(severity: number | undefined): DiagnosticSeverity {\n  switch (severity) {\n    case 1:\n      return 'error';\n    case 2:\n      return 'warning';\n    case 3:\n      return 'info';\n    case 4:\n      return 'hint';\n    default:\n      return 'warning';\n  }\n}\n\nexport class LSPManager {\n  private clients: Map<string, LSPClient> = new Map();\n  private initPromises: Map<string, Promise<void>> = new Map();\n  private fileLocks: Map<string, Promise<void>> = new Map();\n  private processManager: SandboxProcessManager;\n  private _root: string;\n  private config: LSPConfig;\n  private serverDefs: Record<string, LSPServerDef>;\n  private customExtensions: Record<string, string>;\n  private filesystem?: {\n    exists(path: string): Promise<boolean>;\n  };\n\n  constructor(\n    processManager: SandboxProcessManager,\n    root: string,\n    config: LSPConfig = {},\n    filesystem?: {\n      exists(path: string): Promise<boolean>;\n    },\n  ) {\n    this.processManager = processManager;\n    this._root = root;\n    this.config = config;\n    this.serverDefs = buildServerDefs(config);\n    this.customExtensions = buildCustomExtensions(config.servers);\n    this.filesystem = filesystem;\n  }\n\n  /** Default project root (fallback when per-file walkup finds nothing). */\n  get root(): string {\n    return this._root;\n  }\n\n  /**\n   * Resolve the project root for a given file path using the server's markers.\n   * Uses the workspace filesystem when available (supports remote filesystems),\n   * falls back to sync walkUp (local disk) otherwise.\n   */\n  private async resolveRoot(filePath: string, markers: string[]): Promise<string> {\n    const fileDir = path.dirname(filePath);\n    if (this.filesystem) {\n      return (await walkUpAsync(fileDir, markers, this.filesystem)) ?? this._root;\n    }\n    return walkUp(fileDir, markers) ?? this._root;\n  }\n\n  /**\n   * Acquire a per-file lock so that concurrent getDiagnostics calls for the\n   * same file are serialized (preventing interleaved open/change/close).\n   * Different files can run in parallel.\n   */\n  private async acquireFileLock(filePath: string): Promise<() => void> {\n    // Wait for any existing lock on this file\n    while (this.fileLocks.has(filePath)) {\n      await this.fileLocks.get(filePath);\n    }\n\n    let release!: () => void;\n    const lockPromise = new Promise<void>(resolve => {\n      release = resolve;\n    });\n    this.fileLocks.set(filePath, lockPromise);\n\n    return () => {\n      this.fileLocks.delete(filePath);\n      release();\n    };\n  }\n\n  /**\n   * Initialize an LSP client for the given server definition and project root.\n   * Handles timeout, deduplication of concurrent init calls, and caching.\n   */\n  private async initClient(serverDef: LSPServerDef, projectRoot: string, key: string): Promise<LSPClient | null> {\n    // In-progress initialization — wait for it\n    if (this.initPromises.has(key)) {\n      await this.initPromises.get(key);\n      return this.clients.get(key) || null;\n    }\n\n    // Create and initialize\n    const initTimeout = this.config.initTimeout ?? 15000;\n    let timedOut = false;\n    const initPromise = (async () => {\n      const client = new LSPClient(serverDef, projectRoot, this.processManager);\n      await client.initialize(initTimeout);\n      if (timedOut) {\n        await client.shutdown().catch(() => {});\n        return;\n      }\n      this.clients.set(key, client);\n    })();\n\n    this.initPromises.set(key, initPromise);\n    initPromise.catch(() => {}); // prevent unhandled rejection if timeout wins\n\n    try {\n      await Promise.race([\n        initPromise,\n        new Promise<void>((_, reject) =>\n          setTimeout(() => reject(new Error('LSP client initialization timed out')), initTimeout + 1000),\n        ),\n      ]);\n      return this.clients.get(key) || null;\n    } catch (err) {\n      timedOut = true;\n      this.clients.delete(key);\n      const command = serverDef.command(projectRoot);\n      const hint = this.config.binaryOverrides?.[serverDef.id]\n        ? ` (using binaryOverrides: \"${this.config.binaryOverrides[serverDef.id]}\")`\n        : command\n          ? ` (command: \"${command}\")`\n          : '';\n      console.warn(`[LSP] Failed to start ${serverDef.name}${hint}: ${err instanceof Error ? err.message : err}`);\n      return null;\n    } finally {\n      this.initPromises.delete(key);\n    }\n  }\n\n  /**\n   * Get or create an LSP client for a file path.\n   * Resolves the project root per-file using the server's markers.\n   * Returns null if no server is available.\n   */\n  async getClient(filePath: string): Promise<LSPClient | null> {\n    const servers = getServersForFile(filePath, this.config.disableServers, this.serverDefs, this.customExtensions);\n    if (servers.length === 0) return null;\n\n    // Prefer well-known language servers\n    const serverDef =\n      servers.find(\n        s =>\n          s.languageIds.includes('typescript') ||\n          s.languageIds.includes('javascript') ||\n          s.languageIds.includes('python') ||\n          s.languageIds.includes('go'),\n      ) ?? servers[0]!;\n\n    const projectRoot = await this.resolveRoot(filePath, serverDef.markers);\n\n    // Check if the server's command is available at this root\n    if (serverDef.command(projectRoot) === undefined) return null;\n\n    const key = `${serverDef.name}:${projectRoot}`;\n\n    // Existing client — check liveness before returning\n    if (this.clients.has(key)) {\n      const existing = this.clients.get(key)!;\n      if (!existing.isAlive) {\n        this.clients.delete(key);\n        existing.shutdown().catch(() => {});\n      } else {\n        return existing;\n      }\n    }\n\n    return this.initClient(serverDef, projectRoot, key);\n  }\n\n  /**\n   * Get LSP client ready to query a file.\n   * Opens the file in the client so queries can be made.\n   * Returns null when no LSP client is available.\n   */\n  async prepareQuery(filePath: string): Promise<{\n    client: LSPClient;\n    uri: string;\n    languageId: string | null;\n    serverName: string;\n  } | null> {\n    const client = await this.getClient(filePath);\n    if (!client) return null;\n\n    const languageId = getLanguageId(filePath, this.customExtensions);\n    if (!languageId) return null;\n\n    // Open the file (content doesn't matter for position queries, but server may need it)\n    const fs = await import('node:fs/promises');\n    let content = '';\n    try {\n      content = await fs.readFile(filePath, 'utf-8');\n    } catch {\n      content = '';\n    }\n\n    client.notifyOpen(filePath, content, languageId);\n\n    // Use the same URI format as notifyOpen (pathToFileURL for proper encoding)\n    const { pathToFileURL } = await import('node:url');\n    const uri = pathToFileURL(filePath).toString();\n    return { client, uri, languageId, serverName: client.serverName };\n  }\n\n  /**\n   * Convenience method: open file, send content, wait for diagnostics, return normalized results.\n   * Returns null when no LSP client is available; otherwise returns diagnostics\n   * (or an empty array on runtime failures after client acquisition).\n   * Uses a per-file lock to serialize concurrent calls for the same file.\n   */\n  async getDiagnostics(filePath: string, content: string): Promise<LSPDiagnostic[] | null> {\n    const release = await this.acquireFileLock(filePath);\n    try {\n      const client = await this.getClient(filePath);\n      if (!client) return null;\n\n      const languageId = getLanguageId(filePath, this.customExtensions);\n      if (!languageId) return [];\n\n      // Open + change → triggers diagnostics\n      client.notifyOpen(filePath, content, languageId);\n      client.notifyChange(filePath, content, 1);\n\n      const diagnosticTimeout = this.config.diagnosticTimeout ?? 5000;\n      let rawDiagnostics: any[];\n      try {\n        rawDiagnostics = await client.waitForDiagnostics(filePath, diagnosticTimeout);\n      } finally {\n        client.notifyClose(filePath);\n      }\n\n      return rawDiagnostics.map((d: any) => ({\n        severity: mapSeverity(d.severity),\n        message: d.message,\n        line: (d.range?.start?.line ?? 0) + 1, // LSP is 0-indexed, we report 1-indexed\n        character: (d.range?.start?.character ?? 0) + 1,\n        source: d.source,\n      }));\n    } catch {\n      return [];\n    } finally {\n      release();\n    }\n  }\n\n  /**\n   * Get diagnostics from ALL matching language servers for a file.\n   * Deduplicates results by (line, character, message).\n   * Individual server failures don't block other servers.\n   */\n  async getDiagnosticsMulti(filePath: string, content: string): Promise<LSPDiagnostic[]> {\n    const servers = getServersForFile(filePath, this.config.disableServers, this.serverDefs, this.customExtensions);\n    if (servers.length === 0) return [];\n\n    const release = await this.acquireFileLock(filePath);\n    try {\n      const languageId = getLanguageId(filePath, this.customExtensions);\n      if (!languageId) return [];\n\n      const allDiagnostics: LSPDiagnostic[] = [];\n\n      const results = await Promise.allSettled(\n        servers.map(async serverDef => {\n          const projectRoot = await this.resolveRoot(filePath, serverDef.markers);\n          if (serverDef.command(projectRoot) === undefined) return [];\n\n          const key = `${serverDef.name}:${projectRoot}`;\n\n          // Existing client — check liveness\n          if (this.clients.has(key)) {\n            const existing = this.clients.get(key)!;\n            if (!existing.isAlive) {\n              this.clients.delete(key);\n              existing.shutdown().catch(() => {});\n            } else {\n              return this.collectDiagnostics(existing, filePath, content, languageId);\n            }\n          }\n\n          const client = await this.initClient(serverDef, projectRoot, key);\n          if (!client) return [];\n\n          return this.collectDiagnostics(client, filePath, content, languageId);\n        }),\n      );\n\n      for (const result of results) {\n        if (result.status === 'fulfilled') {\n          allDiagnostics.push(...result.value);\n        }\n      }\n\n      // Deduplicate by (line, character, message)\n      const seen = new Set<string>();\n      return allDiagnostics.filter(d => {\n        const key = `${d.line}:${d.character}:${d.message}`;\n        if (seen.has(key)) return false;\n        seen.add(key);\n        return true;\n      });\n    } finally {\n      release();\n    }\n  }\n\n  /**\n   * Collect diagnostics from a single client for a file.\n   */\n  private async collectDiagnostics(\n    client: LSPClient,\n    filePath: string,\n    content: string,\n    languageId: string,\n  ): Promise<LSPDiagnostic[]> {\n    client.notifyOpen(filePath, content, languageId);\n    client.notifyChange(filePath, content, 1);\n\n    const diagnosticTimeout = this.config.diagnosticTimeout ?? 5000;\n    let rawDiagnostics: any[];\n    try {\n      rawDiagnostics = await client.waitForDiagnostics(filePath, diagnosticTimeout);\n    } finally {\n      client.notifyClose(filePath);\n    }\n\n    return rawDiagnostics.map((d: any) => ({\n      severity: mapSeverity(d.severity),\n      message: d.message,\n      line: (d.range?.start?.line ?? 0) + 1,\n      character: (d.range?.start?.character ?? 0) + 1,\n      source: d.source,\n    }));\n  }\n\n  /**\n   * Shutdown all managed LSP clients.\n   */\n  async shutdownAll(): Promise<void> {\n    await Promise.allSettled(Array.from(this.clients.values()).map(client => client.shutdown()));\n    this.clients.clear();\n    this.initPromises.clear();\n    this.fileLocks.clear();\n  }\n}\n","/**\n * Sandbox Errors\n *\n * Error classes for sandbox operations including execution and mounting.\n */\n\nimport type { SandboxOperation } from './types';\n\n// =============================================================================\n// Base Error\n// =============================================================================\n\nexport class SandboxError extends Error {\n  constructor(\n    message: string,\n    public readonly code: string,\n    public readonly details?: Record<string, unknown>,\n  ) {\n    super(message);\n    this.name = 'SandboxError';\n  }\n}\n\n// =============================================================================\n// Execution Errors\n// =============================================================================\n\nexport class SandboxExecutionError extends SandboxError {\n  constructor(\n    message: string,\n    public readonly exitCode: number,\n    public readonly stdout: string,\n    public readonly stderr: string,\n  ) {\n    super(message, 'EXECUTION_FAILED', { exitCode, stdout, stderr });\n    this.name = 'SandboxExecutionError';\n  }\n}\n\nexport class SandboxTimeoutError extends SandboxError {\n  constructor(\n    public readonly timeoutMs: number,\n    public readonly operation: SandboxOperation,\n  ) {\n    super(`Execution timed out after ${timeoutMs}ms`, 'TIMEOUT', { timeoutMs, operation });\n    this.name = 'SandboxTimeoutError';\n  }\n}\n\nexport class SandboxNotReadyError extends SandboxError {\n  constructor(idOrStatus: string) {\n    super(`Sandbox is not ready: ${idOrStatus}`, 'NOT_READY', { id: idOrStatus });\n    this.name = 'SandboxNotReadyError';\n  }\n}\n\nexport class IsolationUnavailableError extends SandboxError {\n  constructor(\n    public readonly backend: string,\n    public readonly reason: string,\n  ) {\n    super(`Isolation backend '${backend}' is not available: ${reason}`, 'ISOLATION_UNAVAILABLE', { backend, reason });\n    this.name = 'IsolationUnavailableError';\n  }\n}\n\n// =============================================================================\n// Mount Errors\n// =============================================================================\n\n/**\n * Base error for mount operations.\n */\nexport class MountError extends SandboxError {\n  constructor(\n    message: string,\n    public readonly mountPath: string,\n    details?: Record<string, unknown>,\n  ) {\n    super(message, 'MOUNT_ERROR', { ...details, mountPath });\n    this.name = 'MountError';\n  }\n}\n\n/**\n * Error thrown when sandbox doesn't support mounting.\n */\nexport class MountNotSupportedError extends SandboxError {\n  constructor(sandboxProvider: string) {\n    super(`Sandbox provider '${sandboxProvider}' does not support mounting`, 'MOUNT_NOT_SUPPORTED', {\n      sandboxProvider,\n    });\n    this.name = 'MountNotSupportedError';\n  }\n}\n\n/**\n * Error thrown when a filesystem cannot be mounted.\n */\nexport class FilesystemNotMountableError extends SandboxError {\n  constructor(filesystemProvider: string, reason?: string) {\n    const message = reason\n      ? `Filesystem '${filesystemProvider}' cannot be mounted: ${reason}`\n      : `Filesystem '${filesystemProvider}' does not support mounting`;\n    super(message, 'FILESYSTEM_NOT_MOUNTABLE', { filesystemProvider, reason });\n    this.name = 'FilesystemNotMountableError';\n  }\n}\n","import type { execa as execaType } from 'execa';\n\nlet cached: typeof execaType | undefined;\nlet loading: Promise<typeof execaType> | undefined;\n\n/**\n * Lazily imports execa using a runtime-constructed module specifier.\n * This prevents bundlers (Vite/Rollup/esbuild) from resolving execa at build time,\n * which is necessary for Cloudflare Workers where execa's transitive deps\n * (npm-run-path → unicorn-magic) use Node-only conditional exports.\n */\nexport async function getExeca(): Promise<typeof execaType> {\n  if (cached) {\n    return cached;\n  }\n  if (!loading) {\n    loading = (async () => {\n      try {\n        const mod = 'execa';\n        const execa = (await import(/* @vite-ignore */ /* webpackIgnore: true */ mod)).execa;\n        cached = execa;\n        return execa;\n      } catch (err) {\n        throw new Error(\n          'execa is required for local process execution but is not available in this environment. ' +\n            'LocalProcessManager is not supported in Cloudflare Workers or other non-Node runtimes.',\n          { cause: err },\n        );\n      }\n    })();\n  }\n  return loading;\n}\n","/**\n * Process Handle (Base Class)\n *\n * Abstract base class for process handles.\n * Manages stdout/stderr callback dispatch and provides lazy\n * reader/writer stream getters — subclasses only implement\n * the platform-specific primitives.\n */\n\nimport { Readable, Writable } from 'node:stream';\n\nimport type { CommandResult } from '../types';\nimport type { SpawnProcessOptions } from './types';\n\nexport const DEFAULT_MAX_RETAINED_PROCESS_OUTPUT_BYTES = 1024 * 1024;\nconst RETAINED_OUTPUT_COMPACT_CHUNK_THRESHOLD = 128;\n\n/** @internal */\nexport function validateMaxRetainedProcessOutputBytes(maxRetainedBytes: number): number {\n  if (maxRetainedBytes === Infinity) return maxRetainedBytes;\n  if (!Number.isFinite(maxRetainedBytes) || maxRetainedBytes < 0 || !Number.isInteger(maxRetainedBytes)) {\n    throw new RangeError('maxRetainedBytes must be a non-negative integer or Infinity');\n  }\n  return maxRetainedBytes;\n}\n\nfunction advanceStartByUtf8Bytes(\n  value: string,\n  start: number,\n  minimumBytesToDrop: number,\n): { start: number; droppedBytes: number } {\n  let nextStart = start;\n  let droppedBytes = 0;\n\n  while (nextStart < value.length && droppedBytes < minimumBytesToDrop) {\n    const codePoint = value.codePointAt(nextStart)!;\n\n    if (codePoint < 0x80) droppedBytes += 1;\n    else if (codePoint < 0x800) droppedBytes += 2;\n    else if (codePoint < 0x10000) droppedBytes += 3;\n    else droppedBytes += 4;\n\n    nextStart += codePoint > 0xffff ? 2 : 1;\n  }\n\n  return { start: nextStart, droppedBytes };\n}\n\ninterface RetainedOutputChunk {\n  data: string;\n  start: number;\n  bytes: number;\n  dataBytes: number;\n}\n\nclass RetainedOutputBuffer {\n  private chunks: RetainedOutputChunk[] = [];\n  private bytes = 0;\n  private droppedBytes = 0;\n  private cachedValue: string | undefined;\n\n  constructor(private readonly maxBytes: number) {}\n\n  append(data: string): void {\n    const dataBytes = Buffer.byteLength(data);\n    if (dataBytes === 0) return;\n    if (this.maxBytes === 0) {\n      this.droppedBytes += dataBytes;\n      return;\n    }\n\n    this.chunks.push({ data, start: 0, bytes: dataBytes, dataBytes });\n    this.bytes += dataBytes;\n    this.cachedValue = undefined;\n\n    this.trim();\n    this.compactIfNeeded();\n  }\n\n  toString(): string {\n    this.cachedValue ??= this.chunks.map(chunk => chunk.data.slice(chunk.start)).join('');\n    return this.cachedValue;\n  }\n\n  get truncated(): boolean {\n    return this.droppedBytes > 0;\n  }\n\n  get dropped(): number {\n    return this.droppedBytes;\n  }\n\n  private trim(): void {\n    if (this.maxBytes === Infinity) return;\n\n    while (this.bytes > this.maxBytes && this.chunks.length > 0) {\n      const overflowBytes = this.bytes - this.maxBytes;\n      const firstChunk = this.chunks[0]!;\n\n      if (firstChunk.bytes <= overflowBytes) {\n        this.chunks.shift();\n        this.bytes -= firstChunk.bytes;\n        this.droppedBytes += firstChunk.bytes;\n        continue;\n      }\n\n      const { start, droppedBytes } = advanceStartByUtf8Bytes(firstChunk.data, firstChunk.start, overflowBytes);\n      firstChunk.start = start;\n      firstChunk.bytes -= droppedBytes;\n      this.bytes -= droppedBytes;\n      this.droppedBytes += droppedBytes;\n\n      if (firstChunk.bytes === 0) {\n        this.chunks.shift();\n        continue;\n      }\n\n      if (firstChunk.dataBytes > this.maxBytes) {\n        // V8 sliced strings retain their parent, so detach a bounded suffix from an oversized source chunk.\n        firstChunk.data = Buffer.from(firstChunk.data.slice(firstChunk.start), 'utf8').toString('utf8');\n        firstChunk.start = 0;\n        firstChunk.dataBytes = firstChunk.bytes;\n      }\n    }\n  }\n\n  private compactIfNeeded(): void {\n    if (this.chunks.length <= RETAINED_OUTPUT_COMPACT_CHUNK_THRESHOLD) return;\n    const data = this.toString();\n    this.bytes = Buffer.byteLength(data);\n    this.chunks = this.bytes === 0 ? [] : [{ data, start: 0, bytes: this.bytes, dataBytes: this.bytes }];\n    this.cachedValue = data;\n  }\n}\n\n/**\n * Handle to a spawned process.\n *\n * Subclasses implement the platform-specific primitives (kill, sendStdin,\n * wait). The base class handles bounded stdout/stderr accumulation, callback\n * dispatch via `emitStdout`/`emitStderr`, lazy `reader`/`writer` stream\n * getters, and optional streaming callbacks on `wait()`.\n *\n * **For consumers:**\n * - `handle.stdout` — poll retained output\n * - `handle.wait()` — wait for exit, optionally with streaming callbacks\n * - `handle.reader` / `handle.writer` — Node.js stream interop (LSP, JSON-RPC, pipes)\n * - `onStdout`/`onStderr` callbacks in {@link SpawnProcessOptions} — stream at spawn time\n *\n * **For implementors:** Call `emitStdout(data)` / `emitStderr(data)` from\n * your transport callback (ChildProcess events, WebSocket messages, etc.)\n * to dispatch data. Pass `options` through to `super(options)` to wire\n * user callbacks automatically.\n *\n * @example\n * ```typescript\n * // Poll model\n * const handle = await sandbox.processes.spawn('node server.js');\n * console.log(handle.stdout);\n *\n * // Stream model — callbacks at spawn time\n * const handle = await sandbox.processes.spawn('npm run dev', {\n *   onStdout: (data) => console.log(data),\n * });\n *\n * // Stream model — callbacks during wait\n * const result = await handle.wait({\n *   onStdout: (data) => process.stdout.write(data),\n *   onStderr: (data) => process.stderr.write(data),\n * });\n *\n * // Stream model — pipe to LSP, JSON-RPC, etc.\n * const handle = await sandbox.processes.spawn('typescript-language-server --stdio');\n * const connection = createMessageConnection(\n *   new StreamMessageReader(handle.reader),\n *   new StreamMessageWriter(handle.writer),\n * );\n * ```\n */\nexport abstract class ProcessHandle {\n  /** Process ID */\n  abstract readonly pid: string;\n  /** Exit code, undefined while the process is still running */\n  abstract readonly exitCode: number | undefined;\n  /** The command that was spawned (set by the process manager) */\n  command?: string;\n  /** Kill the running process (SIGKILL). Returns true if killed, false if not found. */\n  abstract kill(): Promise<boolean>;\n  /** Send data to the process's stdin */\n  abstract sendStdin(data: string): Promise<void>;\n\n  /**\n   * Wait for the process to finish and return the result.\n   *\n   * Optionally pass `onStdout`/`onStderr` callbacks to stream output chunks\n   * while waiting. The callbacks are automatically removed when `wait()`\n   * resolves, so there's no cleanup needed by the caller.\n   *\n   * Subclasses implement `wait()` with platform-specific logic — the base\n   * constructor wraps it to handle the optional streaming callbacks.\n   */\n  async wait(_options?: {\n    onStdout?: (data: string) => void;\n    onStderr?: (data: string) => void;\n  }): Promise<CommandResult> {\n    throw new Error(`${this.constructor.name} must implement wait()`);\n  }\n\n  private _stdout: RetainedOutputBuffer;\n  private _stderr: RetainedOutputBuffer;\n  private _stdoutListeners = new Set<(data: string) => void>();\n  private _stderrListeners = new Set<(data: string) => void>();\n  private _reader?: Readable;\n  private _writer?: Writable;\n\n  constructor(options?: Pick<SpawnProcessOptions, 'maxRetainedBytes' | 'onStdout' | 'onStderr'>) {\n    const maxRetainedBytes = validateMaxRetainedProcessOutputBytes(\n      options?.maxRetainedBytes ?? DEFAULT_MAX_RETAINED_PROCESS_OUTPUT_BYTES,\n    );\n    this._stdout = new RetainedOutputBuffer(maxRetainedBytes);\n    this._stderr = new RetainedOutputBuffer(maxRetainedBytes);\n\n    // Spawn-time callbacks are permanent listeners\n    if (options?.onStdout) this._stdoutListeners.add(options.onStdout);\n    if (options?.onStderr) this._stderrListeners.add(options.onStderr);\n\n    // Capture subclass wait() (via prototype chain) before shadowing\n    // with a wrapper that handles optional streaming callbacks.\n    const implWait = this.wait.bind(this);\n\n    this.wait = async (waitOptions?: { onStdout?: (data: string) => void; onStderr?: (data: string) => void }) => {\n      if (waitOptions?.onStdout) this._stdoutListeners.add(waitOptions.onStdout);\n      if (waitOptions?.onStderr) this._stderrListeners.add(waitOptions.onStderr);\n      try {\n        const result = await implWait();\n        return {\n          ...result,\n          stdoutTruncated: this.stdoutTruncated,\n          stderrTruncated: this.stderrTruncated,\n          stdoutDroppedBytes: this.stdoutDroppedBytes,\n          stderrDroppedBytes: this.stderrDroppedBytes,\n        };\n      } finally {\n        if (waitOptions?.onStdout) this._stdoutListeners.delete(waitOptions.onStdout);\n        if (waitOptions?.onStderr) this._stderrListeners.delete(waitOptions.onStderr);\n      }\n    };\n  }\n\n  /** Retained stdout so far */\n  get stdout(): string {\n    return this._stdout.toString();\n  }\n\n  /** Retained stderr so far */\n  get stderr(): string {\n    return this._stderr.toString();\n  }\n\n  /** Whether stdout has dropped older output due to the retention limit */\n  get stdoutTruncated(): boolean {\n    return this._stdout.truncated;\n  }\n\n  /** Whether stderr has dropped older output due to the retention limit */\n  get stderrTruncated(): boolean {\n    return this._stderr.truncated;\n  }\n\n  /** Number of stdout bytes dropped due to the retention limit */\n  get stdoutDroppedBytes(): number {\n    return this._stdout.dropped;\n  }\n\n  /** Number of stderr bytes dropped due to the retention limit */\n  get stderrDroppedBytes(): number {\n    return this._stderr.dropped;\n  }\n\n  /**\n   * Emit stdout data — accumulates, dispatches to user callback, and pushes to reader stream.\n   * @internal Called by subclasses and process managers to dispatch transport data.\n   */\n  emitStdout(data: string): void {\n    this._stdout.append(data);\n    for (const listener of this._stdoutListeners) listener(data);\n    this._reader?.push(data);\n  }\n\n  /**\n   * Emit stderr data — accumulates and dispatches to user callback.\n   * @internal Called by subclasses and process managers to dispatch transport data.\n   */\n  emitStderr(data: string): void {\n    this._stderr.append(data);\n    for (const listener of this._stderrListeners) listener(data);\n  }\n\n  /** Readable stream of stdout (for use with StreamMessageReader, pipes, etc.) */\n  get reader(): Readable {\n    if (!this._reader) {\n      this._reader = new Readable({ read() {} });\n      void this.wait().then(\n        () => this._reader!.push(null),\n        () => this._reader!.push(null),\n      );\n    }\n    return this._reader;\n  }\n\n  /** Writable stream to stdin (for use with StreamMessageWriter, pipes, etc.) */\n  get writer(): Writable {\n    if (!this._writer) {\n      this._writer = new Writable({\n        write: (chunk, _encoding, cb) => {\n          this.sendStdin(chunk.toString()).then(() => cb(), cb);\n        },\n      });\n    }\n    return this._writer;\n  }\n}\n","/**\n * Sandbox Process Manager (Base Class)\n *\n * Abstract base class for sandbox process management.\n * Wraps all methods with ensureRunning() so the sandbox is\n * automatically started before any process operation.\n * Subclasses implement spawn(), list(), get().\n */\n\nimport type { MastraSandbox } from '../mastra-sandbox';\nimport { validateMaxRetainedProcessOutputBytes } from './process-handle';\nimport type { ProcessHandle } from './process-handle';\nimport type { ProcessInfo, SpawnProcessOptions } from './types';\n\n// =============================================================================\n// Sandbox Process Manager (Base Class)\n// =============================================================================\n\n/**\n * Abstract base class for process management in sandboxes.\n *\n * Wraps subclass overrides of `spawn()`, `list()`, and `get()` with\n * `sandbox.ensureRunning()` so the sandbox is lazily started before\n * any process operation.\n *\n * Subclasses implement the actual platform-specific logic for all methods.\n *\n * @typeParam TSandbox - The sandbox type. Subclasses narrow this to access\n *   sandbox-specific properties (e.g. `workingDirectory`, `instance`).\n *\n * @example\n * ```typescript\n * const handle = await sandbox.processes.spawn('node server.js');\n * console.log(handle.pid, handle.stdout);\n *\n * const all = await sandbox.processes.list();\n * const proc = await sandbox.processes.get(handle.pid);\n * await proc?.kill();\n * ```\n */\nexport interface ProcessManagerOptions {\n  env?: Record<string, string | undefined>;\n}\n\nexport abstract class SandboxProcessManager<TSandbox extends MastraSandbox = MastraSandbox> {\n  /**\n   * The sandbox this process manager belongs to.\n   * Set automatically by MastraSandbox when processes are passed into the constructor.\n   * @internal\n   */\n  sandbox!: TSandbox;\n\n  protected readonly env: Record<string, string | undefined>;\n\n  /** Tracked process handles keyed by PID. Populated by spawn(), used by get()/kill(). */\n  protected readonly _tracked = new Map<string, ProcessHandle>();\n\n  /** PIDs that have been read after exit and should not be re-discovered by subclass fallbacks. */\n  protected readonly _dismissed = new Set<string>();\n\n  constructor({ env = {} }: ProcessManagerOptions = {}) {\n    this.env = env;\n\n    // Capture subclass overrides (via prototype chain) before shadowing\n    // with wrapped versions that add ensureRunning().\n    const impl = {\n      spawn: this.spawn.bind(this),\n      list: this.list.bind(this),\n      get: this.get.bind(this),\n    };\n\n    this.spawn = async (...args: Parameters<typeof impl.spawn>) => {\n      // Validate before starting a sandbox; ProcessHandle validates again for direct subclass construction.\n      if (args[1]?.maxRetainedBytes !== undefined) {\n        validateMaxRetainedProcessOutputBytes(args[1].maxRetainedBytes);\n      }\n      await this.sandbox.ensureRunning();\n      const handle = await impl.spawn(...args);\n      handle.command = args[0];\n\n      // Wire abort signal to handle.kill() so all providers get abort support automatically.\n      const abortSignal = args[1]?.abortSignal;\n      if (abortSignal) {\n        const onAbort = () => {\n          handle.kill().catch(() => {});\n        };\n        if (abortSignal.aborted) {\n          handle.kill().catch(() => {});\n        } else {\n          abortSignal.addEventListener('abort', onAbort, { once: true });\n          // Clean up listener when process exits\n          handle.wait().then(\n            () => abortSignal.removeEventListener('abort', onAbort),\n            () => abortSignal.removeEventListener('abort', onAbort),\n          );\n        }\n      }\n\n      return handle;\n    };\n\n    this.list = async () => {\n      await this.sandbox.ensureRunning();\n      return impl.list();\n    };\n\n    this.get = async (...args: Parameters<typeof impl.get>) => {\n      await this.sandbox.ensureRunning();\n      // Skip PIDs that were already read after exit and dismissed.\n      if (this._dismissed.has(args[0])) return undefined;\n      const handle = await impl.get(...args);\n      // Prune exited processes when their output is read — this is the\n      // only automatic cleanup path. Keeps output available until the\n      // consumer has seen it at least once.\n      if (handle?.exitCode !== undefined) {\n        this._tracked.delete(handle.pid);\n        this._dismissed.add(handle.pid);\n      }\n      return handle;\n    };\n  }\n\n  /** Spawn a process. */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n    throw new Error(`${this.constructor.name} must implement spawn()`);\n  }\n\n  /** List all tracked processes. */\n  async list(): Promise<ProcessInfo[]> {\n    throw new Error(`${this.constructor.name} must implement list()`);\n  }\n\n  /** Get a handle to a process by PID. Subclasses can override for fallback behavior. */\n  async get(pid: string): Promise<ProcessHandle | undefined> {\n    return this._tracked.get(pid);\n  }\n\n  /** Kill a process by PID. Returns true if killed, false if not found. */\n  async kill(pid: string): Promise<boolean> {\n    const handle = await this.get(pid);\n    if (!handle) return false;\n    const killed = await handle.kill();\n    if (killed) {\n      // Wait for termination so handle.exitCode is populated before returning.\n      // Without this, a subsequent get() could still report the process as running.\n      await handle.wait().catch(() => {});\n    }\n    // Release tracked handle to free accumulated output buffers.\n    this._tracked.delete(handle.pid);\n    this._dismissed.add(handle.pid);\n    return killed;\n  }\n}\n","/**\n * Local Process Manager\n *\n * Local implementation of SandboxProcessManager using execa.\n * Tracks processes in-memory since there's no server to query.\n */\n\nimport * as path from 'node:path';\nimport { StringDecoder } from 'node:string_decoder';\n\nimport type { ResultPromise, Options as ExecaOptions } from 'execa';\n\nimport { getExeca } from './execa';\nimport type { LocalSandbox } from './local-sandbox';\nimport { ProcessHandle, SandboxProcessManager } from './process-manager';\nimport type { ProcessInfo, SpawnProcessOptions } from './process-manager';\nimport type { CommandResult } from './types';\n\nconst isWindows = process.platform === 'win32';\n\n// =============================================================================\n// Local Process Handle\n// =============================================================================\n\n/**\n * Local implementation of ProcessHandle wrapping an execa subprocess.\n * Not exported — internal to this module.\n */\nclass LocalProcessHandle extends ProcessHandle {\n  readonly pid: string;\n  exitCode: number | undefined;\n\n  private readonly _numericPid: number;\n  private subprocess: ResultPromise;\n  private readonly waitPromise: Promise<CommandResult>;\n  private readonly startTime: number;\n\n  constructor(subprocess: ResultPromise, pid: number, startTime: number, options?: SpawnProcessOptions) {\n    super(options);\n    this.pid = String(pid);\n    this._numericPid = pid;\n    this.subprocess = subprocess;\n    this.startTime = startTime;\n\n    let timedOut = false;\n    const timeoutId = options?.timeout\n      ? setTimeout(() => {\n          timedOut = true;\n          // Kill the entire process tree so child processes are also terminated.\n          // We handle timeout ourselves rather than using execa's timeout option\n          // because execa only kills the direct subprocess, not the process tree.\n          void killProcessTree(this._numericPid, subprocess, 'SIGTERM');\n        }, options.timeout)\n      : undefined;\n\n    const stdoutDecoder = new StringDecoder();\n    const stderrDecoder = new StringDecoder();\n    let stdoutDecoderEnded = false;\n    let stderrDecoderEnded = false;\n\n    const flushStdoutDecoder = () => {\n      if (stdoutDecoderEnded) return;\n      stdoutDecoderEnded = true;\n      const data = stdoutDecoder.end();\n      if (data) this.emitStdout(data);\n    };\n\n    const flushStderrDecoder = () => {\n      if (stderrDecoderEnded) return;\n      stderrDecoderEnded = true;\n      const data = stderrDecoder.end();\n      if (data) this.emitStderr(data);\n    };\n\n    this.waitPromise = new Promise<CommandResult>(resolve => {\n      subprocess.on('close', (code: number | null, signal: NodeJS.Signals | null) => {\n        if (timeoutId) clearTimeout(timeoutId);\n        flushStdoutDecoder();\n        flushStderrDecoder();\n        if (timedOut) {\n          const timeoutMsg = `\\nProcess timed out after ${options!.timeout}ms`;\n          this.emitStderr(timeoutMsg);\n          this.exitCode = 124;\n        } else {\n          this.exitCode = signal && code === null ? 128 : (code ?? 0);\n        }\n        resolve({\n          success: this.exitCode === 0,\n          exitCode: this.exitCode,\n          stdout: this.stdout,\n          stderr: this.stderr,\n          executionTimeMs: Date.now() - this.startTime,\n          killed: signal !== null,\n          timedOut,\n        });\n      });\n\n      subprocess.on('error', (err: Error) => {\n        if (timeoutId) clearTimeout(timeoutId);\n        flushStdoutDecoder();\n        flushStderrDecoder();\n        this.emitStderr(err.message);\n        this.exitCode = 1;\n        resolve({\n          success: false,\n          exitCode: 1,\n          stdout: this.stdout,\n          stderr: this.stderr,\n          executionTimeMs: Date.now() - this.startTime,\n        });\n      });\n    });\n\n    subprocess.stdout?.on('data', (data: Buffer) => {\n      const decoded = stdoutDecoder.write(data);\n      if (decoded) this.emitStdout(decoded);\n    });\n    subprocess.stdout?.on('end', flushStdoutDecoder);\n\n    subprocess.stderr?.on('data', (data: Buffer) => {\n      const decoded = stderrDecoder.write(data);\n      if (decoded) this.emitStderr(decoded);\n    });\n    subprocess.stderr?.on('end', flushStderrDecoder);\n  }\n\n  async wait(): Promise<CommandResult> {\n    return this.waitPromise;\n  }\n\n  async kill(): Promise<boolean> {\n    if (this.exitCode !== undefined) return false;\n    // Kill the entire process tree to ensure child processes spawned by the\n    // shell are also terminated. Without this, commands like\n    // \"echo foo; sleep 60\" would leave orphaned children holding stdio open.\n    await killProcessTree(this._numericPid, this.subprocess, 'SIGKILL');\n    return true;\n  }\n\n  async sendStdin(data: string): Promise<void> {\n    if (this.exitCode !== undefined) {\n      throw new Error(`Process ${this.pid} has already exited with code ${this.exitCode}`);\n    }\n    if (!this.subprocess.stdin) {\n      throw new Error(`Process ${this.pid} does not have stdin available`);\n    }\n    return new Promise<void>((resolve, reject) => {\n      this.subprocess.stdin!.write(data, (err: Error | null | undefined) => (err ? reject(err) : resolve()));\n    });\n  }\n}\n\n// =============================================================================\n// Process Tree Killing\n// =============================================================================\n\n/**\n * Kill a process and all its children.\n *\n * On Unix, we use process groups (negative PID) since processes are spawned\n * with `detached: true` which creates a new process group.\n *\n * On Windows, `process.kill(-pid)` doesn't work (no process groups), and\n * `detached: true` opens a new console window. Instead we use `taskkill /T`\n * which recursively kills the process tree by PID.\n */\nasync function killProcessTree(pid: number, subprocess: ResultPromise, signal: NodeJS.Signals): Promise<void> {\n  if (isWindows) {\n    try {\n      // /T = kill child processes, /F = force, /PID = target process\n      const execa = await getExeca();\n      await execa('taskkill', ['/T', '/F', '/PID', String(pid)], { reject: false, stdio: 'ignore' });\n    } catch {\n      // taskkill binary not found — fall back to direct kill\n      subprocess.kill(signal);\n    }\n  } else {\n    try {\n      process.kill(-pid, signal);\n    } catch {\n      subprocess.kill(signal);\n    }\n  }\n}\n\n// =============================================================================\n// Local Process Manager\n// =============================================================================\n\n/**\n * Local implementation of SandboxProcessManager.\n * Spawns processes via execa and tracks them in-memory.\n */\nexport class LocalProcessManager extends SandboxProcessManager<LocalSandbox> {\n  async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n    let cwd = this.sandbox.workingDirectory;\n    if (options.cwd) {\n      if (path.isAbsolute(options.cwd)) {\n        cwd = options.cwd;\n      } else {\n        // Prevent duplicate nesting when agent passes cwd that's already workspace-relative\n        const normalizedWorkingDir = path.resolve(this.sandbox.workingDirectory);\n        const normalizedOptionsCwd = path.resolve(options.cwd);\n        // Check if path is already under workspace (exact match or nested subpath)\n        const isAlreadyWorkspacePath =\n          normalizedOptionsCwd === normalizedWorkingDir ||\n          normalizedOptionsCwd.startsWith(`${normalizedWorkingDir}${path.sep}`);\n\n        cwd = isAlreadyWorkspacePath ? normalizedOptionsCwd : path.resolve(this.sandbox.workingDirectory, options.cwd);\n      }\n    }\n    const env = this.sandbox.buildEnv(options.env);\n    const wrapped = this.sandbox.wrapCommandForIsolation(command);\n\n    // Base options shared across all platforms.\n    const baseOptions = {\n      cwd,\n      env,\n      stdio: 'pipe' as const,\n      // Don't throw on non-zero exit — we handle exit codes ourselves.\n      reject: false,\n      // Don't buffer output — we stream it via ProcessHandle callbacks.\n      buffer: false,\n      // Don't strip newlines — preserve raw output for ProcessHandle accumulation.\n      stripFinalNewline: false,\n      // Don't extend process.env — the sandbox controls the full environment via buildEnv().\n      extendEnv: false,\n    };\n\n    let execaOptions: ExecaOptions;\n\n    if (isWindows) {\n      // On Windows, `detached: true` opens a new console window (visible cmd.exe\n      // popup) and breaks stdout/stderr piping. `shell: true` without `detached`\n      // works correctly — it uses cmd.exe for shell interpretation and pipes\n      // stdout/stderr back to the parent process without any visible window.\n      //\n      // Process tree killing uses `taskkill /T` instead of Unix process groups.\n      execaOptions = {\n        ...baseOptions,\n        shell: this.sandbox.isolation === 'none',\n      };\n    } else {\n      // On Unix, `detached: true` creates a new process group so we can kill the\n      // entire tree via `process.kill(-pid, signal)`.\n      //\n      // Non-isolated: use shell mode so the host shell interprets the command string\n      // (pipes, redirects, chaining, etc.). Isolated (seatbelt/bwrap): the wrapper\n      // already includes `sh -c` inside the sandbox, so we spawn the wrapper directly.\n      execaOptions = {\n        ...baseOptions,\n        detached: true,\n        shell: this.sandbox.isolation === 'none',\n      };\n    }\n\n    const execa = await getExeca();\n    const subprocess = execa(wrapped.command, wrapped.args, execaOptions);\n\n    // execa sets pid synchronously when the process spawns successfully.\n    // If pid is undefined, the spawn failed (bad cwd, missing command, etc.).\n    // Await the subprocess to get execa's detailed error message.\n    if (!subprocess.pid) {\n      const result = await subprocess;\n      throw new Error(result.message || 'Process failed to spawn');\n    }\n\n    const handle = new LocalProcessHandle(subprocess, subprocess.pid, Date.now(), options);\n    this._tracked.set(handle.pid, handle);\n    return handle;\n  }\n\n  async list(): Promise<ProcessInfo[]> {\n    return Array.from(this._tracked.values()).map(handle => ({\n      pid: handle.pid,\n      running: handle.exitCode === undefined,\n      exitCode: handle.exitCode,\n    }));\n  }\n}\n","/**\n * Shared types for local mount operations.\n */\n\nexport const LOG_PREFIX = '[LocalSandbox]';\n\n/**\n * Context for local mount operations.\n * Uses a run function instead of E2B's sandbox.commands.run().\n */\nexport interface LocalMountContext {\n  run: (\n    command: string,\n    args: string[],\n    options?: { timeout?: number },\n  ) => Promise<{ stdout: string; stderr: string; exitCode: number }>;\n  platform: NodeJS.Platform;\n  logger: {\n    debug: (message: string, ...args: unknown[]) => void;\n    info: (message: string, ...args: unknown[]) => void;\n    warn: (message: string, ...args: unknown[]) => void;\n    error: (message: string, ...args: unknown[]) => void;\n  };\n}\n\n/**\n * Error thrown when a required FUSE tool (s3fs, gcsfuse, macFUSE) is not installed.\n *\n * Distinguished from general mount errors so `LocalSandbox.mount()` can mark the\n * mount as `unavailable` (warning) rather than `error`. The workspace still works\n * via SDK filesystem methods — only sandbox process access to the mount path is affected.\n */\nexport class MountToolNotFoundError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = 'MountToolNotFoundError';\n  }\n}\n","/**\n * Mount Manager\n *\n * Encapsulates all mount-related state and operations for sandboxes.\n * Used by BaseSandbox to manage filesystem mounts.\n */\n\nimport { createHash } from 'node:crypto';\n\nimport type { IMastraLogger } from '../../logger';\nimport type { WorkspaceFilesystem } from '../filesystem/filesystem';\nimport type { FilesystemMountConfig, MountResult } from '../filesystem/mount';\n\nimport type { Workspace } from '../workspace';\nimport { MountToolNotFoundError } from './mounts/types';\nimport type { WorkspaceSandbox } from './sandbox';\nimport type { MountEntry, MountState } from './types';\n\n// Type-only import — erased at compile time, no circular dependency at runtime.\n\n/**\n * Mount function signature.\n */\nexport type MountFn = (filesystem: WorkspaceFilesystem, mountPath: string) => Promise<MountResult>;\n\n/**\n * onMount hook result.\n * - false: skip mount\n * - { success, error? }: hook handled it\n * - void: use default mount\n */\nexport type OnMountResult = false | { success: boolean; error?: string } | void;\n\n/**\n * Arguments passed to the onMount hook.\n */\nexport interface OnMountArgs {\n  /** The filesystem being mounted */\n  filesystem: WorkspaceFilesystem;\n  /** The mount path in the sandbox */\n  mountPath: string;\n  /** The mount configuration from filesystem.getMountConfig() (undefined if not supported) */\n  config: FilesystemMountConfig | undefined;\n  /** The sandbox instance for custom mount implementations */\n  sandbox: WorkspaceSandbox;\n  /** The workspace instance */\n  workspace: Workspace;\n}\n\n/**\n * onMount hook function.\n *\n * Called for each filesystem before mounting into sandbox.\n * Return value controls mounting behavior (see {@link OnMountResult}).\n *\n * @example Skip local filesystems\n * ```typescript\n * onMount: ({ filesystem }) => {\n *   if (filesystem.provider === 'local') return false;\n * }\n * ```\n *\n * @example Custom mount implementation\n * ```typescript\n * onMount: async ({ filesystem, mountPath, sandbox }) => {\n *   if (mountPath === '/custom') {\n *     await sandbox.executeCommand?.('my-mount-script', [mountPath]);\n *     return { success: true };\n *   }\n * }\n * ```\n */\nexport type OnMountHook = (args: OnMountArgs) => Promise<OnMountResult> | OnMountResult;\n\n/**\n * MountManager configuration.\n */\nexport interface MountManagerConfig {\n  /** The mount implementation from the sandbox */\n  mount: MountFn;\n  /** Logger instance */\n  logger: IMastraLogger;\n}\n\n/**\n * Manages filesystem mounts for a sandbox.\n *\n * Provides methods for tracking mount state, updating entries,\n * and processing pending mounts.\n */\nexport class MountManager {\n  private _entries: Map<string, MountEntry> = new Map();\n  private _mountFn: MountFn;\n  private _onMount?: OnMountHook;\n  private _sandbox?: WorkspaceSandbox;\n  private _workspace?: Workspace;\n  private logger: IMastraLogger;\n\n  constructor(config: MountManagerConfig) {\n    this._mountFn = config.mount;\n    this.logger = config.logger;\n  }\n\n  /**\n   * Set the sandbox and workspace references for onMount hook args.\n   * Called by Workspace during construction.\n   */\n  setContext(context: { sandbox: WorkspaceSandbox; workspace: Workspace }): void {\n    this._sandbox = context.sandbox;\n    this._workspace = context.workspace;\n  }\n\n  /**\n   * Set the onMount hook for custom mount handling.\n   * Called before each mount - can skip, handle, or defer to default.\n   */\n  setOnMount(hook: OnMountHook | undefined): void {\n    this._onMount = hook;\n  }\n\n  /**\n   * Update the logger instance.\n   * Called when the sandbox receives a logger from Mastra.\n   * @internal\n   */\n  __setLogger(logger: IMastraLogger): void {\n    this.logger = logger;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Entry Access\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Get all mount entries.\n   */\n  get entries(): ReadonlyMap<string, MountEntry> {\n    return this._entries;\n  }\n\n  /**\n   * Get a mount entry by path.\n   */\n  get(path: string): MountEntry | undefined {\n    return this._entries.get(path);\n  }\n\n  /**\n   * Check if a mount exists at the given path.\n   */\n  has(path: string): boolean {\n    return this._entries.has(path);\n  }\n\n  // ---------------------------------------------------------------------------\n  // Entry Modification\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Add pending mounts from workspace config.\n   * These will be processed when `processPending()` is called.\n   */\n  add(mounts: Record<string, WorkspaceFilesystem>): void {\n    const paths = Object.keys(mounts);\n    this.logger.debug('Adding pending mounts', { count: paths.length, paths });\n\n    for (const [path, filesystem] of Object.entries(mounts)) {\n      this._entries.set(path, {\n        filesystem,\n        state: 'pending',\n      });\n    }\n  }\n\n  /**\n   * Update a mount entry's state.\n   * Creates the entry if it doesn't exist.\n   */\n  set(\n    path: string,\n    updates: {\n      filesystem?: WorkspaceFilesystem;\n      state: MountState;\n      config?: FilesystemMountConfig;\n      error?: string;\n    },\n  ): void {\n    const existing = this._entries.get(path);\n\n    if (existing) {\n      existing.state = updates.state;\n      if (updates.config) {\n        existing.config = updates.config;\n        existing.configHash = this.hashConfig(updates.config);\n      }\n      if ('error' in updates) {\n        existing.error = updates.error;\n      }\n    } else if (updates.filesystem) {\n      // Create new entry (for direct mount() calls without add())\n      this._entries.set(path, {\n        filesystem: updates.filesystem,\n        state: updates.state,\n        config: updates.config,\n        configHash: updates.config ? this.hashConfig(updates.config) : undefined,\n        error: updates.error,\n      });\n    } else {\n      this.logger.debug('set() called for unknown path without filesystem', { path });\n    }\n  }\n\n  /**\n   * Delete a mount entry.\n   */\n  delete(path: string): boolean {\n    return this._entries.delete(path);\n  }\n\n  /**\n   * Clear all mount entries.\n   */\n  clear(): void {\n    this._entries.clear();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Mount Processing\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Process all pending mounts.\n   * Call this after sandbox is ready (in start()).\n   */\n  async processPending(): Promise<void> {\n    const pendingCount = [...this._entries.values()].filter(e => e.state === 'pending').length;\n    if (pendingCount === 0) {\n      return;\n    }\n\n    this.logger.debug('Processing pending mounts', { count: pendingCount });\n\n    for (const [path, entry] of this._entries) {\n      if (entry.state !== 'pending') {\n        continue;\n      }\n\n      const fsProvider = entry.filesystem.provider;\n\n      // Get config if available\n      const config = entry.filesystem.getMountConfig?.();\n\n      // Call onMount hook if configured\n      if (this._onMount) {\n        try {\n          const hookResult = await this._onMount({\n            filesystem: entry.filesystem,\n            mountPath: path,\n            config,\n            sandbox: this._sandbox!,\n            workspace: this._workspace!,\n          });\n\n          // false = skip mount entirely\n          if (hookResult === false) {\n            entry.state = 'unsupported';\n            entry.error = 'Skipped by onMount hook';\n            this.logger.debug('Mount skipped by onMount hook', { path, provider: fsProvider });\n            continue;\n          }\n\n          // { success, error? } = hook handled it\n          if (hookResult && typeof hookResult === 'object') {\n            if (hookResult.success) {\n              entry.state = 'mounted';\n              entry.config = config;\n              entry.configHash = config ? this.hashConfig(config) : undefined;\n              this.logger.info('Mount handled by onMount hook', { path, provider: fsProvider });\n            } else {\n              entry.state = 'error';\n              entry.error = hookResult.error ?? 'Mount hook failed';\n              this.logger.error('Mount hook failed', { path, provider: fsProvider, error: entry.error });\n            }\n            continue;\n          }\n\n          // void = continue with default mount\n        } catch (err) {\n          entry.state = 'error';\n          entry.error = `Mount hook error: ${String(err)}`;\n          this.logger.error('Mount hook threw error', { path, provider: fsProvider, error: entry.error });\n          continue;\n        }\n      }\n\n      // Check if filesystem supports mounting (for default behavior)\n      if (!config) {\n        entry.state = 'unsupported';\n        entry.error = 'Filesystem does not support mounting';\n        this.logger.debug('Filesystem does not support mounting', { path, provider: fsProvider });\n        continue;\n      }\n\n      // Store config and mark as mounting\n      entry.config = config;\n      entry.configHash = this.hashConfig(config);\n      entry.state = 'mounting';\n\n      this.logger.debug('Mounting filesystem', { path, provider: fsProvider, type: config.type });\n\n      // Call the sandbox's mount implementation\n      try {\n        const result = await this._mountFn(entry.filesystem, path);\n        if (result.success) {\n          entry.state = 'mounted';\n          this.logger.info('Mount successful', { path, provider: fsProvider });\n        } else if (result.unavailable) {\n          entry.state = 'unavailable';\n          entry.error = result.error ?? 'FUSE tool not installed';\n          this.logger.warn('FUSE mount unavailable', { path, provider: fsProvider, error: entry.error });\n        } else {\n          entry.state = 'error';\n          entry.error = result.error ?? 'Mount failed';\n          this.logger.error('Mount failed', { path, provider: fsProvider, error: entry.error });\n        }\n      } catch (err) {\n        if (err instanceof MountToolNotFoundError) {\n          entry.state = 'unavailable';\n          entry.error = String(err);\n          this.logger.warn('FUSE mount unavailable', { path, provider: fsProvider, error: entry.error });\n        } else {\n          entry.state = 'error';\n          entry.error = String(err);\n          this.logger.error('Mount threw error', { path, provider: fsProvider, error: entry.error });\n        }\n      }\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Marker File Helpers\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Generate a marker filename for a mount path.\n   * Used by sandboxes to store mount metadata for reconnection detection.\n   *\n   * @param mountPath - The mount path to generate a filename for\n   * @returns A safe filename like \"mount-abc123\"\n   */\n  markerFilename(mountPath: string): string {\n    let hash = 0;\n    for (let i = 0; i < mountPath.length; i++) {\n      const char = mountPath.charCodeAt(i);\n      hash = (hash << 5) - hash + char;\n      hash |= 0; // Convert to 32-bit integer\n    }\n    return `mount-${Math.abs(hash).toString(36)}`;\n  }\n\n  /**\n   * Generate marker file content for a mount path.\n   * Format: \"path|configHash\" - used for detecting config changes on reconnect.\n   *\n   * @param mountPath - The mount path\n   * @returns Marker content string, or null if no config hash available\n   */\n  getMarkerContent(mountPath: string): string | null {\n    const entry = this._entries.get(mountPath);\n    if (!entry?.configHash) {\n      return null;\n    }\n    return `${mountPath}|${entry.configHash}`;\n  }\n\n  /**\n   * Parse marker file content.\n   *\n   * @param content - The marker file content (format: \"path|configHash\")\n   * @returns Parsed path and configHash, or null if invalid format\n   */\n  parseMarkerContent(content: string): { path: string; configHash: string } | null {\n    const separatorIndex = content.lastIndexOf('|');\n    if (separatorIndex <= 0) {\n      return null;\n    }\n    const path = content.slice(0, separatorIndex);\n    const configHash = content.slice(separatorIndex + 1);\n    if (!path || !configHash) return null;\n    return { path, configHash };\n  }\n\n  /**\n   * Check if a config hash matches the expected hash for a mount path.\n   *\n   * @param mountPath - The mount path to check\n   * @param storedHash - The hash from the marker file\n   * @returns true if the hashes match\n   */\n  isConfigMatching(mountPath: string, storedHash: string): boolean {\n    const entry = this._entries.get(mountPath);\n    return entry?.configHash === storedHash;\n  }\n\n  /**\n   * Compute a hash for a mount config. Used for comparing configs across mounts.\n   *\n   * @param config - The config to hash\n   * @returns A hash string suitable for comparison\n   */\n  computeConfigHash(config: FilesystemMountConfig): string {\n    return this.hashConfig(config);\n  }\n\n  // ---------------------------------------------------------------------------\n  // Internal\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Hash a mount config for comparison.\n   */\n  private hashConfig(config: FilesystemMountConfig): string {\n    const normalized = JSON.stringify(this.sortKeysDeep(config));\n    return createHash('sha256').update(normalized).digest('hex').slice(0, 16);\n  }\n\n  private sortKeysDeep(obj: unknown): unknown {\n    if (obj === null || typeof obj !== 'object') return obj;\n    if (Array.isArray(obj)) return obj.map(item => this.sortKeysDeep(item));\n    return Object.keys(obj as Record<string, unknown>)\n      .sort()\n      .reduce(\n        (acc, key) => {\n          acc[key] = this.sortKeysDeep((obj as Record<string, unknown>)[key]);\n          return acc;\n        },\n        {} as Record<string, unknown>,\n      );\n  }\n}\n","/**\n * Shell-quote an argument for safe interpolation into a shell command string.\n * Safe characters (alphanumeric, `.`, `_`, `-`, `/`, `=`, `:`, `@`) pass through.\n * Everything else is wrapped in single quotes with embedded quotes escaped.\n */\nexport function shellQuote(arg: string): string {\n  if (/^[a-zA-Z0-9._\\-\\/=:@]+$/.test(arg)) return arg;\n  return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n/**\n * Result of splitting a shell command string.\n */\nexport interface ShellSplitResult {\n  /** Command parts between operators */\n  parts: string[];\n  /** Operators that were found (&&, ||, ;) */\n  operators: string[];\n}\n\n/**\n * Split a shell command string on operators (&&, ||, ;) while respecting quotes.\n *\n * This is a quote-aware splitter that won't split inside single or double quoted strings.\n * Handles escaped quotes within quoted strings.\n *\n * @example\n * ```typescript\n * splitShellCommand('echo \"hello && world\" && ls')\n * // => { parts: ['echo \"hello && world\"', 'ls'], operators: ['&&'] }\n *\n * splitShellCommand(\"bash -c 'cd /tmp && pwd' || echo fail\")\n * // => { parts: [\"bash -c 'cd /tmp && pwd'\", 'echo fail'], operators: ['||'] }\n * ```\n */\nexport function splitShellCommand(command: string): ShellSplitResult {\n  const parts: string[] = [];\n  const operators: string[] = [];\n\n  let current = '';\n  let i = 0;\n  let inSingleQuote = false;\n  let inDoubleQuote = false;\n\n  while (i < command.length) {\n    const char = command[i]!;\n    const next = command[i + 1];\n\n    // Handle escape sequences (backslash)\n    if (char === '\\\\' && i + 1 < command.length) {\n      current += char + next;\n      i += 2;\n      continue;\n    }\n\n    // Toggle quote states\n    if (char === \"'\" && !inDoubleQuote) {\n      inSingleQuote = !inSingleQuote;\n      current += char;\n      i++;\n      continue;\n    }\n\n    if (char === '\"' && !inSingleQuote) {\n      inDoubleQuote = !inDoubleQuote;\n      current += char;\n      i++;\n      continue;\n    }\n\n    // Only check for operators when not inside quotes\n    if (!inSingleQuote && !inDoubleQuote) {\n      // Check for && or ||\n      if ((char === '&' && next === '&') || (char === '|' && next === '|')) {\n        parts.push(current.trim());\n        operators.push(char + next);\n        current = '';\n        i += 2;\n        // Skip whitespace after operator\n        while (i < command.length && /\\s/.test(command[i]!)) i++;\n        continue;\n      }\n\n      // Check for ;\n      if (char === ';') {\n        parts.push(current.trim());\n        operators.push(';');\n        current = '';\n        i++;\n        // Skip whitespace after operator\n        while (i < command.length && /\\s/.test(command[i]!)) i++;\n        continue;\n      }\n    }\n\n    current += char;\n    i++;\n  }\n\n  // Add the final part\n  if (current.trim()) {\n    parts.push(current.trim());\n  }\n\n  return { parts, operators };\n}\n\n/**\n * Reassemble command parts with their operators.\n *\n * @example\n * ```typescript\n * reassembleShellCommand(['echo hello', 'ls'], ['&&'])\n * // => 'echo hello && ls'\n * ```\n */\nexport function reassembleShellCommand(parts: string[], operators: string[]): string {\n  let result = parts[0] ?? '';\n  for (let i = 0; i < operators.length; i++) {\n    result += ` ${operators[i]} ${parts[i + 1] ?? ''}`;\n  }\n  return result.trim();\n}\n","/**\n * MastraSandbox Base Class\n *\n * Abstract base class for sandbox providers that want automatic logger integration.\n * Extends MastraBase to receive the Mastra logger when registered with a Mastra instance.\n *\n * MountManager is automatically created if the subclass implements `mount()`.\n * Use `declare readonly mounts: MountManager` to get non-optional typing.\n *\n * ## Lifecycle Management\n *\n * The base class provides race-condition-safe lifecycle wrappers:\n * - `_start()` - Handles concurrent calls, status management, and mount processing\n * - `_stop()` - Handles concurrent calls and status management\n * - `_destroy()` - Handles concurrent calls and status management\n *\n * Subclasses override the plain `start()`, `stop()`, and `destroy()` methods\n * to provide their implementation. Callers use the `_`-prefixed wrappers\n * (or `callLifecycle()`) which add status tracking and race-condition safety.\n *\n * External providers can extend this class to get logger support, or implement\n * the WorkspaceSandbox interface directly if they don't need logging.\n */\n\nimport { MastraBase } from '../../base';\nimport type { IMastraLogger } from '../../logger';\nimport { RegisteredLogger } from '../../logger/constants';\nimport type { WorkspaceFilesystem } from '../filesystem/filesystem';\nimport type { MountResult } from '../filesystem/mount';\nimport type { ProviderStatus } from '../lifecycle';\nimport { SandboxNotReadyError } from './errors';\nimport { MountManager } from './mount-manager';\nimport type { SandboxProcessManager } from './process-manager';\nimport type { SandboxFileInput, SandboxNetworking, WorkspaceSandbox } from './sandbox';\nimport type { CommandResult, ExecuteCommandOptions, SandboxInfo } from './types';\nimport { shellQuote } from './utils';\n\n/**\n * Lifecycle hook that fires during sandbox state transitions.\n * Receives the sandbox instance so users can call `executeCommand`, read files, etc.\n */\nexport type SandboxLifecycleHook = (args: { sandbox: WorkspaceSandbox }) => void | Promise<void>;\n\n/**\n * Options for the MastraSandbox base class constructor.\n * Providers extend this to add their own options while inheriting lifecycle hooks.\n */\nexport interface MastraSandboxOptions {\n  /** Called after the sandbox reaches 'running' status */\n  onStart?: SandboxLifecycleHook;\n  /** Called before the sandbox stops */\n  onStop?: SandboxLifecycleHook;\n  /** Called before the sandbox is destroyed */\n  onDestroy?: SandboxLifecycleHook;\n\n  /**\n   * Process manager for this sandbox.\n   *\n   * When provided, the base class automatically:\n   * 1. Sets the sandbox back-reference on the process manager\n   * 2. Exposes it via `this.processes`\n   * 3. Creates a default `executeCommand` implementation (spawn + wait)\n   *\n   * @example\n   * ```typescript\n   * class MySandbox extends MastraSandbox {\n   *   constructor() {\n   *     super({\n   *       name: 'MySandbox',\n   *       processes: new MyProcessManager({ env: myEnv }),\n   *     });\n   *   }\n   * }\n   * ```\n   */\n  processes?: SandboxProcessManager;\n}\n\n/**\n * Abstract base class for sandbox providers with logger support.\n *\n * Providers that extend this class automatically receive the Mastra logger\n * when the sandbox is used with a Mastra instance. MountManager is also\n * automatically created if the subclass implements `mount()`.\n *\n * @example\n * ```typescript\n * class MyCustomSandbox extends MastraSandbox {\n *   declare readonly mounts: MountManager;  // Non-optional type\n *   readonly id = 'my-sandbox';\n *   readonly name = 'MyCustomSandbox';\n *   readonly provider = 'custom';\n *   status: ProviderStatus = 'pending';\n *\n *   constructor() {\n *     super({\n *       name: 'MyCustomSandbox',\n *       processes: new MyProcessManager({ env: myEnv }),\n *     });\n *   }\n *\n *   async start(): Promise<void> { /* startup logic *\\/ }\n *   async mount(filesystem, mountPath) { ... }\n *   async unmount(mountPath) { ... }\n * }\n * ```\n */\nexport abstract class MastraSandbox extends MastraBase implements WorkspaceSandbox {\n  /** Unique identifier for this sandbox instance */\n  abstract readonly id: string;\n\n  /** Human-readable name (e.g., 'E2B Sandbox', 'Docker') */\n  abstract readonly name: string;\n\n  /** Provider type identifier */\n  abstract readonly provider: string;\n\n  /** Current status of the sandbox */\n  abstract status: ProviderStatus;\n\n  // ---------------------------------------------------------------------------\n  // Optional WorkspaceSandbox members\n  //\n  // Re-declared here so that variables typed as `MastraSandbox` (not just\n  // `WorkspaceSandbox`) can see them.  TypeScript's `implements` is a\n  // constraint check, not a type merge — optional interface members are\n  // invisible on the class type unless explicitly listed.\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Execute a shell command and wait for completion.\n   *\n   * Method syntax (not property syntax) is intentional — it prevents\n   * `useDefineForClassFields` from emitting `this.executeCommand = undefined`\n   * which would shadow prototype methods defined by subclasses.\n   */\n  executeCommand?(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult>;\n\n  /** Optional networking capability - implement to expose public port URLs */\n  readonly networking?: SandboxNetworking;\n\n  /**\n   * Optional bulk file upload into the sandbox's own filesystem.\n   *\n   * Method syntax (not property syntax) is intentional — it prevents\n   * `useDefineForClassFields` from emitting `this.writeFiles = undefined`\n   * which would shadow prototype methods defined by subclasses.\n   */\n  writeFiles?(files: SandboxFileInput[]): Promise<void>;\n\n  /** Process manager */\n  readonly processes?: SandboxProcessManager;\n\n  /** Mount manager - automatically created if subclass implements mount() */\n  readonly mounts?: MountManager;\n\n  /** Optional mount method - implement to enable mounting support */\n  mount?(filesystem: WorkspaceFilesystem, mountPath: string): Promise<MountResult>;\n\n  /** Optional unmount method */\n  unmount?(mountPath: string): Promise<void>;\n\n  /** Get instructions describing how this sandbox works */\n  getInstructions?(): string;\n\n  /** Get sandbox status and metadata */\n  getInfo?(): SandboxInfo | Promise<SandboxInfo>;\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle Promise Tracking (prevents race conditions)\n  // ---------------------------------------------------------------------------\n\n  /** Promise for _start() to prevent race conditions from concurrent calls */\n  protected _startPromise?: Promise<void>;\n\n  /** Promise for _stop() to prevent race conditions from concurrent calls */\n  protected _stopPromise?: Promise<void>;\n\n  /** Promise for _destroy() to prevent race conditions from concurrent calls */\n  protected _destroyPromise?: Promise<void>;\n\n  /** Lifecycle callbacks */\n  private readonly _onStart?: SandboxLifecycleHook;\n  private readonly _onStop?: SandboxLifecycleHook;\n  private readonly _onDestroy?: SandboxLifecycleHook;\n\n  constructor(options: { name: string } & MastraSandboxOptions) {\n    super({ name: options.name, component: RegisteredLogger.WORKSPACE });\n\n    this._onStart = options.onStart;\n    this._onStop = options.onStop;\n    this._onDestroy = options.onDestroy;\n\n    // Automatically create MountManager if subclass implements mount()\n    if (this.mount) {\n      this.mounts = new MountManager({\n        mount: this.mount.bind(this),\n        logger: this.logger,\n      });\n    }\n\n    // Wire up process manager if provided\n    if (options.processes) {\n      const pm = options.processes;\n      // Set the sandbox back-reference. The process manager reads this\n      // lazily (at call time), so it's fine that the subclass constructor\n      // hasn't finished yet.\n      pm.sandbox = this;\n      this.processes = pm;\n\n      // Auto-create executeCommand (spawn + wait) unless the subclass\n      // defines its own implementation.\n      if (!this.executeCommand) {\n        this.executeCommand = async (command: string, args?: string[], opts?: ExecuteCommandOptions) => {\n          const fullCommand = args?.length ? `${command} ${args.map(a => shellQuote(a)).join(' ')}` : command;\n          this.logger.debug('Executing command', { sandbox: this.name, command: fullCommand, cwd: opts?.cwd });\n\n          const handle = await pm.spawn(fullCommand, { ...opts, maxRetainedBytes: opts?.maxRetainedBytes ?? Infinity });\n          const result = await handle.wait();\n\n          this.logger.debug('Command completed', {\n            sandbox: this.name,\n            exitCode: result.exitCode,\n            duration: result.executionTimeMs,\n          });\n\n          return { ...result, command: fullCommand };\n        };\n      }\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle Wrappers (race-condition-safe)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Start the sandbox (wrapper with status management and race-condition safety).\n   *\n   * This method is race-condition-safe - concurrent calls will return the same promise.\n   * Handles status management and automatically processes pending mounts after startup.\n   *\n   * Subclasses override `start()` to provide their startup logic.\n   */\n  async _start(): Promise<void> {\n    // Already running\n    if (this.status === 'running') {\n      return;\n    }\n\n    // Wait for in-flight stop/destroy before starting.\n    // Intentionally no .catch() — if teardown is failing, _start() should propagate\n    // that error rather than silently starting on top of a broken state.\n    if (this._stopPromise) await this._stopPromise;\n    if (this._destroyPromise) await this._destroyPromise;\n\n    // Cannot start a destroyed sandbox\n    if (this.status === 'destroyed') {\n      throw new Error('Cannot start a destroyed sandbox');\n    }\n\n    // Start already in progress - return existing promise\n    if (this._startPromise) {\n      return this._startPromise;\n    }\n\n    // Create and store the start promise\n    this._startPromise = this._executeStart();\n\n    try {\n      await this._startPromise;\n    } finally {\n      this._startPromise = undefined;\n    }\n  }\n\n  /**\n   * Internal start execution - handles status and mount processing.\n   */\n  private async _executeStart(): Promise<void> {\n    this.status = 'starting';\n\n    try {\n      await this.start();\n      this.status = 'running';\n\n      // Fire onStart callback after sandbox is running — treat failure as non-fatal\n      // so that a bad callback doesn't kill an otherwise healthy sandbox\n      try {\n        await this._onStart?.({ sandbox: this });\n      } catch (error) {\n        this.logger.warn('onStart callback failed', { error });\n      }\n    } catch (error) {\n      this.status = 'error';\n      throw error;\n    }\n\n    // Process any pending mounts after successful start\n    // Mount failures are tracked individually in MountManager and\n    // shouldn't mark the sandbox itself as errored\n    try {\n      await this.mounts?.processPending();\n    } catch (error) {\n      // Mount failures are tracked in MountManager — log but don't affect sandbox status\n      this.logger.warn('Unexpected error processing pending mounts', { error });\n    }\n  }\n\n  /**\n   * Override this method to implement sandbox startup logic.\n   *\n   * Called by `_start()` after status is set to 'starting'.\n   * Status will be set to 'running' on success, 'error' on failure.\n   *\n   * @example\n   * ```typescript\n   * async start(): Promise<void> {\n   *   this._sandbox = await Sandbox.create({ ... });\n   * }\n   * ```\n   */\n  async start(): Promise<void> {\n    // Default no-op - subclasses override\n  }\n\n  /**\n   * Ensure the sandbox is running.\n   *\n   * Calls `_start()` if status is not 'running'. Useful for lazy initialization\n   * where operations should automatically start the sandbox if needed.\n   *\n   * @throws {SandboxNotReadyError} if the sandbox fails to reach 'running' status\n   *\n   * @example\n   * ```typescript\n   * async executeCommand(command: string): Promise<CommandResult> {\n   *   await this.ensureRunning();\n   *   // Now safe to use the sandbox\n   * }\n   * ```\n   */\n  async ensureRunning(): Promise<void> {\n    // Already destroyed — cannot use this sandbox\n    if (this.status === 'destroyed') {\n      throw new SandboxNotReadyError(this.id);\n    }\n    // During teardown the sandbox is still operational (e.g. destroy()\n    // may need to list/kill processes).  Allow operations to proceed\n    // without trying to restart.\n    if (this.status === 'destroying' || this.status === 'stopping') {\n      return;\n    }\n    if (this.status !== 'running') {\n      await this._start();\n    }\n    if (this.status !== 'running') {\n      throw new SandboxNotReadyError(this.id);\n    }\n  }\n\n  /**\n   * Stop the sandbox (wrapper with status management and race-condition safety).\n   *\n   * This method is race-condition-safe - concurrent calls will return the same promise.\n   * Handles status management.\n   *\n   * Subclasses override `stop()` to provide their stop logic.\n   */\n  async _stop(): Promise<void> {\n    // Already stopped\n    if (this.status === 'stopped') {\n      return;\n    }\n\n    // Wait for in-flight start before stopping\n    if (this._startPromise) await this._startPromise.catch(() => {});\n\n    // Stop already in progress - return existing promise\n    if (this._stopPromise) {\n      return this._stopPromise;\n    }\n\n    // Create and store the stop promise\n    this._stopPromise = this._executeStop();\n\n    try {\n      await this._stopPromise;\n    } finally {\n      this._stopPromise = undefined;\n    }\n  }\n\n  /**\n   * Internal stop execution - handles status.\n   */\n  private async _executeStop(): Promise<void> {\n    this.status = 'stopping';\n\n    try {\n      // Fire onStop callback before stopping\n      await this._onStop?.({ sandbox: this });\n\n      await this.stop();\n      this.status = 'stopped';\n    } catch (error) {\n      this.status = 'error';\n      throw error;\n    }\n  }\n\n  /**\n   * Override this method to implement sandbox stop logic.\n   *\n   * Called by `_stop()` after status is set to 'stopping'.\n   * Status will be set to 'stopped' on success, 'error' on failure.\n   */\n  async stop(): Promise<void> {\n    // Default no-op - subclasses override\n  }\n\n  /**\n   * Destroy the sandbox and clean up all resources (wrapper with status management).\n   *\n   * This method is race-condition-safe - concurrent calls will return the same promise.\n   * Handles status management.\n   *\n   * Subclasses override `destroy()` to provide their destroy logic.\n   */\n  async _destroy(): Promise<void> {\n    // Already destroyed\n    if (this.status === 'destroyed') {\n      return;\n    }\n\n    // Never started — nothing to clean up\n    if (this.status === 'pending') {\n      this.status = 'destroyed';\n      return;\n    }\n\n    // Wait for in-flight start/stop before destroying\n    if (this._startPromise) await this._startPromise.catch(() => {});\n    if (this._stopPromise) await this._stopPromise.catch(() => {});\n\n    // Destroy already in progress - return existing promise\n    if (this._destroyPromise) {\n      return this._destroyPromise;\n    }\n\n    // Create and store the destroy promise\n    this._destroyPromise = this._executeDestroy();\n\n    try {\n      await this._destroyPromise;\n    } finally {\n      this._destroyPromise = undefined;\n    }\n  }\n\n  /**\n   * Internal destroy execution - handles status.\n   */\n  private async _executeDestroy(): Promise<void> {\n    this.status = 'destroying';\n\n    try {\n      // Fire onDestroy callback before destroying\n      await this._onDestroy?.({ sandbox: this });\n\n      await this.destroy();\n      this.status = 'destroyed';\n    } catch (error) {\n      this.status = 'error';\n      throw error;\n    }\n  }\n\n  /**\n   * Override this method to implement sandbox destroy logic.\n   *\n   * Called by `_destroy()` after status is set to 'destroying'.\n   * Status will be set to 'destroyed' on success, 'error' on failure.\n   */\n  async destroy(): Promise<void> {\n    // Default no-op - subclasses override\n  }\n\n  // ---------------------------------------------------------------------------\n  // Logger Propagation\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Override to propagate logger to MountManager.\n   * @internal\n   */\n  override __setLogger(logger: IMastraLogger): void {\n    super.__setLogger(logger);\n    // Propagate to MountManager if it exists\n    this.mounts?.__setLogger(logger);\n  }\n}\n","/**\n * Platform Detection\n *\n * Detects available sandboxing backends for the current platform.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport os from 'node:os';\n\nimport type { IsolationBackend, SandboxDetectionResult } from './types';\n\n/**\n * Check if a command exists on the system.\n */\nfunction commandExists(command: string): boolean {\n  try {\n    // Use 'which' on Unix-like systems\n    execFileSync('which', [command], { stdio: 'ignore' });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Check if seatbelt (sandbox-exec) is available.\n * This is built-in on macOS.\n */\nexport function isSeatbeltAvailable(): boolean {\n  if (os.platform() !== 'darwin') {\n    return false;\n  }\n  return commandExists('sandbox-exec');\n}\n\n/**\n * Check if bubblewrap (bwrap) is available.\n * This must be installed on Linux systems.\n */\nexport function isBwrapAvailable(): boolean {\n  if (os.platform() !== 'linux') {\n    return false;\n  }\n  return commandExists('bwrap');\n}\n\n/**\n * Detect the best available isolation backend for the current platform.\n *\n * @returns The recommended isolation backend and availability info\n *\n * @example\n * ```typescript\n * const result = detectIsolation();\n * if (result.available) {\n *   console.log(`Using ${result.backend} for sandboxing`);\n * } else {\n *   console.warn(result.message);\n * }\n * ```\n */\nexport function detectIsolation(): SandboxDetectionResult {\n  const platform = os.platform();\n\n  if (platform === 'darwin') {\n    const available = isSeatbeltAvailable();\n    return {\n      backend: 'seatbelt',\n      available,\n      message: available\n        ? 'macOS seatbelt (sandbox-exec) is available'\n        : 'macOS seatbelt (sandbox-exec) not found - this is unexpected on macOS',\n    };\n  }\n\n  if (platform === 'linux') {\n    const available = isBwrapAvailable();\n    return {\n      backend: 'bwrap',\n      available,\n      message: available\n        ? 'Linux bubblewrap (bwrap) is available'\n        : 'Linux bubblewrap (bwrap) not found. Install with: apt install bubblewrap (Debian/Ubuntu) or dnf install bubblewrap (Fedora)',\n    };\n  }\n\n  // Windows or other platforms\n  return {\n    backend: 'none',\n    available: false,\n    message: `Native sandboxing is not supported on ${platform}. Commands will run without isolation.`,\n  };\n}\n\n/**\n * Check if a specific isolation backend is available.\n *\n * @param backend - The isolation backend to check\n * @returns Whether the backend is available on this system\n */\nexport function isIsolationAvailable(backend: IsolationBackend): boolean {\n  switch (backend) {\n    case 'seatbelt':\n      return isSeatbeltAvailable();\n    case 'bwrap':\n      return isBwrapAvailable();\n    case 'none':\n      return true;\n    default:\n      return false;\n  }\n}\n\n/**\n * Get the recommended isolation backend for this platform.\n * Returns 'none' if no sandboxing is available.\n */\nexport function getRecommendedIsolation(): IsolationBackend {\n  const result = detectIsolation();\n  return result.available ? result.backend : 'none';\n}\n","/**\n * Seatbelt (macOS sandbox-exec)\n *\n * macOS built-in sandboxing using sandbox-exec with SBPL profiles.\n *\n * Important: Uses `-p` (inline profile) instead of `-f` (file) because\n * `-f` doesn't work reliably with path filters on modern macOS.\n *\n * Note on macOS sandbox limitations:\n * - `(allow file-read* (subpath ...))` only works WITH a preceding `(allow file-read*)`\n * - So for reads: allow all, then deny specific paths\n * - For writes: allow specific paths with subpath filters\n *\n * Based on the approach used by Claude Code's sandbox-runtime:\n * https://github.com/anthropic-experimental/sandbox-runtime\n */\n\nimport { realpathSync } from 'node:fs';\n\nimport type { NativeSandboxConfig } from './types';\n\n/**\n * Mach services needed for basic operation\n */\nconst MACH_SERVICES = [\n  'com.apple.distributed_notifications@Uv3',\n  'com.apple.logd',\n  'com.apple.system.logger',\n  'com.apple.system.notification_center',\n  'com.apple.system.opendirectoryd.libinfo',\n  'com.apple.system.opendirectoryd.membership',\n  'com.apple.bsd.dirhelper',\n  'com.apple.securityd.xpc',\n  'com.apple.SecurityServer',\n  'com.apple.trustd.agent',\n];\n\n/**\n * Escape a path for use in SBPL profile.\n * Uses JSON.stringify for proper escaping.\n */\nfunction escapePath(pathStr: string): string {\n  return JSON.stringify(pathStr);\n}\n\nfunction canonicalizePath(pathStr: string): string {\n  try {\n    return realpathSync(pathStr);\n  } catch {\n    return pathStr;\n  }\n}\n\n/**\n * Generate a seatbelt profile for the given configuration.\n *\n * The profile:\n * - Allows all file reads (can't restrict with subpath on macOS)\n * - Restricts file writes to temp directories, configured writable paths, and the workspace unless read-only\n * - Blocks network unless explicitly allowed\n *\n * @param workspacePath - The workspace directory to sandbox\n * @param config - Additional sandbox configuration\n * @returns The generated SBPL profile content\n */\nexport function generateSeatbeltProfile(workspacePath: string, config: NativeSandboxConfig): string {\n  // Fail-closed: seatbelt cannot restrict process-exec, so reject unsupported config\n  if (config.allowSystemBinaries === false) {\n    throw new Error(\n      'allowSystemBinaries: false is not supported by seatbelt (macOS). ' +\n        'Use bubblewrap on Linux or remove this restriction.',\n    );\n  }\n\n  const lines: string[] = [];\n\n  // Version and default deny\n  lines.push('(version 1)');\n  lines.push('(deny default (with message \"mastra-sandbox\"))');\n  lines.push('');\n\n  // Process permissions\n  lines.push('; Process permissions');\n  lines.push('(allow process-exec)');\n  lines.push('(allow process-fork)');\n  lines.push('(allow process-info* (target same-sandbox))');\n  lines.push('(allow signal (target same-sandbox))');\n  lines.push('');\n\n  // Mach IPC\n  lines.push('; Mach IPC');\n  lines.push('(allow mach-lookup');\n  for (const service of MACH_SERVICES) {\n    lines.push(`  (global-name \"${service}\")`);\n  }\n  lines.push(')');\n  lines.push('');\n\n  // IPC\n  lines.push('; IPC');\n  lines.push('(allow ipc-posix-shm)');\n  lines.push('(allow ipc-posix-sem)');\n  lines.push('');\n\n  // User preferences\n  lines.push('; User preferences');\n  lines.push('(allow user-preference-read)');\n  lines.push('');\n\n  // sysctl\n  lines.push('; sysctl');\n  lines.push('(allow sysctl-read)');\n  lines.push('');\n\n  // Device files\n  lines.push('; Device files');\n  lines.push('(allow file-ioctl (literal \"/dev/null\"))');\n  lines.push('(allow file-ioctl (literal \"/dev/zero\"))');\n  lines.push('(allow file-ioctl (literal \"/dev/random\"))');\n  lines.push('(allow file-ioctl (literal \"/dev/urandom\"))');\n  lines.push('(allow file-ioctl (literal \"/dev/tty\"))');\n  lines.push('');\n\n  // File read access - allow all reads (macOS limitation: can't use subpath without this)\n  lines.push('; File read access (allow all - macOS sandbox limitation)');\n  lines.push('(allow file-read*)');\n\n  // Add custom read-only paths as additional allows (technically redundant but explicit)\n  for (const p of config.readOnlyPaths ?? []) {\n    lines.push(`(allow file-read* (subpath ${escapePath(p)}))`);\n  }\n  lines.push('');\n\n  // File write access - restrict to workspace and temp\n  lines.push('; File write access (restricted to workspace and temp)');\n\n  const canonicalWorkspacePath = canonicalizePath(workspacePath);\n\n  // Workspace\n  if (!config.readOnly) {\n    lines.push(`(allow file-write* (subpath ${escapePath(canonicalWorkspacePath)}))`);\n  }\n\n  // Temp directories (needed for many operations). When the workspace is read-only,\n  // exclude it from these broad allows so temp-backed workspaces remain protected.\n  for (const tempPath of ['/private/tmp', '/var/folders', '/private/var/folders']) {\n    if (config.readOnly) {\n      lines.push(\n        `(allow file-write* (require-all (subpath ${escapePath(tempPath)}) (require-not (subpath ${escapePath(canonicalWorkspacePath)}))))`,\n      );\n    } else {\n      lines.push(`(allow file-write* (subpath ${escapePath(tempPath)}))`);\n    }\n  }\n\n  // Custom read-write paths\n  for (const p of config.readWritePaths ?? []) {\n    lines.push(`(allow file-write* (subpath ${escapePath(canonicalizePath(p))}))`);\n  }\n  lines.push('');\n\n  // Network\n  lines.push('; Network');\n  if (config.allowNetwork) {\n    lines.push('(allow network*)');\n  } else {\n    lines.push('(deny network* (with message \"mastra-sandbox-network\"))');\n  }\n\n  return lines.join('\\n');\n}\n\n/**\n * Build the command arguments for sandbox-exec.\n *\n * Uses `-p` (inline profile) instead of `-f` (file) because\n * `-f` doesn't work reliably with path filters on modern macOS.\n *\n * @param command - The full shell command string to run\n * @param profile - The SBPL profile content (not a file path)\n * @returns Wrapped command and arguments for sandbox-exec\n */\nexport function buildSeatbeltCommand(command: string, profile: string): { command: string; args: string[] } {\n  return {\n    command: 'sandbox-exec',\n    args: ['-p', profile, 'sh', '-c', command],\n  };\n}\n","/**\n * Bubblewrap (Linux bwrap)\n *\n * Linux sandboxing using user namespaces and bind mounts.\n * https://github.com/containers/bubblewrap\n */\n\nimport * as path from 'node:path';\n\nimport type { NativeSandboxConfig } from './types';\n\n/**\n * System paths to mount read-only by default.\n * These are needed for basic command execution.\n */\nconst DEFAULT_READONLY_BINDS = [\n  '/usr',\n  '/lib',\n  '/lib64',\n  '/bin',\n  '/sbin',\n  '/etc/alternatives',\n  '/etc/ssl',\n  '/etc/ca-certificates',\n  '/etc/resolv.conf',\n  '/etc/hosts',\n  '/etc/passwd',\n  '/etc/group',\n  '/etc/nsswitch.conf',\n  '/etc/ld.so.cache',\n  '/etc/localtime',\n];\n\n/**\n * Build the bwrap command arguments for the given configuration.\n *\n * @param command - The full shell command string to run inside the sandbox\n * @param workspacePath - The workspace directory (mounted read-write)\n * @param config - Additional sandbox configuration\n * @returns Wrapped command and arguments for bwrap\n */\nexport function buildBwrapCommand(\n  command: string,\n  workspacePath: string,\n  config: NativeSandboxConfig,\n): { command: string; args: string[] } {\n  // If custom bwrap args are provided, use them directly\n  if (config.bwrapArgs && config.bwrapArgs.length > 0) {\n    return {\n      command: 'bwrap',\n      args: [...config.bwrapArgs, '--', 'sh', '-c', command],\n    };\n  }\n\n  const bwrapArgs: string[] = [];\n\n  // Create new namespaces for isolation\n  bwrapArgs.push('--unshare-pid'); // PID namespace (can't see host processes)\n  bwrapArgs.push('--unshare-ipc'); // IPC namespace\n  bwrapArgs.push('--unshare-uts'); // UTS namespace (separate hostname)\n\n  // Network isolation (unless explicitly allowed)\n  if (!config.allowNetwork) {\n    bwrapArgs.push('--unshare-net');\n  }\n\n  // Mount a new /proc for the PID namespace\n  bwrapArgs.push('--proc', '/proc');\n\n  // Mount a tmpfs at /tmp\n  bwrapArgs.push('--tmpfs', '/tmp');\n\n  // Mount system paths read-only\n  for (const path of DEFAULT_READONLY_BINDS) {\n    // Use --ro-bind-try to skip paths that don't exist on this system\n    bwrapArgs.push('--ro-bind-try', path, path);\n  }\n\n  // Mount custom read-only paths\n  for (const path of config.readOnlyPaths ?? []) {\n    bwrapArgs.push('--ro-bind', path, path);\n  }\n\n  // Allow system binaries by default (node, python, etc.)\n  if (config.allowSystemBinaries !== false) {\n    // Include the Node.js binary location\n    const nodePath = process.execPath;\n    const nodeDir = path.dirname(nodePath);\n\n    // Mount the node directory if it's not already covered\n    if (!DEFAULT_READONLY_BINDS.some(p => nodeDir.startsWith(p))) {\n      bwrapArgs.push('--ro-bind', nodeDir, nodeDir);\n    }\n\n    // Also mount common runtime locations\n    bwrapArgs.push('--ro-bind-try', '/opt', '/opt');\n    bwrapArgs.push('--ro-bind-try', '/snap', '/snap');\n  }\n\n  // Mount workspace (read-only or read-write)\n  if (config.readOnly) {\n    bwrapArgs.push('--ro-bind', workspacePath, workspacePath);\n  } else {\n    bwrapArgs.push('--bind', workspacePath, workspacePath);\n  }\n\n  // Mount custom read-write paths\n  for (const path of config.readWritePaths ?? []) {\n    bwrapArgs.push('--bind', path, path);\n  }\n\n  // Set the working directory\n  bwrapArgs.push('--chdir', workspacePath);\n\n  // Die with parent (clean up if the parent process dies)\n  bwrapArgs.push('--die-with-parent');\n\n  // Add the command separator and run via sh -c for shell interpretation\n  bwrapArgs.push('--', 'sh', '-c', command);\n\n  return {\n    command: 'bwrap',\n    args: bwrapArgs,\n  };\n}\n","/**\n * Command Wrapper\n *\n * Wraps commands with the appropriate sandbox backend.\n */\n\nimport { buildBwrapCommand } from './bubblewrap';\nimport { buildSeatbeltCommand, generateSeatbeltProfile } from './seatbelt';\nimport type { IsolationBackend, NativeSandboxConfig } from './types';\n\nexport interface WrappedCommand {\n  command: string;\n  args: string[];\n}\n\nexport interface WrapCommandOptions {\n  /** The isolation backend to use */\n  backend: IsolationBackend;\n  /** The workspace directory path */\n  workspacePath: string;\n  /** Pre-generated seatbelt profile content (optional, will be generated if not provided) */\n  seatbeltProfile?: string;\n  /** Native sandbox configuration */\n  config: NativeSandboxConfig;\n}\n\n/**\n * Wrap a command with the appropriate sandbox backend.\n *\n * @param command - The full shell command string to run\n * @param options - Wrapping options\n * @returns The wrapped command and arguments\n *\n * @example\n * ```typescript\n * const wrapped = wrapCommand('node script.js', {\n *   backend: 'seatbelt',\n *   workspacePath: '/workspace',\n *   config: { allowNetwork: false },\n * });\n * // wrapped.command = 'sandbox-exec'\n * // wrapped.args = ['-p', '<profile>', 'sh', '-c', 'node script.js']\n * ```\n */\nexport function wrapCommand(command: string, options: WrapCommandOptions): WrappedCommand {\n  switch (options.backend) {\n    case 'seatbelt': {\n      const profile = options.seatbeltProfile ?? generateSeatbeltProfile(options.workspacePath, options.config);\n      return buildSeatbeltCommand(command, profile);\n    }\n\n    case 'bwrap': {\n      return buildBwrapCommand(command, options.workspacePath, options.config);\n    }\n\n    case 'none':\n    default:\n      return { command, args: [] };\n  }\n}\n","/**\n * Local Sandbox Provider\n *\n * A sandbox implementation that executes commands on the local machine.\n * This is the default sandbox for development and local agents.\n *\n * Supports optional native OS sandboxing:\n * - macOS: Uses seatbelt (sandbox-exec) for filesystem and network isolation\n * - Linux: Uses bubblewrap (bwrap) for namespace isolation\n */\n\nimport * as crypto from 'node:crypto';\nimport { realpathSync } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { RequestContext } from '../../request-context';\n\nimport type { WorkspaceFilesystem } from '../filesystem/filesystem';\nimport { expandTilde } from '../filesystem/fs-utils';\nimport type { FilesystemMountConfig, MountResult } from '../filesystem/mount';\nimport type { ProviderStatus } from '../lifecycle';\nimport type { InstructionsOption } from '../types';\nimport { resolveInstructions } from '../utils';\nimport { IsolationUnavailableError } from './errors';\nimport { LocalProcessManager } from './local-process-manager';\nimport { MastraSandbox } from './mastra-sandbox';\nimport type { MastraSandboxOptions } from './mastra-sandbox';\nimport type { MountManager } from './mount-manager';\nimport type { IsolationBackend, NativeSandboxConfig } from './native-sandbox';\nimport { detectIsolation, isIsolationAvailable, generateSeatbeltProfile, wrapCommand } from './native-sandbox';\nimport type { SandboxCloneOptions } from './sandbox';\nimport type { SandboxInfo } from './types';\n\n// =============================================================================\n// Mount Path Validation\n// =============================================================================\n\n/**\n * Directory for mount marker files used to detect config changes across restarts.\n *\n * Resolved lazily so `os.tmpdir()` is never invoked at module-load time. The\n * Agent/evals runtime (which transitively imports this module) is bundled into\n * the Studio client, where `node:os` is shimmed to an empty object and\n * `os.tmpdir` is `undefined`. Evaluating it at import time crashes Studio boot.\n * See https://github.com/mastra-ai/mastra/issues/18519.\n */\nexport function getMarkerDir(): string {\n  return path.join(os.tmpdir(), '.mastra-mounts');\n}\n\n/** Allowlist pattern for mount paths — absolute path with safe characters only. */\nconst SAFE_MOUNT_PATH = /^\\/[a-zA-Z0-9_.\\-/]+$/;\n\nfunction validateMountPath(mountPath: string): void {\n  if (!SAFE_MOUNT_PATH.test(mountPath)) {\n    throw new Error(\n      `Invalid mount path: ${mountPath}. Must be an absolute path with alphanumeric, dash, dot, underscore, or slash characters only.`,\n    );\n  }\n  const segments = mountPath.split('/').filter(Boolean);\n  if (segments.length === 0) {\n    throw new Error(`Invalid mount path: ${mountPath}. Root path \"/\" is not allowed.`);\n  }\n  if (segments.some(seg => seg === '.' || seg === '..')) {\n    throw new Error(`Invalid mount path: ${mountPath}. Path segments cannot be \".\" or \"..\".`);\n  }\n}\n\n/** Canonicalize mount path so `/data`, `/data/`, `//data` all resolve to `/data`. */\nfunction normalizeMountPath(mountPath: string): string {\n  return `/${mountPath.split('/').filter(Boolean).join('/')}`;\n}\n\n// =============================================================================\n// Local Sandbox\n// =============================================================================\n\n/**\n * Local sandbox provider configuration.\n */\nexport interface LocalSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n  /** Unique identifier for this sandbox instance */\n  id?: string;\n  /** Working directory for command execution */\n  workingDirectory?: string;\n  /**\n   * Environment variables to set for command execution.\n   * PATH is included by default unless overridden (needed for finding executables).\n   * Other host environment variables are not inherited unless explicitly passed.\n   *\n   * @example\n   * ```typescript\n   * // Default - only PATH is available\n   * env: undefined\n   *\n   * // Add specific variables\n   * env: { NODE_ENV: 'production', HOME: process.env.HOME }\n   *\n   * // Full host environment (less secure)\n   * env: process.env\n   * ```\n   */\n  env?: NodeJS.ProcessEnv;\n  /** Default timeout for operations in ms (default: 30000) */\n  timeout?: number;\n  /**\n   * Isolation backend for sandboxed execution.\n   * - 'none': No sandboxing (direct execution on host) - default\n   * - 'seatbelt': macOS sandbox-exec (built-in on macOS)\n   * - 'bwrap': Linux bubblewrap (requires installation)\n   *\n   * Use `LocalSandbox.detectIsolation()` to get the recommended backend.\n   * @default 'none'\n   */\n  isolation?: IsolationBackend;\n  /**\n   * Configuration for native sandboxing.\n   * Only used when isolation is 'seatbelt' or 'bwrap'.\n   */\n  nativeSandbox?: NativeSandboxConfig;\n  /**\n   * Custom instructions that override the default instructions\n   * returned by `getInstructions()`.\n   *\n   * - `string` — Fully replaces the default instructions.\n   *   Pass an empty string to suppress instructions entirely.\n   * - `(opts) => string` — Receives the default instructions and\n   *   optional request context so you can extend or customise per-request.\n   */\n  instructions?: InstructionsOption;\n}\n\n/**\n * Local sandbox implementation.\n *\n * Executes commands directly on the host machine.\n * This is the recommended sandbox for development and trusted local execution.\n *\n * @example\n * ```typescript\n * import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core';\n *\n * const workspace = new Workspace({\n *   filesystem: new LocalFilesystem({ basePath: './my-workspace' }),\n *   sandbox: new LocalSandbox({ workingDirectory: './my-workspace' }),\n * });\n *\n * await workspace.init();\n * const result = await workspace.executeCommand('node', ['script.js']);\n * ```\n */\nexport class LocalSandbox extends MastraSandbox {\n  readonly id: string;\n  readonly name = 'LocalSandbox';\n  readonly provider = 'local';\n\n  status: ProviderStatus = 'pending';\n\n  readonly workingDirectory: string;\n  readonly isolation: IsolationBackend;\n  declare readonly processes: LocalProcessManager;\n  declare readonly mounts: MountManager;\n  private readonly env: NodeJS.ProcessEnv;\n  private _nativeSandboxConfig: NativeSandboxConfig;\n  private _seatbeltProfile?: string;\n  private _seatbeltProfilePath?: string;\n  private _sandboxFolderPath?: string;\n  private _userProvidedProfilePath = false;\n  private readonly _createdAt: Date;\n  private readonly _instructionsOverride?: InstructionsOption;\n  private _activeMountPaths: Set<string> = new Set();\n  /** Snapshot of `readWritePaths` from ctor; entries here are never removed on unmount. */\n  private readonly _initialReadWritePaths: Set<string>;\n  /** Refcount for isolation paths added by mounts (not present in `_initialReadWritePaths`). */\n  private _mountIsolationRefCount = new Map<string, number>();\n  /** Normalized mount path → canonical isolation path recorded for that mount. */\n  private _mountPathToIsolationPath = new Map<string, string>();\n\n  constructor(options: LocalSandboxOptions = {}) {\n    // Validate isolation backend before super (fail fast)\n    const requestedIsolation = options.isolation ?? 'none';\n    if (requestedIsolation !== 'none' && !isIsolationAvailable(requestedIsolation)) {\n      const detection = detectIsolation();\n      throw new IsolationUnavailableError(requestedIsolation, detection.message);\n    }\n\n    super({\n      ...options,\n      name: 'LocalSandbox',\n      processes: new LocalProcessManager({ env: options.env ?? {} }),\n    });\n\n    this.id = options.id ?? this.generateId();\n    this._createdAt = new Date();\n    this.workingDirectory = expandTilde(options.workingDirectory ?? path.join(process.cwd(), '.sandbox'));\n    this.env = options.env ?? {};\n    this._nativeSandboxConfig = {\n      ...options.nativeSandbox,\n      readWritePaths: [...(options.nativeSandbox?.readWritePaths ?? [])],\n      readOnlyPaths: [...(options.nativeSandbox?.readOnlyPaths ?? [])],\n    };\n    this._initialReadWritePaths = new Set(this._nativeSandboxConfig.readWritePaths ?? []);\n    this.isolation = requestedIsolation;\n    this._instructionsOverride = options.instructions;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Cloning\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Construct a sibling `LocalSandbox` that inherits this sandbox's\n   * configuration (isolation, native sandbox config, instructions) with\n   * per-instance overrides.\n   *\n   * Performs no I/O — the sandbox clone creates its working directory on its\n   * own `start()`. `sandboxId` and `idleTimeoutMinutes` have no local\n   * equivalent and are ignored: local sandboxes reattach by logical `id` and\n   * have no provider-managed idle teardown.\n   */\n  clone(options: SandboxCloneOptions = {}): LocalSandbox {\n    return new LocalSandbox({\n      ...(options.id !== undefined && { id: options.id }),\n      workingDirectory: options.workingDirectory ?? this.workingDirectory,\n      env: options.env ?? this.env,\n      isolation: this.isolation,\n      nativeSandbox: {\n        ...this._nativeSandboxConfig,\n        readWritePaths: [...this._initialReadWritePaths],\n        readOnlyPaths: [...(this._nativeSandboxConfig.readOnlyPaths ?? [])],\n      },\n      ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n    });\n  }\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Start the local sandbox.\n   * Creates working directory and sets up seatbelt profile if using macOS isolation.\n   * Status management is handled by the base class.\n   */\n  async start(): Promise<void> {\n    this.logger.debug('Starting sandbox', {\n      workingDirectory: this.workingDirectory,\n      isolation: this.isolation,\n    });\n\n    await fs.mkdir(this.workingDirectory, { recursive: true });\n\n    // Set up seatbelt profile for macOS sandboxing\n    if (this.isolation === 'seatbelt') {\n      const userProvidedPath = this._nativeSandboxConfig.seatbeltProfilePath;\n\n      if (userProvidedPath) {\n        // User provided a custom path\n        this._seatbeltProfilePath = userProvidedPath;\n        this._userProvidedProfilePath = true;\n\n        // Check if file exists at user's path\n        try {\n          this._seatbeltProfile = await fs.readFile(userProvidedPath, 'utf-8');\n        } catch (err: unknown) {\n          if (err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n            throw err;\n          }\n          // File doesn't exist, generate default and write to user's path\n          this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);\n          // Ensure parent directory exists\n          await fs.mkdir(path.dirname(userProvidedPath), { recursive: true });\n          await fs.writeFile(userProvidedPath, this._seatbeltProfile, 'utf-8');\n        }\n      } else {\n        // No custom path, use default location\n        this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);\n\n        // Generate a deterministic hash from workspace path and config\n        // This allows identical sandboxes to share profiles while preventing collisions\n        const configHash = crypto\n          .createHash('sha256')\n          .update(this.workingDirectory)\n          .update(JSON.stringify(this._nativeSandboxConfig))\n          .digest('hex')\n          .slice(0, 8);\n\n        // Write profile to .sandbox-profiles/ in cwd (outside working directory)\n        // This prevents sandboxed processes from reading/modifying their own security profile\n        this._sandboxFolderPath = path.join(process.cwd(), '.sandbox-profiles');\n        await fs.mkdir(this._sandboxFolderPath, { recursive: true });\n        this._seatbeltProfilePath = path.join(this._sandboxFolderPath, `seatbelt-${configHash}.sb`);\n        await fs.writeFile(this._seatbeltProfilePath, this._seatbeltProfile, 'utf-8');\n      }\n    }\n\n    this.logger.debug('Sandbox started', { workingDirectory: this.workingDirectory });\n  }\n\n  /**\n   * Stop the local sandbox.\n   * Unmounts all active mounts before stopping.\n   * Status management is handled by the base class.\n   */\n  async stop(): Promise<void> {\n    this.logger.debug('Stopping sandbox', { workingDirectory: this.workingDirectory });\n\n    // Unmount all active mounts (best-effort)\n    for (const mountPath of [...this._activeMountPaths]) {\n      try {\n        await this.unmount(mountPath);\n      } catch {\n        // Best-effort unmount\n      }\n    }\n  }\n\n  /**\n   * Destroy the local sandbox and clean up resources.\n   * Unmounts all filesystems, clears mount state, and cleans up seatbelt profile.\n   * Status management is handled by the base class.\n   */\n  async destroy(): Promise<void> {\n    this.logger.debug('Destroying sandbox', { workingDirectory: this.workingDirectory });\n\n    // Kill all background processes\n    const procs = await this.processes.list();\n    await Promise.all(procs.map(p => this.processes.kill(p.pid)));\n\n    // Unmount all active mounts\n    for (const mountPath of [...this._activeMountPaths]) {\n      try {\n        await this.unmount(mountPath);\n      } catch {\n        // Ignore errors during cleanup\n      }\n    }\n    this._activeMountPaths.clear();\n    this.mounts.clear();\n\n    // Clean up seatbelt profile only if it was auto-generated (not user-provided)\n    if (this._seatbeltProfilePath && !this._userProvidedProfilePath) {\n      try {\n        await fs.unlink(this._seatbeltProfilePath);\n      } catch {\n        // Ignore errors if file doesn't exist\n      }\n    }\n    this._seatbeltProfilePath = undefined;\n    this._seatbeltProfile = undefined;\n    this._userProvidedProfilePath = false;\n\n    // Try to remove .sandbox folder if empty\n    if (this._sandboxFolderPath) {\n      try {\n        await fs.rmdir(this._sandboxFolderPath);\n      } catch {\n        // Ignore errors - folder may not be empty or may not exist\n      }\n      this._sandboxFolderPath = undefined;\n    }\n  }\n\n  /** @deprecated Use `status === 'running'` instead. */\n  async isReady(): Promise<boolean> {\n    return this.status === 'running';\n  }\n\n  async getInfo(): Promise<SandboxInfo> {\n    return {\n      id: this.id,\n      name: this.name,\n      provider: this.provider,\n      status: this.status,\n      createdAt: this._createdAt,\n      resources: {\n        memoryMB: Math.round(os.totalmem() / 1024 / 1024),\n        cpuCores: os.cpus().length,\n      },\n      metadata: {\n        workingDirectory: this.workingDirectory,\n        platform: os.platform(),\n        nodeVersion: process.version,\n        isolation: this.isolation,\n        isolationConfig:\n          this.isolation !== 'none'\n            ? {\n                allowNetwork: this._nativeSandboxConfig.allowNetwork ?? false,\n                readOnlyPaths: this._nativeSandboxConfig.readOnlyPaths,\n                readWritePaths: this._nativeSandboxConfig.readWritePaths,\n              }\n            : undefined,\n      },\n    };\n  }\n\n  getInstructions(opts?: { requestContext?: RequestContext }): string {\n    return resolveInstructions(this._instructionsOverride, () => this._getDefaultInstructions(), opts?.requestContext);\n  }\n\n  private _getDefaultInstructions(): string {\n    return `Local command execution. Working directory: \"${this.workingDirectory}\".`;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Internal Utils\n  // ---------------------------------------------------------------------------\n\n  private generateId(): string {\n    return `local-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n  }\n\n  /**\n   * Build the environment object for execution.\n   * Always includes PATH by default (needed for finding executables).\n   * Merges the sandbox's configured env with any additional env from the command.\n   * @internal Used by LocalProcessManager.\n   */\n  buildEnv(additionalEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n    return {\n      PATH: process.env.PATH, // Always include PATH for finding executables\n      ...this.env,\n      ...additionalEnv,\n    };\n  }\n\n  // ---------------------------------------------------------------------------\n  // Mount Support\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Mount a filesystem at a path on the local host.\n   *\n   * - **local** — Creates a symlink from `<workingDir>/<mount>` to the basePath.\n   *\n   * Virtual mount paths (e.g. `/s3`) are resolved under the sandbox's workingDirectory.\n   * Other mount types can be handled via the `onMount` hook.\n   */\n  async mount(filesystem: WorkspaceFilesystem, mountPath: string): Promise<MountResult> {\n    validateMountPath(mountPath);\n    mountPath = normalizeMountPath(mountPath);\n\n    // Resolve virtual mount path to host filesystem path\n    const hostPath = this.resolveHostPath(mountPath);\n\n    this.logger.debug('Mounting', { mountPath, hostPath });\n\n    // Get mount config\n    const config = filesystem.getMountConfig?.() as FilesystemMountConfig | undefined;\n    if (!config) {\n      const error = `Filesystem \"${filesystem.id}\" does not provide a mount config`;\n      this.logger.error('Filesystem does not provide a mount config', { filesystemId: filesystem.id });\n      this.mounts.set(mountPath, { filesystem, state: 'error', error });\n      return { success: false, mountPath, error };\n    }\n\n    // Check if already mounted with matching config\n    const existingMount = await this.checkExistingMount(hostPath, config);\n    if (existingMount === 'matching') {\n      this.logger.debug('Detected existing mount with correct config, skipping', {\n        provider: filesystem.provider,\n        filesystemId: filesystem.id,\n        hostPath,\n      });\n      this.mounts.set(mountPath, { filesystem, state: 'mounted', config });\n      this._activeMountPaths.add(mountPath);\n      this.addMountPathToIsolation(mountPath, hostPath);\n      return { success: true, mountPath };\n    } else if (existingMount === 'foreign') {\n      // Something is already mounted/symlinked here but we didn't create it — refuse to touch it\n      const error = `Cannot mount at ${hostPath}: path is already occupied by an existing mount or symlink that was not created by Mastra. Unmount it manually or use a different mount path.`;\n      this.logger.error('Mount path occupied by foreign mount or symlink', { hostPath });\n      this.mounts.set(mountPath, { filesystem, state: 'error', config, error });\n      return { success: false, mountPath, error };\n    } else if (existingMount === 'mismatched') {\n      this.logger.debug('Config mismatch on our mount, unmounting to re-mount with new config');\n      await this.unmount(mountPath);\n    }\n\n    this.logger.debug('Mount config type', { type: config.type });\n\n    // Reject unsupported types early — before any filesystem work\n    if (config.type !== 'local') {\n      const error = `Unsupported mount type: ${(config as FilesystemMountConfig).type}`;\n      this.mounts.set(mountPath, { filesystem, state: 'unsupported', config, error });\n      return { success: false, mountPath, error };\n    }\n\n    this.mounts.set(mountPath, { filesystem, state: 'mounting', config });\n\n    // Check if host path exists and would conflict with the symlink\n    try {\n      const entries = await fs.readdir(hostPath);\n      if (entries.length > 0) {\n        const error = `Cannot mount at ${hostPath}: directory exists and is not empty. Mounting would hide existing files. Use a different path or empty the directory first.`;\n        this.logger.error('Cannot mount at non-empty directory', { hostPath });\n        this.mounts.set(mountPath, { filesystem, state: 'error', config, error });\n        return { success: false, mountPath, error };\n      }\n      // Empty directory from a previous failed attempt — remove so symlink can be created\n      await fs.rmdir(hostPath);\n    } catch (err: unknown) {\n      const code = err instanceof Error && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\n      if (code === 'ENOTDIR') {\n        const error = `Cannot mount at ${hostPath}: path is a regular file. Use a different mount path or remove the file first.`;\n        this.logger.error('Cannot mount at path that is a regular file', { hostPath });\n        this.mounts.set(mountPath, { filesystem, state: 'error', config, error });\n        return { success: false, mountPath, error };\n      }\n      // ENOENT: path doesn't exist yet — exactly what we want for symlink creation\n    }\n\n    // Create symlink: ensure parent directory exists, then link\n    const localConfig = config as { type: 'local'; basePath: string };\n    try {\n      await fs.mkdir(path.dirname(hostPath), { recursive: true });\n      await fs.symlink(localConfig.basePath, hostPath);\n      this.logger.debug('Symlinked local mount', { hostPath, basePath: localConfig.basePath });\n    } catch (error) {\n      this.logger.error('Error mounting filesystem', {\n        provider: filesystem.provider,\n        filesystemId: filesystem.id,\n        hostPath,\n        error,\n      });\n      this.mounts.set(mountPath, { filesystem, state: 'error', config, error: String(error) });\n\n      return { success: false, mountPath, error: String(error) };\n    }\n\n    // Mark as mounted\n    this.mounts.set(mountPath, { filesystem, state: 'mounted', config });\n    this._activeMountPaths.add(mountPath);\n\n    // Write marker file\n    await this.writeMarkerFile(mountPath, hostPath);\n\n    // Dynamically add host path to isolation allowlist\n    this.addMountPathToIsolation(mountPath, hostPath);\n\n    this.logger.debug('Mounted', { mountPath, hostPath });\n    return { success: true, mountPath };\n  }\n\n  /**\n   * Unmount a filesystem from a path.\n   */\n  async unmount(mountPath: string): Promise<void> {\n    validateMountPath(mountPath);\n    mountPath = normalizeMountPath(mountPath);\n\n    const hostPath = this.resolveHostPath(mountPath);\n\n    this.logger.debug('Unmounting', { mountPath, hostPath });\n\n    this.removeMountIsolationForPath(mountPath);\n\n    // Check if it's a symlink — symlinks are just unlinked, not FUSE-unmounted\n    let isSymlink = false;\n    try {\n      const stats = await fs.lstat(hostPath);\n      isSymlink = stats.isSymbolicLink();\n    } catch {\n      // Path doesn't exist — proceed with cleanup\n    }\n\n    this.mounts.delete(mountPath);\n    this._activeMountPaths.delete(mountPath);\n\n    // Clean up marker file\n    const filename = this.mounts.markerFilename(hostPath);\n    const markerPath = path.join(getMarkerDir(), filename);\n    try {\n      await fs.unlink(markerPath);\n    } catch {\n      // Ignore if doesn't exist\n    }\n\n    // Remove symlink\n    if (isSymlink) {\n      try {\n        await fs.unlink(hostPath);\n        this.logger.debug('Unmounted and removed symlink', { hostPath });\n      } catch {\n        this.logger.debug('Could not remove symlink', { hostPath });\n      }\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Mount Helpers (private)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Write a marker file for detecting config changes.\n   * Uses hostPath (resolved OS path) for the marker filename and content,\n   * and mountPath (virtual path) for looking up the entry.\n   */\n  private async writeMarkerFile(mountPath: string, hostPath: string): Promise<void> {\n    const entry = this.mounts.get(mountPath);\n    if (!entry?.configHash) return;\n\n    const filename = this.mounts.markerFilename(hostPath);\n    const markerContent = `${hostPath}|${entry.configHash}`;\n    const markerFilePath = path.join(getMarkerDir(), filename);\n\n    try {\n      await fs.mkdir(getMarkerDir(), { recursive: true });\n      await fs.writeFile(markerFilePath, markerContent, 'utf-8');\n    } catch {\n      this.logger.debug('Could not write marker file', { markerFilePath });\n    }\n  }\n\n  /**\n   * Check if a path is already mounted and if the config matches.\n   * Uses hostPath (resolved OS path) for checking the actual mount point.\n   */\n  private async checkExistingMount(\n    hostPath: string,\n    newConfig: FilesystemMountConfig,\n  ): Promise<'not_mounted' | 'matching' | 'mismatched' | 'foreign'> {\n    // Check if it's a symlink (local mount)\n    try {\n      const stats = await fs.lstat(hostPath);\n      if (stats.isSymbolicLink() && newConfig.type === 'local') {\n        // Validate symlink target matches config before checking marker\n        const linkTarget = await fs.readlink(hostPath).catch(() => null);\n        const resolvedTarget = linkTarget ? path.resolve(path.dirname(hostPath), linkTarget) : null;\n        const expectedTarget = path.resolve((newConfig as { type: 'local'; basePath: string }).basePath);\n        if (!resolvedTarget || resolvedTarget !== expectedTarget) {\n          // Symlink exists but points somewhere else — check if we created it\n          return (await this.hasMarkerFile(hostPath)) ? 'mismatched' : 'foreign';\n        }\n        // Symlink target matches — validate via marker file\n        return this.checkMarkerFile(hostPath, newConfig);\n      } else if (stats.isSymbolicLink()) {\n        // Symlink exists for a non-local config — check if we created it\n        return (await this.hasMarkerFile(hostPath)) ? 'mismatched' : 'foreign';\n      }\n    } catch {\n      // Not a symlink or doesn't exist — treat as not mounted\n    }\n    return 'not_mounted';\n  }\n\n  /**\n   * Check if a marker file exists for a given host path (regardless of content).\n   * Returns true if we previously created a mount here.\n   */\n  private async hasMarkerFile(hostPath: string): Promise<boolean> {\n    const filename = this.mounts.markerFilename(hostPath);\n    const markerPath = path.join(getMarkerDir(), filename);\n    try {\n      await fs.access(markerPath);\n      return true;\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * Check if a marker file matches the given config.\n   * Returns 'matching' if hash matches, 'mismatched' if hash differs,\n   * or 'foreign' if no marker exists (we didn't create this mount).\n   */\n  private async checkMarkerFile(\n    hostPath: string,\n    newConfig: FilesystemMountConfig,\n  ): Promise<'matching' | 'mismatched' | 'foreign'> {\n    const filename = this.mounts.markerFilename(hostPath);\n    const markerPath = path.join(getMarkerDir(), filename);\n\n    try {\n      const content = await fs.readFile(markerPath, 'utf-8');\n      const parsed = this.mounts.parseMarkerContent(content.trim());\n\n      if (!parsed) {\n        // Marker exists but is malformed — we created it but can't verify, treat as ours\n        return 'mismatched';\n      }\n\n      const newConfigHash = this.mounts.computeConfigHash(newConfig);\n      this.logger.debug('Marker check', { storedHash: parsed.configHash, newConfigHash });\n\n      if (parsed.path === hostPath && parsed.configHash === newConfigHash) {\n        return 'matching';\n      }\n\n      return 'mismatched';\n    } catch {\n      // No marker file — this mount was not created by us\n      return 'foreign';\n    }\n  }\n\n  /**\n   * Dynamically add a mount path to the sandbox isolation allowlist.\n   *\n   * - Seatbelt: pushes to readWritePaths, regenerates inline profile\n   * - Bwrap: pushes to readWritePaths (buildBwrapCommand reads config each call)\n   *\n   * Local mounts are symlinks under `workingDirectory`. Bubblewrap cannot\n   * `--bind` a symlink (it fails with \"Unable to mount source on destination\"),\n   * so we store the canonical path (`realpath`) of the mount point — the same\n   * directory the symlink refers to.\n   */\n  private addMountPathToIsolation(mountPath: string, hostPath: string): void {\n    if (this.isolation === 'none') return;\n\n    const normMount = normalizeMountPath(mountPath);\n    if (this._mountPathToIsolationPath.has(normMount)) {\n      return;\n    }\n\n    let isolationPath = hostPath;\n    try {\n      isolationPath = realpathSync(hostPath);\n    } catch {\n      // Symlink not visible yet or race; keep literal path for best-effort allowlist\n    }\n\n    if (!this._nativeSandboxConfig.readWritePaths) {\n      this._nativeSandboxConfig = { ...this._nativeSandboxConfig, readWritePaths: [] };\n    }\n    const paths = this._nativeSandboxConfig.readWritePaths!;\n\n    if (!paths.includes(isolationPath)) {\n      paths.push(isolationPath);\n    }\n    if (!this._initialReadWritePaths.has(isolationPath)) {\n      this._mountIsolationRefCount.set(isolationPath, (this._mountIsolationRefCount.get(isolationPath) ?? 0) + 1);\n    }\n    this._mountPathToIsolationPath.set(normMount, isolationPath);\n\n    // Seatbelt: regenerate the inline profile so the next executeCommand() picks it up\n    if (this.isolation === 'seatbelt') {\n      this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);\n    }\n    // Bwrap: buildBwrapCommand reads config.readWritePaths each call, so no extra work needed\n  }\n\n  /**\n   * Reverse {@link addMountPathToIsolation}: drop refcounted paths from the allowlist on unmount\n   * while preserving user-provided `readWritePaths` from construction.\n   */\n  private removeMountIsolationForPath(mountPath: string): void {\n    if (this.isolation === 'none') return;\n\n    const normMount = normalizeMountPath(mountPath);\n    const isolationPath = this._mountPathToIsolationPath.get(normMount);\n    if (isolationPath === undefined) {\n      return;\n    }\n    this._mountPathToIsolationPath.delete(normMount);\n\n    if (this._initialReadWritePaths.has(isolationPath)) {\n      return;\n    }\n\n    const prev = this._mountIsolationRefCount.get(isolationPath) ?? 0;\n    const next = prev - 1;\n    if (next <= 0) {\n      this._mountIsolationRefCount.delete(isolationPath);\n      const paths = this._nativeSandboxConfig.readWritePaths;\n      if (paths) {\n        const idx = paths.indexOf(isolationPath);\n        if (idx !== -1) {\n          paths.splice(idx, 1);\n        }\n      }\n      if (this.isolation === 'seatbelt') {\n        this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);\n      }\n    } else {\n      this._mountIsolationRefCount.set(isolationPath, next);\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Isolation\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Resolve a virtual mount path to a host filesystem path.\n   *\n   * Virtual paths like \"/s3\" become `<workingDir>/s3`. This differs from E2B\n   * where root-level paths like `/s3` are used directly (E2B runs in a VM with sudo).\n   * LocalSandbox runs on the host, so mounts are scoped under workingDirectory.\n   */\n  private resolveHostPath(mountPath: string): string {\n    return path.join(this.workingDirectory, mountPath.replace(/^\\/+/, ''));\n  }\n\n  /**\n   * Wrap a command with the configured isolation backend.\n   * @internal Used by LocalProcessManager for background process isolation.\n   */\n  wrapCommandForIsolation(command: string): { command: string; args: string[] } {\n    if (this.isolation === 'none') {\n      return { command, args: [] };\n    }\n\n    return wrapCommand(command, {\n      backend: this.isolation,\n      workspacePath: this.workingDirectory,\n      seatbeltProfile: this._seatbeltProfile,\n      config: this._nativeSandboxConfig,\n    });\n  }\n\n  /**\n   * Detect the best available isolation backend for this platform.\n   * Returns detection result with backend recommendation and availability.\n   *\n   * @example\n   * ```typescript\n   * const result = LocalSandbox.detectIsolation();\n   * const sandbox = new LocalSandbox({\n   *   isolation: result.available ? result.backend : 'none',\n   * });\n   * ```\n   */\n  static detectIsolation() {\n    return detectIsolation();\n  }\n}\n","/**\n * Line Utilities\n *\n * Utility functions for working with line-based content:\n * - Extract lines by range\n * - Convert character positions to line numbers\n * - Format content with line number prefixes\n */\n\n/**\n * Line range where content was found\n */\nexport interface LineRange {\n  /** Starting line number (1-indexed) */\n  start: number;\n  /** Ending line number (1-indexed, inclusive) */\n  end: number;\n}\n\n/**\n * Extract lines from content by line range.\n *\n * @param content - The document content\n * @param startLine - Starting line number (1-indexed)\n * @param endLine - Ending line number (1-indexed, inclusive)\n * @returns Object with extracted content and metadata\n */\nexport function extractLines(\n  content: string,\n  startLine?: number,\n  endLine?: number,\n): {\n  content: string;\n  lines: { start: number; end: number };\n  totalLines: number;\n} {\n  const allLines = content.split('\\n');\n  const totalLines = allLines.length;\n\n  // Default to full content\n  const start = Math.max(1, startLine ?? 1);\n  const end = Math.min(totalLines, endLine ?? totalLines);\n\n  if (start > end) {\n    return {\n      content: '',\n      lines: { start: 0, end: 0 },\n      totalLines,\n    };\n  }\n\n  // Extract the requested range (convert to 0-indexed)\n  const extractedLines = allLines.slice(start - 1, end);\n\n  return {\n    content: extractedLines.join('\\n'),\n    lines: { start, end },\n    totalLines,\n  };\n}\n\n/**\n * Extract lines using offset/limit style parameters (like Claude Code).\n *\n * @param content - The document content\n * @param offset - Line number to start from (1-indexed, default: 1)\n * @param limit - Maximum number of lines to read (default: all remaining)\n * @returns Object with extracted content and metadata\n */\nexport function extractLinesWithLimit(\n  content: string,\n  offset?: number,\n  limit?: number,\n): {\n  content: string;\n  lines: { start: number; end: number };\n  totalLines: number;\n} {\n  const startLine = offset ?? 1;\n  const endLine = limit ? startLine + limit - 1 : undefined;\n  return extractLines(content, startLine, endLine);\n}\n\n/**\n * Format content with line number prefixes.\n * Output format matches Claude Code: \"     1→content here\"\n *\n * @param content - The content to format\n * @param startLineNumber - The line number of the first line (1-indexed)\n * @returns Formatted content with line numbers\n */\nexport function formatWithLineNumbers(content: string, startLineNumber: number = 1): string {\n  const lines = content.split('\\n');\n  const maxLineNum = startLineNumber + lines.length - 1;\n  const padWidth = Math.max(6, String(maxLineNum).length + 1);\n\n  return lines\n    .map((line, i) => {\n      const lineNum = startLineNumber + i;\n      return `${String(lineNum).padStart(padWidth)}→${line}`;\n    })\n    .join('\\n');\n}\n\n/**\n * Convert a character index to a line number.\n * Useful for converting RAG chunk character offsets to line numbers.\n *\n * @param content - The full document content\n * @param charIndex - The character index (0-indexed)\n * @returns The line number (1-indexed), or undefined if charIndex is out of bounds\n */\nexport function charIndexToLineNumber(content: string, charIndex: number): number | undefined {\n  if (charIndex < 0 || charIndex > content.length) {\n    return undefined;\n  }\n\n  // Count newlines before the character index\n  let lineNumber = 1;\n  for (let i = 0; i < charIndex && i < content.length; i++) {\n    if (content[i] === '\\n') {\n      lineNumber++;\n    }\n  }\n\n  return lineNumber;\n}\n\n/**\n * Convert character range to line range.\n * Useful for converting RAG chunk character offsets to line ranges.\n *\n * @param content - The full document content\n * @param startCharIdx - Start character index (0-indexed)\n * @param endCharIdx - End character index (0-indexed, exclusive)\n * @returns LineRange (1-indexed) or undefined if indices are out of bounds\n */\nexport function charRangeToLineRange(content: string, startCharIdx: number, endCharIdx: number): LineRange | undefined {\n  const startLine = charIndexToLineNumber(content, startCharIdx);\n  // For end, we want the line containing the last character (endCharIdx - 1)\n  const endLine = charIndexToLineNumber(content, Math.max(0, endCharIdx - 1));\n\n  if (startLine === undefined || endLine === undefined) {\n    return undefined;\n  }\n\n  return { start: startLine, end: endLine };\n}\n\n/**\n * Count occurrences of a string in content.\n *\n * @param content - The content to search\n * @param searchString - The string to find\n * @returns Number of occurrences\n */\nexport function countOccurrences(content: string, searchString: string): number {\n  if (!searchString) return 0;\n\n  let count = 0;\n  let position = 0;\n\n  while ((position = content.indexOf(searchString, position)) !== -1) {\n    count++;\n    position += searchString.length;\n  }\n\n  return count;\n}\n\n/**\n * Replace a string in content, with validation for uniqueness.\n *\n * @param content - The content to modify\n * @param oldString - The string to find and replace\n * @param newString - The replacement string\n * @param replaceAll - If true, replace all occurrences; if false, require unique match\n * @returns Object with result content and metadata\n * @throws Error if oldString is not found or not unique (when replaceAll is false)\n */\nexport function replaceString(\n  content: string,\n  oldString: string,\n  newString: string,\n  replaceAll: boolean = false,\n): {\n  content: string;\n  replacements: number;\n} {\n  const count = countOccurrences(content, oldString);\n\n  if (count === 0) {\n    throw new StringNotFoundError(oldString);\n  }\n\n  if (!replaceAll && count > 1) {\n    throw new StringNotUniqueError(oldString, count);\n  }\n\n  // Escape $ in newString to prevent replacement pattern interpretation.\n  // In String.prototype.replace(), $& means \"matched substring\", $$ means literal $, etc.\n  // We want literal replacement, so escape all $ as $$.\n  const escapedNewString = newString.replace(/\\$/g, '$$$$');\n  if (replaceAll) {\n    // Replace all occurrences - split/join doesn't interpret $ patterns\n    const result = content.split(oldString).join(newString);\n    return { content: result, replacements: count };\n  } else {\n    // Replace first (and only) occurrence - use escaped string\n    const result = content.replace(oldString, escapedNewString);\n    return { content: result, replacements: 1 };\n  }\n}\n\n/**\n * Error thrown when string is not found during replacement.\n */\nexport class StringNotFoundError extends Error {\n  constructor(public readonly searchString: string) {\n    super(`The specified text was not found. Make sure you use the exact text from the file.`);\n    this.name = 'StringNotFoundError';\n  }\n}\n\n/**\n * Error thrown when string appears multiple times but unique match required.\n */\nexport class StringNotUniqueError extends Error {\n  constructor(\n    public readonly searchString: string,\n    public readonly occurrences: number,\n  ) {\n    super(\n      `The specified text appears ${occurrences} times. Provide more surrounding context to make the match unique, or use replace_all to replace all occurrences.`,\n    );\n    this.name = 'StringNotUniqueError';\n  }\n}\n","/**\n * BM25 (Best Matching 25) implementation for keyword-based search.\n *\n * BM25 is a probabilistic ranking function used for information retrieval.\n * It ranks documents based on the query terms appearing in each document,\n * taking into account term frequency and document length normalization.\n */\n\nimport type { LineRange } from '../line-utils';\n\n/**\n * BM25 configuration parameters\n */\nexport interface BM25Config {\n  /**\n   * Controls term frequency saturation.\n   * Higher values give more weight to term frequency.\n   * Typical range: 1.2 - 2.0\n   * @default 1.5\n   */\n  k1?: number;\n\n  /**\n   * Controls document length normalization.\n   * 0 = no length normalization, 1 = full normalization\n   * @default 0.75\n   */\n  b?: number;\n}\n\n/**\n * Represents a document in the BM25 index\n */\nexport interface BM25Document {\n  /** Document identifier */\n  id: string;\n  /** Document content */\n  content: string;\n  /** Pre-computed tokens for the document */\n  tokens: string[];\n  /** Token frequency map */\n  termFrequencies: Map<string, number>;\n  /** Total number of tokens */\n  length: number;\n  /** Optional metadata */\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Result from a BM25 search\n */\nexport interface BM25SearchResult {\n  /** Document identifier */\n  id: string;\n  /** Document content */\n  content: string;\n  /** BM25 score (higher is more relevant) */\n  score: number;\n  /** Optional metadata */\n  metadata?: Record<string, unknown>;\n  /** Line range where query terms were found (if computed) */\n  lineRange?: LineRange;\n}\n\n/**\n * Tokenization options\n */\nexport interface TokenizeOptions {\n  /** Convert to lowercase */\n  lowercase?: boolean;\n  /** Remove punctuation */\n  removePunctuation?: boolean;\n  /** Minimum token length */\n  minLength?: number;\n  /** Custom stopwords to remove */\n  stopwords?: Set<string>;\n  /** Custom split pattern (default: /\\s+/) */\n  splitPattern?: RegExp;\n  /**\n   * Custom tokenizer function that bypasses the built-in pipeline entirely.\n   * When provided, all other options (lowercase, removePunctuation, etc.) are ignored.\n   * Useful for CJK languages that need morphological analysis or n-gram tokenization.\n   *\n   * @example\n   * ```ts\n   * // Character bigram tokenizer for CJK\n   * tokenizer: (text) => {\n   *   const tokens: string[] = [];\n   *   const normalized = text.toLowerCase();\n   *   for (let i = 0; i < normalized.length - 1; i++) {\n   *     const bigram = normalized.slice(i, i + 2).trim();\n   *     if (bigram.length === 2) tokens.push(bigram);\n   *   }\n   *   return tokens;\n   * }\n   * ```\n   */\n  tokenizer?: (text: string) => string[];\n}\n\n/**\n * Default English stopwords\n */\nexport const DEFAULT_STOPWORDS = new Set([\n  'a',\n  'an',\n  'and',\n  'are',\n  'as',\n  'at',\n  'be',\n  'by',\n  'for',\n  'from',\n  'has',\n  'he',\n  'in',\n  'is',\n  'it',\n  'its',\n  'of',\n  'on',\n  'or',\n  'that',\n  'the',\n  'to',\n  'was',\n  'were',\n  'will',\n  'with',\n]);\n\n/**\n * Default tokenization options\n */\nconst DEFAULT_TOKENIZE_OPTIONS: Omit<Required<TokenizeOptions>, 'tokenizer'> = {\n  lowercase: true,\n  removePunctuation: true,\n  minLength: 2,\n  stopwords: DEFAULT_STOPWORDS,\n  splitPattern: /\\s+/,\n};\n\n/**\n * Tokenize text into an array of terms\n */\nexport function tokenize(text: string, options: TokenizeOptions = {}): string[] {\n  // If a custom tokenizer is provided, bypass the built-in pipeline entirely\n  if (options.tokenizer) {\n    return options.tokenizer(text);\n  }\n\n  const opts = { ...DEFAULT_TOKENIZE_OPTIONS, ...options };\n\n  let processed = text;\n\n  // Convert to lowercase if enabled\n  if (opts.lowercase) {\n    processed = processed.toLowerCase();\n  }\n\n  // Remove punctuation if enabled — use Unicode-aware pattern to preserve\n  // non-Latin characters (CJK, Arabic, Thai, etc.).  \\p{L} matches any\n  // Unicode letter, \\p{N} any Unicode digit, so the negated class strips\n  // only characters that are neither letters, digits, underscores, nor\n  // whitespace.\n  if (opts.removePunctuation) {\n    processed = processed.replace(/[^\\p{L}\\p{N}_\\s]/gu, ' ');\n  }\n\n  // Split into tokens\n  const tokens = processed.split(opts.splitPattern).filter(token => {\n    // Filter by minimum length\n    if (token.length < opts.minLength) {\n      return false;\n    }\n    // Filter stopwords\n    if (opts.stopwords?.has(token)) {\n      return false;\n    }\n    return true;\n  });\n\n  return tokens;\n}\n\n// Re-export line utilities from line-utils.ts (except findLineRange which is defined here)\nexport {\n  extractLines,\n  extractLinesWithLimit,\n  formatWithLineNumbers,\n  replaceString,\n  StringNotFoundError,\n  StringNotUniqueError,\n} from '../line-utils';\n\n/**\n * Find the line range where query terms appear in content.\n * Returns the range spanning from the first to the last line containing any query term.\n *\n * @param content - The document content\n * @param queryTerms - Tokenized query terms to find\n * @param options - Tokenization options (should match indexing options)\n * @returns LineRange if terms found, undefined otherwise\n */\nexport function findLineRange(\n  content: string,\n  queryTerms: string[],\n  options: TokenizeOptions = {},\n): LineRange | undefined {\n  if (queryTerms.length === 0) return undefined;\n\n  const lines = content.split('\\n');\n\n  // Default tokenize options for matching\n  const defaultOpts = { lowercase: true, removePunctuation: true, minLength: 2 };\n  const opts = { ...defaultOpts, ...options };\n\n  // Normalize query terms for matching\n  const normalizedTerms = new Set(queryTerms.map(t => (opts.lowercase ? t.toLowerCase() : t)));\n\n  let firstMatchLine: number | undefined;\n  let lastMatchLine: number | undefined;\n\n  for (let i = 0; i < lines.length; i++) {\n    const lineTokens = tokenize(lines[i]!, options);\n\n    // Check if any query term appears in this line\n    for (const token of lineTokens) {\n      if (normalizedTerms.has(token)) {\n        const lineNum = i + 1; // 1-indexed\n        if (firstMatchLine === undefined) {\n          firstMatchLine = lineNum;\n        }\n        lastMatchLine = lineNum;\n        break; // Found a match on this line, move to next line\n      }\n    }\n  }\n\n  if (firstMatchLine !== undefined && lastMatchLine !== undefined) {\n    return { start: firstMatchLine, end: lastMatchLine };\n  }\n\n  return undefined;\n}\n\n/**\n * Compute term frequencies for a list of tokens\n */\nfunction computeTermFrequencies(tokens: string[]): Map<string, number> {\n  const frequencies = new Map<string, number>();\n  for (const token of tokens) {\n    frequencies.set(token, (frequencies.get(token) || 0) + 1);\n  }\n  return frequencies;\n}\n\n/**\n * BM25 Index for keyword-based document retrieval\n */\nexport class BM25Index {\n  /** BM25 k1 parameter */\n  readonly k1: number;\n  /** BM25 b parameter */\n  readonly b: number;\n\n  /** Documents in the index */\n  #documents: Map<string, BM25Document> = new Map();\n  /** Inverted index: term -> document IDs containing the term */\n  #invertedIndex: Map<string, Set<string>> = new Map();\n  /** Document frequency: term -> number of documents containing the term */\n  #documentFrequency: Map<string, number> = new Map();\n  /** Average document length */\n  #avgDocLength: number = 0;\n  /** Total number of documents */\n  #docCount: number = 0;\n  /** Tokenization options */\n  #tokenizeOptions: TokenizeOptions;\n\n  constructor(config: BM25Config = {}, tokenizeOptions: TokenizeOptions = {}) {\n    this.k1 = config.k1 ?? 1.5;\n    this.b = config.b ?? 0.75;\n    this.#tokenizeOptions = tokenizeOptions;\n  }\n\n  /**\n   * Add a document to the index\n   */\n  add(id: string, content: string, metadata?: Record<string, unknown>): void {\n    // Remove existing document if it exists\n    if (this.#documents.has(id)) {\n      this.remove(id);\n    }\n\n    const tokens = tokenize(content, this.#tokenizeOptions);\n    const termFrequencies = computeTermFrequencies(tokens);\n\n    const doc: BM25Document = {\n      id,\n      content,\n      tokens,\n      termFrequencies,\n      length: tokens.length,\n      metadata,\n    };\n\n    this.#documents.set(id, doc);\n    this.#docCount++;\n\n    // Update inverted index and document frequency\n    for (const term of termFrequencies.keys()) {\n      if (!this.#invertedIndex.has(term)) {\n        this.#invertedIndex.set(term, new Set());\n      }\n      this.#invertedIndex.get(term)!.add(id);\n      this.#documentFrequency.set(term, (this.#documentFrequency.get(term) || 0) + 1);\n    }\n\n    // Update average document length\n    this.#updateAvgDocLength();\n  }\n\n  /**\n   * Remove a document from the index\n   */\n  remove(id: string): boolean {\n    const doc = this.#documents.get(id);\n    if (!doc) {\n      return false;\n    }\n\n    // Update inverted index and document frequency\n    for (const term of doc.termFrequencies.keys()) {\n      const docIds = this.#invertedIndex.get(term);\n      if (docIds) {\n        docIds.delete(id);\n        if (docIds.size === 0) {\n          this.#invertedIndex.delete(term);\n          this.#documentFrequency.delete(term);\n        } else {\n          this.#documentFrequency.set(term, (this.#documentFrequency.get(term) || 1) - 1);\n        }\n      }\n    }\n\n    this.#documents.delete(id);\n    this.#docCount--;\n\n    // Update average document length\n    this.#updateAvgDocLength();\n\n    return true;\n  }\n\n  /**\n   * Clear all documents from the index\n   */\n  clear(): void {\n    this.#documents.clear();\n    this.#invertedIndex.clear();\n    this.#documentFrequency.clear();\n    this.#docCount = 0;\n    this.#avgDocLength = 0;\n  }\n\n  /**\n   * Search for documents matching the query\n   */\n  search(query: string, topK: number = 10, minScore: number = 0): BM25SearchResult[] {\n    const queryTokens = tokenize(query, this.#tokenizeOptions);\n\n    if (queryTokens.length === 0 || this.#docCount === 0) {\n      return [];\n    }\n\n    const scores = new Map<string, number>();\n\n    // Calculate BM25 scores for each document\n    for (const queryTerm of queryTokens) {\n      const docIds = this.#invertedIndex.get(queryTerm);\n      if (!docIds) {\n        continue;\n      }\n\n      const df = this.#documentFrequency.get(queryTerm) || 0;\n      const idf = this.#computeIDF(df);\n\n      for (const docId of docIds) {\n        const doc = this.#documents.get(docId)!;\n        const tf = doc.termFrequencies.get(queryTerm) || 0;\n        const termScore = this.#computeTermScore(tf, doc.length, idf);\n\n        scores.set(docId, (scores.get(docId) || 0) + termScore);\n      }\n    }\n\n    // Sort by score and return top K results\n    const results: BM25SearchResult[] = [];\n\n    for (const [docId, score] of scores.entries()) {\n      if (score >= minScore) {\n        const doc = this.#documents.get(docId)!;\n        results.push({\n          id: docId,\n          content: doc.content,\n          score,\n          metadata: doc.metadata,\n        });\n      }\n    }\n\n    // Sort by score descending\n    results.sort((a, b) => b.score - a.score);\n\n    return results.slice(0, topK);\n  }\n\n  /**\n   * Get a document by ID\n   */\n  get(id: string): BM25Document | undefined {\n    return this.#documents.get(id);\n  }\n\n  /**\n   * Check if a document exists in the index\n   */\n  has(id: string): boolean {\n    return this.#documents.has(id);\n  }\n\n  /**\n   * Get the number of documents in the index\n   */\n  get size(): number {\n    return this.#docCount;\n  }\n\n  /**\n   * Get all document IDs\n   */\n  get documentIds(): string[] {\n    return Array.from(this.#documents.keys());\n  }\n\n  /**\n   * Serialize the index to a JSON-compatible object\n   */\n  serialize(): BM25IndexData {\n    const documents: SerializedBM25Document[] = [];\n    for (const [id, doc] of this.#documents.entries()) {\n      documents.push({\n        id,\n        content: doc.content,\n        tokens: doc.tokens,\n        termFrequencies: Object.fromEntries(doc.termFrequencies),\n        length: doc.length,\n        metadata: doc.metadata,\n      });\n    }\n\n    return {\n      k1: this.k1,\n      b: this.b,\n      documents,\n      avgDocLength: this.#avgDocLength,\n    };\n  }\n\n  /**\n   * Deserialize an index from a JSON object\n   */\n  static deserialize(data: BM25IndexData, tokenizeOptions: TokenizeOptions = {}): BM25Index {\n    const index = new BM25Index({ k1: data.k1, b: data.b }, tokenizeOptions);\n\n    for (const doc of data.documents) {\n      const termFrequencies = new Map(Object.entries(doc.termFrequencies));\n\n      const document: BM25Document = {\n        id: doc.id,\n        content: doc.content,\n        tokens: doc.tokens,\n        termFrequencies,\n        length: doc.length,\n        metadata: doc.metadata,\n      };\n\n      index.#documents.set(doc.id, document);\n      index.#docCount++;\n\n      // Rebuild inverted index and document frequency\n      for (const term of termFrequencies.keys()) {\n        if (!index.#invertedIndex.has(term)) {\n          index.#invertedIndex.set(term, new Set());\n        }\n        index.#invertedIndex.get(term)!.add(doc.id);\n        index.#documentFrequency.set(term, (index.#documentFrequency.get(term) || 0) + 1);\n      }\n    }\n\n    index.#avgDocLength = data.avgDocLength;\n\n    return index;\n  }\n\n  /**\n   * Update average document length after add/remove operations\n   */\n  #updateAvgDocLength(): void {\n    if (this.#docCount === 0) {\n      this.#avgDocLength = 0;\n      return;\n    }\n\n    let totalLength = 0;\n    for (const doc of this.#documents.values()) {\n      totalLength += doc.length;\n    }\n    this.#avgDocLength = totalLength / this.#docCount;\n  }\n\n  /**\n   * Compute IDF (Inverse Document Frequency) for a term\n   */\n  #computeIDF(df: number): number {\n    // Using Robertson-Spärck Jones IDF formula\n    return Math.log((this.#docCount - df + 0.5) / (df + 0.5) + 1);\n  }\n\n  /**\n   * Compute the BM25 score component for a single term\n   */\n  #computeTermScore(tf: number, docLength: number, idf: number): number {\n    const numerator = tf * (this.k1 + 1);\n    const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.#avgDocLength));\n    return idf * (numerator / denominator);\n  }\n}\n\n/**\n * Serialized document format for persistence\n */\ninterface SerializedBM25Document {\n  id: string;\n  content: string;\n  tokens: string[];\n  termFrequencies: Record<string, number>;\n  length: number;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Serialized index data for persistence\n */\nexport interface BM25IndexData {\n  k1: number;\n  b: number;\n  documents: SerializedBM25Document[];\n  avgDocLength: number;\n}\n","/**\n * SearchEngine - Unified search engine supporting BM25, vector, and hybrid search.\n *\n * Provides search capabilities for Workspace, enabling keyword-based (BM25),\n * semantic (vector), and combined hybrid search across indexed content.\n */\n\nimport pMap from 'p-map';\n\nimport type { MastraVector, VectorFilter } from '../../vector';\nimport type { LineRange } from '../line-utils';\n\nimport { BM25Index, tokenize, findLineRange } from './bm25';\nimport type { BM25Config, TokenizeOptions } from './bm25';\n\n/**\n * Search mode options\n */\nexport type SearchMode = 'vector' | 'bm25' | 'hybrid';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Single-text embedder - takes one text and returns its embedding.\n *\n * This is the legacy embedder shape and remains the default. Each document is\n * embedded with a separate call.\n */\nexport interface SingleEmbedder {\n  (text: string): Promise<number[]>;\n}\n\n/**\n * Batch-capable embedder - takes an array of texts and returns their embeddings\n * in the same order.\n *\n * Branded with `batch: true` so {@link SearchEngine} can detect batch support at\n * runtime and dispatch to a single batched embedder call instead of one call\n * per document. This dramatically speeds up large index rebuilds against\n * providers that support batch embedding (e.g. OpenAI's `embedMany`).\n *\n * @example\n * ```ts\n * import { embedMany } from 'ai';\n * import { openai } from '@ai-sdk/openai';\n *\n * const model = openai.embedding('text-embedding-3-small');\n * const embedder: BatchEmbedder = Object.assign(\n *   async (texts: string[]) => {\n *     const { embeddings } = await embedMany({ model, values: texts });\n *     return embeddings;\n *   },\n *   { batch: true as const, maxBatchSize: 2048 },\n * );\n * ```\n */\nexport interface BatchEmbedder {\n  (texts: string[]): Promise<number[][]>;\n  /** Brand that marks this embedder as batch-capable. */\n  readonly batch: true;\n  /**\n   * Maximum number of texts the underlying provider accepts per call. When\n   * unset, all pending texts are sent in a single request.\n   */\n  readonly maxBatchSize?: number;\n}\n\n/**\n * Embedder interface - either a legacy single-text embedder or a batch-capable\n * embedder branded with `batch: true`.\n */\nexport type Embedder = SingleEmbedder | BatchEmbedder;\n\n/**\n * Type guard: returns true when the embedder is the batch-capable variant.\n */\nexport function isBatchEmbedder(embedder: Embedder): embedder is BatchEmbedder {\n  return typeof embedder === 'function' && (embedder as Partial<BatchEmbedder>).batch === true;\n}\n\n/**\n * Configuration for vector search\n */\nexport interface VectorConfig {\n  /** Vector store for semantic search */\n  vectorStore: MastraVector;\n  /** Embedder function for generating vectors */\n  embedder: Embedder;\n  /** Index name for the vector store */\n  indexName: string;\n}\n\n/**\n * Configuration for BM25 search\n */\nexport interface BM25SearchConfig {\n  /** BM25 algorithm parameters */\n  bm25?: BM25Config;\n  /** Tokenization options */\n  tokenize?: TokenizeOptions;\n}\n\n/**\n * A document to be indexed\n */\nexport interface IndexDocument {\n  /** Unique identifier for this document */\n  id: string;\n  /** Text content to index */\n  content: string;\n  /** Optional metadata to store with the document */\n  metadata?: Record<string, unknown>;\n  /**\n   * For chunked documents: the starting line number of this chunk in the original document.\n   * When provided, lineRange in search results will be adjusted to reflect original document lines.\n   * (1-indexed)\n   */\n  startLineOffset?: number;\n}\n\n/**\n * Base search result with common fields\n */\nexport interface SearchResult {\n  /** Document identifier */\n  id: string;\n  /** Document content */\n  content: string;\n  /** Search score (0-1 for normalized results) */\n  score: number;\n  /** Line range where query terms appear */\n  lineRange?: LineRange;\n  /** Optional metadata */\n  metadata?: Record<string, unknown>;\n  /** Score breakdown by search type */\n  scoreDetails?: {\n    vector?: number;\n    bm25?: number;\n  };\n}\n\n/**\n * Options for searching\n */\nexport interface SearchOptions {\n  /** Maximum number of results to return */\n  topK?: number;\n  /** Minimum score threshold */\n  minScore?: number;\n  /** Search mode: 'bm25', 'vector', or 'hybrid' */\n  mode?: SearchMode;\n  /** Weight for vector scores in hybrid search (0-1, default 0.5) */\n  vectorWeight?: number;\n  /** Filter for vector search */\n  filter?: Record<string, unknown>;\n}\n\n/** Options for batch indexing */\nexport interface IndexManyOptions {\n  /**\n   * Maximum number of documents to index concurrently (embedder + vector upsert).\n   * Must be a safe integer ≥ 1 (same rule as `p-map`).\n   * @default 8\n   */\n  concurrency?: number;\n  /**\n   * When `true` (default), the first rejected `index` rejects the whole `indexMany` call.\n   * When `false`, all documents are processed; if any failed, the promise rejects with an `AggregateError`.\n   */\n  stopOnError?: boolean;\n}\n\n/** Default `indexMany` / lazy-vector flush concurrency (embedder + upsert). */\nconst DEFAULT_INDEX_MANY_CONCURRENCY = 8;\n\n/**\n * Configuration for SearchEngine\n */\nexport interface SearchEngineConfig {\n  /** BM25 configuration (enables BM25 search) */\n  bm25?: BM25SearchConfig;\n  /** Vector configuration (enables vector search) */\n  vector?: VectorConfig;\n  /** Whether to use lazy vector indexing (default: false = eager) */\n  lazyVectorIndex?: boolean;\n}\n\n// =============================================================================\n// Chunking\n// =============================================================================\n\nconst DEFAULT_MAX_CHUNK_CHARS = 4000;\nconst DEFAULT_OVERLAP_LINES = 3;\n\nexport interface ChunkOptions {\n  maxChunkChars?: number;\n  overlapLines?: number;\n}\n\nexport interface TextChunk {\n  content: string;\n  startLine: number;\n}\n\n/**\n * Split text into line-based chunks that stay within a character budget.\n *\n * Each chunk is formed by accumulating whole lines until adding the next line\n * would exceed `maxChunkChars`. Adjacent chunks share `overlapLines` lines so\n * that context around chunk boundaries is preserved for embedding quality.\n *\n * Returns the original text as a single chunk when it already fits.\n */\nexport function splitIntoChunks(text: string, options: ChunkOptions = {}): TextChunk[] {\n  const maxChars = Math.max(1, Math.floor(options.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS));\n  const overlapLines = Math.max(0, Math.floor(options.overlapLines ?? DEFAULT_OVERLAP_LINES));\n\n  if (text.length <= maxChars) {\n    return [{ content: text, startLine: 1 }];\n  }\n\n  const lines = text.split('\\n');\n  const chunks: TextChunk[] = [];\n  let start = 0;\n\n  while (start < lines.length) {\n    let end = start;\n    let charCount = 0;\n\n    while (end < lines.length) {\n      const lineLen = lines[end]!.length + (end > start ? 1 : 0);\n      if (charCount + lineLen > maxChars && end > start) break;\n      charCount += lineLen;\n      end++;\n    }\n\n    const chunkContent = lines.slice(start, end).join('\\n');\n\n    if (chunkContent.length <= maxChars) {\n      chunks.push({ content: chunkContent, startLine: start + 1 });\n    } else {\n      // Single line exceeds maxChars — split by character boundaries.\n      for (let offset = 0; offset < chunkContent.length; offset += maxChars) {\n        chunks.push({\n          content: chunkContent.slice(offset, offset + maxChars),\n          startLine: start + 1,\n        });\n      }\n    }\n\n    const nextStart = end - overlapLines;\n    start = nextStart <= start ? end : nextStart;\n  }\n\n  return chunks;\n}\n\n// =============================================================================\n// SearchEngine\n// =============================================================================\n\n/**\n * Unified search engine supporting BM25, vector, and hybrid search.\n *\n * Used internally by Workspace to provide consistent search functionality.\n *\n * @example\n * ```typescript\n * const engine = new SearchEngine({\n *   bm25: { tokenize: { lowercase: true } },\n *   vector: { vectorStore, embedder, indexName: 'my-index' },\n * });\n *\n * // Index documents\n * await engine.index({ id: 'doc1', content: 'Hello world' });\n *\n * // Search\n * const results = await engine.search('hello', { mode: 'hybrid', topK: 5 });\n * ```\n */\nexport class SearchEngine {\n  /** BM25 index for keyword search */\n  #bm25Index?: BM25Index;\n\n  /** Tokenization options (stored for lineRange computation) */\n  #tokenizeOptions?: TokenizeOptions;\n\n  /** Vector configuration */\n  #vectorConfig?: VectorConfig;\n\n  /** Whether to use lazy vector indexing */\n  #lazyVectorIndex: boolean;\n\n  /** All indexed document IDs (used for prefix-based removal across backends) */\n  #indexedIds: Set<string> = new Set();\n\n  /** Documents pending vector indexing (for lazy mode) */\n  #pendingVectorDocs: IndexDocument[] = [];\n\n  /** Whether vector index has been built (for lazy mode) */\n  #vectorIndexBuilt: boolean = false;\n\n  /** Whether createIndex has been attempted on the vector store */\n  #vectorIndexReady: boolean = false;\n\n  constructor(config: SearchEngineConfig = {}) {\n    // Initialize BM25 if configured\n    if (config.bm25 !== undefined) {\n      this.#tokenizeOptions = config.bm25.tokenize;\n      this.#bm25Index = new BM25Index(config.bm25.bm25, this.#tokenizeOptions);\n    }\n\n    // Store vector config if provided\n    if (config.vector) {\n      this.#vectorConfig = config.vector;\n    }\n\n    this.#lazyVectorIndex = config.lazyVectorIndex ?? false;\n  }\n\n  // ===========================================================================\n  // Public API\n  // ===========================================================================\n\n  /**\n   * Index a document for search\n   */\n  async index(doc: IndexDocument): Promise<void> {\n    // Merge startLineOffset into metadata for retrieval at search time\n    const metadata: Record<string, unknown> = {\n      ...doc.metadata,\n    };\n    if (doc.startLineOffset !== undefined) {\n      metadata._startLineOffset = doc.startLineOffset;\n    }\n\n    this.#indexedIds.add(doc.id);\n\n    // BM25 indexing (always synchronous and immediate)\n    if (this.#bm25Index) {\n      this.#bm25Index.add(doc.id, doc.content, metadata);\n    }\n\n    // Vector indexing\n    if (this.#vectorConfig) {\n      const docWithMergedMetadata = { ...doc, metadata };\n      if (this.#lazyVectorIndex) {\n        // Store for later indexing\n        this.#pendingVectorDocs.push(docWithMergedMetadata);\n        this.#vectorIndexBuilt = false;\n      } else {\n        // Index immediately\n        await this.#indexVector(docWithMergedMetadata);\n      }\n    }\n  }\n\n  /**\n   * Index multiple documents (up to `concurrency` at a time when async vector work runs).\n   *\n   * @param docs - Documents to index\n   * @param options - `p-map` options; `concurrency` defaults to 8\n   */\n  async indexMany(docs: IndexDocument[], options?: IndexManyOptions): Promise<void> {\n    const stopOnError = options?.stopOnError;\n    const concurrency = options?.concurrency ?? DEFAULT_INDEX_MANY_CONCURRENCY;\n    await pMap(docs, doc => this.index(doc), { stopOnError, concurrency });\n  }\n\n  /**\n   * Remove a document from the index\n   */\n  async remove(id: string): Promise<void> {\n    this.#indexedIds.delete(id);\n\n    // Remove from BM25\n    if (this.#bm25Index) {\n      this.#bm25Index.remove(id);\n    }\n\n    // Remove from vector store\n    if (this.#vectorConfig) {\n      try {\n        await this.#vectorConfig.vectorStore.deleteVector({\n          indexName: this.#vectorConfig.indexName,\n          id,\n        });\n      } catch {\n        // Vector may not exist, ignore\n      }\n\n      // Also remove from pending docs if in lazy mode\n      if (this.#lazyVectorIndex) {\n        this.#pendingVectorDocs = this.#pendingVectorDocs.filter(d => d.id !== id);\n      }\n    }\n  }\n\n  /**\n   * Remove all documents whose ID starts with the given prefix.\n   * Used to remove all chunks belonging to a single source document.\n   */\n  async removeByPrefix(prefix: string): Promise<void> {\n    const matchedIds = [...this.#indexedIds].filter(id => id.startsWith(prefix));\n\n    for (const id of matchedIds) {\n      this.#indexedIds.delete(id);\n    }\n\n    if (this.#bm25Index) {\n      for (const id of matchedIds) {\n        this.#bm25Index.remove(id);\n      }\n    }\n\n    if (this.#vectorConfig) {\n      if (this.#lazyVectorIndex) {\n        this.#pendingVectorDocs = this.#pendingVectorDocs.filter(d => !d.id.startsWith(prefix));\n      }\n\n      for (const id of matchedIds) {\n        try {\n          await this.#vectorConfig.vectorStore.deleteVector({\n            indexName: this.#vectorConfig.indexName,\n            id,\n          });\n        } catch {\n          // Vector may not exist, ignore\n        }\n      }\n    }\n  }\n\n  /**\n   * Remove a source document and all of its chunked variants.\n   *\n   * This also attempts a metadata-based bulk delete for chunk vectors so stale\n   * chunk IDs from previous process runs are cleaned up in persistent stores.\n   */\n  async removeSource(sourceId: string): Promise<void> {\n    await this.remove(sourceId);\n    await this.removeByPrefix(`${sourceId}#chunk-`);\n\n    if (this.#vectorConfig) {\n      try {\n        await this.#vectorConfig.vectorStore.deleteVectors({\n          indexName: this.#vectorConfig.indexName,\n          filter: { sourceFile: sourceId } as VectorFilter,\n        });\n      } catch {\n        // Bulk delete/filter may not be supported by all vector backends.\n      }\n    }\n  }\n\n  /**\n   * Clear all indexed documents\n   */\n  clear(): void {\n    this.#indexedIds.clear();\n    if (this.#bm25Index) {\n      this.#bm25Index.clear();\n    }\n    this.#pendingVectorDocs = [];\n    this.#vectorIndexBuilt = false;\n    // Note: We don't clear the vector store here as it may be shared\n  }\n\n  /**\n   * Search for documents\n   */\n  async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {\n    const { topK = 10, minScore, mode, vectorWeight = 0.5, filter } = options;\n\n    const effectiveMode = this.#determineSearchMode(mode);\n\n    if (effectiveMode === 'bm25') {\n      return this.#searchBM25(query, topK, minScore);\n    }\n\n    if (effectiveMode === 'vector') {\n      return this.#searchVector(query, topK, minScore, filter);\n    }\n\n    // Hybrid search\n    return this.#searchHybrid(query, topK, minScore, vectorWeight, filter);\n  }\n\n  /**\n   * Check if BM25 search is available\n   */\n  get canBM25(): boolean {\n    return !!this.#bm25Index;\n  }\n\n  /**\n   * Check if vector search is available\n   */\n  get canVector(): boolean {\n    return !!this.#vectorConfig;\n  }\n\n  /**\n   * Check if hybrid search is available\n   */\n  get canHybrid(): boolean {\n    return this.canBM25 && this.canVector;\n  }\n\n  /**\n   * Get the BM25 index (for serialization/debugging)\n   */\n  get bm25Index(): BM25Index | undefined {\n    return this.#bm25Index;\n  }\n\n  // ===========================================================================\n  // Private Methods\n  // ===========================================================================\n\n  /**\n   * Determine the effective search mode\n   */\n  #determineSearchMode(requestedMode?: SearchMode): SearchMode {\n    if (requestedMode) {\n      if (requestedMode === 'vector' && !this.canVector) {\n        throw new Error('Vector search requires vector configuration.');\n      }\n      if (requestedMode === 'bm25' && !this.canBM25) {\n        throw new Error('BM25 search requires BM25 configuration.');\n      }\n      if (requestedMode === 'hybrid' && !this.canHybrid) {\n        throw new Error('Hybrid search requires both vector and BM25 configuration.');\n      }\n      return requestedMode;\n    }\n\n    // Auto-determine based on available configuration\n    if (this.canHybrid) {\n      return 'hybrid';\n    }\n    if (this.canVector) {\n      return 'vector';\n    }\n    if (this.canBM25) {\n      return 'bm25';\n    }\n\n    throw new Error('No search configuration available. Provide bm25 or vector config.');\n  }\n\n  /**\n   * Embed a single text, dispatching to the batch path with a one-element array\n   * when the configured embedder is batch-capable.\n   */\n  async #embedOne(text: string): Promise<number[]> {\n    if (!this.#vectorConfig) {\n      throw new Error('Vector configuration is required to embed text.');\n    }\n    const { embedder } = this.#vectorConfig;\n    if (isBatchEmbedder(embedder)) {\n      const [embedding] = await embedder([text]);\n      if (!embedding) {\n        throw new Error('Batch embedder returned no embedding for input text.');\n      }\n      return embedding;\n    }\n    return embedder(text);\n  }\n\n  /**\n   * Embed many texts. Uses a single batched call (chunked by `maxBatchSize`)\n   * when the embedder is batch-capable; otherwise falls back to parallel\n   * single-text calls.\n   */\n  async #embedAll(texts: string[]): Promise<number[][]> {\n    if (!this.#vectorConfig) {\n      throw new Error('Vector configuration is required to embed texts.');\n    }\n    if (texts.length === 0) return [];\n\n    const { embedder } = this.#vectorConfig;\n\n    if (isBatchEmbedder(embedder)) {\n      const max = embedder.maxBatchSize;\n      if (max === undefined || texts.length <= max) {\n        return embedder(texts);\n      }\n      // Chunk by maxBatchSize and run chunks in parallel up to DEFAULT_INDEX_MANY_CONCURRENCY.\n      const chunks: string[][] = [];\n      for (let i = 0; i < texts.length; i += max) {\n        chunks.push(texts.slice(i, i + max));\n      }\n      const results = await pMap(chunks, chunk => embedder(chunk), {\n        concurrency: DEFAULT_INDEX_MANY_CONCURRENCY,\n      });\n      return results.flat();\n    }\n\n    return pMap(texts, text => embedder(text), {\n      concurrency: DEFAULT_INDEX_MANY_CONCURRENCY,\n    });\n  }\n\n  /**\n   * Index a single document in the vector store.\n   *\n   * Used by the eager (non-lazy) write path. The lazy flush path embeds all\n   * pending docs together via {@link SearchEngine.#flushVectorBatch} for true\n   * batch embedding when supported.\n   */\n  async #indexVector(doc: IndexDocument): Promise<void> {\n    if (!this.#vectorConfig) return;\n\n    const { vectorStore, indexName } = this.#vectorConfig;\n\n    const embedding = await this.#embedOne(doc.content);\n\n    if (!this.#vectorIndexReady) {\n      // Some backends (e.g. LibSQLVector) require createIndex before upsert.\n      // createIndex is expected to be idempotent; we ignore errors here and let\n      // upsert determine whether the index is actually usable.\n      try {\n        await vectorStore.createIndex({ indexName, dimension: embedding.length });\n      } catch {\n        // Already exists, temporarily unavailable, or not required by backend.\n      }\n    }\n\n    await vectorStore.upsert({\n      indexName,\n      vectors: [embedding],\n      metadata: [\n        {\n          id: doc.id,\n          text: doc.content,\n          ...doc.metadata,\n        },\n      ],\n      ids: [doc.id],\n    });\n\n    // Mark index as ready only after a successful upsert so createIndex is retried\n    // on subsequent writes if the previous attempt did not produce a usable index.\n    this.#vectorIndexReady = true;\n  }\n\n  /**\n   * Embed and upsert a batch of documents in as few provider calls as possible.\n   *\n   * - If the embedder is batch-capable, all texts go through a single embedder\n   *   call (chunked by `maxBatchSize`), then a single `upsert` with all vectors.\n   * - Otherwise falls back to per-doc embedding via {@link SearchEngine.#indexVector}.\n   */\n  async #flushVectorBatch(docs: IndexDocument[]): Promise<void> {\n    if (!this.#vectorConfig || docs.length === 0) return;\n\n    const { vectorStore, embedder, indexName } = this.#vectorConfig;\n\n    if (!isBatchEmbedder(embedder)) {\n      // Single-text embedder: parallelize per-doc work, preserving prior semantics.\n      await pMap(docs, doc => this.#indexVector(doc), {\n        concurrency: DEFAULT_INDEX_MANY_CONCURRENCY,\n      });\n      return;\n    }\n\n    const embeddings = await this.#embedAll(docs.map(d => d.content));\n    if (embeddings.length !== docs.length) {\n      throw new Error(`Batch embedder returned ${embeddings.length} embeddings for ${docs.length} inputs.`);\n    }\n\n    if (!this.#vectorIndexReady) {\n      const dim = embeddings[0]!.length;\n      try {\n        await vectorStore.createIndex({ indexName, dimension: dim });\n      } catch {\n        // Already exists, temporarily unavailable, or not required by backend.\n      }\n    }\n\n    await vectorStore.upsert({\n      indexName,\n      vectors: embeddings,\n      metadata: docs.map(doc => ({\n        id: doc.id,\n        text: doc.content,\n        ...doc.metadata,\n      })),\n      ids: docs.map(d => d.id),\n    });\n\n    this.#vectorIndexReady = true;\n  }\n\n  /**\n   * Collapse duplicate document ids so a single deterministic upsert runs per id (last queue entry wins).\n   */\n  #dedupePendingVectorDocsLastWins(docs: readonly IndexDocument[]): IndexDocument[] {\n    const byId = new Map<string, IndexDocument>();\n    for (const doc of docs) {\n      byId.set(doc.id, doc);\n    }\n    return [...byId.values()];\n  }\n\n  /**\n   * Ensure vector index is built (for lazy mode).\n   *\n   * Drains the pending queue into a local batch before awaiting upserts so concurrent `index()` calls\n   * append to a fresh queue and are not wiped by a blanket clear. Loops until the queue is empty so\n   * documents added mid-flush are indexed before search runs. Re-queues the batch on flush failure.\n   */\n  async #ensureVectorIndex(): Promise<void> {\n    if (!this.#lazyVectorIndex) {\n      return;\n    }\n\n    if (this.#pendingVectorDocs.length === 0) {\n      this.#vectorIndexBuilt = true;\n      return;\n    }\n\n    while (this.#pendingVectorDocs.length > 0) {\n      const batch = this.#pendingVectorDocs;\n      this.#pendingVectorDocs = [];\n\n      const uniqueDocs = this.#dedupePendingVectorDocsLastWins(batch);\n\n      try {\n        await this.#flushVectorBatch(uniqueDocs);\n      } catch (error) {\n        this.#pendingVectorDocs = [...uniqueDocs, ...this.#pendingVectorDocs];\n        throw error;\n      }\n    }\n\n    this.#vectorIndexBuilt = true;\n  }\n\n  /**\n   * BM25 keyword search\n   */\n  #searchBM25(query: string, topK: number, minScore?: number): SearchResult[] {\n    if (!this.#bm25Index) {\n      throw new Error('BM25 search requires BM25 configuration.');\n    }\n\n    const results = this.#bm25Index.search(query, topK, minScore);\n    const queryTokens = tokenize(query, this.#tokenizeOptions);\n\n    return results.map(result => {\n      const rawLineRange = findLineRange(result.content, queryTokens, this.#tokenizeOptions);\n      const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);\n      const { _startLineOffset, ...cleanMetadata } = result.metadata ?? {};\n\n      return {\n        id: result.id,\n        content: result.content,\n        score: result.score,\n        lineRange,\n        metadata: Object.keys(cleanMetadata).length > 0 ? cleanMetadata : undefined,\n        scoreDetails: { bm25: result.score },\n      };\n    });\n  }\n\n  /**\n   * Vector semantic search\n   */\n  async #searchVector(\n    query: string,\n    topK: number,\n    minScore?: number,\n    filter?: Record<string, unknown>,\n  ): Promise<SearchResult[]> {\n    if (!this.#vectorConfig) {\n      throw new Error('Vector search requires vector configuration.');\n    }\n\n    // Ensure lazy index is built\n    await this.#ensureVectorIndex();\n\n    const { vectorStore, indexName } = this.#vectorConfig;\n\n    const queryEmbedding = await this.#embedOne(query);\n\n    const vectorResults = await vectorStore.query({\n      indexName,\n      queryVector: queryEmbedding,\n      topK,\n      filter: filter as VectorFilter,\n    });\n\n    const queryTokens = tokenize(query, this.#tokenizeOptions);\n    const results: SearchResult[] = [];\n\n    for (const result of vectorResults) {\n      if (minScore !== undefined && result.score < minScore) {\n        continue;\n      }\n\n      const id = (result.metadata?.id as string) ?? result.id;\n      const content = (result.metadata?.text as string) ?? '';\n\n      // Extract metadata, excluding internal fields\n      const { id: _id, text: _text, _startLineOffset, ...restMetadata } = result.metadata ?? {};\n\n      const rawLineRange = findLineRange(content, queryTokens, this.#tokenizeOptions);\n      const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);\n\n      results.push({\n        id,\n        content,\n        score: result.score,\n        lineRange,\n        metadata: Object.keys(restMetadata).length > 0 ? restMetadata : undefined,\n        scoreDetails: { vector: result.score },\n      });\n    }\n\n    return results;\n  }\n\n  /**\n   * Hybrid search combining vector and BM25 scores\n   */\n  async #searchHybrid(\n    query: string,\n    topK: number,\n    minScore?: number,\n    vectorWeight: number = 0.5,\n    filter?: Record<string, unknown>,\n  ): Promise<SearchResult[]> {\n    // Get more results than requested to account for merging\n    const expandedTopK = Math.min(topK * 2, 50);\n\n    // Perform both searches in parallel\n    const [vectorResults, bm25Results] = await Promise.all([\n      this.#searchVector(query, expandedTopK, undefined, filter),\n      Promise.resolve(this.#searchBM25(query, expandedTopK, undefined)),\n    ]);\n\n    // Normalize BM25 scores to 0-1 range\n    const normalizedBM25 = this.#normalizeBM25Scores(bm25Results);\n\n    // Create score maps by document id\n    const bm25Map = new Map<string, SearchResult>();\n    for (const result of normalizedBM25) {\n      bm25Map.set(result.id, result);\n    }\n\n    const vectorMap = new Map<string, SearchResult>();\n    for (const result of vectorResults) {\n      vectorMap.set(result.id, result);\n    }\n\n    // Combine scores\n    const combinedResults = new Map<string, SearchResult>();\n    const allIds = new Set([...vectorMap.keys(), ...bm25Map.keys()]);\n    const bm25Weight = 1 - vectorWeight;\n\n    for (const id of allIds) {\n      const vectorResult = vectorMap.get(id);\n      const bm25Result = bm25Map.get(id);\n\n      const vectorScore = vectorResult?.scoreDetails?.vector ?? 0;\n      const bm25Score = bm25Result?.score ?? 0; // Already normalized\n\n      const combinedScore = vectorWeight * vectorScore + bm25Weight * bm25Score;\n\n      // Use data from whichever source has it\n      const baseResult = vectorResult ?? bm25Result!;\n\n      combinedResults.set(id, {\n        id,\n        content: baseResult.content,\n        score: combinedScore,\n        lineRange: bm25Result?.lineRange ?? vectorResult?.lineRange,\n        metadata: baseResult.metadata,\n        scoreDetails: {\n          vector: vectorResult?.scoreDetails?.vector,\n          bm25: bm25Result?.scoreDetails?.bm25,\n        },\n      });\n    }\n\n    // Sort by combined score and apply filters\n    let results = Array.from(combinedResults.values());\n    results.sort((a, b) => b.score - a.score);\n\n    if (minScore !== undefined) {\n      results = results.filter(r => r.score >= minScore);\n    }\n\n    return results.slice(0, topK);\n  }\n\n  /**\n   * Normalize BM25 scores to 0-1 range using min-max normalization\n   */\n  #normalizeBM25Scores(results: SearchResult[]): SearchResult[] {\n    if (results.length === 0) return results;\n\n    const scores = results.map(r => r.scoreDetails?.bm25 ?? r.score);\n    const maxScore = Math.max(...scores);\n    const minScore = Math.min(...scores);\n    const range = maxScore - minScore;\n\n    if (range === 0) {\n      return results.map(r => ({ ...r, score: 1 }));\n    }\n\n    return results.map(r => ({\n      ...r,\n      score: ((r.scoreDetails?.bm25 ?? r.score) - minScore) / range,\n    }));\n  }\n\n  /**\n   * Adjust line range for chunked documents.\n   * If the document has a _startLineOffset in metadata, adjust the line range\n   * to reflect the original document's line numbers.\n   */\n  #adjustLineRange(lineRange: LineRange | undefined, metadata?: Record<string, unknown>): LineRange | undefined {\n    if (!lineRange) return undefined;\n\n    const startLineOffset = metadata?._startLineOffset;\n    if (typeof startLineOffset !== 'number') {\n      return lineRange;\n    }\n\n    // Adjust line numbers: chunk lines are 1-indexed relative to chunk,\n    // offset is 1-indexed relative to original document\n    // So line 1 in chunk with offset 10 becomes line 10 in original\n    return {\n      start: lineRange.start + startLineOffset - 1,\n      end: lineRange.end + startLineOffset - 1,\n    };\n  }\n}\n","import type { BlobStore } from '../../storage/domains/blobs/base';\nimport type { SkillVersionTree } from '../../storage/types';\nimport type { SkillSource, SkillSourceEntry, SkillSourceStat } from './skill-source';\n\n/**\n * Trim leading `.`/`/`/`\\` characters and trailing `/`/`\\` separators from a\n * path. Index scan instead of regex to avoid backtracking (CodeQL\n * js/polynomial-redos).\n */\nfunction trimPathEdges(path: string): string {\n  let start = 0;\n  let end = path.length;\n  while (start < end && (path[start] === '.' || path[start] === '/' || path[start] === '\\\\')) start++;\n  while (end > start && (path[end - 1] === '/' || path[end - 1] === '\\\\')) end--;\n  return path.slice(start, end);\n}\n\n/**\n * A SkillSource implementation that reads skill files from a versioned\n * content-addressable blob store, using a SkillVersionTree manifest.\n *\n * This is used by production agents to read from published skill versions\n * rather than the live filesystem. The SkillVersionTree maps file paths\n * to blob hashes, and the BlobStore provides the actual content.\n */\nexport class VersionedSkillSource implements SkillSource {\n  readonly #tree: SkillVersionTree;\n  readonly #blobStore: BlobStore;\n  readonly #versionCreatedAt: Date;\n\n  /** Computed set of directory paths from the tree entries */\n  readonly #directories: Set<string>;\n\n  constructor(tree: SkillVersionTree, blobStore: BlobStore, versionCreatedAt: Date) {\n    this.#tree = tree;\n    this.#blobStore = blobStore;\n    this.#versionCreatedAt = versionCreatedAt;\n    this.#directories = this.#computeDirectories();\n  }\n\n  /**\n   * Compute all directory paths implied by the file tree.\n   * For a file at \"references/api.md\", this adds \"\" (root), \"references\".\n   */\n  #computeDirectories(): Set<string> {\n    const dirs = new Set<string>();\n    dirs.add(''); // root\n    dirs.add('.'); // root alias\n\n    for (const filePath of Object.keys(this.#tree.entries)) {\n      const parts = filePath.split('/');\n      // Add all parent directories\n      for (let i = 1; i < parts.length; i++) {\n        dirs.add(parts.slice(0, i).join('/'));\n      }\n    }\n    return dirs;\n  }\n\n  /**\n   * Normalize a path by stripping leading/trailing slashes and dots.\n   */\n  #normalizePath(path: string): string {\n    return trimPathEdges(path);\n  }\n\n  async exists(path: string): Promise<boolean> {\n    const normalized = this.#normalizePath(path);\n    // Check if it's a file\n    if (this.#tree.entries[normalized]) return true;\n    // Check if it's a directory\n    return this.#directories.has(normalized);\n  }\n\n  async stat(path: string): Promise<SkillSourceStat> {\n    const normalized = this.#normalizePath(path);\n    const name = normalized.split('/').pop() || normalized || '.';\n\n    // Check if it's a file in the tree\n    const entry = this.#tree.entries[normalized];\n    if (entry) {\n      return {\n        name,\n        type: 'file',\n        size: entry.size,\n        createdAt: this.#versionCreatedAt,\n        modifiedAt: this.#versionCreatedAt,\n        mimeType: entry.mimeType,\n      };\n    }\n\n    // Check if it's a directory\n    if (this.#directories.has(normalized)) {\n      return {\n        name,\n        type: 'directory',\n        size: 0,\n        createdAt: this.#versionCreatedAt,\n        modifiedAt: this.#versionCreatedAt,\n      };\n    }\n\n    throw new Error(`Path not found in skill version tree: ${path}`);\n  }\n\n  async readFile(path: string): Promise<string | Buffer> {\n    const normalized = this.#normalizePath(path);\n    const entry = this.#tree.entries[normalized];\n\n    if (!entry) {\n      throw new Error(`File not found in skill version tree: ${path}`);\n    }\n\n    const blob = await this.#blobStore.get(entry.blobHash);\n    if (!blob) {\n      throw new Error(`Blob not found for hash ${entry.blobHash} (file: ${path})`);\n    }\n\n    // Decode base64-encoded binary content back to Buffer\n    if (entry.encoding === 'base64') {\n      return Buffer.from(blob.content, 'base64');\n    }\n\n    return blob.content;\n  }\n\n  async readdir(path: string): Promise<SkillSourceEntry[]> {\n    const normalized = this.#normalizePath(path);\n\n    if (!this.#directories.has(normalized)) {\n      throw new Error(`Directory not found in skill version tree: ${path}`);\n    }\n\n    const prefix = normalized === '' ? '' : normalized + '/';\n    const seen = new Set<string>();\n    const entries: SkillSourceEntry[] = [];\n\n    for (const filePath of Object.keys(this.#tree.entries)) {\n      if (!filePath.startsWith(prefix)) continue;\n\n      // Get the next segment after the prefix\n      const remaining = filePath.slice(prefix.length);\n      const nextSegment = remaining.split('/')[0];\n      if (!nextSegment || seen.has(nextSegment)) continue;\n      seen.add(nextSegment);\n\n      // If there's more after the next segment, it's a directory\n      const isDirectory = remaining.includes('/');\n      entries.push({\n        name: nextSegment,\n        type: isDirectory ? 'directory' : 'file',\n      });\n    }\n\n    return entries;\n  }\n\n  async realpath(path: string): Promise<string> {\n    return this.#normalizePath(path);\n  }\n}\n","import type { BlobStore } from '../../storage/domains/blobs/base';\nimport type { SkillVersionTree } from '../../storage/types';\nimport type { SkillSource, SkillSourceEntry, SkillSourceStat } from './skill-source';\nimport { VersionedSkillSource } from './versioned-skill-source';\n\n/**\n * A skill entry for the composite source.\n * Each entry represents one skill's versioned tree, mounted under a directory name.\n */\nexport interface VersionedSkillEntry {\n  /** Directory name for this skill (used as the subdirectory under the root) */\n  dirName: string;\n  /** The skill version's file tree manifest */\n  tree: SkillVersionTree;\n  /** When this version was created */\n  versionCreatedAt: Date;\n}\n\n/**\n * A SkillSource that composes multiple versioned skill trees into a virtual directory.\n *\n * Each skill is mounted under a directory name, so the composite source looks like:\n *   /                           (root - virtual)\n *   /brand-guidelines/          (skill 1 root)\n *   /brand-guidelines/SKILL.md  (skill 1 files from blob store)\n *   /tone-of-voice/             (skill 2 root)\n *   /tone-of-voice/SKILL.md     (skill 2 files from blob store)\n *\n * This allows WorkspaceSkillsImpl to discover skills normally by scanning the root\n * for subdirectories containing SKILL.md.\n *\n * Can also include a fallback source for \"live\" skills that read from the filesystem.\n */\nexport class CompositeVersionedSkillSource implements SkillSource {\n  readonly #sources: Map<string, VersionedSkillSource> = new Map();\n  readonly #fallback?: SkillSource;\n  readonly #fallbackSkills: Set<string>;\n  readonly #maxVersionCreatedAt: Date;\n\n  constructor(\n    entries: VersionedSkillEntry[],\n    blobStore: BlobStore,\n    options?: {\n      /** Fallback source for \"live\" skills that read from the filesystem */\n      fallback?: SkillSource;\n      /** Skill directory names that should be served from the fallback source */\n      fallbackSkills?: string[];\n    },\n  ) {\n    let maxTime = 0;\n    for (const entry of entries) {\n      this.#sources.set(entry.dirName, new VersionedSkillSource(entry.tree, blobStore, entry.versionCreatedAt));\n      const t = entry.versionCreatedAt.getTime();\n      if (t > maxTime) maxTime = t;\n    }\n    this.#maxVersionCreatedAt = maxTime > 0 ? new Date(maxTime) : new Date(0);\n    this.#fallback = options?.fallback;\n    this.#fallbackSkills = new Set(options?.fallbackSkills ?? []);\n  }\n\n  #normalizePath(path: string): string {\n    // Strip any leading '.', '/', '\\' and any trailing '/', '\\' without\n    // using a regex to avoid polynomial backtracking on attacker-crafted\n    // paths like many leading slashes or dots.\n    let start = 0;\n    while (start < path.length) {\n      const c = path.charCodeAt(start);\n      if (c === 46 /* '.' */ || c === 47 /* '/' */ || c === 92 /* '\\' */) {\n        start++;\n      } else {\n        break;\n      }\n    }\n    let end = path.length;\n    while (end > start) {\n      const c = path.charCodeAt(end - 1);\n      if (c === 47 /* '/' */ || c === 92 /* '\\' */) {\n        end--;\n      } else {\n        break;\n      }\n    }\n    return start === 0 && end === path.length ? path : path.slice(start, end);\n  }\n\n  /**\n   * Route a path to the correct source.\n   * Returns the source and the remaining path within that source.\n   */\n  #routePath(path: string): { source: SkillSource; subPath: string; mountDir: string } | null {\n    const normalized = this.#normalizePath(path);\n\n    // Root: handled by this source directly\n    if (normalized === '') return null;\n\n    const segments = normalized.split('/');\n    const skillDir = segments[0]!;\n    const subPath = segments.slice(1).join('/');\n\n    // Check if this skill should use the fallback source\n    if (this.#fallbackSkills.has(skillDir) && this.#fallback) {\n      return { source: this.#fallback, subPath: normalized, mountDir: '' };\n    }\n\n    // Check if this skill has a versioned source\n    const versionedSource = this.#sources.get(skillDir);\n    if (versionedSource) {\n      return { source: versionedSource, subPath, mountDir: skillDir };\n    }\n\n    // Try the fallback for unknown paths\n    if (this.#fallback) {\n      return { source: this.#fallback, subPath: normalized, mountDir: '' };\n    }\n\n    return null;\n  }\n\n  async exists(path: string): Promise<boolean> {\n    const normalized = this.#normalizePath(path);\n\n    // Root always exists\n    if (normalized === '') return true;\n\n    const route = this.#routePath(path);\n    if (!route) return false;\n\n    return route.source.exists(route.subPath);\n  }\n\n  async stat(path: string): Promise<SkillSourceStat> {\n    const normalized = this.#normalizePath(path);\n\n    // Root directory\n    if (normalized === '') {\n      return {\n        name: '.',\n        type: 'directory',\n        size: 0,\n        createdAt: this.#maxVersionCreatedAt,\n        modifiedAt: this.#maxVersionCreatedAt,\n      };\n    }\n\n    const route = this.#routePath(path);\n    if (!route) {\n      throw new Error(`Path not found in composite skill source: ${path}`);\n    }\n\n    return route.source.stat(route.subPath);\n  }\n\n  async readFile(path: string): Promise<string | Buffer> {\n    const route = this.#routePath(path);\n    if (!route) {\n      throw new Error(`File not found in composite skill source: ${path}`);\n    }\n\n    return route.source.readFile(route.subPath);\n  }\n\n  async readdir(path: string): Promise<SkillSourceEntry[]> {\n    const normalized = this.#normalizePath(path);\n\n    // Root: list all mounted skill directories\n    if (normalized === '') {\n      const entries: SkillSourceEntry[] = [];\n      const seen = new Set<string>();\n\n      for (const dirName of this.#sources.keys()) {\n        entries.push({ name: dirName, type: 'directory' });\n        seen.add(dirName);\n      }\n\n      // Also list fallback skills\n      for (const dirName of this.#fallbackSkills) {\n        if (!seen.has(dirName)) {\n          entries.push({ name: dirName, type: 'directory' });\n          seen.add(dirName);\n        }\n      }\n\n      return entries;\n    }\n\n    const route = this.#routePath(path);\n    if (!route) {\n      throw new Error(`Directory not found in composite skill source: ${path}`);\n    }\n\n    return route.source.readdir(route.subPath);\n  }\n\n  async realpath(path: string): Promise<string> {\n    const normalized = this.#normalizePath(path);\n    if (normalized === '') return '';\n\n    const route = this.#routePath(path);\n    if (!route) {\n      throw new Error(`Path not found in composite skill source: ${path}`);\n    }\n\n    const realSubPath = route.source.realpath ? await route.source.realpath(route.subPath) : route.subPath;\n    return [route.mountDir, realSubPath].filter(Boolean).join('/');\n  }\n}\n","import { createHash } from 'node:crypto';\nimport matter from 'gray-matter';\nimport type { BlobStore } from '../../storage/domains/blobs/base';\nimport type {\n  SkillVersionTree,\n  SkillVersionTreeEntry,\n  StorageBlobEntry,\n  StorageSkillFileNode,\n  StorageSkillSnapshotType,\n} from '../../storage/types';\nimport type { SkillSource, SkillSourceEntry } from './skill-source';\n\n/**\n * Result of collecting a skill's filesystem tree.\n * Contains the tree manifest, the blob entries to store, and parsed SKILL.md fields.\n */\nexport interface SkillPublishResult {\n  /** Denormalized snapshot fields parsed from SKILL.md frontmatter */\n  snapshot: Omit<StorageSkillSnapshotType, 'tree'>;\n  /** Content-addressable file tree manifest */\n  tree: SkillVersionTree;\n  /** Blob entries to store (already deduplicated by hash) */\n  blobs: StorageBlobEntry[];\n  /** UI-facing nested file tree (folders + files with content) for the stored skill record */\n  files: StorageSkillFileNode[];\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\n/**\n * Compute SHA-256 hex hash of content (string or Buffer).\n */\nfunction hashContent(content: string | Buffer): string {\n  if (Buffer.isBuffer(content)) {\n    return createHash('sha256').update(content).digest('hex');\n  }\n  return createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\n/**\n * Simple extension-based MIME type detection.\n */\nfunction detectMimeType(filename: string): string | undefined {\n  const ext = filename.slice(filename.lastIndexOf('.')).toLowerCase();\n  const mimeTypes: Record<string, string> = {\n    '.md': 'text/markdown',\n    '.txt': 'text/plain',\n    '.json': 'application/json',\n    '.yaml': 'text/yaml',\n    '.yml': 'text/yaml',\n    '.sh': 'text/x-shellscript',\n    '.py': 'text/x-python',\n    '.js': 'text/javascript',\n    '.ts': 'text/typescript',\n    '.html': 'text/html',\n    '.css': 'text/css',\n    '.png': 'image/png',\n    '.jpg': 'image/jpeg',\n    '.jpeg': 'image/jpeg',\n    '.svg': 'image/svg+xml',\n  };\n  return mimeTypes[ext];\n}\n\n/**\n * Whether a MIME type represents binary content that cannot be safely stored as UTF-8 text.\n */\nfunction isBinaryMimeType(mimeType: string | undefined): boolean {\n  if (!mimeType) return false;\n  // Text-based types are safe for UTF-8\n  if (mimeType.startsWith('text/')) return false;\n  // JSON and YAML are text-safe\n  if (mimeType === 'application/json') return false;\n  // SVG is XML-based text\n  if (mimeType === 'image/svg+xml') return false;\n  // Everything else (image/png, image/jpeg, application/octet-stream, etc.) is binary\n  return true;\n}\n\ninterface WalkedFile {\n  path: string;\n  /** Text content (UTF-8) or raw binary content (Buffer) */\n  content: string | Buffer;\n  /** Whether this file is binary */\n  isBinary: boolean;\n}\n\n/**\n * Recursively walk a directory in a SkillSource, returning all files\n * with their relative paths and content. Binary files are returned as Buffers.\n */\nasync function walkSkillDirectory(\n  source: SkillSource,\n  basePath: string,\n  currentPath: string = basePath,\n): Promise<WalkedFile[]> {\n  const entries: SkillSourceEntry[] = await source.readdir(currentPath);\n  const files: WalkedFile[] = [];\n\n  for (const entry of entries) {\n    const entryPath = joinPath(currentPath, entry.name);\n\n    if (entry.type === 'directory') {\n      const subFiles = await walkSkillDirectory(source, basePath, entryPath);\n      files.push(...subFiles);\n    } else {\n      const rawContent = await source.readFile(entryPath);\n      const relativePath = entryPath.substring(basePath.length + 1);\n      const mimeType = detectMimeType(entry.name);\n      const isBinary = isBinaryMimeType(mimeType);\n\n      if (isBinary) {\n        // Keep binary content as Buffer\n        const buf = Buffer.isBuffer(rawContent) ? rawContent : Buffer.from(rawContent, 'utf-8');\n        files.push({ path: relativePath, content: buf, isBinary: true });\n      } else {\n        // Text content as string\n        const content = typeof rawContent === 'string' ? rawContent : rawContent.toString('utf-8');\n        files.push({ path: relativePath, content, isBinary: false });\n      }\n    }\n  }\n\n  return files;\n}\n\n/**\n * Trim slashes from a segment without regex backtracking (CodeQL js/polynomial-redos).\n */\nfunction trimSlashes(segment: string, trimLeading: boolean): string {\n  let start = 0;\n  let end = segment.length;\n  if (trimLeading) {\n    while (start < end && segment[start] === '/') start++;\n  }\n  while (end > start && segment[end - 1] === '/') end--;\n  return segment.slice(start, end);\n}\n\n/**\n * Join path segments using forward slashes.\n */\nfunction joinPath(...segments: string[]): string {\n  return segments\n    .map((seg, i) => trimSlashes(seg, i > 0))\n    .filter(Boolean)\n    .join('/');\n}\n\n/**\n * Collect file paths under a specific subdirectory prefix.\n */\nfunction collectSubdirPaths(allPaths: string[], subdir: string): string[] {\n  const prefix = subdir + '/';\n  return allPaths.filter(p => p.startsWith(prefix)).map(p => p.substring(prefix.length));\n}\n\n/**\n * Build a nested folder/file tree from a flat list of walked files for the\n * UI-facing `files` column on the stored skill record. Binary file content is\n * base64-encoded so it can round-trip through the string-typed `content` field.\n */\nfunction buildSkillFileNodes(files: WalkedFile[]): StorageSkillFileNode[] {\n  const root: StorageSkillFileNode[] = [];\n\n  for (const file of files) {\n    const segments = file.path.split('/').filter(Boolean);\n    if (segments.length === 0) continue;\n\n    let cursor = root;\n    for (let i = 0; i < segments.length - 1; i++) {\n      const segment = segments[i]!;\n      let folder = cursor.find(node => node.type === 'folder' && node.name === segment);\n      if (!folder) {\n        folder = { name: segment, type: 'folder', children: [] };\n        cursor.push(folder);\n      }\n      if (!folder.children) folder.children = [];\n      cursor = folder.children;\n    }\n\n    const fileName = segments[segments.length - 1]!;\n    const content = file.isBinary\n      ? (Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content as string)).toString('base64')\n      : (file.content as string);\n    cursor.push({ name: fileName, type: 'file', content });\n  }\n\n  return root;\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * A flat file entry used by snapshot parsing helpers.\n * Path is the skill-relative path (e.g. `SKILL.md`, `references/foo.md`).\n */\nexport interface SkillSnapshotFile {\n  path: string;\n  content: string | Buffer;\n}\n\n/**\n * Parse a flat array of skill files into a denormalized snapshot.\n *\n * Finds `SKILL.md`, parses its YAML frontmatter into structured fields\n * (name, description, license, compatibility, metadata), and uses the\n * markdown body as `instructions`. Discovers `references/`, `scripts/`,\n * and `assets/` subdirectory paths from the file list.\n *\n * Used by both the publish flow (which has files from a SkillSource walk)\n * and the registry install flow (which has files fetched from an external\n * registry like skills.sh). The Agent Skills spec puts metadata in\n * frontmatter and agent-facing prose in the body — this helper enforces\n * that split so frontmatter never leaks into the runtime instructions.\n *\n * @throws if `SKILL.md` is missing from the file list\n */\nexport function parseSkillSnapshotFromFiles(files: SkillSnapshotFile[]): Omit<StorageSkillSnapshotType, 'tree'> {\n  const skillMdFile = files.find(f => f.path === 'SKILL.md');\n  if (!skillMdFile) {\n    throw new Error('SKILL.md not found in skill files');\n  }\n\n  const skillMdContent =\n    typeof skillMdFile.content === 'string' ? skillMdFile.content : skillMdFile.content.toString('utf-8');\n  const parsed = matter(skillMdContent);\n  const frontmatter = parsed.data;\n  const instructions = parsed.content.trim();\n\n  const allPaths = files.map(f => f.path);\n  const references = collectSubdirPaths(allPaths, 'references');\n  const scripts = collectSubdirPaths(allPaths, 'scripts');\n  const assets = collectSubdirPaths(allPaths, 'assets');\n\n  return {\n    name: frontmatter.name,\n    description: frontmatter.description,\n    instructions,\n    license: frontmatter.license,\n    compatibility: frontmatter.compatibility,\n    metadata: frontmatter.metadata,\n    ...(references.length > 0 ? { references } : {}),\n    ...(scripts.length > 0 ? { scripts } : {}),\n    ...(assets.length > 0 ? { assets } : {}),\n  };\n}\n\n/**\n * Collect a skill from a SkillSource for publishing.\n * Walks the skill directory, hashes all files, parses SKILL.md frontmatter,\n * and returns everything needed to create a new version.\n *\n * @param source - The SkillSource to read from (live filesystem or any other source)\n * @param skillPath - Path to the skill directory (containing SKILL.md)\n */\nexport async function collectSkillForPublish(source: SkillSource, skillPath: string): Promise<SkillPublishResult> {\n  // 1. Walk the skill directory recursively, reading all files\n  const files = await walkSkillDirectory(source, skillPath);\n\n  // 2. Build tree entries and blob entries, deduplicating blobs by hash\n  const treeEntries: Record<string, SkillVersionTreeEntry> = {};\n  const blobMap = new Map<string, StorageBlobEntry>();\n  const now = new Date();\n\n  for (const file of files) {\n    const hash = hashContent(file.content);\n    const mimeType = detectMimeType(file.path);\n\n    if (file.isBinary) {\n      // Binary file: store as base64-encoded string\n      const buf = Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content as string);\n      const size = buf.length;\n      const base64Content = buf.toString('base64');\n\n      treeEntries[file.path] = {\n        blobHash: hash,\n        size,\n        mimeType,\n        encoding: 'base64',\n      };\n\n      if (!blobMap.has(hash)) {\n        blobMap.set(hash, {\n          hash,\n          content: base64Content,\n          size,\n          mimeType,\n          createdAt: now,\n        });\n      }\n    } else {\n      // Text file: store as UTF-8 string\n      const content = file.content as string;\n      const size = Buffer.byteLength(content, 'utf-8');\n\n      treeEntries[file.path] = {\n        blobHash: hash,\n        size,\n        mimeType,\n      };\n\n      if (!blobMap.has(hash)) {\n        blobMap.set(hash, {\n          hash,\n          content,\n          size,\n          mimeType,\n          createdAt: now,\n        });\n      }\n    }\n  }\n\n  const tree: SkillVersionTree = { entries: treeEntries };\n  const blobs = Array.from(blobMap.values());\n  const fileNodes = buildSkillFileNodes(files);\n\n  // 3. Parse SKILL.md frontmatter and discover references/scripts/assets paths\n  let snapshot: Omit<StorageSkillSnapshotType, 'tree'>;\n  try {\n    snapshot = parseSkillSnapshotFromFiles(files);\n  } catch (err) {\n    // Surface the skill path to make the error easier to debug\n    if (err instanceof Error && err.message.includes('SKILL.md not found')) {\n      throw new Error(`SKILL.md not found in ${skillPath}`);\n    }\n    throw err;\n  }\n\n  return { snapshot, tree, blobs, files: fileNodes };\n}\n\n/**\n * Publish a skill: collect files, store blobs, create version.\n * This is the full publish flow.\n *\n * @param source - The SkillSource to read from\n * @param skillPath - Path to the skill directory\n * @param blobStore - Where to store file blobs\n */\nexport async function publishSkillFromSource(\n  source: SkillSource,\n  skillPath: string,\n  blobStore: BlobStore,\n): Promise<SkillPublishResult> {\n  const result = await collectSkillForPublish(source, skillPath);\n  // Store blobs in batch\n  await blobStore.putMany(result.blobs);\n  return result;\n}\n","/**\n * Workspace Tracing Utilities\n *\n * Creates and manages WORKSPACE_ACTION spans for workspace tool operations.\n * Each workspace tool wraps its core operation in a span that captures\n * category, operation name, and operation-specific input/output.\n *\n * Data placement follows span conventions:\n * - `input`: what the operation receives (path, command, query, etc.)\n * - `output`: what the operation produces (results, bytes, exit codes, etc.)\n * - `attributes`: span metadata (category, workspaceId, provider, success)\n */\n\nimport type { AnySpan, WorkspaceActionAttributes } from '../../observability/types/tracing';\nimport { SpanType } from '../../observability/types/tracing';\nimport type { ToolExecutionContext } from '../../tools/types';\nimport type { Workspace } from '../workspace';\n\n/**\n * Options for starting a workspace action span.\n */\nexport interface WorkspaceSpanOptions {\n  /** Action category */\n  category: WorkspaceActionAttributes['category'];\n  /** Operation name (e.g. 'readFile', 'executeCommand') */\n  operation: string;\n  /** Input data to record on the span (path, command, query, etc.) */\n  input?: unknown;\n  /** Initial attributes (workspace metadata, provider info) */\n  attributes?: Partial<Omit<WorkspaceActionAttributes, 'category'>>;\n}\n\n/**\n * Handle returned by startWorkspaceSpan for ending the span.\n */\nexport interface WorkspaceSpanHandle {\n  /** The underlying span (undefined when tracing is not active) */\n  span: AnySpan | undefined;\n  /** End the span with final attributes and output */\n  end(attrs?: Partial<WorkspaceActionAttributes>, output?: unknown): void;\n  /** End the span with an error */\n  error(err: unknown, attrs?: Partial<WorkspaceActionAttributes>): void;\n}\n\nconst ENV_FIELD_NAMES = new Set(['env', 'environment', 'process_env']);\nconst SECRET_FIELD_PATTERN =\n  /(^|[_-])(api[_-]?key|key|token|secret|password|passwd|pwd|credential|credentials|auth|authorization|cookie|session)([_-]|$)/i;\n\nfunction normalizeFieldName(key: string): string {\n  return key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return Object.prototype.toString.call(value) === '[object Object]';\n}\n\nfunction redactEnv(value: unknown) {\n  return {\n    redacted: true,\n    keys: isPlainObject(value) || Array.isArray(value) ? Object.keys(value).sort() : undefined,\n  };\n}\n\nfunction sanitizeWorkspaceTraceData(value: unknown, seen = new WeakSet<object>()): unknown {\n  if (Array.isArray(value)) {\n    if (seen.has(value)) {\n      return '[redacted:circular]';\n    }\n    seen.add(value);\n    try {\n      return value.map(item => sanitizeWorkspaceTraceData(item, seen));\n    } finally {\n      seen.delete(value);\n    }\n  }\n\n  if (!isPlainObject(value)) {\n    return value;\n  }\n\n  if (seen.has(value)) {\n    return '[redacted:circular]';\n  }\n  seen.add(value);\n  try {\n    return Object.fromEntries(\n      Object.entries(value).map(([key, entry]) => {\n        const normalized = normalizeFieldName(key);\n        if (ENV_FIELD_NAMES.has(normalized)) {\n          return [key, redactEnv(entry)];\n        }\n        if (SECRET_FIELD_PATTERN.test(normalized)) {\n          return [key, '[redacted]'];\n        }\n        return [key, sanitizeWorkspaceTraceData(entry, seen)];\n      }),\n    );\n  } finally {\n    seen.delete(value);\n  }\n}\n\n/**\n * Start a WORKSPACE_ACTION child span from the tool execution context.\n *\n * Returns a handle with `end()` and `error()` methods. If no tracing context\n * is available (no parent span), all operations are safe no-ops.\n *\n * @example\n * ```typescript\n * const span = startWorkspaceSpan(context, workspace, {\n *   category: 'filesystem',\n *   operation: 'readFile',\n *   input: { path },\n *   attributes: { filesystemProvider: filesystem.provider },\n * });\n * try {\n *   const result = await filesystem.readFile(path);\n *   span.end({ success: true }, { bytesTransferred: result.length });\n *   return result;\n * } catch (err) {\n *   span.error(err);\n *   throw err;\n * }\n * ```\n */\nexport function startWorkspaceSpan(\n  context: ToolExecutionContext | undefined,\n  workspace: Workspace | undefined,\n  options: WorkspaceSpanOptions,\n): WorkspaceSpanHandle {\n  const currentSpan = context?.tracing?.currentSpan ?? context?.tracingContext?.currentSpan;\n\n  if (!currentSpan) {\n    return noOpHandle;\n  }\n\n  const { category, operation, input, attributes } = options;\n\n  const span = currentSpan.createChildSpan<SpanType.WORKSPACE_ACTION>({\n    type: SpanType.WORKSPACE_ACTION,\n    name: `workspace:${category}:${operation}`,\n    input: sanitizeWorkspaceTraceData(input),\n    attributes: {\n      category,\n      workspaceId: workspace?.id,\n      workspaceName: workspace?.name,\n      ...attributes,\n    },\n  });\n\n  return {\n    span,\n    end(attrs?: Partial<WorkspaceActionAttributes>, output?: unknown) {\n      span?.end({\n        output: sanitizeWorkspaceTraceData(output),\n        attributes: {\n          ...attrs,\n        },\n      });\n    },\n    error(err: unknown, attrs?: Partial<WorkspaceActionAttributes>) {\n      const error = err instanceof Error ? err : new Error(String(err));\n      span?.error({\n        error,\n        attributes: {\n          success: false,\n          ...attrs,\n        },\n      });\n    },\n  };\n}\n\n/** No-op handle when tracing is not available */\nconst noOpHandle: WorkspaceSpanHandle = {\n  span: undefined,\n  end() {},\n  error() {},\n};\n","/**\n * Skill Tools — Factory\n *\n * Creates the built-in skill tools for agents. These tools let the model\n * discover and read skill instructions on demand.\n *\n * Design: stateless. The `skill` tool returns the full skill instructions\n * in its tool result — no activation state tracking needed. Instructions\n * persist naturally in conversation history. If context gets compacted,\n * the model just calls the tool again.\n */\n\nimport { z } from 'zod/v4';\n\nimport { createTool } from '../../tools';\nimport { extractLines } from '../line-utils';\nimport { startWorkspaceSpan } from '../tools/tracing';\nimport type { Skill, WorkspaceSkills } from './types';\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create all skill tools for a workspace with skills.\n * Returns an empty object if the workspace has no skills.\n *\n * Tools are added at the Agent level (like workspace tools), not inside\n * a processor, to avoid losing tool execute functions on serialization.\n */\nexport function createSkillTools(skills: WorkspaceSkills) {\n  return {\n    skill: createSkillTool(skills),\n    skill_search: createSkillSearchTool(skills),\n    skill_read: createSkillReadTool(skills),\n  };\n}\n\n/**\n * Format a skill into the activation payload: instructions followed by\n * any references/scripts/assets listings. Shared between the `skill` tool\n * and explicit user activations so both paths produce identical output.\n */\nexport function formatSkillActivation(skill: Skill): string {\n  const parts = [skill.instructions];\n\n  if (skill.references?.length) {\n    parts.push(`\\n\\n## References\\n${skill.references.map(r => `- references/${r}`).join('\\n')}`);\n  }\n  if (skill.scripts?.length) {\n    parts.push(`\\n\\n## Scripts\\n${skill.scripts.map(s => `- scripts/${s}`).join('\\n')}`);\n  }\n  if (skill.assets?.length) {\n    parts.push(`\\n\\n## Assets\\n${skill.assets.map(a => `- assets/${a}`).join('\\n')}`);\n  }\n\n  return parts.join('');\n}\n\n// =============================================================================\n// Individual Tools\n// =============================================================================\n\n/**\n * Resolve a skill identifier (name or path) to a Skill.\n * The `skills.get()` method handles both name-based lookup (with tie-breaking)\n * and path-based lookup (escape hatch for disambiguation).\n *\n * Calls `maybeRefresh()` first so edits on disk are picked up between tool\n * invocations without restarting the server. Refresh is gated by an internal\n * staleness check + cooldown, so the cost is a small directory `stat` rather\n * than a full re-walk. File-level reloads (SKILL.md content edits) only\n * trigger when the workspace is configured with `checkSkillFileMtime: true`.\n */\nasync function resolveSkill(\n  skills: WorkspaceSkills,\n  identifier: string,\n): Promise<{ skill: Skill } | { notFound: string }> {\n  await skills.maybeRefresh();\n\n  const skill = await skills.get(identifier);\n  if (skill) return { skill };\n\n  const allSkills = await skills.list();\n  const skillEntries = allSkills.map(s => `${s.name} (${s.path})`);\n  return { notFound: `Skill \"${identifier}\" not found. Available skills: ${skillEntries.join(', ')}` };\n}\n\nfunction createSkillTool(skills: WorkspaceSkills) {\n  const tool = createTool({\n    id: 'skill',\n    description:\n      \"Activate a skill to load its full instructions. You should activate skills proactively when they are relevant to the user's request without asking for permission first.\",\n    inputSchema: z.object({\n      name: z\n        .string()\n        .describe('The name or path of the skill to activate. Use the path when multiple skills share the same name.'),\n    }),\n    execute: async ({ name }, context) => {\n      const span = startWorkspaceSpan(context, context?.workspace, {\n        category: 'skill',\n        operation: 'activate',\n        input: { name },\n      });\n\n      try {\n        const result = await resolveSkill(skills, name);\n\n        if ('notFound' in result) {\n          span.end({ success: false });\n          return result.notFound;\n        }\n\n        const { skill } = result;\n        const output = formatSkillActivation(skill);\n\n        span.end({ success: true });\n        return output;\n      } catch (err) {\n        span.error(err);\n        throw err;\n      }\n    },\n  });\n\n  return tool;\n}\n\nfunction createSkillSearchTool(skills: WorkspaceSkills) {\n  const tool = createTool({\n    id: 'skill_search',\n    description:\n      'Search across skill content to find relevant information. Useful when you need to find specific details within skills.',\n    inputSchema: z.object({\n      query: z.string().describe('The search query'),\n      skillNames: z.array(z.string()).optional().describe('Optional list of skill names to search within'),\n      topK: z.number().optional().describe('Maximum number of results to return (default: 5)'),\n    }),\n    execute: async ({ query, skillNames, topK }, context) => {\n      const span = startWorkspaceSpan(context, context?.workspace, {\n        category: 'skill',\n        operation: 'search',\n        input: { query, skillNames, topK },\n        attributes: {},\n      });\n\n      try {\n        await skills.maybeRefresh();\n        const results = await skills.search(query, { topK, skillNames });\n\n        if (results.length === 0) {\n          span.end({ success: true }, { resultCount: 0 });\n          return 'No results found.';\n        }\n\n        span.end({ success: true }, { resultCount: results.length });\n        return results\n          .map(r => {\n            const preview = r.content.substring(0, 200) + (r.content.length > 200 ? '...' : '');\n            const location = r.lineRange ? ` (lines ${r.lineRange.start}-${r.lineRange.end})` : '';\n            return `[${r.skillName}]${location} (score: ${r.score.toFixed(2)})\\n${preview}`;\n          })\n          .join('\\n\\n');\n      } catch (err) {\n        span.error(err);\n        throw err;\n      }\n    },\n  });\n\n  return tool;\n}\n\nfunction createSkillReadTool(skills: WorkspaceSkills) {\n  const tool = createTool({\n    id: 'skill_read',\n    description:\n      'Read a file from a skill directory (references, scripts, or assets). The path is relative to the skill root.',\n    inputSchema: z.object({\n      skillName: z\n        .string()\n        .describe('The name or path of the skill. Use the path when multiple skills share the same name.'),\n      path: z\n        .string()\n        .describe('Path to the file relative to the skill root (e.g. \"references/colors.md\", \"scripts/run.sh\")'),\n      startLine: z\n        .number()\n        .optional()\n        .describe('Starting line number (1-indexed). If omitted, starts from the beginning.'),\n      endLine: z\n        .number()\n        .optional()\n        .describe('Ending line number (1-indexed, inclusive). If omitted, reads to the end.'),\n    }),\n    execute: async ({ skillName, path, startLine, endLine }, context) => {\n      const span = startWorkspaceSpan(context, context?.workspace, {\n        category: 'skill',\n        operation: 'read',\n        input: { skillName, path, startLine, endLine },\n        attributes: {},\n      });\n\n      try {\n        // Resolve skill by name or path (get() handles both with tie-breaking)\n        const resolved = await resolveSkill(skills, skillName);\n        if ('notFound' in resolved) {\n          span.end({ success: false });\n          return resolved.notFound;\n        }\n        const resolvedPath = resolved.skill.path;\n\n        // Try each reader using the resolved path to target the exact skill candidate\n        let content: string | Buffer | null = null;\n        content = await skills.getReference(resolvedPath, path);\n        if (content === null) content = await skills.getScript(resolvedPath, path);\n        if (content === null) content = await skills.getAsset(resolvedPath, path);\n\n        if (content === null) {\n          const refs = (await skills.listReferences(resolvedPath)).map(f => `references/${f}`);\n          const scriptsList = (await skills.listScripts(resolvedPath)).map(f => `scripts/${f}`);\n          const assets = (await skills.listAssets(resolvedPath)).map(f => `assets/${f}`);\n          const allFiles = [...refs, ...scriptsList, ...assets];\n          const fileList = allFiles.length > 0 ? `\\nAvailable files: ${allFiles.join(', ')}` : '';\n          span.end({ success: false });\n          return `File \"${path}\" not found in skill \"${skillName}\".${fileList}`;\n        }\n\n        // Detect binary content — getReference/getScript may return binary as garbled utf-8 strings\n        const textContent = typeof content === 'string' ? content : content.toString('utf-8');\n        if (textContent.slice(0, 1000).includes('\\0')) {\n          const fullPath = `${resolved.skill.path}/${path}`;\n          const size = typeof content === 'string' ? Buffer.byteLength(content) : content.length;\n          span.end({ success: true }, { bytesTransferred: size });\n          return `Binary file: ${fullPath} (${size} bytes)`;\n        }\n        content = textContent;\n\n        const result = extractLines(content, startLine, endLine);\n\n        // An empty range is indistinguishable from a failed read, so the model keeps paginating\n        if (result.lines.start === 0 && result.lines.end === 0) {\n          const reason =\n            (startLine ?? 1) > result.totalLines\n              ? `Requested startLine ${startLine} is past the end of the file. The file has been fully read; stop paginating.`\n              : `Requested range ${startLine}-${endLine} is empty because startLine is greater than endLine.`;\n          span.end({ success: true }, { bytesTransferred: 0 });\n          return `File \"${path}\" has ${result.totalLines} lines (valid range 1-${result.totalLines}). ${reason}`;\n        }\n\n        // Header ranged reads so the model sees EOF coming instead of overshooting to find it\n        const output =\n          startLine !== undefined || endLine !== undefined\n            ? `${path} (lines ${result.lines.start}-${result.lines.end} of ${result.totalLines})\\n${result.content}`\n            : result.content;\n\n        span.end({ success: true }, { bytesTransferred: Buffer.byteLength(result.content, 'utf-8') });\n        return output;\n      } catch (err) {\n        span.error(err);\n        throw err;\n      }\n    },\n  });\n\n  return tool;\n}\n","/**\n * Workspace Class\n *\n * A Workspace combines a Filesystem and a Sandbox to provide agents\n * with a complete environment for storing files and executing code.\n *\n * Users pass provider instances directly to the Workspace constructor.\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core';\n * import { LocalFilesystem } from '@mastra/workspace-fs-local';\n * import { AgentFS } from '@mastra/workspace-fs-agentfs';\n * import { ComputeSDKSandbox } from '@mastra/workspace-sandbox-computesdk';\n *\n * // Simple workspace with local filesystem\n * const workspace = new Workspace({\n *   filesystem: new LocalFilesystem({ basePath: './workspace' }),\n * });\n *\n * // Full workspace with AgentFS and cloud sandbox\n * const fullWorkspace = new Workspace({\n *   filesystem: new AgentFS({ path: './agent.db' }),\n *   sandbox: new ComputeSDKSandbox({ provider: 'e2b' }),\n * });\n *\n * await fullWorkspace.init();\n * await fullWorkspace.filesystem?.writeFile('/code/app.py', 'print(\"Hello!\")');\n * const result = await fullWorkspace.sandbox?.executeCommand?.('python3', ['app.py'], { cwd: '/code' });\n * ```\n */\n\nimport * as path from 'node:path';\nimport pMap, { pMapSkip } from 'p-map';\nimport type { MastraBrowser } from '../browser';\nimport type { IMastraLogger } from '../logger';\nimport { RequestContext } from '../request-context';\nimport type { MastraVector } from '../vector';\n\nimport { WorkspaceError, SearchNotAvailableError } from './errors';\nimport { CompositeFilesystem, LocalFilesystem } from './filesystem';\nimport type { WorkspaceFilesystem, FilesystemInfo } from './filesystem';\nimport { MastraFilesystem } from './filesystem/mastra-filesystem';\nimport { resolvePathPattern } from './glob';\nimport type { ReaddirEntry } from './glob';\nimport { callLifecycle } from './lifecycle';\nimport { findProjectRoot, isLSPAvailable, LSPManager } from './lsp';\nimport type { LSPConfig } from './lsp/types';\nimport type { WorkspaceSandbox, OnMountHook } from './sandbox';\nimport { LocalSandbox } from './sandbox/local-sandbox';\nimport { MastraSandbox } from './sandbox/mastra-sandbox';\nimport type {\n  BM25Config,\n  BM25SearchConfig,\n  TokenizeOptions,\n  Embedder,\n  SearchOptions,\n  SearchResult,\n  IndexDocument,\n} from './search';\nimport { SearchEngine, splitIntoChunks } from './search';\nimport type { WorkspaceSkills, SkillsResolver, SkillSource } from './skills';\nimport { WorkspaceSkillsImpl, LocalSkillSource } from './skills';\nimport type { WorkspaceToolsConfig } from './tools';\nimport type { WorkspaceStatus } from './types';\n\n/** Workspace instructions for a resolver-backed sandbox in `'placeholder'` mode. */\nconst DYNAMIC_SANDBOX_INSTRUCTIONS =\n  'Dynamic sandbox configured. Shell commands execute in a request-scoped sandbox resolved at tool execution time.';\n\n// =============================================================================\n// Workspace Configuration\n// =============================================================================\n\n/**\n * A function that resolves a WorkspaceFilesystem dynamically based on request context.\n * Called on each tool invocation, allowing different filesystems per request.\n */\nexport type WorkspaceFilesystemResolver = (context: {\n  requestContext: RequestContext;\n}) => WorkspaceFilesystem | Promise<WorkspaceFilesystem>;\n\n/**\n * A function that resolves a WorkspaceSandbox dynamically based on request context.\n * Called on each tool invocation, allowing different sandboxes per request.\n *\n * The caller owns the returned sandbox's lifecycle.\n */\nexport type WorkspaceSandboxResolver = (context: {\n  requestContext: RequestContext;\n}) => WorkspaceSandbox | Promise<WorkspaceSandbox>;\n\n/**\n * How a resolver-backed sandbox contributes to workspace instructions:\n * `'placeholder'` (default) emits stable text without calling the resolver,\n * `'resolve'` uses the resolved sandbox's instructions, a function returns\n * custom text from `requestContext` without resolving.\n */\nexport type DynamicSandboxInstructions =\n  | 'placeholder'\n  | 'resolve'\n  | ((context: { requestContext: RequestContext }) => string);\n\n/**\n * Produces a stable cache key (e.g. a thread or tenant id) for a resolver-backed\n * sandbox. Resolved sandboxes are memoized per key instead of per RequestContext\n * instance. Return `undefined` to fall back to per-RequestContext memoization.\n */\nexport type WorkspaceSandboxCacheKey = (context: { requestContext: RequestContext }) => string | undefined;\n\n/**\n * Configuration for creating a Workspace.\n * Users pass provider instances directly.\n *\n * Generic type parameters allow the workspace to preserve the concrete types\n * of filesystem and sandbox providers, so accessors return the exact type\n * you passed in.\n */\nexport interface WorkspaceConfig<\n  TFilesystem extends WorkspaceFilesystem | undefined = WorkspaceFilesystem | undefined,\n  TSandbox extends WorkspaceSandbox | undefined = WorkspaceSandbox | undefined,\n  TMounts extends Record<string, WorkspaceFilesystem> | undefined = undefined,\n> {\n  /** Unique identifier (auto-generated if not provided) */\n  id?: string;\n\n  /** Human-readable name */\n  name?: string;\n\n  /**\n   * Filesystem provider instance, or a resolver function for dynamic per-request filesystems.\n   *\n   * Static: Pass a LocalFilesystem, AgentFS, or any WorkspaceFilesystem instance.\n   * Dynamic: Pass a function `({ requestContext }) => WorkspaceFilesystem` to resolve\n   * a different filesystem per request. The resolver is called at tool execution time.\n   *\n   * Extend MastraFilesystem for automatic logger integration (static instances only).\n   */\n  filesystem?: TFilesystem | WorkspaceFilesystemResolver;\n\n  /**\n   * Sandbox provider instance, or a resolver function for dynamic per-request sandboxes.\n   *\n   * Static: Pass a LocalSandbox, ComputeSDKSandbox, or any WorkspaceSandbox instance.\n   * Dynamic: Pass a function `({ requestContext }) => WorkspaceSandbox` to resolve\n   * a different sandbox per request. The resolver is called at tool execution time.\n   *\n   * When using a resolver, the caller owns the returned sandbox's lifecycle.\n   * Mounts and `lsp: true` are incompatible with a resolver.\n   *\n   * Extend MastraSandbox for automatic logger integration (static instances only).\n   */\n  sandbox?: TSandbox | WorkspaceSandboxResolver;\n\n  /**\n   * Controls how a resolver-backed `sandbox` contributes to workspace instructions.\n   * Defaults to `dynamicSandbox: 'placeholder'`. No effect on a static sandbox.\n   * See {@link DynamicSandboxInstructions}.\n   */\n  instructions?: {\n    dynamicSandbox?: DynamicSandboxInstructions;\n  };\n\n  /**\n   * Stable cache key for a resolver-backed `sandbox`, so background-process tools\n   * reach the same sandbox across requests. No effect on a static sandbox.\n   * See {@link WorkspaceSandboxCacheKey}.\n   */\n  sandboxCacheKey?: WorkspaceSandboxCacheKey;\n\n  /**\n   * Mount multiple filesystems at different paths.\n   * Creates a CompositeFilesystem that routes operations based on path.\n   *\n   * When a sandbox is configured, filesystems are automatically mounted\n   * into the sandbox at their respective paths during init().\n   *\n   * Use the `onMount` hook to skip or customize mounting for specific filesystems.\n   *\n   * The concrete mount types are preserved — use `workspace.filesystem.mounts.get()`\n   * for typed access to individual mounts.\n   *\n   * @example\n   * ```typescript\n   * const workspace = new Workspace({\n   *   sandbox: new E2BSandbox({ timeout: 60000 }),\n   *   mounts: {\n   *     '/data': new S3Filesystem({ bucket: 'my-data', ... }),\n   *     '/skills': new S3Filesystem({ bucket: 'skills', readOnly: true, ... }),\n   *   },\n   * });\n   *\n   * await workspace.init();\n   * workspace.filesystem                    // CompositeFilesystem<{ '/data': S3Filesystem, '/skills': S3Filesystem }>\n   * workspace.filesystem.mounts.get('/data') // S3Filesystem\n   * ```\n   */\n  mounts?: TMounts;\n\n  /**\n   * Hook called before mounting each filesystem into the sandbox.\n   *\n   * Return values:\n   * - `false` - Skip mount entirely (don't mount this filesystem)\n   * - `{ success: true }` - Hook handled the mount successfully\n   * - `{ success: false, error?: string }` - Hook attempted mount but failed\n   * - `undefined` / no return - Use provider's default mount behavior\n   *\n   * This is useful for:\n   * - Skipping specific filesystems (e.g., local filesystems in remote sandbox)\n   * - Custom mount implementations\n   * - Syncing files instead of FUSE mounting\n   *\n   * Note: If your hook handles the mount, you're responsible for the entire\n   * implementation. The sandbox provider won't do any additional tracking.\n   *\n   * @example Skip local filesystems\n   * ```typescript\n   * const workspace = new Workspace({\n   *   sandbox: new E2BSandbox(),\n   *   mounts: {\n   *     '/data': new S3Filesystem({ bucket: 'data', ... }),\n   *     '/local': new LocalFilesystem({ basePath: './data' }),\n   *   },\n   *   onMount: ({ filesystem }) => {\n   *     if (filesystem.provider === 'local') return false;\n   *   },\n   * });\n   * ```\n   *\n   * @example Custom mount implementation\n   * ```typescript\n   * onMount: async ({ filesystem, mountPath, config, sandbox }) => {\n   *   if (config?.type === 's3') {\n   *     await sandbox.executeCommand?.('my-s3-mount', [mountPath]);\n   *     return { success: true };\n   *   }\n   * }\n   * ```\n   */\n  onMount?: OnMountHook;\n\n  // ---------------------------------------------------------------------------\n  // Browser Configuration\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Browser provider for web automation.\n   *\n   * Must be a `MastraBrowser` instance with `providerType: 'cli'` (e.g., `BrowserViewer`).\n   * SDK providers (`AgentBrowser`, `StagehandBrowser`) are not supported here —\n   * use `Agent.browser` for SDK providers.\n   *\n   * The browser is launched via Playwright and exposes a CDP URL that CLI tools\n   * (`agent-browser`, `browser-use`, `browse`) can connect to.\n   *\n   * @example\n   * ```typescript\n   * import { BrowserViewer } from '@mastra/browser-viewer';\n   *\n   * const workspace = new Workspace({\n   *   sandbox: new LocalSandbox({ cwd: './workspace' }),\n   *   browser: new BrowserViewer({\n   *     cli: 'agent-browser',\n   *     headless: false,\n   *   }),\n   * });\n   * ```\n   */\n  browser?: MastraBrowser;\n\n  // ---------------------------------------------------------------------------\n  // Search Configuration\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Vector store for semantic search.\n   * When provided along with embedder, enables vector and hybrid search.\n   */\n  vectorStore?: MastraVector;\n\n  /**\n   * Embedder function for generating vectors.\n   * Required when vectorStore is provided.\n   */\n  embedder?: Embedder;\n\n  /**\n   * Enable BM25 keyword search.\n   * Pass `true` for defaults, a {@link BM25Config} for custom k1/b parameters,\n   * or a `{ bm25?, tokenize? }` object to also customise tokenization.\n   *\n   * The `tokenize` field accepts a {@link TokenizeOptions} object that lets you\n   * tune how text is split into tokens (e.g. for CJK or other non-Latin scripts).\n   *\n   * @example\n   * ```ts\n   * new Workspace({\n   *   bm25: {\n   *     k1: 1.5,\n   *     b: 0.75,\n   *     tokenize: { removePunctuation: false, minLength: 1 },\n   *   },\n   * });\n   * ```\n   */\n  bm25?: boolean | BM25Config | { bm25?: BM25Config; tokenize?: TokenizeOptions };\n\n  /**\n   * Custom index name for the vector store.\n   * If not provided, defaults to a sanitized version of `${id}_search`.\n   *\n   * Must be a valid SQL identifier for SQL-based stores (PgVector, LibSQL):\n   * - Start with a letter or underscore\n   * - Contain only letters, numbers, or underscores\n   * - Maximum 63 characters\n   *\n   * @example 'my_workspace_vectors'\n   */\n  searchIndexName?: string;\n\n  /**\n   * Paths to auto-index on init().\n   * Files in these directories will be indexed for search.\n   * @example ['docs', 'support']\n   */\n  autoIndexPaths?: string[];\n\n  /**\n   * Paths where skills are located.\n   * Workspace will discover SKILL.md files in these directories.\n   *\n   * Can be a static array of paths or a function that returns paths\n   * dynamically based on request context (e.g., user tier, tenant).\n   *\n   * @example Static paths\n   * ```typescript\n   * skills: ['skills', 'node_modules/@myorg/skills']\n   * ```\n   *\n   * @example Dynamic paths\n   * ```typescript\n   * skills: (ctx) => {\n   *   const tier = ctx.requestContext?.get('userTier');\n   *   return tier === 'premium'\n   *     ? ['skills/basic', 'skills/premium']\n   *     : ['skills/basic'];\n   * }\n   * ```\n   */\n  skills?: SkillsResolver;\n\n  /**\n   * Custom SkillSource to use for skill discovery.\n   * When provided, this source is used instead of the workspace filesystem or LocalSkillSource.\n   *\n   * Use `VersionedSkillSource` to read skills from the content-addressable blob store,\n   * serving a specific published version without touching the live filesystem.\n   *\n   * @example\n   * ```typescript\n   * import { VersionedSkillSource } from '@mastra/core/workspace';\n   *\n   * const workspace = new Workspace({\n   *   skills: ['skills'],\n   *   skillSource: new VersionedSkillSource(tree, blobStore, versionCreatedAt),\n   * });\n   * ```\n   */\n  skillSource?: SkillSource;\n\n  /**\n   * Check SKILL.md file mtime in addition to directory mtime for staleness detection.\n   *\n   * When enabled, allows hot-reload detection of in-place SKILL.md edits\n   * (e.g., fixing a validation error or updating a skill description).\n   *\n   * Trade-off: This doubles the stat() calls per skill during staleness checks.\n   * Recommended for local development only. Not recommended for cloud storage\n   * backends (S3, etc.) where stat() calls have higher latency.\n   *\n   * @default false\n   */\n  checkSkillFileMtime?: boolean;\n\n  // ---------------------------------------------------------------------------\n  // LSP Configuration\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Enable LSP diagnostics for edit tools.\n   *\n   * When enabled, edit tools (edit_file, write_file, ast_edit) will append\n   * type errors, warnings, and other diagnostics from language servers after edits.\n   *\n   * LSP requires a sandbox with a process manager (`sandbox.processes`) to spawn\n   * language server processes. It works with any sandbox backend (local, E2B, etc.).\n   *\n   * Requires optional peer dependencies: `vscode-jsonrpc`, `vscode-languageserver-protocol`,\n   * and the relevant language server (e.g. `typescript-language-server` for TypeScript).\n   *\n   * - `true` — Enable with defaults\n   * - `LSPConfig` object — Enable with custom timeouts/settings\n   *\n   * @default undefined (disabled)\n   */\n  lsp?: boolean | LSPConfig;\n\n  // ---------------------------------------------------------------------------\n  // Tool Configuration\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Per-tool configuration for workspace tools.\n   * Controls which tools are enabled and their safety settings.\n   *\n   * This replaces the provider-level `requireApproval` and `requireReadBeforeWrite`\n   * settings, allowing more granular control per tool.\n   *\n   * @example\n   * ```typescript\n   * tools: {\n   *   mastra_workspace_read_file: {\n   *     enabled: true,\n   *     requireApproval: false,\n   *   },\n   *   mastra_workspace_write_file: {\n   *     enabled: true,\n   *     requireApproval: true,\n   *     requireReadBeforeWrite: true,\n   *   },\n   *   mastra_workspace_execute_command: {\n   *     enabled: true,\n   *     requireApproval: true,\n   *   },\n   * }\n   * ```\n   */\n  tools?: WorkspaceToolsConfig;\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle Options\n  // ---------------------------------------------------------------------------\n\n  /** Auto-sync between fs and sandbox (default: false) */\n  autoSync?: boolean;\n\n  /** Timeout for individual operations in milliseconds */\n  operationTimeout?: number;\n}\n\n// Re-export WorkspaceStatus from types\nexport type { WorkspaceStatus } from './types';\n\n/**\n * A Workspace with any combination of filesystem, sandbox, and mounts.\n * Use this when you need to accept any Workspace regardless of its generic parameters.\n */\nexport type AnyWorkspace = Workspace<WorkspaceFilesystem | undefined, WorkspaceSandbox | undefined, any>;\n\n/** A workspace entry in the Mastra registry, enriched with source metadata. */\nexport interface RegisteredWorkspace {\n  workspace: Workspace;\n  source: 'mastra' | 'agent';\n  agentId?: string;\n  agentName?: string;\n}\n\n// =============================================================================\n// Path Context Types\n// =============================================================================\n\n/**\n * Information about how filesystem and sandbox paths relate.\n * Used by agents to understand how to access workspace files from sandbox code.\n */\nexport interface PathContext {\n  /** Filesystem details (if available) */\n  filesystem?: {\n    provider: string;\n    /** Absolute base path on disk (for local filesystems) */\n    basePath?: string;\n  };\n\n  /** Sandbox details (if available) */\n  sandbox?: {\n    provider: string;\n    /** Working directory for command execution */\n    workingDirectory?: string;\n  };\n\n  /**\n   * Human-readable instructions for how to access filesystem files from sandbox code.\n   * Combined from filesystem and sandbox provider instructions.\n   */\n  instructions: string;\n}\n\nexport interface WorkspaceInfo {\n  id: string;\n  name: string;\n  status: WorkspaceStatus;\n  createdAt: Date;\n  lastAccessedAt: Date;\n\n  /** Filesystem info (if available) */\n  filesystem?: FilesystemInfo & {\n    totalFiles?: number;\n    totalSize?: number;\n  };\n\n  /** Sandbox info (if available) */\n  sandbox?: {\n    provider: string;\n    status: string;\n    resources?: {\n      memoryMB?: number;\n      memoryUsedMB?: number;\n      cpuCores?: number;\n      cpuPercent?: number;\n      diskMB?: number;\n      diskUsedMB?: number;\n    };\n  };\n}\n\n/**\n * Maximum concurrent `readFile` calls when batch-loading files for search auto-indexing\n * (`batchReadFiles`).\n */\nconst FS_READ_CONCURRENCY = 8;\n\n/**\n * Parse the user-facing `bm25` config union into the `BM25SearchConfig` shape\n * that `SearchEngine` expects.\n */\nfunction parseBM25Config(\n  bm25: boolean | BM25Config | { bm25?: BM25Config; tokenize?: TokenizeOptions },\n): BM25SearchConfig {\n  if (typeof bm25 === 'boolean') return {};\n  if ('bm25' in bm25 || 'tokenize' in bm25) {\n    return {\n      bm25: (bm25 as { bm25?: BM25Config }).bm25,\n      tokenize: (bm25 as { tokenize?: TokenizeOptions }).tokenize,\n    };\n  }\n  return { bm25: bm25 as BM25Config };\n}\n\n// =============================================================================\n// Workspace Class\n// =============================================================================\n\n/**\n * Workspace provides agents with filesystem and execution capabilities.\n *\n * At minimum, a workspace has either a filesystem or a sandbox (or both).\n * Users pass instantiated provider objects to the constructor.\n */\nexport class Workspace<\n  TFilesystem extends WorkspaceFilesystem | undefined = WorkspaceFilesystem | undefined,\n  TSandbox extends WorkspaceSandbox | undefined = WorkspaceSandbox | undefined,\n  TMounts extends Record<string, WorkspaceFilesystem> | undefined = undefined,\n> {\n  readonly id: string;\n  readonly name: string;\n  readonly createdAt: Date;\n  lastAccessedAt: Date;\n\n  private _status: WorkspaceStatus = 'pending';\n  private _destroyPromise?: Promise<void>;\n  private readonly _fs?: WorkspaceFilesystem;\n  private readonly _filesystemResolver?: WorkspaceFilesystemResolver;\n  private readonly _sandbox?: WorkspaceSandbox;\n  private readonly _sandboxResolver?: WorkspaceSandboxResolver;\n  // Per-request memoization so one resolver call serves both instructions and tool execution.\n  private readonly _filesystemRequestCache = new WeakMap<RequestContext, Promise<WorkspaceFilesystem>>();\n  private readonly _sandboxRequestCache = new WeakMap<RequestContext, Promise<WorkspaceSandbox>>();\n  // Resolver memoization keyed by sandboxCacheKey (survives RequestContext churn).\n  private readonly _sandboxKeyCache = new Map<string, Promise<WorkspaceSandbox>>();\n  private readonly _sandboxCacheKey?: WorkspaceSandboxCacheKey;\n  private readonly _dynamicSandboxInstructions: DynamicSandboxInstructions;\n  private readonly _browser?: MastraBrowser;\n  private readonly _config: WorkspaceConfig<TFilesystem, TSandbox, TMounts>;\n  private readonly _searchEngine?: SearchEngine;\n  private _skills?: WorkspaceSkills;\n  private _lsp?: LSPManager;\n  private _logger?: IMastraLogger;\n\n  constructor(config: WorkspaceConfig<TFilesystem, TSandbox, TMounts>) {\n    this.id = config.id ?? this.generateId();\n    this.name = config.name ?? `workspace-${this.id.slice(0, 8)}`;\n    this.createdAt = new Date();\n    this.lastAccessedAt = new Date();\n\n    this._config = config;\n\n    if (typeof config.sandbox === 'function') {\n      this._sandboxResolver = config.sandbox as WorkspaceSandboxResolver;\n    } else {\n      this._sandbox = config.sandbox;\n    }\n    this._sandboxCacheKey = config.sandboxCacheKey;\n    this._dynamicSandboxInstructions = config.instructions?.dynamicSandbox ?? 'placeholder';\n\n    // Setup mounts - creates CompositeFilesystem and informs sandbox\n    if (config.mounts && Object.keys(config.mounts).length > 0) {\n      // Validate: can't use both filesystem and mounts\n      if (config.filesystem) {\n        throw new WorkspaceError('Cannot use both \"filesystem\" and \"mounts\"', 'INVALID_CONFIG');\n      }\n      if (this._sandboxResolver) {\n        throw new WorkspaceError(\n          'Cannot use \"mounts\" with a dynamic sandbox resolver. ' +\n            'Mounts are attached to a sandbox instance at construction time. ' +\n            'Either pass a static sandbox instance, or have your resolver return a sandbox with its mounts already configured.',\n          'INVALID_CONFIG',\n        );\n      }\n\n      // Warn: contained: false is incompatible with mounts\n      for (const [mountPath, fs] of Object.entries(config.mounts)) {\n        if (fs instanceof LocalFilesystem && !fs.contained) {\n          console.warn(\n            `[Workspace] LocalFilesystem at mount \"${mountPath}\" has contained: false, which is incompatible with mounts. ` +\n              `CompositeFilesystem strips mount prefixes and produces absolute paths (e.g. \"/file.txt\"), ` +\n              `which a non-contained LocalFilesystem interprets as real host paths instead of paths ` +\n              `relative to basePath. Use contained: true (default) or allowedPaths for specific exceptions.`,\n          );\n        }\n      }\n\n      this._fs = new CompositeFilesystem({ mounts: config.mounts });\n      if (this._sandbox?.mounts) {\n        // Inform sandbox about mounts so it can process them on start()\n        this._sandbox.mounts.setContext({ sandbox: this._sandbox, workspace: this as unknown as Workspace });\n        this._sandbox.mounts.add(config.mounts);\n        if (config.onMount) {\n          this._sandbox.mounts.setOnMount(config.onMount);\n        }\n      }\n    } else if (typeof config.filesystem === 'function') {\n      // Reject class constructors — a common mistake is passing the class itself instead of an instance\n      if (/^class\\s/.test(Function.prototype.toString.call(config.filesystem))) {\n        throw new WorkspaceError(\n          'filesystem received a class constructor instead of an instance or resolver function. ' +\n            'Pass an instance (e.g., new LocalFilesystem(...)) or a resolver function (({ requestContext }) => fs).',\n          'INVALID_CONFIG',\n        );\n      }\n      // Dynamic filesystem resolver — stored separately, no static _fs instance\n      this._filesystemResolver = config.filesystem as WorkspaceFilesystemResolver;\n    } else {\n      this._fs = config.filesystem;\n    }\n\n    // Validate and store browser provider\n    if (config.browser) {\n      if (config.browser.providerType !== 'cli') {\n        throw new WorkspaceError(\n          `Workspace.browser requires a CLI provider (providerType: 'cli'), but got '${config.browser.providerType}'. ` +\n            `SDK providers should be used with Agent.browser instead.`,\n          'INVALID_CONFIG',\n          this.id,\n        );\n      }\n      this._browser = config.browser;\n    }\n\n    // Validate vector search config - embedder is required with vectorStore\n    if (config.vectorStore && !config.embedder) {\n      throw new WorkspaceError('vectorStore requires an embedder', 'INVALID_SEARCH_CONFIG');\n    }\n\n    // Create search engine if search is configured\n    if (config.bm25 || (config.vectorStore && config.embedder)) {\n      const buildIndexName = (): string => {\n        // Sanitize default name: replace all non-alphanumeric chars with underscores\n        const defaultName = `${this.id}_search`.replace(/[^a-zA-Z0-9_]/g, '_');\n        const indexName = config.searchIndexName ?? defaultName;\n\n        // Validate SQL identifier format\n        if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(indexName)) {\n          throw new WorkspaceError(\n            `Invalid searchIndexName: \"${indexName}\". Must start with a letter or underscore, and contain only letters, numbers, or underscores.`,\n            'INVALID_SEARCH_CONFIG',\n            this.id,\n          );\n        }\n        if (indexName.length > 63) {\n          throw new WorkspaceError(\n            `searchIndexName exceeds 63 characters (got ${indexName.length})`,\n            'INVALID_SEARCH_CONFIG',\n            this.id,\n          );\n        }\n        return indexName;\n      };\n\n      this._searchEngine = new SearchEngine({\n        bm25: config.bm25 ? parseBM25Config(config.bm25) : undefined,\n        vector:\n          config.vectorStore && config.embedder\n            ? {\n                vectorStore: config.vectorStore,\n                embedder: config.embedder,\n                indexName: buildIndexName(),\n              }\n            : undefined,\n      });\n    }\n\n    // Initialize LSP if configured and a process manager is available\n    if (config.lsp) {\n      const processes = this._sandbox?.processes;\n      if (this._sandboxResolver) {\n        console.warn(\n          `[Workspace \"${this.name}\"] lsp: true is incompatible with a dynamic sandbox resolver — LSP needs a process manager at construction time, but the sandbox is resolved per request. LSP disabled.`,\n        );\n      } else if (!this._sandbox) {\n        console.warn(\n          `[Workspace \"${this.name}\"] lsp: true requires a sandbox with a process manager. No sandbox configured — LSP disabled.`,\n        );\n      } else if (!processes) {\n        console.warn(\n          `[Workspace \"${this.name}\"] lsp: true requires a sandbox with a process manager. Sandbox \"${this._sandbox.name ?? 'unknown'}\" does not provide one — LSP disabled.`,\n        );\n      } else if (!isLSPAvailable()) {\n        console.warn(\n          `[Workspace \"${this.name}\"] lsp: true requires vscode-jsonrpc and vscode-languageserver-protocol packages. Install them to enable LSP diagnostics.`,\n        );\n      } else {\n        const lspConfig = config.lsp === true ? {} : config.lsp;\n        const defaultRoot = lspConfig.root ?? findProjectRoot(process.cwd()) ?? process.cwd();\n        this._lsp = new LSPManager(processes, defaultRoot, lspConfig, this._fs);\n      }\n    }\n\n    // Validate at least one provider is given\n    // Note: skills alone is also valid - uses LocalSkillSource for read-only skills\n    if (!this._fs && !this._filesystemResolver && !this._sandbox && !this._sandboxResolver && !this.hasSkillsConfig()) {\n      throw new WorkspaceError('Workspace requires at least a filesystem, sandbox, or skills', 'NO_PROVIDERS');\n    }\n  }\n\n  private generateId(): string {\n    return `ws-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n  }\n\n  private hasSkillsConfig(): boolean {\n    return (\n      this._config.skills !== undefined && (typeof this._config.skills === 'function' || this._config.skills.length > 0)\n    );\n  }\n\n  get status(): WorkspaceStatus {\n    return this._status;\n  }\n\n  /**\n   * The filesystem provider (if configured).\n   *\n   * Returns the concrete type you passed to the constructor.\n   * When `mounts` is used instead of `filesystem`, returns `CompositeFilesystem`\n   * parameterized with the concrete mount types.\n   */\n  get filesystem(): [TMounts] extends [Record<string, WorkspaceFilesystem>]\n    ? CompositeFilesystem<TMounts>\n    : TFilesystem {\n    return this._fs as any;\n  }\n\n  /**\n   * The sandbox provider (if configured).\n   *\n   * Returns the concrete type you passed to the constructor.\n   */\n  get sandbox(): TSandbox {\n    return this._sandbox as any;\n  }\n\n  /**\n   * The browser provider (if configured).\n   *\n   * Returns the MastraBrowser instance (must be a CLI provider like BrowserViewer).\n   */\n  get browser(): MastraBrowser | undefined {\n    return this._browser;\n  }\n\n  /**\n   * Get the per-tool configuration for this workspace.\n   * Returns undefined if no tools config was provided.\n   */\n  getToolsConfig(): WorkspaceToolsConfig | undefined {\n    return this._config.tools;\n  }\n\n  /**\n   * The LSP manager (if configured, initialized, and a process manager is available).\n   * Returns undefined if LSP is not configured, deps are missing, or sandbox has no process manager.\n   */\n  get lsp(): LSPManager | undefined {\n    return this._lsp;\n  }\n\n  /**\n   * Update the per-tool configuration for this workspace.\n   * Takes effect on the next `createWorkspaceTools()` call.\n   *\n   * @example\n   * ```typescript\n   * // Disable write tools for read-only mode\n   * workspace.setToolsConfig({\n   *   mastra_workspace_write_file: { enabled: false },\n   *   mastra_workspace_edit_file: { enabled: false },\n   * });\n   *\n   * // Re-enable all tools\n   * workspace.setToolsConfig(undefined);\n   * ```\n   */\n  setToolsConfig(config: WorkspaceToolsConfig | undefined): void {\n    this._config.tools = config;\n  }\n\n  /**\n   * Returns true if a filesystem is configured, either as a static instance or a resolver function.\n   */\n  hasFilesystemConfig(): boolean {\n    return this._fs !== undefined || this._filesystemResolver !== undefined;\n  }\n\n  /**\n   * Resolve the filesystem for a given request context.\n   * When a resolver function is configured, calls it with the provided requestContext.\n   * When a static filesystem is configured, returns it directly.\n   * Returns undefined if no filesystem is configured.\n   */\n  async resolveFilesystem({\n    requestContext,\n  }: {\n    requestContext: RequestContext;\n  }): Promise<WorkspaceFilesystem | undefined> {\n    if (!this._filesystemResolver) return this._fs;\n    let pending = this._filesystemRequestCache.get(requestContext);\n    if (!pending) {\n      pending = Promise.resolve(this._filesystemResolver({ requestContext }));\n      this._filesystemRequestCache.set(requestContext, pending);\n    }\n    return pending;\n  }\n\n  /**\n   * Returns true if a sandbox is configured, either as a static instance or a resolver function.\n   */\n  hasSandboxConfig(): boolean {\n    return this._sandbox !== undefined || this._sandboxResolver !== undefined;\n  }\n\n  /**\n   * Returns true when the sandbox is resolved dynamically per request.\n   */\n  hasSandboxResolver(): boolean {\n    return this._sandboxResolver !== undefined;\n  }\n\n  /**\n   * Returns true when resolver-backed sandboxes are cached by a stable key.\n   */\n  hasSandboxCacheKey(): boolean {\n    return this._sandboxCacheKey !== undefined;\n  }\n\n  /**\n   * Resolve the sandbox for a given request context. Calls the resolver function\n   * if configured, otherwise returns the static sandbox (or undefined). Results\n   * are memoized by `sandboxCacheKey` when set, else per RequestContext instance.\n   */\n  async resolveSandbox({ requestContext }: { requestContext: RequestContext }): Promise<WorkspaceSandbox | undefined> {\n    if (!this._sandboxResolver) return this._sandbox;\n\n    const cacheKey = this._sandboxCacheKey?.({ requestContext });\n    if (cacheKey != null) {\n      let keyed = this._sandboxKeyCache.get(cacheKey);\n      if (!keyed) {\n        keyed = Promise.resolve().then(() => this._sandboxResolver!({ requestContext }));\n        this._sandboxKeyCache.set(cacheKey, keyed);\n        keyed.catch(() => {\n          if (this._sandboxKeyCache.get(cacheKey) === keyed) {\n            this._sandboxKeyCache.delete(cacheKey);\n          }\n        });\n      }\n      return keyed;\n    }\n\n    let pending = this._sandboxRequestCache.get(requestContext);\n    if (!pending) {\n      pending = Promise.resolve().then(() => this._sandboxResolver!({ requestContext }));\n      this._sandboxRequestCache.set(requestContext, pending);\n      pending.catch(() => {\n        if (this._sandboxRequestCache.get(requestContext) === pending) {\n          this._sandboxRequestCache.delete(requestContext);\n        }\n      });\n    }\n    return pending;\n  }\n\n  /**\n   * Clear cached resolver-backed sandboxes stored by `sandboxCacheKey`.\n   *\n   * This only clears the keyed cache. Per-RequestContext WeakMap entries are\n   * garbage-collection managed and cannot be cleared by this method.\n   *\n   * The workspace does not own resolver-returned sandboxes, so this only drops\n   * references from the workspace cache. Callers remain responsible for\n   * destroying any sandbox instances they created.\n   */\n  clearSandboxCache(cacheKey?: string): void {\n    if (cacheKey === undefined) {\n      this._sandboxKeyCache.clear();\n      return;\n    }\n    this._sandboxKeyCache.delete(cacheKey);\n  }\n\n  /**\n   * Access skills stored in this workspace.\n   * Skills are SKILL.md files discovered from the configured skillPaths.\n   *\n   * Returns undefined if no skillPaths are configured.\n   *\n   * @example\n   * ```typescript\n   * const skills = await workspace.skills?.list();\n   * const skill = await workspace.skills?.get('skills/brand-guidelines');\n   * const results = await workspace.skills?.search('brand colors');\n   * ```\n   */\n  get skills(): WorkspaceSkills | undefined {\n    // Skills require skills config\n    if (!this.hasSkillsConfig()) {\n      return undefined;\n    }\n\n    // Lazy initialization\n    if (!this._skills) {\n      // Priority: explicit skillSource > workspace filesystem > LocalSkillSource (read-only from local disk)\n      const source = this._config.skillSource ?? this._fs ?? new LocalSkillSource();\n\n      this._skills = new WorkspaceSkillsImpl({\n        source,\n        skills: this._config.skills!,\n        searchEngine: this._searchEngine,\n        validateOnLoad: true,\n        checkSkillFileMtime: this._config.checkSkillFileMtime,\n      });\n    }\n\n    return this._skills;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Search Capabilities\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Check if BM25 keyword search is available.\n   */\n  get canBM25(): boolean {\n    return this._searchEngine?.canBM25 ?? false;\n  }\n\n  /**\n   * Check if vector semantic search is available.\n   */\n  get canVector(): boolean {\n    return this._searchEngine?.canVector ?? false;\n  }\n\n  /**\n   * Check if hybrid search is available.\n   */\n  get canHybrid(): boolean {\n    return this._searchEngine?.canHybrid ?? false;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Search Operations\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Index content for search.\n   * The path becomes the document ID in search results.\n   *\n   * @param path - File path (used as document ID)\n   * @param content - Text content to index\n   * @param options - Index options (metadata, type hints)\n   * @throws {SearchNotAvailableError} if search is not configured\n   */\n  async index(\n    path: string,\n    content: string,\n    options?: {\n      type?: 'text' | 'image' | 'file';\n      mimeType?: string;\n      metadata?: Record<string, unknown>;\n      startLineOffset?: number;\n    },\n  ): Promise<void> {\n    if (!this._searchEngine) {\n      throw new SearchNotAvailableError();\n    }\n    this.lastAccessedAt = new Date();\n\n    const doc: IndexDocument = {\n      id: path,\n      content,\n      metadata: {\n        type: options?.type,\n        mimeType: options?.mimeType,\n        ...options?.metadata,\n      },\n      startLineOffset: options?.startLineOffset,\n    };\n\n    await this._searchEngine.index(doc);\n  }\n\n  /**\n   * Search indexed content.\n   *\n   * @param query - Search query string\n   * @param options - Search options (topK, mode, filters)\n   * @returns Array of search results\n   * @throws {SearchNotAvailableError} if search is not configured\n   */\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    if (!this._searchEngine) {\n      throw new SearchNotAvailableError();\n    }\n    this.lastAccessedAt = new Date();\n    return this._searchEngine.search(query, options);\n  }\n\n  /**\n   * Rebuild the search index from filesystem paths.\n   * Used internally for auto-indexing on init.\n   *\n   * Paths can be plain directories, single files, or glob patterns.\n   * Uses resolvePathPattern for unified resolution: file matches are\n   * indexed directly, directory matches are recursed.\n   */\n  private async rebuildSearchIndex(paths: string[]): Promise<void> {\n    if (!this._searchEngine || !this._fs || paths.length === 0) {\n      return;\n    }\n\n    // Clear existing BM25 index\n    this._searchEngine.clear();\n\n    // Adapt filesystem readdir to the ReaddirEntry interface\n    const readdir = async (dir: string): Promise<ReaddirEntry[]> => {\n      const entries = await this._fs!.readdir(dir);\n      return entries.map(e => ({ name: e.name, type: e.type, isSymlink: e.isSymlink }));\n    };\n\n    // Index all files from specified paths (track across patterns to avoid re-indexing overlaps)\n    const indexedPaths = new Set<string>();\n    for (const pathOrGlob of paths) {\n      try {\n        const resolved = await resolvePathPattern(pathOrGlob, readdir);\n        const filesToIndex = new Set<string>();\n        const directoryRoots: string[] = [];\n        for (const entry of resolved) {\n          if (entry.type === 'file') {\n            filesToIndex.add(entry.path);\n            continue;\n          }\n          // Skip directories already covered by a parent directory\n          const alreadyCovered = directoryRoots.some(root => entry.path === root || entry.path.startsWith(`${root}/`));\n          if (!alreadyCovered) directoryRoots.push(entry.path);\n        }\n        // Index direct file matches first so they aren't lost if a directory scan fails\n        const indexed = await this.indexFilesForSearch(\n          Array.from(filesToIndex).filter(filePath => !indexedPaths.has(filePath)),\n        );\n        for (const filePath of indexed) indexedPaths.add(filePath);\n\n        for (const dir of directoryRoots) {\n          try {\n            const files = (await this.getAllFiles(dir)).filter(filePath => !indexedPaths.has(filePath));\n            const indexed = await this.indexFilesForSearch(files);\n            for (const filePath of indexed) indexedPaths.add(filePath);\n          } catch {\n            // Skip directories that can't be read\n          }\n        }\n      } catch {\n        // Skip paths that don't exist or can't be read\n      }\n    }\n  }\n\n  /**\n   * Load file contents for search indexing in parallel (bounded by {@link FS_READ_CONCURRENCY}).\n   * Paths that cannot be read as UTF-8 text are omitted (same behavior as {@link indexFileForSearch}).\n   */\n  private async batchReadFiles(files: string[]): Promise<Array<{ filePath: string; docs: IndexDocument[] }>> {\n    if (!this._fs || files.length === 0) {\n      return [];\n    }\n\n    const fs = this._fs;\n    return pMap(\n      files,\n      async (filePath): Promise<{ filePath: string; docs: IndexDocument[] } | typeof pMapSkip> => {\n        try {\n          const content = (await fs.readFile(filePath, { encoding: 'utf-8' })) as string;\n          const chunks = splitIntoChunks(content);\n          const docs: IndexDocument[] =\n            chunks.length === 1\n              ? [{ id: filePath, content }]\n              : chunks.map((chunk, i) => ({\n                  id: `${filePath}#chunk-${i}`,\n                  content: chunk.content,\n                  startLineOffset: chunk.startLine,\n                  metadata: { sourceFile: filePath },\n                }));\n          return { filePath, docs };\n        } catch {\n          return pMapSkip;\n        }\n      },\n      { stopOnError: false, concurrency: FS_READ_CONCURRENCY },\n    );\n  }\n\n  /**\n   * Batch-read paths and {@link SearchEngine.indexMany}\n   *\n   * @returns paths that were indexed successfully.\n   * @remarks Falls back to one-at-a-time indexing on failure of {@link SearchEngine.indexMany}\n   */\n  private async indexFilesForSearch(paths: string[]): Promise<string[]> {\n    const engine = this._searchEngine;\n    if (!engine) return [];\n    try {\n      const entries = await this.batchReadFiles(paths);\n      // Clear stale single-doc/chunked entries from previous indexing passes.\n      await pMap(entries, ({ filePath }) => engine.removeSource(filePath), {\n        concurrency: FS_READ_CONCURRENCY,\n      });\n      const docs = entries.flatMap(({ docs }) => docs);\n      await engine.indexMany(docs);\n      return entries.map(({ filePath }) => filePath);\n    } catch {\n      const indexed: string[] = [];\n      for (const filePath of paths) {\n        const id = await this.indexFileForSearch(filePath);\n        if (id !== undefined) {\n          indexed.push(id);\n        }\n      }\n      return indexed;\n    }\n  }\n\n  /**\n   * Index a single file for search. Skips files that can't be read as text.\n   * Large files are automatically split into chunks to stay within embedding\n   * model token limits.\n   *\n   * @returns `filePath` when indexed, or `undefined` if read/index failed.\n   */\n  private async indexFileForSearch(filePath: string): Promise<string | undefined> {\n    let content: string;\n    try {\n      content = (await this._fs!.readFile(filePath, { encoding: 'utf-8' })) as string;\n    } catch {\n      // Skip files that can't be read as text (e.g. binary files, invalid UTF-8)\n      return;\n    }\n\n    // Clear stale single-doc/chunked entries from previous indexing passes.\n    await this._searchEngine!.removeSource(filePath);\n\n    const chunks = splitIntoChunks(content);\n\n    if (chunks.length === 1) {\n      try {\n        await this._searchEngine!.index({ id: filePath, content });\n        return filePath;\n      } catch (error) {\n        this._logger?.warn(`Failed to index file \"${filePath}\" for search`, { error });\n        return;\n      }\n    }\n\n    let anyIndexed = false;\n    for (let i = 0; i < chunks.length; i++) {\n      const chunk = chunks[i]!;\n      try {\n        await this._searchEngine!.index({\n          id: `${filePath}#chunk-${i}`,\n          content: chunk.content,\n          startLineOffset: chunk.startLine,\n          metadata: { sourceFile: filePath },\n        });\n        anyIndexed = true;\n      } catch (error) {\n        this._logger?.warn(`Failed to index chunk ${i} of file \"${filePath}\" for search`, { error });\n      }\n    }\n    return anyIndexed ? filePath : undefined;\n  }\n\n  private async getAllFiles(\n    dir: string,\n    depth: number = 0,\n    maxDepth: number = 10,\n    filesystem: WorkspaceFilesystem | undefined = this._fs,\n  ): Promise<string[]> {\n    if (!filesystem || depth >= maxDepth) return [];\n\n    const files: string[] = [];\n    const entries = await filesystem.readdir(dir);\n\n    for (const entry of entries) {\n      const fullPath = dir === '.' || dir === '' ? entry.name : `${dir}/${entry.name}`;\n      if (entry.type === 'file') {\n        files.push(fullPath);\n      } else if (entry.type === 'directory' && !entry.isSymlink) {\n        // Skip symlink directories to prevent infinite recursion from cycles\n        files.push(...(await this.getAllFiles(fullPath, depth + 1, maxDepth, filesystem)));\n      }\n    }\n\n    return files;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Initialize the workspace.\n   * Starts the sandbox, initializes the filesystem, and auto-mounts filesystems.\n   *\n   * Resolver-backed providers are skipped because there is no instance until\n   * the resolver runs.\n   */\n  async init(): Promise<void> {\n    this._status = 'initializing';\n\n    try {\n      if (this._fs) {\n        await callLifecycle(this._fs, 'init');\n      }\n\n      if (this._sandbox) {\n        await callLifecycle(this._sandbox, 'start');\n      }\n\n      // Note: Browser is NOT launched here - it's launched lazily in execute-command\n      // when a browser CLI command is detected. This matches SDK provider behavior\n      // and enables thread-scoped browsers.\n\n      // Auto-index files if autoIndexPaths is configured\n      if (this._searchEngine && this._config.autoIndexPaths && this._config.autoIndexPaths.length > 0) {\n        await this.rebuildSearchIndex(this._config.autoIndexPaths ?? []);\n      }\n\n      this._status = 'ready';\n    } catch (error) {\n      this._status = 'error';\n      throw error;\n    }\n  }\n\n  /**\n   * Destroy the workspace and clean up all resources.\n   */\n  async destroy(): Promise<void> {\n    if (this._status === 'destroyed') {\n      return;\n    }\n    if (this._status === 'destroying' && this._destroyPromise) {\n      return await this._destroyPromise;\n    }\n\n    this._status = 'destroying';\n    this._destroyPromise = this._performDestroy();\n\n    try {\n      await this._destroyPromise;\n    } finally {\n      this._destroyPromise = undefined;\n    }\n  }\n\n  private async _performDestroy(): Promise<void> {\n    try {\n      // Shutdown LSP before sandbox — LSP clients need running processes to send shutdown/exit\n      if (this._lsp) {\n        try {\n          await this._lsp.shutdownAll();\n        } catch {\n          // LSP shutdown errors are non-blocking\n        }\n        this._lsp = undefined;\n      }\n\n      // Close browser before sandbox\n      if (this._browser) {\n        try {\n          await this._browser.close();\n        } catch {\n          // Browser close errors are non-blocking\n        }\n      }\n\n      if (this._sandbox) {\n        await callLifecycle(this._sandbox, 'destroy');\n      }\n\n      if (this._fs) {\n        await callLifecycle(this._fs, 'destroy');\n      }\n\n      this.clearSandboxCache();\n\n      this._status = 'destroyed';\n    } catch (error) {\n      this._status = 'error';\n      throw error;\n    }\n  }\n\n  /**\n   * Get workspace information.\n   * @param options.includeFileCount - Whether to count total files (can be slow for large workspaces)\n   */\n  async getInfo(options?: {\n    includeFileCount?: boolean;\n    requestContext?: RequestContext;\n    resolveDynamicProviders?: boolean;\n  }): Promise<WorkspaceInfo> {\n    const info: WorkspaceInfo = {\n      id: this.id,\n      name: this.name,\n      status: this._status,\n      createdAt: this.createdAt,\n      lastAccessedAt: this.lastAccessedAt,\n    };\n\n    const shouldResolveDynamicProviders = options?.resolveDynamicProviders ?? true;\n    // Prefer a provider already resolved for this request. When getInfo runs on\n    // the effective workspace proxy during tool execution, `this.filesystem` is\n    // the resolved instance, so metadata stays accurate without re-resolving.\n    const filesystem =\n      (this.filesystem as WorkspaceFilesystem | undefined) ??\n      (this._filesystemResolver && shouldResolveDynamicProviders\n        ? await this.resolveFilesystem({ requestContext: options?.requestContext ?? new RequestContext() })\n        : undefined);\n\n    if (filesystem) {\n      const fsInfo = await filesystem.getInfo?.();\n      info.filesystem = {\n        id: fsInfo?.id ?? filesystem.id,\n        name: fsInfo?.name ?? filesystem.name,\n        provider: fsInfo?.provider ?? filesystem.provider,\n        readOnly: fsInfo?.readOnly ?? filesystem.readOnly,\n        status: fsInfo?.status,\n        error: fsInfo?.error,\n        icon: fsInfo?.icon,\n        metadata: fsInfo?.metadata,\n      };\n\n      if (options?.includeFileCount) {\n        try {\n          const files = await this.getAllFiles('.', 0, 10, filesystem);\n          info.filesystem.totalFiles = files.length;\n        } catch {\n          // Ignore errors - filesystem may not support listing\n        }\n      }\n    } else if (this._filesystemResolver) {\n      info.filesystem = {\n        id: `${this.id}-dynamic-filesystem`,\n        name: 'DynamicFilesystem',\n        provider: 'dynamic',\n        status: 'pending',\n      };\n    }\n\n    // `this.sandbox` picks up a sandbox already resolved for this request when\n    // getInfo runs on the effective workspace proxy. getInfo never invokes the\n    // sandbox resolver itself — resolver-backed sandboxes can provision real\n    // infrastructure, so resolving stays a tool-execution concern.\n    const sandbox = this.sandbox as WorkspaceSandbox | undefined;\n    if (sandbox) {\n      const sandboxInfo = await sandbox.getInfo?.();\n      info.sandbox = {\n        provider: sandbox.provider,\n        status: sandboxInfo?.status ?? sandbox.status,\n        resources: sandboxInfo?.resources,\n      };\n    } else if (this._sandboxResolver) {\n      info.sandbox = { provider: 'dynamic', status: 'pending' };\n    }\n\n    return info;\n  }\n\n  /**\n   * Get human-readable instructions describing the workspace environment.\n   *\n   * When both a sandbox with mounts and a filesystem exist, each mount path\n   * is classified as sandbox-accessible (state === 'mounted') or\n   * workspace-only (pending / mounting / error / unsupported). When there's\n   * no sandbox or no mounts, falls back to provider-level instructions.\n   *\n   * @param opts - Optional options including request context for per-request customisation\n   * @returns Combined instructions string (may be empty)\n   */\n  private getInstructionsForProviders(\n    filesystem: WorkspaceFilesystem | undefined,\n    sandbox: WorkspaceSandbox | undefined,\n    opts?: { requestContext?: RequestContext },\n  ): string {\n    const parts: string[] = [];\n\n    // Sandbox-level instructions (working directory, provider type)\n    const sandboxInstructions = sandbox?.getInstructions?.(opts);\n    if (sandboxInstructions) parts.push(sandboxInstructions);\n\n    // Mount state overlay: check actual MountManager state\n    const mountEntries = sandbox?.mounts?.entries;\n    if (mountEntries && mountEntries.size > 0) {\n      const sandboxAccessible: string[] = [];\n      const workspaceOnly: string[] = [];\n      const workingDir = sandbox instanceof LocalSandbox ? sandbox.workingDirectory : undefined;\n\n      for (const [mountPath, entry] of mountEntries) {\n        const fsName = entry.filesystem.displayName || entry.filesystem.provider;\n        const access = entry.filesystem.readOnly ? 'read-only' : 'read-write';\n\n        // Resolve mount path against workingDirectory when available\n        // so the LLM sees the actual usable path (e.g. /tmp/sandbox/s3 instead of /s3)\n        const displayPath = workingDir ? path.join(workingDir, mountPath.replace(/^\\/+/, '')) : mountPath;\n\n        if (entry.state === 'mounted' || entry.state === 'pending' || entry.state === 'mounting') {\n          // mounted: ready now. pending/mounting: will be ready when sandbox starts\n          // (executeCommand triggers ensureRunning which processes pending mounts)\n          sandboxAccessible.push(`  - ${displayPath}: ${fsName} (${access})`);\n        } else {\n          // error, unsupported, unavailable — NOT accessible in sandbox\n          workspaceOnly.push(`  - ${mountPath}: ${fsName} (${access})`);\n        }\n      }\n\n      if (sandboxAccessible.length) {\n        parts.push(`Sandbox-mounted filesystems (accessible in shell commands):\\n${sandboxAccessible.join('\\n')}`);\n      }\n      if (workspaceOnly.length) {\n        parts.push(\n          `Workspace-only filesystems (use file tools, NOT available in shell commands):\\n${workspaceOnly.join('\\n')}`,\n        );\n      }\n    } else {\n      // No mounts or no sandbox — fall back to filesystem-level instructions\n      const fsInstructions = filesystem?.getInstructions?.(opts);\n      if (fsInstructions) parts.push(fsInstructions);\n    }\n\n    return parts.join('\\n\\n');\n  }\n\n  getInstructions(opts?: { requestContext?: RequestContext }): string {\n    return this.getInstructionsForProviders(this._fs, this._sandbox, opts);\n  }\n\n  /**\n   * Get human-readable instructions describing the workspace environment.\n   *\n   * Resolves a dynamic filesystem per request. A resolver-backed sandbox is not\n   * resolved here unless `instructions.dynamicSandbox` is `'resolve'`.\n   */\n  async getInstructionsAsync(opts?: { requestContext?: RequestContext }): Promise<string> {\n    const requestContext = opts?.requestContext ?? new RequestContext();\n    const filesystem = this._filesystemResolver ? await this.resolveFilesystem({ requestContext }) : this._fs;\n    const resolvedOpts = { ...opts, requestContext };\n\n    // Resolver-backed sandbox: emit placeholder text without calling the resolver.\n    if (this._sandboxResolver && this._dynamicSandboxInstructions !== 'resolve') {\n      const sandboxText =\n        typeof this._dynamicSandboxInstructions === 'function'\n          ? this._dynamicSandboxInstructions({ requestContext })\n          : DYNAMIC_SANDBOX_INSTRUCTIONS;\n      const fsText = this.getInstructionsForProviders(filesystem, undefined, resolvedOpts);\n      return [sandboxText, fsText].filter(Boolean).join('\\n\\n');\n    }\n\n    const sandbox = this._sandboxResolver ? await this.resolveSandbox({ requestContext }) : this._sandbox;\n    return this.getInstructionsForProviders(filesystem, sandbox, resolvedOpts);\n  }\n\n  /**\n   * Get information about how filesystem and sandbox paths relate.\n   * Useful for understanding how to access workspace files from sandbox code.\n   *\n   * @deprecated Use {@link getInstructions} instead. `getInstructions()` is\n   * mount-state-aware and feeds into the system message via\n   * `WorkspaceInstructionsProcessor`.\n   *\n   * @returns PathContext with paths and instructions from providers\n   */\n  getPathContext(): PathContext {\n    return this.getPathContextForProviders(this._fs, this._sandbox);\n  }\n\n  private getPathContextForProviders(\n    filesystem: WorkspaceFilesystem | undefined,\n    sandbox: WorkspaceSandbox | undefined,\n  ): PathContext {\n    const fsInstructions = filesystem?.getInstructions?.();\n    const sandboxInstructions = sandbox?.getInstructions?.();\n\n    return {\n      filesystem: filesystem\n        ? {\n            provider: filesystem.provider,\n            basePath: filesystem.basePath,\n          }\n        : undefined,\n      sandbox: sandbox\n        ? {\n            provider: sandbox.provider,\n            workingDirectory: sandbox instanceof LocalSandbox ? sandbox.workingDirectory : undefined,\n          }\n        : undefined,\n      instructions: [fsInstructions, sandboxInstructions].filter(Boolean).join(' '),\n    };\n  }\n\n  // ---------------------------------------------------------------------------\n  // Logger Integration\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Set the logger for this workspace and propagate to providers.\n   * Called by Mastra when the logger is set.\n   * @internal\n   */\n  __setLogger(logger: IMastraLogger): void {\n    this._logger = logger;\n\n    // Propagate logger to filesystem provider if it extends MastraFilesystem\n    // Skip when using a resolver — no static instance to set logger on\n    if (this._fs instanceof MastraFilesystem) {\n      this._fs.__setLogger(logger);\n    }\n\n    // Propagate logger to sandbox provider if it extends MastraSandbox\n    if (this._sandbox instanceof MastraSandbox) {\n      this._sandbox.__setLogger(logger);\n    }\n  }\n}\n","/**\n * Workspace Sandbox Interface\n *\n * Defines the contract for sandbox providers that can be used with Workspace.\n * Users pass sandbox provider instances to the Workspace constructor.\n *\n * Sandboxes provide isolated environments for code and command execution.\n * They may have their own filesystem that's separate from the workspace FS.\n *\n * Built-in providers (via ComputeSDK):\n * - E2B: Cloud sandboxes\n * - Modal: GPU-enabled sandboxes\n * - Docker: Container-based execution\n * - Local: Development-only local execution\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core';\n * import { ComputeSDKSandbox } from '@mastra/workspace-sandbox-computesdk';\n *\n * const workspace = new Workspace({\n *   sandbox: new ComputeSDKSandbox({ provider: 'e2b' }),\n * });\n * ```\n */\n\nimport type { RequestContext } from '../../request-context';\nimport type { WorkspaceFilesystem } from '../filesystem/filesystem';\nimport type { MountResult } from '../filesystem/mount';\nimport type { SandboxLifecycle } from '../lifecycle';\n\nimport type { MountManager } from './mount-manager';\nimport type { SandboxProcessManager } from './process-manager';\nimport type { CommandResult, ExecuteCommandOptions, SandboxInfo } from './types';\n\n// =============================================================================\n// Networking Capability\n// =============================================================================\n\n/**\n * Optional networking capability for sandboxes that can expose ports publicly.\n *\n * Providers that support public port exposure (Vercel Sandbox, E2B, Daytona,\n * Modal, Blaxel, etc.) implement this to surface public URLs through the\n * abstraction. Enables preview URLs and sandbox deploys.\n */\nexport interface SandboxNetworking {\n  /**\n   * Get the public URL for an exposed port.\n   *\n   * @param port - The port number inside the sandbox\n   * @returns The public URL for the port, or null if the port is not exposed\n   *   or the sandbox is not running\n   */\n  getPortUrl(port: number): Promise<string | null>;\n}\n\n/** A file to write into the sandbox filesystem via {@link WorkspaceSandbox.writeFiles}. */\nexport interface SandboxFileInput {\n  /** Destination path inside the sandbox */\n  path: string;\n  /** File contents */\n  content: string | Buffer;\n}\n\n/**\n * Type guard: does this sandbox support the networking capability?\n *\n * @example\n * ```typescript\n * if (supportsNetworking(sandbox)) {\n *   const url = await sandbox.networking.getPortUrl(4111);\n * }\n * ```\n */\nexport function supportsNetworking(\n  sandbox: WorkspaceSandbox,\n): sandbox is WorkspaceSandbox & { networking: SandboxNetworking } {\n  return typeof sandbox.networking?.getPortUrl === 'function';\n}\n\n// =============================================================================\n// Sandbox Derivation\n// =============================================================================\n\n/**\n * Options for cloning a configured sandbox's configuration into an independent\n * sibling sandbox. See {@link WorkspaceSandbox.clone}.\n */\nexport interface SandboxCloneOptions {\n  /** Unique identifier for the sandbox clone instance. */\n  id?: string;\n  /**\n   * Reattach to an existing provider sandbox (by the provider's own id)\n   * instead of provisioning a new one.\n   */\n  sandboxId?: string;\n  /** Environment variables baked into the sandbox clone. */\n  env?: Record<string, string>;\n  /** Provider working directory for the sandbox clone. */\n  workingDirectory?: string;\n  /** Idle teardown window (minutes) for the sandbox clone. */\n  idleTimeoutMinutes?: number;\n  /**\n   * Provider checkpoint used to seed and preserve the sandbox clone.\n   * Providers without checkpoint support may ignore this option.\n   */\n  checkpointName?: string;\n}\n\n// =============================================================================\n// Sandbox Interface\n// =============================================================================\n\n/**\n * Abstract sandbox interface for code and command execution.\n *\n * Providers implement this interface to provide execution capabilities.\n * Users instantiate providers and pass them to the Workspace constructor.\n *\n * Sandboxes provide isolated environments for running untrusted code.\n * They may have their own filesystem that's separate from the workspace FS.\n *\n * Lifecycle methods (from SandboxLifecycle interface) are all optional:\n * - start(): Begin operation (spin up instance)\n * - stop(): Pause operation (pause instance)\n * - destroy(): Clean up resources (terminate instance)\n * - isReady(): Check if ready for operations\n * - getInfo(): Get status and metadata\n */\nexport interface WorkspaceSandbox extends SandboxLifecycle<SandboxInfo> {\n  /** Unique identifier for this sandbox instance */\n  readonly id: string;\n\n  /** Human-readable name (e.g., 'E2B Sandbox', 'Docker') */\n  readonly name: string;\n\n  /** Provider type identifier */\n  readonly provider: string;\n\n  /**\n   * Get instructions describing how this sandbox works.\n   * Used in tool descriptions to help agents understand execution context.\n   *\n   * @param opts - Optional options including request context for per-request customisation\n   * @returns A string describing how to use this sandbox\n   */\n  getInstructions?(opts?: { requestContext?: RequestContext }): string;\n\n  // ---------------------------------------------------------------------------\n  // Cloning (Optional)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Construct an independent sibling sandbox that inherits this sandbox's\n   * configuration (credentials, provider settings, defaults) with\n   * per-instance overrides.\n   *\n   * Performs no I/O — the sandbox clone provisions (or reattaches, when\n   * `sandboxId` is set) on its own `start()`. Implement this when one\n   * configured sandbox should act as the template for a fleet of independent\n   * sandboxes (e.g. one per project).\n   *\n   * Optional — consumers that need fleets (like the MastraCode web factory)\n   * only support sandboxes that implement it.\n   */\n  clone?(options?: SandboxCloneOptions): WorkspaceSandbox;\n\n  // ---------------------------------------------------------------------------\n  // Command Execution\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Execute a shell command and wait for it to complete.\n   * Optional - if not implemented, the workspace_execute_command tool won't be available.\n   *\n   * @example\n   * ```typescript\n   * await sandbox.executeCommand('npm install');\n   *\n   * // With options\n   * await sandbox.executeCommand('npm install', [], { timeout: 60000 });\n   *\n   * // With args array (each arg is shell-quoted automatically)\n   * await sandbox.executeCommand('npm', ['install'], { timeout: 60000 });\n   * ```\n   *\n   * @throws {SandboxExecutionError} if command fails to start\n   * @throws {SandboxTimeoutError} if command times out\n   */\n  executeCommand?(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult>;\n\n  // ---------------------------------------------------------------------------\n  // Networking (Optional)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Networking capability for sandboxes that can expose ports publicly.\n   * Optional - only available on providers that support public port exposure.\n   * Enables preview URLs and sandbox deploys.\n   *\n   * @example\n   * ```typescript\n   * const url = await sandbox.networking?.getPortUrl(4111);\n   * ```\n   */\n  readonly networking?: SandboxNetworking;\n\n  // ---------------------------------------------------------------------------\n  // File Upload (Optional)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Bulk-write files into the sandbox's own filesystem.\n   * Optional fast path - providers with a native file-upload API implement this.\n   * Callers should fall back to `executeCommand` when unavailable.\n   *\n   * @example\n   * ```typescript\n   * await sandbox.writeFiles?.([{ path: '/app/index.mjs', content: bundle }]);\n   * ```\n   */\n  writeFiles?(files: SandboxFileInput[]): Promise<void>;\n\n  // ---------------------------------------------------------------------------\n  // Process Management (Optional)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Process manager.\n   * Optional - if not implemented, process management tools won't be available.\n   *\n   * Provides methods to spawn long-running processes, list them, and interact\n   * with them via their {@link ProcessHandle} (kill, sendStdin, wait, read output).\n   *\n   * @example\n   * ```typescript\n   * const handle = await sandbox.processes.spawn('node server.js');\n   * console.log(handle.pid);\n   *\n   * const procs = await sandbox.processes.list();\n   * const proc = await sandbox.processes.get(handle.pid);\n   * await proc?.sendStdin('hello\\n');\n   * await proc?.kill();\n   * ```\n   */\n  readonly processes?: SandboxProcessManager;\n\n  // ---------------------------------------------------------------------------\n  // Mounting Support (Optional)\n  // ---------------------------------------------------------------------------\n\n  /**\n   * Mount manager for tracking and processing filesystem mounts.\n   * Only available if the sandbox implements mount().\n   *\n   * @example\n   * ```typescript\n   * // Add pending mounts\n   * sandbox.mounts?.add({ '/data': s3fs });\n   *\n   * // Check mount entries\n   * const entries = sandbox.mounts?.entries;\n   * ```\n   */\n  readonly mounts?: MountManager;\n\n  /**\n   * Mount a filesystem at a path in the sandbox.\n   * Uses FUSE tools (s3fs, gcsfuse) to mount cloud storage.\n   *\n   * @param filesystem - The filesystem to mount\n   * @param mountPath - Path in the sandbox where filesystem should be mounted\n   * @returns Mount result with success status and mount path\n   * @throws {MountError} if mount fails\n   * @throws {MountNotSupportedError} if sandbox doesn't support mounting\n   * @throws {FilesystemNotMountableError} if filesystem cannot be mounted\n   */\n  mount?(filesystem: WorkspaceFilesystem, mountPath: string): Promise<MountResult>;\n\n  /**\n   * Unmount a filesystem from a path in the sandbox.\n   *\n   * @param mountPath - Path to unmount\n   */\n  unmount?(mountPath: string): Promise<void>;\n}\n","export const WORKSPACE_TOOLS_PREFIX = 'mastra_workspace' as const;\n\n/**\n * Workspace tool name constants.\n * Use these to reference workspace tools by name.\n *\n * @example\n * ```typescript\n * import { WORKSPACE_TOOLS } from '@mastra/core/workspace';\n *\n * if (toolName === WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND) {\n *   // Handle sandbox execution\n * }\n * ```\n */\nexport const WORKSPACE_TOOLS = {\n  FILESYSTEM: {\n    READ_FILE: `${WORKSPACE_TOOLS_PREFIX}_read_file` as const,\n    WRITE_FILE: `${WORKSPACE_TOOLS_PREFIX}_write_file` as const,\n    EDIT_FILE: `${WORKSPACE_TOOLS_PREFIX}_edit_file` as const,\n    LIST_FILES: `${WORKSPACE_TOOLS_PREFIX}_list_files` as const,\n    DELETE: `${WORKSPACE_TOOLS_PREFIX}_delete` as const,\n    FILE_STAT: `${WORKSPACE_TOOLS_PREFIX}_file_stat` as const,\n    MKDIR: `${WORKSPACE_TOOLS_PREFIX}_mkdir` as const,\n    GREP: `${WORKSPACE_TOOLS_PREFIX}_grep` as const,\n    AST_EDIT: `${WORKSPACE_TOOLS_PREFIX}_ast_edit` as const,\n  },\n  SANDBOX: {\n    EXECUTE_COMMAND: `${WORKSPACE_TOOLS_PREFIX}_execute_command` as const,\n    GET_PROCESS_OUTPUT: `${WORKSPACE_TOOLS_PREFIX}_get_process_output` as const,\n    KILL_PROCESS: `${WORKSPACE_TOOLS_PREFIX}_kill_process` as const,\n  },\n  SEARCH: {\n    SEARCH: `${WORKSPACE_TOOLS_PREFIX}_search` as const,\n    INDEX: `${WORKSPACE_TOOLS_PREFIX}_index` as const,\n  },\n  LSP: {\n    LSP_INSPECT: `${WORKSPACE_TOOLS_PREFIX}_lsp_inspect` as const,\n  },\n} as const;\n\n/**\n * Type representing any workspace tool name.\n */\nexport type WorkspaceToolName =\n  | (typeof WORKSPACE_TOOLS.FILESYSTEM)[keyof typeof WORKSPACE_TOOLS.FILESYSTEM]\n  | (typeof WORKSPACE_TOOLS.SEARCH)[keyof typeof WORKSPACE_TOOLS.SEARCH]\n  | (typeof WORKSPACE_TOOLS.SANDBOX)[keyof typeof WORKSPACE_TOOLS.SANDBOX]\n  | (typeof WORKSPACE_TOOLS.LSP)[keyof typeof WORKSPACE_TOOLS.LSP];\n","/**\n * Workspace Tool Helpers\n *\n * Runtime assertions for extracting workspace resources from tool execution context.\n */\n\nimport path from 'node:path';\n\nimport type { ToolExecutionContext } from '../../tools/types';\nimport { WorkspaceNotAvailableError, FilesystemNotAvailableError, SandboxNotAvailableError } from '../errors';\nimport type { WorkspaceFilesystem } from '../filesystem';\nimport type { LSPDiagnostic, DiagnosticSeverity } from '../lsp/types';\nimport type { WorkspaceSandbox } from '../sandbox';\nimport type { Workspace } from '../workspace';\n\n/**\n * Extract workspace from tool execution context.\n * Throws if workspace is not available.\n */\nexport function requireWorkspace(context: ToolExecutionContext): Workspace {\n  if (!context?.workspace) {\n    throw new WorkspaceNotAvailableError();\n  }\n  return context.workspace;\n}\n\n/**\n * Extract filesystem from workspace in tool execution context.\n * Throws if workspace or filesystem is not available.\n */\nexport function requireFilesystem(context: ToolExecutionContext): {\n  workspace: Workspace;\n  filesystem: WorkspaceFilesystem;\n} {\n  const workspace = requireWorkspace(context);\n  if (!workspace.filesystem) {\n    throw new FilesystemNotAvailableError();\n  }\n  return { workspace, filesystem: workspace.filesystem };\n}\n\n/**\n * Extract sandbox from workspace in tool execution context.\n * Throws if workspace or sandbox is not available.\n */\nexport function requireSandbox(context: ToolExecutionContext): {\n  workspace: Workspace;\n  sandbox: WorkspaceSandbox;\n} {\n  const workspace = requireWorkspace(context);\n  if (!workspace.sandbox) {\n    throw new SandboxNotAvailableError();\n  }\n  return { workspace, sandbox: workspace.sandbox };\n}\n\nexport function getDynamicSandboxCacheKeyHint(workspace: Workspace): string {\n  const hasResolver = workspace.hasSandboxResolver();\n  const hasCacheKey = workspace.hasSandboxCacheKey();\n\n  if (!hasResolver || hasCacheKey) return '';\n\n  return ' If this process was started from a dynamic sandbox resolver, configure sandboxCacheKey or have the resolver return the same sandbox for follow-up calls.';\n}\n\n/**\n * Emit workspace metadata as a data chunk so the UI can render workspace info immediately.\n * Should be called at the start of every workspace tool's execute function.\n */\nexport async function emitWorkspaceMetadata(context: ToolExecutionContext, toolName: string) {\n  const workspace = requireWorkspace(context);\n  const info = await workspace.getInfo({ requestContext: context?.requestContext, resolveDynamicProviders: false });\n  const toolCallId = context?.agent?.toolCallId;\n  await context?.writer?.custom({\n    type: 'data-workspace-metadata',\n    data: { toolName, toolCallId, ...info },\n  });\n}\n\n/**\n * Get LSP diagnostics text to append to edit tool results.\n * Non-blocking — returns empty string on any failure.\n *\n * LSP is a Workspace-level feature. This helper checks if the workspace\n * has an LSP manager and uses it to get diagnostics for the edited file.\n *\n * @param workspace - The workspace (must have an LSP manager for diagnostics)\n * @param filePath - Relative path within the filesystem (as used by the tool)\n * @param content - The file content after the edit\n * @returns Formatted diagnostics text, or empty string if unavailable\n */\nexport async function getEditDiagnosticsText(workspace: Workspace, filePath: string, content: string): Promise<string> {\n  try {\n    const lspManager = workspace.lsp;\n    if (!lspManager) return '';\n\n    // Use the filesystem's path resolution to get the real disk path.\n    // This correctly handles contained: true (virtual paths → basePath)\n    // and contained: false (absolute paths used as-is).\n    // Use posix resolve to ensure POSIX LSP paths don't get Windows drive letters appended,\n    // but fall back to win32 resolve if the root is explicitly a Windows absolute path.\n    const isWindowsRoot =\n      /^[A-Za-z]:[\\\\/]/.test(lspManager.root) || /^(?:\\\\\\\\|\\/\\/)[^\\\\]+[\\\\][^\\\\/]+/.test(lspManager.root);\n    const absolutePath =\n      workspace.filesystem?.resolveAbsolutePath?.(filePath) ??\n      (isWindowsRoot\n        ? path.win32.resolve(lspManager.root, filePath.replace(/^\\/+/, ''))\n        : path.posix.resolve(lspManager.root, filePath.replace(/^\\/+/, '')));\n\n    const DIAG_TIMEOUT_MS = 10_000;\n    let diagTimer: ReturnType<typeof setTimeout>;\n    const diagnostics = await Promise.race([\n      lspManager.getDiagnostics(absolutePath, content),\n      new Promise<LSPDiagnostic[] | null>((_, reject) => {\n        diagTimer = setTimeout(() => reject(new Error('LSP diagnostics timeout')), DIAG_TIMEOUT_MS);\n      }),\n    ]).finally(() => clearTimeout(diagTimer!));\n    // null means no LSP client was available — don't show anything\n    if (diagnostics === null) return '';\n    if (diagnostics.length === 0) return '';\n\n    // Deduplicate by severity + location + message\n    const seen = new Set<string>();\n    const deduped = diagnostics.filter(d => {\n      const key = `${d.severity}:${d.line}:${d.character}:${d.message}`;\n      if (seen.has(key)) return false;\n      seen.add(key);\n      return true;\n    });\n\n    // Group diagnostics by severity\n    const groups: Record<DiagnosticSeverity, LSPDiagnostic[]> = {\n      error: [],\n      warning: [],\n      info: [],\n      hint: [],\n    };\n\n    for (const d of deduped) {\n      groups[d.severity].push(d);\n    }\n\n    const lines: string[] = ['\\n\\nLSP Diagnostics:'];\n\n    const severityLabels: [DiagnosticSeverity, string][] = [\n      ['error', 'Errors'],\n      ['warning', 'Warnings'],\n      ['info', 'Info'],\n      ['hint', 'Hints'],\n    ];\n\n    for (const [severity, label] of severityLabels) {\n      const items = groups[severity];\n      if (items.length === 0) continue;\n      lines.push(`${label}:`);\n      for (const d of items) {\n        const source = d.source ? ` [${d.source}]` : '';\n        lines.push(`  ${d.line}:${d.character} - ${d.message}${source}`);\n      }\n    }\n\n    let result = lines.join('\\n');\n\n    // Truncate to ~500 tokens (~2000 chars) to avoid bloating tool output\n    const maxChars = 2000;\n    if (result.length > maxChars) {\n      const cutoff = result.lastIndexOf('\\n', maxChars);\n      result = result.slice(0, cutoff > 0 ? cutoff : maxChars) + '\\n  ... (truncated)';\n    }\n\n    return result;\n  } catch {\n    return '';\n  }\n}\n","/**\n * AST Edit Tool\n *\n * Provides AST-aware code transformations for workspace files.\n * Uses @ast-grep/napi for syntax-aware pattern matching and transforms.\n *\n * Requires @ast-grep/napi as an optional peer dependency.\n */\n\nimport { createRequire } from 'node:module';\n\nimport { z } from 'zod/v4';\n\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { FileNotFoundError, WorkspaceReadOnlyError } from '../errors';\nimport { emitWorkspaceMetadata, getEditDiagnosticsText, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface Replacement {\n  start: number;\n  end: number;\n  text: string;\n}\n\ninterface TransformResult {\n  content: string;\n  count: number;\n  error?: string;\n}\n\ninterface ImportSpec {\n  module: string;\n  names: string[];\n  isDefault?: boolean;\n}\n\n/**\n * Minimal interface for an ast-grep SgNode.\n * Avoids importing @ast-grep/napi types directly since it's an optional dep.\n */\ninterface SgNode {\n  text(): string;\n  range(): { start: { index: number }; end: { index: number } };\n  findAll(config: { rule: Record<string, unknown> }): SgNode[];\n  getMatch(name: string): SgNode | null;\n}\n\n/** Minimal interface for the ast-grep Lang enum values. */\ntype LangValue = unknown;\n\n/** The subset of @ast-grep/napi we use after dynamic import. */\ninterface AstGrepModule {\n  parse(lang: LangValue, content: string): { root(): SgNode };\n  Lang: Record<string, LangValue>;\n}\n\n// =============================================================================\n// Dynamic Import\n// =============================================================================\n\n// Cache the import result so we only try once\nlet astGrepModule: AstGrepModule | null | undefined;\nlet loadingPromise: Promise<AstGrepModule | null> | undefined;\n\n/**\n * Try to load @ast-grep/napi. Returns null if not available.\n * Uses dynamic import to avoid compile-time dependency.\n * Concurrent callers share the same in-flight promise.\n */\nexport async function loadAstGrep(): Promise<AstGrepModule | null> {\n  if (astGrepModule !== undefined) {\n    return astGrepModule;\n  }\n  if (!loadingPromise) {\n    loadingPromise = (async () => {\n      try {\n        // Dynamic import with string concatenation to prevent bundlers from resolving at build time\n        const moduleName = '@ast-grep' + '/napi';\n        const mod = await import(/* @vite-ignore */ /* webpackIgnore: true */ moduleName);\n        astGrepModule = { parse: mod.parse, Lang: mod.Lang };\n        return astGrepModule;\n      } catch {\n        astGrepModule = null;\n        return null;\n      }\n    })();\n  }\n  return loadingPromise;\n}\n\n/**\n * Check if @ast-grep/napi is available without importing it.\n * Useful for deciding whether to create the tool at registration time.\n */\nexport function isAstGrepAvailable(): boolean {\n  if (astGrepModule !== undefined) {\n    return astGrepModule !== null;\n  }\n\n  try {\n    const req = createRequire(import.meta.url);\n    req.resolve('@ast-grep/napi');\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n// =============================================================================\n// Language Detection\n// =============================================================================\n\n/**\n * Map file extension to ast-grep Lang enum.\n *\n * Only languages with built-in tree-sitter grammars in @ast-grep/napi are\n * supported. Python, Go, Rust, etc. require separate @ast-grep/lang-* packages\n * which are not currently integrated.\n */\nexport function getLanguageFromPath(filePath: string, Lang: Record<string, LangValue>): LangValue | null {\n  const ext = filePath.split('.').pop()?.toLowerCase();\n  switch (ext) {\n    case 'ts':\n      return Lang.TypeScript;\n    case 'tsx':\n    case 'jsx':\n      return Lang.Tsx;\n    case 'js':\n      return Lang.JavaScript;\n    case 'html':\n      return Lang.Html;\n    case 'css':\n      return Lang.Css;\n    default:\n      return null;\n  }\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Escape regex metacharacters in a string. */\nfunction escapeRegex(str: string): string {\n  return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Rename all identifier occurrences matching `oldName` to `newName`.\n * Not scope-aware: renames all occurrences regardless of scope.\n */\nfunction renameIdentifiers(content: string, root: SgNode, oldName: string, newName: string): TransformResult {\n  let modifiedContent = content;\n  let count = 0;\n\n  const identifiers = root.findAll({\n    rule: {\n      kind: 'identifier',\n      regex: `^${escapeRegex(oldName)}$`,\n    },\n  });\n\n  const replacements: Replacement[] = [];\n  const seen = new Set<number>();\n\n  for (const id of identifiers) {\n    const range = id.range();\n    if (seen.has(range.start.index)) continue;\n    seen.add(range.start.index);\n    replacements.push({ start: range.start.index, end: range.end.index, text: newName });\n    count++;\n  }\n\n  replacements.sort((a, b) => b.start - a.start);\n\n  for (const { start, end, text } of replacements) {\n    modifiedContent = modifiedContent.slice(0, start) + text + modifiedContent.slice(end);\n  }\n\n  return { content: modifiedContent, count };\n}\n\n// =============================================================================\n// Transform Functions\n// =============================================================================\n\n/**\n * Build an import statement string from its parts.\n */\nfunction buildImportStatement(defaultName: string | null, namedImports: string[], moduleStr: string): string {\n  if (defaultName && namedImports.length > 0) {\n    return `import ${defaultName}, { ${namedImports.join(', ')} } from ${moduleStr};`;\n  } else if (defaultName) {\n    return `import ${defaultName} from ${moduleStr};`;\n  } else {\n    return `import { ${namedImports.join(', ')} } from ${moduleStr};`;\n  }\n}\n\n/**\n * Merge new names into an existing import statement.\n * Returns null if nothing needs to change.\n */\nfunction mergeIntoExistingImport(\n  content: string,\n  existingImport: SgNode,\n  names: string[],\n  isDefault?: boolean,\n): string | null {\n  const text = existingImport.text();\n\n  // Namespace imports (import * as X from 'mod') cannot be merged into\n  if (/^import\\s+\\*\\s+as\\s+/.test(text)) return null;\n\n  // Parse existing structure from the import text\n  // Matches: import [default] [, { named }] from 'module'\n  const defaultMatch = text.match(/^import\\s+(?!type\\s)(?!\\{)(\\w+)/);\n  const namedMatch = text.match(/\\{([^}]*)\\}/);\n  const moduleMatch = text.match(/([\"'][^\"']+[\"'])\\s*;?\\s*$/);\n\n  if (!moduleMatch) return null;\n  const moduleStr = moduleMatch[1] ?? '';\n\n  let existingDefault = defaultMatch ? (defaultMatch[1] ?? null) : null;\n  const existingNamed = namedMatch\n    ? (namedMatch[1] ?? '')\n        .split(',')\n        .map((s: string) => s.trim())\n        .filter(Boolean)\n    : [];\n\n  let newDefault = existingDefault;\n  const newNamed = [...existingNamed];\n\n  if (isDefault && names.length > 0) {\n    // First name is the default import\n    if (!existingDefault) {\n      newDefault = names[0] ?? null;\n    }\n    // Remaining names are named imports\n    for (const name of names.slice(1)) {\n      if (!newNamed.includes(name)) {\n        newNamed.push(name);\n      }\n    }\n  } else {\n    for (const name of names) {\n      if (!newNamed.includes(name)) {\n        newNamed.push(name);\n      }\n    }\n  }\n\n  // Check if anything changed\n  const defaultChanged = newDefault !== existingDefault;\n  const namedChanged = newNamed.length !== existingNamed.length;\n  if (!defaultChanged && !namedChanged) return null;\n\n  const importStatement = buildImportStatement(newDefault, newNamed, moduleStr);\n  const range = existingImport.range();\n  return content.slice(0, range.start.index) + importStatement + content.slice(range.end.index);\n}\n\n/**\n * Add an import statement to the file.\n * Inserts after the last existing import, or at the beginning if none exist.\n * If the module is already imported, merges new names into it.\n */\nexport function addImport(content: string, root: SgNode, importSpec: ImportSpec): string {\n  const { module, names, isDefault } = importSpec;\n\n  const imports = root.findAll({ rule: { kind: 'import_statement' } });\n\n  // Check if a mergeable import from this module already exists.\n  // Skip type-only and namespace imports — they can't be merged with value imports.\n  const existingImport = imports.find(imp => {\n    const text = imp.text();\n    if (/^import\\s+type\\s/.test(text)) return false;\n    if (/^import\\s+\\*\\s+as\\s+/.test(text)) return false;\n    return text.includes(`'${module}'`) || text.includes(`\"${module}\"`);\n  });\n\n  if (existingImport) {\n    // Try to merge new names into the existing import\n    return mergeIntoExistingImport(content, existingImport, names, isDefault) ?? content;\n  }\n\n  // Build new import statement\n  const moduleStr = `'${module}'`;\n  const importStatement = buildImportStatement(\n    isDefault ? names[0]! : null,\n    isDefault ? names.slice(1) : names,\n    moduleStr,\n  );\n\n  // Insert after last import or at file start\n  const lastImport = imports.at(-1);\n  if (lastImport) {\n    const pos = lastImport.range().end.index;\n    return content.slice(0, pos) + '\\n' + importStatement + content.slice(pos);\n  } else {\n    return importStatement + '\\n\\n' + content;\n  }\n}\n\n/**\n * Remove an import by module name.\n * Matches against the import source string.\n */\nexport function removeImport(content: string, root: SgNode, targetName: string): string {\n  const imports = root.findAll({ rule: { kind: 'import_statement' } });\n\n  for (const imp of imports) {\n    const text = imp.text();\n    const moduleMatch = text.match(/from\\s+['\"]([^'\"]+)['\"]|import\\s+['\"]([^'\"]+)['\"]/);\n    const moduleName = moduleMatch?.[1] ?? moduleMatch?.[2];\n\n    if (moduleName === targetName || moduleName?.startsWith(`${targetName}/`)) {\n      const range = imp.range();\n      const start = range.start.index;\n      let end = range.end.index;\n      // Remove trailing newline if present\n      if (content[end] === '\\n') end++;\n      return content.slice(0, start) + content.slice(end);\n    }\n  }\n\n  return content;\n}\n\n/**\n * Pattern-based replacement using AST metavariables.\n * Pattern uses $VARNAME placeholders that match any AST node.\n * Replacement substitutes matched text back in.\n */\nexport function patternReplace(content: string, root: SgNode, pattern: string, replacement: string): TransformResult {\n  let modifiedContent = content;\n  let count = 0;\n\n  try {\n    const matches = root.findAll({ rule: { pattern } });\n    const replacements: Replacement[] = [];\n\n    // Extract metavariables from the pattern once (constant across all matches)\n    const metaVars = [...pattern.matchAll(/\\$(\\w+)/g)].map(m => m[1]).filter((v): v is string => v !== undefined);\n\n    for (const match of matches) {\n      const range = match.range();\n\n      // Build replacement text with variable substitution\n      let replacementText = replacement;\n      for (const varName of metaVars) {\n        const matchedNode = match.getMatch(varName);\n        if (matchedNode) {\n          replacementText = replacementText.replace(new RegExp(`\\\\$${varName}`, 'g'), matchedNode.text());\n        }\n      }\n\n      replacements.push({ start: range.start.index, end: range.end.index, text: replacementText });\n      count++;\n    }\n\n    replacements.sort((a, b) => b.start - a.start);\n\n    for (const { start, end, text } of replacements) {\n      modifiedContent = modifiedContent.slice(0, start) + text + modifiedContent.slice(end);\n    }\n  } catch (err) {\n    return {\n      content: modifiedContent,\n      count: 0,\n      error: err instanceof Error ? err.message : 'Pattern matching failed',\n    };\n  }\n\n  return { content: modifiedContent, count };\n}\n\n// =============================================================================\n// Tool Definition\n// =============================================================================\n\nexport const astEditTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.AST_EDIT,\n  description: `Edit code using AST-based analysis for intelligent transformations.\n\nUse \\`transform\\` for structured operations (imports, renames). Use \\`pattern\\`/\\`replacement\\` only for general find-and-replace.\n\nTransforms:\n- add-import: Add or merge imports. Skips duplicates. For default imports, put the default name first in \\`names\\`.\n  { transform: \"add-import\", importSpec: { module: \"react\", names: [\"useState\", \"useEffect\"] } }\n  { transform: \"add-import\", importSpec: { module: \"express\", names: [\"express\"], isDefault: true } }\n  { transform: \"add-import\", importSpec: { module: \"express\", names: [\"express\", \"Router\"], isDefault: true } } → import express, { Router } from 'express'\n- remove-import: Remove an import by module name.\n  { transform: \"remove-import\", targetName: \"lodash\" }\n- rename: Rename all occurrences of an identifier (not scope-aware).\n  { transform: \"rename\", targetName: \"oldName\", newName: \"newName\" }\n\nPattern replace (for everything else):\n  { pattern: \"console.log($ARG)\", replacement: \"logger.debug($ARG)\" }`,\n  inputSchema: z.object({\n    path: z.string().describe('The path to the file to edit'),\n    pattern: z\n      .string()\n      .optional()\n      .describe('AST pattern to search for (supports $VARIABLE placeholders, e.g., \"console.log($ARG)\")'),\n    replacement: z\n      .string()\n      .optional()\n      .describe('Replacement pattern (can use captured $VARIABLES, e.g., \"logger.debug($ARG)\")'),\n    transform: z\n      .enum(['add-import', 'remove-import', 'rename'])\n      .optional()\n      .describe('Structured transformation to apply'),\n    targetName: z\n      .string()\n      .optional()\n      .describe('Required for remove-import and rename transforms. The current name to target.'),\n    newName: z.string().optional().describe('Required for rename transform. The new name to replace targetName with.'),\n    importSpec: z\n      .object({\n        module: z.string().describe('Module to import from'),\n        names: z.array(z.string()).min(1).describe('Names to import. For default imports, put the default name first.'),\n        isDefault: z.boolean().optional().describe('Whether the first name is a default import'),\n      })\n      .optional()\n      .describe('Required for add-import transform. Specifies the module and names to import.'),\n  }),\n  execute: async ({ path, pattern, replacement, transform, targetName, newName, importSpec }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.AST_EDIT);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'astEdit',\n      input: { path, transform, pattern },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      if (filesystem.readOnly) {\n        throw new WorkspaceReadOnlyError('ast_edit');\n      }\n\n      // Load ast-grep (cached after first call)\n      const astGrep = await loadAstGrep();\n      if (!astGrep) {\n        span.end({ success: false });\n        return '@ast-grep/napi is not available. Install it to use AST editing.';\n      }\n      const { parse, Lang } = astGrep;\n\n      // Read current content\n      let content: string | Buffer;\n      try {\n        content = await filesystem.readFile(path, { encoding: 'utf-8' });\n      } catch (error) {\n        if (error instanceof FileNotFoundError) {\n          span.end({ success: false });\n          return `File not found: ${path}. Use the write file tool to create it first.`;\n        }\n        throw error;\n      }\n\n      if (typeof content !== 'string') {\n        span.end({ success: false });\n        return `Cannot perform AST edits on binary files. Use the write file tool instead.`;\n      }\n\n      // Parse AST\n      const lang = getLanguageFromPath(path, Lang);\n      if (!lang) {\n        span.end({ success: false });\n        return `Unsupported file type for AST editing: ${path}`;\n      }\n      const ast = parse(lang, content);\n      const root = ast.root();\n\n      let modifiedContent = content;\n      const changes: string[] = [];\n\n      if (transform) {\n        switch (transform) {\n          case 'add-import': {\n            if (!importSpec) {\n              span.end({ success: false });\n              return 'Error: importSpec is required for add-import transform';\n            }\n            modifiedContent = addImport(content, root, importSpec);\n            changes.push(`Added import from '${importSpec.module}'`);\n            break;\n          }\n\n          case 'remove-import': {\n            if (!targetName) {\n              span.end({ success: false });\n              return 'Error: targetName is required for remove-import transform';\n            }\n            modifiedContent = removeImport(content, root, targetName);\n            changes.push(`Removed import '${targetName}'`);\n            break;\n          }\n\n          case 'rename': {\n            if (!targetName || !newName) {\n              span.end({ success: false });\n              return 'Error: targetName and newName are required for rename transform';\n            }\n            const renameResult = renameIdentifiers(content, root, targetName, newName);\n            modifiedContent = renameResult.content;\n            changes.push(`Renamed '${targetName}' to '${newName}' (${renameResult.count} occurrences)`);\n            break;\n          }\n        }\n      } else if (pattern && replacement !== undefined) {\n        const result = patternReplace(content, root, pattern, replacement);\n        if (result.error) {\n          span.end({ success: false });\n          return `Error: AST pattern matching failed: ${result.error}`;\n        }\n        modifiedContent = result.content;\n        changes.push(`Replaced ${result.count} occurrences of pattern`);\n      } else if (pattern && replacement === undefined) {\n        span.end({ success: false });\n        return 'Error: replacement is required when pattern is provided';\n      } else if (!pattern && replacement !== undefined) {\n        span.end({ success: false });\n        return 'Error: pattern is required when replacement is provided';\n      } else {\n        span.end({ success: false });\n        return 'Error: Must provide either transform or pattern/replacement';\n      }\n\n      // Write back if modified\n      const wasModified = modifiedContent !== content;\n      if (wasModified) {\n        await filesystem.writeFile(path, modifiedContent, {\n          overwrite: true,\n          expectedMtime: (context as any)?.__expectedMtime,\n        });\n      }\n\n      if (!wasModified) {\n        span.end({ success: true });\n        return `No changes made to ${path} (${changes.join('; ')})`;\n      }\n\n      let output = `${path}: ${changes.join('; ')}`;\n      output += await getEditDiagnosticsText(workspace, path, modifiedContent);\n      span.end({ success: true }, { bytesTransferred: Buffer.byteLength(modifiedContent, 'utf-8') });\n      return output;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { WorkspaceReadOnlyError } from '../errors';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const deleteFileTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.DELETE,\n  description: 'Delete a file or directory from the workspace filesystem',\n  inputSchema: z.object({\n    path: z.string().describe('The path to the file or directory to delete'),\n    recursive: z\n      .boolean()\n      .optional()\n      .default(false)\n      .describe('If true, delete directories and their contents recursively. Required for non-empty directories.'),\n  }),\n  execute: async ({ path, recursive }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.DELETE);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'delete',\n      input: { path, recursive },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      if (filesystem.readOnly) {\n        throw new WorkspaceReadOnlyError('delete');\n      }\n\n      const stat = await filesystem.stat(path);\n      if (stat.type === 'directory') {\n        await filesystem.rmdir(path, { recursive, force: recursive });\n      } else {\n        await filesystem.deleteFile(path);\n      }\n\n      span.end({ success: true });\n      return `Deleted ${path}`;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { WorkspaceReadOnlyError } from '../errors';\nimport { charRangeToLineRange, replaceString, StringNotFoundError, StringNotUniqueError } from '../line-utils';\nimport { emitWorkspaceMetadata, getEditDiagnosticsText, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nfunction getEditedLineRanges(content: string, oldString: string, newString: string, replaceAll: boolean): string {\n  if (!oldString) return '';\n  const ranges: string[] = [];\n  let position = 0;\n\n  while ((position = content.indexOf(oldString, position)) !== -1) {\n    const range = charRangeToLineRange(content, position, position + Math.max(oldString.length, 1));\n    if (range) {\n      const newLineCount = newString.split('\\n').length;\n      const end = range.start + Math.max(newLineCount, range.end - range.start + 1) - 1;\n      ranges.push(end === range.start ? String(range.start) : `${range.start}-${end}`);\n    }\n    position += oldString.length;\n    if (!replaceAll) break;\n  }\n\n  if (ranges.length === 0) return '';\n  return ranges.length === 1 ? ` (lines ${ranges[0]})` : ` (lines ${ranges.join(', ')})`;\n}\n\nexport const editFileTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE,\n  description: `Edit a file by replacing specific text. The old_string must match exactly and be unique in the file.\n\nUsage:\n- Read the file first to get the exact text to replace.\n- By default, read file output includes line number prefixes (e.g., \"     1→\"). Ensure you preserve the exact indentation as it appears AFTER the arrow. Never include any part of the line number prefix in old_string or new_string.\n- Include enough surrounding context (multiple lines) to make old_string unique. If it still isn't unique, include more lines.\n- Use replace_all only when intentionally replacing all occurrences.`,\n  inputSchema: z.object({\n    path: z.string().describe('The path to the file to edit'),\n    old_string: z.string().describe('The exact text to find and replace. Must be unique in the file.'),\n    new_string: z.string().describe('The text to replace old_string with'),\n    replace_all: z\n      .boolean()\n      .optional()\n      .default(false)\n      .describe('If true, replace all occurrences. If false (default), old_string must be unique.'),\n  }),\n  execute: async ({ path, old_string, new_string, replace_all }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'editFile',\n      input: { path, replace_all },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      if (filesystem.readOnly) {\n        throw new WorkspaceReadOnlyError('edit_file');\n      }\n\n      const content = await filesystem.readFile(path, { encoding: 'utf-8' });\n\n      if (typeof content !== 'string') {\n        span.end({ success: false });\n        return `Cannot edit binary files. Use the write file tool instead.`;\n      }\n\n      const shouldReplaceAll = replace_all ?? false;\n      const lineRanges = getEditedLineRanges(content, old_string, new_string, shouldReplaceAll);\n      const result = replaceString(content, old_string, new_string, shouldReplaceAll);\n      await filesystem.writeFile(path, result.content, {\n        overwrite: true,\n        expectedMtime: (context as any)?.__expectedMtime,\n      });\n\n      let output = `Replaced ${result.replacements} occurrence${result.replacements !== 1 ? 's' : ''} in ${path}${lineRanges}`;\n      output += await getEditDiagnosticsText(workspace, path, result.content);\n      span.end({ success: true }, { bytesTransferred: Buffer.byteLength(result.content, 'utf-8') });\n      return output;\n    } catch (error) {\n      if (error instanceof StringNotFoundError) {\n        span.end({ success: false });\n        return error.message;\n      }\n      if (error instanceof StringNotUniqueError) {\n        span.end({ success: false });\n        return error.message;\n      }\n      span.error(error);\n      throw error;\n    }\n  },\n});\n","import { shellQuote, splitShellCommand, reassembleShellCommand } from '../workspace/sandbox/utils';\nimport type { MastraBrowser } from './browser';\n\n/**\n * Configuration for a browser CLI provider.\n */\nexport interface BrowserCliConfig {\n  /** Regex pattern to match the CLI command */\n  pattern: RegExp;\n  /** Flag used to pass CDP URL to the CLI */\n  flag: string;\n  /** Flag to pass threadId as session name for isolation */\n  sessionFlag?: string;\n  /** Command to run before other commands to establish CDP connection */\n  warmupCommand?: (cdpUrl: string, threadId: string) => string;\n  /** Pattern to detect agent-provided external CDP */\n  externalCdpPattern?: RegExp;\n  /** Pattern to extract the CDP URL from command (with capture group) */\n  externalCdpExtractor?: RegExp;\n}\n\n/**\n * CLI provider patterns for CDP URL injection.\n * Maps CLI command prefixes to their CDP URL flag.\n * All CLIs accept either port number or full WebSocket URL.\n * We use full URL for consistency.\n *\n * warmupCommand: Some CLIs (like agent-browser) need a \"connect\" command\n * to be run first to establish their daemon's CDP connection before other\n * commands will work properly.\n */\nconst CLI_CDP_PATTERNS: Record<string, BrowserCliConfig> = {\n  'agent-browser': {\n    pattern: /^agent-browser\\b/,\n    flag: '--cdp',\n    sessionFlag: '--session',\n    // agent-browser daemon needs explicit connect command to establish CDP connection\n    // Must include session flag to isolate threads\n    warmupCommand: (cdpUrl: string, threadId: string) =>\n      `agent-browser --session ${shellQuote(threadId)} connect ${shellQuote(cdpUrl)}`,\n    // agent-browser external CDP detection:\n    // - \"connect <url>\" subcommand for external CDP\n    // - \"--cdp <url>\" with full wss:// URL (not just port) also indicates external CDP\n    // External CDP: \"connect <url>\", \"--cdp <url>\", or \"--cdp <port>\"\n    externalCdpPattern: /(?:\\bconnect\\s+[\"']?wss?:\\/\\/|--cdp\\s+[\"']?\\S)/,\n    // Extract URL from: connect \"wss://...\" or --cdp \"wss://...\" (ports don't have extractable URLs)\n    externalCdpExtractor: /(?:\\bconnect|--cdp)\\s+[\"']?(wss?:\\/\\/[^\\s\"']+)[\"']?/,\n  },\n  'browser-use': {\n    // browser-use CLI installs as multiple aliases: browser, browseruse, bu\n    // The skill docs say \"browser-use\" but the primary binary is \"browser\"\n    // Order matters: longer matches first to avoid \"browser\" matching before \"browser-use\"\n    pattern: /^(?:browser-use|browseruse|browser|bu)\\b/,\n    flag: '--cdp-url',\n    sessionFlag: '--session',\n    // browser-use uses --cdp-url for external CDP\n    externalCdpPattern: /--cdp-url\\s+[\"']?\\S+/,\n    // Extract URL from: --cdp-url \"wss://...\" or --cdp-url 'wss://...' or --cdp-url wss://...\n    externalCdpExtractor: /--cdp-url\\s+[\"']?(wss?:\\/\\/[^\\s\"']+)[\"']?/,\n  },\n  browse: {\n    pattern: /^browse\\b/,\n    flag: '--ws',\n    // browse uses --ws for external CDP\n    externalCdpPattern: /--ws\\s+[\"']?\\S+/,\n    // Extract URL from: --ws \"wss://...\" or --ws 'wss://...' or --ws wss://...\n    externalCdpExtractor: /--ws\\s+[\"']?(wss?:\\/\\/[^\\s\"']+)[\"']?/,\n  },\n};\n\n/**\n * Result of processing a command for browser CLI handling.\n */\nexport interface BrowserCliProcessResult {\n  /** The (potentially modified) command to execute */\n  command: string;\n  /** Warmup commands that need to be run before the main command */\n  warmupCommands: string[];\n  /** Whether external CDP was detected (agent managing their own browser) */\n  usingExternalCdp: boolean;\n  /** External CDP URL if detected */\n  externalCdpUrl?: string;\n}\n\n/**\n * Handles browser CLI detection, CDP injection, and warmup for execute_command.\n * Centralizes all browser CLI-specific logic that was previously in execute-command.ts.\n */\nexport class BrowserCliHandler {\n  /**\n   * Track which CLI providers have been warmed up per browser instance and thread.\n   * Key format: `${browserId}:${cliName}:${threadId}`\n   * Browser ID scopes warmup state so different agents/workspaces don't share state.\n   * @internal Exposed for testing\n   */\n  warmedUpClis = new Set<string>();\n\n  /**\n   * Track cleanup callbacks for warmed up CLIs to avoid duplicate registrations.\n   * Key format: `${browserId}:${cliName}:${threadId}`\n   * @internal Exposed for testing\n   */\n  warmupCleanups = new Map<string, () => void>();\n\n  /**\n   * Build a warmup key scoped to browser instance.\n   */\n  private makeWarmupKey(browserId: string, cliName: string, threadId: string): string {\n    return `${browserId}:${cliName}:${threadId}`;\n  }\n\n  /**\n   * Check if a command is a browser CLI command and return its config.\n   */\n  getBrowserCliConfig(command: string): { name: string; config: BrowserCliConfig } | null {\n    for (const [name, config] of Object.entries(CLI_CDP_PATTERNS)) {\n      if (config.pattern.test(command)) {\n        return { name, config };\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Check if any browser CLI command already has a CDP flag specified.\n   * If so, the agent is managing their own CDP connection and we should skip injection.\n   */\n  hasExternalCdpFlag(parts: string[]): boolean {\n    for (const part of parts) {\n      const match = this.getBrowserCliConfig(part.trim());\n      if (match) {\n        // Use provider-specific pattern if available, otherwise fall back to flag pattern\n        if (match.config.externalCdpPattern) {\n          if (match.config.externalCdpPattern.test(part)) {\n            return true;\n          }\n        } else {\n          const flagPattern = new RegExp(`${match.config.flag}\\\\s+\\\\S+`);\n          if (flagPattern.test(part)) {\n            return true;\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n  /**\n   * Extract external CDP URL from command parts.\n   * Returns the first CDP URL found, or null if none.\n   */\n  extractExternalCdpUrl(parts: string[]): string | null {\n    for (const part of parts) {\n      const match = this.getBrowserCliConfig(part.trim());\n      if (match?.config.externalCdpExtractor) {\n        const urlMatch = part.match(match.config.externalCdpExtractor);\n        if (urlMatch?.[1]) {\n          return urlMatch[1];\n        }\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Inject CDP URL and session flag into a single browser CLI command.\n   * Returns the modified command or the original if no injection needed.\n   */\n  private injectCdpUrlIntoSingleCommand(\n    command: string,\n    cdpUrl: string,\n    config: BrowserCliConfig,\n    threadId?: string,\n  ): string {\n    // Check if CDP flag is already present\n    const flagPattern = new RegExp(`${config.flag}\\\\s+\\\\S+`);\n    if (flagPattern.test(command)) {\n      return command; // Already has CDP URL, don't override\n    }\n\n    // Build injection: CDP URL + session flag (for thread isolation)\n    // Use shell escaping to prevent command injection from crafted values\n    let injection = `${config.flag} ${shellQuote(cdpUrl)}`;\n    if (config.sessionFlag && threadId) {\n      // Check if session flag already present\n      const sessionPattern = new RegExp(`${config.sessionFlag}\\\\s+\\\\S+`);\n      if (!sessionPattern.test(command)) {\n        injection += ` ${config.sessionFlag} ${shellQuote(threadId)}`;\n      }\n    }\n\n    // Inject flags after the CLI command name\n    return command.replace(config.pattern, `$& ${injection}`);\n  }\n\n  /**\n   * Inject CDP URL and session flag into all browser CLI commands in a potentially\n   * chained command string (commands joined by &&, ||, or ;).\n   */\n  injectCdpUrl(command: string, cdpUrl: string, threadId?: string): string {\n    const { parts, operators } = splitShellCommand(command);\n\n    const modifiedParts = parts.map((part: string) => {\n      const trimmed = part.trim();\n      const cliMatch = this.getBrowserCliConfig(trimmed);\n      if (cliMatch) {\n        return this.injectCdpUrlIntoSingleCommand(trimmed, cdpUrl, cliMatch.config, threadId);\n      }\n      return part; // Keep original (preserves whitespace)\n    });\n\n    return reassembleShellCommand(modifiedParts, operators);\n  }\n\n  /**\n   * Check if a warmup has been completed for a browser/CLI/thread combination.\n   */\n  isWarmedUp(browserId: string, cliName: string, threadId: string): boolean {\n    return this.warmedUpClis.has(this.makeWarmupKey(browserId, cliName, threadId));\n  }\n\n  /**\n   * Mark a browser/CLI/thread combination as warmed up.\n   */\n  markWarmedUp(browserId: string, cliName: string, threadId: string): void {\n    this.warmedUpClis.add(this.makeWarmupKey(browserId, cliName, threadId));\n  }\n\n  /**\n   * Register a cleanup callback for when a browser closes.\n   * The cleanup will remove the warmup state for the given browser/CLI/thread.\n   */\n  registerWarmupCleanup(browserId: string, cliName: string, threadId: string, browser: MastraBrowser): void {\n    const warmupKey = this.makeWarmupKey(browserId, cliName, threadId);\n    if (!this.warmupCleanups.has(warmupKey)) {\n      const cleanup = browser.onBrowserClosed(() => {\n        this.warmedUpClis.delete(warmupKey);\n        this.warmupCleanups.delete(warmupKey);\n      }, threadId);\n      this.warmupCleanups.set(warmupKey, cleanup);\n    }\n  }\n\n  /**\n   * Get warmup commands that need to be run for the detected browser CLIs.\n   */\n  getWarmupCommands(\n    browserId: string,\n    browserClis: Array<{ name: string; config: BrowserCliConfig }>,\n    cdpUrl: string,\n    threadId: string,\n  ): Array<{ cliName: string; command: string }> {\n    const warmups: Array<{ cliName: string; command: string }> = [];\n    const seen = new Set<string>();\n\n    for (const { name: cliName, config: cliConfig } of browserClis) {\n      // Deduplicate: if same CLI appears multiple times in chained command, only warmup once\n      if (seen.has(cliName)) continue;\n      seen.add(cliName);\n\n      if (cliConfig.warmupCommand && !this.isWarmedUp(browserId, cliName, threadId)) {\n        warmups.push({\n          cliName,\n          command: cliConfig.warmupCommand(cdpUrl, threadId),\n        });\n      }\n    }\n\n    return warmups;\n  }\n\n  /**\n   * Process a command for browser CLI handling.\n   * Detects browser CLIs, checks for external CDP, and prepares injection.\n   *\n   * This is the main entry point - call this from execute-command.ts.\n   */\n  analyzeCommand(command: string): {\n    /** Detected browser CLIs in the command */\n    browserClis: Array<{ name: string; config: BrowserCliConfig }>;\n    /** Command parts split by shell operators */\n    parts: string[];\n    /** Whether external CDP was detected */\n    usingExternalCdp: boolean;\n    /** External CDP URL if detected */\n    externalCdpUrl: string | null;\n  } {\n    const { parts } = splitShellCommand(command);\n\n    const browserClis = parts\n      .map((part: string) => this.getBrowserCliConfig(part.trim()))\n      .filter((match): match is NonNullable<typeof match> => match !== null);\n\n    const usingExternalCdp = this.hasExternalCdpFlag(parts);\n    const externalCdpUrl = usingExternalCdp ? this.extractExternalCdpUrl(parts) : null;\n\n    return {\n      browserClis,\n      parts,\n      usingExternalCdp,\n      externalCdpUrl,\n    };\n  }\n}\n\n/**\n * Singleton instance for use across the application.\n * This preserves warmup state across command executions.\n */\nexport const browserCliHandler = new BrowserCliHandler();\n","import { estimateTokenCount, sliceByTokens } from 'tokenx';\n\n/** Default number of lines to return (tail). */\nexport const DEFAULT_TAIL_LINES = 200;\n\n/** Default estimated token limit for tool output. Safety net on top of line-based tail. */\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 2_000;\n\n// ---------------------------------------------------------------------------\n// ANSI stripping\n// ---------------------------------------------------------------------------\n\n/**\n * Strip ANSI escape codes from text.\n * Covers CSI sequences (colors, cursor), OSC sequences (hyperlinks), and C1 controls.\n * Based on the pattern from chalk/ansi-regex.\n */\n\n// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI control chars are intentional\nconst ANSI_RE =\n  /(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))|(?:[\\u001B\\u009B][\\[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~])/g;\n\nexport function stripAnsi(text: string): string {\n  return text.replace(ANSI_RE, '');\n}\n\n/**\n * `toModelOutput` handler for sandbox tools.\n * Strips ANSI escape codes so the model sees clean text, while the raw\n * output (with colors) is preserved in the stream/TUI.\n *\n * Returns `{ type: 'text', value: '...' }` to match the AI SDK's\n * expected tool-result output format.\n */\nexport function sandboxToModelOutput(output: unknown): unknown {\n  if (typeof output === 'string') {\n    return { type: 'text', value: stripAnsi(output) };\n  }\n  return output;\n}\n\n// ---------------------------------------------------------------------------\n// Tail (line-based truncation)\n// ---------------------------------------------------------------------------\n\n/**\n * Return the last N lines of output, similar to `tail -n`.\n * - `n > 0`: last N lines\n * - `n === 0`: no limit (return all)\n * - `undefined/null`: use DEFAULT_TAIL_LINES\n */\nexport function applyTail(output: string, tail: number | null | undefined): string {\n  if (!output) return output;\n  const n = Math.abs(tail ?? DEFAULT_TAIL_LINES);\n  if (n === 0) return output; // 0 = no limit\n  // Strip trailing newline before splitting so it doesn't count as a line\n  const trailingNewline = output.endsWith('\\n');\n  const lines = (trailingNewline ? output.slice(0, -1) : output).split('\\n');\n  if (lines.length <= n) return output;\n  const sliced = lines.slice(-n).join('\\n');\n  const body = trailingNewline ? sliced + '\\n' : sliced;\n  return `[showing last ${n} of ${lines.length} lines]\\n${body}`;\n}\n\n// ---------------------------------------------------------------------------\n// Token-based truncation (uses tokenx for fast, lightweight estimation)\n// ---------------------------------------------------------------------------\n\n/**\n * Token-based output limit. Truncates output to fit within a token budget.\n * Uses tokenx for fast token estimation and truncates at the token level\n * (not line boundaries) to maximise use of the budget.\n *\n * @param output - The text to truncate\n * @param limit - Maximum tokens (default: DEFAULT_MAX_OUTPUT_TOKENS)\n * @param from - Which end to truncate from:\n *   - `'start'` (default): Remove tokens from the start, keep the end\n *   - `'end'`: Remove tokens from the end, keep the start\n */\nexport async function applyTokenLimit(\n  output: string,\n  limit: number = DEFAULT_MAX_OUTPUT_TOKENS,\n  from: 'start' | 'end' = 'start',\n): Promise<string> {\n  if (!output) return output;\n\n  const totalTokens = estimateTokenCount(output);\n  if (totalTokens <= limit) return output;\n\n  const kept = from === 'start' ? sliceByTokens(output, -limit) : sliceByTokens(output, 0, limit);\n\n  const position = from === 'start' ? 'last' : 'first';\n  return from === 'start'\n    ? `[output truncated: showing ${position} ~${limit} of ~${totalTokens} tokens]\\n${kept}`\n    : `${kept}\\n[output truncated: showing ${position} ~${limit} of ~${totalTokens} tokens]`;\n}\n\n/**\n * Head+tail sandwich truncation. Keeps lines from both the start and end\n * of the output, with a truncation notice in the middle.\n * Uses tokenx for fast token estimation.\n *\n * @param output - The text to truncate\n * @param limit - Maximum tokens (default: DEFAULT_MAX_OUTPUT_TOKENS)\n * @param headRatio - Fraction of the token budget to allocate to the head (default: 0.1 = 10%)\n */\nexport async function applyTokenLimitSandwich(\n  output: string,\n  limit: number = DEFAULT_MAX_OUTPUT_TOKENS,\n  headRatio: number = 0.1,\n): Promise<string> {\n  if (!output) return output;\n\n  const totalTokens = estimateTokenCount(output);\n  if (totalTokens <= limit) return output;\n  const headBudget = Math.floor(limit * headRatio);\n  const tailBudget = limit - headBudget;\n\n  const head = headBudget > 0 ? sliceByTokens(output, 0, headBudget) : '';\n  const tail = tailBudget > 0 ? sliceByTokens(output, -tailBudget) : '';\n\n  const notice = `[...output truncated — showing first ~${headBudget} + last ~${tailBudget} of ~${totalTokens} tokens...]`;\n  return [head, notice, tail].filter(Boolean).join('\\n');\n}\n\n/**\n * Apply both tail (line-based) and token limit (safety net) to output.\n */\nexport async function truncateOutput(\n  output: string,\n  tail?: number | null,\n  tokenLimit?: number,\n  tokenFrom?: 'start' | 'end' | 'sandwich',\n): Promise<string> {\n  const tailed = applyTail(output, tail);\n  if (tokenFrom === 'sandwich') {\n    return applyTokenLimitSandwich(tailed, tokenLimit);\n  }\n  return applyTokenLimit(tailed, tokenLimit, tokenFrom);\n}\n","import { z } from 'zod/v4';\nimport { browserCliHandler } from '../../browser/cli-handler';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { SandboxFeatureNotSupportedError } from '../errors';\nimport { emitWorkspaceMetadata, requireSandbox } from './helpers';\nimport { DEFAULT_TAIL_LINES, truncateOutput, sandboxToModelOutput } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nconst NUMERIC_TIMEOUT_STRING_REGEX = /^\\d+(?:\\.\\d+)?$/;\n\n/**\n * Base input schema for execute_command (no background param).\n * Extended with `background` in tools.ts when sandbox.processes exists.\n */\nexport const executeCommandInputSchema = z.object({\n  command: z\n    .string()\n    .describe('The shell command to execute (e.g., \"npm install\", \"ls -la src/\", \"cat file.txt | grep error\")'),\n  timeout: z\n    .preprocess(value => {\n      if (typeof value !== 'string') {\n        return value;\n      }\n      const trimmed = value.trim();\n      return NUMERIC_TIMEOUT_STRING_REGEX.test(trimmed) ? Number(trimmed) : value;\n    }, z.number())\n    .nullish()\n    .describe('Maximum execution time in seconds. Example: 60 for 1 minute.'),\n  cwd: z.string().nullish().describe('Working directory for the command'),\n  tail: z\n    .number()\n    .nullish()\n    .describe(\n      `For foreground commands: limit output to the last N lines, similar to tail -n. Defaults to ${DEFAULT_TAIL_LINES}. Use 0 for no limit.`,\n    ),\n});\n\n/** Schema with background param included. */\nexport const executeCommandWithBackgroundSchema = executeCommandInputSchema.extend({\n  background: z\n    .boolean()\n    .optional()\n    .describe(\n      'Run the command in the background. Returns a PID immediately instead of waiting for completion. Use get_process_output to check on it later.',\n    ),\n});\n\n/**\n * Extract `| tail -N` or `| tail -n N` from the end of a command.\n * LLMs are trained to pipe to tail for long outputs, but this prevents streaming —\n * the user sees nothing until the command finishes. By stripping the tail pipe and\n * applying it programmatically afterward, all output streams in real time while\n * the final result sent to the model is still truncated.\n *\n * Returns the cleaned command and extracted tail line count (if any).\n */\nfunction extractTailPipe(command: string): { command: string; tail?: number } {\n  const match = command.match(/\\|\\s*tail\\s+(?:-n\\s+)?(-?\\d+)\\s*$/);\n  if (match) {\n    const lines = Math.abs(parseInt(match[1]!, 10));\n    if (lines > 0) {\n      return {\n        command: command.replace(/\\|\\s*tail\\s+(?:-n\\s+)?-?\\d+\\s*$/, '').trim(),\n        tail: lines,\n      };\n    }\n  }\n  return { command };\n}\n\n/** Shared execute function used by both foreground-only and background-capable tool variants. */\nasync function executeCommand(input: Record<string, any>, context: any) {\n  let { command, cwd, tail } = input;\n  const timeout = input.timeout != null ? (input.timeout as number) * 1000 : undefined;\n  const background = input.background as boolean | undefined;\n  const { workspace, sandbox } = requireSandbox(context);\n\n  // Extract tail pipe from command so output can stream in real time\n  if (!background) {\n    const extracted = extractTailPipe(command);\n    command = extracted.command;\n    // Extracted tail overrides schema tail param (explicit pipe intent takes priority)\n    if (extracted.tail != null) {\n      tail = extracted.tail;\n    }\n  }\n\n  // Lazy browser launch and CDP URL injection for browser CLI commands\n  const browser = workspace.browser;\n  const { browserClis, usingExternalCdp, externalCdpUrl } = browserCliHandler.analyzeCommand(command);\n\n  if (browser && browserClis.length > 0 && !usingExternalCdp) {\n    const threadId = context?.agent?.threadId ?? context?.threadId ?? 'default';\n\n    // Launch browser if not already running (for this thread if thread-scoped)\n    if (!browser.isBrowserRunning(threadId)) {\n      await browser.launch(threadId);\n    }\n\n    const cdpUrl = browser.getCdpUrl(threadId);\n    const browserId = browser.id;\n\n    if (cdpUrl) {\n      // Run warmup commands for CLIs that need them\n      const warmups = browserCliHandler.getWarmupCommands(browserId, browserClis, cdpUrl, threadId);\n      for (const { cliName, command: warmupCmd } of warmups) {\n        try {\n          if (sandbox.executeCommand) {\n            await sandbox.executeCommand(warmupCmd, [], { timeout: 10000 });\n          }\n          // Only mark as warmed up after successful warmup\n          browserCliHandler.markWarmedUp(browserId, cliName, threadId);\n          // Register cleanup when browser closes\n          browserCliHandler.registerWarmupCleanup(browserId, cliName, threadId, browser);\n        } catch {\n          // Don't mark as warmed up - will retry on next command\n          // This allows recovery if the CLI daemon wasn't ready\n        }\n      }\n\n      // Inject CDP URL into all browser CLI commands in the chain\n      command = browserCliHandler.injectCdpUrl(command, cdpUrl, threadId);\n    }\n  } else if (browser && browserClis.length > 0 && usingExternalCdp && externalCdpUrl) {\n    // Agent is using their own external CDP - connect BrowserViewer to it for screencast\n    const threadId = context?.agent?.threadId ?? context?.threadId ?? 'default';\n    try {\n      await browser.connectToExternalCdp(externalCdpUrl, threadId);\n    } catch {\n      // Non-fatal - agent can still use the external CDP, just no screencast\n    }\n  }\n\n  await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND);\n  const toolCallId = context?.agent?.toolCallId;\n  const toolConfig = workspace.getToolsConfig()?.[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND];\n  const tokenLimit = toolConfig?.maxOutputTokens;\n  const tokenFrom = 'sandwich' as const;\n\n  const span = startWorkspaceSpan(context, workspace, {\n    category: 'sandbox',\n    operation: background ? 'spawnProcess' : 'executeCommand',\n    input: { command, cwd, timeout: input.timeout, background },\n    attributes: { sandboxProvider: sandbox.provider },\n  });\n\n  // Background mode: spawn via process manager and return immediately\n  if (background) {\n    if (!sandbox.processes) {\n      const err = new SandboxFeatureNotSupportedError('processes');\n      span.error(err);\n      throw err;\n    }\n\n    const bgConfig = toolConfig?.backgroundProcesses;\n\n    // Resolve abort signal: undefined = use context signal (from agent), null/false = disabled\n    const bgAbortSignal =\n      bgConfig?.abortSignal === undefined ? context?.abortSignal : bgConfig.abortSignal || undefined;\n\n    // Use `let` so callbacks can reference handle.pid via closure.\n    // spawn() resolves before any data events fire (Node event loop guarantees this).\n    let handle: Awaited<ReturnType<typeof sandbox.processes.spawn>>;\n    handle = await sandbox.processes.spawn(command, {\n      cwd: cwd ?? undefined,\n      timeout: timeout ?? undefined,\n      abortSignal: bgAbortSignal,\n      onStdout: bgConfig?.onStdout\n        ? (data: string) => bgConfig.onStdout!(data, { pid: handle.pid, toolCallId })\n        : undefined,\n      onStderr: bgConfig?.onStderr\n        ? (data: string) => bgConfig.onStderr!(data, { pid: handle.pid, toolCallId })\n        : undefined,\n    });\n\n    // Wire exit callback (fire-and-forget)\n    if (bgConfig?.onExit) {\n      void handle.wait().then(result => {\n        bgConfig.onExit!({\n          pid: handle.pid,\n          exitCode: result.exitCode,\n          stdout: result.stdout,\n          stderr: result.stderr,\n          stdoutTruncated: result.stdoutTruncated,\n          stderrTruncated: result.stderrTruncated,\n          stdoutDroppedBytes: result.stdoutDroppedBytes,\n          stderrDroppedBytes: result.stderrDroppedBytes,\n          toolCallId,\n        });\n      });\n    }\n\n    span.end({ success: true }, { pid: Number(handle.pid) || undefined });\n    return `Started background process (PID: ${handle.pid})`;\n  }\n\n  // Foreground mode: execute and wait for completion\n  if (!sandbox.executeCommand) {\n    const err = new SandboxFeatureNotSupportedError('executeCommand');\n    span.error(err);\n    throw err;\n  }\n\n  const startedAt = Date.now();\n  let stdout = '';\n  let stderr = '';\n  try {\n    const result = await sandbox.executeCommand(command, [], {\n      timeout: timeout ?? undefined,\n      cwd: cwd ?? undefined,\n      abortSignal: context?.abortSignal, // foreground processes use agent's abort signal\n      onStdout: async (data: string) => {\n        stdout += data;\n        await context?.writer?.custom({\n          type: 'data-sandbox-stdout',\n          data: { output: data, timestamp: Date.now(), toolCallId },\n          transient: true,\n        });\n      },\n      onStderr: async (data: string) => {\n        stderr += data;\n        await context?.writer?.custom({\n          type: 'data-sandbox-stderr',\n          data: { output: data, timestamp: Date.now(), toolCallId },\n          transient: true,\n        });\n      },\n    });\n\n    await context?.writer?.custom({\n      type: 'data-sandbox-exit',\n      data: {\n        exitCode: result.exitCode,\n        success: result.success,\n        executionTimeMs: result.executionTimeMs,\n        toolCallId,\n      },\n    });\n\n    span.end({ success: result.success }, { exitCode: result.exitCode });\n\n    if (!result.success) {\n      const parts = [\n        await truncateOutput(result.stdout, tail, tokenLimit, tokenFrom),\n        await truncateOutput(result.stderr, tail, tokenLimit, tokenFrom),\n      ].filter(Boolean);\n      parts.push(`Exit code: ${result.exitCode}`);\n      return parts.join('\\n');\n    }\n\n    return (await truncateOutput(result.stdout, tail, tokenLimit, tokenFrom)) || '(no output)';\n  } catch (error) {\n    await context?.writer?.custom({\n      type: 'data-sandbox-exit',\n      data: {\n        exitCode: -1,\n        success: false,\n        executionTimeMs: Date.now() - startedAt,\n        toolCallId,\n      },\n    });\n    span.end({ success: false }, { exitCode: -1 });\n    const parts = [\n      await truncateOutput(stdout, tail, tokenLimit, tokenFrom),\n      await truncateOutput(stderr, tail, tokenLimit, tokenFrom),\n    ].filter(Boolean);\n    const errorMessage = error instanceof Error ? error.message : String(error);\n    parts.push(`Error: ${errorMessage}`);\n    return parts.join('\\n');\n  }\n}\n\nconst baseDescription = `Execute a shell command in the workspace sandbox.\n\nExamples:\n  \"npm install && npm run build\"\n  \"ls -la src/\"\n  \"cat config.json | jq '.database'\"\n  \"cd /app && python main.py\"\n\nUsage:\n- Commands run in a shell, so pipes, redirects, and chaining (&&, ||, ;) all work.\n- Always quote file paths that contain spaces (e.g., cd \"/path/with spaces\").\n- Use the timeout parameter (in seconds) to limit execution time. Behavior when omitted depends on the sandbox provider.\n- Optionally use cwd to override the working directory. Commands run from the sandbox default if omitted.`;\n\n/** Foreground-only tool (no background param in schema). */\nexport const executeCommandTool = createTool({\n  id: WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND,\n  description: baseDescription,\n  inputSchema: executeCommandInputSchema,\n  execute: executeCommand,\n  toModelOutput: sandboxToModelOutput,\n});\n\n/** Tool with background param in schema (used when sandbox.processes exists). */\nexport const executeCommandWithBackgroundTool = createTool({\n  id: WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND,\n  description: `${baseDescription}\n\nSet background: true to run long-running commands (dev servers, watchers) without blocking. You'll get a PID to track the process.`,\n  inputSchema: executeCommandWithBackgroundSchema,\n  execute: executeCommand,\n  toModelOutput: sandboxToModelOutput,\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { FileNotFoundError } from '../errors';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const fileStatTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT,\n  description:\n    'Get file or directory metadata from the workspace. Returns existence, type, size, and modification time.',\n  inputSchema: z.object({\n    path: z.string().describe('The path to check'),\n  }),\n  execute: async ({ path }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'stat',\n      input: { path },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      const stat = await filesystem.stat(path);\n      const modifiedAt = stat.modifiedAt.toISOString();\n\n      const parts = [`${path}`, `Type: ${stat.type}`];\n      if (stat.size !== undefined) parts.push(`Size: ${stat.size} bytes`);\n      parts.push(`Modified: ${modifiedAt}`);\n      span.end({ success: true }, { bytesTransferred: stat.size });\n      return parts.join(' ');\n    } catch (error) {\n      if (error instanceof FileNotFoundError) {\n        span.end({ success: false });\n        return `${path}: not found`;\n      }\n      span.error(error);\n      throw error;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { SandboxFeatureNotSupportedError } from '../errors';\nimport { emitWorkspaceMetadata, getDynamicSandboxCacheKeyHint, requireSandbox } from './helpers';\nimport { DEFAULT_TAIL_LINES, truncateOutput, sandboxToModelOutput } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const getProcessOutputTool = createTool({\n  id: WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT,\n  description: `Get the current output (stdout, stderr) and status of a background process by its PID.\n\nUse this after starting a background command with execute_command (background: true) to check if the process is still running and read its output.`,\n  toModelOutput: sandboxToModelOutput,\n  inputSchema: z.object({\n    pid: z.string().describe('The process ID returned when the background command was started'),\n    tail: z\n      .number()\n      .optional()\n      .describe(\n        `Number of lines to return, similar to tail -n. Positive or negative returns last N lines from end. Defaults to ${DEFAULT_TAIL_LINES}. Use 0 for no limit.`,\n      ),\n    wait: z\n      .boolean()\n      .optional()\n      .describe(\n        'If true, block until the process exits and return the final output. Useful for short-lived background commands where you want to wait for the result.',\n      ),\n  }),\n  execute: async ({ pid, tail, wait: shouldWait }, context) => {\n    const { workspace, sandbox } = requireSandbox(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'sandbox',\n      operation: 'getProcessOutput',\n      input: { pid, tail, wait: shouldWait },\n      attributes: { sandboxProvider: sandbox.provider },\n    });\n\n    const toolCallId = context?.agent?.toolCallId;\n\n    try {\n      if (!sandbox.processes) {\n        throw new SandboxFeatureNotSupportedError('processes');\n      }\n      const handle = await sandbox.processes.get(pid);\n      if (!handle) {\n        span.end({ success: false });\n        return `No background process found with PID ${pid}.${getDynamicSandboxCacheKeyHint(workspace)}`;\n      }\n\n      // Emit process info so the UI can display the command\n      if (handle.command) {\n        await context?.writer?.custom({\n          type: 'data-sandbox-command',\n          data: { command: handle.command, pid, toolCallId },\n        });\n      }\n\n      // If wait requested, block until process exits with streaming callbacks\n      if (shouldWait && handle.exitCode === undefined) {\n        const result = await handle.wait({\n          onStdout: context?.writer\n            ? async (data: string) => {\n                await context.writer!.custom({\n                  type: 'data-sandbox-stdout',\n                  data: { output: data, timestamp: Date.now(), toolCallId },\n                  transient: true,\n                });\n              }\n            : undefined,\n          onStderr: context?.writer\n            ? async (data: string) => {\n                await context.writer!.custom({\n                  type: 'data-sandbox-stderr',\n                  data: { output: data, timestamp: Date.now(), toolCallId },\n                  transient: true,\n                });\n              }\n            : undefined,\n        });\n\n        await context?.writer?.custom({\n          type: 'data-sandbox-exit',\n          data: {\n            exitCode: result.exitCode,\n            success: result.success,\n            executionTimeMs: result.executionTimeMs,\n            toolCallId,\n          },\n        });\n      }\n\n      const running = handle.exitCode === undefined;\n\n      const tokenLimit = workspace.getToolsConfig()?.[WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT]?.maxOutputTokens;\n      const stdout = await truncateOutput(handle.stdout, tail, tokenLimit, 'sandwich');\n      const stderr = await truncateOutput(handle.stderr, tail, tokenLimit, 'sandwich');\n\n      if (!stdout && !stderr) {\n        span.end({ success: true }, { exitCode: handle.exitCode });\n        return '(no output yet)';\n      }\n\n      const parts: string[] = [];\n\n      // Only label stdout/stderr when both are present\n      if (stdout && stderr) {\n        parts.push('stdout:', stdout, '', 'stderr:', stderr);\n      } else if (stdout) {\n        parts.push(stdout);\n      } else {\n        parts.push('stderr:', stderr);\n      }\n\n      if (!running) {\n        parts.push('', `Exit code: ${handle.exitCode}`);\n      }\n\n      span.end({ success: true }, { exitCode: handle.exitCode });\n      return parts.join('\\n');\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","/**\n * Gitignore support for workspace tools.\n *\n * Reads `.gitignore` from the workspace filesystem root and provides\n * a filter function that tools can use during directory walking.\n */\n\nimport ignore from 'ignore';\n\nimport type { WorkspaceFilesystem } from './filesystem';\n\nexport type IgnoreFilter = (relativePath: string) => boolean;\n\n/**\n * Load `.gitignore` from the workspace root and return a filter function.\n *\n * The returned function takes a path relative to the workspace root and\n * returns `true` if the path is ignored (should be skipped).\n *\n * Returns `undefined` if no `.gitignore` exists or it can't be read.\n */\nexport async function loadGitignore(filesystem: WorkspaceFilesystem): Promise<IgnoreFilter | undefined> {\n  let content: string;\n  try {\n    const raw = await filesystem.readFile('.gitignore', { encoding: 'utf-8' });\n    if (typeof raw !== 'string' || !raw.trim()) return undefined;\n    content = raw;\n  } catch {\n    return undefined;\n  }\n\n  const ig = ignore().add(content);\n\n  return (relativePath: string): boolean => {\n    // The `ignore` package expects paths without leading './' or '/'\n    const normalized = relativePath.replace(/^\\.\\//, '').replace(/^\\//, '');\n    if (!normalized) return false;\n    return ig.ignores(normalized);\n  };\n}\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { isTextFile } from '../filesystem/fs-utils';\nimport { loadGitignore } from '../gitignore';\nimport type { GlobMatcher } from '../glob';\nimport { createGlobMatcher, extractGlobBase, isGlobPattern } from '../glob';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { applyTokenLimit } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const grepTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.GREP,\n  description: `Search file contents using a regex pattern. Walks the filesystem and returns matching lines with file paths and line numbers.\n\nUsage:\n- Basic search: { pattern: \"TODO\" }\n- Regex: { pattern: \"function\\\\s+\\\\w+\\\\(\" }\n- Multiple terms: { pattern: \"TODO|FIXME|HACK\" }\n- Case-insensitive: { pattern: \"error\", caseSensitive: false }\n- Search in directory: { pattern: \"import\", path: \"./src\" }\n- Filter by glob: { pattern: \"import\", path: \"**/*.ts\" }\n- Combined path + glob: { pattern: \"import\", path: \"src/**/*.ts\" }\n- Multiple file types: { pattern: \"import\", path: \"**/*.{ts,tsx,js}\" }\n- Multiple directories: { pattern: \"TODO\", path: \"{src,lib}/**/*.ts\" }\n- With context: { pattern: \"function\", contextLines: 2 }`,\n  inputSchema: z.object({\n    pattern: z.string().describe('Regex pattern to search for'),\n    path: z\n      .string()\n      .optional()\n      .default('.')\n      .describe(\n        'File, directory, or glob pattern to search within (default: \".\"). ' +\n          'A plain path searches that file or directory. ' +\n          'A glob pattern (e.g., \"**/*.ts\", \"src/**/*.test.ts\") filters which files to search.',\n      ),\n    contextLines: z\n      .number()\n      .optional()\n      .default(0)\n      .describe('Number of lines of context to include before and after each match (default: 0)'),\n    maxCount: z\n      .number()\n      .optional()\n      .describe(\n        'Maximum matches per file. Moves on to the next file after this many matches. Similar to grep -m flag.',\n      ),\n    caseSensitive: z\n      .boolean()\n      .optional()\n      .default(true)\n      .describe('Whether the search is case-sensitive (default: true)'),\n    includeHidden: z\n      .boolean()\n      .optional()\n      .default(false)\n      .describe('Include hidden files and directories (names starting with \".\") in the search (default: false)'),\n  }),\n  execute: async (\n    { pattern, path: inputPath = '.', contextLines = 0, maxCount, caseSensitive = true, includeHidden = false },\n    context,\n  ) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.GREP);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'grep',\n      input: { pattern, path: inputPath, contextLines, maxCount },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      // Guard against excessively long patterns as a cheap ReDoS heuristic\n      const MAX_PATTERN_LENGTH = 1000;\n      if (pattern.length > MAX_PATTERN_LENGTH) {\n        span.end({ success: false });\n        return `Error: Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`;\n      }\n\n      // Validate regex\n      let regex: RegExp;\n      try {\n        regex = new RegExp(pattern, caseSensitive ? 'g' : 'gi');\n      } catch (e) {\n        span.end({ success: false });\n        return `Error: Invalid regex pattern: ${(e as Error).message}`;\n      }\n\n      // Determine search root and glob filter from the combined path parameter\n      let searchPath: string;\n      let globMatcher: GlobMatcher | undefined;\n\n      if (isGlobPattern(inputPath)) {\n        // Path contains glob characters — extract the static base as search root\n        searchPath = extractGlobBase(inputPath);\n        globMatcher = createGlobMatcher(inputPath, { dot: includeHidden });\n      } else {\n        searchPath = inputPath;\n      }\n\n      // Load gitignore filter.\n      // If the user explicitly targets a gitignored path (e.g. \"./dist\"), skip\n      // filtering so they can still search there. Otherwise apply as normal.\n      const rawIgnoreFilter = await loadGitignore(filesystem);\n      const searchPathNormalized = searchPath.replace(/^\\.\\//, '').replace(/\\/$/, '');\n      const targetIsIgnored = rawIgnoreFilter && searchPathNormalized && rawIgnoreFilter(searchPathNormalized + '/');\n      const ignoreFilter = targetIsIgnored ? undefined : rawIgnoreFilter;\n\n      // Collect files to search\n      let filePaths: string[];\n\n      // Never search inside .git even when explicitly targeted\n      const normalizedSearch = searchPath.replace(/\\/$/, '');\n      if (normalizedSearch === '.git' || normalizedSearch.endsWith('/.git')) {\n        filePaths = [];\n      } else {\n        // Check if searchPath is a file or directory\n        try {\n          const stat = await filesystem.stat(searchPath);\n          if (stat.type === 'file') {\n            // Single file — search it directly\n            filePaths = isTextFile(searchPath) ? [searchPath] : [];\n          } else {\n            // Directory — walk recursively\n            const collectFiles = async (dir: string): Promise<string[]> => {\n              const files: string[] = [];\n              let entries;\n              try {\n                entries = await filesystem.readdir(dir);\n              } catch {\n                return files;\n              }\n\n              for (const entry of entries) {\n                // Always skip .git directory — its internals are never useful and waste tokens\n                if (entry.type === 'directory' && entry.name === '.git') continue;\n\n                // Skip hidden files/dirs unless includeHidden is set\n                if (!includeHidden && entry.name.startsWith('.')) continue;\n\n                const fullPath = dir.endsWith('/') ? `${dir}${entry.name}` : `${dir}/${entry.name}`;\n\n                // Skip gitignored paths\n                if (ignoreFilter) {\n                  const relativePath = fullPath.replace(/^\\.\\//, '');\n                  const checkPath = entry.type === 'directory' ? `${relativePath}/` : relativePath;\n                  if (ignoreFilter(checkPath)) continue;\n                }\n\n                if (entry.type === 'file') {\n                  // Skip non-text files\n                  if (!isTextFile(entry.name)) continue;\n                  // Apply glob filter (createGlobMatcher normalizes leading slashes)\n                  if (globMatcher && !globMatcher(fullPath)) continue;\n                  files.push(fullPath);\n                } else if (entry.type === 'directory' && !entry.isSymlink) {\n                  files.push(...(await collectFiles(fullPath)));\n                }\n              }\n              return files;\n            };\n            filePaths = await collectFiles(searchPath);\n          }\n        } catch {\n          // Path doesn't exist\n          filePaths = [];\n        }\n      }\n\n      const outputLines: string[] = [];\n      const filesWithMatches = new Set<string>();\n      let totalMatchCount = 0;\n      let truncated = false;\n      const MAX_LINE_LENGTH = 500;\n      const GLOBAL_CAP = 1000;\n      const normalizedContextLines = Math.max(0, Math.floor(contextLines));\n      let emittedContextHunk = false;\n\n      for (const filePath of filePaths) {\n        if (truncated) break;\n\n        let content: string;\n        try {\n          const raw = await filesystem.readFile(filePath, { encoding: 'utf-8' });\n          if (typeof raw !== 'string') continue;\n          content = raw;\n        } catch {\n          continue;\n        }\n\n        const lines = content.split('\\n');\n        let fileMatchCount = 0;\n        const fileMatches: Array<{ lineIndex: number; columnIndex: number }> = [];\n\n        for (let i = 0; i < lines.length; i++) {\n          const currentLine = lines[i]!;\n          // Reset regex lastIndex for each line since we use 'g' flag\n          regex.lastIndex = 0;\n          const lineMatch = regex.exec(currentLine);\n          if (!lineMatch) continue;\n\n          filesWithMatches.add(filePath);\n\n          fileMatches.push({ lineIndex: i, columnIndex: lineMatch.index });\n\n          totalMatchCount++;\n          fileMatchCount++;\n\n          // Per-file limit (like grep -m)\n          if (maxCount !== undefined && fileMatchCount >= maxCount) break;\n\n          // Global cap to protect context window\n          if (totalMatchCount >= GLOBAL_CAP) {\n            truncated = true;\n            break;\n          }\n        }\n\n        if (normalizedContextLines > 0) {\n          const hunks: Array<{\n            start: number;\n            end: number;\n            matchesByLine: Map<number, number>;\n          }> = [];\n\n          for (const match of fileMatches) {\n            const start = Math.max(0, match.lineIndex - normalizedContextLines);\n            const end = Math.min(lines.length - 1, match.lineIndex + normalizedContextLines);\n            const previousHunk = hunks[hunks.length - 1];\n\n            if (previousHunk && start <= previousHunk.end + 1) {\n              previousHunk.end = Math.max(previousHunk.end, end);\n              previousHunk.matchesByLine.set(match.lineIndex, match.columnIndex);\n            } else {\n              hunks.push({\n                start,\n                end,\n                matchesByLine: new Map([[match.lineIndex, match.columnIndex]]),\n              });\n            }\n          }\n\n          for (const hunk of hunks) {\n            if (emittedContextHunk) {\n              outputLines.push('--');\n            }\n            emittedContextHunk = true;\n\n            for (let i = hunk.start; i <= hunk.end; i++) {\n              const columnIndex = hunk.matchesByLine.get(i);\n\n              if (columnIndex !== undefined) {\n                let lineContent = lines[i]!;\n                if (lineContent.length > MAX_LINE_LENGTH) {\n                  lineContent = lineContent.slice(0, MAX_LINE_LENGTH) + '...';\n                }\n                outputLines.push(`${filePath}:${i + 1}:${columnIndex + 1}: ${lineContent}`);\n              } else {\n                outputLines.push(`${filePath}:${i + 1}- ${lines[i]}`);\n              }\n            }\n          }\n        } else {\n          for (const match of fileMatches) {\n            let lineContent = lines[match.lineIndex]!;\n            if (lineContent.length > MAX_LINE_LENGTH) {\n              lineContent = lineContent.slice(0, MAX_LINE_LENGTH) + '...';\n            }\n            outputLines.push(`${filePath}:${match.lineIndex + 1}:${match.columnIndex + 1}: ${lineContent}`);\n          }\n        }\n      }\n\n      // Summary line — placed at the top so it's always visible after truncation\n      const summaryParts = [`${totalMatchCount} match${totalMatchCount !== 1 ? 'es' : ''}`];\n      summaryParts.push(`across ${filesWithMatches.size} file${filesWithMatches.size !== 1 ? 's' : ''}`);\n      if (truncated) {\n        summaryParts.push(`(truncated at ${GLOBAL_CAP})`);\n      }\n      const summary = summaryParts.join(' ');\n      outputLines.unshift(summary, '---');\n\n      const output = await applyTokenLimit(\n        outputLines.join('\\n'),\n        workspace.getToolsConfig()?.[WORKSPACE_TOOLS.FILESYSTEM.GREP]?.maxOutputTokens,\n        'end',\n      );\n      span.end({ success: true }, { resultCount: totalMatchCount });\n      return output;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { emitWorkspaceMetadata, requireWorkspace } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const indexContentTool = createTool({\n  id: WORKSPACE_TOOLS.SEARCH.INDEX,\n  description: 'Index content for search. The path becomes the document ID in search results.',\n  inputSchema: z.object({\n    path: z.string().describe('The document ID/path for search results'),\n    content: z.string().describe('The text content to index'),\n    metadata: z.record(z.string(), z.unknown()).optional().describe('Optional metadata to store with the document'),\n  }),\n  execute: async ({ path, content, metadata }, context) => {\n    const workspace = requireWorkspace(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.SEARCH.INDEX);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'search',\n      operation: 'index',\n      input: { path, contentLength: content.length },\n      attributes: {},\n    });\n\n    try {\n      await workspace.index(path, content, { metadata });\n      span.end({ success: true }, { bytesTransferred: Buffer.byteLength(content, 'utf-8') });\n      return `Indexed ${path}`;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { SandboxFeatureNotSupportedError } from '../errors';\nimport { emitWorkspaceMetadata, getDynamicSandboxCacheKeyHint, requireSandbox } from './helpers';\nimport { truncateOutput, sandboxToModelOutput } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nconst KILL_TAIL_LINES = 50;\n\nexport const killProcessTool = createTool({\n  id: WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS,\n  description: `Kill a background process by its PID.\n\nUse this to stop a long-running background process that was started with execute_command (background: true). Returns the last ${KILL_TAIL_LINES} lines of output.`,\n  toModelOutput: sandboxToModelOutput,\n  inputSchema: z.object({\n    pid: z.string().describe('The process ID of the background process to kill'),\n  }),\n  execute: async ({ pid }, context) => {\n    const { workspace, sandbox } = requireSandbox(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'sandbox',\n      operation: 'killProcess',\n      input: { pid },\n      attributes: { sandboxProvider: sandbox.provider },\n    });\n\n    const toolCallId = context?.agent?.toolCallId;\n\n    try {\n      if (!sandbox.processes) {\n        throw new SandboxFeatureNotSupportedError('processes');\n      }\n      // Snapshot output before kill\n      const handle = await sandbox.processes.get(pid);\n\n      // Emit command info so the UI can display the original command\n      if (handle?.command) {\n        await context?.writer?.custom({\n          type: 'data-sandbox-command',\n          data: { command: handle.command, pid, toolCallId },\n        });\n      }\n\n      const killed = await sandbox.processes.kill(pid);\n\n      if (!killed) {\n        await context?.writer?.custom({\n          type: 'data-sandbox-exit',\n          data: { exitCode: handle?.exitCode ?? -1, success: false, killed: false, toolCallId },\n        });\n        span.end({ success: false });\n        return `Process ${pid} was not found or had already exited.${getDynamicSandboxCacheKeyHint(workspace)}`;\n      }\n\n      await context?.writer?.custom({\n        type: 'data-sandbox-exit',\n        data: { exitCode: handle?.exitCode ?? 137, success: false, killed: true, toolCallId },\n      });\n\n      const parts: string[] = [`Process ${pid} has been killed.`];\n\n      if (handle) {\n        const tokenLimit = workspace.getToolsConfig()?.[WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS]?.maxOutputTokens;\n        const stdout = handle.stdout\n          ? await truncateOutput(handle.stdout, KILL_TAIL_LINES, tokenLimit, 'sandwich')\n          : '';\n        const stderr = handle.stderr\n          ? await truncateOutput(handle.stderr, KILL_TAIL_LINES, tokenLimit, 'sandwich')\n          : '';\n\n        if (stdout) {\n          parts.push('', '--- stdout (last output) ---', stdout);\n        }\n        if (stderr) {\n          parts.push('', '--- stderr (last output) ---', stderr);\n        }\n      }\n\n      span.end({ success: true }, { exitCode: handle?.exitCode ?? 137 });\n      return parts.join('\\n');\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","/**\n * Tree Formatter\n *\n * Formats directory structures as ASCII tree output.\n * Works with any WorkspaceFilesystem implementation.\n *\n * @example\n * ```typescript\n * import { formatAsTree } from './tree-formatter';\n *\n * const result = await formatAsTree(filesystem, '/', { maxDepth: 3 });\n * console.log(result.tree);\n * // .\n * // src\n * //   index.ts\n * //   utils\n * //     helpers.ts\n * // package.json\n * console.log(result.summary);\n * // \"2 directories, 3 files\"\n * ```\n */\n\nimport type { WorkspaceFilesystem, FileEntry } from '../filesystem';\nimport type { IgnoreFilter } from '../gitignore';\nimport { loadGitignore } from '../gitignore';\nimport { createGlobMatcher } from '../glob';\nimport type { GlobMatcher } from '../glob';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TreeOptions {\n  /** Maximum recursion depth (default: Infinity). Similar to tree's -L flag. */\n  maxDepth?: number;\n  /** Show hidden files/directories starting with '.' (default: false). Similar to tree's -a flag. */\n  showHidden?: boolean;\n  /** List directories only, no files (default: false). Similar to tree's -d flag. */\n  dirsOnly?: boolean;\n  /** Pattern to exclude from listing (e.g., 'node_modules'). Similar to tree's -I flag. */\n  exclude?: string | string[];\n  /** Filter by file extension (e.g., '.ts'). Similar to tree's -P flag. */\n  extension?: string | string[];\n  /** Glob pattern(s) to filter files. Matches against paths relative to the listed directory. Directories always pass through so their contents can be checked. */\n  pattern?: string | string[];\n  /** Filter function that returns true if a relative path should be ignored (e.g., from .gitignore). */\n  ignoreFilter?: IgnoreFilter;\n  /** Respect .gitignore entries in the listed directory (default: true). */\n  respectGitignore?: boolean;\n}\n\nexport interface TreeResult {\n  /** ASCII tree representation */\n  tree: string;\n  /** Human-readable summary (e.g., \"3 directories, 12 files\") */\n  summary: string;\n  /** Number of directories found */\n  dirCount: number;\n  /** Number of files found */\n  fileCount: number;\n  /** Whether output was truncated due to maxDepth */\n  truncated: boolean;\n  /** Relative paths for compact output */\n  paths: string[];\n}\n\n// =============================================================================\n// Tree Formatting\n// =============================================================================\n\n/**\n * Format a directory as an ASCII tree.\n *\n * @param fs - WorkspaceFilesystem implementation\n * @param path - Root path to format\n * @param options - Formatting options\n * @returns Tree result with formatted string and counts\n */\nexport async function formatAsTree(fs: WorkspaceFilesystem, path: string, options?: TreeOptions): Promise<TreeResult> {\n  const maxDepth = options?.maxDepth ?? Infinity;\n  const showHidden = options?.showHidden ?? false;\n  const dirsOnly = options?.dirsOnly ?? false;\n  const exclude = options?.exclude;\n  const extension = options?.extension;\n  const pattern = options?.pattern;\n  const respectGitignore = options?.respectGitignore ?? true;\n\n  // Use provided ignoreFilter, or load from .gitignore if respectGitignore is enabled.\n  // If the user explicitly targets an ignored path (e.g. \"/dist\"), skip filtering\n  // so they can still list there.\n  let ignoreFilter = options?.ignoreFilter;\n  if (!ignoreFilter && respectGitignore) {\n    const rawFilter = await loadGitignore(fs);\n    if (rawFilter) {\n      const normalizedPath = path.replace(/^\\.\\//, '').replace(/^\\//, '').replace(/\\/$/, '');\n      const targetIsIgnored = normalizedPath && rawFilter(normalizedPath + '/');\n      ignoreFilter = targetIsIgnored ? undefined : rawFilter;\n    }\n  }\n\n  // Compile glob matcher once before the walk (if pattern provided)\n  let globMatcher: GlobMatcher | undefined;\n  if (pattern) {\n    const patterns = Array.isArray(pattern) ? pattern : [pattern];\n    globMatcher = createGlobMatcher(patterns, { dot: showHidden });\n  }\n\n  const lines: string[] = ['.'];\n  const paths: string[] = [];\n  let dirCount = 0;\n  let fileCount = 0;\n  let truncated = false;\n\n  /**\n   * Build tree recursively using tab indentation\n   */\n  async function buildTree(currentPath: string, depth: number): Promise<void> {\n    // Never descend into .git even when explicitly targeted as root\n    const normalizedCurrentPath = currentPath.replace(/\\/$/, '');\n    if (normalizedCurrentPath === '.git' || normalizedCurrentPath.endsWith('/.git')) {\n      return;\n    }\n\n    if (depth >= maxDepth) {\n      truncated = true;\n      return;\n    }\n\n    let entries: FileEntry[];\n    try {\n      entries = await fs.readdir(currentPath);\n    } catch (error) {\n      // At root level (depth 0), propagate errors so users see auth/access issues\n      // For subdirectories, silently skip (permission issues on nested dirs are common)\n      if (depth === 0) {\n        throw error;\n      }\n      return;\n    }\n\n    // Filter entries\n    let filtered = entries;\n\n    // Always skip .git directory — its internals are never useful and waste tokens.\n    // This is applied before any other filtering so we never traverse into .git.\n    filtered = filtered.filter(e => !(e.type === 'directory' && e.name === '.git'));\n\n    // Filter hidden files unless showHidden\n    if (!showHidden) {\n      filtered = filtered.filter(e => !e.name.startsWith('.'));\n    }\n\n    // Filter by exclude pattern (like tree's -I flag, supports pipe-separated patterns)\n    if (exclude) {\n      const patterns = Array.isArray(exclude)\n        ? exclude\n        : exclude\n            .split('|')\n            .map(p => p.trim())\n            .filter(Boolean);\n      filtered = filtered.filter(e => {\n        return !patterns.some(pattern => e.name.includes(pattern));\n      });\n    }\n\n    // Filter by gitignore rules (paths must be relative to workspace root, not listing root)\n    if (ignoreFilter) {\n      filtered = filtered.filter(e => {\n        const relativePath = getRelativePath('', currentPath, e.name);\n        // Append trailing slash for directories so gitignore dir patterns match\n        const checkPath = e.type === 'directory' ? `${relativePath}/` : relativePath;\n        return !ignoreFilter(checkPath);\n      });\n    }\n\n    // Filter to directories only (like tree's -d flag)\n    if (dirsOnly) {\n      filtered = filtered.filter(e => e.type === 'directory');\n    }\n\n    // Filter by extension (only affects files, directories always pass)\n    if (extension && !dirsOnly) {\n      const extensions = Array.isArray(extension) ? extension : [extension];\n      filtered = filtered.filter(e => {\n        if (e.type === 'directory') return true;\n        return extensions.some(ext => {\n          // Support both '.ts' and 'ts' formats\n          const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;\n          return e.name.endsWith(normalizedExt);\n        });\n      });\n    }\n\n    // Filter by glob pattern (only affects files, directories always pass)\n    if (globMatcher && !dirsOnly) {\n      filtered = filtered.filter(e => {\n        if (e.type === 'directory') return true;\n        const relativePath = getRelativePath(path, currentPath, e.name);\n        return globMatcher(relativePath);\n      });\n    }\n\n    // Sort: directories first, then alphabetically\n    filtered.sort((a, b) => {\n      if (a.type === 'directory' && b.type !== 'directory') return -1;\n      if (a.type !== 'directory' && b.type === 'directory') return 1;\n      return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n    });\n\n    const indent = '\\t'.repeat(depth);\n\n    for (let i = 0; i < filtered.length; i++) {\n      const entry = filtered[i]!;\n\n      // Format entry name, including symlink target if present\n      const displayName =\n        entry.isSymlink && entry.symlinkTarget ? `${entry.name} -> ${entry.symlinkTarget}` : entry.name;\n\n      lines.push(`${indent}${displayName}`);\n      paths.push(getRelativePath(path, currentPath, entry.name));\n\n      if (entry.type === 'directory') {\n        dirCount++;\n        // Don't recurse into symlinks (matches native tree behavior)\n        // This also prevents infinite loops from circular symlinks\n        if (!entry.isSymlink) {\n          const childPath = joinPath(currentPath, entry.name);\n          await buildTree(childPath, depth + 1);\n        }\n      } else {\n        fileCount++;\n      }\n    }\n  }\n\n  await buildTree(path, 0);\n\n  // Build summary\n  const dirPart = dirCount === 1 ? '1 directory' : `${dirCount} directories`;\n  const filePart = fileCount === 1 ? '1 file' : `${fileCount} files`;\n  let summary = `${dirPart}, ${filePart}`;\n  if (truncated) {\n    summary += ` (truncated at depth ${maxDepth})`;\n  }\n\n  return {\n    tree: lines.join('\\n'),\n    summary,\n    dirCount,\n    fileCount,\n    truncated,\n    paths,\n  };\n}\n\n/**\n * Format entries directly (without filesystem access).\n * Useful when you already have the entries and want tree output.\n *\n * @param entries - Flat list of entries with path-like names (e.g., \"dir/subdir/file.txt\")\n * @returns Formatted tree string\n */\nexport function formatEntriesAsTree(entries: Array<{ name: string; type: 'file' | 'directory' }>): string {\n  // Build a nested structure from flat paths\n  interface TreeNode {\n    name: string;\n    type: 'file' | 'directory';\n    children: Map<string, TreeNode>;\n  }\n\n  const root: TreeNode = { name: '.', type: 'directory', children: new Map() };\n\n  for (const entry of entries) {\n    const parts = entry.name.split('/');\n    let current = root;\n\n    for (let i = 0; i < parts.length; i++) {\n      const part = parts[i]!;\n      const isLastPart = i === parts.length - 1;\n\n      if (!current.children.has(part)) {\n        current.children.set(part, {\n          name: part,\n          type: isLastPart ? entry.type : 'directory',\n          children: new Map(),\n        });\n      }\n      current = current.children.get(part)!;\n    }\n  }\n\n  // Render tree\n  const lines: string[] = ['.'];\n\n  function renderNode(node: TreeNode, depth: number): void {\n    const children = Array.from(node.children.values());\n    // Sort: directories first, then alphabetically\n    children.sort((a, b) => {\n      if (a.type === 'directory' && b.type !== 'directory') return -1;\n      if (a.type !== 'directory' && b.type === 'directory') return 1;\n      return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n    });\n\n    const indent = '\\t'.repeat(depth);\n\n    for (let i = 0; i < children.length; i++) {\n      const child = children[i]!;\n\n      lines.push(`${indent}${child.name}`);\n\n      if (child.children.size > 0) {\n        renderNode(child, depth + 1);\n      }\n    }\n  }\n\n  renderNode(root, 0);\n  return lines.join('\\n');\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction getRelativePath(rootPath: string, currentPath: string, entryName: string): string {\n  const isRootEquivalent = (p: string) => p === '/' || p === '' || p === '.';\n  const entryPath =\n    currentPath === rootPath || (isRootEquivalent(currentPath) && isRootEquivalent(rootPath))\n      ? entryName\n      : `${currentPath === '/' ? '' : currentPath}/${entryName}`;\n\n  if (isRootEquivalent(rootPath)) {\n    // Strip leading './' or '/' so callers always get a clean relative path\n    const cleaned = entryPath.replace(/^\\.\\//, '');\n    return cleaned.startsWith('/') ? cleaned.slice(1) : cleaned;\n  }\n\n  const relativePath = entryPath.startsWith(rootPath + '/') ? entryPath.slice(rootPath.length + 1) : entryPath;\n  return relativePath || entryPath;\n}\n\n/**\n * Join path segments, handling root paths correctly\n */\nfunction joinPath(base: string, name: string): string {\n  if (base === '' || base === './' || base === '.') {\n    return name;\n  }\n  if (base === '/') {\n    return `/${name}`;\n  }\n  return `${base}/${name}`;\n}\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { applyTokenLimit } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\nimport { formatAsTree } from './tree-formatter';\n\nexport const listFilesTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES,\n  description: `List files and directories in the workspace filesystem.\nReturns a compact tab-indented listing for efficient token usage.\nOptions mirror common tree command flags for familiarity.\n\nExamples:\n- List workspace root: { path: \".\" }\n- Deep listing: { path: \"src\", maxDepth: 5 }\n- Directories only: { path: \".\", dirsOnly: true }\n- Exclude node_modules: { path: \".\", exclude: \"node_modules\" }\n- Find TypeScript files: { path: \"src\", pattern: \"**/*.ts\" }\n- Find config files: { path: \".\", pattern: \"*.config.{js,ts}\" }\n- Multiple patterns: { path: \".\", pattern: [\"**/*.ts\", \"**/*.tsx\"] }\n\nTo list ALL files, omit the pattern parameter — do NOT pass pattern: \"*\".`,\n  inputSchema: z.object({\n    path: z.string().default('.').describe('Directory path to list'),\n    maxDepth: z\n      .number()\n      .optional()\n      .default(2)\n      .describe('Maximum depth to descend (default: 2). Similar to tree -L flag.'),\n    showHidden: z\n      .boolean()\n      .optional()\n      .default(false)\n      .describe('Show hidden files starting with \".\" (default: false). Similar to tree -a flag.'),\n    dirsOnly: z\n      .boolean()\n      .optional()\n      .default(false)\n      .describe('List directories only, no files (default: false). Similar to tree -d flag.'),\n    exclude: z.string().optional().describe('Pattern to exclude (e.g., \"node_modules\"). Similar to tree -I flag.'),\n    extension: z.string().optional().describe('Filter by file extension (e.g., \".ts\"). Similar to tree -P flag.'),\n    pattern: z\n      .union([z.string(), z.array(z.string())])\n      .optional()\n      .describe(\n        'Glob pattern(s) to filter files. Omit this parameter to list all files (do NOT pass \"*\"). Use \"**/*.ext\" to match files recursively across directories. \"*\" only matches within a single directory level (standard glob). Glob patterns only filter files — directories are always shown to preserve tree structure. Examples: \"**/*.ts\", \"src/**/*.test.ts\", \"*.config.{js,ts}\".',\n      ),\n    respectGitignore: z\n      .boolean()\n      .optional()\n      .default(true)\n      .describe('Respect .gitignore in the listed directory (default: true).'),\n  }),\n  execute: async (\n    { path = '.', maxDepth = 2, showHidden, dirsOnly, exclude, extension, pattern, respectGitignore },\n    context,\n  ) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES);\n\n    // Agents often pass pattern: [] or pattern: '' expecting \"list everything\".\n    // Empty/whitespace-only patterns would otherwise filter out every file, or\n    // throw from picomatch. Normalize them to undefined so the listing falls back\n    // to its unfiltered behavior.\n    const normalizedPattern = (() => {\n      if (pattern === undefined) return undefined;\n      if (Array.isArray(pattern)) {\n        const cleaned = pattern.filter(p => typeof p === 'string' && p.trim().length > 0);\n        return cleaned.length > 0 ? cleaned : undefined;\n      }\n      return pattern.trim().length > 0 ? pattern : undefined;\n    })();\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'listFiles',\n      input: { path, maxDepth, pattern: normalizedPattern },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      const result = await formatAsTree(filesystem, path, {\n        maxDepth,\n        showHidden,\n        dirsOnly,\n        exclude: exclude || undefined,\n        extension: extension || undefined,\n        pattern: normalizedPattern,\n        respectGitignore,\n      });\n\n      const output = await applyTokenLimit(\n        `${result.tree}\\n\\n${result.summary}`,\n        workspace.getToolsConfig()?.[WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES]?.maxOutputTokens ?? 1_000,\n        'end',\n      );\n      span.end({ success: true }, { resultCount: result.fileCount });\n      return output;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","/**\n * LSP Inspect Tool\n *\n * Inspect code at a specific position using the Language Server Protocol.\n * The agent provides a file path, line number, and a `<<<` marker in the\n * line content to indicate the cursor position.\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { z } from 'zod/v4';\n\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { requireWorkspace, emitWorkspaceMetadata } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nconst CURSOR_MARKER = '<<<';\n\n/**\n * Get a single line preview from a file at the specified line number.\n * Returns the trimmed line content, or null if the line cannot be read.\n */\nasync function getLinePreview(filePath: string, lineNumber: number): Promise<string | null> {\n  try {\n    const content = await fs.readFile(filePath, 'utf-8');\n    const lines = content.split('\\n');\n    const line = lines[lineNumber - 1];\n    return line?.trim() ?? null;\n  } catch {\n    return null;\n  }\n}\n\nfunction getAbsolutePath(\n  workspacePath: string,\n  lspRoot: string,\n  resolveAbsolutePath?: (path: string) => string | undefined,\n) {\n  const resolvedPath = resolveAbsolutePath?.(workspacePath);\n  if (resolvedPath) {\n    return resolvedPath;\n  }\n\n  if (path.isAbsolute(workspacePath)) {\n    return workspacePath;\n  }\n\n  return path.resolve(lspRoot, workspacePath);\n}\n\nfunction locationUriToPath(uri: string): string | null {\n  if (!uri.startsWith('file://')) {\n    return null;\n  }\n\n  try {\n    return fileURLToPath(uri);\n  } catch {\n    return null;\n  }\n}\n\nfunction locationKey(location: { path: string; line: number }): string {\n  return `${location.path}:L${location.line}`;\n}\n\n/**\n * Compress a file path by replacing the current working directory prefix with $cwd\n */\nfunction compressPath(filePath: string): string {\n  const cwd = process.cwd();\n  if (filePath.startsWith(cwd)) {\n    return '$cwd' + filePath.slice(cwd.length);\n  }\n  return filePath;\n}\n\nexport const lspInspectTool = createTool({\n  id: WORKSPACE_TOOLS.LSP.LSP_INSPECT,\n  description:\n    'Inspect code at a specific symbol position using the Language Server Protocol. ' +\n    'Provide an absolute file path, a 1-indexed line number, and the exact line content with <<< marking the cursor position. ' +\n    'Exactly one <<< marker is required. ' +\n    'Returns hover information, any diagnostics reported on that line, plus definition and implementation locations when available. ' +\n    'Use this for type information, symbol navigation, and go-to-definition; use view to read the surrounding implementation.',\n\n  inputSchema: z.object({\n    path: z.string().describe('Absolute path to the file'),\n    line: z.number().int().positive().describe('Line number (1-indexed)'),\n    match: z\n      .string()\n      .describe(\n        'Line content with <<< marking the cursor position. ' +\n          'Exactly one <<< marker is required. ' +\n          'Example: \"const foo = <<<bar()\" means cursor is at bar',\n      ),\n  }),\n\n  execute: async ({ path: filePath, line, match }, context) => {\n    const workspace = requireWorkspace(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.LSP.LSP_INSPECT);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'lspInspect',\n      input: { path: filePath, line },\n      attributes: {},\n    });\n\n    // Parse cursor position from match\n    const cursorPositions = [];\n    let searchStart = 0;\n    while (true) {\n      const pos = match.indexOf(CURSOR_MARKER, searchStart);\n      if (pos === -1) break;\n      cursorPositions.push(pos);\n      searchStart = pos + CURSOR_MARKER.length;\n    }\n\n    if (cursorPositions.length === 0) {\n      span.end({ success: false });\n      return {\n        error: `No <<< cursor marker found in match`,\n      };\n    }\n\n    if (cursorPositions.length > 1) {\n      span.end({ success: false });\n      return {\n        error: `Multiple <<< markers found (found ${cursorPositions.length}, expected 1)`,\n      };\n    }\n\n    // 1-indexed character position (LSP uses 1-indexed)\n    const character = cursorPositions[0]! + 1;\n\n    // Get the LSP manager\n    const lspManager = workspace.lsp;\n    if (!lspManager) {\n      span.end({ success: false });\n      return {\n        error: 'LSP is not configured for this workspace. Enable LSP in workspace config to use this tool.',\n      };\n    }\n\n    const absolutePath = getAbsolutePath(\n      filePath,\n      lspManager.root,\n      workspace.filesystem?.resolveAbsolutePath?.bind(workspace.filesystem),\n    );\n\n    let fileContent = '';\n    try {\n      fileContent = await fs.readFile(absolutePath, 'utf-8');\n    } catch {\n      fileContent = '';\n    }\n\n    // Get client and prepare for querying\n    let queryResult;\n    try {\n      queryResult = await lspManager.prepareQuery(absolutePath);\n    } catch (err) {\n      span.end({ success: false });\n      return {\n        error: `Failed to initialize LSP client: ${err instanceof Error ? err.message : String(err)}`,\n      };\n    }\n\n    if (!queryResult) {\n      span.end({ success: false });\n      return {\n        error: `No language server available for files of this type: ${filePath}`,\n      };\n    }\n\n    const { client, uri } = queryResult;\n\n    // LSP uses 0-indexed positions\n    const position = { line: line - 1, character: character - 1 };\n\n    // Execute queries - minimal output\n    const result: Record<string, unknown> = {};\n\n    try {\n      // Primary query: hover\n      const hoverResult = await client.queryHover(uri, position).catch(() => null);\n      if (hoverResult) {\n        const contents = hoverResult.contents;\n        if (contents) {\n          if (typeof contents === 'string') {\n            result.hover = { value: contents, kind: 'plaintext' };\n          } else if (Array.isArray(contents)) {\n            // Usually [MarkupContent] or [string]\n            const first = contents[0];\n            if (typeof first === 'string') {\n              result.hover = { value: first, kind: 'plaintext' };\n            } else if (first?.value) {\n              result.hover = { value: first.value, kind: first.kind ?? 'markdown' };\n            }\n          } else if (contents.value) {\n            result.hover = { value: contents.value, kind: contents.kind ?? 'markdown' };\n          }\n        }\n      }\n\n      const diagnosticsPromise = fileContent\n        ? Promise.resolve()\n            .then(() => {\n              client.notifyChange(absolutePath, fileContent, 1);\n              return client.waitForDiagnostics(absolutePath, 5000, true);\n            })\n            .catch(() => [])\n        : Promise.resolve([]);\n\n      // Secondary queries: diagnostics, definition, and implementation\n      const [diagnosticsResult, definitionResult, implResult] = await Promise.all([\n        diagnosticsPromise,\n        client.queryDefinition(uri, position).catch(() => []),\n        client.queryImplementation(uri, position).catch(() => []),\n      ]);\n\n      if (diagnosticsResult && diagnosticsResult.length > 0) {\n        const lineDiagnostics = diagnosticsResult\n          .map((diagnostic: any) => ({\n            line: typeof diagnostic.line === 'number' ? diagnostic.line : (diagnostic.range?.start?.line ?? -1) + 1,\n            severity:\n              typeof diagnostic.severity === 'number'\n                ? diagnostic.severity === 1\n                  ? 'error'\n                  : diagnostic.severity === 2\n                    ? 'warning'\n                    : diagnostic.severity === 3\n                      ? 'info'\n                      : 'hint'\n                : diagnostic.severity,\n            message: diagnostic.message,\n            source: diagnostic.source ?? null,\n          }))\n          .filter(diagnostic => diagnostic.line === line)\n          .map(({ severity, message, source }) => ({ severity, message, source }));\n\n        if (lineDiagnostics.length > 0) {\n          result.diagnostics = lineDiagnostics;\n        }\n      }\n\n      const definitionLocations = definitionResult\n        .map((loc: any) => ({\n          uri: loc.uri ?? loc.targetUri,\n          range: loc.range ?? loc.targetRange,\n        }))\n        .map((loc: any) => {\n          const resolvedPath = loc.uri ? locationUriToPath(String(loc.uri)) : null;\n          return resolvedPath\n            ? {\n                path: resolvedPath,\n                line: (loc.range?.start?.line ?? 0) + 1,\n                character: (loc.range?.start?.character ?? 0) + 1,\n              }\n            : null;\n        })\n        .filter((loc): loc is { path: string; line: number; character: number } => Boolean(loc))\n        .filter(loc => !(loc.path === absolutePath && loc.line === line));\n\n      if (definitionLocations.length > 0) {\n        const previews = await Promise.all(definitionLocations.map(loc => getLinePreview(loc.path, loc.line)));\n        result.definition = definitionLocations.map((loc, i) => ({\n          location: `${compressPath(loc.path)}:L${loc.line}:C${loc.character}`,\n          preview: previews[i],\n        }));\n      }\n\n      const definitionKeys = new Set(definitionLocations.map(locationKey));\n      const implementationLocations = implResult\n        .map((loc: any) => ({\n          uri: loc.uri ?? loc.targetUri,\n          range: loc.range ?? loc.targetRange,\n        }))\n        .map((loc: any) => {\n          const resolvedPath = loc.uri ? locationUriToPath(String(loc.uri)) : null;\n          return resolvedPath\n            ? {\n                path: resolvedPath,\n                line: (loc.range?.start?.line ?? 0) + 1,\n                character: (loc.range?.start?.character ?? 0) + 1,\n              }\n            : null;\n        })\n        .filter((loc): loc is { path: string; line: number; character: number } => Boolean(loc))\n        .filter(loc => !definitionKeys.has(locationKey(loc)) && !(loc.path === absolutePath && loc.line === line));\n\n      if (implementationLocations.length > 0) {\n        result.implementation = implementationLocations.map(\n          loc => `${compressPath(loc.path)}:L${loc.line}:C${loc.character}`,\n        );\n      }\n    } catch (err) {\n      result.error = `LSP query failed: ${err instanceof Error ? err.message : String(err)}`;\n    } finally {\n      // Clean up - close the file\n      client.notifyClose(absolutePath);\n    }\n\n    span.end({ success: !result.error });\n    return result;\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { WorkspaceReadOnlyError } from '../errors';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const mkdirTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.MKDIR,\n  description: 'Create a directory in the workspace filesystem',\n  inputSchema: z.object({\n    path: z.string().describe('The path of the directory to create'),\n    recursive: z\n      .boolean()\n      .optional()\n      .default(true)\n      .describe('Whether to create parent directories if they do not exist'),\n  }),\n  execute: async ({ path, recursive }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.MKDIR);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'mkdir',\n      input: { path, recursive },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      if (filesystem.readOnly) {\n        throw new WorkspaceReadOnlyError('mkdir');\n      }\n\n      await filesystem.mkdir(path, { recursive });\n      span.end({ success: true });\n      return `Created directory ${path}`;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { extractLinesWithLimit, formatWithLineNumbers } from '../line-utils';\nimport { emitWorkspaceMetadata, requireFilesystem } from './helpers';\nimport { applyTokenLimit } from './output-helpers';\nimport { startWorkspaceSpan } from './tracing';\n\n/**\n * Internal marker on the tool's text result that signals to `toModelOutput`\n * that the file should be surfaced to the model as a media part (image or\n * binary file) rather than as plain text. We attach this on a wrapper object\n * but only the `text` field is shown to the model (via toModelOutput); the\n * marker is stripped before it ever reaches the model.\n *\n * The shape is intentionally JSON-serialisable so it round-trips through\n * storage layers that snapshot tool results.\n */\ntype MediaToolResult = {\n  __workspaceMedia: true;\n  text: string;\n  mediaType: string;\n  data: string;\n};\n\nfunction isMediaToolResult(value: unknown): value is MediaToolResult {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    (value as Record<string, unknown>).__workspaceMedia === true &&\n    typeof (value as Record<string, unknown>).text === 'string' &&\n    typeof (value as Record<string, unknown>).mediaType === 'string' &&\n    typeof (value as Record<string, unknown>).data === 'string'\n  );\n}\n\n/**\n * Default mime types surfaced to the model as media parts. The list is\n * intentionally the intersection of image formats supported by the major\n * model providers (Anthropic, OpenAI, Gemini) plus `application/pdf`.\n * `image/*` is *not* used so we don't surface exotic subtypes like SVG/BMP\n * that some providers reject. Override `mediaTypes` to broaden this.\n */\nconst DEFAULT_MEDIA_TYPES: string[] = ['image/png', 'image/jpeg', 'image/webp', 'application/pdf'];\n\n/**\n * Default cap (in bytes) on inline media reads. Files larger than this fall\n * back to metadata-only output instead of being fully base64-encoded into\n * the model context (and persisted in storage on rehydration). 10 MiB is\n * roughly aligned with provider per-image/per-pdf limits.\n */\nconst DEFAULT_MAX_MEDIA_BYTES = 10 * 1024 * 1024;\n\n/**\n * `application/*` mime types that are actually text content and safe to read\n * as utf-8. Anything not matching `text/*` and not in this list is treated\n * as opaque binary when there's no explicit encoding and no media match.\n */\nconst TEXT_APPLICATION_TYPES = new Set([\n  'application/json',\n  'application/javascript',\n  'application/typescript',\n  'application/xml',\n  'application/graphql',\n  'application/x-sh',\n  'application/x-yaml',\n  'application/yaml',\n  'application/ld+json',\n  'application/sql',\n]);\n\nfunction isTextLikeMimeType(mimeType: string | undefined): boolean {\n  if (!mimeType) return true;\n  // `application/octet-stream` is the catch-all for unknown extensions; we\n  // optimistically treat it as text so files like `.log` or `.conf` (which\n  // have no registered mime mapping) still get read as utf-8.\n  if (mimeType === 'application/octet-stream') return true;\n  if (mimeType.startsWith('text/')) return true;\n  if (TEXT_APPLICATION_TYPES.has(mimeType)) return true;\n  if (mimeType.endsWith('+json') || mimeType.endsWith('+xml')) return true;\n  return false;\n}\n\n/**\n * Validates a single `mediaTypes` pattern. Accepts:\n * - `*` or `*​/*` — match anything\n * - `type/*` — match all subtypes of a top-level type (e.g. `image/*`)\n * - `type/subtype` — exact mime type (e.g. `application/pdf`, `application/vnd.api+json`)\n *\n * Throws a descriptive error for anything else so misconfigurations surface\n * immediately instead of silently failing to match.\n */\nconst MEDIA_TYPE_PATTERN = /^(?:\\*|\\*\\/\\*|[a-z0-9!#$&^_.+-]+\\/(?:\\*|[a-z0-9!#$&^_.+-]+))$/i;\n\nfunction validateMediaTypePatterns(patterns: string[]): void {\n  for (const pattern of patterns) {\n    if (typeof pattern !== 'string' || !MEDIA_TYPE_PATTERN.test(pattern)) {\n      throw new Error(\n        `Invalid \\`mediaTypes\\` pattern: ${JSON.stringify(pattern)}. Expected \\`*\\`, \\`*/*\\`, \\`type/*\\`, or a full mime type like \\`application/pdf\\`.`,\n      );\n    }\n  }\n}\n\n/**\n * Build a predicate from the `mediaTypes` config option.\n * Supports glob patterns (e.g. `'image/*'`), custom functions, and `false`\n * to disable media parts entirely.\n */\nfunction buildMediaTypeCheck(\n  config: string[] | ((mimeType: string) => boolean) | false | undefined,\n): (mimeType: string | undefined) => boolean {\n  if (config === false) return () => false;\n  if (typeof config === 'function') {\n    return (mimeType: string | undefined) => (mimeType ? config(mimeType) : false);\n  }\n  const patterns = config ?? DEFAULT_MEDIA_TYPES;\n  validateMediaTypePatterns(patterns);\n  return (mimeType: string | undefined) => {\n    if (!mimeType) return false;\n    return patterns.some(pattern => {\n      if (pattern === '*' || pattern === '*/*') return true;\n      if (pattern.endsWith('/*')) {\n        return mimeType.startsWith(pattern.slice(0, -1));\n      }\n      return mimeType === pattern;\n    });\n  };\n}\n\nexport const readFileTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.READ_FILE,\n  description:\n    'Read a file from the workspace filesystem. Text files come back as text — use offset/limit to read a line range from large files. Supported media files come back as a native file part you can view directly. Other binary files return only their metadata (path, size, mime type) since their raw contents are not useful to read.',\n  inputSchema: z.object({\n    path: z.string().describe('The path to the file to read (e.g., \"data/config.json\")'),\n    offset: z\n      .number()\n      .int()\n      .min(1)\n      .optional()\n      .describe(\n        'Line number to start reading from (1-indexed). Only used when reading text files; ignored for media and other binary files. Defaults to line 1 if omitted.',\n      ),\n    limit: z\n      .number()\n      .int()\n      .min(1)\n      .optional()\n      .describe(\n        'Maximum number of lines to read. Only used when reading text files; ignored for media and other binary files. Defaults to the end of the file if omitted.',\n      ),\n    showLineNumbers: z\n      .boolean()\n      .optional()\n      .default(true)\n      .describe(\n        'Prefix each line with its line number. Only used when reading text files; ignored for media and other binary files. Defaults to true if omitted.',\n      ),\n    encoding: z\n      .enum(['utf-8', 'utf8', 'base64', 'hex', 'binary'])\n      .optional()\n      .describe(\n        \"Usually omit this — text files and supported media are handled automatically. Pass `base64` or `hex` to get the file's raw bytes encoded as text when you need to inspect an unsupported binary file that would otherwise only return metadata (e.g. checking a file header or magic bytes).\",\n      ),\n  }),\n  execute: async ({ path, encoding, offset, limit, showLineNumbers }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.READ_FILE);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'readFile',\n      input: { path, encoding, offset, limit },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      const stat = await filesystem.stat(path);\n\n      const readFileConfig = workspace.getToolsConfig()?.[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE];\n      const shouldReturnAsMedia = buildMediaTypeCheck(readFileConfig?.mediaTypes);\n\n      // When the caller didn't ask for a specific encoding and the file's\n      // mime type matches the `mediaTypes` predicate, read as base64 and\n      // return a MediaToolResult so `toModelOutput` can surface it as a\n      // file/image part the model can natively consume.\n      if (!encoding && shouldReturnAsMedia(stat.mimeType)) {\n        const maxMediaBytes = readFileConfig?.maxMediaBytes ?? DEFAULT_MAX_MEDIA_BYTES;\n        // Avoid materializing huge media files (and persisting their base64\n        // string through storage on rehydration). Fall back to metadata-only\n        // when the file exceeds the configured size cap.\n        if (stat.size > maxMediaBytes) {\n          span.end({ success: true }, { bytesTransferred: 0 });\n          return `${stat.path} (${stat.size} bytes, ${stat.mimeType}) — exceeds maxMediaBytes (${maxMediaBytes}). Returning metadata only; configure \\`maxMediaBytes\\` on the read_file tool to raise this cap.`;\n        }\n        const base64 = (await filesystem.readFile(path, { encoding: 'base64' })) as string;\n        const header = `${stat.path} (${stat.size} bytes, ${stat.mimeType})`;\n        span.end({ success: true }, { bytesTransferred: stat.size });\n        return {\n          __workspaceMedia: true,\n          text: header,\n          mediaType: stat.mimeType!,\n          data: base64,\n        } satisfies MediaToolResult;\n      }\n\n      // When the caller didn't ask for a specific encoding and the file is\n      // a binary type that's neither text-readable nor in `mediaTypes`,\n      // return metadata only so the agent knows about the file without\n      // dumping useless base64 into the conversation.\n      if (!encoding && !isTextLikeMimeType(stat.mimeType)) {\n        span.end({ success: true }, { bytesTransferred: 0 });\n        return `${stat.path} (${stat.size} bytes, ${stat.mimeType ?? 'unknown'}) — binary file not readable as text. Pass an explicit \\`encoding\\` (e.g. \\`base64\\`) to read the raw bytes, or configure \\`mediaTypes\\` on the read_file tool to surface it as a media part.`;\n      }\n\n      const effectiveEncoding = (encoding as BufferEncoding) ?? 'utf-8';\n      const fullContent = await filesystem.readFile(path, { encoding: effectiveEncoding });\n\n      const isTextEncoding = !encoding || encoding === 'utf-8' || encoding === 'utf8';\n\n      const tokenLimit = readFileConfig?.maxOutputTokens;\n\n      if (!isTextEncoding) {\n        const output = await applyTokenLimit(\n          `${stat.path} (${stat.size} bytes, ${effectiveEncoding})\\n${fullContent}`,\n          tokenLimit,\n          'end',\n        );\n        span.end({ success: true }, { bytesTransferred: stat.size });\n        return output;\n      }\n\n      if (typeof fullContent !== 'string') {\n        const output = await applyTokenLimit(\n          `${stat.path} (${stat.size} bytes, base64)\\n${fullContent.toString('base64')}`,\n          tokenLimit,\n          'end',\n        );\n        span.end({ success: true }, { bytesTransferred: stat.size });\n        return output;\n      }\n\n      const hasLineRange = offset !== undefined || limit !== undefined;\n      const result = extractLinesWithLimit(fullContent, offset, limit);\n\n      const shouldShowLineNumbers = showLineNumbers !== false;\n      const hasExtractedLines = result.lines.start !== 0 || result.lines.end !== 0;\n      const formattedContent =\n        shouldShowLineNumbers && hasExtractedLines\n          ? formatWithLineNumbers(result.content, result.lines.start)\n          : result.content;\n\n      let header: string;\n      if (hasLineRange) {\n        header = `${stat.path} (lines ${result.lines.start}-${result.lines.end} of ${result.totalLines}, ${stat.size} bytes)`;\n      } else {\n        header = `${stat.path} (${stat.size} bytes)`;\n      }\n\n      const output = await applyTokenLimit(`${header}\\n${formattedContent}`, tokenLimit, 'end');\n      span.end({ success: true }, { bytesTransferred: stat.size });\n      return output;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n  toModelOutput: (output: unknown) => {\n    if (isMediaToolResult(output)) {\n      return {\n        type: 'content',\n        value: [\n          { type: 'text', text: output.text },\n          { type: 'media', data: output.data, mediaType: output.mediaType },\n        ],\n      };\n    }\n    // For plain string output, return undefined so we don't store a duplicate\n    // copy on providerMetadata.mastra.modelOutput — the original string result\n    // is already what the model sees.\n    return undefined;\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { emitWorkspaceMetadata, requireWorkspace } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const searchInputSchema = z.object({\n  query: z.string().describe('The search query string'),\n  topK: z.number().optional().default(5).describe('Maximum number of results to return'),\n  mode: z\n    .enum(['bm25', 'vector', 'hybrid'])\n    .optional()\n    .describe('Search mode: bm25 for keyword search, vector for semantic search, hybrid for both combined'),\n  minScore: z.number().optional().describe('Minimum score threshold (0-1 for normalized scores)'),\n});\n\nexport const searchTool = createTool({\n  id: WORKSPACE_TOOLS.SEARCH.SEARCH,\n  description:\n    'Search indexed content in the workspace. Supports keyword (BM25), semantic (vector), and hybrid search modes.',\n  inputSchema: searchInputSchema,\n  execute: async ({ query, topK, mode, minScore }, context) => {\n    const workspace = requireWorkspace(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.SEARCH.SEARCH);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'search',\n      operation: 'search',\n      input: { query, topK, mode, minScore },\n      attributes: {},\n    });\n\n    try {\n      // Resolve effective mode before searching — fall back gracefully if requested\n      // mode isn't supported (e.g. 'hybrid' requested but only BM25 configured)\n      const effectiveMode =\n        mode === 'hybrid' && !workspace.canHybrid\n          ? workspace.canVector\n            ? 'vector'\n            : 'bm25'\n          : mode === 'vector' && !workspace.canVector\n            ? 'bm25'\n            : (mode ?? (workspace.canHybrid ? 'hybrid' : workspace.canVector ? 'vector' : 'bm25'));\n\n      const results = await workspace.search(query, {\n        topK,\n        mode: effectiveMode as 'bm25' | 'vector' | 'hybrid' | undefined,\n        minScore,\n      });\n\n      const lines = results.map(r => {\n        const lineInfo = r.lineRange ? `:${r.lineRange.start}-${r.lineRange.end}` : '';\n        return `${r.id}${lineInfo}: ${r.content}`;\n      });\n\n      lines.push('---');\n      lines.push(`${results.length} result${results.length !== 1 ? 's' : ''} (${effectiveMode} search)`);\n\n      span.end({ success: true }, { resultCount: results.length });\n      return lines.join('\\n');\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","import { z } from 'zod/v4';\nimport { createTool } from '../../tools';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { WorkspaceReadOnlyError } from '../errors';\nimport { emitWorkspaceMetadata, getEditDiagnosticsText, requireFilesystem } from './helpers';\nimport { startWorkspaceSpan } from './tracing';\n\nexport const writeFileTool = createTool({\n  id: WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE,\n  description: 'Write content to a file in the workspace filesystem. Creates parent directories if needed.',\n  inputSchema: z.object({\n    path: z.string().describe('The path where to write the file (e.g., \"data/output.txt\")'),\n    content: z.string().describe('The content to write to the file'),\n    overwrite: z.boolean().optional().default(true).describe('Whether to overwrite the file if it already exists'),\n  }),\n  execute: async ({ path, content, overwrite }, context) => {\n    const { workspace, filesystem } = requireFilesystem(context);\n    await emitWorkspaceMetadata(context, WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE);\n\n    const span = startWorkspaceSpan(context, workspace, {\n      category: 'filesystem',\n      operation: 'writeFile',\n      input: { path, overwrite, contentLength: content.length },\n      attributes: { filesystemProvider: filesystem.provider },\n    });\n\n    try {\n      if (filesystem.readOnly) {\n        throw new WorkspaceReadOnlyError('write_file');\n      }\n\n      await filesystem.writeFile(path, content, {\n        overwrite,\n        expectedMtime: (context as any)?.__expectedMtime,\n      });\n\n      const size = Buffer.byteLength(content, 'utf-8');\n      let output = `Wrote ${size} bytes to ${path}`;\n      output += await getEditDiagnosticsText(workspace, path, content);\n      span.end({ success: true }, { bytesTransferred: size });\n      return output;\n    } catch (err) {\n      span.error(err);\n      throw err;\n    }\n  },\n});\n","/**\n * Workspace Tools — Factory\n *\n * Creates the built-in workspace tools for agents. Individual tools are\n * defined in their own files; this module applies WorkspaceToolsConfig\n * (enabled, requireApproval, requireReadBeforeWrite) and injects workspace\n * into the tool execution context.\n */\n\nimport { z } from 'zod/v4';\nimport { RequestContext } from '../../request-context';\nimport type { WorkspaceToolName } from '../constants';\nimport { WORKSPACE_TOOLS } from '../constants';\nimport { FileNotFoundError, FileReadRequiredError } from '../errors';\nimport { InMemoryFileReadTracker, InMemoryFileWriteLock } from '../filesystem';\nimport type { FileReadTracker, FileWriteLock, WorkspaceFilesystem } from '../filesystem';\nimport type { WorkspaceSandbox } from '../sandbox';\nimport type { Workspace } from '../workspace';\nimport { isAstGrepAvailable, astEditTool } from './ast-edit';\nimport { deleteFileTool } from './delete-file';\nimport { editFileTool } from './edit-file';\nimport { executeCommandTool, executeCommandWithBackgroundTool } from './execute-command';\nimport { fileStatTool } from './file-stat';\nimport { getProcessOutputTool } from './get-process-output';\nimport { grepTool } from './grep';\nimport { indexContentTool } from './index-content';\nimport { killProcessTool } from './kill-process';\nimport { listFilesTool } from './list-files';\nimport { lspInspectTool } from './lsp-inspect';\nimport { mkdirTool } from './mkdir';\nimport { readFileTool } from './read-file';\nimport { searchInputSchema, searchTool } from './search';\nimport type {\n  WorkspaceToolsConfig,\n  DynamicToolConfigValue,\n  ToolConfigContext,\n  ToolConfigWithArgsContext,\n  WorkspaceToolHooks,\n} from './types';\nexport type {\n  WorkspaceToolConfig,\n  WorkspaceToolsConfig,\n  ExecuteCommandToolConfig,\n  BackgroundProcessConfig,\n  BackgroundProcessMeta,\n  BackgroundProcessExitMeta,\n  ToolConfigContext,\n  ToolConfigWithArgsContext,\n  DynamicToolConfigValue,\n  WorkspaceToolHookContext,\n  WorkspaceToolBeforeHookResult,\n  WorkspaceToolAfterHookContext,\n  WorkspaceToolHooks,\n} from './types';\nimport { writeFileTool } from './write-file';\n\n/**\n * Resolve a DynamicToolConfigValue to a boolean.\n * If it's a function, calls it with the provided context.\n * On error, returns the safeDefault.\n */\nasync function resolveDynamicValue<TContext>(\n  value: DynamicToolConfigValue<TContext> | undefined,\n  context: TContext | undefined,\n  safeDefault: boolean,\n): Promise<boolean> {\n  if (value === undefined) return safeDefault;\n  if (typeof value === 'boolean') return value;\n  if (!context) return safeDefault;\n  try {\n    return await value(context);\n  } catch (error) {\n    console.warn('[Workspace Tools] Dynamic config function threw, using safe default:', error);\n    return safeDefault;\n  }\n}\n\nfunction hasFilesystemConfig(workspace: Workspace): boolean {\n  if (typeof (workspace as any)?.hasFilesystemConfig === 'function') {\n    return (workspace as any).hasFilesystemConfig();\n  }\n  return !!workspace.filesystem;\n}\n\nfunction hasSandboxConfig(workspace: Workspace): boolean {\n  if (typeof (workspace as any)?.hasSandboxConfig === 'function') {\n    return (workspace as any).hasSandboxConfig();\n  }\n  return !!workspace.sandbox;\n}\n\n/**\n * Normalize a requestContext value to a plain Record.\n * Callers may pass a Map-like RequestContext (with `.entries()`) or a plain\n * object.  Dynamic config functions always receive a plain object so that\n * bracket-notation access (`requestContext['key']`) works consistently.\n */\nfunction toPlainRequestContext(requestContext: unknown): Record<string, unknown> {\n  if (!requestContext) return {};\n  if (typeof (requestContext as any).entries === 'function') {\n    return Object.fromEntries((requestContext as any).entries());\n  }\n  return requestContext as Record<string, unknown>;\n}\n\n/** Resolved tool config with `enabled` as a boolean and execution-time values as raw config. */\nexport interface ResolvedToolConfig {\n  enabled: boolean;\n  requireApproval: DynamicToolConfigValue<ToolConfigWithArgsContext>;\n  requireReadBeforeWrite?: DynamicToolConfigValue<ToolConfigWithArgsContext>;\n  maxOutputTokens?: number;\n  name?: string;\n  hooks?: WorkspaceToolHooks;\n}\n\n/**\n * Resolves the effective configuration for a specific tool.\n *\n * Resolution order (later overrides earlier):\n * 1. Built-in defaults (enabled: true, requireApproval: false)\n * 2. Top-level config (tools.enabled, tools.requireApproval)\n * 3. Per-tool config (tools[toolName].enabled, tools[toolName].requireApproval)\n *\n * `enabled` is resolved to a boolean immediately (requires context if dynamic).\n * `requireApproval` and `requireReadBeforeWrite` are passed through as-is\n * for execution-time evaluation (they may need args).\n */\nexport async function resolveToolConfig(\n  toolsConfig: WorkspaceToolsConfig | undefined,\n  toolName: WorkspaceToolName,\n  context?: ToolConfigContext,\n): Promise<ResolvedToolConfig> {\n  let enabled: DynamicToolConfigValue = true;\n  let requireApproval: DynamicToolConfigValue<ToolConfigWithArgsContext> = false;\n  let requireReadBeforeWrite: DynamicToolConfigValue<ToolConfigWithArgsContext> | undefined;\n  let maxOutputTokens: number | undefined;\n  let name: string | undefined;\n  const hooks = toolsConfig?.hooks;\n\n  if (toolsConfig) {\n    if (toolsConfig.enabled !== undefined) {\n      enabled = toolsConfig.enabled;\n    }\n    if (toolsConfig.requireApproval !== undefined) {\n      requireApproval = toolsConfig.requireApproval;\n    }\n\n    const perToolConfig = toolsConfig[toolName];\n    if (perToolConfig) {\n      if (perToolConfig.enabled !== undefined) {\n        enabled = perToolConfig.enabled;\n      }\n      if (perToolConfig.requireApproval !== undefined) {\n        requireApproval = perToolConfig.requireApproval;\n      }\n      if (perToolConfig.requireReadBeforeWrite !== undefined) {\n        requireReadBeforeWrite = perToolConfig.requireReadBeforeWrite;\n      }\n      if (perToolConfig.maxOutputTokens !== undefined) {\n        maxOutputTokens = perToolConfig.maxOutputTokens;\n      }\n      if (perToolConfig.name !== undefined) {\n        name = perToolConfig.name;\n      }\n    }\n  }\n\n  // Resolve `enabled` now (tool-listing time) — safe default: false (fail-closed)\n  const resolvedEnabled = await resolveDynamicValue(enabled, context, false);\n\n  return { enabled: resolvedEnabled, requireApproval, requireReadBeforeWrite, maxOutputTokens, name, hooks };\n}\n\n// ---------------------------------------------------------------------------\n// Wrapper helpers\n// ---------------------------------------------------------------------------\n\ntype ResolveTargets = { filesystem?: boolean; sandbox?: boolean };\n\n/**\n * Resolve the effective workspace for tool execution. When a dynamic resolver\n * is configured for a requested provider (no static instance), resolves it from\n * requestContext and returns a proxy workspace that exposes the resolved value.\n * Returns the workspace unchanged when no resolution is needed, so tools that\n * don't touch a given provider don't pay the cost of calling its resolver.\n */\nasync function resolveEffectiveWorkspace(\n  workspace: Workspace,\n  context: any,\n  targets: ResolveTargets,\n): Promise<Workspace> {\n  workspace.lastAccessedAt = new Date();\n\n  const needsFilesystem = !!(targets.filesystem && !workspace.filesystem && hasFilesystemConfig(workspace));\n  const needsSandbox = !!(targets.sandbox && !workspace.sandbox && hasSandboxConfig(workspace));\n  if (!needsFilesystem && !needsSandbox) return workspace;\n\n  const requestContext: RequestContext = context?.requestContext ?? new RequestContext();\n  const overrides: { filesystem?: WorkspaceFilesystem; sandbox?: WorkspaceSandbox } = {};\n\n  if (needsFilesystem) {\n    const resolvedFs = await workspace.resolveFilesystem({ requestContext });\n    if (resolvedFs) overrides.filesystem = resolvedFs;\n  }\n\n  if (needsSandbox) {\n    const resolvedSandbox = await workspace.resolveSandbox({ requestContext });\n    if (resolvedSandbox) overrides.sandbox = resolvedSandbox;\n  }\n\n  if (!overrides.filesystem && !overrides.sandbox) return workspace;\n\n  return new Proxy(workspace, {\n    get(target: any, prop: string | symbol) {\n      if (prop === 'filesystem' && overrides.filesystem) return overrides.filesystem;\n      if (prop === 'sandbox' && overrides.sandbox) return overrides.sandbox;\n      return target[prop];\n    },\n  });\n}\n\n/**\n * Clone a standalone tool with config overrides and inject workspace into context.\n * `targets` declares which providers the tool needs resolved per request.\n */\nfunction wrapTool(tool: any, workspace: Workspace, targets: ResolveTargets): any {\n  return {\n    ...tool,\n    execute: async (input: any, context: any = {}) => {\n      const effectiveWorkspace = await resolveEffectiveWorkspace(context?.workspace ?? workspace, context, targets);\n      const enrichedContext = { ...context, workspace: effectiveWorkspace };\n      return tool.execute(input, enrichedContext);\n    },\n  };\n}\n\n/**\n * Wrap a tool with read-before-write tracking (readTracker).\n *\n * - mode 'read': records the read after execution\n * - mode 'write': checks before execution, clears after\n */\nfunction wrapWithReadTracker(\n  tool: any,\n  workspace: Workspace,\n  readTracker: FileReadTracker,\n  config: { requireReadBeforeWrite?: DynamicToolConfigValue<ToolConfigWithArgsContext> },\n  mode: 'read' | 'write',\n): any {\n  return {\n    ...tool,\n    execute: async (input: any, context: any = {}) => {\n      const effectiveWorkspace = await resolveEffectiveWorkspace(context?.workspace ?? workspace, context, {\n        filesystem: true,\n      });\n      let enrichedContext: any = { ...context, workspace: effectiveWorkspace };\n      const fs: WorkspaceFilesystem | undefined = effectiveWorkspace.filesystem;\n\n      // Pre-execution: enforce read-before-write policy and/or attach\n      // optimistic-concurrency mtime for write tools.\n      if (mode === 'write' && fs) {\n        // Optimistic concurrency: attach the mtime from the last read\n        // *before* stat so it's preserved even when the file has been\n        // deleted externally (stat throws FileNotFoundError).\n        const record = readTracker.getReadRecord(input.path);\n        if (record) {\n          enrichedContext = { ...enrichedContext, __expectedMtime: record.modifiedAtRead };\n        }\n\n        try {\n          const stat = await fs.stat(input.path);\n\n          // Policy gate: require the agent to have read the file first.\n          // Only evaluate when explicitly configured (opt-in policy).\n          // Safe default true = fail-closed if a dynamic function throws.\n          if (config.requireReadBeforeWrite !== undefined) {\n            const shouldRequireRead = await resolveDynamicValue(\n              config.requireReadBeforeWrite,\n              { args: input, requestContext: enrichedContext.requestContext ?? {}, workspace: effectiveWorkspace },\n              true,\n            );\n            if (shouldRequireRead) {\n              const check = readTracker.needsReRead(input.path, stat.modifiedAt);\n              if (check.needsReRead) {\n                throw new FileReadRequiredError(input.path, check.reason!);\n              }\n            }\n          }\n        } catch (error) {\n          if (!(error instanceof FileNotFoundError)) {\n            throw error;\n          }\n          // Missing file: if a read record exists the expectedMtime is\n          // already attached, so downstream writeFile can treat this as\n          // stale. Otherwise it's a genuinely new file.\n        }\n      }\n\n      const result = await tool.execute(input, enrichedContext);\n\n      // Post-execution: track reads / clear write records\n      if (mode === 'read' && fs) {\n        try {\n          const stat = await fs.stat(input.path);\n          readTracker.recordRead(input.path, stat.modifiedAt);\n        } catch {\n          // Ignore stat errors for tracking\n        }\n      } else if (mode === 'write') {\n        readTracker.clearReadRecord(input.path);\n      }\n\n      return result;\n    },\n  };\n}\n\n/**\n * Wrap a tool with a per-file write lock.\n *\n * The lock serializes the entire execute pipeline (including any\n * read-before-write checks) so concurrent calls to the same path\n * run one at a time.\n */\nfunction wrapWithToolHooks(\n  tool: any,\n  hooks: WorkspaceToolHooks,\n  toolName: string,\n  workspaceToolName: WorkspaceToolName,\n): any {\n  return {\n    ...tool,\n    execute: async (input: any, context: any = {}) => {\n      const hookContext = { toolName, workspaceToolName, input, context };\n      const beforeResult = await hooks.beforeToolCall?.(hookContext);\n      if (beforeResult?.proceed === false) {\n        return beforeResult.output;\n      }\n\n      let output: unknown;\n      try {\n        output = await tool.execute(input, context);\n      } catch (error) {\n        await hooks.afterToolCall?.({ ...hookContext, output, error });\n        throw error;\n      }\n\n      await hooks.afterToolCall?.({ ...hookContext, output });\n      return output;\n    },\n  };\n}\n\nfunction wrapWithWriteLock(tool: any, writeLock: FileWriteLock): any {\n  return {\n    ...tool,\n    execute: async (input: any, context: any = {}) => {\n      if (!input.path) {\n        throw new Error('wrapWithWriteLock: input.path is required');\n      }\n      return writeLock.withLock(input.path, () => tool.execute(input, context));\n    },\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates workspace tools that will be auto-injected into agents.\n *\n * @param workspace - The workspace instance to bind tools to\n * @returns Record of workspace tools\n */\nexport async function createWorkspaceTools(\n  workspace: Workspace,\n  configContext?: Omit<ToolConfigContext, 'requestContext'> & { requestContext?: unknown },\n) {\n  // Seed fallback context so dynamic enabled functions always get called,\n  // even if the caller omits configContext.  Normalize requestContext so\n  // user-provided functions always receive a plain Record, not a Map.\n  const effectiveConfigContext: ToolConfigContext = configContext\n    ? { ...configContext, requestContext: toPlainRequestContext(configContext.requestContext) }\n    : { requestContext: {}, workspace };\n  const tools: Record<string, any> = {};\n  const toolsConfig = workspace.getToolsConfig();\n  const isReadOnly = workspace.filesystem?.readOnly ?? false;\n\n  // Shared write lock — serializes concurrent writes to the same file path\n  const writeLock: FileWriteLock = new InMemoryFileWriteLock();\n\n  // Shared read tracker — always active so optimistic concurrency (mtime\n  // checking) works on every write, regardless of the requireReadBeforeWrite\n  // policy setting.\n  const readTracker: FileReadTracker = new InMemoryFileReadTracker();\n\n  // Helper: add a tool with config-driven filtering\n  const addTool = async (\n    name: WorkspaceToolName,\n    tool: any,\n    opts?: {\n      requireWrite?: boolean;\n      readTrackerMode?: 'read' | 'write';\n      useWriteLock?: boolean;\n      targets?: ResolveTargets;\n    },\n  ) => {\n    const config = await resolveToolConfig(toolsConfig, name, effectiveConfigContext);\n    if (!config.enabled) return;\n    if (opts?.requireWrite && isReadOnly) return;\n\n    // Handle dynamic requireApproval: if it's a function, store as needsApprovalFn\n    // and set requireApproval to true so the execution pipeline knows to check\n    let wrapped: any;\n    if (typeof config.requireApproval === 'function') {\n      const approvalFn = config.requireApproval;\n      wrapped = {\n        ...tool,\n        requireApproval: true,\n        needsApprovalFn: async (\n          args: Record<string, unknown>,\n          ctx?: {\n            requestContext?: Record<string, unknown> | { entries(): Iterable<[string, unknown]> };\n            workspace?: object;\n          },\n        ) =>\n          resolveDynamicValue(\n            approvalFn,\n            {\n              args,\n              requestContext: toPlainRequestContext(ctx?.requestContext),\n              workspace: ctx?.workspace ?? workspace,\n            },\n            true,\n          ),\n      };\n    } else {\n      wrapped = { ...tool, requireApproval: config.requireApproval };\n    }\n\n    if (opts?.readTrackerMode) {\n      wrapped = wrapWithReadTracker(wrapped, workspace, readTracker, config, opts.readTrackerMode);\n    } else {\n      wrapped = wrapTool(wrapped, workspace, opts?.targets ?? {});\n    }\n\n    // Use custom name if provided, otherwise use the default constant name\n    const exposedName = config.name ?? name;\n    if (tools[exposedName]) {\n      throw new Error(\n        `Duplicate workspace tool name \"${exposedName}\": tool \"${name}\" conflicts with an already-registered tool. ` +\n          `Check your tools config for duplicate \"name\" values.`,\n      );\n    }\n    // When the tool is renamed, update its id to match so fallback-by-id\n    // resolution (in tool-call-step, llm-execution-step, etc.) won't allow\n    // the model to call the tool using the old default name.\n    if (exposedName !== name && 'id' in wrapped) {\n      wrapped = { ...wrapped, id: exposedName };\n    }\n\n    if (config.hooks) {\n      wrapped = wrapWithToolHooks(wrapped, config.hooks, exposedName, name);\n    }\n\n    // Write lock is outermost — serializes the entire enriched execute pipeline\n    if (opts?.useWriteLock) {\n      wrapped = wrapWithWriteLock(wrapped, writeLock);\n    }\n\n    tools[exposedName] = wrapped;\n  };\n\n  // Filesystem tools — add when filesystem is available (static instance or resolver function)\n  if (hasFilesystemConfig(workspace)) {\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.READ_FILE, readFileTool, { readTrackerMode: 'read' });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE, writeFileTool, {\n      requireWrite: true,\n      readTrackerMode: 'write',\n      useWriteLock: true,\n    });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE, editFileTool, {\n      requireWrite: true,\n      readTrackerMode: 'write',\n      useWriteLock: true,\n    });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES, listFilesTool, { targets: { filesystem: true } });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.DELETE, deleteFileTool, {\n      requireWrite: true,\n      useWriteLock: true,\n      targets: { filesystem: true },\n    });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT, fileStatTool, { targets: { filesystem: true } });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.MKDIR, mkdirTool, { requireWrite: true, targets: { filesystem: true } });\n    await addTool(WORKSPACE_TOOLS.FILESYSTEM.GREP, grepTool, { targets: { filesystem: true } });\n\n    // AST edit tool (only if @ast-grep/napi is available at runtime)\n    if (isAstGrepAvailable()) {\n      await addTool(WORKSPACE_TOOLS.FILESYSTEM.AST_EDIT, astEditTool, {\n        requireWrite: true,\n        readTrackerMode: 'write',\n        useWriteLock: true,\n      });\n    }\n  }\n\n  // Search tools\n  if (workspace.canBM25 || workspace.canVector) {\n    // Build a dynamic search tool that only exposes modes the workspace supports.\n    // This prevents the LLM from picking an unsupported mode (e.g. 'hybrid' when\n    // only BM25 is configured), rather than relying solely on runtime fallback.\n    const availableModes = [\n      workspace.canBM25 ? 'bm25' : null,\n      workspace.canVector ? 'vector' : null,\n      workspace.canHybrid ? 'hybrid' : null,\n    ].filter((m): m is 'bm25' | 'vector' | 'hybrid' => m !== null);\n\n    const dynamicSearchTool = {\n      ...searchTool,\n      inputSchema: searchInputSchema.extend({\n        mode: z\n          .enum(availableModes as [(typeof availableModes)[number], ...(typeof availableModes)[number][]])\n          .optional()\n          .describe(`Search mode: ${availableModes.join(', ')}`),\n      }),\n    };\n    await addTool(WORKSPACE_TOOLS.SEARCH.SEARCH, dynamicSearchTool);\n    await addTool(WORKSPACE_TOOLS.SEARCH.INDEX, indexContentTool, { requireWrite: true });\n  }\n\n  if (workspace.sandbox) {\n    if (workspace.sandbox.executeCommand) {\n      // Pick the right tool variant based on whether processes are available\n      const baseTool = workspace.sandbox.processes ? executeCommandWithBackgroundTool : executeCommandTool;\n      await addTool(WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND, baseTool, { targets: { sandbox: true } });\n    }\n\n    // Background process tools (only when process manager is available)\n    if (workspace.sandbox.processes) {\n      await addTool(WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT, getProcessOutputTool, { targets: { sandbox: true } });\n      await addTool(WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS, killProcessTool, { targets: { sandbox: true } });\n    }\n  } else if (hasSandboxConfig(workspace)) {\n    await addTool(WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND, executeCommandWithBackgroundTool, {\n      targets: { sandbox: true },\n    });\n    await addTool(WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT, getProcessOutputTool, { targets: { sandbox: true } });\n    await addTool(WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS, killProcessTool, { targets: { sandbox: true } });\n  }\n\n  // LSP tools — always available (tool handles case when LSP not configured).\n  // Needs the filesystem resolved so lsp_inspect can map paths via the\n  // request's filesystem (resolveAbsolutePath) on dynamic-filesystem workspaces.\n  await addTool(WORKSPACE_TOOLS.LSP.LSP_INSPECT, lspInspectTool, { targets: { filesystem: true } });\n\n  return tools;\n}\n"],"mappings":"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"}