Skip to content

September 3, 2026

Latest

Choose a tag to compare

@PaulieScanlon PaulieScanlon released this 04 Sep 13:14
· 75 commits to main since this release

Highlights

Reusable Sandbox Templates + Warm Repo Checkouts (E2B & Platform)

New reusable template APIs in @mastra/platform-workspace plus @mastra/e2b repo templates let sandboxes start from a pre-cloned, pre-built repository image (with background rebuilds and resource sizing), dramatically reducing cold-start time for code sessions and workspace-backed agents.

Unified workingDirectory Across All Sandbox Providers

A new workingDirectory option in MastraSandboxOptions is now honored by every sandbox provider (with a sandbox.workingDirectory getter), standardizing default command/process CWD behavior across Docker/E2B/Vercel/Railway/etc. while still allowing per-command cwd overrides.

Client-Side Tools Can Use Server-Defined toModelOutput

Client-executed tools (browser tools without execute) now support server-defined toModelOutput, so the server can transform the returned payload into model-ready content (e.g., turning { fileId, dataUrl } into an image content part) without custom input processors.

Observability Feedback Review Workflow (API + Storage + UI Plumbing)

Feedback records now support reviewStatus (needs-review/reviewed) with filtering in listFeedback, a new storage method updateFeedbackReviewStatus, and a new server endpoint PATCH /api/observability/feedback/:feedbackId/review-status exposed in @mastra/client-js.

New @mastra/evals/vitest Test Runner Integration

@mastra/evals/vitest lets you run runEvals as Vitest tests with expectEvals/expectEval, optional matchers, and a reporter that prints per-test scores—making evals easier to gate in CI.

Breaking Changes

  • @mastra/factory: sandbox config is now a callback (ctx => new E2BSandbox({ id: ctx.sessionId })) rather than an options object; workdir/maxSandboxes and the sandbox fleet/reattach model are removed.
  • @mastra/playground-ui: Chip, ChipsGroup, and StatusBadge were removed in favor of a single Badge component (now a <span> with updated prop typing).

Changelog

@mastra/core@1.64.0

