npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@salesforce/sfdx-agent-harness-mastra

v0.33.0

Published

Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk

Readme

@salesforce/sfdx-agent-harness-mastra

Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk. Provides MastraHarnessFactory, the entry point consumers pass to createAgentManager.

Closed source. This package is published to npm under the Salesforce Public Code License and is for use by Salesforce only.

Install

npm install @salesforce/sfdx-agent-sdk @salesforce/sfdx-agent-harness-mastra

Quick start

import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
import { MastraHarnessFactory } from '@salesforce/sfdx-agent-harness-mastra';

const manager = await createAgentManager('/path/to/storage', new MastraHarnessFactory());

See the @salesforce/sfdx-agent-sdk README for the full consumer-facing API. For internal architecture, see ARCHITECTURE.md.

Removing the storage directory on Windows

manager.shutdown() checkpoints the WAL and closes the libsql client (forcedotcom/agentic-dx#429, mastra-ai/mastra#17285), but the underlying SQLite handle release on Windows is asynchronous at the OS level. After shutdown() returns, Windows can briefly hold the file lock — so a naïve fs.rm(storageRoot) immediately afterward can fail with EBUSY. Pass maxRetries and retryDelay so Node retries until the OS lets go:

import { rm } from 'node:fs/promises';

await manager.shutdown();
await rm(storageRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });

Linux and macOS release the handle eagerly; the retry options are no-ops on those platforms.

Public API

Core

| Export | Kind | Description | | ------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | MastraHarnessFactory | class | HarnessFactory that produces a Mastra-backed AgentHarness bound to a storage root. | | MastraHarnessFactoryConfig | interface | Configuration for MastraHarnessFactory (toolApprovalTimeoutMs?). | | MASTRA_BUILT_IN_TOOL_POLICIES | const | Frozen ReadonlyArray<ToolPolicyRule> the harness feeds the SDK's resolveToolApprovalPolicy as the tiers.harness slice — allow rules for Mastra's built-in updateWorkingMemory / skill / skill_search / skill_read tools. | | MASTRA_BUILT_IN_TOOL_NAMES | const | The four built-in tool leaf names MASTRA_BUILT_IN_TOOL_POLICIES covers. |

Connectivity (model + provider auth) is supplied by the SDK's AgentConnectivityResolver. Use the bundled DefaultAgentConnectivityResolver for the standard Salesforce LLM Gateway path, or ApiKeyConnectivityResolver for direct-provider / LLMG Express api-key endpoints — both ship from @salesforce/sfdx-agent-sdk. The harness dispatches on ModelConnectivityInfo.providerHint internally to pick the right pass-through builder for each provider.

HTTPS proxy routing

The harness honors HTTPS_PROXY / HTTP_PROXY / NO_PROXY (either casing) via @salesforce/agentic-common's resolveProxyDispatcher(). When either proxy var is set at factory create() time, the harness wraps every in-process fetch call site (LLM gateway language-model builders + MCP remote transports) with an undici-backed routing function — no globalThis mutation. When no proxy env is set, fetch runs unwrapped (zero overhead).

Snapshot timing. HTTPS_PROXY / HTTP_PROXY are captured at create() time and pinned for the harness's lifetime; NO_PROXY is re-evaluated per request. Set the proxy env vars before constructing the factory's first harness.

Extension surface

These types let consumers reach Mastra-specific features through the SDK's typed extensions slot. The SDK infers MastraAgentHarness from MastraHarnessFactory.create(), so consumers usually don't reference these types explicitly — they show up via inference at the call site.

