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 workspacesLien 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.
FonctionnementLien 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.
UtilisationLien direct vers Utilisation
Créer un workspaceLien direct vers Créer un workspace
Créez un workspace en instanciant la classe Workspace avec les fonctionnalités souhaitées :
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 globalLien 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 :
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’agentLien direct vers Workspace au niveau de l’agent
Attribuez directement un workspace à un agent pour remplacer le workspace global :
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 nettoyageLien 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 configurationLien 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.
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 uniquementLien 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 uniquementLien 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énario | Modèle |
|---|---|
| Développement local avec fichiers et commandes | filesystem + sandbox (tous deux locaux, même répertoire) |
| Stockage cloud accessible dans une sandbox cloud | mounts + sandbox |
| Plusieurs fournisseurs cloud dans une même sandbox | mounts + sandbox (un montage par fournisseur) |
| L’agent lit et écrit des fichiers sans avoir besoin d’exécuter de commandes | filesystem uniquement |
| L’agent exécute des commandes sans avoir besoin d’outils de fichiers | sandbox uniquement |
| Agent à plusieurs rôles ou application mutualisée avec stockage propre à chaque requête | filesystem avec une fonction de résolution |
| Agent mutualisé avec une portée d’exécution propre à chaque requête | sandbox avec une fonction de résolution |
Configuration des outilsLien 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.
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 outilsLien direct vers Options des outils
| Option | Type | Description |
|---|---|---|
enabled | boolean | (context) => boolean | Indique 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. |
requireApproval | boolean | (context) => boolean | Indique 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. |
requireReadBeforeWrite | boolean | (context) => boolean | Pour 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. |
name | string | Nom personnalisé de l’outil. Remplace le nom par défaut mastra_workspace_*. |
maxOutputTokens | number | Nombre 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 outilsLien 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.
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’outilsLien 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.
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 outilsLien 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 :
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 LSPLien 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 sortiesLien 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 :
- 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). - 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 écritureLien 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 :
- 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. - Niveau du système de fichiers : au moment de l’écriture,
writeFile()compare la date de modification actuelle du fichier à la valeur attendue (transmise viaexpectedMtimedans les options d’écriture). Si elles ne correspondent pas, la méthode lève une erreurStaleFileError. 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,
})
InitialisationLien 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.
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 deautoIndexPaths. 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.