Minor Changes

  • Added a review workflow status to observability feedback. (#22805)

    • Feedback records now carry a reviewStatus (needs-review | reviewed), defaulting to needs-review and settable at creation; listFeedback can filter on it.
    • New storage method updateFeedbackReviewStatus and PATCH /api/observability/feedback/:feedbackId/review-status endpoint (requires observability:write), exposed on the client as updateFeedbackReviewStatus.
    const { feedback } = await client.listFeedback({
      filters: { reviewStatus: 'needs-review' },
      pagination: { page: 0, perPage: 20 },
    });
    
    await client.updateFeedbackReviewStatus({
      feedbackId: feedback[0].feedbackId,
      reviewStatus: 'reviewed',
    });

    Also in: @mastra/client-js@1.43.0, @mastra/server@1.64.0

  • Added support for server-defined toModelOutput on client-side tools. When a tool without an execute function runs in the browser and sends its result back, the server tool definition's toModelOutput now transforms that result before the model sees it — matching AI SDK behavior. This lets a client tool return a compact payload (like an uploaded file id or base64 image) and have the server map it into real model content: (#22437)

    import { createTool } from '@mastra/core/tools';
    import { z } from 'zod';
    
    // No execute: the browser runs this tool and returns { fileId, dataUrl }
    const takeScreenshot = createTool({
      id: 'takeScreenshot',
      description: 'Captures the screen',
      inputSchema: z.object({}),
      outputSchema: z.object({ fileId: z.string(), dataUrl: z.string() }),
      toModelOutput: output => ({
        type: 'content',
        value: [{ type: 'image-url', url: output.dataUrl }],
      }),
    });

    Previously the model only ever saw the raw JSON tool result and transforming it required a custom input processor.

  • Added a workingDirectory option to MastraSandboxOptions, honored by every sandbox provider (#22697)

    Every sandbox now accepts one instance-level workingDirectory option that sets the default directory for command execution and process spawns. A per-command cwd always wins over it, and when neither is provided each provider keeps its previous default (E2B home, docker /workspace, Vercel serverless /tmp, and so on). The effective value is readable through the new sandbox.workingDirectory getter.

    const sandbox = new E2BSandbox({ workingDirectory: '/home/user/my-repo' });
    await sandbox.executeCommand('pwd'); // /home/user/my-repo
    await sandbox.executeCommand('pwd', [], { cwd: '/tmp' }); // /tmp

    Providers that already carried this concept under other names keep those names working as deprecated aliases feeding the same field: workingDir on @mastra/docker and @mastra/apple-container, and workdir on @mastra/modal. When both the alias and workingDirectory are set, workingDirectory wins. Use absolute paths: the value is passed to the provider as-is, so ~ and environment variables like $HOME are not expanded (except where a provider documents expansion, such as LocalSandbox expanding ~).

    Also in: @mastra/agentcore@0.5.0, @mastra/apple-container@0.5.0, @mastra/blaxel@0.9.0, @mastra/cloudflare-sandbox@0.3.0, @mastra/daytona@0.10.0, @mastra/docker@0.7.0, @mastra/e2b@0.11.0, @mastra/modal@0.6.0, @mastra/platform-workspace@1.5.0, @mastra/railway@0.8.0, @mastra/vercel@1.5.0

Patch Changes

  • Update provider registry and model documentation with latest models and providers (3910c77)

  • Fixed SlashCommandChannelHandler, SlashCommandChannelHandlerConfig, and SlashCommandEvent not being exported from @mastra/core/channels. Standalone slash-command handlers can now be typed directly instead of reaching through ChannelHandlers['onSlashCommand']. (#22814)

    import type { SlashCommandChannelHandler } from '@mastra/core/channels';
    
    const onSlashCommand: SlashCommandChannelHandler = async (event, defaultHandler) => {
      if (event.command === '/help') {
        await event.channel.post('Available commands: /help');
        return;
      }
      await defaultHandler();
    };
  • Fixed approval event delivery for function-form toolDisplay in streaming channels. A function-form toolDisplay now receives approval events when streaming: true, matching static mode. Return { kind: 'post', message } to replace the built-in approval card; undefined, blank, or stream results fall back to the built-in card so the approval stays actionable. (#22832)

  • Exported the validateToolOutput helper from @mastra/core/tools so integrations can validate tool results against a schema and produce the same structured validation error as createTool. (#22779)

  • Update README to include accurate, up-to-date information (#22858)

    Also in: @mastra/acp@0.4.1, @mastra/agent-browser@0.5.2, @mastra/agent-builder@1.1.16, @mastra/agentcore@0.5.0, @mastra/agentfs@0.2.1, @mastra/ai-sdk@1.10.1, @mastra/apple-container@0.5.0, @mastra/archil@0.2.1, @mastra/arize@1.3.13, @mastra/arthur@0.4.13, @mastra/astra@1.1.1, @mastra/auth@1.1.3, @mastra/auth-auth0@1.2.3, @mastra/auth-better-auth@1.1.5, @mastra/auth-clerk@1.2.4, @mastra/auth-cloud@1.2.5, @mastra/auth-firebase@1.1.2, @mastra/auth-google@0.1.2, @mastra/auth-neon@0.3.2, @mastra/auth-okta@0.2.2, @mastra/auth-studio@1.3.5, @mastra/auth-supabase@1.1.4, @mastra/auth-workos@1.6.5, @mastra/azure@0.3.1, @mastra/blaxel@0.9.0, @mastra/braintrust@1.3.10, @mastra/brightdata@0.3.1, @mastra/browser-firecrawl@0.2.2, @mastra/browser-viewer@0.2.3, @mastra/chroma@1.1.3, @mastra/claude@0.3.1, @mastra/clickhouse@1.16.1, @mastra/client-js@1.43.0, @mastra/cloudflare@1.6.4, @mastra/cloudflare-d1@1.3.2, @mastra/cloudflare-sandbox@0.3.0, @mastra/code-sdk@1.6.0, @mastra/codemod@1.1.2, @mastra/convex@1.5.6, @mastra/couchbase@1.1.2, @mastra/cursor@0.3.1, @mastra/datadog@1.4.5, @mastra/daytona@0.10.0, @mastra/deepeval@0.1.7, @mastra/deployer@1.64.0, @mastra/deployer-cloud@1.64.0, @mastra/deployer-cloudflare@1.2.23, @mastra/deployer-netlify@1.2.23, @mastra/deployer-sandbox@0.3.8, @mastra/deployer-vercel@1.2.23, @mastra/docker@0.7.0, @mastra/dsql@1.3.4, @mastra/duckdb@1.6.4, @mastra/dynamodb@1.3.3, @mastra/e2b@0.11.0, @mastra/e2b-desktop@0.1.1, @mastra/editor@0.14.3, @mastra/elasticsearch@1.4.1, @mastra/elysia@0.1.4, @mastra/evals@1.10.0, @mastra/express@1.5.8, @mastra/factory@0.12.0, @mastra/fastembed@1.3.1, @mastra/fastify@1.5.8, @mastra/files-sdk@0.3.1, @mastra/gcs@0.3.3, @mastra/github-signals@0.4.0, @mastra/google-cloud-pubsub@1.1.3, @mastra/google-drive@0.2.1, @mastra/hono@1.7.6, @mastra/inngest@1.8.9, @mastra/isolated-vm@0.1.2, @mastra/koa@1.7.8, @mastra/laminar@1.3.15, @mastra/lance@1.3.2, @mastra/langfuse@1.5.4, @mastra/langsmith@1.3.15, @mastra/libsql@1.22.3, @mastra/livekit@0.3.1, @mastra/loggers@1.3.1, @mastra/mcp@1.17.3, @mastra/mcp-docs-server@1.2.23, @mastra/mcp-registry-registry@1.1.2, @mastra/memory@1.28.2, @mastra/mesa@0.2.1, @mastra/modal@0.6.0, @mastra/mongodb@1.18.5, @mastra/mssql@1.7.4, @mastra/mysql@0.8.5, @mastra/nestjs@0.2.23, @mastra/next@0.2.22, @mastra/observability@1.17.5, @mastra/openai@1.1.1, @mastra/opencode@0.1.23, @mastra/opensearch@1.1.1, @mastra/oracledb@0.2.2, @mastra/otel-bridge@1.5.5, @mastra/otel-exporter@1.3.13, @mastra/parallel@0.1.1, @mastra/perplexity@0.2.1, @mastra/pg@1.22.3, @mastra/pinecone@1.1.1, @mastra/platform-workspace@1.5.0, @mastra/playground-ui@52.0.0, @mastra/posthog@1.3.7, @mastra/qdrant@1.1.3, @mastra/quickjs@0.1.1, @mastra/rag@2.6.1, @mastra/railway@0.8.0, @mastra/react@1.4.10, @mastra/redis@1.4.3, @mastra/redis-streams@0.4.1, @mastra/s3@0.6.2, @mastra/s3vectors@1.1.2, @mastra/schema-compat@1.3.8, @mastra/sentry@1.2.15, @mastra/server@1.64.0, @mastra/slack@1.6.3, @mastra/spanner@1.6.4, @mastra/stagehand@0.3.4, @mastra/tanstack-start@0.2.22, @mastra/tavily@1.1.2, @mastra/telegram@0.1.1, @mastra/temporal@0.4.2, @mastra/turbopuffer@1.2.1, @mastra/turso@0.1.3, @mastra/upstash@1.4.4, @mastra/valkey@0.2.1, @mastra/valkey-streams@0.5.1, @mastra/vectorize@1.1.2, @mastra/vercel@1.5.0, @mastra/voice-aws-nova-sonic@0.2.2, @mastra/voice-azure@0.12.1, @mastra/voice-cloudflare@0.13.2, @mastra/voice-deepgram@0.13.1, @mastra/voice-elevenlabs@0.13.1, @mastra/voice-gladia@0.13.1, @mastra/voice-google@0.14.2, @mastra/voice-google-gemini-live@0.14.8, @mastra/voice-inworld@0.4.2, @mastra/voice-mistral@0.1.1, @mastra/voice-modelslab@0.2.1, @mastra/voice-murf@0.13.1, @mastra/voice-openai@0.13.1, @mastra/voice-openai-realtime@0.13.8, @mastra/voice-playai@0.13.1, @mastra/voice-sarvam@1.1.1, @mastra/voice-speechify@0.14.1, @mastra/voice-xai-realtime@0.2.8, @mastra/voyageai@0.4.1

  • Fixed per-tool requireApproval functions (needsApprovalFn) receiving no context on durable agents and agent.network(). They now get the same { requestContext, workspace } second argument as stream()/generate(), so approval logic that reads the request context works consistently. On durable agents the request context is restored from the persisted run snapshot when the check runs in another process or after a resume. Fixes #22491 (#22841)

  • Fixed tool observability helpers to emit correlated logs and child spans during agent execution. (#22887)

  • Fixed the scheduler polling storage every 10 seconds in apps that never create a schedule, which kept Railway/Neon-style deployments from scaling to zero. The scheduler now runs a single listSchedules() check at boot and only starts polling when a schedule exists or is created (or when scheduler: { enabled: true } / MASTRA_WORKERS=scheduler opts in explicitly), and worker processes learn about schedules created by the API process through the shared PubSub backend instead of constant polling. Also fixed deferred notifications sent from a workers: false API process never being dispatched: that process now registers the dispatcher schedule so a standalone worker can run it. (#22909)

  • Fixed signals sent after DurableAgent recovery so they are drained by the resumed run. (#22781)

  • Fixed durable agent run recovery losing the fallback model list. After a process restart, DurableAgent.recover() now restores the live fallback models with the ids the run was prepared with, so recovered runs keep using custom or dynamically resolved fallback model instances instead of failing or falling back to models rebuilt from serialized config. Fixes #22594. (#22860)

  • runEvals now honours the trajectory contract in both gate loops. A scorer created with type: 'trajectory' is typed as receiving output: Trajectory, and the scorers.trajectory path already resolved one and threaded expectedTrajectory; the top-level gates loop and the per-turn turns[].gates loop passed the raw target output and no expectedTrajectory, so such a gate scored 0 on every item as soon as it read a trajectory field. Workflow targets resolve the trajectory from step results, matching the scorer path, instead of being handed the workflow's own result. Gate failures are also no longer silent: a throwing gate still scores 0, but the cause is logged with the gate id instead of being discarded by a bare catch. (#22903)

  • Fixed AgentController dropping streamed assistant text and reasoning after page reload. Text and reasoning deltas that arrive without a seeded part (for example after a mid-stream step rotation) are now folded into the message instead of being silently discarded. Fixes #22712 (#22775)

  • Fixed LocalSandbox native isolation being unable to open /dev/null, which broke git, ssh, and shell redirections (e.g. 2>/dev/null) inside the sandbox. On Linux, the Bubblewrap backend now mounts a fresh /dev with standard device nodes, emitted after all configured binds so existing workarounds like readOnlyPaths: ['/dev'] no longer shadow it. On macOS, the Seatbelt profile now allows writing to the standard device nodes (/dev/null, /dev/zero, /dev/random, /dev/urandom, /dev/tty). Fixes #22702 (#22791)

  • Fixed durable agent traces being polluted by output-stream processor spans. The durable per-chunk processor pipeline ran without a tracing context, so every output stream processor span exported with no parent. Span stores that label a trace by its newest root row then showed a processor id instead of the agent. (#22677)

    • Output-stream processor spans now nest under the run's agent run span.
    • Tool-call chunks and resumed runs parent their processor spans the same way.
    • The tool-call pipeline ends its processor spans right after each chunk, so none stay open.
    • Callers without a tracing context no longer create processor spans, so orphan trace roots can never appear.

    Fixes #22602

  • Fixed plan approvals so mode changes resume the original agent run. (#22476)

  • Fixed durable agent output processors receiving an empty request context, including after server restarts. (#22327)

  • Fixed durable agents dropping already-streamed assistant text from memory when a run is aborted mid-stream. The partial response was visible in the live stream and in the onAbort callback, but disappeared after a reload or memory recall — only the user message remained. Aborted runs now persist the partial assistant message to memory, matching the regular agent's behavior. Fixes #22593 (#22872)

  • Fixed background tasks advertising the _background override to every tool. Previously, enabling backgroundTasks on the Mastra instance injected the _background field into every tool's input schema and listed every tool as background-eligible in the system prompt, even when neither the agent nor the tool opted in. Now only tools that are actually background-eligible — via the agent's backgroundTasks.tools config or the tool's own background: { enabled: true } — advertise the override, matching the runtime dispatch behavior. This removes roughly 2,000 characters of prompt overhead per ineligible tool and stops the model from being told it can background tools it cannot. Fixes #22724. (#22777)

  • Fixed approval resumes when Agent Controller uses in-memory storage. (#22476)

  • Fixed workspace skill discovery silently reporting zero skills when the workspace filesystem is mis-wired. Invalid-argument errors (Node ERR_INVALID_ARG*, e.g. a non-string path handed to the skill source) now surface from refresh() instead of being logged as an inaccessible skills path warning. Genuine access failures and network errors keep the warn-and-continue behavior. Closes #22639 (#22823)

  • Fixed UnixSocketPubSub accepting unbounded inbound frames. Added a maxInboundFrameBytes option (default 64 MiB); a connection that sends a larger frame, or an unterminated partial frame beyond that size, is disconnected, and partial frames are buffered compactly regardless of how they are fragmented, so a single peer can no longer exhaust broker memory. Fixes #22376 (#22882)

    import { UnixSocketPubSub } from '@mastra/core/events';
    
    const pubsub = new UnixSocketPubSub('/tmp/mastra.sock', {
      maxInboundFrameBytes: 8 * 1024 * 1024, // 8 MiB
    });
  • Removed the vulnerable @ai-sdk/provider-utils@3.x dependency (CVE-2026-8769 / GHSA-866g-f22w-33x8) from @mastra/core. The helpers it supplied are now sourced from the patched provider-utils 4.x line already installed, so security audits no longer flag @mastra/core and its dependents. AI SDK v5 model compatibility is unchanged. Fixes #22592. (#22790)

  • Fixed listResolved() on versioned storage domains issuing one version query per listed entity. @mastra/core adds an overridable getVersions(ids) method that listResolved() uses to fetch all active versions in a single batch; adapters that don't override it keep their previous per-id behavior. @mastra/pg and @mastra/libsql override it for the agents and skills domains with a single WHERE id IN (...) query. Fixes #22524 (#22828)

    Also in: @mastra/libsql@1.22.3, @mastra/pg@1.22.3

  • Fixed observability signals from Studio (mastra dev) being tagged with environment: production. Runs started through mastra dev now resolve to development unless an explicit environment is configured. Fixes #21941 (#22734)

  • Record TripWire aborts on workflow-path PROCESSOR_RUN spans as span errors with the structured tripwireAbort attribute (reason, retry, metadata), matching the legacy processor-runner path. Previously these spans ended like successful runs with only output.tripwire, dropping the retry flag and error info. (#22350)

  • Fixed onDelegationComplete reporting successful sub-agent runs when the finish reason is error. (#22483)

  • Fixed scheduled workflows disappearing from Studio's Schedules tab for dynamically created workflows. Dynamic workflow definitions now persist their schedule configuration, so schedules are re-declared after a restart instead of being deleted as orphans. Schedules of dynamic workflows that fail to load are also kept instead of being swept. Fixes #22756 (#22778)

    Also in: @mastra/libsql@1.22.3, @mastra/mongodb@1.18.5, @mastra/mssql@1.7.4, @mastra/mysql@0.8.5, @mastra/pg@1.22.3, @mastra/spanner@1.6.4

  • Fixed span metadata values being silently erased by keys whose value is undefined. Values extracted via requestContextKeys (for example a threadId set on a RequestContext) now reach exported spans even when the agent has no memory configured, so exporters like Arize can group traces into sessions again. Keys passed in tracingOptions.metadata with undefined values no longer remove values the span already has; keys with real values still take precedence. Fixes #22597. (#22742)

    Also in: @mastra/observability@1.17.5

  • Remove CHANGELOG.md from distributed npm files resulting in reduced package size (#22737)

    Also in: @mastra/acp@0.4.1, @mastra/agent-browser@0.5.2, @mastra/agent-builder@1.1.16, @mastra/agentcore@0.5.0, @mastra/agentfs@0.2.1, @mastra/ai-sdk@1.10.1, @mastra/apple-container@0.5.0, @mastra/archil@0.2.1, @mastra/arize@1.3.13, @mastra/arthur@0.4.13, @mastra/astra@1.1.1, @mastra/auth@1.1.3, @mastra/auth-auth0@1.2.3, @mastra/auth-better-auth@1.1.5, @mastra/auth-clerk@1.2.4, @mastra/auth-cloud@1.2.5, @mastra/auth-firebase@1.1.2, @mastra/auth-google@0.1.2, @mastra/auth-neon@0.3.2, @mastra/auth-okta@0.2.2, @mastra/auth-studio@1.3.5, @mastra/auth-supabase@1.1.4, @mastra/auth-workos@1.6.5, @mastra/azure@0.3.1, @mastra/blaxel@0.9.0, @mastra/braintrust@1.3.10, @mastra/brightdata@0.3.1, @mastra/browser-firecrawl@0.2.2, @mastra/browser-viewer@0.2.3, @mastra/chroma@1.1.3, @mastra/claude@0.3.1, @mastra/clickhouse@1.16.1, @mastra/client-js@1.43.0, @mastra/cloudflare@1.6.4, @mastra/cloudflare-d1@1.3.2, @mastra/cloudflare-sandbox@0.3.0, @mastra/code-sdk@1.6.0, @mastra/codemod@1.1.2, @mastra/convex@1.5.6, @mastra/couchbase@1.1.2, @mastra/cursor@0.3.1, @mastra/datadog@1.4.5, @mastra/daytona@0.10.0, @mastra/deepeval@0.1.7, @mastra/deployer@1.64.0, @mastra/deployer-cloud@1.64.0, @mastra/deployer-cloudflare@1.2.23, @mastra/deployer-netlify@1.2.23, @mastra/deployer-sandbox@0.3.8, @mastra/deployer-vercel@1.2.23, @mastra/docker@0.7.0, @mastra/dsql@1.3.4, @mastra/duckdb@1.6.4, @mastra/dynamodb@1.3.3, @mastra/e2b@0.11.0, @mastra/e2b-desktop@0.1.1, @mastra/editor@0.14.3, @mastra/elasticsearch@1.4.1, @mastra/elysia@0.1.4, @mastra/evals@1.10.0, @mastra/express@1.5.8, @mastra/factory@0.12.0, @mastra/fastembed@1.3.1, @mastra/fastify@1.5.8, @mastra/files-sdk@0.3.1, @mastra/gcs@0.3.3, @mastra/github-signals@0.4.0, @mastra/google-cloud-pubsub@1.1.3, @mastra/google-drive@0.2.1, @mastra/hono@1.7.6, @mastra/inngest@1.8.9, @mastra/isolated-vm@0.1.2, @mastra/koa@1.7.8, @mastra/laminar@1.3.15, @mastra/lance@1.3.2, @mastra/langfuse@1.5.4, @mastra/langsmith@1.3.15, @mastra/libsql@1.22.3, @mastra/livekit@0.3.1, @mastra/loggers@1.3.1, @mastra/mcp@1.17.3, @mastra/mcp-docs-server@1.2.23, @mastra/mcp-registry-registry@1.1.2, @mastra/memory@1.28.2, @mastra/mesa@0.2.1, @mastra/modal@0.6.0, @mastra/mongodb@1.18.5, @mastra/mssql@1.7.4, @mastra/mysql@0.8.5, @mastra/nestjs@0.2.23, @mastra/next@0.2.22, @mastra/observability@1.17.5, @mastra/openai@1.1.1, @mastra/opencode@0.1.23, @mastra/opensearch@1.1.1, @mastra/oracledb@0.2.2, @mastra/otel-bridge@1.5.5, @mastra/otel-exporter@1.3.13, @mastra/parallel@0.1.1, @mastra/perplexity@0.2.1, @mastra/pg@1.22.3, @mastra/pinecone@1.1.1, @mastra/platform-workspace@1.5.0, @mastra/playground-ui@52.0.0, @mastra/posthog@1.3.7, @mastra/qdrant@1.1.3, @mastra/quickjs@0.1.1, @mastra/rag@2.6.1, @mastra/railway@0.8.0, @mastra/react@1.4.10, @mastra/redis@1.4.3, @mastra/redis-streams@0.4.1, @mastra/s3@0.6.2, @mastra/s3vectors@1.1.2, @mastra/schema-compat@1.3.8, @mastra/sentry@1.2.15, @mastra/server@1.64.0, @mastra/slack@1.6.3, @mastra/spanner@1.6.4, @mastra/stagehand@0.3.4, @mastra/tanstack-start@0.2.22, @mastra/tavily@1.1.2, @mastra/telegram@0.1.1, @mastra/temporal@0.4.2, @mastra/turbopuffer@1.2.1, @mastra/turso@0.1.3, @mastra/upstash@1.4.4, @mastra/valkey@0.2.1, @mastra/valkey-streams@0.5.1, @mastra/vectorize@1.1.2, @mastra/vercel@1.5.0, @mastra/voice-aws-nova-sonic@0.2.2, @mastra/voice-azure@0.12.1, @mastra/voice-cloudflare@0.13.2, @mastra/voice-deepgram@0.13.1, @mastra/voice-elevenlabs@0.13.1, @mastra/voice-gladia@0.13.1, @mastra/voice-google@0.14.2, @mastra/voice-google-gemini-live@0.14.8, @mastra/voice-inworld@0.4.2, @mastra/voice-mistral@0.1.1, @mastra/voice-modelslab@0.2.1, @mastra/voice-murf@0.13.1, @mastra/voice-openai@0.13.1, @mastra/voice-openai-realtime@0.13.8, @mastra/voice-playai@0.13.1, @mastra/voice-sarvam@1.1.1, @mastra/voice-speechify@0.14.1, @mastra/voice-xai-realtime@0.2.8, @mastra/voyageai@0.4.1

@mastra/browser-viewer@0.2.3

Patch Changes

  • Fixed the browser viewer reporting no URL and no tabs. getCurrentUrl() and getBrowserState() on BrowserViewer always returned null because they were never wired to the internal tab state. The viewer now returns the current URL and the list of open tabs. Fixes #22539. (#22624)

@mastra/clickhouse@1.16.1

Patch Changes

  • Corrected the minimum supported @mastra/core version to match the APIs used by this store. (#22564)

    Also in: @mastra/cloudflare-d1@1.3.2, @mastra/dsql@1.3.4, @mastra/duckdb@1.6.4, @mastra/dynamodb@1.3.3, @mastra/elasticsearch@1.4.1, @mastra/mssql@1.7.4, @mastra/oracledb@0.2.2, @mastra/redis@1.4.3, @mastra/spanner@1.6.4, @mastra/valkey@0.2.1

  • Added reviewStatus support to observability feedback storage so feedback can be listed by review state and marked as reviewed. Existing rows default to needs-review; updates are append-only (a new row is inserted and reads use FINAL). (#22805)

    const { feedback } = await storage.listFeedback({ filters: { reviewStatus: 'needs-review' } });
    await storage.updateFeedbackReviewStatus({ feedbackId: feedback[0].feedbackId, reviewStatus: 'reviewed' });

@mastra/client-js@1.43.0

Minor Changes

  • Add host-injected trace signal management contracts and an OSS-owned Intelligence settings pane for custom signal configuration. Custom signal instructions use one task prompt field without a separate response-rules contract. (#21758)

    <TraceIntelligenceProvider signalManagement={signalManagement}>
      <TraceIntelligenceEntityIndex {...indexProps} />
    </TraceIntelligenceProvider>

    Also in: @mastra/playground-ui@52.0.0

  • Added custom trace signal names and server-provided signal catalog types to Trace Intelligence responses. (#21758)

    import type { ThemeEntitiesResponse } from '@mastra/client-js';
    
    function signalLabels(response: ThemeEntitiesResponse) {
      return response.entities[0]?.signalCatalog?.map(signal => signal.label) ?? [];
    }
  • Added rolling-compatible Trace Intelligence entity index metadata types and an index-first list and compact view with controlled search, sorting, and view state. (#21758)

    <TraceIntelligenceEntityIndex
      search={search}
      sort={sort}
      view={view}
      getEntityHref={entity => `/intelligence/entities/${entity.entityType}/${entity.entityId}`}
    />

    Also in: @mastra/playground-ui@52.0.0

Patch Changes

  • Added the optional hasBrowser field to the serialized agent response type. (#22789)

@mastra/code-sdk@1.6.0

Minor Changes

  • SandboxFilesystem accepts a lazy workdir — a resolver function awaited on the first file operation and memoized — for sandboxes whose workspace root is only knowable once the VM is running (repos clone into the VM's own home dir). basePath reports empty and resolveAbsolutePath returns undefined until the root resolves; a failed resolution is not memoized, so the next operation retries. (#22065)

  • Remove the sandbox reattach seam (@mastra/code-sdk/agents/sandbox-reattachregisterSandboxReattach/reattachProjectSandbox) and the state-driven sandbox workspace branch in getDynamicWorkspace (state.projectRepositoryId/sandboxId/sandboxWorkdir). Factory resolves session workspaces through its own sandbox callback; the UI-pushed sandbox coordinates in controller state were read by a code path that could no longer execute. The sandboxId/sandboxWorkdir/worktreePath state fields are removed from the state schema entirely — nothing reads them (the workdir is always live-resolved from the sandbox, and the sandbox id is the session id). Old clients still sending them are unaffected: unknown state keys are stripped on parse. (#22065)

  • Enabled first-message thread title generation for all Mastra Code clients. (#22560)

Patch Changes

  • Improve internal observational-memory processing. (#22738)

    Also in: @mastra/factory@0.12.0, @mastra/memory@1.28.2

  • Signed-in Factory accounts now get a clear, actionable error when no usable provider credential is available, instead of a silent failure. (#22721)

  • Hosted sessions no longer leak the host process's environment into the system prompt. The dynamic instructions builder drops its process.cwd() fallback: a session without a projectPath gets no working directory, no host git-branch probe, and loads no instruction files at all (project locations would resolve against the server's cwd and global locations against the server's homedir). Factory additionally blanks the SDK's default project identity seed (projectPath/projectName/gitBranch from the host's own checkout) so chat-only sessions show "(no workspace attached)" instead of the server's repo and branch; repo-backed sessions keep getting their real session workdir pinned by workspace resolution. (#22065)

    Also in: @mastra/factory@0.12.0

@mastra/daytona@0.10.0

Minor Changes

  • An explicit workingDirectory option skips the automatic working-directory probe; the probe still runs when the option is absent. (#22697)

@mastra/deployer@1.64.0

Patch Changes

  • Fixed the browser screencast stream and session probe not finding workspace-level CLI browser providers. The server's browser stream getToolset now falls back to the agent's workspace browser when no agent-level browser is configured. Fixes #22535 (#22789)

@mastra/dsql@1.3.4

Patch Changes

  • Fixed memory thread and resource timestamps being written in the server's local timezone. createdAt and updatedAt are now stored as UTC, so both timestamp column variants hold the same instant regardless of where the process runs. (#22818)

@mastra/duckdb@1.6.4

Patch Changes

  • Add reviewStatus support to observability feedback storage: new column with migration (defaults to needs-review), read/write mapping, reviewStatus list filtering, and updateFeedbackReviewStatus implementation. (#22805)

    Also in: @mastra/pg@1.22.3

@mastra/e2b@0.11.0

Minor Changes

  • Added repository templates, so sandboxes start with a warm checkout (#22065)

    createRepoTemplate() builds an E2B template with the repository already cloned and its setup command already run. Sessions then start from a prepared image instead of paying a cold clone and install.

    new E2BSandbox({
      id: sessionId,
      template: createRepoTemplate({
        getRepositoryAccess: async () => ({
          cloneUrl: 'https://github.com/acme/widgets.git',
          authorization: { scheme: 'bearer', token: await mintInstallationToken() },
        }),
        setupCommand: 'pnpm install',
      }),
    });

    getRepositoryAccess supplies the clone URL and, for private repositories, a short-lived credential. It returns undefined from createRepoTemplate() when the accessor is absent, so a session with no repository needs no conditional at the call site. The credential authenticates the head lookup and the build's clone through an in-shell auth header, reaching the template definition's environment but never the image filesystem. It's set as GH_TOKEN, the same variable a session installs before running setup, so a setup command behaves identically in both places.

    Only the first build ever blocks a start

    There's one template per repository, setup command, and workdir, with the commit sha as a tag (mastra-repo-<owner>-<repo>-<hash>:sha-<sha>). Without an explicit sha the template pins itself to the repository's current default-branch head at resolution time. When the head moves, the next sandbox boots immediately from the previous build while the new sha builds in the background, and runtime setup fast-forwards the checkout. A failed build falls back to the default template plus a runtime clone, so a broken build never wedges a session.

    Added buildEnv for setup commands that need credentials

    Registry tokens, private index URLs, and anything else the setup command needs at build time. Accepts a record or an async resolver. Values are part of the template's identity, so changing one produces a new template.

    Added refreshRepoTemplate() for warming templates ahead of time

    The same resolution the lazy start path performs, exposed standalone and awaited, so a cron or a merge-to-main handler can build the template before anyone opens a session.

    Default template ships a current Node.js LTS with corepack enabled

    The e2b base image carries Node 20.9.0, old enough that corepack-fetched package managers crash on it, so a setup command like pnpm i && pnpm build failed out of the box. The default mountable template now installs a pinned Node 24.20.0 over the stale runtime and enables corepack with the download prompt disabled, so pnpm and yarn resolve to whatever a repository's packageManager field pins. Repo templates build on the default mountable template, so they inherit the working toolchain. Pick a different release with the new nodeVersion option:

    createDefaultMountableTemplate({ nodeVersion: '22.23.2' });

    The version is exact and identity-bearing: changing it builds a new template, so a version change can never silently reuse a build at the old runtime. Existing default and repo templates rebuild once on first use after upgrading.

    Machine resources: cpuCount and memoryMB

    The built template's sandboxes get exactly that machine size. Resources are part of the template's identity — hashed into the template name alongside the repository, setup command, and build env — so a resize builds a new template instead of silently reusing one built at the old size. Absent options normalize to the SDK defaults (2 vCPU, 1024 MB). When a repo template's build fails and the sandbox degrades to the default mountable template, the default is built at the requested size too, so a 2 GB session's setup never lands in a 1 GB fallback and runs out of memory.

    new E2BSandbox({
      id: sessionId,
      template: createRepoTemplate({ ...ctx, memoryMB: 2048, cpuCount: 4 }),
    });
  • createRepoTemplate now runs each command (clone, fetch, checkout, and each setup command) as its own cached build step, and setupCommand accepts an array. A new workingDirectory option sets the cwd for the build and for sandboxes created from the template; the repository is cloned to <workingDirectory>/<repo>. When omitted, the clone lands in the base image's working directory instead of $HOME. (#22698)

    createRepoTemplate({
      getRepositoryAccess,
      setupCommand: ['pnpm i', 'pnpm build'],
      workingDirectory: '/workspace',
    });

    Also in: @mastra/platform-workspace@1.5.0

Patch Changes

  • Repo templates now write .mastra-sandbox/setup beside the checkout as their last build step. It contains sha256:<digest of the setup commands>, so a sandbox booted from the template can tell that this setup already ran. (#22837)

    Also in: @mastra/platform-workspace@1.5.0

  • Repository templates now clone with --depth=1 --single-branch, so template builds transfer less history. (#22840)

    Also in: @mastra/platform-workspace@1.5.0

  • Fixed repo templates silently degrading to a repo-less template on hosts without a git binary (deployed Mastra servers), which made every session cold-clone at runtime. GitHub (github.com) clone URLs now resolve the default-branch head through the GitHub REST API; other hosts keep using git ls-remote. (#22833)

    Also in: @mastra/platform-workspace@1.5.0

@mastra/editor@0.14.3

Patch Changes

  • Preserved tool schemas when applying stored descriptions. (#22459)

@mastra/evals@1.10.0

Minor Changes

  • Add @mastra/evals/vitest for running runEvals evaluations as Vitest tests. (#22665)

    • expectEvals/expectEval fail the test when the eval doesn't pass.
    • Optional custom matchers (toHaveVerdict, toHaveScoreAbove, toHaveScoreBelow, toPassGates, toPassThresholds) via @mastra/evals/vitest/setup.
    • MastraEvalsReporter prints per-test scores in the runner output.
    import { test } from 'vitest';
    import { expectEvals } from '@mastra/evals/vitest';
    
    test('capitals agent answers with the expected city', { timeout: 60_000 }, async () => {
      await expectEvals({
        target: capitalsAgent,
        data: [{ input: 'What is the capital of France?', groundTruth: 'Paris' }],
        gates: [containsGroundTruth],
      }).toPass();
    });

@mastra/factory@0.12.0

Minor Changes

  • Attention now rides the project feed stream: an automation failure, a proposal parked for approval, an approval, a dismissal, a retry, a supersede or a work-item deletion reaches every open page as it happens, and the attention list falls back to polling only while its stream is down. (#22604)

    Marking your own list read stays local — a read receipt changes nobody else's view, so it is not broadcast.

  • Added sandboxStart: 'eager' | 'lazy' to MastraFactoryConfig. 'eager' starts a session's sandbox as soon as its workspace is first resolved instead of on the agent's first command. Defaults to 'lazy'. (#22577)

    new MastraFactory({
      sandbox: ctx => new PlatformSandbox({ id: ctx.sessionId }),
      sandboxStart: 'eager',
    });
  • Removed the automatic sandbox snapshot Factory took after every agent turn. (#22846)

    PlatformSandbox.destroy() on E2B now only kills the sandbox instead of first asking the platform to delete a recovery checkpoint.

    Also in: @mastra/platform-workspace@1.5.0

  • BREAKING: sandbox is now a callback that constructs the session's sandbox (#22065)

    // Before — no longer accepted
    sandbox: { machine: new RailwaySandbox({ apiToken }), workdir: '/workspace', maxSandboxes: 4 }
    
    // After
    sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })

    A factory still configured with the old options object fails at prepare() with a message showing the replacement: machine becomes the provider instance you construct inside the callback, workdir is gone (remote providers clone into the VM's home directory, local providers use their own workingDirectory), and maxSandboxes is gone with the fleet. Omit sandbox entirely to run without sandboxes. ctx.getRepositoryAccess resolves the session repository's clone URL plus a fresh short-lived credential (undefined when the session has no repository), so providers can authenticate work such as private-repo template builds.

    Session sandboxes now boot lazily at the first real command instead of being provisioned up front. The sandbox fleet (pooling, budgets, reattach/revival, base checkpoints) is deleted, and the /ensure endpoint and the session UI's "preparing sandbox" step go with it — opening a thread provisions nothing. To check whether a sandbox is configured, read sandboxEnabled from GET /web/github/status.

    A failing setup command no longer wedges the session. The first failure surfaces loudly in the tool result that triggered it, then later starts skip the known-bad command — the clone and branch checkout still run, so the agent can repair or rerun setup itself. Infrastructure failures (clone, checkout, transport) keep failing hard and retry in full.

    Existing databases keep the fleet-era tables and columns as untouched orphans; dropping them is a manual operation.

  • Session start checks for the .mastra-sandbox/setup marker beside the checkout and skips the setup command when the marker holds the sha256 digest of the project's current setup command. Sandboxes booted from a warm repo template carry the marker already; setup runs once when it is missing or stale and writes the marker afterwards. (#22837)

  • Runs started by the Factory no longer stall without appearing in Needs attention. (#22530)

    A run that writes a plan used to suspend inside its thread and wait forever: the card said Building, nothing built, and no error appeared anywhere.

    Added: an Auto-approve plans switch on the board

    Find it in the board's automation settings, beside Auto-start runs. Off by default, which is what runs already did — except a plan nobody is watching now surfaces in Needs attention instead of hanging. On, the Factory answers the plan itself and the run carries the item through to Done. An agent that keeps re-planning is stopped after three approvals and handed to a person.

    Fixed: who a parked plan waits for

    With the switch off, where a parked plan goes depends on who started the run. A plan on a rule-started run escalates through the rule's own decision, the record Needs attention is built on. A plan on a run a person started keeps waiting for that person, because that pause is the point. With the switch on, the Factory answers both.

    Fixed: two smaller holes on the same path

    An agent asking to move its own card no longer parks the run behind an approval prompt nobody is watching; the rules engine still governs every move. And a failure that can never succeed on a retry stops burning attempts before it reaches someone.

  • Board lanes now mean engagement: a card enters a working lane only when a run starts on it or a person moves it there, and resting a card takes the Factory's hand off it. (#22531)

    Every GitHub issue and pull request arrives in Intake. Trust moved out of the column layout and onto the card: arrivals are stamped with whether the Factory may pick them up on its own, an External mark shows cards the execution gate treats as externally authored, and a card whose run the Factory would start shows that as a suggestion you can release with a click. Reviewing means a review is running — before, a maintainer's pull request was born there with nothing reviewing it.

    Consent follows the same line. A person's drag into a working lane hands the Factory the work; any entry into Intake, Done or Canceled takes it back, whoever rested the card — a verdict, a mirrored close, a drag. The close-out run a resting transition queued still fires, pre-approved by the transition that committed it. An external event can no longer pull a rested card back into a working lane or start a run on it without a person's consent, and a card from an author without write access never self-starts — even armed, even with auto-run on. A GitHub card missing its trust stamp — created before stamps existed — fails closed and asks too. The reconcile sweep keeps the stamp current in both directions: it backfills missing stamps and withdraws trust from authors whose write access was revoked, each within one sweep cycle (a few minutes by default); the Factory's own pull requests count as trusted through their authorship.

    A card parked in Intake offers Resume as its primary action, re-entering the deepest seat it used, and asking the card's agent in chat to resume does the same through the governed transition. Dragging a card out no longer opens a session just to say so: the stop notice reaches whichever session is live on the card, or nobody. A run landing its card in a lane no longer dispatches a second run.

  • A Factory run waiting on any answer now surfaces instead of parking silently. (#22649)

    Fixed: questions stalled the same way plans used to

    The plan gate covered submit_plan only. A run that asked a question through ask_user still stalled with the card saying Building. Any tool suspension on an unattended run now lands in Needs attention as "Agent is waiting for an answer".

    Unchanged: pauses that belong to a person

    Person-started runs are untouched — their pauses wait for the person reading them. Auto-approved plans stay the only pause the Factory answers itself, because a question has no approvable default.

  • Approving a proposed run now happens in the attention inbox instead of sending someone to the Rules page. (#22709)

    A queue of proposals is a handful of decisions repeated, not a list of distinct ones. A rules engine proposes the same run for every card it matches, so fifty rows reading invokeSkill · triage said nothing that "32 triage runs" does not. The queue sits grouped by role in a panel above the timeline, collapsed, one line per shape; expanding a group names the work item each proposal is for, so a row finally says what it would start. The banner that only counted the queue is gone, and the sidebar popover's approval link opens the inbox.

    A group can be dismissed whole, behind a confirm step — the way out of a queue nobody wants. There is deliberately no matching "run all": that would bill dozens of agent runs from one click, with no bulk route to make it atomic.

    The queue says how much of itself it is showing. The count in the header is the true pending total; when more proposals exist than one page holds, the panel says how many of them loaded, and the per-group "oldest" timestamp is dropped rather than reporting the oldest of a partial page as the oldest of the queue.

    An attention row says what landed as a badgemention, comment, failed — carrying the icon of its kind and dimming once read, instead of a coloured bead beside a sentence fragment, with the card as the row's title and the author ahead of the message. The sidebar popover scrolls through its preview again: its list had been capped by the popover's own height rather than the scroller's, which left the scroller measuring no overflow and the popover clipping the rows it could not show.

    The Rules page holds only what nothing else shows now that failures and approvals both land in attention — the full effect lifecycle, decisions with no work item, and the succeeded/dismissed history — so it leaves the Factory navigation and is reached from an attention row or global search.

  • Added a hands-off start for work items. (#22652)

    How it works

    Pick "Investigate hands-off" or "Build hands-off" in a card's menu instead of the plain start — restarts too: a card whose run already happened offers "Re-review hands-off" and a hands-off twin of its lane's run. "Prepare approval" has no twin — that run's outcome is a maintainer decision, which hands-off cannot remove. The run's parked plans are approved on your behalf, even while the project's Auto-approve plans switch stays off.

    The grant sticks to the item, not the run, so the Factory's own follow-up runs on that card stay hands-off too. Other cards keep waiting for plan review, and a hands-off run that keeps re-planning still stops after three approvals.

  • Factory Overview now shows what landed in the repository, not only what moved on the board. (#22709)

    A Latest commits section reads the connected repository's default branch newest-first, on the same day-rail the Activity and attention pages use: the branch tip carries a ring, everything behind it a filled bead, and each row gives the subject, its author, the short sha and when it landed, opening the commit on GitHub. A Factory with no repository linked says so rather than sitting on a skeleton.

    A Factory whose board was busy all day but whose main branch has not moved now says so on the page that is supposed to answer that question, instead of sending someone to GitHub to find out. The section leans on GET /web/github/projects/:id/commits, so it costs one rate-limited call per visit rather than a poll.

  • Commenting on a work item now notifies everyone already in that discussion, not just the people it names. Participants land in a separate activity tier of GET /web/factory/projects/:id/attention, counted apart under activityUnreadCount so the notification badge and sound stay reserved for mentions and failures. The attention inbox also refreshes while open now: comment-driven entries arrive over the feed stream, and the list polls every 5s for the rest. The sidebar popover asks the server for the badge tier (?tier=badge), so busy discussions can no longer crowd mentions and failures out of its five slots. (#22571)

  • Rebuilt the Factory Overview at /factories/:id/overview around what needed a person and what the Factory shipped, and gave board traffic its own page. (#22709)

    The page opens on a stage funnel for the work created in the selected window (7, 30 or 90 days), each card placed by the furthest stage it ever reached rather than by where it sits today.

    • The saturated core is what got that far with nobody stepping in, the pale sheath what a person had to close. Moves made by Factory rules count as unattended, so the autonomy figures no longer bill automation to a person.
    • Every loss peels off as its own hatched arm, billed to the column the work actually stopped in, so a drop keeps the thickness it cost instead of turning into whitespace.
    • Each column carries its typical hold. Hovering one says how much of it ran hands-off, what it lost since the column before, and what landed as a merged pull request; hovering an arm says how much stopped there and whether it was called off, still holding the column, or left without a decision.
    • The first rung reads Entered, not Intake: that board column also holds live GitHub and Linear candidates with no work item behind them, which the page cannot count.
    • Pull requests the Factory reviewed are counted beside the funnel — a count and only a count, since whether they went on to merge is the team's decision rather than the Factory's work.

    Why reach and not time-to-ship. On a real board almost nothing that lands in Done has passed through Execute and Review: Done is where cards get closed, not where work lands. A "shipped in this window, this fast, this hands-off" figure reads off that same history and cannot stand behind any of the three. Reach can, from the same records, so that is what the page reports.

    Under the funnel: what is stalled, what is running now, the latest commits, and a preview of activity and of what needs you.

    Board traffic has its own Activity page at /factories/:id/activity, reading as a rail cut by day. It shows everything the Factory did, not only stage moves — the board's own stage history for the moves, and the audit trail for the runs started, commits, pushes and comments a move is not, two sources that never describe the same fact.

    • Each entry reads as a sentence: who acted, what they did, which card, hung off a coloured bead on one continuous rail.
    • A card walked through several stages by one actor folds into a single chain instead of repeating its title, and neighbours saying the same thing about a different card become one sentence with those cards listed in a panel under it.
    • Days are cut by a ruled heading and each row carries its minute on the right edge, so a screenful reads as prose down the middle rather than as a column of timestamps.

    The attention inbox and the rule effects page now read on that same rail — cut by day, each row hanging off the mark of what it is (the kind of message, or the status of the effect) and carrying its time on the right edge, so the three pages are one surface instead of three list styles. The attention inbox also paginates like they do: older items load as the list is scrolled instead of waiting on a click.

    Every page opens at its top. The data router carried the window scroll across navigations, so leaving a scrolled Overview landed on Activity halfway down it.

  • Work item comment feeds now update live instead of on a five-second poll: while a browser holds its feed stream, a new comment shows up the moment it lands, and a browser whose stream dropped falls back to the old poll until it reconnects. (#22570)

    Delivery rides the factory's pubsub, so reaching browsers across replicas takes a shared broker — the in-process default only serves readers held by the replica that took the write:

    import { MastraFactory } from '@mastra/factory';
    import { RedisStreamsPubSub } from '@mastra/redis-streams';
    
    export const factory = new MastraFactory({
      pubsub: new RedisStreamsPubSub({ url: process.env.REDIS_URL }),
    });
  • Slack threads and work-item feeds are now one conversation seen from two windows. (#22641)

    Slack → feed — a message starting with aside, the human chatter the agent deliberately never answers, now lands as a comment on the card the thread created. A sender who has linked their Slack account is attributed to their Mastra user; an unlinked one is stored under their Slack identity and display name, so the thread stays complete either way.

    Feed → Slack — a comment written in the Factory feed is posted into the bound Slack thread, attributed as **Name**: body (the app cannot post as the commenter).

    A Slack card is now keyed by workspace as well as thread: ExternalWorkItemSource grows an optional workspaceId, and externalSourceKey — the one builder cards and mirrored comments now share — includes it. A channel id and a message ts only identify a thread inside the workspace that issued them, so without it two workspaces running the same app could share a key: an aside could land on another tenant's card, or recover their comment instead of storing its own. Cards created before this ships keep their unscoped key, and the lookup still accepts that older form, so their threads keep syncing. Nothing writes it any more, so the set only shrinks.

    Both directions are create-only: comment edits and deletions do not propagate, and Slack edits and deletions never reach the feed because the adapter does not deliver those events to handlers. Mirroring stays best-effort — a failed post is logged, not retried — and it runs past the response: the comment is stored and its feed frame is out before the platform is called, so writing a comment never waits on Slack. createComment hands the in-flight mirror back as mirrored for callers that need to observe it. Slack's own client is now given a 15s per-request timeout, which it did not have; its default retry policy can otherwise sit on a rate-limited chat.postMessage for about thirty minutes.

    A channel integration opts in by implementing the new feedPublisher slot alongside channels:

    class SlackIntegration implements FactoryIntegration {
      channels(ctx: IntegrationContext) {
        return createSlackChannelsConfig({ ...deps, feed: ctx.feed });
      }
    
      feedPublisher(ctx: IntegrationContext) {
        return new SlackFeedPublisher({ controller: ctx.controller });
      }
    }

Patch Changes

  • Factory badges now follow the unified design-system Badge: same soft corners, inset ring and size scale everywhere, and each badge names the color it wants rather than a mood. Provider access, model packs and the model picker keep their meaning — green for a working credential, blue for one that comes from the org or the environment — but they now read as one family instead of three slightly different pills. (#22640)

  • The completion chime is lower and longer. It swells into a soft tail that rings out over about two and a half seconds, instead of the short ding that stopped dead almost as soon as it started. (#22566)

    It is also much louder than the chime it replaces. If you leave the completion sound on, check your volume after this release.

  • Fixed issues and pull requests from outside the write-access circle asking for approval again at every lane after a person had already started them. Starting, dragging, or approving a run on such a card now carries that consent through the runs queued by the card's agent on its way to review. One gesture takes the card to a pull request instead of one click per lane. The same holds when a person creates a card straight into a working lane or moves it there through the API, and when the card's agent moves it on from a chat: the run queued by that move no longer waits for a click. Runs queued by a GitHub event on the card still ask first. An agent still cannot pull a rested external card back into work. A run pre-approved by an agent opens its session under the repository connector, never under the agent's id. (#22862)

  • Sessions on a remote sandbox with an absolute workingDirectory resolve the checkout at <workingDirectory>/<repo> without probing the VM. Sandboxes without one (or with a non-absolute one) keep the probe behavior. (#22698)

  • Session start no longer runs git pull on an existing checkout; it only clones when the repo is missing from the sandbox. Start-path phases (workspace.onStart, workspace.setup-marker, workspace.setup) now log their timings. (#22840)

  • Reduced the GitHub integration's REST request volume. Collaborator permission lookups are cached for 30 minutes per repo and login, and the PR/issue reconcile sweeps default to hourly instead of every 5 minutes; event polling and webhooks remain the primary sync. Override the interval with MASTRACODE_PLATFORM_GITHUB_RECONCILE_INTERVAL_MS (platform) or MASTRACODE_GITHUB_RECONCILE_INTERVAL_MS (direct), or the _PR_ / _ISSUE_ variants. (#22835)

  • Improved Factory PR reviews by treating external CI status and issue links as advisory context while keeping independently verified defects blocking. (#22667)

  • Improved the board card status shown while a linked card is synced. Instead of "Filing a linked card…", cards now name the system they mirror, for example "Syncing GitHub pull request…" or "Couldn't sync GitHub issue". (#22883)

  • Factory panels now take their shadow and their chart ramp from the design system instead of redeclaring them, so they stay in step with every other surface when those tokens change. (#22713)

  • Session status (#22786)

    • Fixed session status disagreeing between sidebar rows, board cards, and the open chat.
    • Running, setting up, and waiting-on-you states now read the same after a reload and in every tab.
    • Removed the per-browser "your turn" mark; a card waiting on a person is marked from the card itself, and a finished session with nothing waiting shows as idle.

    Chat

    • Fixed the favicon claiming the session awaits input while history is still loading.
    • Allow steering or stopping a running session as soon as it is connected.

    Done sound

    • Plays once when a run watched in this tab ends.
  • A run started from a work item with comments shows its prompt as the collapsed skill row again, with the item's discussion in its own collapsible row beside it, instead of one raw message wide enough to break the page. Only that discussion may follow the skill envelope; anything else keeps the message raw. (#22551)

    The Mastra Code terminal folds the same prompt into its skill row.

  • Factory now remembers a maintainer's acceptance of non-bug work. Triage classifies feature requests, questions and docs as needing a person's decision; before, that decision was checked again on every later agent move, so a card a maintainer had dragged into Planning still stalled when the plan agent tried to advance it to Build. (#22921)

    • The first time a person moves a held card into Planning or Build, the acceptance is recorded on the work item. Later agent transitions along the working lanes proceed without a second gesture, and the gate only guards the exit from Intake/Triage. Cards accepted before this release are recognised by their stage, and pick up the record on their next human move.
    • On acceptance, the GitHub status: needs approval label is removed automatically (best effort; a label failure never blocks the move).
    • A held card on the board leads with the decision — Accept and plan, Accept and build, or Close — and says why it waits (Feature request · needs your approval). Runs, re-runs and suggested runs on that card are withheld until it is accepted, so nothing advances it as a side effect. Bugs are never held.
  • Audit log range picking moves off the chart and onto a ruler below it. (#22709)

    The chart is display-only. Marks outside the selected range fade instead of being framed by a drag rectangle, gridlines follow the day ticks and fade out top and bottom, and dashed guides mark the selected limits.

    The ruler under it carries a single translucent lens. Drag its sides to resize, drag its body to slide the window; the day and time of each edge read above and below it. Selection is continuous down to the minute rather than snapped, so a window of a few minutes is as reachable as one of several days, and the exact range also reads out next to the event count. Arrow keys nudge an edge, Escape returns to the full range.

    On narrow screens the lens gives way to 1h/6h/24h/7d chips. A full-width drag surface left no room for precise handles and blocked vertical scrolling.

    The axis stops moving under the marks. It now spans everything loaded rather than the current category filter, so toggling a category no longer rescales it, and the chart holds a fixed height at any width instead of squashing its lanes on a narrow screen. Each category lane carries a faint dotted rule, so a mark reads as sitting on its lane and a chart with nothing to plot still shows its shape rather than going blank.

    An empty log says so instead of drawing a chart over an invented seven-day window — filtering to a category with no events used to shift the axis onto dates where nothing had happened — and the empty states now say what is missing and how to get back.

  • Fixed authenticated workspace skill runs so tenant OAuth credentials remain available to agent and memory models. (#22721)

  • Improved Factory issue triage to label issues by domain in addition to confirmed direct @mastra/core bugs. Triage now selects domain labels such as Agents, Workflows, Memory, Observability (AI Telemetry), and RAG from where the change would land, applying several only when a change genuinely spans domains. (#22753)

  • The reconcile sweep now records author trust for cards it had stopped visiting, so a board whose cards reached Done or Canceled before trust was recorded gets its answers on the next sweep instead of never. The board's External mark reads a recorded answer instead of the absence of one, so a card nobody was ever asked about is no longer labelled an outside contribution. The execution-consent gate is unchanged: a card with no recorded answer still asks for a person before it starts a run. (#22644)

  • Fixed board cards for imported GitHub and Linear issues/PRs showing "just now" — cards now show how long ago the issue or PR was opened upstream instead of when the factory first saw it. (#22848)

  • Added GET /web/github/projects/:id/commits (optional branch, limit), which lists recent commits for an installed repository. (#22709)

    It reads with the same installation token that already clones and pushes, rather than through getInstallationOctokit: the Platform build answers that call with a stub carrying pull-request reads only, so anything reaching for repos.* would have been undefined at runtime while the cast kept the compiler quiet.

  • Fixed repository-local Factory skills not loading when the bundled skills exist. Factory now layers the consumer repo's src/mastra/public/factory-skills directory over the bundled Factory skills, so projects can add custom pipeline skills (or override built-in ones) without patching node_modules. The local skills root is also resolved correctly for the cwd variants used by mastra factory dev --dir src/mastra. Fixes #22707. (#22723)

  • Fixed "Linked card could not be filed: A work item cannot relate to itself" on Review cards. When GitHub polling re-observed a pull request that already had a card, the dispatcher tried to link the card to itself as its parent. (#22848)

  • Fixed Factory project sessions using organization model credentials. (#22741)

  • Improved how a session says what it is doing. A sidebar row now carries an activity rail down its left edge instead of a dot in its trailing slot: marks travel down the rail while a run is underway, in the setup colour while the sandbox is still starting, and settle into a slow breath once the session is waiting on you. A board card shows the same thing on its own border — a lit head running the outline while work is in flight, the whole outline lit once the card is waiting on you. (#22751)

    Removed the marker for a session that is merely bound to a card: the card offers an Open session button instead, and the work item panel drops its dot for the same reason. Moving lifecycle off the row's trailing slot frees it — the actions menu no longer displaces the marker on hover, and a merged pull request keeps its badge.

  • Fixed the "ready — your turn" mark only going away when a session was opened from the sidebar. Opening the same thread through a board card, a deep link, or the attention inbox left the mark lit, and a run finishing in the very thread being read marked it as needing attention. Landing on a session's route — through any door — now dismisses its mark, and the open session never gets marked at all. The completion sound still plays for it, so a backgrounded tab still calls you back. (#22749)

  • Factory board cards now show live session status: idle, initializing, working, and ready. Cards and sidebar rows read the same status, so they always agree. A cloning workspace shows initializing, a running session shows working, and a finished run waiting for you shows ready. (#22748)

  • Improved Factory PR review to audit documentation changes on mastra-ai/mastra with the repository's docs-audit skill, so docs changes are graded against the canonical authoring guidance instead of only general review judgement. (#22766)

  • Fixed automated runs abandoning their session when a run is re-prepared (for example after a server restart). The run now lands back in the work item's existing session for that role instead of creating a replacement — so the session keeps its original owner instead of switching to whoever approved the run, and no orphaned sandbox is left behind. (#22410)

  • Fixed personal memory settings in the web app showing (and running) the default Gemini observer model after signing in with another provider. Signing in with a provider OAuth flow or saving a personal API key now seeds your unset observer and reflector models from that provider, matching the TUI onboarding behavior. (#22847)

  • Session comments panel (#22551)

    The comments panel opens at half the height of the chat and grows with the conversation up to the full column, instead of opening full height around an empty feed. It morphs open from the workspace card. The composer is a flush bar at the bottom edge and takes focus as the panel opens. A comment that lands while you are watching rises into place. Editing a comment saves on Enter and keeps Shift+Enter for a new line, with Cancel and Save inside the bottom right of the field. The edit field grows with its text up to ten lines, then scrolls, with no resize handle. The workspace card's corners are concentric with the rows inside it. Session rows in the sidebar no longer open their hover details on a touch viewport, where the card could only appear behind the tap that already navigated away.

    Board cards

    A sent comment lands in the feed as soon as the server stores it, with no dimmed placeholder. A failed send keeps the draft and shows the error. Opening a card widens a copy of it over the card, every row anchored where the card had it, and pulls a tray out from beneath, a little narrower than the copy, so opening moves nothing you were about to click. Hovering a card no longer floats its full title over it; the open copy shows it whole.

    The tray holds one timeline in time order: the item's runs, moves and comments, with the composer at the bottom. The description heads that stream in a block of its own, an Activity rule between them. The tray opens at its full height whatever the stream holds, waits for the description and the comments together, and lands them one after another, so nothing shifts once it loads.

    Cards share a minimum height, with their bottom row pinned to the bottom edge. That row leads with one small button, the likeliest next click: the item's run, Retry after a failure, the suggested run while one waits, Open session while one runs. It lights up only while the card waits on you, to release a suggested run, retry a failure, or answer a session that asked. It goes quiet the moment a run is starting, so a lit button on the board always means your turn. A card in a lane leads with that lane's own run. The other buttons sit tucked under the first like pulled tabs, on the card and in its open copy alike. While a session runs, no rival run is offered beside it. The suggestion itself is a badge in the status row. The last worker sits at the right of the bottom row, the name before the picture, and the External badge sits at the right of its own row above. An expand icon appears beside the card's menu on hover, in the spot where the open copy puts Collapse. The copy names its source link with the item's icon and number instead of an arrow. The card's corners round a little more, concentric with the buttons in them, and the empty state of a column rounds its corners the same way.

    Intake and the rest of the board

    An intake candidate opens the same way, its run buttons in the copy and its description in the tray. Labels stay on one line that scrolls sideways instead of wrapping. A card near the bottom of the window opens its tray above the card instead of climbing to fit, and in the last column the tray slides left by its overflow while the copy stays over the card. The board dims under the open card, and a click on the dim closes it without pressing whatever sat underneath.

    On a phone

    The sheet opens straight onto the same timeline and composer, and hugs its content instead of opening at a fixed height.

@mastra/github-signals@0.4.0

Minor Changes

  • Added multi-PR GitHub signal tools and improved notification filtering. (#22407)

    Agents can subscribe to multiple pull requests, unsubscribe from multiple pull requests, and unsubscribe from all tracked pull requests. The tool input shape now uses a prs array instead of the old single-PR top-level fields.

    Before:

    { "owner": "mastra-ai", "repo": "mastra", "number": 123 }

    After:

    { "prs": [{ "owner": "mastra-ai", "repo": "mastra", "number": 123 }] }

    Unsubscribe all:

    { "all": true }

    GitHub signal notifications now also filter repeated low-value bot comments such as skipped CodeRabbit reviews and bot status summaries.

@mastra/hono@1.7.6

Patch Changes

  • Fixed custom API routes registered with registerApiRoute failing with a 500 error ("Response body object should not be disturbed or locked") when server middleware read the request body (e.g. await c.req.json()) before the route handler ran. Request bodies now survive middleware reads via json(), text(), formData(), or the raw request. Fixes #22596 (#22776)

    Also in: @mastra/server@1.64.0

@mastra/inngest@1.8.9

Patch Changes

  • Fix durable tool execution on the Inngest engine running with no tracing context. The extract-tool-calls step now forwards the LLM step's exported model_step span onto every tool-call input (matching @mastra/core's durable workflow), so each tool call creates a live tool_call span with execution-time children (e.g. workspace_action and client-tool spans) nested under the LLM call. The retroactive tool_call span creation in the collect step was removed — it produced childless duplicate spans and redundant step-span end/tool-result chunk events already handled by the shared LLM mapping step. Fixes #19842. (#22279)

@mastra/langfuse@1.5.4

Patch Changes

  • Map the root span's input/output to langfuse.trace.input/langfuse.trace.output so Langfuse traces carry trace-level input and output again. Previously the exporter only mapped input/output onto observations, leaving every trace's top-level input/output empty — which broke LLM-as-a-judge evaluators bound to Trace input/output and removed the request/response summary from the Langfuse trace view. User-facing behavior otherwise unchanged; existing trace name/tags/metadata mappings take the same precedence as before. (#22696)

@mastra/libsql@1.22.3

Patch Changes

  • Fixed local LibSQL vector corruption during concurrent writes. (#22834)

  • Fix the LibSQL vector filter's $size operator emitting invalid SQL: the builder misused the filter value as a parameter index, producing named references ($2, $5, …) that never matched the positional bindings. $size now binds through a standard ? placeholder like every other operator, and the shared vector filter tests (supportsSize) are enabled for LibSQL accordingly. (#22752)

@mastra/livekit@0.3.1

Patch Changes

  • @mastra/livekit/worker now exports MastraVoiceAgent and createMastraVoiceAgent (with the MastraVoiceAgentOptions and MastraStreamOptions types). This is the voice.Agent subclass createLiveKitWorker() builds per session, so you can construct it yourself when you own the voice.AgentSession — for example to test a Mastra-backed agent with @livekit/agents' voice.testing harness without speech-to-text, text-to-speech, or a running worker. (#22820)

    import { initializeLogger, voice } from '@livekit/agents';
    import { createMastraVoiceAgent } from '@mastra/livekit/worker';
    
    initializeLogger({ level: 'silent', pretty: false }); // required outside a LiveKit worker
    
    const session = new voice.AgentSession();
    await session.start({ agent: createMastraVoiceAgent({ agent: supportAgent, memory: false }) });
    
    const result = session.run({ userInput: 'What are your opening hours?' });
    await result.wait();
    result.expect.nextEvent().isMessage({ role: 'assistant' });

@mastra/loggers@1.3.1

Patch Changes

  • Fixed Errors logged under the error key being recorded as an empty object. (#22913)

    Pino only applies its error serializer to the err key, and an Error's message and stack are non-enumerable. So logger.warn('...', { error }), the convention used throughout Mastra, was written as error: {} and the real failure was lost. The most visible symptom was Error listing tools for agent with no details.

    PinoLogger now applies Pino's standard error serializer to the error key as well, so the type, message, and stack are recorded. The built-in err key keeps working. Values under error that are not error-like (no string message property) are passed through unchanged. Error-like plain objects are normalized to the same { type, message, stack } shape with their own fields preserved.

    A new serializers option lets you override or extend the defaults:

    new PinoLogger({
      serializers: { error: err => ({ message: err.message }) },
    });

@mastra/mcp@1.17.3

Patch Changes

  • Fixed MCP tools not validating structured tool results against the tool's output schema. Tools rebuilt from a cached catalog with toolFromDefinition / toolsFromDefinitions (and live-discovered tools whose result bypasses the SDK check) now validate structuredContent before it reaches the model, and return the same structured validation error that createTool produces on mismatch. Valid results, in-band tool errors, and results without structuredContent are unchanged. Fixes #22549 (#22779)

  • Required @mastra/core 1.64 or newer so MCP can use the tool output validation API added in that release. (#22817)

@mastra/memory@1.28.2

Patch Changes

  • Added persistent reminder conversations and asynchronous ask_memory questions with correlated partial and terminal replies. The tool returns immediately with a reply ID and pending status, then delivers answers later as correlated signals. (#22783)

    Enable the experimental reminder sidekick on observational memory:

    import { Memory, Subconscious } from '@mastra/memory';
    
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'openai/gpt-5-mini',
          experimental_subconscious: new Subconscious({ observation: ['remind'] }),
        },
      },
    });
  • Improve tool schema compatibility with providers that validate tool definitions server-side. (#22487)

  • Fixed observational memory prompts appearing in stored conversation history. (#22324)

@mastra/observability@1.17.5

Patch Changes

  • Fixed a startup crash on older @mastra/core versions by defining two small id helpers locally instead of importing them from core. (#22889)

    This package imported generateSignalId and resolveExportedSpanId from @mastra/core/observability. Both are recent additions to core — generateSignalId in 1.26.0, resolveExportedSpanId in 1.63.0 — while the declared peer range still accepted @mastra/core from 1.16.0 up. A named ESM import of an export that does not exist fails when Node links the module graph, so an older core installed with no warning and then took the whole app down before any application code ran:

    SyntaxError: The requested module '@mastra/core/observability'
    does not provide an export named 'resolveExportedSpanId'
    

    Most projects never named this package — it arrives as a transitive dependency of an observability exporter such as @mastra/langfuse or @mastra/otel-exporter — so the failure typically surfaced first in a deploy.

    Both helpers are self-contained: generateSignalId wraps crypto.randomUUID, and resolveExportedSpanId is structurally typed against an optional span method rather than any core class. Keeping local copies removes the version coupling entirely and makes the declared >=1.16.0 range true again, rather than moving the floor up to 1.63.0.

    No API change. If you hit the error above, this release fixes it without requiring a @mastra/core upgrade.

@mastra/pg@1.22.3

Patch Changes

  • Fixed PgVector failing with "Vector table does not exist" when no schemaName is configured and the connecting role has a same-named schema on its search_path. Catalog lookups used by listIndexes, describeIndex and the namespace migration now resolve through the effective search_path (like the table creation already did) instead of assuming the public schema. Without an explicit schemaName, listIndexes now reports vector tables from every schema on the search_path. Fixes #22545 (#22827)

@mastra/platform-workspace@1.5.0

Minor Changes

  • Added reusable sandbox templates to Platform workspaces. Build templates through PlatformSandbox with the portable Template() API; Platform content-addresses each serialized definition for reuse. Public repositories can be warmed lazily with createRepoTemplate(). Use cpuCount() and memoryMB() to size E2B template builds and sandboxes created from the exact or a resource-matched stale build; createRepoTemplate() accepts the same sizing as plain options. Railway ignores these resource methods. (#22065)

    const sandbox = new PlatformSandbox({
      environmentId,
      template: Template().cpuCount(4).memoryMB(8192).runCmd('pnpm install'),
    });
    
    // createRepoTemplate takes the whole sandbox context: a session with no
    // repository gets undefined back and boots the provider default.
    const repoSandbox = new PlatformSandbox({
      environmentId,
      template: createRepoTemplate({
        getRepositoryAccess: async () => ({ cloneUrl: 'https://github.com/mastra-ai/mastra.git' }),
        setupCommand: 'pnpm install --frozen-lockfile',
        memoryMB: 2048,
      }),
    });

    Template environment values are serialized by default. Pass { ephemeral: true } to setEnvs() for short-lived build credentials that must stay outside the definition, identity, persistent record, and runtime environment. Template.build() can eagerly start or reuse the provider build without provisioning a sandbox. Railway includes transient values in its provider cache input, so rotating one may trigger another Railway build while the Platform template ID remains stable.

    PlatformSandbox.start() never blocks on a template build. When the exact template is not yet ready, Platform boots the sandbox on the best available fallback (an E2B prior member of the same family with matching effective resources if one exists, otherwise the provider base template) and builds the exact template in the background. A provider-base fallback may use provider-default resources. The sandbox surfaces templatePending for observability; reconcile filesystem state in your own runtime setup (for example, an onStart hook that runs git fetch && git checkout <sha>).

    Template().withFamily(key) attaches a caller-supplied family key that groups successive builds of the "same thing" (e.g. the same repository+workdir across commits) so an E2B definition can warm-start on a resource-matched prior member of the same family. Railway doesn't use family fallback. createRepoTemplate() populates the key automatically as repo:<cloneUrl>:<workdir>.

  • Changed Platform sandboxes to use E2B by default. Set sandboxProvider or SANDBOX_PROVIDER to railway to opt into Railway. (#22065)

  • createRepoTemplate accepts buildEnv: environment variables for the build steps only (for example, remote cache credentials). They never enter the template definition or identity. Template fallback warnings now redact credentials. (#22698)

@mastra/playground-ui@52.0.0

Minor Changes

  • Render server-defined trace signals in catalog order with custom labels, colors, lifecycle progress, and built-in fallbacks. (#21758)

    <TraceIntelligenceProvider signalCatalog={entity.signalCatalog}>
      <TraceIntelligenceEntityDetail entityType={entity.entityType} entityId={entity.entityId} />
    </TraceIntelligenceProvider>
  • Added a Messages tab to trace details so developers can review the user message, response, and tool calls for an agent turn. (#22911)

  • Added a shadow-panel token and a --chart-soft-1 through --chart-soft-5 sequential color ramp to the design system. (#22713)

    shadow-panel — the resting elevation for a panel that sits on the page rather than floating over it. It reads one step below shadow-dialog, so a page tiled with panels still looks like a single plane.

    <div className="border-border1 bg-surface3 shadow-panel rounded-xl border"></div>

    --chart-soft-1..5 — one hue, lightness carrying the reading, for a single measure shown at five depths. Use it where --chart-1..5 does not fit: that palette is categorical (five series, no order between them), this one is ordered. It flips with the theme, darkening on white and brightening on near-black.

    <Bar fill="var(--chart-soft-1)" />
  • Removed the separate Chip, ChipsGroup, and StatusBadge exports. Badge is now the single compact label and status primitive, with nine color-based variants, muted emphasis, sizes, icons, and dot or pulse indicators. Its variants are neutral, green, red, blue, yellow, purple, orange, cyan, and pink; omitting variant uses neutral. (#22640)

    ChipsGroup has no direct replacement. Use a layout element appropriate to the surrounding UI around Badge instances.

    Badge now renders an inline <span> instead of a <div>, and BadgeProps now extends HTMLAttributes<HTMLSpanElement> instead of HTMLAttributes<HTMLDivElement>. Update block-layout assumptions and div-specific refs or handlers when migrating.

    Badges use soft corners and a subtle ring, with an inner shadow in light mode and an inner glow in dark mode.

    Before:

    import { Chip, ChipsGroup } from '@mastra/playground-ui/components/Chip';
    import { StatusBadge } from '@mastra/playground-ui/components/StatusBadge';
    
    <ChipsGroup>
      <Chip color="purple" intensity="muted">Baseline</Chip>
      <Chip color="blue" intensity="muted">Candidate</Chip>
    </ChipsGroup>
    <StatusBadge variant="success" withDot>Connected</StatusBadge>

    After:

    import { Badge } from '@mastra/playground-ui/components/Badge';
    
    <div className="flex items-center gap-1">
      <Badge variant="purple" emphasis="muted">Baseline</Badge>
      <Badge variant="blue" emphasis="muted">Candidate</Badge>
    </div>
    <Badge variant="green" indicator="dot">Connected</Badge>

Patch Changes

  • Pinned the lucide-react peer dependency to the workspace catalog, so the published range tracks the version the design system is actually built against. Consumers now need lucide-react 1.37 or later. (#22718)

  • Fixed flat and factory section headings to use medium weight. (#22900)

  • Improved trace search in Studio: when a span matches your search only through its metadata, attributes or error payload, its name in the timeline is now painted in a distinct color so you can see why the row is there without opening it. Spans matching by name keep the usual highlight. (#22660)

  • Fixed span search results being hidden below the fold: opening a span from the traces search now scrolls the first highlighted match in the span detail panel into view (#22690)

  • useInView accepts an optional root ref so infinite-scroll sentinels can observe a list's own scroll viewport instead of the window. The Studio /inbox page now uses the shared PageLayout, Tabs, and DataList components with per-list scrolling and infinite loading for feedback. (#22805)

  • Redesigned the trace side panel summary in Studio. The metadata grid was replaced with a compact, single-line description under the trace title using icons and tooltips for the linked entity, start time, duration, input and output tokens, and estimated cost. The "Details" tab is now labeled "Spans", and secondary trace header actions are consolidated into a single actions menu. (#22804)

  • Improved the Studio traces list: combined the Date and Time columns into a single Created column, capped the Input column width so other columns stay visible, rendered trace status as a colored badge, and made the list shrink to the viewport instead of scrolling horizontally. Renamed the trace panel action to "Add full trace to dataset". (#22682)

  • Simplify the Trace Intelligence signal settings pane and correct index controls and pending-signal elevation styling. (#22908)

@mastra/react@1.4.10

Patch Changes

  • Moved the lucide-react icon dependency to 1.37, in step with the rest of the repo. No API change: the icons ship inside the built bundle and never appear in the published types. Apps pinned to lucide-react 0.x keep working, they just resolve a second copy. (#22718)

@mastra/s3@0.6.2

Patch Changes

  • Fixed S3 file stats to report MIME types so workspace read_file can return supported media correctly. (#22683)

@mastra/server@1.64.0

Patch Changes

  • Fixed the browser viewer registry giving up permanently when no browser toolset was available on first connect. It now retries the lookup when the next viewer connects. Related to #22537 (#22826)

  • Serialized agents now report a hasBrowser capability flag that is true for agent-level SDK browsers and workspace-level CLI browsers (which expose no SDK tools). Fixes #22535 (#22789)

  • Fixed the Studio browser viewer checking whether the wrong thread's browser was running. When a viewer connected for a specific thread, the server asked the browser toolset about the globally "current" thread instead of the viewer's thread, which could log "Browser ready" and "Browser not running" for the same thread and leave the viewer stuck waiting. The viewer's thread ID is now passed through so the check answers for the right thread. Fixes #22538 (#22815)

  • Fixed suspended tool responses timing out while the resumed agent run continues. The endpoint now acknowledges immediately, preventing spurious 504 responses and delayed response-body errors for long-running continuations. (#22680)

  • Added a startup warning when server.auth is configured without mapUserToResourceId. Without that callback, built-in routes trust the resource ID sent by the client (for example memory.resource), so an authenticated user could read or write threads belonging to another user. Configure mapUserToResourceId to derive the resource ID from the authenticated user. Fixes #22875. (#22915)

@mastra/upstash@1.4.4

Patch Changes

  • Corrected the minimum supported @mastra/core version to match this store's runtime dependencies. (#22564)

Other updated packages

The following packages were updated with dependency changes only: