Aller au contenu principal

Workspaces

Ajouté dans : @mastra/core@1.1.0

Un workspace Mastra fournit aux agents un environnement persistant pour stocker des fichiers et exécuter des commandes. Les agents utilisent les outils du workspace pour lire et écrire des fichiers, exécuter des commandes shell et rechercher du contenu indexé.

Un workspace prend en charge les fonctionnalités suivantes :

  • Système de fichiers : stockage de fichiers (lecture, écriture, listage, suppression, copie, déplacement et recherche avec grep)
  • Sandbox : exécution de commandes (commandes shell) et processus en arrière-plan
  • Inspection LSP : requêtes de survol, de définition et d’implémentation via des serveurs de langage
  • Recherche : recherche BM25, vectorielle ou hybride dans le contenu indexé
  • Skills : instructions réutilisables pour les agents

Quand utiliser les workspaces
Lien direct vers Quand utiliser les workspaces

Utilisez un workspace lorsque votre agent doit accéder au système de fichiers local, à des commandes shell, à l’inspection sémantique du code, à la recherche indexée ou à des instructions de skill réutilisables.

Fonctionnement
Lien direct vers Fonctionnement

Lorsque vous attribuez un workspace à un agent, Mastra inclut les outils correspondants dans l’ensemble d’outils de l’agent. Celui-ci peut alors les utiliser pour interagir avec des fichiers et exécuter des commandes.

Vous pouvez créer un workspace avec n’importe quelle combinaison des fonctionnalités prises en charge. L’agent reçoit uniquement les outils correspondant à la configuration.

Utilisation
Lien direct vers Utilisation

Créer un workspace
Lien direct vers Créer un workspace

Créez un workspace en instanciant la classe Workspace avec les fonctionnalités souhaitées :

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({
basePath: './workspace',
}),
sandbox: new LocalSandbox({
workingDirectory: './workspace',
}),
skills: ['skills'],
})

Le tableau skills indique les chemins des répertoires contenant les définitions de skills. Consultez la page Skills.

Workspace global
Lien direct vers Workspace global

Définissez un workspace sur l’instance Mastra. Tous les agents en héritent, sauf s’ils définissent le leur :

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
})

const mastra = new Mastra({
workspace,
})

Workspace au niveau de l’agent
Lien direct vers Workspace au niveau de l’agent

Attribuez directement un workspace à un agent pour remplacer le workspace global :

src/mastra/agents/my-agent.ts
import { Agent } from '@mastra/core/agent'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './agent-workspace' }),
})

export const myAgent = new Agent({
id: 'my-agent',
model: 'openai/gpt-5.6-sol',
workspace,
})

Cycle de vie et nettoyage
Lien direct vers Cycle de vie et nettoyage

Mastra enregistre les workspaces globaux et ceux des agents afin qu’ils puissent être répertoriés et récupérés lors de l’exécution. Lorsque vous appelez mastra.shutdown(), Mastra détruit les workspaces enregistrés dont il est propriétaire. Cette opération ferme les ressources du workspace, comme les serveurs de langage, les navigateurs, les processus de sandbox et les handles des fournisseurs de systèmes de fichiers.

Pour effectuer un nettoyage manuel, utilisez mastra.removeWorkspace(). Transmettez { destroy: true } lorsque le workspace doit être détruit avant d’être retiré du registre.

Les fournisseurs statiques appartiennent au workspace. Les fournisseurs reposant sur un résolveur appartiennent à votre application, car le workspace les crée au moment de la requête. Consultez la section sur la gestion du cycle de vie des sandboxes lors de l’exécution pour connaître le modèle de nettoyage des résolveurs.

Modèles de configuration
Lien direct vers Modèles de configuration

Les workspaces prennent en charge plusieurs modèles de configuration selon les fonctionnalités dont votre agent a besoin. Les principaux composants sont filesystem (outils de fichiers) et sandbox (exécution de commandes), tandis que mounts permet de relier le stockage cloud aux sandboxes.

Système de fichiers + sandbox (local)
Lien direct vers Système de fichiers + sandbox (local)

Pour le développement local, associez un LocalFilesystem et un LocalSandbox qui pointent vers le même répertoire. Comme tous deux fonctionnent sur la machine locale, les fichiers écrits via le système de fichiers sont immédiatement accessibles aux commandes de la sandbox :

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
})

