nominee-mastra
v3.0.0
Published
Maps nominee ask rules into Mastra's native pause/resume (default off); otherwise ask throws ActionPendingError for durable resume.
Maintainers
Readme
nominee-mastra
Decision-bound tools for Mastra.
import { Nominee, allow, ask } from 'nominee'
import { nomineeTool } from 'nominee-mastra'
import { z } from 'zod'
const closeIssue = nomineeTool({
id: 'close-issue',
description: 'Close one GitHub issue',
inputSchema: z.object({ repo: z.string(), issue: z.number() }),
outputSchema: z.object({ closed: z.boolean() }),
nominee: new Nominee({
policy: {
rules: [allow('github.issue.read'), ask('github.issue.close')],
fallback: 'deny',
},
}),
action: 'github.issue.close',
user: ({ requestContext }) => String(requestContext.userId),
resource: ({ input }) => `repo:${input.repo}#${input.issue}`,
connection: 'github',
scopes: ['issues:write'],
nativeApprovals: true,
execute: async ({ repo, issue }, { token }) => {
await closeGitHubIssue({ repo, issue, token })
return { closed: true }
},
})Set nativeApprovals: true to map Nominee ask rules into Mastra's native
pause/resume flow for Mastra agent tools. The adapter binds approval evidence
to Mastra's runtime-generated toolCallId; workflow or direct execution
without that marker fails closed to Nominee's portable durable approval handle
(ActionPendingError). Leave nativeApprovals off to use that portable flow
everywhere. In both modes, denied calls never reach execute, and credentials
are resolved only after capability consumption.
What happens on ask
By default (nativeApprovals: false), a Nominee ask that cannot be settled inline throws ActionPendingError out of the tool's execute — the durable, portable path. Catch it, persist the actionId and the original input (the durable action record stores only an input hash), then resume later with resolveActionApproval() → resumeAction() → executeCapability(). With nativeApprovals: true, ask instead maps into Mastra's native pause/resume flow for agent tools, with approval evidence bound to Mastra's toolCallId; calls that lack that marker (workflows, direct execution) fail closed to the portable ActionPendingError path. In both modes denied calls never reach execute. Full walkthrough: Approvals that outlive the request.
Observe before enforcing
Use the same tool with new Nominee({ mode: 'observe' }) to inventory the
callbacks that actually run before writing a policy. The adapter's
requireApproval hook returns false in this mode—including for an explicitly
configured approval—while the original ask/deny verdict remains on the action
and receipts. Observation reports do not retain raw string/boolean values or
user IDs; numeric aggregates may be sensitive. Observe mode is not a security
control and cannot be combined with production: true.
