mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
chore: Remove remaining Mastra references from Instance AI (docs + source) (#32482)
This commit is contained in:
parent
ef148c6cab
commit
7b7a34993e
|
|
@ -332,7 +332,7 @@ EXAMPLES
|
|||
Example 1: Log under budget. Return empty arrays.
|
||||
|
||||
Input:
|
||||
[obs_001] CRITICAL (14:30) User is migrating @n8n/agents from Mastra to internal SDK
|
||||
[obs_001] CRITICAL (14:30) User is migrating the backend from REST to gRPC
|
||||
[obs_002] IMPORTANT (14:35) User adopted two-stage compression model (Observer + Reflector)
|
||||
Budget: 5000 tokens. Current: 600 tokens.
|
||||
|
||||
|
|
@ -421,13 +421,13 @@ BAD: Merging across topics.
|
|||
|
||||
Input:
|
||||
[obs_001] CRITICAL User works at Acme
|
||||
[obs_002] CRITICAL User is migrating @n8n/agents from Mastra
|
||||
[obs_002] CRITICAL User is migrating the backend to gRPC
|
||||
|
||||
Wrong merge:
|
||||
{
|
||||
"supersedes": ["obs_001", "obs_002"],
|
||||
"marker": "CRITICAL",
|
||||
"text": "User works at Acme and is migrating @n8n/agents from Mastra"
|
||||
"text": "User works at Acme and is migrating the backend to gRPC"
|
||||
}
|
||||
|
||||
These are about different topics. Do NOT merge them. Leave both as separate observations.
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ When the AI agent needs to invoke a local tool the call flows through
|
|||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant A as AI Agent (Mastra tool)
|
||||
participant A as AI Agent (@n8n/agents tool)
|
||||
participant GW as LocalGateway
|
||||
participant SRV as Controller (SSE)
|
||||
participant D as computer-use Daemon
|
||||
|
|
@ -304,7 +304,7 @@ rejects all outstanding promises immediately with `"Local gateway disconnected"`
|
|||
|
||||
When a tool group operates in `Ask` mode and no stored rule matches the
|
||||
resource, the daemon returns a `GATEWAY_CONFIRMATION_REQUIRED` error instead
|
||||
of a result. The Mastra tool layer handles this by suspending the agent —
|
||||
of a result. The `@n8n/agents` tool layer handles this by suspending the agent —
|
||||
persisting its state to the database — and resuming it after the user
|
||||
responds. This means the confirmation survives page reloads and server
|
||||
restarts.
|
||||
|
|
@ -316,7 +316,7 @@ sequenceDiagram
|
|||
participant DB as Database
|
||||
participant D as computer-use Daemon
|
||||
|
||||
Note over SRV: First invocation — tool execute() called by Mastra
|
||||
Note over SRV: First invocation — tool execute() called by @n8n/agents
|
||||
SRV->>D: callTool({ name, args }) via LocalGateway
|
||||
D-->>SRV: { isError: true, content: ["GATEWAY_CONFIRMATION_REQUIRED::..."] }
|
||||
SRV->>SRV: parse GatewayConfirmationRequiredPayload
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const data: ExecutionResult = parseExecutionResult(response);
|
|||
### Zod schemas are the source of truth
|
||||
|
||||
Every tool has an input schema (what the LLM sends) and an output schema
|
||||
(what the tool returns). Mastra uses these schemas to generate tool
|
||||
(what the tool returns). `@n8n/agents` uses these schemas to generate tool
|
||||
descriptions for the LLM, validate inputs at runtime, and type-check the
|
||||
execute function. If the TypeScript type and the Zod schema are defined
|
||||
separately, they drift — the LLM sees one contract, the code enforces
|
||||
|
|
@ -120,7 +120,7 @@ it('should stream tool-call event when agent uses a tool', async () => {
|
|||
|
||||
The clean interface boundary (ADR-002) makes each layer testable in
|
||||
isolation. Verify the contract at each boundary — not the wiring between
|
||||
them. Tools can be tested without Mastra, the reducer without SSE, adapters
|
||||
them. Tools can be tested without `@n8n/agents`, the reducer without SSE, adapters
|
||||
without the agent.
|
||||
|
||||
For each tool, test:
|
||||
|
|
@ -203,15 +203,15 @@ When backend and frontend both switch on event types with duplicated logic,
|
|||
a change to the format requires updating both in lockstep. Extract the
|
||||
shared part into `@n8n/api-types` or a shared utility.
|
||||
|
||||
## Mastra Patterns
|
||||
## Agent SDK Patterns
|
||||
|
||||
### Tool definitions
|
||||
|
||||
Mastra uses Zod schemas for both runtime validation and LLM tool
|
||||
`@n8n/agents` uses Zod schemas for both runtime validation and LLM tool
|
||||
descriptions. The `.describe()` strings on schema fields become the
|
||||
parameter descriptions the LLM sees when deciding how to call a tool.
|
||||
Missing or vague descriptions lead to bad tool calls. The `outputSchema`
|
||||
lets Mastra validate return values and gives the LLM structured expectations.
|
||||
lets `@n8n/agents` validate return values and gives the LLM structured expectations.
|
||||
|
||||
- Always define both `inputSchema` and `outputSchema`
|
||||
- Use `.describe()` on Zod fields — these are the LLM's parameter docs
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ directly to the event bus (ADR-014). They cannot spawn their own sub-agents.
|
|||
|
||||
### 3. Observational Memory
|
||||
|
||||
Mastra's observational memory compresses old messages into dense observations via
|
||||
`@n8n/agents` observational memory compresses old messages into dense observations via
|
||||
background Observer and Reflector agents. Tool-heavy workloads (workflow
|
||||
definitions, execution results) get 5–40x compression. This prevents context
|
||||
degradation over 50+ step autonomous loops (see ADR-016).
|
||||
|
|
@ -197,7 +197,7 @@ The agent package — framework-agnostic business logic.
|
|||
- **Storage** (`storage/`) — iteration logs, task storage, planned task storage, workflow loop storage, agent tree snapshots
|
||||
- **MCP client** (`mcp/`) — manages connections to external MCP servers, schema sanitization for Anthropic compatibility
|
||||
- **Domain access** (`domain-access/`) — domain gating and access tracking for external URL approval
|
||||
- **Stream mapping** (`stream/`) — Mastra chunk → canonical event translation, HITL consumption
|
||||
- **Stream mapping** (`stream/`) — agent chunk → canonical event translation, HITL consumption
|
||||
- **Event bus interface** (`event-bus/`) — publishing agent events to the thread channel
|
||||
- **Tracing** (`tracing/`) — LangSmith integration for step-level observability
|
||||
- **System prompt** (`agent/`) — dynamic context-aware prompt based on instance configuration
|
||||
|
|
@ -292,19 +292,19 @@ Instance AI uses n8n's module system (`@BackendModule`). This means:
|
|||
|
||||
## Runtime & Streaming
|
||||
|
||||
The agent runtime is built on Mastra's streaming primitives with added
|
||||
The agent runtime is built on `@n8n/agents` streaming primitives with added
|
||||
resumability, HITL suspension, and background task management.
|
||||
|
||||
### Stream Execution
|
||||
|
||||
```
|
||||
streamAgentRun() → agent.stream() → executeResumableStream()
|
||||
├─ for each chunk: mapMastraChunkToEvent() → eventBus.publish()
|
||||
├─ for each chunk: mapAgentChunkToEvent() → eventBus.publish()
|
||||
├─ on suspension: wait for confirmation → agent.resumeStream() → loop
|
||||
└─ return StreamRunResult {status, mastraRunId, text}
|
||||
└─ return StreamRunResult {status, agentRunId, text}
|
||||
```
|
||||
|
||||
The `executeResumableStream()` loop consumes Mastra chunks, translates them to
|
||||
The `executeResumableStream()` loop consumes agent chunks, translates them to
|
||||
canonical `InstanceAiEvent` schema, publishes to the event bus, and handles HITL
|
||||
suspension/resume cycles. Two control modes:
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ Today the main consumer is the workflow builder. The agent writes TypeScript fil
|
|||
|
||||
## How the Pieces Fit Together
|
||||
|
||||
There are three layers between the agent and actual code execution: a workspace abstraction from Mastra, a sandbox provider (n8n sandbox service or Daytona), and the execution runtime inside the sandbox. Here is how they relate:
|
||||
There are three layers between the agent and actual code execution: a workspace abstraction from `@n8n/agents`, a sandbox provider (n8n sandbox service or Daytona), and the execution runtime inside the sandbox. Here is how they relate:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Agent ["Agent Layer"]
|
||||
LLM[LLM] --> AgentRuntime["Agent Runtime (Mastra)"]
|
||||
LLM[LLM] --> AgentRuntime["Agent Runtime (@n8n/agents)"]
|
||||
end
|
||||
|
||||
subgraph WorkspaceLayer ["Workspace Abstraction (Mastra)"]
|
||||
subgraph WorkspaceLayer ["Workspace Abstraction (@n8n/agents)"]
|
||||
AgentRuntime --> Workspace["Workspace"]
|
||||
Workspace --> FS["Filesystem Interface<br/>(read, write, list, edit files)"]
|
||||
Workspace --> Sandbox["Sandbox Interface<br/>(execute shell commands)"]
|
||||
|
|
@ -42,14 +42,14 @@ graph TB
|
|||
|
||||
The agent never talks to Daytona, the n8n sandbox service, or the host filesystem directly. It only sees the Workspace, which exposes two capabilities: a filesystem (read/write/list files) and a sandbox (run shell commands). The Workspace routes those operations to whichever provider is configured.
|
||||
|
||||
## Mastra Workspaces
|
||||
## Workspaces
|
||||
|
||||
Mastra is the agent framework that Instance AI uses. A Mastra **Workspace** is a pairing of two things:
|
||||
`@n8n/agents` is the agent SDK that Instance AI uses. A **Workspace** is a pairing of two things:
|
||||
|
||||
1. **A Sandbox** — an interface for executing shell commands. It accepts a command string and returns stdout, stderr, and an exit code. Think of it as a remote terminal.
|
||||
2. **A Filesystem** — an interface for file operations: read, write, list, delete, copy, move. Think of it as a remote disk.
|
||||
|
||||
When a Workspace is attached to an agent, Mastra automatically exposes built-in tools to the LLM: `read_file`, `write_file`, `edit_file`, `list_files`, `grep`, `execute_command`, and others. The agent uses these tools naturally in its reasoning loop — it writes a file, runs a command, reads the output, and decides what to do next.
|
||||
When a Workspace is attached to an agent, `@n8n/agents` automatically exposes built-in tools to the LLM: `read_file`, `write_file`, `edit_file`, `list_files`, `grep`, `execute_command`, and others. The agent uses these tools naturally in its reasoning loop — it writes a file, runs a command, reads the output, and decides what to do next.
|
||||
|
||||
The key design property is that the Workspace abstraction is provider-agnostic. The agent's code and prompts are identical regardless of whether the workspace is backed by n8n sandbox service or Daytona. The provider choice is purely an infrastructure decision.
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ sequenceDiagram
|
|||
D-->>n8n: Sandbox ID
|
||||
|
||||
n8n->>S: Write node-types catalog via filesystem API
|
||||
n8n->>n8n: Wrap sandbox as Mastra Workspace
|
||||
n8n->>n8n: Wrap sandbox as Workspace
|
||||
n8n->>n8n: Inject Workspace into builder agent
|
||||
|
||||
Note over S: Agent works inside sandbox
|
||||
|
|
@ -124,7 +124,7 @@ The process starts with a **pre-warmed image**. On first use, n8n builds a Dayto
|
|||
|
||||
One thing that cannot be baked into the image is the **node-types catalog** (a searchable index of all available n8n nodes). It is too large for the image build API, so it is written to each sandbox after creation via the filesystem API.
|
||||
|
||||
Once the sandbox is provisioned and the catalog is written, n8n wraps it in a Mastra Workspace and hands it to the builder agent. From that point, the agent works autonomously inside the sandbox — writing files, running the compiler, fixing errors, iterating — until it produces a valid workflow.
|
||||
Once the sandbox is provisioned and the catalog is written, n8n wraps it in a Workspace and hands it to the builder agent. From that point, the agent works autonomously inside the sandbox — writing files, running the compiler, fixing errors, iterating — until it produces a valid workflow.
|
||||
|
||||
### What is inside a Daytona sandbox
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ Once the sandbox is provisioned and the catalog is written, n8n wraps it in a Ma
|
|||
|
||||
## n8n Sandbox Service: Default Provider
|
||||
|
||||
The n8n sandbox service exposes a simple HTTP API for creating sandboxes, executing shell commands, and manipulating files. Instance AI uses it through a custom Mastra sandbox and filesystem adapter.
|
||||
The n8n sandbox service exposes a simple HTTP API for creating sandboxes, executing shell commands, and manipulating files. Instance AI uses it through a custom `@n8n/agents` sandbox and filesystem adapter.
|
||||
|
||||
This provider supports the builder's file and command workflow, but it does not expose interactive process handles. That means `execute_command` works, while process-manager-backed features such as long-lived spawned subprocesses are out of scope for this provider.
|
||||
|
||||
|
|
|
|||
|
|
@ -482,14 +482,14 @@ replaying all SSE events.
|
|||
|
||||
### How It Works
|
||||
|
||||
1. **Mastra V2 messages** — Mastra persists tool invocations, reasoning, and
|
||||
text in its V2 message format. The backend parses these into rich
|
||||
1. **Persisted messages** — `@n8n/agents` persists tool invocations, reasoning, and
|
||||
text in its message format. The backend parses these into rich
|
||||
`InstanceAiMessage[]` objects with tool calls and flat agent trees.
|
||||
|
||||
2. **Agent tree snapshots** — after each `run-finish`, the backend replays
|
||||
events through `buildAgentTreeFromEvents()` and stores the resulting tree
|
||||
in thread metadata. This preserves the full sub-agent hierarchy (tool
|
||||
calls, text, reasoning) that the V2 message format alone cannot capture.
|
||||
calls, text, reasoning) that the message format alone cannot capture.
|
||||
|
||||
3. **SSE cursor** — the messages response includes `nextEventId`. The frontend
|
||||
sets its SSE cursor to `nextEventId - 1` so the SSE connection only receives
|
||||
|
|
|
|||
|
|
@ -720,11 +720,11 @@ everything; sub-agents receive only what they need.
|
|||
|
||||
1. Create a file in `src/tools/<domain>/` following the naming convention `<verb>-<noun>.tool.ts`
|
||||
2. Define input/output schemas with Zod (`.describe()` on fields — these are the LLM's parameter docs)
|
||||
3. Export a factory function that takes the service context and returns a Mastra tool
|
||||
3. Export a factory function that takes the service context and returns an `@n8n/agents` tool
|
||||
4. Register the tool in `src/tools/index.ts` (in `createAllTools` or `createOrchestrationTools`)
|
||||
5. If the tool requires a new service method, add it to the interface in `src/types.ts`
|
||||
and implement it in the backend adapter
|
||||
6. New native domain tools are automatically available for delegation — the
|
||||
orchestrator can include them in sub-agent tool subsets via `delegate`
|
||||
7. For HITL tools, define `suspendSchema` and `resumeSchema` — Mastra handles
|
||||
7. For HITL tools, define `suspendSchema` and `resumeSchema` — `@n8n/agents` handles
|
||||
the suspension/resume lifecycle automatically
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ function loadSourceMapSupport(): SourceMapSupportModule | undefined {
|
|||
}
|
||||
|
||||
const FILTERED_PATH_FRAGMENTS = [
|
||||
'/node_modules/@mastra/core/dist/chunk-',
|
||||
'/node_modules/@ai-sdk/',
|
||||
'/node_modules/langsmith/',
|
||||
'/node_modules/zod/',
|
||||
|
|
|
|||
|
|
@ -307,10 +307,9 @@ export function createToolsFromLocalMcpServer(
|
|||
try {
|
||||
if (toolName === 'browser_create_credential') {
|
||||
// when converting json schema the `inputSchema` has the correct shape and parsed to correct output
|
||||
// but during execution all unspecified key from `data` and `resolveData` are stripped.
|
||||
// somewhere in mastra core the inputSchema is converted multiple times back and forth and
|
||||
// gets transformed to jsonSchema with `additionalProperties=false`
|
||||
// this does not happen when passing the schema directly
|
||||
// but during execution all unspecified keys from `data` and `resolveData` are stripped,
|
||||
// because the schema is converted back and forth and transformed to jsonSchema with
|
||||
// `additionalProperties=false`. Passing the schema directly avoids this.
|
||||
inputSchema = loadMcpBrowserCredential().browserCreateCredentialSchema;
|
||||
} else {
|
||||
// Convert JSON Schema → Zod (v3) so the LLM sees the actual parameter shapes.
|
||||
|
|
|
|||
|
|
@ -6001,7 +6001,7 @@
|
|||
"instanceAi.debug.threads.title": "Thread Inspector",
|
||||
"instanceAi.debug.threads.current": "Current",
|
||||
"instanceAi.debug.threads.messages": "Messages",
|
||||
"instanceAi.debug.threads.noThreads": "No threads found in Mastra storage",
|
||||
"instanceAi.debug.threads.noThreads": "No threads found in storage",
|
||||
"instanceAi.debug.threads.noMessages": "No messages stored",
|
||||
"instanceAi.debug.threads.refresh": "Refresh",
|
||||
"instanceAi.debug.threads.fetchError": "Failed to load thread data",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const useInstanceAiStore = defineStore('instanceAi', () => {
|
|||
const thread = threads.value.find((t) => t.id === threadId);
|
||||
if (thread) thread.title = title;
|
||||
},
|
||||
// Refresh thread list to pick up Mastra-generated titles
|
||||
// Refresh thread list to pick up auto-generated titles
|
||||
onRunFinish: () => {
|
||||
void loadThreads();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ export function createThreadRuntime(
|
|||
if (parsed.data.type === 'run-start' || parsed.data.type === 'run-finish') {
|
||||
triggerRef(messages);
|
||||
}
|
||||
// When a run finishes, refresh thread list to pick up Mastra-generated titles
|
||||
// When a run finishes, refresh thread list to pick up auto-generated titles
|
||||
if (previousRunId && activeRunId.value === null) {
|
||||
hooks.onRunFinish();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user