L’agent reçoit à la fois les outils de fichiers et execute_command. Il s’agit de la configuration complète la plus simple.

Montages + sandbox (stockage cloud)
Lien direct vers Montages + sandbox (stockage cloud)

Lorsque vous devez rendre un stockage cloud accessible dans une sandbox, utilisez mounts. Cette option monte le système de fichiers cloud dans la sandbox à l’aide de FUSE, afin que les commandes puissent lire et écrire des fichiers dans le chemin de montage :

const workspace = new Workspace({
mounts: {
'/data': new S3Filesystem({
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
}),
'/skills': new GCSFilesystem({
bucket: 'agent-skills',
}),
},
sandbox: new E2BSandbox({ id: 'dev-sandbox' }),
})

En interne, mounts crée un CompositeFilesystem qui achemine les opérations des outils de fichiers vers le bon fournisseur selon le préfixe du chemin. Les commandes de la sandbox accèdent directement aux chemins montés (par exemple, ls /data).

Vous pouvez monter plusieurs fournisseurs sur des chemins différents. Chaque chemin de montage doit être unique et ne pas chevaucher les autres.

remarque

filesystem et mounts s’excluent mutuellement : vous ne pouvez pas utiliser les deux dans un même workspace. Utilisez filesystem avec un fournisseur unique sans sandbox, ou mounts lorsque vous devez associer du stockage cloud à une sandbox.

Système de fichiers uniquement
Lien direct vers Système de fichiers uniquement

Utilisez un seul filesystem lorsque les agents ont uniquement besoin de lire et d’écrire des fichiers. L’exécution de commandes n’est pas disponible.

const workspace = new Workspace({
filesystem: new S3Filesystem({
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
}),
})

L’agent reçoit des outils de fichiers (read_file, write_file, list_directory, grep, etc.) qui agissent directement sur le fournisseur de stockage.

Sandbox uniquement
Lien direct vers Sandbox uniquement

Utilisez une seule sandbox lorsque les agents ont uniquement besoin d’exécuter des commandes. Aucun outil de fichiers n’est ajouté.

const workspace = new Workspace({
sandbox: new E2BSandbox({ id: 'dev-sandbox' }),
})

L’agent reçoit l’outil execute_command.

Système de fichiers dynamique (par requête)
Lien direct vers Système de fichiers dynamique (par requête)

Transmettez une fonction de résolution à filesystem pour renvoyer un système de fichiers différent pour chaque requête. Cela est utile pour les applications mutualisées ou les agents à plusieurs rôles, lorsque chaque requête nécessite une racine de stockage ou des autorisations différentes.

const workspace = new Workspace({
filesystem: ({ requestContext }) => {
const role = requestContext.get('agent-role') || 'guest'
return new LocalFilesystem({
basePath: `/workspaces/${role}`,
readOnly: role !== 'admin',
})
},
})

Une même instance de workspace traite toutes les requêtes. Le résolveur s’exécute au moment où l’outil est appelé, de sorte que chaque requête dispose de son propre système de fichiers. Consultez la section sur le système de fichiers dynamique pour plus de détails.

Sandbox dynamique (par requête)
Lien direct vers Sandbox dynamique (par requête)

Transmettez une fonction de résolution à sandbox pour renvoyer une sandbox différente pour chaque requête. Cela est utile pour les déploiements mutualisés, lorsque chaque utilisateur ou rôle nécessite un répertoire de travail isolé ou des autorisations d’exécution différentes.

const workspace = new Workspace({
sandbox: ({ requestContext }) => {
const userId = requestContext.get('user-id') as string
return new LocalSandbox({
workingDirectory: `/workspaces/${userId}`,
})
},
})

Le résolveur est incompatible avec mounts et lsp: true, car ces deux options nécessitent une instance de sandbox concrète au moment de la construction. Consultez la section sur la sandbox dynamique pour plus de détails.

Quel modèle utiliser ?
Lien direct vers Quel modèle utiliser ?

ScénarioModèle
Développement local avec fichiers et commandesfilesystem + sandbox (tous deux locaux, même répertoire)
Stockage cloud accessible dans une sandbox cloudmounts + sandbox
Plusieurs fournisseurs cloud dans une même sandboxmounts + sandbox (un montage par fournisseur)
L’agent lit et écrit des fichiers sans avoir besoin d’exécuter de commandesfilesystem uniquement
L’agent exécute des commandes sans avoir besoin d’outils de fichierssandbox uniquement
Agent à plusieurs rôles ou application mutualisée avec stockage propre à chaque requêtefilesystem avec une fonction de résolution
Agent mutualisé avec une portée d’exécution propre à chaque requêtesandbox avec une fonction de résolution