| Export | Kind | Description | | ------------------------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | MastraAgentHarness | type | Branded AgentHarness subtype; carries the Mastra-specific extensions namespace and the inferred config type. | | MastraAgentConfig | type | AgentConfig extended with Mastra-only fields (toolSearch, skillSearch). Inferred at manager.createAgent automatically — annotation rarely needed. | | MastraToolSearchConfig | type | Shape of MastraAgentConfig.toolSearch (pool, alwaysActive, topK, ttl). | | MastraSkillSearchConfig | type | Shape of MastraAgentConfig.skillSearch (topK). Opts into Mastra's SkillSearchProcessor. | | ToolSearchHandle | type | Stable interface for the tool-search runtime: id, getStateStats, clearState, clearAllState, cleanupNow. Wraps Mastra's internal processor. | | SkillSearchHandle | type | Stable interface for the skill-search runtime: id (Mastra's processor has no other public methods today; the harness owns lifecycle). |

The Mastra-specific accessors live on manager.extensions.mastra (getToolSearchProcessor(agentId), getSkillSearchProcessor(agentId), formatMcpToolName(serverName, toolName)) and require no separate import.

Tool search

Hide a large tool catalog behind Mastra's search_tools / load_tool meta-tools:

import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
import { MastraHarnessFactory } from '@salesforce/sfdx-agent-harness-mastra';

const manager = await createAgentManager(storageRoot, new MastraHarnessFactory());

const agent = await manager.createAgent(projectRoot, {
  instructions: '...',
  tools: [...customTools],
  mcpServers: { sfdx: { type: 'stdio', command: 'npx', args: ['@salesforce/mcp'] } },
  toolSearch: {
    pool: '*', // or an explicit name list
    alwaysActive: [
      // Every tool from a server (post-discovery expansion).
      { serverName: 'sfdx' },
      // Exactly one tool on a specific server.
      { serverName: 'other', toolName: 'lookup_account' },
      // Any tool by this name, regardless of source — built-ins, workspace
      // tools, consumer-declared tools, AND any MCP server's tool surface.
      { toolName: 'resume_tool_operation' },
    ],
    topK: 5, // advisory; defaults to Mastra's 5
    ttl: 3_600_000, // ms; advisory; defaults to Mastra's 1h
  },
});

When toolSearch is set, createAgent blocks on MCP discovery so MCP tools enter the searchable pool. Without toolSearch, MCP discovery stays non-blocking (existing behavior). alwaysActive tools are visible directly to the model; everything else lives in the searchable pool.

Each alwaysActive entry is an AlwaysActiveEntry (re-exported from @salesforce/sfdx-agent-sdk/harness and shared with ClaudeAgentConfig.toolSearch.alwaysActive). At least one of serverName or toolName must be present — {} rejects at config time. Unknown pool names are dropped; unmatched { toolName }-only entries are kept for late MCP discovery.

MastraToolSearchConfig.topK and ttl are advisory — Mastra applies its own defaults if omitted.

Skill search

Skills (AgentConfig.skills) load eagerly by default. Set MastraAgentConfig.skillSearch to switch to on-demand loading via harness-injected search_skills / load_skill meta-tools (Mastra's SkillSearchProcessor):

const agent = await manager.createAgent(projectRoot, {
  instructions: '...',
  skills: ['/path/to/skills'],
  skillSearch: { topK: 5 }, // ranked, on-demand discovery; eager catalog stays out of the prompt
});

skillSearch.topK is advisory; Mastra defaults to 5 if omitted. The same skillSearch field exists on ClaudeAgentConfig, so cross-harness consumers can opt into on-demand skill discovery on either harness with the same shape.

Breaking change (#468). Pre-#468, on-demand skill search was implicitly gated on skills && toolSearch. That coupling has been removed: skillSearch is now the explicit opt-in, independent of toolSearch. Consumers who relied on the coupling must add skillSearch: {} to their config to keep the previous behavior.

Operational handles

Reach the tool-search and skill-search runtime state for an agent. The accessors return stable ToolSearchHandle / SkillSearchHandle interfaces — not Mastra's internal processor types — so consumer code keeps compiling across Mastra version bumps.

const tsHandle = manager.extensions.mastra.getToolSearchProcessor(agent.getId());
//    ^? ToolSearchHandle | undefined
const stats = tsHandle?.getStateStats();
// { threadCount, oldestAccessTime }

const ssHandle = manager.extensions.mastra.getSkillSearchProcessor(agent.getId());
//    ^? SkillSearchHandle | undefined

Both return undefined when the agent doesn't have the corresponding feature configured (or doesn't exist). The Mastra-internal pool / index / loaded-tools state is intentionally not on the handle interface; the documented methods are getStateStats(), clearState(threadId?), clearAllState(), and cleanupNow() on ToolSearchHandle, plus id on both handles.

MCP tool-event enrichment

The harness enriches every tool-call, tool-result, and tool-approval-request event with the originating MCP server name and any annotations the server declared, so consumers can branch on tool hints or group by server without parsing the namespaced tool name themselves. serverName is undefined for non-MCP tools (workspace tools, consumer-declared tools); annotations is undefined when the server omitted the field (per the MCP spec).

for await (const event of result.eventStream) {
  if (event.type === 'tool-approval-request') {
    if (event.annotations?.readOnlyHint) {
      // Auto-approve safe reads.
      await session.approveToolCall(event.toolCall.toolCallId);
    } else {
      // Group/label by server when present, e.g. "sfdx wants to: deploy_metadata".
      const approved = await promptUser(event.serverName, event.toolCall);
      approved
        ? await session.approveToolCall(event.toolCall.toolCallId)
        : await session.declineToolCall(event.toolCall.toolCallId);
    }
  }
}

Tool-approval policy

The harness gates harness-executed tools through the SDK's resolveToolApprovalPolicy (configure rules via AgentConfig.toolPolicies / AgentConfig.defaultToolDecision — see the SDK README). For each non-consumer tool the harness resolves a decision and acts on it:

  • allow — the tool runs without surfacing a tool-approval-request.
  • deny — the tool is refused and the model receives a tool-result(isError=true) so it can recover; no tool-approval-request surfaces.
  • require-approval — a tool-approval-request is emitted and the workflow suspends until the consumer settles.

Gating is opt-in: it engages only when the agent's config sets toolPolicies or defaultToolDecision. With neither configured, tools run without gating (no suspension), matching the SDK's "no policy ⇒ no gating" default. The harness adds MASTRA_BUILT_IN_TOOL_POLICIES (Mastra's updateWorkingMemory / skill / skill_search / skill_read built-ins) and the SDK's skill_bridge meta-tool rules as allow defaults, so capability-discovery never prompts. Consumer-executed tools (AgentConfig.tools) always bypass the gate — their execution is the consumer's responsibility via submitToolResult. Set StreamOptions.batchApprovals: true to surface parallel require-approval requests on one stream for a batch-approval UI.

Tool approval timeout

When the approval gate is engaged and a tool resolves to require-approval, the harness suspends the workflow on each tool-approval-request and waits for the consumer to call approveToolCall(...) / declineToolCall(...) / submitToolResult(...). To bound how long a pending approval can wait — for example, when a UI process crashes or a user walks away — the factory accepts a toolApprovalTimeoutMs option. It defaults to 600_000 (10 minutes) and matches the ClaudeHarnessFactoryConfig knob of the same name.

const harnessFactory = new MastraHarnessFactory({
  toolApprovalTimeoutMs: 60_000, // auto-deny pending approvals after 60s
});

When the timer fires, the harness aborts the suspended Mastra run via the per-(agentId, threadId) MastraApprovalCoordinator, marks every emitted approval as auto-resolved, and yields a synthetic terminal pair on the active eventStream:

{ type: 'error', error: Error('Tool approval timed out after 60000ms.'), code: 'tool-approval-timeout' }
{ type: 'finish', finishReason: 'error' }

Subsequent approveToolCall / declineToolCall / submitToolResult calls for the timed-out tool are silent no-ops (issue #589 idempotent settle) — the coordinator already auto-resolved them, so the consumer's intended outcome is already true and there's nothing to fail. Only a toolCallId the coordinator never saw at all (typo, wrong session, called before tool-approval-request ever fired) throws, and that throw is an AgentSDKError whose type === AgentSDKErrorType.TOOL_CALL_NOT_FOUND. The granularity is per toolCallId — each emitted approval arms its own timer; the first one to fire tears down the whole turn (since Mastra can't continue the suspended run after timeout anyway). To observe the timeout, keep iterating the eventStream past the tool-approval-request event so the synthetic terminal pair lands in your loop:

for await (const event of result.eventStream) {
  if (event.type === 'error' && event.code === 'tool-approval-timeout') {
    // Pending approval was never answered.
  }
}

Tool-result redaction

Tool-result redaction is configured at the manager layer, not the factory. Pass a hooksForAgent callback to createAgentManager; the SDK threads the resolved AgentHooks bag through createAgent's options.hooks, and the Mastra harness registers a ToolResultRedactionProcessor on the agent's inputProcessors chain whose processInputStep walks new tool-result rows on the MessageList and replaces their content in place. The same ToolResultRedactor works on the Claude harness. See the SDK README → "Tool-Result Redaction" for the full pattern (including a failClosed(...) wrapper consumers can use to substitute a safe stub on throw).

The processor composes with existing harness-internal processors (ToolSearchProcessor, SkillSearchProcessor); no consumer-supplied processor passthrough is needed. Throws propagate from processInputStep and the harness's approval coordinator surfaces them as an error ChatEvent on the consumer's eventStream — see ARCHITECTURE.md for the full contract.

Using LLM Gateway Express

LLMG Express, Anthropic-direct, OpenAI-direct, and any other api-key endpoint flow through the SDK's ApiKeyConnectivityResolver (no harness-specific factory needed):

import { ApiKeyConnectivityResolver, createAgentManager } from '@salesforce/sfdx-agent-sdk';
import { MastraHarnessFactory } from '@salesforce/sfdx-agent-harness-mastra';

const manager = await createAgentManager('/path/to/storage', new MastraHarnessFactory(), {
  connectivityResolver: new ApiKeyConnectivityResolver({
    getApiKey: () => process.env.LLMG_EXPRESS_API_KEY!,
    baseUrl: process.env.LLMG_EXPRESS_BASE_URL!,
    providerHint: 'openai-compatible',
  }),
});

Development

npm run build              # TypeScript compilation
npm run test               # Unit tests with coverage
npm run lint               # ESLint
npm run lint:fix           # Auto-fix ESLint issues
npm run format             # Prettier
npm run clean              # Remove build artifacts

From the repo root:

  • npm run build -w @salesforce/sfdx-agent-harness-mastra
  • npm run test -w @salesforce/sfdx-agent-harness-mastra