Configuration des outils
Lien direct vers Configuration des outils

Configurez le comportement des outils via l’option tools du workspace. Celle-ci détermine les outils activés et leur comportement.

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
tools: {
// Global defaults
enabled: true,
requireApproval: false,

// Per-tool overrides
[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: {
requireApproval: true,
requireReadBeforeWrite: true,
},
[WORKSPACE_TOOLS.FILESYSTEM.DELETE]: {
enabled: false,
},
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
requireApproval: true,
},
},
})

Options des outils
Lien direct vers Options des outils

OptionTypeDescription
enabledboolean | (context) => booleanIndique si l’outil est disponible (valeur par défaut : true). Lorsqu’il s’agit d’une fonction, celle-ci est évaluée au moment de répertorier les outils.
requireApprovalboolean | (context) => booleanIndique si l’outil nécessite l’approbation de l’utilisateur avant son exécution (valeur par défaut : false). Lorsqu’il s’agit d’une fonction, celle-ci est évaluée au moment de l’exécution et peut accéder à args.
requireReadBeforeWriteboolean | (context) => booleanPour les outils d’écriture : exige la lecture préalable du fichier (valeur par défaut : false). Lorsqu’il s’agit d’une fonction, celle-ci est évaluée au moment de l’exécution et peut accéder à args.
namestringNom personnalisé de l’outil. Remplace le nom par défaut mastra_workspace_*.
maxOutputTokensnumberNombre maximal de tokens dans la sortie de l’outil (valeur par défaut : 2000). Toute sortie dépassant cette limite est tronquée à l’aide de tiktoken.

Configuration dynamique des outils
Lien direct vers Configuration dynamique des outils

Les options d’outil qui acceptent des fonctions reçoivent un objet de contexte et renvoient une valeur booléenne. Elles permettent ainsi d’adapter le comportement des outils au contexte.

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
tools: {
// Dynamic enabled: disable command execution unless explicitly allowed
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
enabled: async ({ requestContext }) => {
return requestContext['allowExecution'] === 'true'
},
},

// Dynamic requireApproval: only require approval for protected paths
[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: {
requireApproval: async ({ args }) => {
return (args.path as string).startsWith('/protected')
},
requireReadBeforeWrite: true,
},
},
})

Les fonctions de enabled reçoivent { requestContext, workspace }. Celles de requireApproval et requireReadBeforeWrite reçoivent également args, car elles sont évaluées lors de l’appel de l’outil.

Remappage des noms d’outils
Lien direct vers Remappage des noms d’outils

Renommez les outils du workspace pour respecter les conventions attendues par votre agent. La clé de configuration reste la constante WORKSPACE_TOOLS d’origine ; seul le nom exposé change.

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
lsp: true,
tools: {
[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: { name: 'view' },
[WORKSPACE_TOOLS.FILESYSTEM.GREP]: { name: 'search_content' },
[WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES]: { name: 'find_files' },
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: { name: 'execute_command' },
[WORKSPACE_TOOLS.LSP.LSP_INSPECT]: { name: 'lsp_inspect' },
},
})

L’agent voit view, search_content, find_files, execute_command et lsp_inspect à la place des noms mastra_workspace_* par défaut. Les noms d’outils doivent être uniques : les doublons ou les conflits avec d’autres noms par défaut provoquent une erreur.

Hooks des outils
Lien direct vers Hooks des outils

Définissez tools.hooks pour exécuter une logique avant et après chaque appel d’un outil de workspace activé. Les hooks s’exécutent après le remappage des noms ; leur contexte comprend donc à la fois le toolName exposé et le workspaceToolName d’origine :

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
tools: {
hooks: {
beforeToolCall: ({ toolName, workspaceToolName, input }) => {
console.log(`Running ${toolName} (${workspaceToolName})`, input)
},
afterToolCall: ({ toolName, output, error }) => {
console.log(`Finished ${toolName}`, { output, error })
},
},
},
})

Renvoyez { proceed: false, output } depuis beforeToolCall pour ignorer l’appel de l’outil et utiliser output comme résultat.

Si l’agent propriétaire définit également des hooks d’outils, ceux du workspace s’exécutent dans l’enveloppe des hooks de l’agent. L’ordre est le suivant : beforeToolCall de l’agent, beforeToolCall du workspace, l’outil, afterToolCall du workspace, puis afterToolCall de l’agent.

Inspection LSP
Lien direct vers Inspection LSP

Activez lsp sur un workspace pour ajouter l’inspection sémantique du code via des serveurs de langage. Cette option ajoute par défaut l’outil mastra_workspace_lsp_inspect, qui peut renvoyer des informations de survol, des emplacements de définition et les implémentations d’un symbole à une position précise du curseur.

Consultez la page Inspection LSP pour découvrir la configuration, des exemples et le remappage des noms d’outils.

Troncature des sorties
Lien direct vers Troncature des sorties

Les outils du workspace tronquent automatiquement les sorties volumineuses pour éviter de dépasser les limites de contexte du LLM. Les niveaux de troncature suivants s’appliquent :

  1. Fin basée sur les lignes : la sortie des commandes est limitée par défaut aux 200 dernières lignes (configurable pour chaque commande via le paramètre tail).
  2. Limite basée sur les tokens : la sortie des outils est limitée par défaut à 2 000 tokens.

Définissez maxOutputTokens pour chaque outil afin d’ajuster la limite de tokens :

const workspace = new Workspace({
// ...
tools: {
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
maxOutputTokens: 5000,
},
},
})

Les séquences d’échappement ANSI (couleurs et séquences de curseur) sont automatiquement supprimées de la sortie des commandes avant son envoi au modèle.

Lecture avant écriture
Lien direct vers Lecture avant écriture

Lorsque requireReadBeforeWrite est activé sur les outils d’écriture, les agents doivent lire un fichier avant de le modifier. Cela évite d’écraser des fichiers que l’agent n’a pas consultés :

  • Nouveaux fichiers : peuvent être écrits sans lecture préalable (ils n’existent pas encore)
  • Fichiers existants : doivent d’abord être lus
  • Fichiers modifiés de l’extérieur : si un fichier a changé depuis sa lecture par l’agent, l’écriture échoue

La sécurité des écritures de fichiers est appliquée à deux niveaux :

  1. Niveau de l’outil : avant l’exécution d’un outil d’écriture, le suivi des lectures vérifie si le fichier a été modifié depuis sa dernière lecture. Si c’est le cas, l’outil lève une erreur FileReadRequiredError.
  2. Niveau du système de fichiers : au moment de l’écriture, writeFile() compare la date de modification actuelle du fichier à la valeur attendue (transmise via expectedMtime dans les options d’écriture). Si elles ne correspondent pas, la méthode lève une erreur StaleFileError. Ce mécanisme détecte les modifications externes, par exemple l’enregistrement du fichier dans un éditeur, qui se produisent entre la vérification au niveau de l’outil et l’écriture effective.

Lorsque requireReadBeforeWrite est activé, les outils du workspace transmettent automatiquement la date de modification enregistrée. Vous pouvez également utiliser directement expectedMtime lorsque vous appelez filesystem.writeFile() en dehors des outils :

const stat = await filesystem.stat('/docs/file.md')
// ... later ...
await filesystem.writeFile('/docs/file.md', newContent, {
expectedMtime: stat.modifiedAt,
})

Initialisation
Lien direct vers Initialisation

L’appel de init() est facultatif dans la plupart des cas, car certains fournisseurs s’initialisent lors de la première opération. Appelez init() manuellement lorsque vous utilisez un workspace en dehors de Mastra (scripts autonomes ou tests), ou lorsque vous devez préprovisionner des ressources avant la première interaction de l’agent.

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
})

// Optional: pre-create directories and sandbox before first use
await workspace.init()

Fonctionnement de init()
Lien direct vers what-init-does

L’initialisation exécute la logique de configuration de chaque fournisseur configuré :

  • LocalFilesystem : crée le répertoire de base s’il n’existe pas.
  • LocalSandbox : crée le répertoire de travail.
  • Search (si configuré) : indexe les fichiers provenant de autoIndexPaths. Consultez la page Recherche et indexation.

Les fournisseurs externes peuvent effectuer des opérations de configuration supplémentaires, comme établir des connexions ou s’authentifier.